Merge branch 'server-cluster-locking-api' of git://linux-nfs.org/~bfields/linux
authorLinus Torvalds <torvalds@woody.linux-foundation.org>
Mon, 7 May 2007 19:34:24 +0000 (12:34 -0700)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Mon, 7 May 2007 19:34:24 +0000 (12:34 -0700)
* 'server-cluster-locking-api' of git://linux-nfs.org/~bfields/linux:
  gfs2: nfs lock support for gfs2
  lockd: add code to handle deferred lock requests
  lockd: always preallocate block in nlmsvc_lock()
  lockd: handle test_lock deferrals
  lockd: pass cookie in nlmsvc_testlock
  lockd: handle fl_grant callbacks
  lockd: save lock state on deferral
  locks: add fl_grant callback for asynchronous lock return
  nfsd4: Convert NFSv4 to new lock interface
  locks: add lock cancel command
  locks: allow {vfs,posix}_lock_file to return conflicting lock
  locks: factor out generic/filesystem switch from setlock code
  locks: factor out generic/filesystem switch from test_lock
  locks: give posix_test_lock same interface as ->lock
  locks: make ->lock release private data before returning in GETLK case
  locks: create posix-to-flock helper functions
  locks: trivial removal of unnecessary parentheses

1  2 
fs/locks.c
fs/nfs/nfs4proc.c
include/linux/fs.h
include/linux/lockd/lockd.h

diff --combined fs/locks.c
@@@ -203,7 -203,8 +203,7 @@@ static void init_once(void *foo, struc
  {
        struct file_lock *lock = (struct file_lock *) foo;
  
 -      if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) !=
 -                                      SLAB_CTOR_CONSTRUCTOR)
 +      if (!(flags & SLAB_CTOR_CONSTRUCTOR))
                return;
  
        locks_init_lock(lock);
@@@ -665,11 -666,11 +665,11 @@@ static int locks_block_on_timeout(struc
  }
  
  int
- posix_test_lock(struct file *filp, struct file_lock *fl,
-               struct file_lock *conflock)
+ posix_test_lock(struct file *filp, struct file_lock *fl)
  {
        struct file_lock *cfl;
  
+       fl->fl_type = F_UNLCK;
        lock_kernel();
        for (cfl = filp->f_path.dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) {
                if (!IS_POSIX(cfl))
                        break;
        }
        if (cfl) {
-               __locks_copy_lock(conflock, cfl);
+               __locks_copy_lock(fl, cfl);
                unlock_kernel();
                return 1;
        }
@@@ -800,7 -801,7 +800,7 @@@ out
        return error;
  }
  
- static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request, struct file_lock *conflock)
+ static int __posix_lock_file(struct inode *inode, struct file_lock *request, struct file_lock *conflock)
  {
        struct file_lock *fl;
        struct file_lock *new_fl = NULL;
   * posix_lock_file - Apply a POSIX-style lock to a file
   * @filp: The file to apply the lock to
   * @fl: The lock to be applied
+  * @conflock: Place to return a copy of the conflicting lock, if found.
   *
   * Add a POSIX style lock to a file.
   * We merge adjacent & overlapping locks whenever possible.
   * whether or not a lock was successfully freed by testing the return
   * value for -ENOENT.
   */
- int posix_lock_file(struct file *filp, struct file_lock *fl)
- {
-       return __posix_lock_file_conf(filp->f_path.dentry->d_inode, fl, NULL);
- }
- EXPORT_SYMBOL(posix_lock_file);
- /**
-  * posix_lock_file_conf - Apply a POSIX-style lock to a file
-  * @filp: The file to apply the lock to
-  * @fl: The lock to be applied
-  * @conflock: Place to return a copy of the conflicting lock, if found.
-  *
-  * Except for the conflock parameter, acts just like posix_lock_file.
-  */
- int posix_lock_file_conf(struct file *filp, struct file_lock *fl,
+ int posix_lock_file(struct file *filp, struct file_lock *fl,
                        struct file_lock *conflock)
  {
-       return __posix_lock_file_conf(filp->f_path.dentry->d_inode, fl, conflock);
+       return __posix_lock_file(filp->f_path.dentry->d_inode, fl, conflock);
  }
- EXPORT_SYMBOL(posix_lock_file_conf);
+ EXPORT_SYMBOL(posix_lock_file);
  
  /**
   * posix_lock_file_wait - Apply a POSIX-style lock to a file
@@@ -1050,7 -1038,7 +1037,7 @@@ int posix_lock_file_wait(struct file *f
        int error;
        might_sleep ();
        for (;;) {
-               error = posix_lock_file(filp, fl);
+               error = posix_lock_file(filp, fl, NULL);
                if ((error != -EAGAIN) || !(fl->fl_flags & FL_SLEEP))
                        break;
                error = wait_event_interruptible(fl->fl_wait, !fl->fl_next);
@@@ -1122,7 -1110,7 +1109,7 @@@ int locks_mandatory_area(int read_write
        fl.fl_end = offset + count - 1;
  
        for (;;) {
-               error = __posix_lock_file_conf(inode, &fl, NULL);
+               error = __posix_lock_file(inode, &fl, NULL);
                if (error != -EAGAIN)
                        break;
                if (!(fl.fl_flags & FL_SLEEP))
@@@ -1610,12 -1598,62 +1597,62 @@@ asmlinkage long sys_flock(unsigned int 
        return error;
  }
  
+ /**
+  * vfs_test_lock - test file byte range lock
+  * @filp: The file to test lock for
+  * @fl: The lock to test
+  * @conf: Place to return a copy of the conflicting lock, if found
+  *
+  * Returns -ERRNO on failure.  Indicates presence of conflicting lock by
+  * setting conf->fl_type to something other than F_UNLCK.
+  */
+ int vfs_test_lock(struct file *filp, struct file_lock *fl)
+ {
+       if (filp->f_op && filp->f_op->lock)
+               return filp->f_op->lock(filp, F_GETLK, fl);
+       posix_test_lock(filp, fl);
+       return 0;
+ }
+ EXPORT_SYMBOL_GPL(vfs_test_lock);
+ static int posix_lock_to_flock(struct flock *flock, struct file_lock *fl)
+ {
+       flock->l_pid = fl->fl_pid;
+ #if BITS_PER_LONG == 32
+       /*
+        * Make sure we can represent the posix lock via
+        * legacy 32bit flock.
+        */
+       if (fl->fl_start > OFFT_OFFSET_MAX)
+               return -EOVERFLOW;
+       if (fl->fl_end != OFFSET_MAX && fl->fl_end > OFFT_OFFSET_MAX)
+               return -EOVERFLOW;
+ #endif
+       flock->l_start = fl->fl_start;
+       flock->l_len = fl->fl_end == OFFSET_MAX ? 0 :
+               fl->fl_end - fl->fl_start + 1;
+       flock->l_whence = 0;
+       return 0;
+ }
+ #if BITS_PER_LONG == 32
+ static void posix_lock_to_flock64(struct flock64 *flock, struct file_lock *fl)
+ {
+       flock->l_pid = fl->fl_pid;
+       flock->l_start = fl->fl_start;
+       flock->l_len = fl->fl_end == OFFSET_MAX ? 0 :
+               fl->fl_end - fl->fl_start + 1;
+       flock->l_whence = 0;
+       flock->l_type = fl->fl_type;
+ }
+ #endif
  /* Report the first existing lock that would conflict with l.
   * This implements the F_GETLK command of fcntl().
   */
  int fcntl_getlk(struct file *filp, struct flock __user *l)
  {
-       struct file_lock *fl, cfl, file_lock;
+       struct file_lock file_lock;
        struct flock flock;
        int error;
  
        if (error)
                goto out;
  
-       if (filp->f_op && filp->f_op->lock) {
-               error = filp->f_op->lock(filp, F_GETLK, &file_lock);
-               if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private)
-                       file_lock.fl_ops->fl_release_private(&file_lock);
-               if (error < 0)
-                       goto out;
-               else
-                 fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
-       } else {
-               fl = (posix_test_lock(filp, &file_lock, &cfl) ? &cfl : NULL);
-       }
+       error = vfs_test_lock(filp, &file_lock);
+       if (error)
+               goto out;
   
-       flock.l_type = F_UNLCK;
-       if (fl != NULL) {
-               flock.l_pid = fl->fl_pid;
- #if BITS_PER_LONG == 32
-               /*
-                * Make sure we can represent the posix lock via
-                * legacy 32bit flock.
-                */
-               error = -EOVERFLOW;
-               if (fl->fl_start > OFFT_OFFSET_MAX)
-                       goto out;
-               if ((fl->fl_end != OFFSET_MAX)
-                   && (fl->fl_end > OFFT_OFFSET_MAX))
+       flock.l_type = file_lock.fl_type;
+       if (file_lock.fl_type != F_UNLCK) {
+               error = posix_lock_to_flock(&flock, &file_lock);
+               if (error)
                        goto out;
- #endif
-               flock.l_start = fl->fl_start;
-               flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
-                       fl->fl_end - fl->fl_start + 1;
-               flock.l_whence = 0;
-               flock.l_type = fl->fl_type;
        }
        error = -EFAULT;
        if (!copy_to_user(l, &flock, sizeof(flock)))
        return error;
  }
  
+ /**
+  * vfs_lock_file - file byte range lock
+  * @filp: The file to apply the lock to
+  * @cmd: type of locking operation (F_SETLK, F_GETLK, etc.)
+  * @fl: The lock to be applied
+  * @conf: Place to return a copy of the conflicting lock, if found.
+  *
+  * A caller that doesn't care about the conflicting lock may pass NULL
+  * as the final argument.
+  *
+  * If the filesystem defines a private ->lock() method, then @conf will
+  * be left unchanged; so a caller that cares should initialize it to
+  * some acceptable default.
+  *
+  * To avoid blocking kernel daemons, such as lockd, that need to acquire POSIX
+  * locks, the ->lock() interface may return asynchronously, before the lock has
+  * been granted or denied by the underlying filesystem, if (and only if)
+  * fl_grant is set. Callers expecting ->lock() to return asynchronously
+  * will only use F_SETLK, not F_SETLKW; they will set FL_SLEEP if (and only if)
+  * the request is for a blocking lock. When ->lock() does return asynchronously,
+  * it must return -EINPROGRESS, and call ->fl_grant() when the lock
+  * request completes.
+  * If the request is for non-blocking lock the file system should return
+  * -EINPROGRESS then try to get the lock and call the callback routine with
+  * the result. If the request timed out the callback routine will return a
+  * nonzero return code and the file system should release the lock. The file
+  * system is also responsible to keep a corresponding posix lock when it
+  * grants a lock so the VFS can find out which locks are locally held and do
+  * the correct lock cleanup when required.
+  * The underlying filesystem must not drop the kernel lock or call
+  * ->fl_grant() before returning to the caller with a -EINPROGRESS
+  * return code.
+  */
+ int vfs_lock_file(struct file *filp, unsigned int cmd, struct file_lock *fl, struct file_lock *conf)
+ {
+       if (filp->f_op && filp->f_op->lock)
+               return filp->f_op->lock(filp, cmd, fl);
+       else
+               return posix_lock_file(filp, fl, conf);
+ }
+ EXPORT_SYMBOL_GPL(vfs_lock_file);
  /* Apply the lock described by l to an open file descriptor.
   * This implements both the F_SETLK and F_SETLKW commands of fcntl().
   */
@@@ -1732,21 -1789,17 +1788,17 @@@ again
        if (error)
                goto out;
  
-       if (filp->f_op && filp->f_op->lock != NULL)
-               error = filp->f_op->lock(filp, cmd, file_lock);
-       else {
-               for (;;) {
-                       error = posix_lock_file(filp, file_lock);
-                       if ((error != -EAGAIN) || (cmd == F_SETLK))
-                               break;
-                       error = wait_event_interruptible(file_lock->fl_wait,
-                                       !file_lock->fl_next);
-                       if (!error)
-                               continue;
-                       locks_delete_block(file_lock);
+       for (;;) {
+               error = vfs_lock_file(filp, cmd, file_lock, NULL);
+               if (error != -EAGAIN || cmd == F_SETLK)
                        break;
-               }
+               error = wait_event_interruptible(file_lock->fl_wait,
+                               !file_lock->fl_next);
+               if (!error)
+                       continue;
+               locks_delete_block(file_lock);
+               break;
        }
  
        /*
@@@ -1769,7 -1822,7 +1821,7 @@@ out
   */
  int fcntl_getlk64(struct file *filp, struct flock64 __user *l)
  {
-       struct file_lock *fl, cfl, file_lock;
+       struct file_lock file_lock;
        struct flock64 flock;
        int error;
  
        if (error)
                goto out;
  
-       if (filp->f_op && filp->f_op->lock) {
-               error = filp->f_op->lock(filp, F_GETLK, &file_lock);
-               if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private)
-                       file_lock.fl_ops->fl_release_private(&file_lock);
-               if (error < 0)
-                       goto out;
-               else
-                 fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
-       } else {
-               fl = (posix_test_lock(filp, &file_lock, &cfl) ? &cfl : NULL);
-       }
-  
-       flock.l_type = F_UNLCK;
-       if (fl != NULL) {
-               flock.l_pid = fl->fl_pid;
-               flock.l_start = fl->fl_start;
-               flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
-                       fl->fl_end - fl->fl_start + 1;
-               flock.l_whence = 0;
-               flock.l_type = fl->fl_type;
-       }
+       error = vfs_test_lock(filp, &file_lock);
+       if (error)
+               goto out;
+       flock.l_type = file_lock.fl_type;
+       if (file_lock.fl_type != F_UNLCK)
+               posix_lock_to_flock64(&flock, &file_lock);
        error = -EFAULT;
        if (!copy_to_user(l, &flock, sizeof(flock)))
                error = 0;
@@@ -1875,21 -1915,17 +1914,17 @@@ again
        if (error)
                goto out;
  
-       if (filp->f_op && filp->f_op->lock != NULL)
-               error = filp->f_op->lock(filp, cmd, file_lock);
-       else {
-               for (;;) {
-                       error = posix_lock_file(filp, file_lock);
-                       if ((error != -EAGAIN) || (cmd == F_SETLK64))
-                               break;
-                       error = wait_event_interruptible(file_lock->fl_wait,
-                                       !file_lock->fl_next);
-                       if (!error)
-                               continue;
-                       locks_delete_block(file_lock);
+       for (;;) {
+               error = vfs_lock_file(filp, cmd, file_lock, NULL);
+               if (error != -EAGAIN || cmd == F_SETLK64)
                        break;
-               }
+               error = wait_event_interruptible(file_lock->fl_wait,
+                               !file_lock->fl_next);
+               if (!error)
+                       continue;
+               locks_delete_block(file_lock);
+               break;
        }
  
        /*
@@@ -1934,10 -1970,7 +1969,7 @@@ void locks_remove_posix(struct file *fi
        lock.fl_ops = NULL;
        lock.fl_lmops = NULL;
  
-       if (filp->f_op && filp->f_op->lock != NULL)
-               filp->f_op->lock(filp, F_SETLK, &lock);
-       else
-               posix_lock_file(filp, &lock);
+       vfs_lock_file(filp, F_SETLK, &lock, NULL);
  
        if (lock.fl_ops && lock.fl_ops->fl_release_private)
                lock.fl_ops->fl_release_private(&lock);
@@@ -2014,6 -2047,22 +2046,22 @@@ posix_unblock_lock(struct file *filp, s
  
  EXPORT_SYMBOL(posix_unblock_lock);
  
+ /**
+  * vfs_cancel_lock - file byte range unblock lock
+  * @filp: The file to apply the unblock to
+  * @fl: The lock to be unblocked
+  *
+  * Used by lock managers to cancel blocked requests
+  */
+ int vfs_cancel_lock(struct file *filp, struct file_lock *fl)
+ {
+       if (filp->f_op && filp->f_op->lock)
+               return filp->f_op->lock(filp, F_CANCELLK, fl);
+       return 0;
+ }
+ EXPORT_SYMBOL_GPL(vfs_cancel_lock);
  static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx)
  {
        struct inode *inode = NULL;
diff --combined fs/nfs/nfs4proc.c
@@@ -2647,7 -2647,8 +2647,7 @@@ static int __nfs4_proc_set_acl(struct i
        nfs_inode_return_delegation(inode);
        buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
        ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
 -      if (ret == 0)
 -              nfs4_write_cached_acl(inode, buf, buflen);
 +      nfs_zap_caches(inode);
        return ret;
  }
  
@@@ -3017,6 -3018,7 +3017,7 @@@ static int _nfs4_proc_getlk(struct nfs4
                case -NFS4ERR_DENIED:
                        status = 0;
        }
+       request->fl_ops->fl_release_private(request);
  out:
        up_read(&clp->cl_sem);
        return status;
diff --combined include/linux/fs.h
@@@ -696,13 -696,12 +696,13 @@@ struct file_ra_state 
        unsigned long size;
        unsigned long flags;            /* ra flags RA_FLAG_xxx*/
        unsigned long cache_hit;        /* cache hit count*/
 -      unsigned long prev_page;        /* Cache last read() position */
 +      unsigned long prev_index;       /* Cache last read() position */
        unsigned long ahead_start;      /* Ahead window */
        unsigned long ahead_size;
        unsigned long ra_pages;         /* Maximum readahead window */
        unsigned long mmap_hit;         /* Cache hit stat for mmap accesses */
        unsigned long mmap_miss;        /* Cache miss stat for mmap accesses */
 +      unsigned int prev_offset;       /* Offset where last read() ended in a page */
  };
  #define RA_FLAG_MISS 0x01     /* a cache miss occured against this file */
  #define RA_FLAG_INCACHE 0x02  /* file is already in cache */
@@@ -786,6 -785,7 +786,7 @@@ struct file_lock_operations 
  struct lock_manager_operations {
        int (*fl_compare_owner)(struct file_lock *, struct file_lock *);
        void (*fl_notify)(struct file_lock *);  /* unblock callback */
+       int (*fl_grant)(struct file_lock *, struct file_lock *, int);
        void (*fl_copy_lock)(struct file_lock *, struct file_lock *);
        void (*fl_release_private)(struct file_lock *);
        void (*fl_break)(struct file_lock *);
@@@ -844,24 -844,21 +845,26 @@@ extern int fcntl_setlease(unsigned int 
  extern int fcntl_getlease(struct file *filp);
  
  /* fs/sync.c */
 -extern int do_sync_file_range(struct file *file, loff_t offset, loff_t endbyte,
 -                      unsigned int flags);
 +extern int do_sync_mapping_range(struct address_space *mapping, loff_t offset,
 +                      loff_t endbyte, unsigned int flags);
 +static inline int do_sync_file_range(struct file *file, loff_t offset,
 +                      loff_t endbyte, unsigned int flags)
 +{
 +      return do_sync_mapping_range(file->f_mapping, offset, endbyte, flags);
 +}
  
  /* fs/locks.c */
  extern void locks_init_lock(struct file_lock *);
  extern void locks_copy_lock(struct file_lock *, struct file_lock *);
  extern void locks_remove_posix(struct file *, fl_owner_t);
  extern void locks_remove_flock(struct file *);
- extern int posix_test_lock(struct file *, struct file_lock *, struct file_lock *);
- extern int posix_lock_file_conf(struct file *, struct file_lock *, struct file_lock *);
- extern int posix_lock_file(struct file *, struct file_lock *);
+ extern int posix_test_lock(struct file *, struct file_lock *);
+ extern int posix_lock_file(struct file *, struct file_lock *, struct file_lock *);
  extern int posix_lock_file_wait(struct file *, struct file_lock *);
  extern int posix_unblock_lock(struct file *, struct file_lock *);
+ 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 void lease_get_mtime(struct inode *, struct timespec *time);
@@@ -1417,7 -1414,7 +1420,7 @@@ extern void mnt_set_mountpoint(struct v
  extern int vfs_statfs(struct dentry *, struct kstatfs *);
  
  /* /sys/fs */
 -extern struct subsystem fs_subsys;
 +extern struct kset fs_subsys;
  
  #define FLOCK_VERIFY_READ  1
  #define FLOCK_VERIFY_WRITE 2
@@@ -88,7 -88,7 +88,7 @@@ struct nlm_wait
  /*
   * Memory chunk for NLM client RPC request.
   */
 -#define NLMCLNT_OHSIZE                (sizeof(utsname()->nodename)+10)
 +#define NLMCLNT_OHSIZE                ((__NEW_UTS_LEN) + 10u)
  struct nlm_rqst {
        unsigned int            a_flags;        /* initial RPC task flags */
        struct nlm_host *       a_host;         /* host handle */
@@@ -119,6 -119,9 +119,9 @@@ struct nlm_file 
   * couldn't be granted because of a conflicting lock).
   */
  #define NLM_NEVER             (~(unsigned long) 0)
+ /* timeout on non-blocking call: */
+ #define NLM_TIMEOUT           (7 * HZ)
  struct nlm_block {
        struct kref             b_count;        /* Reference count */
        struct list_head        b_list;         /* linked list of all blocks */
        unsigned int            b_id;           /* block id */
        unsigned char           b_granted;      /* VFS granted lock */
        struct nlm_file *       b_file;         /* file in question */
+       struct cache_req *      b_cache_req;    /* deferred request handling */
+       struct file_lock *      b_fl;           /* set for GETLK */
+       struct cache_deferred_req * b_deferred_req;
+       unsigned int            b_flags;        /* block flags */
+ #define B_QUEUED              1       /* lock queued */
+ #define B_GOT_CALLBACK                2       /* got lock or conflicting lock */
+ #define B_TIMED_OUT           4       /* filesystem too slow to respond */
  };
  
  /*
@@@ -185,8 -195,8 +195,8 @@@ typedef int          (*nlm_host_match_fn_t)(st
  __be32                  nlmsvc_lock(struct svc_rqst *, struct nlm_file *,
                                        struct nlm_lock *, int, struct nlm_cookie *);
  __be32                  nlmsvc_unlock(struct nlm_file *, struct nlm_lock *);
- __be32                  nlmsvc_testlock(struct nlm_file *, struct nlm_lock *,
-                                       struct nlm_lock *);
+ __be32                  nlmsvc_testlock(struct svc_rqst *, struct nlm_file *,
+                       struct nlm_lock *, struct nlm_lock *, struct nlm_cookie *);
  __be32                  nlmsvc_cancel_blocked(struct nlm_file *, struct nlm_lock *);
  unsigned long   nlmsvc_retry_blocked(void);
  void            nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *,