From: Babu Moger <babu.moger@amd.com>
To: Tony Luck <tony.luck@intel.com>, Fenghua Yu <fenghuay@nvidia.com>,
Reinette Chatre <reinette.chatre@intel.com>,
Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>,
Peter Newman <peternewman@google.com>,
James Morse <james.morse@arm.com>,
Drew Fustini <dfustini@baylibre.com>,
Dave Martin <Dave.Martin@arm.com>, Chen Yu <yu.c.chen@intel.com>
Cc: Borislav Petkov <bp@alien8.de>,
x86@kernel.org, linux-kernel@vger.kernel.org,
patches@lists.linux.dev
Subject: Re: [PATCH v2 5/5] fs/resctrl: Fix issues with worker threads when CPUs are taken offline
Date: Mon, 18 May 2026 15:55:11 -0500 [thread overview]
Message-ID: <170821c5-0d78-4c7a-a52d-e37a5133356c@amd.com> (raw)
In-Reply-To: <20260515193944.15114-6-tony.luck@intel.com>
Hi Tony,
On 5/15/26 14:39, Tony Luck wrote:
> From: Reinette Chatre <reinette.chatre@intel.com>
>
> Sashiko noticed[1] a user-after-free in the resctrl worker thread code
user-after-free -> use-after-free
> where the rdt_l3_mon_domain structure was freed while the worker was blocked
> waiting for locks.
>
> The root issue is that cancel_delayed_work() does not block in the case where
> the worker thread is executing. This results in the race that Sashiko noticed,
> but also causes problems when the CPU that has been chosen to service the
> worker thread is taken offline.
>
> Note that worker threads are allowed to delete their own work_struct
> (see comment in kernel/workqueue.c:process_one_work()) so there can't be
> any problems on the return path from the worker in this case where the
> work_struct was deleted by other code while the worker was executing.
>
> Indicate failure of cancel_delayed_work() calls in resctrl_offline_cpu()
> by setting d->mbm_work_cpu or d->cqm_work_cpu to nr_cpu_ids. Make the worker
> threads check to see if they are no longer bound to the right CPU. In this
> case search the L3 domain list for any domain(s) with the work cpu set to
> nr_cpu_ids. In the case where the last CPU was removed from a domain, the
> domain has been removed from the list and there is nothing to do. If the
> domain still exists, then restart the worker on any of the remaining CPUs.
>
> Remove redundant cancel_delayed_work() calls from resctrl_offline_mon_domain().
>
> Fixes: 24247aeeabe9 ("x86/intel_rdt/cqm: Improve limbo list processing")
> Co-developed-by: Tony Luck <tony.luck@intel.com>
> Signed-off-by: Tony Luck <tony.luck@intel.com>
This should be
Co-developed-by: Tony Luck <tony.luck@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Thanks
Babu
> Link: https://sashiko.dev/#/patchset/20260429184858.36423-1-tony.luck%40intel.com [1]
> ---
> fs/resctrl/monitor.c | 55 +++++++++++++++++++++++++++++++++++++++++++
> fs/resctrl/rdtgroup.c | 27 +++++++++++++++------
> 2 files changed, 75 insertions(+), 7 deletions(-)
>
> diff --git a/fs/resctrl/monitor.c b/fs/resctrl/monitor.c
> index 9fd901c78dc6..c422850f044b 100644
> --- a/fs/resctrl/monitor.c
> +++ b/fs/resctrl/monitor.c
> @@ -791,12 +791,38 @@ static void mbm_update(struct rdt_resource *r, struct rdt_l3_mon_domain *d,
> */
> void cqm_handle_limbo(struct work_struct *work)
> {
> + struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3);
> unsigned long delay = msecs_to_jiffies(CQM_LIMBOCHECK_INTERVAL);
> struct rdt_l3_mon_domain *d;
>
> cpus_read_lock();
> mutex_lock(&rdtgroup_mutex);
>
> + /*
> + * Worker was blocked waiting for the CPU it was running on to go
> + * offline. Handle two scenarios:
> + * - Worker was running on the last CPU of a domain. The domain and
> + * thus the work_struct has been freed so do not attempt to obtain
> + * domain via container_of(). All remaining domains have limbo
> + * handlers so the loop will not find any domains needing a
> + * limbo handler. Just exit.
> + * - Worker was running on CPU that just went offline with other
> + * CPUs in domain still running and available to take over the
> + * worker. Offline handler could not schedule a new worker on
> + * another CPU in the domain but signaled that this needs to be
> + * done by setting cqm_work_cpu to nr_cpu_ids. Find the domain
> + * that needs a worker and schedule it after the normal CQM
> + * interval.
> + */
> + if (!is_percpu_thread()) {
> + list_for_each_entry(d, &r->mon_domains, hdr.list) {
> + if (d->cqm_work_cpu == nr_cpu_ids)
> + cqm_setup_limbo_handler(d, CQM_LIMBOCHECK_INTERVAL,
> + RESCTRL_PICK_ANY_CPU);
> + }
> + goto out_unlock;
> + }
> +
> d = container_of(work, struct rdt_l3_mon_domain, cqm_limbo.work);
>
> __check_limbo(d, false);
> @@ -808,6 +834,7 @@ void cqm_handle_limbo(struct work_struct *work)
> delay);
> }
>
> +out_unlock:
> mutex_unlock(&rdtgroup_mutex);
> cpus_read_unlock();
> }
> @@ -852,6 +879,34 @@ void mbm_handle_overflow(struct work_struct *work)
> goto out_unlock;
>
> r = resctrl_arch_get_resource(RDT_RESOURCE_L3);
> +
> + /*
> + * Worker was blocked waiting for the CPU it was running on to go
> + * offline. Handle two scenarios:
> + * - Worker was running on the last CPU of a domain. The domain and
> + * thus the work_struct has been freed so do not attempt to obtain
> + * domain via container_of(). All remaining domains have overflow
> + * handlers so the loop will not find any domains needing an
> + * overflow handler. Just exit.
> + * - Worker was running on CPU that just went offline with other
> + * CPUs in domain still running and available to take over the
> + * worker. Offline handler could not schedule a new worker on
> + * another CPU in the domain but signaled that this needs to be
> + * done by setting mbm_work_cpu to nr_cpu_ids. Find the domain
> + * that needs a worker and schedule it to run after the normal
> + * MBM interval. This is completely safe on CPUs with wide MBM
> + * counters. Likely OK for old CPUs with narrow counters as the
> + * MBM_OVERFLOW_INTERVAL was picked conservatively.
> + */
> + if (!is_percpu_thread()) {
> + list_for_each_entry(d, &r->mon_domains, hdr.list) {
> + if (d->mbm_work_cpu == nr_cpu_ids)
> + mbm_setup_overflow_handler(d, MBM_OVERFLOW_INTERVAL,
> + RESCTRL_PICK_ANY_CPU);
> + }
> + goto out_unlock;
> + }
> +
> d = container_of(work, struct rdt_l3_mon_domain, mbm_over.work);
>
> list_for_each_entry(prgrp, &rdt_all_groups, rdtgroup_list) {
> diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c
> index 282a0acedea8..fd82fc78b058 100644
> --- a/fs/resctrl/rdtgroup.c
> +++ b/fs/resctrl/rdtgroup.c
> @@ -4376,8 +4376,7 @@ void resctrl_offline_mon_domain(struct rdt_resource *r, struct rdt_domain_hdr *h
> goto out_unlock;
>
> d = container_of(hdr, struct rdt_l3_mon_domain, hdr);
> - if (resctrl_is_mbm_enabled())
> - cancel_delayed_work(&d->mbm_over);
> +
> if (resctrl_is_mon_event_enabled(QOS_L3_OCCUP_EVENT_ID) && has_busy_rmid(d)) {
> /*
> * When a package is going down, forcefully
> @@ -4388,7 +4387,6 @@ void resctrl_offline_mon_domain(struct rdt_resource *r, struct rdt_domain_hdr *h
> * package never comes back.
> */
> __check_limbo(d, true);
> - cancel_delayed_work(&d->cqm_limbo);
> }
>
> domain_destroy_l3_mon_state(d);
> @@ -4569,13 +4567,28 @@ void resctrl_offline_cpu(unsigned int cpu)
> d = get_mon_domain_from_cpu(cpu, l3);
> if (d) {
> if (resctrl_is_mbm_enabled() && cpu == d->mbm_work_cpu) {
> - cancel_delayed_work(&d->mbm_over);
> - mbm_setup_overflow_handler(d, 0, cpu);
> + if (cancel_delayed_work(&d->mbm_over)) {
> + mbm_setup_overflow_handler(d, 0, cpu);
> + } else {
> + /*
> + * Unable to schedule work on new CPU if it
> + * is currently running since the re-schedule
> + * will just force new work to run on
> + * current CPU. Mark domain's worker as
> + * needing to be rescheduled to be handled
> + * by worker itself.
> + */
> + d->mbm_work_cpu = nr_cpu_ids;
> + }
> }
> if (resctrl_is_mon_event_enabled(QOS_L3_OCCUP_EVENT_ID) &&
> cpu == d->cqm_work_cpu && has_busy_rmid(d)) {
> - cancel_delayed_work(&d->cqm_limbo);
> - cqm_setup_limbo_handler(d, 0, cpu);
> + if (cancel_delayed_work(&d->cqm_limbo)) {
> + cqm_setup_limbo_handler(d, 0, cpu);
> + } else {
> + /* Same as mbm_work_cpu case above */
> + d->cqm_work_cpu = nr_cpu_ids;
> + }
> }
> }
>
prev parent reply other threads:[~2026-05-18 20:55 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-15 19:39 [PATCH v2 0/5] fs/resctrl: Fix four long-standing issues Tony Luck
2026-05-15 19:39 ` [PATCH v2 1/5] fs/resctrl: Move functions to avoid forward references in subsequent fixes Tony Luck
2026-05-18 20:54 ` Babu Moger
2026-05-15 19:39 ` [PATCH v2 2/5] fs/resctrl: Free mon_data structures on rdt_get_tree() failure Tony Luck
2026-05-18 1:17 ` Chen, Yu C
2026-05-18 20:54 ` Babu Moger
2026-05-15 19:39 ` [PATCH v2 3/5] fs/resctrl: Fix use-after-free during unmount Tony Luck
2026-05-18 20:54 ` Babu Moger
2026-05-15 19:39 ` [PATCH v2 4/5] fs/resctrl: Fix deadlock for errors during mount Tony Luck
2026-05-18 20:54 ` Babu Moger
2026-05-19 4:03 ` Reinette Chatre
2026-05-15 19:39 ` [PATCH v2 5/5] fs/resctrl: Fix issues with worker threads when CPUs are taken offline Tony Luck
2026-05-18 20:55 ` Babu Moger [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=170821c5-0d78-4c7a-a52d-e37a5133356c@amd.com \
--to=babu.moger@amd.com \
--cc=Dave.Martin@arm.com \
--cc=bp@alien8.de \
--cc=dfustini@baylibre.com \
--cc=fenghuay@nvidia.com \
--cc=james.morse@arm.com \
--cc=linux-kernel@vger.kernel.org \
--cc=maciej.wieczor-retman@intel.com \
--cc=patches@lists.linux.dev \
--cc=peternewman@google.com \
--cc=reinette.chatre@intel.com \
--cc=tony.luck@intel.com \
--cc=x86@kernel.org \
--cc=yu.c.chen@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®