diff options
| author | Andreas Gruenbacher <agruenba@redhat.com> | 2018-03-28 12:05:35 +0200 | 
|---|---|---|
| committer | Bob Peterson <rpeterso@redhat.com> | 2018-04-12 09:41:19 -0700 | 
| commit | 3fd5d3ad35dc44aaf0f28d60cc0eb75887bff54d (patch) | |
| tree | e94c5e90a62de825fe65c34093109d4ec40639e6 | |
| parent | 450b1f6f56350c630e795f240dc5a77aa8aa2419 (diff) | |
gfs2: Stop using rhashtable_walk_peek
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 <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
| -rw-r--r-- | fs/gfs2/glock.c | 47 | 
1 files 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);  }  | 
