Skip to contentSkip to conversation

GFS2: Hold gl_hash_table bucket locks in glock_hash_walk

PATCH New Andrew Price 3 participants 8 messages Nov 12, 2015 – Nov 16, 2015 GFS2 file system

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 GFS2: Hold gl_hash_table bucket locks in glock_hash_walk

This lockdep splat was being triggered on umount:

[55715.973122] ===============================
[55715.980169] [ INFO: suspicious RCU usage. ]
[55715.981021] 4.3.0-11553-g8d3de01-dirty #15 Tainted: G        W
[55715.982353] -------------------------------
[55715.983301] fs/gfs2/glock.c:1427 suspicious rcu_dereference_protected() usage!

The code it refers to is the rht_for_each_entry_safe usage in glock_hash_walk. The condition that triggers the warning is lockdep_rht_bucket_is_held(tbl, hash) which is checked in the __rcu_dereference_protected macro.

lockdep_rht_bucket_is_held returns false when rht_bucket_lock(tbl, hash) is not held, which suggests that glock_hash_walk should be holding the lock for each rhashtable bucket it looks at. Holding those locks clears up the warning.

---
 fs/gfs2/glock.c | 2 ++
 1 file changed, 2 insertions(+)
fs/gfs2/glock.c +2 -0
index 32e7471..7384cbb 100644
@@ -1424,11 +1424,13 @@ static void glock_hash_walk(glock_examiner examiner, const struct gfs2_sbd *sdp)
 	rcu_read_lock();
 	tbl = rht_dereference_rcu(gl_hash_table.tbl, &gl_hash_table);
 	for (i = 0; i < tbl->size; i++) {
+		spin_lock(rht_bucket_lock(tbl, i));
 		rht_for_each_entry_safe(gl, pos, next, tbl, i, gl_node) {
 			if ((gl->gl_name.ln_sbd == sdp) &&
 			    lockref_get_not_dead(&gl->gl_lockref))
 				examiner(gl);
 		}
+		spin_unlock(rht_bucket_lock(tbl, i));
 	}
 	rcu_read_unlock();
 	cond_resched();
Signature · 1 line
2.4.3
#1 lore raw

PATCH 1/1 GFS2: Hold gl_hash_table bucket locks in glock_hash_walk

----- Original Message -----

Quoted 42 lines

This lockdep splat was being triggered on umount:

[55715.973122] ===============================
[55715.980169] [ INFO: suspicious RCU usage. ]
[55715.981021] 4.3.0-11553-g8d3de01-dirty #15 Tainted: G        W
[55715.982353] -------------------------------
[55715.983301] fs/gfs2/glock.c:1427 suspicious rcu_dereference_protected()

usage!

The code it refers to is the rht_for_each_entry_safe usage in glock_hash_walk. The condition that triggers the warning is lockdep_rht_bucket_is_held(tbl, hash) which is checked in the __rcu_dereference_protected macro.

lockdep_rht_bucket_is_held returns false when rht_bucket_lock(tbl, hash) is not held, which suggests that glock_hash_walk should be holding the lock for each rhashtable bucket it looks at. Holding those locks clears up the warning.

---
 fs/gfs2/glock.c | 2 ++
 1 file changed, 2 insertions(+)
fs/gfs2/glock.c +2 -0
index 32e7471..7384cbb 100644
@@ -1424,11 +1424,13 @@ static void glock_hash_walk(glock_examiner examiner,
 const struct gfs2_sbd *sdp)
 	rcu_read_lock();
 	tbl = rht_dereference_rcu(gl_hash_table.tbl, &gl_hash_table);
 	for (i = 0; i < tbl->size; i++) {
+		spin_lock(rht_bucket_lock(tbl, i));
 		rht_for_each_entry_safe(gl, pos, next, tbl, i, gl_node) {
 			if ((gl->gl_name.ln_sbd == sdp) &&
 			    lockref_get_not_dead(&gl->gl_lockref))
 				examiner(gl);
 		}
+		spin_unlock(rht_bucket_lock(tbl, i));
 	}
 	rcu_read_unlock();
Quoted 5 lines

cond_resched();
--
2.4.3

Hi Andy,

Thanks. I'll hold onto this until after the merge window is closed.

Regards,

Bob Peterson
Red Hat File Systems

#2 lore raw

PATCH 1/1 GFS2: Hold gl_hash_table bucket locks in glock_hash_walk

Hi,

Quoted 37 lines · Bob Peterson

----- Original Message -----

Quoted 36 lines

This lockdep splat was being triggered on umount:

[55715.973122] ===============================
[55715.980169] [ INFO: suspicious RCU usage. ]
[55715.981021] 4.3.0-11553-g8d3de01-dirty #15 Tainted: G        W
[55715.982353] -------------------------------
[55715.983301] fs/gfs2/glock.c:1427 suspicious rcu_dereference_protected()

