Re: [PATCH 4/8] exportfs: stop retrying once we race with rename/remove

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 25 Oct 2013 16:30:01 -0400 "J. Bruce Fields" <bfields@xxxxxxxxxx>
wrote:

> From: "J. Bruce Fields" <bfields@xxxxxxxxxx>
> 
> There are two places here where we could race with a rename or remove:
> 
> 	- We could find the parent, but then be removed or renamed away
> 	  from that parent directory before finding our name in that
> 	  directory.
> 	- We could find the parent, and find our name in that parent,
> 	  but then be renamed or removed before we look ourselves up by
> 	  that name in that parent.
> 
> In both cases the concurrent rename or remove will take care of
> reconnecting the directory that we're currently examining.  Our target
> directory should then also be connected.  Check this and clear
> DISCONNECTED in these cases instead of looping around again.
> 
> Note: we *do* need to check that this actually happened if we want to be
> robust in the face of corrupted filesystems: a corrupted filesystem
> could just return a completely wrong parent, and we want to fail with an
> error in that case before starting to clear DISCONNECTED on
> non-DISCONNECTED filesystems.
> 
> Signed-off-by: J. Bruce Fields <bfields@xxxxxxxxxx>
> ---
>  fs/exportfs/expfs.c |   45 ++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 40 insertions(+), 5 deletions(-)
> 
> diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
> index c65b748..6b5ddd5 100644
> --- a/fs/exportfs/expfs.c
> +++ b/fs/exportfs/expfs.c
> @@ -90,6 +90,23 @@ find_disconnected_root(struct dentry *dentry)
>  	return dentry;
>  }
>  
> +static bool dentry_connected(struct dentry *dentry)
> +{
> +	dget(dentry);
> +	while (dentry->d_flags & DCACHE_DISCONNECTED) {
> +		struct dentry *parent = dget_parent(dentry);
> +
> +		dput(dentry);
> +		if (IS_ROOT(dentry)) {
> +			dput(parent);
> +			return false;
> +		}
> +		dentry = parent;
> +	}
> +	dput(dentry);
> +	return true;
> +}

This looks remarkably similar to find_disconnected_root().

static bool dentry_connected(struct dentry *dentry)
{
	return !IS_ROOT(find_disconnected_root(dentry));
}

??
....

> +
>  static void clear_disconnected(struct dentry *dentry)
>  {
>  	dget(dentry);
> @@ -189,9 +206,9 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)
>  				dput(pd);
>  				if (err == -ENOENT)
>  					/* some race between get_parent and
> -					 * get_name?  just try again
> +					 * get_name?
>  					 */
> -					continue;
> +					goto out_reconnected;
>  				break;
>  			}
>  			dprintk("%s: found name: %s\n", __func__, nbuf);
> @@ -211,12 +228,12 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)
>  			 * hopefully, npd == pd, though it isn't really
>  			 * a problem if it isn't
>  			 */
> +			dput(npd);
> +			dput(ppd);
>  			if (npd == pd)
>  				noprogress = 0;
>  			else
> -				printk("%s: npd != pd\n", __func__);
> -			dput(npd);
> -			dput(ppd);
> +				goto out_reconnected;
>  			if (IS_ROOT(pd)) {
>  				/* something went wrong, we have to give up */
>  				dput(pd);
> @@ -234,6 +251,24 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)
>  	}
>  
>  	return 0;
> +out_reconnected:
> +	/*
> +	 * Someone must have renamed our entry into another parent, in
> +	 * which case it has been reconnected by the rename.
> +	 *
> +	 * Or someone removed it entirely, in which case filehandle
> +	 * lookup will succeed but the directory is now IS_DEAD and
> +	 * subsequent operations on it will fail.
> +	 *
> +	 * Alternatively, maybe there was no race at all, and the
> +	 * filesystem is just corrupt and gave us a parent that doesn't
> +	 * actually contain any entry pointing to this inode.  So,
> +	 * double check that this worked and return -ESTALE if not:
> +	 */
> +	if (!dentry_connected(target_dir))
> +		return -ESTALE;
> +	clear_disconnected(target_dir);

... or just open-code it.  Then this becomes:

  if (!IS_ROOT(find_disconnected_root(target_dir))) {
         clear_disconnected(target_dir);
         return 0;
  }
  return -ESTALE;

which is pleasing similar to the (new) code higher up:

		struct dentry *pd = find_disconnected_root(target_dir);

		if (!IS_ROOT(pd)) {
			/* must have found a connected parent - great */
			clear_disconnected(target_dir);
                ....


Just a thought,
NeilBrown


> +	return 0;
>  }
>  
>  struct getdents_callback {

Attachment: signature.asc
Description: PGP signature


[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux