Re: [PATCH 06/12] locks: implement delegations

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

 



On Wed,  3 Jul 2013 16:12:30 -0400
"J. Bruce Fields" <bfields@xxxxxxxxxx> wrote:

> From: "J. Bruce Fields" <bfields@xxxxxxxxxx>
> 
> Implement NFSv4 delegations at the vfs level using the new FL_DELEG lock
> type.
> 
> Signed-off-by: J. Bruce Fields <bfields@xxxxxxxxxx>
> ---
>  fs/locks.c         |   49 +++++++++++++++++++++++++++++++++++++++----------
>  include/linux/fs.h |   18 +++++++++++++++---
>  2 files changed, 54 insertions(+), 13 deletions(-)
> 
> diff --git a/fs/locks.c b/fs/locks.c
> index deec4de..2b56954 100644
> --- a/fs/locks.c
> +++ b/fs/locks.c
> @@ -1176,28 +1176,40 @@ static void time_out_leases(struct inode *inode)
>  	}
>  }
>  
> +static bool leases_conflict(struct file_lock *lease, struct file_lock *breaker)
> +{
> +	if ((breaker->fl_flags & FL_DELEG) && (lease->fl_flags & FL_LEASE))
> +		return false;
> +	return locks_conflict(breaker, lease);
> +}
> +
>  /**
>   *	__break_lease	-	revoke all outstanding leases on file
>   *	@inode: the inode of the file to return
> - *	@mode: the open mode (read or write)
> + *	@mode: O_RDONLY: break only write leases; O_WRONLY or O_RDWR:
> + *	    break all leases
> + *	@type: FL_LEASE: break leases and delegations; FL_DELEG: break
> + *	    only delegations
>   *
>   *	break_lease (inlined for speed) has checked there already is at least
>   *	some kind of lock (maybe a lease) on this file.  Leases are broken on
>   *	a call to open() or truncate().  This function can sleep unless you
>   *	specified %O_NONBLOCK to your open().
>   */
> -int __break_lease(struct inode *inode, unsigned int mode)
> +int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
>  {
>  	int error = 0;
>  	struct file_lock *new_fl, *flock;
>  	struct file_lock *fl;
>  	unsigned long break_time;
>  	int i_have_this_lease = 0;
> +	bool lease_conflict = false;
>  	int want_write = (mode & O_ACCMODE) != O_RDONLY;
>  
>  	new_fl = lease_alloc(NULL, want_write ? F_WRLCK : F_RDLCK);
>  	if (IS_ERR(new_fl))
>  		return PTR_ERR(new_fl);
> +	new_fl->fl_flags = type;
>  
>  	lock_flocks();
>  
> @@ -1207,13 +1219,16 @@ int __break_lease(struct inode *inode, unsigned int mode)
>  	if ((flock == NULL) || !IS_LEASE(flock))
>  		goto out;
>  
> -	if (!locks_conflict(flock, new_fl))
> +	for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) {
> +		if (leases_conflict(fl, new_fl)) {
> +			lease_conflict = true;
> +			if (fl->fl_owner == current->files)
> +				i_have_this_lease = 1;
> +		}
> +	}
> +	if (!lease_conflict)
>  		goto out;
>  
> -	for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next)
> -		if (fl->fl_owner == current->files)
> -			i_have_this_lease = 1;
> -
>  	break_time = 0;
>  	if (lease_break_time > 0) {
>  		break_time = jiffies + lease_break_time * HZ;
> @@ -1222,6 +1237,8 @@ int __break_lease(struct inode *inode, unsigned int mode)
>  	}
>  
>  	for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) {
> +		if (!leases_conflict(fl, new_fl))
> +			continue;
>  		if (want_write) {
>  			if (fl->fl_flags & FL_UNLOCK_PENDING)
>  				continue;
> @@ -1263,7 +1280,7 @@ restart:
>  		 */
>  		for (flock = inode->i_flock; flock && IS_LEASE(flock);
>  				flock = flock->fl_next) {
> -			if (locks_conflict(new_fl, flock))
> +			if (leases_conflict(new_fl, flock))
>  				goto restart;
>  		}
>  		error = 0;
> @@ -1343,9 +1360,20 @@ int generic_add_lease(struct file *filp, long arg, struct file_lock **flp)
>  	struct file_lock *fl, **before, **my_before = NULL, *lease;
>  	struct dentry *dentry = filp->f_path.dentry;
>  	struct inode *inode = dentry->d_inode;
> +	bool is_deleg = (*flp)->fl_flags & FL_DELEG;
>  	int error;
>  
>  	lease = *flp;
> +	/*
> +	 * In the delegation case we need mutual exclusion with
> +	 * a number of operations that take the i_mutex.  We trylock
> +	 * because delegations are an optional optimization, and if
> +	 * there's some chance of a conflict--we'd rather not
> +	 * bother, maybe that's a sign this just isn't a good file to
> +	 * hand out a delegation on.
> +	 */
> +	if (is_deleg && !mutex_trylock(&inode->i_mutex))
> +		return -EAGAIN;
>  
>  	error = -EAGAIN;
>  	if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
> @@ -1397,9 +1425,10 @@ int generic_add_lease(struct file *filp, long arg, struct file_lock **flp)
>  		goto out;
>  
>  	locks_insert_lock(before, lease);
> -	return 0;
> -
> +	error = 0;
>  out:
> +	if (is_deleg)
> +		mutex_unlock(&inode->i_mutex);
>  	return error;
>  }
>  
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index 116b3e9..c6cc686 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -1006,7 +1006,7 @@ extern int vfs_test_lock(struct file *, struct file_lock *);
>  extern int vfs_lock_file(struct file *, unsigned int, struct file_lock *, struct file_lock *);
>  extern int vfs_cancel_lock(struct file *filp, struct file_lock *fl);
>  extern int flock_lock_file_wait(struct file *filp, struct file_lock *fl);
> -extern int __break_lease(struct inode *inode, unsigned int flags);
> +extern int __break_lease(struct inode *inode, unsigned int flags, unsigned int type);
>  extern void lease_get_mtime(struct inode *, struct timespec *time);
>  extern int generic_setlease(struct file *, long, struct file_lock **);
>  extern int vfs_setlease(struct file *, long, struct file_lock **);
> @@ -1119,7 +1119,7 @@ static inline int flock_lock_file_wait(struct file *filp,
>  	return -ENOLCK;
>  }
>  
> -static inline int __break_lease(struct inode *inode, unsigned int mode)
> +static inline int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
>  {
>  	return 0;
>  }
> @@ -1951,9 +1951,17 @@ static inline int locks_verify_truncate(struct inode *inode,
>  static inline int break_lease(struct inode *inode, unsigned int mode)
>  {
>  	if (inode->i_flock)
> -		return __break_lease(inode, mode);
> +		return __break_lease(inode, mode, FL_LEASE);
>  	return 0;
>  }
> +
> +static inline int break_deleg(struct inode *inode, unsigned int mode)
> +{
> +	if (inode->i_flock)
> +		return __break_lease(inode, mode, FL_DELEG);
> +	return 0;
> +}
> +
>  #else /* !CONFIG_FILE_LOCKING */
>  static inline int locks_mandatory_locked(struct inode *inode)
>  {
> @@ -1993,6 +2001,10 @@ static inline int break_lease(struct inode *inode, unsigned int mode)
>  	return 0;
>  }
>  
> +static inline int break_deleg(struct inode *inode, unsigned int mode)
> +{
> +	return 0;
> +}
>  #endif /* CONFIG_FILE_LOCKING */
>  
>  /* fs/open.c */

Looks reasonable...

This (of course) has the same potential race that Al ID'ed a few days
ago. We'll probably need to reconcile this patch with whatever fix we
come up with there, but it shouldn't be too difficult.

Acked-by: Jeff Layton <jlayton@xxxxxxxxxx>
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux