Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1a0eae88 authored by Bob Peterson's avatar Bob Peterson Committed by Steven Whitehouse
Browse files

GFS2: glock livelock



This patch fixes a couple gfs2 problems with the reclaiming of
unlinked dinodes.  First, there were a couple of livelocks where
everything would come to a halt waiting for a glock that was
seemingly held by a process that no longer existed.  In fact, the
process did exist, it just had the wrong pid number in the holder
information.  Second, there was a lock ordering problem between
inode locking and glock locking.  Third, glock/inode contention
could sometimes cause inodes to be improperly marked invalid by
iget_failed.

Signed-off-by: default avatarBob Peterson <rpeterso@redhat.com>
parent 602c89d2
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1475,7 +1475,7 @@ struct inode *gfs2_dir_search(struct inode *dir, const struct qstr *name)
		inode = gfs2_inode_lookup(dir->i_sb, 
				be16_to_cpu(dent->de_type),
				be64_to_cpu(dent->de_inum.no_addr),
				be64_to_cpu(dent->de_inum.no_formal_ino), 0);
				be64_to_cpu(dent->de_inum.no_formal_ino));
		brelse(bh);
		return inode;
	}
+1 −1
Original line number Diff line number Diff line
@@ -169,7 +169,7 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb,
	if (error)
		goto fail;

	inode = gfs2_inode_lookup(sb, DT_UNKNOWN, inum->no_addr, 0, 0);
	inode = gfs2_inode_lookup(sb, DT_UNKNOWN, inum->no_addr, 0);
	if (IS_ERR(inode)) {
		error = PTR_ERR(inode);
		goto fail;
+3 −0
Original line number Diff line number Diff line
@@ -855,6 +855,9 @@ void gfs2_holder_reinit(unsigned int state, unsigned flags, struct gfs2_holder *
	gh->gh_flags = flags;
	gh->gh_iflags = 0;
	gh->gh_ip = (unsigned long)__builtin_return_address(0);
	if (gh->gh_owner_pid)
		put_pid(gh->gh_owner_pid);
	gh->gh_owner_pid = get_pid(task_pid(current));
}

/**
+92 −9
Original line number Diff line number Diff line
@@ -158,7 +158,6 @@ void gfs2_set_iop(struct inode *inode)
 * @sb: The super block
 * @no_addr: The inode number
 * @type: The type of the inode
 * @skip_freeing: set this not return an inode if it is currently being freed.
 *
 * Returns: A VFS inode, or an error
 */
@@ -166,16 +165,13 @@ void gfs2_set_iop(struct inode *inode)
struct inode *gfs2_inode_lookup(struct super_block *sb,
				unsigned int type,
				u64 no_addr,
				u64 no_formal_ino, int skip_freeing)
				u64 no_formal_ino)
{
	struct inode *inode;
	struct gfs2_inode *ip;
	struct gfs2_glock *io_gl;
	int error;

	if (skip_freeing)
		inode = gfs2_iget_skip(sb, no_addr);
	else
	inode = gfs2_iget(sb, no_addr);
	ip = GFS2_I(inode);

@@ -234,13 +230,100 @@ fail_glock:
fail_iopen:
	gfs2_glock_put(io_gl);
fail_put:
	if (inode->i_state & I_NEW)
		ip->i_gl->gl_object = NULL;
	gfs2_glock_put(ip->i_gl);
fail:
	if (inode->i_state & I_NEW)
		iget_failed(inode);
	else
		iput(inode);
	return ERR_PTR(error);
}

/**
 * gfs2_unlinked_inode_lookup - Lookup an unlinked inode for reclamation
 * @sb: The super block
 * no_addr: The inode number
 * @@inode: A pointer to the inode found, if any
 *
 * Returns: 0 and *inode if no errors occurred.  If an error occurs,
 *          the resulting *inode may or may not be NULL.
 */

int gfs2_unlinked_inode_lookup(struct super_block *sb, u64 no_addr,
			       struct inode **inode)
{
	struct gfs2_sbd *sdp;
	struct gfs2_inode *ip;
	struct gfs2_glock *io_gl;
	int error;
	struct gfs2_holder gh;

	*inode = gfs2_iget_skip(sb, no_addr);

	if (!(*inode))
		return -ENOBUFS;

	if (!((*inode)->i_state & I_NEW))
		return -ENOBUFS;

	ip = GFS2_I(*inode);
	sdp = GFS2_SB(*inode);
	ip->i_no_formal_ino = -1;

	error = gfs2_glock_get(sdp, no_addr, &gfs2_inode_glops, CREATE, &ip->i_gl);
	if (unlikely(error))
		goto fail;
	ip->i_gl->gl_object = ip;

	error = gfs2_glock_get(sdp, no_addr, &gfs2_iopen_glops, CREATE, &io_gl);
	if (unlikely(error))
		goto fail_put;

	set_bit(GIF_INVALID, &ip->i_flags);
	error = gfs2_glock_nq_init(io_gl, LM_ST_SHARED, LM_FLAG_TRY | GL_EXACT,
				   &ip->i_iopen_gh);
	if (unlikely(error)) {
		if (error == GLR_TRYFAILED)
			error = 0;
		goto fail_iopen;
	}
	ip->i_iopen_gh.gh_gl->gl_object = ip;
	gfs2_glock_put(io_gl);

	(*inode)->i_mode = DT2IF(DT_UNKNOWN);

	/*
	 * We must read the inode in order to work out its type in
	 * this case. Note that this doesn't happen often as we normally
	 * know the type beforehand. This code path only occurs during
	 * unlinked inode recovery (where it is safe to do this glock,
	 * which is not true in the general case).
	 */
	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, LM_FLAG_TRY,
				   &gh);
	if (unlikely(error)) {
		if (error == GLR_TRYFAILED)
			error = 0;
		goto fail_glock;
	}
	/* Inode is now uptodate */
	gfs2_glock_dq_uninit(&gh);
	gfs2_set_iop(*inode);

	return 0;
fail_glock:
	gfs2_glock_dq(&ip->i_iopen_gh);
fail_iopen:
	gfs2_glock_put(io_gl);
fail_put:
	ip->i_gl->gl_object = NULL;
	gfs2_glock_put(ip->i_gl);
fail:
	return error;
}

static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf)
{
	const struct gfs2_dinode *str = buf;
@@ -862,7 +945,7 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name,
		goto fail_gunlock2;

	inode = gfs2_inode_lookup(dir->i_sb, IF2DT(mode), inum.no_addr,
				  inum.no_formal_ino, 0);
				  inum.no_formal_ino);
	if (IS_ERR(inode))
		goto fail_gunlock2;

+3 −2
Original line number Diff line number Diff line
@@ -83,8 +83,9 @@ static inline void gfs2_inum_out(const struct gfs2_inode *ip,

extern void gfs2_set_iop(struct inode *inode);
extern struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type, 
				       u64 no_addr, u64 no_formal_ino,
				       int skip_freeing);
				       u64 no_addr, u64 no_formal_ino);
extern int gfs2_unlinked_inode_lookup(struct super_block *sb, u64 no_addr,
				      struct inode **inode);
extern struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr);

extern int gfs2_inode_refresh(struct gfs2_inode *ip);
Loading