Commit 0abd1557 authored by Al Viro's avatar Al Viro Committed by Andreas Gruenbacher
Browse files

gfs2: fix an oops in gfs2_permission



In RCU mode, we might race with gfs2_evict_inode(), which zeroes
->i_gl.  Freeing of the object it points to is RCU-delayed, so
if we manage to fetch the pointer before it's been replaced with
NULL, we are fine.  Check if we'd fetched NULL and treat that
as "bail out and tell the caller to get out of RCU mode".

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarAndreas Gruenbacher <agruenba@redhat.com>
parent 4c6a0812
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -1868,14 +1868,21 @@ int gfs2_permission(struct mnt_idmap *idmap, struct inode *inode,
{
	struct gfs2_inode *ip;
	struct gfs2_holder i_gh;
	struct gfs2_glock *gl;
	int error;

	gfs2_holder_mark_uninitialized(&i_gh);
	ip = GFS2_I(inode);
	if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
	gl = rcu_dereference(ip->i_gl);
	if (unlikely(!gl)) {
		/* inode is getting torn down, must be RCU mode */
		WARN_ON_ONCE(!(mask & MAY_NOT_BLOCK));
		return -ECHILD;
        }
	if (gfs2_glock_is_locked_by_me(gl) == NULL) {
		if (mask & MAY_NOT_BLOCK)
			return -ECHILD;
		error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
		error = gfs2_glock_nq_init(gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
		if (error)
			return error;
	}
+1 −1
Original line number Diff line number Diff line
@@ -1540,7 +1540,7 @@ static void gfs2_evict_inode(struct inode *inode)
		wait_on_bit_io(&ip->i_flags, GIF_GLOP_PENDING, TASK_UNINTERRUPTIBLE);
		gfs2_glock_add_to_lru(ip->i_gl);
		gfs2_glock_put_eventually(ip->i_gl);
		ip->i_gl = NULL;
		rcu_assign_pointer(ip->i_gl, NULL);
	}
}