Received: by 2002:a05:6a11:4021:0:0:0:0 with SMTP id ky33csp29199pxb; Mon, 13 Sep 2021 12:04:35 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyEzFdTMsst/Rjr8PF10Jyl90yBUwDPXkCmAeNgETe9qwS7XbL1b+dj8P+iq9oJQ2ODSK11 X-Received: by 2002:a05:6402:4cd:: with SMTP id n13mr14822643edw.215.1631559875124; Mon, 13 Sep 2021 12:04:35 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1631559875; cv=none; d=google.com; s=arc-20160816; b=f0SySwJjf78tuEK1FPatb1Gplap8lmGm3Icgb/ssaGPdCS+vrK5L3Aekmz7oJBs0Q5 wVkpV4TA/v1DOkHt8mbL6ENv+Oix8QoYOME73ukNDcwtYt+EQVOaIzSu6zn9Y1MNlOxc Ldlpw7eGFMfK2rtWuRvIhVYhPvsnGtoppCGlkj3+0+gboEv6oiBSSKmsZVJTefDO2Llg UtjfRsPHbd7Uv7DqtTiJQEFr+3WYPJFdgwW82deNvmR8ZuGmV/akNbha63D497dqsUpU NTNnVKXkLL/+9ZDWpwAq3XfDB82O1/SmshtzG7UwA6yUXSwUH8bSPk1i0oFOSP0wxARg Zu0g== 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 :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=hEiWWKw4NAamBObgop+3YRcMry4epC0aV5UqGENoHvQ=; b=ern5tvsQzXcXT+5XNJrLT6YBv+QC0vTOnn2wzl7tRQBW+bPVYaeOqE49UAyfpPbfDp fVfl/BVy3XYDNZEE3hI/GwMBimew41E0w73+MnJexXMM/k5ddZQAZVEAs4EDHqqNB+gF r/F8eIEo2+eacPSZV+eBQ1emOYxZ7hC+zsYamrHrCuwfCgiUYQY5gK67a0oNUHOMUrGz hcgiXIEKW8rH/7odHCk4CQRQykEItmEfs4NKp9pfyYKjXsV2mXDwJH+9XJi3GA2NkV1M lA1GyhD0Z3XvKKESjA5fmWXAkKJsWLDdL2q9ApwVuYiqXagH/UjWUR4W72ueWObpSCq0 en8g== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b=qsbWPPpv; 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=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id v18si8129884edc.149.2021.09.13.12.04.10; Mon, 13 Sep 2021 12:04:35 -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=pass header.i=@linuxfoundation.org header.s=korg header.b=qsbWPPpv; 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=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S242852AbhIMNau (ORCPT + 99 others); Mon, 13 Sep 2021 09:30:50 -0400 Received: from mail.kernel.org ([198.145.29.99]:46966 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S242202AbhIMN14 (ORCPT ); Mon, 13 Sep 2021 09:27:56 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id F2B1961268; Mon, 13 Sep 2021 13:23:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1631539403; bh=j2wWzr1eDgTrxra18QxHg/25fZq3ieTvB0wPK+JE32o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=qsbWPPpv+rZIQCIyByynMlI7cQaB9RodHvJ8K7pfPRGYV2qkGfk9WvWwLkm5P+aWI 6C8zG21qZ82yB6QP23B/of7Xes/+Jv0mnxdUkGzL+Tg9tdzqgvh47wNYJgYNksvlmk h++I3te4RJw+2lVnW1Jsk07h3HvmXWqFhzWvIf3I= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Lorenzo Colitti , Thomas Gleixner , Sasha Levin Subject: [PATCH 5.10 012/236] hrtimer: Avoid double reprogramming in __hrtimer_start_range_ns() Date: Mon, 13 Sep 2021 15:11:57 +0200 Message-Id: <20210913131100.749391015@linuxfoundation.org> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20210913131100.316353015@linuxfoundation.org> References: <20210913131100.316353015@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Thomas Gleixner [ Upstream commit 627ef5ae2df8eeccb20d5af0e4cfa4df9e61ed28 ] 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 Link: https://lore.kernel.org/r/20210713135157.873137732@linutronix.de Signed-off-by: Sasha Levin --- kernel/time/hrtimer.c | 60 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 9505b1f21cdf..4bdceb1ff069 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -1030,12 +1030,13 @@ static void __remove_hrtimer(struct hrtimer *timer, * 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, struct hrtimer_clock_base *base, bool rest 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(struct hrtimer *timer, ktime_t tim, 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(struct hrtimer *timer, ktime_t tim, 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; + } + + first = enqueue_hrtimer(timer, new_base, mode); + if (!force_local) + return first; - return enqueue_hrtimer(timer, new_base, mode); + /* + * 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 *timer) 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); -- 2.30.2