usage!

The code it refers to is the rht_for_each_entry_safe usage in glock_hash_walk. The condition that triggers the warning is lockdep_rht_bucket_is_held(tbl, hash) which is checked in the __rcu_dereference_protected macro.

lockdep_rht_bucket_is_held returns false when rht_bucket_lock(tbl, hash) is not held, which suggests that glock_hash_walk should be holding the lock for each rhashtable bucket it looks at. Holding those locks clears up the warning.

---
  fs/gfs2/glock.c | 2 ++
  1 file changed, 2 insertions(+)
fs/gfs2/glock.c +1 -0
index 32e7471..7384cbb 100644
@@ -1424,11 +1424,13 @@ static void glock_hash_walk(glock_examiner examiner,
 const struct gfs2_sbd *sdp)
  	rcu_read_lock();
  	tbl = rht_dereference_rcu(gl_hash_table.tbl, &gl_hash_table);
  	for (i = 0; i < tbl->size; i++) {
+		spin_lock(rht_bucket_lock(tbl, i));
  		rht_for_each_entry_safe(gl, pos, next, tbl, i, gl_node) {
  			if ((gl->gl_name.ln_sbd == sdp) &&
Quoted 8 lines
Quoted 8 lines
fs/gfs2/glock.c +1 -0
  			    lockref_get_not_dead(&gl->gl_lockref))
  				examiner(gl);
  		}
+		spin_unlock(rht_bucket_lock(tbl, i));
  	}
  	rcu_read_unlock();

cond_resched();
--

Not sure that this is the correct fix. There is nothing changing in the examiner function that I can see which would require the bucket lock, and we already have this with an rcu_read_lock, which is correct since we are only reading the hash lists, not changing them. So I'm wondering why this triggers the warning, and whether there is some annotation issue perhaps?

Steve.

Quoted 12 lines
Quoted 3 lines

2.4.3

Hi Andy,

Thanks. I'll hold onto this until after the merge window is closed.

Regards,

Bob Peterson
Red Hat File Systems

#3 lore raw

PATCH 1/1 GFS2: Hold gl_hash_table bucket locks in glock_hash_walk

Quoted 46 lines · Steven Whitehouse

Hi,

Quoted 43 lines · Bob Peterson

----- Original Message -----

Quoted 42 lines

This lockdep splat was being triggered on umount:

[55715.973122] ===============================
[55715.980169] [ INFO: suspicious RCU usage. ]
[55715.981021] 4.3.0-11553-g8d3de01-dirty #15 Tainted: G        W
[55715.982353] -------------------------------
[55715.983301] fs/gfs2/glock.c:1427 suspicious

rcu_dereference_protected()
usage!

The code it refers to is the rht_for_each_entry_safe usage in glock_hash_walk. The condition that triggers the warning is lockdep_rht_bucket_is_held(tbl, hash) which is checked in the __rcu_dereference_protected macro.

lockdep_rht_bucket_is_held returns false when rht_bucket_lock(tbl, hash) is not held, which suggests that glock_hash_walk should be holding the lock for each rhashtable bucket it looks at. Holding those locks clears up the warning.

---
  fs/gfs2/glock.c | 2 ++
  1 file changed, 2 insertions(+)
fs/gfs2/glock.c +2 -0
index 32e7471..7384cbb 100644
@@ -1424,11 +1424,13 @@ static void glock_hash_walk(glock_examiner
 examiner,
 const struct gfs2_sbd *sdp)
      rcu_read_lock();
      tbl = rht_dereference_rcu(gl_hash_table.tbl, &gl_hash_table);
      for (i = 0; i < tbl->size; i++) {
+        spin_lock(rht_bucket_lock(tbl, i));
          rht_for_each_entry_safe(gl, pos, next, tbl, i, gl_node) {
              if ((gl->gl_name.ln_sbd == sdp) &&
                  lockref_get_not_dead(&gl->gl_lockref))
                  examiner(gl);
          }
+        spin_unlock(rht_bucket_lock(tbl, i));
Quoted 8 lines
Quoted 4 lines
Quoted 4 lines
fs/gfs2/glock.c +0 -0
      }
      rcu_read_unlock();
      cond_resched();

--

Not sure that this is the correct fix. There is nothing changing in the examiner function that I can see which would require the bucket lock, and we already have this with an rcu_read_lock, which is correct since we are only reading the hash lists, not changing them.

Good point, I wasn't sure whether it was safe to assume the examiner functions weren't changing the buckets and erred on the side of caution.

rht_for_each_entry_safe() does an unconditional

rcu_dereference_protected(p, lockdep_rht_bucket_is_held(tbl, hash))

so we're likely using the wrong rht_for_each_entry* macro. I'm currently testing a version which uses rht_for_each_entry_rcu() instead. That one uses

rcu_dereference_check(p, lockdep_rht_bucket_is_held(tbl, hash))

which is defined as

     #define rcu_dereference_check(p, c) \
        __rcu_dereference_check((p), (c) || rcu_read_lock_held(), __rcu)

so that looks like the right one to use. I'll send that patch shortly.

Andy

#4 lore raw

PATCH v2 1/1 GFS2: Use rht_for_each_entry_rcu in glock_hash_walk

In reply to Steven Whitehouse

This lockdep splat was being triggered on umount:

[55715.973122] ===============================
[55715.980169] [ INFO: suspicious RCU usage. ]
[55715.981021] 4.3.0-11553-g8d3de01-dirty #15 Tainted: G        W
[55715.982353] -------------------------------
[55715.983301] fs/gfs2/glock.c:1427 suspicious rcu_dereference_protected() usage!

The code it refers to is the rht_for_each_entry_safe usage in glock_hash_walk. The condition that triggers the warning is lockdep_rht_bucket_is_held(tbl, hash) which is checked in the __rcu_dereference_protected macro.

The rhashtable buckets are not changed in glock_hash_walk so it's safe to rely on the rcu protection. Replace the rht_for_each_entry_safe() usage with rht_for_each_entry_rcu(), which doesn't care whether the bucket lock is held if the rcu read lock is held.

---
 fs/gfs2/glock.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
