[Cluster-devel] [PATCH GFS2] livelock while reclaiming unlinked dinodes

Steven Whitehouse swhiteho at redhat.com
Wed Apr 14 16:31:26 UTC 2010


Hi,

Now in the -nmw git tree. Thanks,

Steve.

On Wed, 2010-04-14 at 11:58 -0400, Bob Peterson wrote:
> Hi,
> 
> Here is a patch for bugzilla bug #570182.  Explanation in the
> patch.
> 
> Regards,
> 
> Bob Peterson
> Red Hat GFS
> 
> Signed-off-by: Bob Peterson <rpeterso at redhat.com> 
> --
> Author: Bob Peterson <bob at krishna.(none)>
> Date:   Tue Apr 13 08:49:33 2010 -0500
> 
>     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.
>     
>     rhbz#570182
> 
> diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
> index 297d7e5..5f1cc15 100644
> --- a/fs/gfs2/dir.c
> +++ b/fs/gfs2/dir.c
> @@ -1507,7 +1507,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;
>  	}
> diff --git a/fs/gfs2/export.c b/fs/gfs2/export.c
> index d15876e..d81bc7e 100644
> --- a/fs/gfs2/export.c
> +++ b/fs/gfs2/export.c
> @@ -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;
> diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
> index c69d5fd..847892b 100644
> --- a/fs/gfs2/glock.c
> +++ b/fs/gfs2/glock.c
> @@ -857,6 +857,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));
>  }
>  
>  /**
> diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
> index 6380cd9..40c1ed0 100644
> --- a/fs/gfs2/inode.c
> +++ b/fs/gfs2/inode.c
> @@ -160,7 +160,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
>   */
> @@ -168,17 +167,14 @@ 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);
> +	inode = gfs2_iget(sb, no_addr);
>  	ip = GFS2_I(inode);
>  
>  	if (!inode)
> @@ -236,13 +232,100 @@ fail_glock:
>  fail_iopen:
>  	gfs2_glock_put(io_gl);
>  fail_put:
> -	ip->i_gl->gl_object = NULL;
> +	if (inode->i_state & I_NEW)
> +		ip->i_gl->gl_object = NULL;
>  	gfs2_glock_put(ip->i_gl);
>  fail:
> -	iget_failed(inode);
> +	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;
> @@ -863,7 +946,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;
>  
> diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h
> index c341aaf..e161461 100644
> --- a/fs/gfs2/inode.h
> +++ b/fs/gfs2/inode.h
> @@ -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);
> diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
> index 8a102f7..9db0be4 100644
> --- a/fs/gfs2/ops_fstype.c
> +++ b/fs/gfs2/ops_fstype.c
> @@ -488,7 +488,7 @@ static int gfs2_lookup_root(struct super_block *sb, struct dentry **dptr,
>  	struct dentry *dentry;
>  	struct inode *inode;
>  
> -	inode = gfs2_inode_lookup(sb, DT_DIR, no_addr, 0, 0);
> +	inode = gfs2_inode_lookup(sb, DT_DIR, no_addr, 0);
>  	if (IS_ERR(inode)) {
>  		fs_err(sdp, "can't read in %s inode: %ld\n", name, PTR_ERR(inode));
>  		return PTR_ERR(inode);
> diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c
> index d87c0f8..ae9a2f7 100644
> --- a/fs/gfs2/rgrp.c
> +++ b/fs/gfs2/rgrp.c
> @@ -952,18 +952,20 @@ static int try_rgrp_fit(struct gfs2_rgrpd *rgd, struct gfs2_alloc *al)
>   * try_rgrp_unlink - Look for any unlinked, allocated, but unused inodes
>   * @rgd: The rgrp
>   *
> - * Returns: The inode, if one has been found
> + * Returns: 0 if no error
> + *          The inode, if one has been found, in inode.
>   */
>  
> -static struct inode *try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked,
> -				     u64 skip)
> +static int try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked,
> +			   u64 skip, struct inode **inode)
>  {
> -	struct inode *inode;
>  	u32 goal = 0, block;
>  	u64 no_addr;
>  	struct gfs2_sbd *sdp = rgd->rd_sbd;
>  	unsigned int n;
> +	int error = 0;
>  
> +	*inode = NULL;
>  	for(;;) {
>  		if (goal >= rgd->rd_data)
>  			break;
> @@ -983,14 +985,14 @@ static struct inode *try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked,
>  		if (no_addr == skip)
>  			continue;
>  		*last_unlinked = no_addr;
> -		inode = gfs2_inode_lookup(rgd->rd_sbd->sd_vfs, DT_UNKNOWN,
> -					  no_addr, -1, 1);
> -		if (!IS_ERR(inode))
> -			return inode;
> +		error = gfs2_unlinked_inode_lookup(rgd->rd_sbd->sd_vfs,
> +						   no_addr, inode);
> +		if (*inode || error)
> +			return error;
>  	}
>  
>  	rgd->rd_flags &= ~GFS2_RDF_CHECK;
> -	return NULL;
> +	return 0;
>  }
>  
>  /**
> @@ -1100,12 +1102,27 @@ static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked)
>  		case 0:
>  			if (try_rgrp_fit(rgd, al))
>  				goto out;
> -			if (rgd->rd_flags & GFS2_RDF_CHECK)
> -				inode = try_rgrp_unlink(rgd, last_unlinked, ip->i_no_addr);
> +			/* If the rg came in already locked, there's no
> +			   way we can recover from a failed try_rgrp_unlink
> +			   because that would require an iput which can only
> +			   happen after the rgrp is unlocked. */
> +			if (!rg_locked && rgd->rd_flags & GFS2_RDF_CHECK)
> +				error = try_rgrp_unlink(rgd, last_unlinked,
> +							ip->i_no_addr, &inode);
>  			if (!rg_locked)
>  				gfs2_glock_dq_uninit(&al->al_rgd_gh);
> -			if (inode)
> +			if (inode) {
> +				if (error) {
> +					if (inode->i_state & I_NEW)
> +						iget_failed(inode);
> +					else
> +						iput(inode);
> +					return ERR_PTR(error);
> +				}
>  				return inode;
> +			}
> +			if (error)
> +				return ERR_PTR(error);
>  			/* fall through */
>  		case GLR_TRYFAILED:
>  			rgd = recent_rgrp_next(rgd);
> @@ -1134,12 +1151,23 @@ static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked)
>  		case 0:
>  			if (try_rgrp_fit(rgd, al))
>  				goto out;
> -			if (rgd->rd_flags & GFS2_RDF_CHECK)
> -				inode = try_rgrp_unlink(rgd, last_unlinked, ip->i_no_addr);
> +			if (!rg_locked && rgd->rd_flags & GFS2_RDF_CHECK)
> +				error = try_rgrp_unlink(rgd, last_unlinked,
> +							ip->i_no_addr, &inode);
>  			if (!rg_locked)
>  				gfs2_glock_dq_uninit(&al->al_rgd_gh);
> -			if (inode)
> +			if (inode) {
> +				if (error) {
> +					if (inode->i_state & I_NEW)
> +						iget_failed(inode);
> +					else
> +						iput(inode);
> +					return ERR_PTR(error);
> +				}
>  				return inode;
> +			}
> +			if (error)
> +				return ERR_PTR(error);
>  			break;
>  
>  		case GLR_TRYFAILED:
> 




More information about the Cluster-devel mailing list