From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.10]) (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 2FC5B1C84AB for ; Mon, 6 Jul 2026 22:46:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.10 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783378006; cv=none; b=ErjF7uFoJBZCWXu1y2JO0wsroUOJrf36lZQhWYPfJ7md8BcWrnHReXesyEghTGXMmRKCtvQzgud2OWDxyWYJoJZCjYRq9yj6O/qgalmDGt4vuh1BZqqICZdxr8eaCByhbwQFFM3vflJVdu4ERjvkl9vy5K6fHBLBMoFZ7lq9yTg= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783378006; c=relaxed/simple; bh=N06nFP2rKh9Q082zQ1ti39fOIdamPmbQxeOLFJ/PMqg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fSUeXjxpvz3qJCeG07J6t8oB+8z3yFCZ7cDwQMeQwznl7E1DEms50bh0IaLZR6vZEDep4X6QTFlrtnLpI/6GSgBxyZzDz4MBI+D4oDz/99HWTmsh+z+7n+j0n3Y4eGHfv1XHomnzDQSS7g1wDLIZw0f78qrWkT2SCCH1tPxegxQ= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=GCElTJ4H; arc=none smtp.client-ip=192.198.163.10 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="GCElTJ4H" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1783378004; x=1814914004; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=N06nFP2rKh9Q082zQ1ti39fOIdamPmbQxeOLFJ/PMqg=; b=GCElTJ4Hghg+USi+K0nu0CMut9NBXaS0mqsyS08RZDX+HbhdE8SFg93V CCU3nCwib/l4OeeEQc1BO0VhS5TqUc/eYqs1aBMVE6kwmu10LQMAGtqrl X9XWCD87HvmZuYmDhrW2VCRms1RQhB+/vXr8LnpjdbKAjOBCfY0RfjUan mlFtUCWPRJ7nIp3rjiF1JWuRUkph06IcCYPVPE6FeN/Wg1GwZW40gYI6j vyJj5JfTjPS5SBB2nDjs2S3L8TUnlLxz0vI1vFJ8ca09qk6skWkJYtK9s G7DQl5/EWfFA276xn/FssFLurPxnC0HXRPB7nl/c2NO7E7B08fjQocsH7 w==; X-CSE-ConnectionGUID: I4qekHjpQgWtvK62VVg7pg== X-CSE-MsgGUID: PdgpSeAWRCeGZZFKvot/KA== X-IronPort-AV: E=McAfee;i="6800,10657,11839"; a="95401521" X-IronPort-AV: E=Sophos;i="6.25,151,1779174000"; d="scan'208";a="95401521" Received: from fmviesa003.fm.intel.com ([10.60.135.143]) by fmvoesa104.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 06 Jul 2026 15:46:41 -0700 X-CSE-ConnectionGUID: zZOZ/pYYRsiGMZRR4nda2g== X-CSE-MsgGUID: wDB4i4U3SNirtMg/c0n8WA== X-ExtLoop1: 1 Received: from rchatre-desk1.jf.intel.com ([10.165.154.99]) by fmviesa003-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 06 Jul 2026 15:46:40 -0700 From: Reinette Chatre To: tony.luck@intel.com, james.morse@arm.com, Dave.Martin@arm.com, babu.moger@amd.com, bp@alien8.de, tglx@linutronix.de, dave.hansen@linux.intel.com Cc: x86@kernel.org, hpa@zytor.com, ben.horgan@arm.com, fustini@kernel.org, fenghuay@nvidia.com, peternewman@google.com, yu.c.chen@intel.com, linux-kernel@vger.kernel.org, patches@lists.linux.dev, reinette.chatre@intel.com Subject: [PATCH v6 06/10] fs/resctrl: Fix deadlock on errors during mount Date: Mon, 6 Jul 2026 15:46:26 -0700 Message-ID: <64ec3695cd286dfdeaa9043c54ff678fe0386d33.1783377598.git.reinette.chatre@intel.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit rdt_get_tree() acquires rdtgroup_mutex before calling kernfs_get_tree(). If superblock setup fails inside kernfs_get_tree(), the VFS calls .kill_sb() (rdt_kill_sb()) on the same thread before kernfs_get_tree() returns. rdt_kill_sb() unconditionally attempts to acquire rdtgroup_mutex and deadlock occurs. Since mount failure resulting from kernfs_get_tree() already calls the resctrl fs unmount handler (rdt_kill_sb()) let both call the same helper to make it clear both paths perform the same cleanup. Call kernfs_get_tree() outside of locks. If kernfs_get_tree() fails and ctx->kfc.new_sb_created is set, then rdt_kill_sb() has already been called and no further cleanup is needed. kernfs_get_tree() may set ctx->kfc.new_sb_created and then fail to obtain an inode for the new kn, causing the rdt_kill_sb() path to run with one fewer reference than required for the root to remain accessible in kernfs_kill_sb(). Add an extra hold on rdtgroup_default.kn to defend against this scenario and ensure the root can be dereferenced safely from kernfs_kill_sb(). Dropping locks before kernfs_get_tree() creates a window where CPU hotplug callbacks can race with the mount operation. Specifically, an online event observing resctrl_mounted == true could concurrently append directories to the unactivated kernfs tree, allocate mon_data structures, and arm background workers. This concurrency is safe because the mount has not yet returned to the VFS, meaning userspace cannot interact with these transient files. If kernfs_get_tree() subsequently fails, the standard resctrl_unmount() teardown safely manages the concurrent modifications: any dynamically generated kernfs nodes are removed, and the associated memory is freed. Any background workers spawned by the hotplug event will naturally exit without re-arming when they acquire rdtgroup_mutex and observe resctrl_mounted == false. Fixes: 5ff193fbde20 ("x86/intel_rdt: Add basic resctrl filesystem support") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260429184858.36423-1-tony.luck%40intel.com [1] Co-developed-by: Tony Luck Signed-off-by: Tony Luck Signed-off-by: Reinette Chatre Reviewed-by: Ben Horgan Reviewed-by: Chen Yu --- Changes since V2: - Switch to "Reported-by/Closes" in changelog Changes since V3: - Add Ben's Reviewed-by tag. - Rework subject and changelog. - s/root kn/root/ in comment. (Chenyu) - Add Chenyu's Reviewed-by tag. - Changelog grammar fixes. - Add snippet to changelog about potential race with hotplug handlers. --- fs/resctrl/rdtgroup.c | 83 +++++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index 9804e40ad36d..ce964721ba8e 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -2989,10 +2989,34 @@ static void resctrl_fs_teardown(void) rdtgroup_destroy_root(); } +static void resctrl_unmount(void) +{ + struct rdt_resource *r; + + cpus_read_lock(); + mutex_lock(&rdtgroup_mutex); + + rdt_disable_ctx(); + + /* Put everything back to default values. */ + for_each_alloc_capable_rdt_resource(r) + resctrl_arch_reset_all_ctrls(r); + + resctrl_fs_teardown(); + if (resctrl_arch_alloc_capable()) + resctrl_arch_disable_alloc(); + if (resctrl_arch_mon_capable()) + resctrl_arch_disable_mon(); + resctrl_mounted = false; + mutex_unlock(&rdtgroup_mutex); + cpus_read_unlock(); +} + static int rdt_get_tree(struct fs_context *fc) { struct rdt_fs_context *ctx = rdt_fc2context(fc); unsigned long flags = RFTYPE_CTRL_BASE; + struct kernfs_node *rdt_root_kn; struct rdt_l3_mon_domain *dom; struct rdt_resource *r; int ret; @@ -3068,10 +3092,6 @@ static int rdt_get_tree(struct fs_context *fc) if (ret) goto out_mondata; - ret = kernfs_get_tree(fc); - if (ret < 0) - goto out_psl; - if (resctrl_arch_alloc_capable()) resctrl_arch_enable_alloc(); if (resctrl_arch_mon_capable()) @@ -3087,10 +3107,38 @@ static int rdt_get_tree(struct fs_context *fc) RESCTRL_PICK_ANY_CPU); } - goto out; + /* + * Ensure root remains accessible after mutex is unlocked so that + * kernfs_kill_sb() can run safely if called by kernfs_get_tree()'s + * failure path after creating a superblock but before taking reference + * on root kn (for example, if unable to get inode for root kn). + */ + kernfs_get(rdtgroup_default.kn); + + /* + * Make backup of the current root kn being created to be used in + * kernfs_put(). The additional reference taken above will prevent the + * kn from being freed before kernfs_kill_sb() can run but + * rdtgroup_default.kn may be set to NULL via rdtgroup_destroy_root() + * and its backing root (rdt_root) could be overwritten before + * kernfs_put() can run. + */ + rdt_root_kn = rdtgroup_default.kn; + + rdt_last_cmd_clear(); + mutex_unlock(&rdtgroup_mutex); + cpus_read_unlock(); + + ret = kernfs_get_tree(fc); + /* + * resctrl can only be mounted once, new superblock only expected + * to be created once. + */ + if (!ctx->kfc.new_sb_created) + resctrl_unmount(); + kernfs_put(rdt_root_kn); + return ret; -out_psl: - rdt_pseudo_lock_release(); out_mondata: if (resctrl_arch_mon_capable()) kernfs_remove(kn_mondata); @@ -3110,7 +3158,6 @@ static int rdt_get_tree(struct fs_context *fc) out_root: rdtgroup_destroy_root(); out: - rdt_last_cmd_clear(); mutex_unlock(&rdtgroup_mutex); cpus_read_unlock(); return ret; @@ -3197,26 +3244,8 @@ static int rdt_init_fs_context(struct fs_context *fc) static void rdt_kill_sb(struct super_block *sb) { - struct rdt_resource *r; - - cpus_read_lock(); - mutex_lock(&rdtgroup_mutex); - - rdt_disable_ctx(); - - /* Put everything back to default values. */ - for_each_alloc_capable_rdt_resource(r) - resctrl_arch_reset_all_ctrls(r); - - resctrl_fs_teardown(); - if (resctrl_arch_alloc_capable()) - resctrl_arch_disable_alloc(); - if (resctrl_arch_mon_capable()) - resctrl_arch_disable_mon(); - resctrl_mounted = false; + resctrl_unmount(); kernfs_kill_sb(sb); - mutex_unlock(&rdtgroup_mutex); - cpus_read_unlock(); } static struct file_system_type rdt_fs_type = { -- 2.54.0