fs/gfs2/glock.c +2 -2
index 32e7471..430326e 100644
@@ -1417,14 +1417,14 @@ static struct shrinker glock_shrinker = {
 static void glock_hash_walk(glock_examiner examiner, const struct gfs2_sbd *sdp)
 {
 	struct gfs2_glock *gl;
-	struct rhash_head *pos, *next;
+	struct rhash_head *pos;
 	const struct bucket_table *tbl;
 	int i;
 
 	rcu_read_lock();
 	tbl = rht_dereference_rcu(gl_hash_table.tbl, &gl_hash_table);
 	for (i = 0; i < tbl->size; i++) {
-		rht_for_each_entry_safe(gl, pos, next, tbl, i, gl_node) {
+		rht_for_each_entry_rcu(gl, pos, tbl, i, gl_node) {
 			if ((gl->gl_name.ln_sbd == sdp) &&
 			    lockref_get_not_dead(&gl->gl_lockref))
 				examiner(gl);
Signature · 1 line
2.4.3
#5 lore raw

PATCH v2 1/1 GFS2: Use rht_for_each_entry_rcu in glock_hash_walk

Hi,

Looks good...

Steve.

Quoted 44 lines · Andrew Price

This lockdep splat was being triggered on umount:

[55715.973122] ===============================
[55715.980169] [ INFO: suspicious RCU usage. ]
[55715.981021] 4.3.0-11553-g8d3de01-dirty #15 Tainted: G        W
[55715.982353] -------------------------------
[55715.983301] fs/gfs2/glock.c:1427 suspicious rcu_dereference_protected() usage!

The code it refers to is the rht_for_each_entry_safe usage in glock_hash_walk. The condition that triggers the warning is lockdep_rht_bucket_is_held(tbl, hash) which is checked in the __rcu_dereference_protected macro.

The rhashtable buckets are not changed in glock_hash_walk so it's safe to rely on the rcu protection. Replace the rht_for_each_entry_safe() usage with rht_for_each_entry_rcu(), which doesn't care whether the bucket lock is held if the rcu read lock is held.

---
  fs/gfs2/glock.c | 4 ++--
  1 file changed, 2 insertions(+), 2 deletions(-)
fs/gfs2/glock.c +2 -2
index 32e7471..430326e 100644
@@ -1417,14 +1417,14 @@ static struct shrinker glock_shrinker = {
  static void glock_hash_walk(glock_examiner examiner, const struct gfs2_sbd *sdp)
  {
  	struct gfs2_glock *gl;
-	struct rhash_head *pos, *next;
+	struct rhash_head *pos;
  	const struct bucket_table *tbl;
  	int i;
 
  	rcu_read_lock();
  	tbl = rht_dereference_rcu(gl_hash_table.tbl, &gl_hash_table);
  	for (i = 0; i < tbl->size; i++) {
-		rht_for_each_entry_safe(gl, pos, next, tbl, i, gl_node) {
+		rht_for_each_entry_rcu(gl, pos, tbl, i, gl_node) {
  			if ((gl->gl_name.ln_sbd == sdp) &&
  			    lockref_get_not_dead(&gl->gl_lockref))
  				examiner(gl);
#6 lore raw

PATCH v2 1/1 GFS2: Use rht_for_each_entry_rcu in glock_hash_walk

In reply to Andrew Price

----- Original Message -----

Quoted 45 lines

This lockdep splat was being triggered on umount:

[55715.973122] ===============================
[55715.980169] [ INFO: suspicious RCU usage. ]
[55715.981021] 4.3.0-11553-g8d3de01-dirty #15 Tainted: G        W
[55715.982353] -------------------------------
[55715.983301] fs/gfs2/glock.c:1427 suspicious rcu_dereference_protected()

usage!

The code it refers to is the rht_for_each_entry_safe usage in glock_hash_walk. The condition that triggers the warning is lockdep_rht_bucket_is_held(tbl, hash) which is checked in the __rcu_dereference_protected macro.

The rhashtable buckets are not changed in glock_hash_walk so it's safe to rely on the rcu protection. Replace the rht_for_each_entry_safe() usage with rht_for_each_entry_rcu(), which doesn't care whether the bucket lock is held if the rcu read lock is held.

---
 fs/gfs2/glock.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
fs/gfs2/glock.c +2 -2
index 32e7471..430326e 100644
@@ -1417,14 +1417,14 @@ static struct shrinker glock_shrinker = {
 static void glock_hash_walk(glock_examiner examiner, const struct gfs2_sbd
 *sdp)
 {
 	struct gfs2_glock *gl;
-	struct rhash_head *pos, *next;
+	struct rhash_head *pos;
 	const struct bucket_table *tbl;
 	int i;
 
 	rcu_read_lock();
 	tbl = rht_dereference_rcu(gl_hash_table.tbl, &gl_hash_table);
 	for (i = 0; i < tbl->size; i++) {
-		rht_for_each_entry_safe(gl, pos, next, tbl, i, gl_node) {
+		rht_for_each_entry_rcu(gl, pos, tbl, i, gl_node) {
 			if ((gl->gl_name.ln_sbd == sdp) &&
 			    lockref_get_not_dead(&gl->gl_lockref))
Quoted 5 lines

examiner(gl);
--
2.4.3

ACK

I'll save it until after this merge window.

Bob Peterson
Red Hat File Systems

#7 lore raw

PATCH v2 1/1 GFS2: Use rht_for_each_entry_rcu in glock_hash_walk

In reply to Andrew Price

----- Original Message -----

Quoted 45 lines

This lockdep splat was being triggered on umount:

[55715.973122] ===============================
[55715.980169] [ INFO: suspicious RCU usage. ]
[55715.981021] 4.3.0-11553-g8d3de01-dirty #15 Tainted: G        W
[55715.982353] -------------------------------
[55715.983301] fs/gfs2/glock.c:1427 suspicious rcu_dereference_protected()

usage!

The code it refers to is the rht_for_each_entry_safe usage in glock_hash_walk. The condition that triggers the warning is lockdep_rht_bucket_is_held(tbl, hash) which is checked in the __rcu_dereference_protected macro.

The rhashtable buckets are not changed in glock_hash_walk so it's safe to rely on the rcu protection. Replace the rht_for_each_entry_safe() usage with rht_for_each_entry_rcu(), which doesn't care whether the bucket lock is held if the rcu read lock is held.

---
 fs/gfs2/glock.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
fs/gfs2/glock.c +2 -2
index 32e7471..430326e 100644
@@ -1417,14 +1417,14 @@ static struct shrinker glock_shrinker = {
 static void glock_hash_walk(glock_examiner examiner, const struct gfs2_sbd
 *sdp)
 {
 	struct gfs2_glock *gl;
-	struct rhash_head *pos, *next;
+	struct rhash_head *pos;
 	const struct bucket_table *tbl;
 	int i;
 
 	rcu_read_lock();
 	tbl = rht_dereference_rcu(gl_hash_table.tbl, &gl_hash_table);
 	for (i = 0; i < tbl->size; i++) {
-		rht_for_each_entry_safe(gl, pos, next, tbl, i, gl_node) {
+		rht_for_each_entry_rcu(gl, pos, tbl, i, gl_node) {
 			if ((gl->gl_name.ln_sbd == sdp) &&
 			    lockref_get_not_dead(&gl->gl_lockref))
Quoted 5 lines

examiner(gl);
--
2.4.3

Hi,

Thanks. This is now applied to the for-next branch of the linux-gfs2 tree:
https://git.kernel.org/cgit/linux/kernel/git/gfs2/linux-gfs2.git/commit/fs/gfs2?h=for-next&id=3dd1dd8c696bdb7c8dcc9456cb23558ad1b336b8

Regards,

Bob Peterson
Red Hat File Systems