From nobody Wed Jun 17 06:18:59 2026 Received: from mx0a-00069f02.pphosted.com (mx0a-00069f02.pphosted.com [205.220.165.32]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C0C943CB2D0 for ; Thu, 23 Apr 2026 09:19:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.165.32 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776935980; cv=none; b=iWJujUeoJEfo6ZwrRmAFkdScy3RI8DZZxYj2VcATBBl6JHUAtDhCPurt1U3H4msDC9KyetF77r6q3wT/Llun1Bw2FNfdifsxGqgtoQBMGjAjx8XtYcjJQ/A1/xvZeHTrvPZVapfH14UxYtZOddUceMlnYcwgKgfjMoM0Qfh656c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776935980; c=relaxed/simple; bh=bXx/s+umxZsk+ewlgTrIW894cqEs0ID/zwh0vsI6ckI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EDjlMblNP4bXbmUXM0HQqXuZCYLgBRlrh7sYa3/GM3IN/AF0G/CJTymxxl2Jjf5R8eMNgOK9HC1QAxHjKF8+mJZTsZtWz9hiRbSAEGldQm7wRqionT8isSZhTJuY8i7QJyUKzjcGVATVLqNmx6Jd6eFbUUaW2bB3wisikl4YLek= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oracle.com; spf=pass smtp.mailfrom=oracle.com; dkim=pass (2048-bit key) header.d=oracle.com header.i=@oracle.com header.b=Xkejb657; arc=none smtp.client-ip=205.220.165.32 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oracle.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oracle.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=oracle.com header.i=@oracle.com header.b="Xkejb657" Received: from pps.filterd (m0333521.ppops.net [127.0.0.1]) by mx0b-00069f02.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 63N7LvCu3738297; Thu, 23 Apr 2026 09:19:18 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=cc :content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=corp-2025-04-25; bh=AfyOm lEm+ai5HstyMoxlQeAUMnugiSmpzZNJ/mmLl54=; b=Xkejb6570Lu/sL/7Swl+e GLlmrdw47QPwlTm23WXTea28besCGfjBb9NVducQYL7gDb20+0yhCvCeB/1XCmqk i1ZnDW59mfZjwSxYlhFdVPLfmRQXPRqc+4imbC87+dJubtYh7VEOGJTCE7NyLyeP 66xF1uOpOyxb95DXymXMKqdYqm/16427FzInd4LJdSAZW3YFKn8DFZEYbeulpHSR eN33QjFoMZBMnn71O+lvMbaLz5OZyUSau5jm1IzGkLdtEuKgeJHa8j299wX6EX86 lCaspy7WZ23pYK8M3EwRVxs/X8yDZ8MQAH2yYWVQdl/1IfTQgBQu3ueIoq5+VQFf w== Received: from iadpaimrmta01.imrmtpd1.prodappiadaev1.oraclevcn.com (iadpaimrmta01.appoci.oracle.com [130.35.100.223]) by mx0b-00069f02.pphosted.com (PPS) with ESMTPS id 4dpenjb4d1-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Thu, 23 Apr 2026 09:19:17 +0000 (GMT) Received: from pps.filterd (iadpaimrmta01.imrmtpd1.prodappiadaev1.oraclevcn.com [127.0.0.1]) by iadpaimrmta01.imrmtpd1.prodappiadaev1.oraclevcn.com (8.18.1.7/8.18.1.7) with ESMTP id 63N9GNIS012949; Thu, 23 Apr 2026 09:19:16 GMT Received: from pps.reinject (localhost [127.0.0.1]) by iadpaimrmta01.imrmtpd1.prodappiadaev1.oraclevcn.com (PPS) with ESMTPS id 4dpjjrd0p9-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Thu, 23 Apr 2026 09:19:16 +0000 (GMT) Received: from iadpaimrmta01.imrmtpd1.prodappiadaev1.oraclevcn.com (iadpaimrmta01.imrmtpd1.prodappiadaev1.oraclevcn.com [127.0.0.1]) by pps.reinject (8.18.1.12/8.18.1.12) with ESMTP id 63N9JF01021237; Thu, 23 Apr 2026 09:19:16 GMT Received: from pssatapa-ol8-test.osdevelopmeniad.oraclevcn.com (pssatapa-ol8-test.allregionaliads.osdevelopmeniad.oraclevcn.com [100.100.253.175]) by iadpaimrmta01.imrmtpd1.prodappiadaev1.oraclevcn.com (PPS) with ESMTP id 4dpjjrd0n7-2; Thu, 23 Apr 2026 09:19:15 +0000 (GMT) From: Partha Satapathy To: partha.satapathy@oracle.com, anna-maria@linutronix.de, frederic@kernel.org, tglx@kernel.org, linux-kernel@vger.kernel.org, tj@kernel.org, jiangshanlai@gmail.com Cc: notify@kernel.org Subject: [PATCH 1/2] timer: add add_timer_active_cpu() Date: Thu, 23 Apr 2026 09:19:06 +0000 Message-ID: <20260423091914.63645-2-partha.satapathy@oracle.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260423091914.63645-1-partha.satapathy@oracle.com> References: <20260423091914.63645-1-partha.satapathy@oracle.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.51,FMLib:17.12.100.49 definitions=2026-04-23_02,2026-04-21_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 bulkscore=0 spamscore=0 suspectscore=0 lowpriorityscore=0 adultscore=0 malwarescore=0 mlxscore=0 mlxlogscore=999 phishscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2604200000 definitions=main-2604230090 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNDIzMDA5MCBTYWx0ZWRfX/ds74WRiKT8+ t2ms9jXzC7sQt1JISn+Gkn5xpmgX70VH2FVvY7Z+HzD+dCLScT/kPATxsbzYMEq/Xy/z2QNRwhB SkPNLsrpnJY+6Vzrgh98bZoLIrHo7MaE8rP3qDkM5OMzYhjuND0unZSN/4X99wtxIaaJOF4/Xef miiQumm5Qy7qWNPusxbgnbUIlgCCNMn9TURsCkKR9cAM4aTbJcEn8riT79G/fbWZPNFMCSbrqA0 f31tHdIMnCnFo4eaeTR4E+UEgOilOeLqwRSbDZBXzcfts16k6c2sDTnFg2SwF/9qPHEx3l2+f8d Pz/E0IrfWjl/uA4vJ6AeapSrFr5M3oulnHuaRS9aLtSdeTk4P6N0vRIBH0UEhrPY1uGjRSAnC5c zuQjWSLBJIREIYCTs69V6l6XVwfl/c9n+5Zvtvo2L1kHdUdkB0lXPBcqll9YNHyF+wsDGcaoX/S aKopx1RDQX2/0EZWnfGE72qK10pgRmja3bnQoDWw= X-Authority-Analysis: v=2.4 cv=Xay5Co55 c=1 sm=1 tr=0 ts=69e9e415 b=1 cx=c_pps a=zPCbziy225d3KhSqZt3L1A==:117 a=zPCbziy225d3KhSqZt3L1A==:17 a=A5OVakUREuEA:10 a=VkNPw1HP01LnGYTKEx00:22 a=jiCTI4zE5U7BLdzWsZGv:22 a=x0eKOSpe3m1H3M0S9YoZ:22 a=yPCof4ZbAAAA:8 a=k9ZU9dsI0lKJxH7jJukA:9 cc=ntf awl=host:12291 X-Proofpoint-ORIG-GUID: _A2zpnJgGc6v80tEIoOgf5pIPengFvhq X-Proofpoint-GUID: _A2zpnJgGc6v80tEIoOgf5pIPengFvhq Content-Type: text/plain; charset="utf-8" From: Partha Sarathi Satapathy add_timer_on() can queue a timer on a CPU that goes offline before the timer is enqueued. When that happens, the timer remains unserviced until the CPU comes back online. Callers can try to avoid that by checking CPU state themselves, but that does not close the race with CPU hotplug. Taking the hotplug lock around every enqueue is also too expensive for users that only want CPU-local placement as a performance hint. Add add_timer_active_cpu() for callers that want to queue a timer on a specific CPU when that CPU is active, but otherwise fall back to an active CPU. Implement this by teaching the enqueue path to verify that the target timer base is active and, if not, requeue the timer on the current CPU. Leave add_timer_on() semantics unchanged for callers that require strict CPU placement. Signed-off-by: Partha Sarathi Satapathy --- include/linux/timer.h | 1 + kernel/time/timer.c | 45 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/include/linux/timer.h b/include/linux/timer.h index 62e1cea71125..8c771b367662 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -148,6 +148,7 @@ static inline int timer_pending(const struct timer_list= * timer) } =20 extern void add_timer_on(struct timer_list *timer, int cpu); +extern void add_timer_active_cpu(struct timer_list *timer, int cpu); extern int mod_timer(struct timer_list *timer, unsigned long expires); extern int mod_timer_pending(struct timer_list *timer, unsigned long expir= es); extern int timer_reduce(struct timer_list *timer, unsigned long expires); diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 1f2364126894..c73a28701a31 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -260,6 +260,7 @@ struct timer_base { bool next_expiry_recalc; bool is_idle; bool timers_pending; + bool is_active; DECLARE_BITMAP(pending_map, WHEEL_SIZE); struct hlist_head vectors[WHEEL_SIZE]; } ____cacheline_aligned; @@ -1296,7 +1297,7 @@ EXPORT_SYMBOL(add_timer_global); * * See add_timer() for further details. */ -void add_timer_on(struct timer_list *timer, int cpu) +static void __add_timer_on(struct timer_list *timer, int cpu, bool need_ol= _cpu) { struct timer_base *new_base, *base; unsigned long flags; @@ -1333,6 +1334,18 @@ void add_timer_on(struct timer_list *timer, int cpu) WRITE_ONCE(timer->flags, (timer->flags & ~TIMER_BASEMASK) | cpu); } +#ifdef CONFIG_HOTPLUG_CPU + if (need_ol_cpu) { + if (!base->is_active) { + raw_spin_unlock(&base->lock); + base =3D this_cpu_ptr(&timer_bases[BASE_LOCAL]); + raw_spin_lock(&base->lock); + cpu =3D smp_processor_id(); + WRITE_ONCE(timer->flags, + (timer->flags & ~TIMER_BASEMASK) | cpu); + } + } +#endif /* CONFIG_HOTPLUG_CPU */ forward_timer_base(base); =20 debug_timer_activate(timer); @@ -1340,8 +1353,31 @@ void add_timer_on(struct timer_list *timer, int cpu) out_unlock: raw_spin_unlock_irqrestore(&base->lock, flags); } + +void add_timer_on(struct timer_list *timer, int cpu) +{ + bool need_ol_cpu =3D false; + + __add_timer_on(timer, cpu, need_ol_cpu); +} EXPORT_SYMBOL_GPL(add_timer_on); =20 +/** + * add_timer_active_cpu - Start a timer on a particular CPU if online or c= urrent + * @timer: The timer to be started + * @cpu: The CPU to start it on + * + * This is like add_timer_on(), except that it queues the timer on the + * given CPU only when that CPU is online or on the current CPU. + */ +void add_timer_active_cpu(struct timer_list *timer, int cpu) +{ + bool need_ol_cpu =3D true; + + __add_timer_on(timer, cpu, need_ol_cpu); +} +EXPORT_SYMBOL_GPL(add_timer_active_cpu); + /** * __timer_delete - Internal function: Deactivate a timer * @timer: The timer to be deactivated @@ -2507,6 +2543,7 @@ int timers_prepare_cpu(unsigned int cpu) base->next_expiry_recalc =3D false; base->timers_pending =3D false; base->is_idle =3D false; + base->is_active =3D true; } return 0; } @@ -2535,6 +2572,11 @@ int timers_dead_cpu(unsigned int cpu) =20 WARN_ON_ONCE(old_base->running_timer); old_base->running_timer =3D NULL; + /* + * Make the dead CPU base unavailable to add_timer_on() + * when the caller wants an active target CPU. + */ + old_base->is_active =3D false; =20 for (i =3D 0; i < WHEEL_SIZE; i++) migrate_timer_list(new_base, old_base->vectors + i); @@ -2559,6 +2601,7 @@ static void __init init_timer_cpu(int cpu) raw_spin_lock_init(&base->lock); base->clk =3D jiffies; base->next_expiry =3D base->clk + TIMER_NEXT_MAX_DELTA; + base->is_active =3D true; timer_base_init_expiry_lock(base); } } --=20 2.43.7 From nobody Wed Jun 17 06:18:59 2026 Received: from mx0a-00069f02.pphosted.com (mx0a-00069f02.pphosted.com [205.220.165.32]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C16783E3D9D for ; Thu, 23 Apr 2026 09:19:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.165.32 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776935977; cv=none; b=kYRVTgAFOmSdRFGFZfrHTEFbtZDd8oGndMAfzY4TXpMQfHiPL+AXYE09vgPJeqZyfesaSPyedzjVeyyodkFEpUjMdeKQman0WSac1pW+L2IYtBko0NzQ8FT9qPLJ65srS/9QCr/a/qeu67TAg3p0OKnxOTXxdBhZaJ0QQS7IX7g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776935977; c=relaxed/simple; bh=wXZsdZqqf2KkuxK81fZWaCDj76g36XMwVLfHgmtUVJw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rc63Mo/wDTSB+YP1kRlfRfUUr3gO8y6t+G0PGc58iYQ5AgjJcf+TT+P/S9PcKCJ48gtpvdtS4OyN4VfEmf/G2fWNhUOhifMxTokK9kVIpLo5G5gsm/Q8K+oHyAbNgn3htVmlRVxv69K/zrFgCwz1LAAuhqEbjU4W2jYCvrAFuOY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oracle.com; spf=pass smtp.mailfrom=oracle.com; dkim=pass (2048-bit key) header.d=oracle.com header.i=@oracle.com header.b=YsAqUWSM; arc=none smtp.client-ip=205.220.165.32 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oracle.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oracle.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=oracle.com header.i=@oracle.com header.b="YsAqUWSM" Received: from pps.filterd (m0246617.ppops.net [127.0.0.1]) by mx0b-00069f02.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 63N5HrI52336472; Thu, 23 Apr 2026 09:19:18 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=cc :content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=corp-2025-04-25; bh=0UzzB WjERvLog2B9W77GjKBma10PfCYQ4QqnHhjFrFw=; b=YsAqUWSM5V5ZWoWc6uxpa vJkJ6W+4VbmITqP2zeuL0iPbgDX475YHbVax4oVLKRefXC1HCFeipekLSgDCF4+l jz0/tpBjkPv5ROGs6EDl7Ru3CAIsQAK5QXOpZLUfhdq0hTZBIGS/iLaZqDJTQEp4 e80GYhQHLNJvYLkIdCO6up5nBDRaaMXovvEwmpxJgPnDYQpYzr5ukzpBWzenDrWp qiIoWKyNhs9aL+EVMFE6VT/TGkpeNgQYEEd07MTu52Df31UOfOaAZavRqAmUQwE2 K4mV3BBevcRXo+wkjzhBtjLZgZLk839lEYrMGNk51QS/qVfzNPDJE88BeSDG+ND6 A== Received: from iadpaimrmta01.imrmtpd1.prodappiadaev1.oraclevcn.com (iadpaimrmta01.appoci.oracle.com [130.35.100.223]) by mx0b-00069f02.pphosted.com (PPS) with ESMTPS id 4dpenmu5a7-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Thu, 23 Apr 2026 09:19:18 +0000 (GMT) Received: from pps.filterd (iadpaimrmta01.imrmtpd1.prodappiadaev1.oraclevcn.com [127.0.0.1]) by iadpaimrmta01.imrmtpd1.prodappiadaev1.oraclevcn.com (8.18.1.7/8.18.1.7) with ESMTP id 63N9GNIb012969; Thu, 23 Apr 2026 09:19:17 GMT Received: from pps.reinject (localhost [127.0.0.1]) by iadpaimrmta01.imrmtpd1.prodappiadaev1.oraclevcn.com (PPS) with ESMTPS id 4dpjjrd0pn-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Thu, 23 Apr 2026 09:19:17 +0000 (GMT) Received: from iadpaimrmta01.imrmtpd1.prodappiadaev1.oraclevcn.com (iadpaimrmta01.imrmtpd1.prodappiadaev1.oraclevcn.com [127.0.0.1]) by pps.reinject (8.18.1.12/8.18.1.12) with ESMTP id 63N9JF03021237; Thu, 23 Apr 2026 09:19:16 GMT Received: from pssatapa-ol8-test.osdevelopmeniad.oraclevcn.com (pssatapa-ol8-test.allregionaliads.osdevelopmeniad.oraclevcn.com [100.100.253.175]) by iadpaimrmta01.imrmtpd1.prodappiadaev1.oraclevcn.com (PPS) with ESMTP id 4dpjjrd0n7-3; Thu, 23 Apr 2026 09:19:16 +0000 (GMT) From: Partha Satapathy To: partha.satapathy@oracle.com, anna-maria@linutronix.de, frederic@kernel.org, tglx@kernel.org, linux-kernel@vger.kernel.org, tj@kernel.org, jiangshanlai@gmail.com Cc: notify@kernel.org Subject: [PATCH 2/2] workqueue: add queue_delayed_work_active_cpu() Date: Thu, 23 Apr 2026 09:19:07 +0000 Message-ID: <20260423091914.63645-3-partha.satapathy@oracle.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260423091914.63645-1-partha.satapathy@oracle.com> References: <20260423091914.63645-1-partha.satapathy@oracle.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.51,FMLib:17.12.100.49 definitions=2026-04-23_02,2026-04-21_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 bulkscore=0 spamscore=0 suspectscore=0 lowpriorityscore=0 adultscore=0 malwarescore=0 mlxscore=0 mlxlogscore=999 phishscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2604200000 definitions=main-2604230090 X-Proofpoint-GUID: ES9JA4dA254qlUS8DNgU6GPRFB92RRE4 X-Authority-Analysis: v=2.4 cv=Z6/c2nRA c=1 sm=1 tr=0 ts=69e9e416 b=1 cx=c_pps a=zPCbziy225d3KhSqZt3L1A==:117 a=zPCbziy225d3KhSqZt3L1A==:17 a=A5OVakUREuEA:10 a=VkNPw1HP01LnGYTKEx00:22 a=jiCTI4zE5U7BLdzWsZGv:22 a=7Gl3-_t3PgB9XO-mQDs3:22 a=yPCof4ZbAAAA:8 a=uWG3VGRt3ocMjy4b0FoA:9 cc=ntf awl=host:12291 X-Proofpoint-ORIG-GUID: ES9JA4dA254qlUS8DNgU6GPRFB92RRE4 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNDIzMDA5MCBTYWx0ZWRfXyU5tAYFrhs5Z nl/Jxvgngu/I8eCdehZ0Z2ocVckTNxFtgTLd1YOOX7hYY2arkf5zjk/0+I2BKnoyjaJB/hIBBvE ewsqeRTvp+YNiAO5Euy6NC00ZbgS6MFsmEjXoS2HF++qgJI51pRZYJKjGy9tur+act7tSfK7ZQF 2i5Jszgd3SurZIMFzB/wZG9Fb6usDXLoXlPXy27nJx6AIkaHYyDhnEsSJIXR8lI6z+kPDnTMZ+Y 36pmKk5LjktPhLJmt9AP1uypXApDavEonNv2o+8vhtrFITkRu/dwBQQg9LX7nJLI3k+9YmXJBi0 XIxH3RyjpgvYpep1fR5ZSo4V9BN4CU+e8y3Z2Y/0FxUVIIshEN32LsVAbcSL4bba8zfD+WdUoso RuCcw7kER+HKUAOcgZ2hwyjuPG/karN91oi/0uqgXXYigejsWsVHeaRcejUNY/sQ70SyzwhAdDP /pHdwXa4NvYFOyKfFG7QEy95mWiVo66BIExRquoU= Content-Type: text/plain; charset="utf-8" From: Partha Sarathi Satapathy Delayed work queued with queue_delayed_work_on() inherits the same CPU hotplug race as add_timer_on(): the backing timer can be enqueued on a CPU that goes offline before the queueing completes, leaving the timer unserviced until that CPU comes back online. As a result, the delayed work item is never queued for execution. Add queue_delayed_work_active_cpu() for callers that want delayed work to target a specific CPU when that CPU is active, but fall back to an active CPU otherwise. For the delayed timer path, use add_timer_active_cpu() instead of add_timer_on(). After enqueueing the timer, update dwork->cpu to match the CPU recorded in the timer so that the work item is queued on the CPU actually selected for the timer. Leave queue_delayed_work_on() unchanged for callers that require the existing strict behavior. Signed-off-by: Partha Sarathi Satapathy --- include/linux/workqueue.h | 3 ++ kernel/workqueue.c | 96 +++++++++++++++++++++++++++++++++------ 2 files changed, 86 insertions(+), 13 deletions(-) diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index dabc351cc127..9799e44bca2b 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -596,6 +596,9 @@ extern bool queue_work_node(int node, struct workqueue_= struct *wq, struct work_struct *work); extern bool queue_delayed_work_on(int cpu, struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay); +extern bool queue_delayed_work_active_cpu(int cpu, struct workqueue_struct= *wq, + struct delayed_work *work, + unsigned long delay); extern bool mod_delayed_work_on(int cpu, struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay); extern bool queue_rcu_work(struct workqueue_struct *wq, struct rcu_work *r= work); diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 253311af47c6..80f5d162624d 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -2483,7 +2483,8 @@ void delayed_work_timer_fn(struct timer_list *t) EXPORT_SYMBOL(delayed_work_timer_fn); =20 static void __queue_delayed_work(int cpu, struct workqueue_struct *wq, - struct delayed_work *dwork, unsigned long delay) + struct delayed_work *dwork, unsigned long delay, + bool dwork_active_cpu) { struct timer_list *timer =3D &dwork->timer; struct work_struct *work =3D &dwork->work; @@ -2504,7 +2505,8 @@ static void __queue_delayed_work(int cpu, struct work= queue_struct *wq, return; } =20 - WARN_ON_ONCE(cpu !=3D WORK_CPU_UNBOUND && !cpu_online(cpu)); + WARN_ON_ONCE(!dwork_active_cpu && cpu !=3D WORK_CPU_UNBOUND && + !cpu_online(cpu)); dwork->wq =3D wq; dwork->cpu =3D cpu; timer->expires =3D jiffies + delay; @@ -2516,10 +2518,17 @@ static void __queue_delayed_work(int cpu, struct wo= rkqueue_struct *wq, cpu =3D housekeeping_any_cpu(HK_TYPE_TIMER); add_timer_on(timer, cpu); } else { - if (likely(cpu =3D=3D WORK_CPU_UNBOUND)) + if (likely(cpu =3D=3D WORK_CPU_UNBOUND)) { add_timer_global(timer); - else + } else if (dwork_active_cpu) { + add_timer_active_cpu(timer, cpu); + /* The target CPU may change if it is offline. */ + /* Avoid selecting the same offline CPU. */ + dwork->cpu =3D READ_ONCE(timer->flags) & + TIMER_CPUMASK; + } else { add_timer_on(timer, cpu); + } } } =20 @@ -2530,18 +2539,23 @@ static void __queue_delayed_work(int cpu, struct wo= rkqueue_struct *wq, * @dwork: work to queue * @delay: number of jiffies to wait before queueing * - * We queue the delayed_work to a specific CPU, for non-zero delays the - * caller must ensure it is online and can't go away. Callers that fail - * to ensure this, may get @dwork->timer queued to an offlined CPU and - * this will prevent queueing of @dwork->work unless the offlined CPU - * becomes online again. + * We queue the delayed_work to a specific CPU, for non-zero delays and + * dwork_active_cpu is not set, caller must ensure it is online and can't + * go away. Callers that fail to ensure this, may get @dwork->timer + * queued to an offlined CPU and this will prevent queueing of + * @dwork->work unless the offlined CPU becomes online again. + * + * If dwork_active_cpu is set, timer queued to an offlined CPU will be + * queued to the current cpu. * * Return: %false if @work was already on a queue, %true otherwise. If * @delay is zero and @dwork is idle, it will be scheduled for immediate * execution. */ -bool queue_delayed_work_on(int cpu, struct workqueue_struct *wq, - struct delayed_work *dwork, unsigned long delay) +static inline bool +__queue_delayed_work_on(int cpu, struct workqueue_struct *wq, + struct delayed_work *dwork, unsigned long delay, + bool dwork_active_cpu) { struct work_struct *work =3D &dwork->work; bool ret =3D false; @@ -2552,15 +2566,70 @@ bool queue_delayed_work_on(int cpu, struct workqueu= e_struct *wq, =20 if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work)) && !clear_pending_if_disabled(work)) { - __queue_delayed_work(cpu, wq, dwork, delay); + __queue_delayed_work(cpu, wq, dwork, delay, dwork_active_cpu); ret =3D true; } =20 local_irq_restore(irq_flags); return ret; } + +/** + * queue_delayed_work_on - queue work on specific CPU after delay + * @cpu: CPU number to execute work on + * @wq: workqueue to use + * @dwork: work to queue + * @delay: number of jiffies to wait before queueing + * + * We queue the delayed_work to a specific CPU, for non-zero delays the + * caller must ensure it is online and can't go away. Callers that fail + * to ensure this, may get @dwork->timer queued to an offlined CPU and + * this will prevent queueing of @dwork->work unless the offlined CPU + * becomes online again. + * + * Return: %false if @work was already on a queue, %true otherwise. If + * @delay is zero and @dwork is idle, it will be scheduled for immediate + * execution. + */ +bool queue_delayed_work_on(int cpu, struct workqueue_struct *wq, + struct delayed_work *dwork, unsigned long delay) +{ + bool ret =3D false; + bool dwork_active_cpu =3D false; + + ret =3D __queue_delayed_work_on(cpu, wq, dwork, delay, dwork_active_cpu); + return ret; +} EXPORT_SYMBOL(queue_delayed_work_on); =20 +/** + * queue_delayed_work_active_cpu - queue delayed work for an active CPU + * @cpu: CPU number to execute work on + * @wq: workqueue to use + * @dwork: work to queue + * @delay: number of jiffies to wait before queueing + * + * This is like queue_delayed_work_on(), except that for delayed work + * the timer is queued on @cpu only if that CPU is online or is the + * current CPU. + * + * Return: %false if @work was already on a queue, %true otherwise. If + * @delay is zero and @dwork is idle, it will be scheduled for immediate + * execution. + * + */ +bool queue_delayed_work_active_cpu(int cpu, struct workqueue_struct *wq, + struct delayed_work *dwork, + unsigned long delay) +{ + bool ret =3D false; + bool dwork_active_cpu =3D true; + + ret =3D __queue_delayed_work_on(cpu, wq, dwork, delay, dwork_active_cpu); + return ret; +} +EXPORT_SYMBOL(queue_delayed_work_active_cpu); + /** * mod_delayed_work_on - modify delay of or queue a delayed work on specif= ic CPU * @cpu: CPU number to execute work on @@ -2584,11 +2653,12 @@ bool mod_delayed_work_on(int cpu, struct workqueue_= struct *wq, { unsigned long irq_flags; bool ret; + bool dwork_active_cpu =3D false; =20 ret =3D work_grab_pending(&dwork->work, WORK_CANCEL_DELAYED, &irq_flags); =20 if (!clear_pending_if_disabled(&dwork->work)) - __queue_delayed_work(cpu, wq, dwork, delay); + __queue_delayed_work(cpu, wq, dwork, delay, dwork_active_cpu); =20 local_irq_restore(irq_flags); return ret; --=20 2.43.7