Received: by 2002:a05:6a10:a841:0:0:0:0 with SMTP id d1csp3448190pxy; Mon, 26 Apr 2021 01:50:57 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwPPbq1uYPaqKX0SyWvlwwiGv+vuOhaonyXdcF/jL2/zhJc4cdLwFeTl3V/pvOgbpLI3i+W X-Received: by 2002:a17:90a:b947:: with SMTP id f7mr20706529pjw.166.1619427057442; Mon, 26 Apr 2021 01:50:57 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1619427057; cv=none; d=google.com; s=arc-20160816; b=FAtAc/OJhgMxHedf3JhHWr7EX1U+zPX1xzdtSLZn+SN2wxMF7FDW8xNI2svsMlUSnT PXjrzOduOV877cCRNLrF+N4B0k4hoLJPVI8xxomzejArsL1428G9XR7F8+fvlcDc1DU/ 92vRjSPt83OCB+M7953iVwssM4UM3sAa9JiJ2GDPrsB2G+QQVySw5WYmDTYEV6lydyJP Xtsny5SJTUbxlPRODPIeLrDR5nToUOiUTWWZOJlZH0l/JLwj7EnrZvtku6G0l26yNFEq rzdvNOlyvdp6K/fH60NIwUJbrn1y8weJRO3xq/knAPJ49P2hlyb83AdwSso0UG0O1e6x rFaQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:mime-version:message-id:date:references :in-reply-to:subject:cc:to:dkim-signature:dkim-signature:from; bh=QCyMnv4HLYYaNHDMqqeqff5nYJqhFU92C+HAIPOa9MU=; b=U7v1Lh4v6FSWv5AnpDnTRvXlz3w7ebAjiOAQyQWtr+ciWo5Sn6Ep3vqly0+iH1iYE9 auVBRKZCW96ugVUA6miHhNL7AFp2VI7usLWupIIFJSE58StV0XnfGhoIEX8wgIwtYSNM buMKBLYZ7GU88pwn0YDxB/YUUlWS60hE9hw7vQxXA2rHmQS4yM4H5u3xr6QdQjQa5YkW tivoJLa1zl16tCOfqZDocjkkosTtfPSxbrmPcvVxtJbB2uxZTgDbV/9NGvSYXJKWT6Hp PviDbJ5gf0auT2L6JR9dEuYtzL+dxJGcmPT6Fa7MjBznMKolXEpbwTd7RBTPjW/msyCf 8nwQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@linutronix.de header.s=2020 header.b=w1r76OFa; dkim=neutral (no key) header.i=@linutronix.de header.s=2020e header.b=vILJ6CS8; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=linutronix.de Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id fs12si16309243pjb.17.2021.04.26.01.50.45; Mon, 26 Apr 2021 01:50:57 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=fail header.i=@linutronix.de header.s=2020 header.b=w1r76OFa; dkim=neutral (no key) header.i=@linutronix.de header.s=2020e header.b=vILJ6CS8; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=linutronix.de Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232405AbhDZIuT (ORCPT + 99 others); Mon, 26 Apr 2021 04:50:19 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55740 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232381AbhDZIuS (ORCPT ); Mon, 26 Apr 2021 04:50:18 -0400 Received: from galois.linutronix.de (Galois.linutronix.de [IPv6:2a0a:51c0:0:12e:550::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 38E6BC061574 for ; Mon, 26 Apr 2021 01:49:37 -0700 (PDT) From: Thomas Gleixner DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1619426974; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=QCyMnv4HLYYaNHDMqqeqff5nYJqhFU92C+HAIPOa9MU=; b=w1r76OFa4nfXCB/k/9RunyS2ofOIMQNSJNeN2iF/VRL8W5omNtxOExKE/Sth8FsMnUA75N AX5jCbjFVgBAWlcUriNo3kHtlhogR/kCGsvze8CoEGHg2PC+8NBHaPK9RwE+38vqDwZA4j IoVua+s9H3JH4MunDIM9MvEvZEbD1VrPL4sOTPy6+FsETZatevorGx+Wbrtgb2tTQH89gx u8FRXmQk5YNOGzrBcAJ7/DQVOamigwhM9cvfpIxfV9XkyXbiPRn4vlip7M6MpyWlJvsQpD frS8O8fpXNF6zqhb70Mn31eV2eNk+mTfY+XkbBUl8qO2Kd5NhPtXgv8CkxiYbA== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1619426974; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=QCyMnv4HLYYaNHDMqqeqff5nYJqhFU92C+HAIPOa9MU=; b=vILJ6CS8MhFPJq6IR6Fg5LgrCd9au6YVLpGVNEhJj1sIyKuFhrYU9C9SBtZf5sJO2uHWQ0 YX7U2hzVk3qz6QDg== To: Lorenzo Colitti Cc: Greg KH , Maciej =?utf-8?Q?=C5=BBenczykows?= =?utf-8?Q?ki?= , Ingo Molnar , Anna-Maria Behnsen , lkml , mikael.beckius@windriver.com, Maciej =?utf-8?Q?=C5=BBenczykowski?= , Will Deacon , Peter Zijlstra Subject: [PATCH] hrtimer: Avoid double reprogramming in __hrtimer_start_range_ns() In-Reply-To: <87eef5qbrx.ffs@nanos.tec.linutronix.de> References: <87r1jbv6jc.ffs@nanos.tec.linutronix.de> <87eef5qbrx.ffs@nanos.tec.linutronix.de> Date: Mon, 26 Apr 2021 10:49:33 +0200 Message-ID: <87v989topu.ffs@nanos.tec.linutronix.de> MIME-Version: 1.0 Content-Type: text/plain Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org If __hrtimer_start_range_ns() is invoked with an already armed hrtimer then the timer has to be canceled first and then added back. If the timer is the first expiring timer then on removal the clockevent device is reprogrammed to the next expiring timer to avoid that the pending expiry fires needlessly. If the new expiry time ends up to be the first expiry again then the clock event device has to reprogrammed again. Avoid this by checking whether the timer is the first to expire and in that case, keep the timer on the current CPU and delay the reprogramming up to the point where the timer has been enqueued again. Reported-by: Lorenzo Colitti Signed-off-by: Thomas Gleixner --- kernel/time/hrtimer.c | 60 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 7 deletions(-) --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -1030,12 +1030,13 @@ static void __remove_hrtimer(struct hrti * remove hrtimer, called with base lock held */ static inline int -remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base, bool restart) +remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base, + bool restart, bool keep_local) { u8 state = timer->state; if (state & HRTIMER_STATE_ENQUEUED) { - int reprogram; + bool reprogram; /* * Remove the timer and force reprogramming when high @@ -1048,8 +1049,16 @@ remove_hrtimer(struct hrtimer *timer, st debug_deactivate(timer); reprogram = base->cpu_base == this_cpu_ptr(&hrtimer_bases); + /* + * If the timer is not restarted then reprogramming is + * required if the timer is local. If it is local and about + * to be restarted, avoid programming it twice (on removal + * and a moment later when it's requeued). + */ if (!restart) state = HRTIMER_STATE_INACTIVE; + else + reprogram &= !keep_local; __remove_hrtimer(timer, base, state, reprogram); return 1; @@ -1103,9 +1112,31 @@ static int __hrtimer_start_range_ns(stru struct hrtimer_clock_base *base) { struct hrtimer_clock_base *new_base; + bool force_local, first; - /* Remove an active timer from the queue: */ - remove_hrtimer(timer, base, true); + /* + * If the timer is on the local cpu base and is the first expiring + * timer then this might end up reprogramming the hardware twice + * (on removal and on enqueue). To avoid that by prevent the + * reprogram on removal, keep the timer local to the current CPU + * and enforce reprogramming after it is queued no matter whether + * it is the new first expiring timer again or not. + */ + force_local = base->cpu_base == this_cpu_ptr(&hrtimer_bases); + force_local &= base->cpu_base->next_timer == timer; + + /* + * Remove an active timer from the queue. In case it is not queued + * on the current CPU, make sure that remove_hrtimer() updates the + * remote data correctly. + * + * If it's on the current CPU and the first expiring timer, then + * skip reprogramming, keep the timer local and enforce + * reprogramming later if it was the first expiring timer. This + * avoids programming the underlying clock event twice (once at + * removal and once after enqueue). + */ + remove_hrtimer(timer, base, true, force_local); if (mode & HRTIMER_MODE_REL) tim = ktime_add_safe(tim, base->get_time()); @@ -1115,9 +1146,24 @@ static int __hrtimer_start_range_ns(stru hrtimer_set_expires_range_ns(timer, tim, delta_ns); /* Switch the timer base, if necessary: */ - new_base = switch_hrtimer_base(timer, base, mode & HRTIMER_MODE_PINNED); + if (!force_local) { + new_base = switch_hrtimer_base(timer, base, + mode & HRTIMER_MODE_PINNED); + } else { + new_base = base; + } - return enqueue_hrtimer(timer, new_base, mode); + first = enqueue_hrtimer(timer, new_base, mode); + if (!force_local) + return first; + + /* + * Timer was forced to stay on the current CPU to avoid + * reprogramming on removal and enqueue. Force reprogram the + * hardware by evaluating the new first expiring timer. + */ + hrtimer_force_reprogram(new_base->cpu_base, 1); + return 0; } /** @@ -1183,7 +1229,7 @@ int hrtimer_try_to_cancel(struct hrtimer base = lock_hrtimer_base(timer, &flags); if (!hrtimer_callback_running(timer)) - ret = remove_hrtimer(timer, base, false); + ret = remove_hrtimer(timer, base, false, false); unlock_hrtimer_base(timer, &flags);