[PATCH] gfs2: Fix recursive quota glock locking in do_sync

Jiakai Xu posted 1 patch 1 week, 6 days ago
fs/gfs2/quota.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
[PATCH] gfs2: Fix recursive quota glock locking in do_sync
Posted by Jiakai Xu 1 week, 6 days ago
During GFS2 unmount, do_sync() in gfs2_quota_sync() may try to re-acquire
quota data glocks that the current process already holds (from earlier
file operations that performed quota accounting). The GFS2 glock layer's
add_to_queue() detects this as recursive locking via the trap_recursive
mechanism and hits BUG().

Fix this by checking gfs2_glock_is_locked_by_me() before each
gfs2_glock_nq_init() call in do_sync(). If the current process already
holds the glock, skip the acquisition. The cleanup path is updated to
only call gfs2_glock_dq_uninit() on holders that were actually acquired.

Fixes: 1e72c0f7c40e ("GFS2: Clean up gfs2_adjust_quota() and do_glock()")
Signed-off-by: Jiakai Xu <xujiakai24@mails.ucas.ac.cn>
---
 fs/gfs2/quota.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c
index 91e9975d25e8..f2a09869ee8f 100644
--- a/fs/gfs2/quota.c
+++ b/fs/gfs2/quota.c
@@ -929,6 +929,19 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda,
 	sort(qda, num_qd, sizeof(struct gfs2_quota_data *), sort_qd, NULL);
 	inode_lock(&ip->i_inode);
 	for (qx = 0; qx < num_qd; qx++) {
+		/*
+		 * If we already hold this quota glock (because do_sync was
+		 * called from a context where quota glocks are already held,
+		 * such as during unmount), skip the recursive acquisition.
+		 *
+		 * We mark the holder with a NULL glock pointer so the cleanup
+		 * path knows not to release it.
+		 */
+		if (gfs2_glock_is_locked_by_me(qda[qx]->qd_gl)) {
+			memset(&ghs[qx], 0, sizeof(ghs[qx]));
+			continue;
+		}
+
 		error = gfs2_glock_nq_init(qda[qx]->qd_gl, LM_ST_EXCLUSIVE,
 					   GL_NOCACHE, &ghs[qx]);
 		if (error)
@@ -990,7 +1003,8 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda,
 	gfs2_glock_dq_uninit(&i_gh);
 out_dq:
 	while (qx--)
-		gfs2_glock_dq_uninit(&ghs[qx]);
+		if (ghs[qx].gh_gl)
+			gfs2_glock_dq_uninit(&ghs[qx]);
 	inode_unlock(&ip->i_inode);
 	kfree(ghs);
 	gfs2_log_flush(glock_sbd(ip->i_gl), ip->i_gl,
-- 
2.34.1