Function rhashtable_walk_peek is problematic because there is no
guarantee that the glock previously returned still exists; when that key
is deleted, rhashtable_walk_peek can end up returning a different key,
which will cause an inconsistent glock dump.  Fix this by keeping track
of the current glock in the seq file iterator functions instead.

Signed-off-by: Andreas Gruenbacher <agrue...@redhat.com>
---
 fs/gfs2/glock.c | 47 ++++++++++++++++++++++++++++-------------------
 1 file changed, 28 insertions(+), 19 deletions(-)

diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index 82fb5583445c..097bd3c0f270 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -1923,28 +1923,37 @@ void gfs2_glock_exit(void)
 
 static void gfs2_glock_iter_next(struct gfs2_glock_iter *gi, loff_t n)
 {
-       if (n == 0)
-               gi->gl = rhashtable_walk_peek(&gi->hti);
-       else {
-               gi->gl = rhashtable_walk_next(&gi->hti);
-               n--;
+       struct gfs2_glock *gl = gi->gl;
+
+       if (gl) {
+               if (n == 0)
+                       return;
+               if (!lockref_put_not_zero(&gl->gl_lockref))
+                       gfs2_glock_queue_put(gl);
        }
        for (;;) {
-               if (IS_ERR_OR_NULL(gi->gl)) {
-                       if (!gi->gl)
-                               return;
-                       if (PTR_ERR(gi->gl) != -EAGAIN) {
-                               gi->gl = NULL;
-                               return;
+               gl = rhashtable_walk_next(&gi->hti);
+               if (IS_ERR_OR_NULL(gl)) {
+                       if (gl == ERR_PTR(-EAGAIN)) {
+                               n = 1;
+                               continue;
                        }
-                       n = 0;
-               } else if (gi->sdp == gi->gl->gl_name.ln_sbd &&
-                          !__lockref_is_dead(&gi->gl->gl_lockref)) {
-                       if (!n--)
-                               break;
+                       gl = NULL;
+                       break;
+               }
+               if (gl->gl_name.ln_sbd != gi->sdp)
+                       continue;
+               if (n <= 1) {
+                       if (!lockref_get_not_dead(&gl->gl_lockref))
+                               continue;
+                       break;
+               } else {
+                       if (__lockref_is_dead(&gl->gl_lockref))
+                               continue;
+                       n--;
                }
-               gi->gl = rhashtable_walk_next(&gi->hti);
        }
+       gi->gl = gl;
 }
 
 static void *gfs2_glock_seq_start(struct seq_file *seq, loff_t *pos)
@@ -1988,7 +1997,6 @@ static void gfs2_glock_seq_stop(struct seq_file *seq, 
void *iter_ptr)
 {
        struct gfs2_glock_iter *gi = seq->private;
 
-       gi->gl = NULL;
        rhashtable_walk_stop(&gi->hti);
 }
 
@@ -2076,7 +2084,8 @@ static int gfs2_glocks_release(struct inode *inode, 
struct file *file)
        struct seq_file *seq = file->private_data;
        struct gfs2_glock_iter *gi = seq->private;
 
-       gi->gl = NULL;
+       if (gi->gl)
+               gfs2_glock_put(gi->gl);
        rhashtable_walk_exit(&gi->hti);
        return seq_release_private(inode, file);
 }
-- 
2.14.3

Reply via email to