Message ID | 20230714120852.23573-4-frederic@kernel.org |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:a6b2:0:b0:3e4:2afc:c1 with SMTP id c18csp2466149vqm; Fri, 14 Jul 2023 05:25:24 -0700 (PDT) X-Google-Smtp-Source: APBJJlGf0AhC/2lLfawOBxKR2uhX3AeC5teJuNoMs0fXqH67vIT+AwZNgSJb7oAhcS3HiXb0Gc34 X-Received: by 2002:a17:902:ef85:b0:1b8:9044:9ede with SMTP id iz5-20020a170902ef8500b001b890449edemr3142806plb.62.1689337523646; Fri, 14 Jul 2023 05:25:23 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689337523; cv=none; d=google.com; s=arc-20160816; b=p0ELzQdtGt2Dy/iSdr2/UxOX0IJxpxCgsLQL+pQ686yvQFYO4QW2e4saes44kn0612 MEpq/NCzuAaMph4/e7Y2edmsoFylSztp/lj2fszWiNrhTLuGp+75DbjYiCoUFzpL0bzn x9uSrMpiXSr9SvHLERWhXCeQCqZh20jc+8zld6Ts8YyLPPm25IS/cyvmvA7LGAZxVsdj 42D9V/JeN2g2Y63b16WkBK8u3HLhQTV+Lr8XG+qpqfj0rL5NqylZzIB5vl4ZmjV8vwHw ocMP2w4Y3wQIkHNuNffY8tdOh5dyc5d0xDuL/d745fj+KXmR1snoJCfmYD4GgzqJXJsz U8MQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=PU/dDkGlaP+BQOkYqUFvlOR5a15Zkn2dL0CNUDn9lZE=; fh=vNYwn+erbeWwD/B9CW78N3vavRIPBl+xONykG2Vq2so=; b=DU++8RtuZvaZMR27ML6JPO7nV77Cmvqj0Jj6MPE66tXk2HStuekrYXysVb304AvaFL VzNKrE2M9cddqQQach2k6NlshzcYOjHA0/u4Tn5re/K2iI0lqKPYy/efRl0yuxnyLBCw 1ETYx5RMS3J8gccv9+90HFYHxzAi//1ncThOmaLn3rj0MZPRRcyqB31vs14DMmTu5nHe BiiyiBhMigFAgKvSbQcQBD/oFr+ZH3cNhNUuI91bRClzFzapxgzIcmMUmJ5HDh2uBHBo flaARkCWYtYp0Ga9xTfUu0t9BBk7WUCnmkHFYMqdJKDSfSJcGkCAQW7kun33ewXVilFM QYng== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=EELJSa6d; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id e11-20020a17090301cb00b001b3be4fadbfsi7315747plh.159.2023.07.14.05.25.10; Fri, 14 Jul 2023 05:25:23 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=EELJSa6d; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235732AbjGNMJK (ORCPT <rfc822;tebrre53rla2o@gmail.com> + 99 others); Fri, 14 Jul 2023 08:09:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57018 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235709AbjGNMJF (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Fri, 14 Jul 2023 08:09:05 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4BF92212B for <linux-kernel@vger.kernel.org>; Fri, 14 Jul 2023 05:09:04 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id CAB1B61CF1 for <linux-kernel@vger.kernel.org>; Fri, 14 Jul 2023 12:09:03 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 58240C433CB; Fri, 14 Jul 2023 12:09:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1689336543; bh=nUjt9aPIm8t8zpCcFZjv3pXhHtLHcHwW+ZKfovq5PGQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EELJSa6dEHGyMIPSwoSdvUpZm/AQsox03og/2qj+DWzf101pcI642XDG3WektyVaN Up2r9pkCt/JsJ+uVAS8qFHYS9q47Vl0auU+lk5wG1K4i0nXHEViENcW+PaUUjtEcO0 I0jp1jssPkj3Jw6hEQOlX8ED2NO/9PvKaEIG/qBLXWrn2yLoOmCw0+wuZvWbClBFWS qHrsbu/BS9vZNPeTJKpc1tLBS/fqB9mAhFrazZOeo40+nKK/LISOXEvWDkjx1COIAG 6WvBimR4igEcq/Q2+o2NdTmwbdUrUgS9zPOq4TX3IDFJsX+BliqACR+lg7nnRcegQ7 +EkBl5Y66fdOQ== From: Frederic Weisbecker <frederic@kernel.org> To: Thomas Gleixner <tglx@linutronix.de> Cc: LKML <linux-kernel@vger.kernel.org>, Frederic Weisbecker <frederic@kernel.org> Subject: [PATCH 3/3] tick/nohz: Don't shutdown the lowres tick from itself Date: Fri, 14 Jul 2023 14:08:52 +0200 Message-Id: <20230714120852.23573-4-frederic@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230714120852.23573-1-frederic@kernel.org> References: <20230714120852.23573-1-frederic@kernel.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-4.4 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_MED, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1771398783140064729 X-GMAIL-MSGID: 1771398783140064729 |
Series |
tick/nohz cleanups
|
|
Commit Message
Frederic Weisbecker
July 14, 2023, 12:08 p.m. UTC
In lowres dynticks mode, just like in highres dynticks mode, when there
is no tick to program in the future, the tick eventually gets
deactivated either:
* From the idle loop if in idle mode.
* From the IRQ exit if in full dynticks mode.
Therefore there is no need to deactivate it from the tick itself. This
just just brings more overhead in the idle tick path for no reason.
Fixes: 62c1256d5447 ("timers/nohz: Switch to ONESHOT_STOPPED in the low-res handler when the tick is stopped")
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
---
kernel/time/tick-sched.c | 24 +++++++++++++-----------
1 file changed, 13 insertions(+), 11 deletions(-)
Comments
On 7/14/23 08:08, Frederic Weisbecker wrote: > In lowres dynticks mode, just like in highres dynticks mode, when there > is no tick to program in the future, the tick eventually gets > deactivated either: > > * From the idle loop if in idle mode. > * From the IRQ exit if in full dynticks mode. > > Therefore there is no need to deactivate it from the tick itself. This > just just brings more overhead in the idle tick path for no reason. > > Fixes: 62c1256d5447 ("timers/nohz: Switch to ONESHOT_STOPPED in the low-res handler when the tick is stopped") > Signed-off-by: Frederic Weisbecker <frederic@kernel.org> > --- > kernel/time/tick-sched.c | 24 +++++++++++++----------- > 1 file changed, 13 insertions(+), 11 deletions(-) > > diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c > index dd25da8531f4..101251e103be 100644 > --- a/kernel/time/tick-sched.c > +++ b/kernel/time/tick-sched.c > @@ -1392,18 +1392,16 @@ static void tick_lowres_handler(struct clock_event_device *dev) > tick_sched_do_timer(ts, now); > tick_sched_handle(ts, regs); > > - if (unlikely(ts->tick_stopped)) { > - /* > - * The clockevent device is not reprogrammed, so change the > - * clock event device to ONESHOT_STOPPED to avoid spurious > - * interrupts on devices which might not be truly one shot. > - */ > - tick_program_event(KTIME_MAX, 1); > - return; > + /* > + * In dynticks mode, tick reprogram is deferred: > + * - to the idle task if in dynticks-idle > + * - to IRQ exit if in full-dynticks. > + */ > + if (likely(!ts->tick_stopped)) { > + hrtimer_forward(&ts->sched_timer, now, TICK_NSEC); > + tick_program_event(hrtimer_get_expires(&ts->sched_timer), 1); > } > > - hrtimer_forward(&ts->sched_timer, now, TICK_NSEC); > - tick_program_event(hrtimer_get_expires(&ts->sched_timer), 1); One slight concern here though, where in the idle loop is the removed statement "tick_program_event(KTIME_MAX, 1);" happening if the tick was already stopped before? If it is happening in tick_nohz_stop_tick(), don't we early return from there and avoid doing that "tick_program_event(KTIME_MAX, 1);" altogether, if the tick was already stopped and the next event has not changed? /* Skip reprogram of event if its not changed */ if (ts->tick_stopped && (expires == ts->next_tick)) { /* Sanity check: make sure clockevent is actually programmed */ if (tick == KTIME_MAX || ts->next_tick == [...] return; [...] } Also just a nit, here you can remove indent by doing: if (unlikely(ts->tick_stopped)) return; hrtimer_forward(&ts->sched_timer, now, TICK_NSEC); tick_program_event(hrtimer_get_expires(&ts->sched_timer), 1); Which is pretty much the original code except for the tick_program_event(). thanks, - Joel > } > > static inline void tick_nohz_activate(struct tick_sched *ts, int mode) > @@ -1508,7 +1506,11 @@ static enum hrtimer_restart tick_highres_handler(struct hrtimer *timer) > else > ts->next_tick = 0; > > - /* No need to reprogram if we are in idle or full dynticks mode */ > + /* > + * In dynticks mode, tick reprogram is deferred: > + * - to the idle task if in dynticks-idle > + * - to IRQ exit if in full-dynticks. > + */ > if (unlikely(ts->tick_stopped)) > return HRTIMER_NORESTART; >
On Fri, Jul 14, 2023 at 02:44:49PM -0400, Joel Fernandes wrote: > On 7/14/23 08:08, Frederic Weisbecker wrote: > One slight concern here though, where in the idle loop is the removed > statement "tick_program_event(KTIME_MAX, 1);" happening if the tick was > already stopped before? If it is happening in tick_nohz_stop_tick(), don't > we early return from there and avoid doing that > "tick_program_event(KTIME_MAX, 1);" altogether, if the tick was already > stopped and the next event has not changed? > > /* Skip reprogram of event if its not changed */ > if (ts->tick_stopped && (expires == ts->next_tick)) { > /* Sanity check: make sure clockevent is actually programmed */ > if (tick == KTIME_MAX || ts->next_tick == [...] > return; > [...] > } Sure, if tick_program_event(KTIME_MAX, 1) was already called in the previous idle loop iteration, then there is no need to call that again. Or am I missing something else? > > Also just a nit, here you can remove indent by doing: > > if (unlikely(ts->tick_stopped)) > return; > hrtimer_forward(&ts->sched_timer, now, TICK_NSEC); > tick_program_event(hrtimer_get_expires(&ts->sched_timer), 1); > > Which is pretty much the original code except for the tick_program_event(). Either I remove an indent or I remove a statement. I guess it's a matter of personal taste. I don't mind either way :-) Thanks.
On Fri, Jul 14, 2023 at 8:01 PM Frederic Weisbecker <frederic@kernel.org> wrote: > > On Fri, Jul 14, 2023 at 02:44:49PM -0400, Joel Fernandes wrote: > > On 7/14/23 08:08, Frederic Weisbecker wrote: > > One slight concern here though, where in the idle loop is the removed > > statement "tick_program_event(KTIME_MAX, 1);" happening if the tick was > > already stopped before? If it is happening in tick_nohz_stop_tick(), don't > > we early return from there and avoid doing that > > "tick_program_event(KTIME_MAX, 1);" altogether, if the tick was already > > stopped and the next event has not changed? > > > > /* Skip reprogram of event if its not changed */ > > if (ts->tick_stopped && (expires == ts->next_tick)) { > > /* Sanity check: make sure clockevent is actually programmed */ > > if (tick == KTIME_MAX || ts->next_tick == [...] > > return; > > [...] > > } > > Sure, if tick_program_event(KTIME_MAX, 1) was already called in the > previous idle loop iteration, then there is no need to call that again. > > Or am I missing something else? Just take it with a grain of salt but I think you need to still call tick_program_event(KTIME_MAX, 1) here for the case where the tick was previously stopped, and then when the next tick fires (say after a long time T), but that tick is a one-off and does not result in restarting the tick -- then there is no one to call "tick_program_event(KTIME_MAX, 1)". I think that's the concern Nick was addressing in [1] so that it resets the tick device correctly? [1] https://lore.kernel.org/lkml/165089105607.4207.3022534114716811208.tip-bot2@tip-bot2/ > > > > Also just a nit, here you can remove indent by doing: > > > > if (unlikely(ts->tick_stopped)) > > return; > > hrtimer_forward(&ts->sched_timer, now, TICK_NSEC); > > tick_program_event(hrtimer_get_expires(&ts->sched_timer), 1); > > > > Which is pretty much the original code except for the tick_program_event(). > > Either I remove an indent or I remove a statement. I guess it's a matter of > personal taste. I don't mind either way :-) Ah true, in defense of the "less indent" way, the original code was also using that style. ;-) But I am also Ok with either way. :-) thanks, - Joel
On Fri, Jul 14, 2023 at 09:02:43PM -0400, Joel Fernandes wrote: > On Fri, Jul 14, 2023 at 8:01 PM Frederic Weisbecker <frederic@kernel.org> wrote: > > > > On Fri, Jul 14, 2023 at 02:44:49PM -0400, Joel Fernandes wrote: > > > On 7/14/23 08:08, Frederic Weisbecker wrote: > > > One slight concern here though, where in the idle loop is the removed > > > statement "tick_program_event(KTIME_MAX, 1);" happening if the tick was > > > already stopped before? If it is happening in tick_nohz_stop_tick(), don't > > > we early return from there and avoid doing that > > > "tick_program_event(KTIME_MAX, 1);" altogether, if the tick was already > > > stopped and the next event has not changed? > > > > > > /* Skip reprogram of event if its not changed */ > > > if (ts->tick_stopped && (expires == ts->next_tick)) { > > > /* Sanity check: make sure clockevent is actually programmed */ > > > if (tick == KTIME_MAX || ts->next_tick == [...] > > > return; > > > [...] > > > } > > > > Sure, if tick_program_event(KTIME_MAX, 1) was already called in the > > previous idle loop iteration, then there is no need to call that again. > > > > Or am I missing something else? > > Just take it with a grain of salt but I think you need to still call > tick_program_event(KTIME_MAX, 1) here for the case where the tick was > previously stopped, and then when the next tick fires (say after a > long time T), but that tick is a one-off and does not result in > restarting the tick -- then there is no one to call > "tick_program_event(KTIME_MAX, 1)". I'm a bit confused about that one-off thing. What can trigger that timer interrupt if it has been stopped? One thing can happen though: a pending timer IRQ while we are stopping the tick (IRQs are disabled in that idle loop portion). But then that pending timer interrupt is not going to reprogram another one. So it remains stopped. Thanks.
> On Jul 15, 2023, at 2:19 PM, Frederic Weisbecker <frederic@kernel.org> wrote: > > On Fri, Jul 14, 2023 at 09:02:43PM -0400, Joel Fernandes wrote: >>> On Fri, Jul 14, 2023 at 8:01 PM Frederic Weisbecker <frederic@kernel.org> wrote: >>> >>> On Fri, Jul 14, 2023 at 02:44:49PM -0400, Joel Fernandes wrote: >>>> On 7/14/23 08:08, Frederic Weisbecker wrote: >>>> One slight concern here though, where in the idle loop is the removed >>>> statement "tick_program_event(KTIME_MAX, 1);" happening if the tick was >>>> already stopped before? If it is happening in tick_nohz_stop_tick(), don't >>>> we early return from there and avoid doing that >>>> "tick_program_event(KTIME_MAX, 1);" altogether, if the tick was already >>>> stopped and the next event has not changed? >>>> >>>> /* Skip reprogram of event if its not changed */ >>>> if (ts->tick_stopped && (expires == ts->next_tick)) { >>>> /* Sanity check: make sure clockevent is actually programmed */ >>>> if (tick == KTIME_MAX || ts->next_tick == [...] >>>> return; >>>> [...] >>>> } >>> >>> Sure, if tick_program_event(KTIME_MAX, 1) was already called in the >>> previous idle loop iteration, then there is no need to call that again. >>> >>> Or am I missing something else? >> >> Just take it with a grain of salt but I think you need to still call >> tick_program_event(KTIME_MAX, 1) here for the case where the tick was >> previously stopped, and then when the next tick fires (say after a >> long time T), but that tick is a one-off and does not result in >> restarting the tick -- then there is no one to call >> "tick_program_event(KTIME_MAX, 1)". > > I'm a bit confused about that one-off thing. What can trigger that timer > interrupt if it has been stopped? It is the tick that has been stopped in this scenario. The timer could still be armed to serve a future hrtimer? I think the naming in the code for is a bit confusing for tick vs timer event, so I could be the confused one. Thanks, - Joel > One thing can happen though: a pending timer IRQ while we are stopping the > tick (IRQs are disabled in that idle loop portion). But then that pending timer > interrupt is not going to reprogram another one. So it remains stopped. > > Thanks.
On Sat, Jul 15, 2023 at 08:18:57PM +0200, Frederic Weisbecker wrote: > On Fri, Jul 14, 2023 at 09:02:43PM -0400, Joel Fernandes wrote: > > On Fri, Jul 14, 2023 at 8:01 PM Frederic Weisbecker <frederic@kernel.org> wrote: > > > > > > On Fri, Jul 14, 2023 at 02:44:49PM -0400, Joel Fernandes wrote: > > > > On 7/14/23 08:08, Frederic Weisbecker wrote: > > > > One slight concern here though, where in the idle loop is the removed > > > > statement "tick_program_event(KTIME_MAX, 1);" happening if the tick was > > > > already stopped before? If it is happening in tick_nohz_stop_tick(), don't > > > > we early return from there and avoid doing that > > > > "tick_program_event(KTIME_MAX, 1);" altogether, if the tick was already > > > > stopped and the next event has not changed? > > > > > > > > /* Skip reprogram of event if its not changed */ > > > > if (ts->tick_stopped && (expires == ts->next_tick)) { > > > > /* Sanity check: make sure clockevent is actually programmed */ > > > > if (tick == KTIME_MAX || ts->next_tick == [...] > > > > return; > > > > [...] > > > > } > > > > > > Sure, if tick_program_event(KTIME_MAX, 1) was already called in the > > > previous idle loop iteration, then there is no need to call that again. > > > > > > Or am I missing something else? > > > > Just take it with a grain of salt but I think you need to still call > > tick_program_event(KTIME_MAX, 1) here for the case where the tick was > > previously stopped, and then when the next tick fires (say after a > > long time T), but that tick is a one-off and does not result in > > restarting the tick -- then there is no one to call > > "tick_program_event(KTIME_MAX, 1)". > > I'm a bit confused about that one-off thing. What can trigger that timer > interrupt if it has been stopped? > > One thing can happen though: a pending timer IRQ while we are stopping the > tick (IRQs are disabled in that idle loop portion). But then that pending timer > interrupt is not going to reprogram another one. So it remains stopped. I think I see what you mean now. Maybe I wrongly assumed the above 'Skip reprogram of event' code could early return and skip over "tick_program_event(KTIME_MAX, 1);", but I think it cannot because of the "expires != ts->next_tick" check. Maybe the "tick_program_event(KTIME_MAX, 1)" bit in tick_nohz_handler() is supposed to handle buggy hardware where an unexpected timer event came through? In such a situation, the idle loop will not write "tick_program_event(KTIME_MAX, 1);" again because it already did so the previous time, as you pointed. Adding Vineeth who is also looking into this code. thanks, - Joel
On Mon, Jul 17, 2023 at 05:30:49PM +0000, Joel Fernandes wrote: > I think I see what you mean now. Maybe I wrongly assumed the above 'Skip > reprogram of event' code could early return and skip over > "tick_program_event(KTIME_MAX, 1);", but I think it cannot because of the > "expires != ts->next_tick" check. > > Maybe the "tick_program_event(KTIME_MAX, 1)" bit in tick_nohz_handler() is > supposed to handle buggy hardware where an unexpected timer event came > through? In such a situation, the idle loop will not write > "tick_program_event(KTIME_MAX, 1);" again because it already did so the > previous time, as you pointed. Well at least if the double write was put there intentionally in order to fix buggy hardware, this was neither mentionned nor commented anywhere AFAICT. Thanks. > > Adding Vineeth who is also looking into this code. > > thanks, > > - Joel >
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index dd25da8531f4..101251e103be 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -1392,18 +1392,16 @@ static void tick_lowres_handler(struct clock_event_device *dev) tick_sched_do_timer(ts, now); tick_sched_handle(ts, regs); - if (unlikely(ts->tick_stopped)) { - /* - * The clockevent device is not reprogrammed, so change the - * clock event device to ONESHOT_STOPPED to avoid spurious - * interrupts on devices which might not be truly one shot. - */ - tick_program_event(KTIME_MAX, 1); - return; + /* + * In dynticks mode, tick reprogram is deferred: + * - to the idle task if in dynticks-idle + * - to IRQ exit if in full-dynticks. + */ + if (likely(!ts->tick_stopped)) { + hrtimer_forward(&ts->sched_timer, now, TICK_NSEC); + tick_program_event(hrtimer_get_expires(&ts->sched_timer), 1); } - hrtimer_forward(&ts->sched_timer, now, TICK_NSEC); - tick_program_event(hrtimer_get_expires(&ts->sched_timer), 1); } static inline void tick_nohz_activate(struct tick_sched *ts, int mode) @@ -1508,7 +1506,11 @@ static enum hrtimer_restart tick_highres_handler(struct hrtimer *timer) else ts->next_tick = 0; - /* No need to reprogram if we are in idle or full dynticks mode */ + /* + * In dynticks mode, tick reprogram is deferred: + * - to the idle task if in dynticks-idle + * - to IRQ exit if in full-dynticks. + */ if (unlikely(ts->tick_stopped)) return HRTIMER_NORESTART;