ocfs2: cluster: avoid lock order inversion in o2hb_region_pin() from drop_item

o2hb_heartbeat_group_drop_item() is called from configfs rmdir with the
parent directory's inode_lock held.  It calls o2hb_region_pin() ->
o2nm_depend_item() -> configfs_depend_item(), which acquires the configfs
root inode_lock.  This creates a parent -> root inode_lock nesting that
could deadlock against paths taking root -> parent (e.g.  subsystem
unregistration).

Fix this by using configfs_depend_item_unlocked() when o2hb_region_pin()
is called from a configfs callback context.  This variant skips the root
inode_lock when caller and target are in the same subsystem, which is safe
because VFS already holds a lock preventing unregistration.

Add o2nm_depend_item_unlocked() wrapper and a from_callback parameter to
o2hb_region_pin() to select the appropriate variant.

Link: https://lore.kernel.org/20260722124933.430554-3-joseph.qi@linux.alibaba.com
Fixes: 58a3158a5d ("ocfs2/cluster: Pin/unpin o2hb regions")
Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com>
Cc: Changwei Ge <gechangwei@live.cn>
Cc: Heming Zhao <heming.zhao@suse.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Jun Piao <piaojun@huawei.com>
Cc: Junxiao Bi <junxiao.bi@oracle.com>
Cc: Mark Fasheh <mark@fasheh.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
Joseph Qi
2026-07-22 20:49:32 +08:00
committed by Andrew Morton
parent af09df89db
commit cd789996db
3 changed files with 17 additions and 7 deletions

View File

@@ -146,7 +146,7 @@ static unsigned int o2hb_dependent_users;
* In global heartbeat mode, we pin/unpin all o2hb regions. This solution
* works for both file system and userdlm domains.
*/
static int o2hb_region_pin(const char *region_uuid);
static int o2hb_region_pin(const char *region_uuid, bool from_callback);
static void o2hb_region_unpin(const char *region_uuid);
/* Only sets a new threshold if there are no active regions.
@@ -2188,7 +2188,7 @@ static void o2hb_heartbeat_group_drop_item(struct config_group *group,
if (bitmap_weight(o2hb_quorum_region_bitmap,
O2NM_MAX_REGIONS) <= O2HB_PIN_CUT_OFF)
o2hb_region_pin(NULL);
o2hb_region_pin(NULL, true);
unlock:
spin_unlock(&o2hb_live_lock);
@@ -2330,7 +2330,7 @@ EXPORT_SYMBOL_GPL(o2hb_setup_callback);
* In local, we only pin the matching region. In global we pin all the active
* regions.
*/
static int o2hb_region_pin(const char *region_uuid)
static int o2hb_region_pin(const char *region_uuid, bool from_callback)
{
int ret = 0, found;
struct o2hb_region *reg, *pinned;
@@ -2385,7 +2385,10 @@ static int o2hb_region_pin(const char *region_uuid)
spin_unlock(&o2hb_live_lock);
/* Ignore ENOENT only for local hb (userdlm domain) */
ret = o2nm_depend_item(&pinned->hr_item);
if (from_callback)
ret = o2nm_depend_item_unlocked(&pinned->hr_item);
else
ret = o2nm_depend_item(&pinned->hr_item);
spin_lock(&o2hb_live_lock);
if (!ret) {
@@ -2483,8 +2486,8 @@ static int o2hb_region_inc_user(const char *region_uuid)
/* local heartbeat */
if (!o2hb_global_heartbeat_active()) {
ret = o2hb_region_pin(region_uuid);
goto unlock;
ret = o2hb_region_pin(region_uuid, false);
goto unlock;
}
/*
@@ -2497,7 +2500,7 @@ static int o2hb_region_inc_user(const char *region_uuid)
if (bitmap_weight(o2hb_quorum_region_bitmap,
O2NM_MAX_REGIONS) <= O2HB_PIN_CUT_OFF)
ret = o2hb_region_pin(NULL);
ret = o2hb_region_pin(NULL, false);
unlock:
spin_unlock(&o2hb_live_lock);

View File

@@ -778,6 +778,12 @@ int o2nm_depend_item(struct config_item *item)
return configfs_depend_item(&o2nm_cluster_group.cs_subsys, item);
}
int o2nm_depend_item_unlocked(struct config_item *item)
{
return configfs_depend_item_unlocked(&o2nm_cluster_group.cs_subsys,
item);
}
void o2nm_undepend_item(struct config_item *item)
{
configfs_undepend_item(item);

View File

@@ -64,6 +64,7 @@ void o2nm_node_get(struct o2nm_node *node);
void o2nm_node_put(struct o2nm_node *node);
int o2nm_depend_item(struct config_item *item);
int o2nm_depend_item_unlocked(struct config_item *item);
void o2nm_undepend_item(struct config_item *item);
int o2nm_depend_node(u8 node_num);
void o2nm_undepend_node(u8 node_num);