Merge tag 'locks-v3.18-1' of git://git.samba.org/jlayton/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 11 Oct 2014 17:21:34 +0000 (13:21 -0400)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 11 Oct 2014 17:21:34 +0000 (13:21 -0400)
Pull file locking related changes from Jeff Layton:
 "This release is a little more busy for file locking changes than the
  last:

   - a set of patches from Kinglong Mee to fix the lockowner handling in
     knfsd
   - a pile of cleanups to the internal file lease API.  This should get
     us a bit closer to allowing for setlease methods that can block.

  There are some dependencies between mine and Bruce's trees this cycle,
  and I based my tree on top of the requisite patches in Bruce's tree"

* tag 'locks-v3.18-1' of git://git.samba.org/jlayton/linux: (26 commits)
  locks: fix fcntl_setlease/getlease return when !CONFIG_FILE_LOCKING
  locks: flock_make_lock should return a struct file_lock (or PTR_ERR)
  locks: set fl_owner for leases to filp instead of current->files
  locks: give lm_break a return value
  locks: __break_lease cleanup in preparation of allowing direct removal of leases
  locks: remove i_have_this_lease check from __break_lease
  locks: move freeing of leases outside of i_lock
  locks: move i_lock acquisition into generic_*_lease handlers
  locks: define a lm_setup handler for leases
  locks: plumb a "priv" pointer into the setlease routines
  nfsd: don't keep a pointer to the lease in nfs4_file
  locks: clean up vfs_setlease kerneldoc comments
  locks: generic_delete_lease doesn't need a file_lock at all
  nfsd: fix potential lease memory leak in nfs4_setlease
  locks: close potential race in lease_get_mtime
  security: make security_file_set_fowner, f_setown and __f_setown void return
  locks: consolidate "nolease" routines
  locks: remove lock_may_read and lock_may_write
  lockd: rip out deferred lock handling from testlock codepath
  NFSD: Get reference of lockowner when coping file_lock
  ...

26 files changed:
Documentation/filesystems/Locking
Documentation/filesystems/vfs.txt
drivers/net/tun.c
drivers/tty/tty_io.c
fs/cifs/cifsfs.c
fs/dlm/plock.c
fs/fcntl.c
fs/gfs2/file.c
fs/libfs.c
fs/lockd/svclock.c
fs/locks.c
fs/nfs/file.c
fs/nfs/internal.h
fs/nfs/nfs4file.c
fs/nfsd/nfs4state.c
fs/nfsd/state.h
fs/notify/dnotify/dnotify.c
include/linux/fs.h
include/linux/lockd/lockd.h
include/linux/security.h
include/trace/events/filelock.h
net/socket.c
security/capability.c
security/security.c
security/selinux/hooks.c
security/smack/smack_lsm.c

index f1997e9..94d93b1 100644 (file)
@@ -464,15 +464,12 @@ prototypes:
                        size_t, unsigned int);
        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *,
                        size_t, unsigned int);
-       int (*setlease)(struct file *, long, struct file_lock **);
+       int (*setlease)(struct file *, long, struct file_lock **, void **);
        long (*fallocate)(struct file *, int, loff_t, loff_t);
 };
 
 locking rules:
-       All may block except for ->setlease.
-       No VFS locks held on entry except for ->setlease.
-
-->setlease has the file_list_lock held and must not sleep.
+       All may block.
 
 ->llseek() locking has moved from llseek to the individual llseek
 implementations.  If your fs is not using generic_file_llseek, you
@@ -496,6 +493,10 @@ components. And there are other reasons why the current interface is a mess...
 ->read on directories probably must go away - we should just enforce -EISDIR
 in sys_read() and friends.
 
+->setlease operations should call generic_setlease() before or after setting
+the lease within the individual filesystem to record the result of the
+operation
+
 --------------------------- dquot_operations -------------------------------
 prototypes:
        int (*write_dquot) (struct dquot *);
index 61d65cc..8be1ea3 100644 (file)
@@ -826,7 +826,7 @@ struct file_operations {
        int (*flock) (struct file *, int, struct file_lock *);
        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, size_t, unsigned int);
        ssize_t (*splice_read)(struct file *, struct pipe_inode_info *, size_t, unsigned int);
-       int (*setlease)(struct file *, long arg, struct file_lock **);
+       int (*setlease)(struct file *, long arg, struct file_lock **, void **);
        long (*fallocate)(struct file *, int mode, loff_t offset, loff_t len);
        int (*show_fdinfo)(struct seq_file *m, struct file *f);
 };
@@ -895,8 +895,9 @@ otherwise noted.
   splice_read: called by the VFS to splice data from file to a pipe. This
               method is used by the splice(2) system call
 
-  setlease: called by the VFS to set or release a file lock lease.
-           setlease has the file_lock_lock held and must not sleep.
+  setlease: called by the VFS to set or release a file lock lease. setlease
+           implementations should call generic_setlease to record or remove
+           the lease in the inode after setting it.
 
   fallocate: called by the VFS to preallocate blocks or punch a hole.
 
index acaaf67..186ce54 100644 (file)
@@ -2152,9 +2152,7 @@ static int tun_chr_fasync(int fd, struct file *file, int on)
                goto out;
 
        if (on) {
-               ret = __f_setown(file, task_pid(current), PIDTYPE_PID, 0);
-               if (ret)
-                       goto out;
+               __f_setown(file, task_pid(current), PIDTYPE_PID, 0);
                tfile->flags |= TUN_FASYNC;
        } else
                tfile->flags &= ~TUN_FASYNC;
index 2f6f9b5..16a2c02 100644 (file)
@@ -2186,8 +2186,9 @@ static int __tty_fasync(int fd, struct file *filp, int on)
                }
                get_pid(pid);
                spin_unlock_irqrestore(&tty->ctrl_lock, flags);
-               retval = __f_setown(filp, pid, type, 0);
+               __f_setown(filp, pid, type, 0);
                put_pid(pid);
+               retval = 0;
        }
 out:
        return retval;
index 889b984..9d7996e 100644 (file)
@@ -813,7 +813,8 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int whence)
        return generic_file_llseek(file, offset, whence);
 }
 
-static int cifs_setlease(struct file *file, long arg, struct file_lock **lease)
+static int
+cifs_setlease(struct file *file, long arg, struct file_lock **lease, void **priv)
 {
        /*
         * Note that this is called by vfs setlease with i_lock held to
@@ -829,7 +830,7 @@ static int cifs_setlease(struct file *file, long arg, struct file_lock **lease)
        if (arg == F_UNLCK ||
            ((arg == F_RDLCK) && CIFS_CACHE_READ(CIFS_I(inode))) ||
            ((arg == F_WRLCK) && CIFS_CACHE_WRITE(CIFS_I(inode))))
-               return generic_setlease(file, arg, lease);
+               return generic_setlease(file, arg, lease, priv);
        else if (tlink_tcon(cfile->tlink)->local_lease &&
                 !CIFS_CACHE_READ(CIFS_I(inode)))
                /*
@@ -840,7 +841,7 @@ static int cifs_setlease(struct file *file, long arg, struct file_lock **lease)
                 * knows that the file won't be changed on the server by anyone
                 * else.
                 */
-               return generic_setlease(file, arg, lease);
+               return generic_setlease(file, arg, lease, priv);
        else
                return -EAGAIN;
 }
index f704458..e0ab3a9 100644 (file)
@@ -30,7 +30,7 @@ struct plock_op {
 
 struct plock_xop {
        struct plock_op xop;
-       void *callback;
+       int (*callback)(struct file_lock *fl, int result);
        void *fl;
        void *file;
        struct file_lock flc;
@@ -190,7 +190,7 @@ static int dlm_plock_callback(struct plock_op *op)
        struct file *file;
        struct file_lock *fl;
        struct file_lock *flc;
-       int (*notify)(void *, void *, int) = NULL;
+       int (*notify)(struct file_lock *fl, int result) = NULL;
        struct plock_xop *xop = (struct plock_xop *)op;
        int rv = 0;
 
@@ -209,7 +209,7 @@ static int dlm_plock_callback(struct plock_op *op)
        notify = xop->callback;
 
        if (op->info.rv) {
-               notify(fl, NULL, op->info.rv);
+               notify(fl, op->info.rv);
                goto out;
        }
 
@@ -228,7 +228,7 @@ static int dlm_plock_callback(struct plock_op *op)
                          (unsigned long long)op->info.number, file, fl);
        }
 
-       rv = notify(fl, NULL, 0);
+       rv = notify(fl, 0);
        if (rv) {
                /* XXX: We need to cancel the fs lock here: */
                log_print("dlm_plock_callback: lock granted after lock request "
index 22d1c3d..99d440a 100644 (file)
@@ -98,26 +98,19 @@ static void f_modown(struct file *filp, struct pid *pid, enum pid_type type,
        write_unlock_irq(&filp->f_owner.lock);
 }
 
-int __f_setown(struct file *filp, struct pid *pid, enum pid_type type,
+void __f_setown(struct file *filp, struct pid *pid, enum pid_type type,
                int force)
 {
-       int err;
-
-       err = security_file_set_fowner(filp);
-       if (err)
-               return err;
-
+       security_file_set_fowner(filp);
        f_modown(filp, pid, type, force);
-       return 0;
 }
 EXPORT_SYMBOL(__f_setown);
 
-int f_setown(struct file *filp, unsigned long arg, int force)
+void f_setown(struct file *filp, unsigned long arg, int force)
 {
        enum pid_type type;
        struct pid *pid;
        int who = arg;
-       int result;
        type = PIDTYPE_PID;
        if (who < 0) {
                type = PIDTYPE_PGID;
@@ -125,9 +118,8 @@ int f_setown(struct file *filp, unsigned long arg, int force)
        }
        rcu_read_lock();
        pid = find_vpid(who);
-       result = __f_setown(filp, pid, type, force);
+       __f_setown(filp, pid, type, force);
        rcu_read_unlock();
-       return result;
 }
 EXPORT_SYMBOL(f_setown);
 
@@ -181,7 +173,7 @@ static int f_setown_ex(struct file *filp, unsigned long arg)
        if (owner.pid && !pid)
                ret = -ESRCH;
        else
-               ret = __f_setown(filp, pid, type, 1);
+                __f_setown(filp, pid, type, 1);
        rcu_read_unlock();
 
        return ret;
@@ -302,7 +294,8 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
                force_successful_syscall_return();
                break;
        case F_SETOWN:
-               err = f_setown(filp, arg, 1);
+               f_setown(filp, arg, 1);
+               err = 0;
                break;
        case F_GETOWN_EX:
                err = f_getown_ex(filp, arg);
index 7f4ed3d..80dd44d 100644 (file)
@@ -913,26 +913,6 @@ out_uninit:
 
 #ifdef CONFIG_GFS2_FS_LOCKING_DLM
 
-/**
- * gfs2_setlease - acquire/release a file lease
- * @file: the file pointer
- * @arg: lease type
- * @fl: file lock
- *
- * We don't currently have a way to enforce a lease across the whole
- * cluster; until we do, disable leases (by just returning -EINVAL),
- * unless the administrator has requested purely local locking.
- *
- * Locking: called under i_lock
- *
- * Returns: errno
- */
-
-static int gfs2_setlease(struct file *file, long arg, struct file_lock **fl)
-{
-       return -EINVAL;
-}
-
 /**
  * gfs2_lock - acquire/release a posix lock on a file
  * @file: the file pointer
@@ -1078,7 +1058,7 @@ const struct file_operations gfs2_file_fops = {
        .flock          = gfs2_flock,
        .splice_read    = generic_file_splice_read,
        .splice_write   = iter_file_splice_write,
-       .setlease       = gfs2_setlease,
+       .setlease       = simple_nosetlease,
        .fallocate      = gfs2_fallocate,
 };
 
index 88e3e00..171d284 100644 (file)
@@ -1075,3 +1075,21 @@ struct inode *alloc_anon_inode(struct super_block *s)
        return inode;
 }
 EXPORT_SYMBOL(alloc_anon_inode);
+
+/**
+ * simple_nosetlease - generic helper for prohibiting leases
+ * @filp: file pointer
+ * @arg: type of lease to obtain
+ * @flp: new lease supplied for insertion
+ * @priv: private data for lm_setup operation
+ *
+ * Generic helper for filesystems that do not wish to allow leases to be set.
+ * All arguments are ignored and it just returns -EINVAL.
+ */
+int
+simple_nosetlease(struct file *filp, long arg, struct file_lock **flp,
+                 void **priv)
+{
+       return -EINVAL;
+}
+EXPORT_SYMBOL(simple_nosetlease);
index ab798a8..13db95f 100644 (file)
@@ -245,7 +245,6 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_host *host,
        block->b_daemon = rqstp->rq_server;
        block->b_host   = host;
        block->b_file   = file;
-       block->b_fl = NULL;
        file->f_count++;
 
        /* Add to file's list of blocks */
@@ -295,7 +294,6 @@ static void nlmsvc_free_block(struct kref *kref)
        nlmsvc_freegrantargs(block->b_call);
        nlmsvc_release_call(block->b_call);
        nlm_release_file(block->b_file);
-       kfree(block->b_fl);
        kfree(block);
 }
 
@@ -508,7 +506,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
                struct nlm_host *host, struct nlm_lock *lock,
                struct nlm_lock *conflock, struct nlm_cookie *cookie)
 {
-       struct nlm_block        *block = NULL;
        int                     error;
        __be32                  ret;
 
@@ -519,63 +516,26 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
                                (long long)lock->fl.fl_start,
                                (long long)lock->fl.fl_end);
 
-       /* Get existing block (in case client is busy-waiting) */
-       block = nlmsvc_lookup_block(file, lock);
-
-       if (block == NULL) {
-               struct file_lock *conf = kzalloc(sizeof(*conf), GFP_KERNEL);
-
-               if (conf == NULL)
-                       return nlm_granted;
-               block = nlmsvc_create_block(rqstp, host, file, lock, cookie);
-               if (block == NULL) {
-                       kfree(conf);
-                       return nlm_granted;
-               }
-               block->b_fl = conf;
-       }
-       if (block->b_flags & B_QUEUED) {
-               dprintk("lockd: nlmsvc_testlock deferred block %p flags %d fl %p\n",
-                       block, block->b_flags, block->b_fl);
-               if (block->b_flags & B_TIMED_OUT) {
-                       nlmsvc_unlink_block(block);
-                       ret = nlm_lck_denied;
-                       goto out;
-               }
-               if (block->b_flags & B_GOT_CALLBACK) {
-                       nlmsvc_unlink_block(block);
-                       if (block->b_fl != NULL
-                                       && block->b_fl->fl_type != F_UNLCK) {
-                               lock->fl = *block->b_fl;
-                               goto conf_lock;
-                       } else {
-                               ret = nlm_granted;
-                               goto out;
-                       }
-               }
-               ret = nlm_drop_reply;
-               goto out;
-       }
-
        if (locks_in_grace(SVC_NET(rqstp))) {
                ret = nlm_lck_denied_grace_period;
                goto out;
        }
+
        error = vfs_test_lock(file->f_file, &lock->fl);
-       if (error == FILE_LOCK_DEFERRED) {
-               ret = nlmsvc_defer_lock_rqst(rqstp, block);
-               goto out;
-       }
        if (error) {
+               /* We can't currently deal with deferred test requests */
+               if (error == FILE_LOCK_DEFERRED)
+                       WARN_ON_ONCE(1);
+
                ret = nlm_lck_denied_nolocks;
                goto out;
        }
+
        if (lock->fl.fl_type == F_UNLCK) {
                ret = nlm_granted;
                goto out;
        }
 
-conf_lock:
        dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n",
                lock->fl.fl_type, (long long)lock->fl.fl_start,
                (long long)lock->fl.fl_end);
@@ -586,10 +546,9 @@ conf_lock:
        conflock->fl.fl_type = lock->fl.fl_type;
        conflock->fl.fl_start = lock->fl.fl_start;
        conflock->fl.fl_end = lock->fl.fl_end;
+       locks_release_private(&lock->fl);
        ret = nlm_lck_denied;
 out:
-       if (block)
-               nlmsvc_release_block(block);
        return ret;
 }
 
@@ -660,29 +619,22 @@ nlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct nlm_lock *l
  * This is a callback from the filesystem for VFS file lock requests.
  * It will be used if lm_grant is defined and the filesystem can not
  * respond to the request immediately.
- * For GETLK request it will copy the reply to the nlm_block.
  * For SETLK or SETLKW request it will get the local posix lock.
  * In all cases it will move the block to the head of nlm_blocked q where
  * nlmsvc_retry_blocked() can send back a reply for SETLKW or revisit the
  * deferred rpc for GETLK and SETLK.
  */
 static void
-nlmsvc_update_deferred_block(struct nlm_block *block, struct file_lock *conf,
-                            int result)
+nlmsvc_update_deferred_block(struct nlm_block *block, int result)
 {
        block->b_flags |= B_GOT_CALLBACK;
        if (result == 0)
                block->b_granted = 1;
        else
                block->b_flags |= B_TIMED_OUT;
-       if (conf) {
-               if (block->b_fl)
-                       __locks_copy_lock(block->b_fl, conf);
-       }
 }
 
-static int nlmsvc_grant_deferred(struct file_lock *fl, struct file_lock *conf,
-                                       int result)
+static int nlmsvc_grant_deferred(struct file_lock *fl, int result)
 {
        struct nlm_block *block;
        int rc = -ENOENT;
@@ -697,7 +649,7 @@ static int nlmsvc_grant_deferred(struct file_lock *fl, struct file_lock *conf,
                                        rc = -ENOLCK;
                                        break;
                                }
-                               nlmsvc_update_deferred_block(block, conf, result);
+                               nlmsvc_update_deferred_block(block, result);
                        } else if (result == 0)
                                block->b_granted = 1;
 
index bb08857..735b8d3 100644 (file)
@@ -230,8 +230,12 @@ void locks_release_private(struct file_lock *fl)
                        fl->fl_ops->fl_release_private(fl);
                fl->fl_ops = NULL;
        }
-       fl->fl_lmops = NULL;
 
+       if (fl->fl_lmops) {
+               if (fl->fl_lmops->lm_put_owner)
+                       fl->fl_lmops->lm_put_owner(fl);
+               fl->fl_lmops = NULL;
+       }
 }
 EXPORT_SYMBOL_GPL(locks_release_private);
 
@@ -267,21 +271,10 @@ void locks_init_lock(struct file_lock *fl)
 
 EXPORT_SYMBOL(locks_init_lock);
 
-static void locks_copy_private(struct file_lock *new, struct file_lock *fl)
-{
-       if (fl->fl_ops) {
-               if (fl->fl_ops->fl_copy_lock)
-                       fl->fl_ops->fl_copy_lock(new, fl);
-               new->fl_ops = fl->fl_ops;
-       }
-       if (fl->fl_lmops)
-               new->fl_lmops = fl->fl_lmops;
-}
-
 /*
  * Initialize a new lock from an existing file_lock structure.
  */
-void __locks_copy_lock(struct file_lock *new, const struct file_lock *fl)
+void locks_copy_conflock(struct file_lock *new, struct file_lock *fl)
 {
        new->fl_owner = fl->fl_owner;
        new->fl_pid = fl->fl_pid;
@@ -290,22 +283,30 @@ void __locks_copy_lock(struct file_lock *new, const struct file_lock *fl)
        new->fl_type = fl->fl_type;
        new->fl_start = fl->fl_start;
        new->fl_end = fl->fl_end;
+       new->fl_lmops = fl->fl_lmops;
        new->fl_ops = NULL;
-       new->fl_lmops = NULL;
+
+       if (fl->fl_lmops) {
+               if (fl->fl_lmops->lm_get_owner)
+                       fl->fl_lmops->lm_get_owner(new, fl);
+       }
 }
-EXPORT_SYMBOL(__locks_copy_lock);
+EXPORT_SYMBOL(locks_copy_conflock);
 
 void locks_copy_lock(struct file_lock *new, struct file_lock *fl)
 {
        /* "new" must be a freshly-initialized lock */
        WARN_ON_ONCE(new->fl_ops);
 
-       __locks_copy_lock(new, fl);
+       locks_copy_conflock(new, fl);
+
        new->fl_file = fl->fl_file;
        new->fl_ops = fl->fl_ops;
-       new->fl_lmops = fl->fl_lmops;
 
-       locks_copy_private(new, fl);
+       if (fl->fl_ops) {
+               if (fl->fl_ops->fl_copy_lock)
+                       fl->fl_ops->fl_copy_lock(new, fl);
+       }
 }
 
 EXPORT_SYMBOL(locks_copy_lock);
@@ -325,17 +326,18 @@ static inline int flock_translate_cmd(int cmd) {
 }
 
 /* Fill in a file_lock structure with an appropriate FLOCK lock. */
-static int flock_make_lock(struct file *filp, struct file_lock **lock,
-               unsigned int cmd)
+static struct file_lock *
+flock_make_lock(struct file *filp, unsigned int cmd)
 {
        struct file_lock *fl;
        int type = flock_translate_cmd(cmd);
+
        if (type < 0)
-               return type;
+               return ERR_PTR(type);
        
        fl = locks_alloc_lock();
        if (fl == NULL)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
 
        fl->fl_file = filp;
        fl->fl_owner = filp;
@@ -344,8 +346,7 @@ static int flock_make_lock(struct file *filp, struct file_lock **lock,
        fl->fl_type = type;
        fl->fl_end = OFFSET_MAX;
        
-       *lock = fl;
-       return 0;
+       return fl;
 }
 
 static int assign_type(struct file_lock *fl, long type)
@@ -426,14 +427,34 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,
 }
 
 /* default lease lock manager operations */
-static void lease_break_callback(struct file_lock *fl)
+static bool
+lease_break_callback(struct file_lock *fl)
 {
        kill_fasync(&fl->fl_fasync, SIGIO, POLL_MSG);
+       return false;
+}
+
+static void
+lease_setup(struct file_lock *fl, void **priv)
+{
+       struct file *filp = fl->fl_file;
+       struct fasync_struct *fa = *priv;
+
+       /*
+        * fasync_insert_entry() returns the old entry if any. If there was no
+        * old entry, then it used "priv" and inserted it into the fasync list.
+        * Clear the pointer to indicate that it shouldn't be freed.
+        */
+       if (!fasync_insert_entry(fa->fa_fd, filp, &fl->fl_fasync, fa))
+               *priv = NULL;
+
+       __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
 }
 
 static const struct lock_manager_operations lease_manager_ops = {
        .lm_break = lease_break_callback,
        .lm_change = lease_modify,
+       .lm_setup = lease_setup,
 };
 
 /*
@@ -444,7 +465,7 @@ static int lease_init(struct file *filp, long type, struct file_lock *fl)
        if (assign_type(fl, type) != 0)
                return -EINVAL;
 
-       fl->fl_owner = current->files;
+       fl->fl_owner = filp;
        fl->fl_pid = current->tgid;
 
        fl->fl_file = filp;
@@ -735,7 +756,7 @@ posix_test_lock(struct file *filp, struct file_lock *fl)
                        break;
        }
        if (cfl) {
-               __locks_copy_lock(fl, cfl);
+               locks_copy_conflock(fl, cfl);
                if (cfl->fl_nspid)
                        fl->fl_pid = pid_vnr(cfl->fl_nspid);
        } else
@@ -941,7 +962,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
                        if (!posix_locks_conflict(request, fl))
                                continue;
                        if (conflock)
-                               __locks_copy_lock(conflock, fl);
+                               locks_copy_conflock(conflock, fl);
                        error = -EAGAIN;
                        if (!(request->fl_flags & FL_SLEEP))
                                goto out;
@@ -1273,7 +1294,7 @@ static void lease_clear_pending(struct file_lock *fl, int arg)
 }
 
 /* We already had a lease on this file; just change its type */
-int lease_modify(struct file_lock **before, int arg)
+int lease_modify(struct file_lock **before, int arg, struct list_head *dispose)
 {
        struct file_lock *fl = *before;
        int error = assign_type(fl, arg);
@@ -1292,11 +1313,10 @@ int lease_modify(struct file_lock **before, int arg)
                        printk(KERN_ERR "locks_delete_lock: fasync == %p\n", fl->fl_fasync);
                        fl->fl_fasync = NULL;
                }
-               locks_delete_lock(before, NULL);
+               locks_delete_lock(before, dispose);
        }
        return 0;
 }
-
 EXPORT_SYMBOL(lease_modify);
 
 static bool past_time(unsigned long then)
@@ -1307,18 +1327,20 @@ static bool past_time(unsigned long then)
        return time_after(jiffies, then);
 }
 
-static void time_out_leases(struct inode *inode)
+static void time_out_leases(struct inode *inode, struct list_head *dispose)
 {
        struct file_lock **before;
        struct file_lock *fl;
 
+       lockdep_assert_held(&inode->i_lock);
+
        before = &inode->i_flock;
        while ((fl = *before) && IS_LEASE(fl) && lease_breaking(fl)) {
                trace_time_out_leases(inode, fl);
                if (past_time(fl->fl_downgrade_time))
-                       lease_modify(before, F_RDLCK);
+                       lease_modify(before, F_RDLCK, dispose);
                if (past_time(fl->fl_break_time))
-                       lease_modify(before, F_UNLCK);
+                       lease_modify(before, F_UNLCK, dispose);
                if (fl == *before)      /* lease_modify may have freed fl */
                        before = &fl->fl_next;
        }
@@ -1331,6 +1353,20 @@ static bool leases_conflict(struct file_lock *lease, struct file_lock *breaker)
        return locks_conflict(breaker, lease);
 }
 
+static bool
+any_leases_conflict(struct inode *inode, struct file_lock *breaker)
+{
+       struct file_lock *fl;
+
+       lockdep_assert_held(&inode->i_lock);
+
+       for (fl = inode->i_flock ; fl && IS_LEASE(fl); fl = fl->fl_next) {
+               if (leases_conflict(fl, breaker))
+                       return true;
+       }
+       return false;
+}
+
 /**
  *     __break_lease   -       revoke all outstanding leases on file
  *     @inode: the inode of the file to return
@@ -1347,12 +1383,11 @@ static bool leases_conflict(struct file_lock *lease, struct file_lock *breaker)
 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;
+       struct file_lock *new_fl;
+       struct file_lock *fl, **before;
        unsigned long break_time;
-       int i_have_this_lease = 0;
-       bool lease_conflict = false;
        int want_write = (mode & O_ACCMODE) != O_RDONLY;
+       LIST_HEAD(dispose);
 
        new_fl = lease_alloc(NULL, want_write ? F_WRLCK : F_RDLCK);
        if (IS_ERR(new_fl))
@@ -1361,20 +1396,9 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
 
        spin_lock(&inode->i_lock);
 
-       time_out_leases(inode);
-
-       flock = inode->i_flock;
-       if ((flock == NULL) || !IS_LEASE(flock))
-               goto out;
+       time_out_leases(inode, &dispose);
 
-       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)
+       if (!any_leases_conflict(inode, new_fl))
                goto out;
 
        break_time = 0;
@@ -1384,7 +1408,9 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
                        break_time++;   /* so that 0 means no break time */
        }
 
-       for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) {
+       for (before = &inode->i_flock;
+                       ((fl = *before) != NULL) && IS_LEASE(fl);
+                       before = &fl->fl_next) {
                if (!leases_conflict(fl, new_fl))
                        continue;
                if (want_write) {
@@ -1393,51 +1419,56 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
                        fl->fl_flags |= FL_UNLOCK_PENDING;
                        fl->fl_break_time = break_time;
                } else {
-                       if (lease_breaking(flock))
+                       if (lease_breaking(inode->i_flock))
                                continue;
                        fl->fl_flags |= FL_DOWNGRADE_PENDING;
                        fl->fl_downgrade_time = break_time;
                }
-               fl->fl_lmops->lm_break(fl);
+               if (fl->fl_lmops->lm_break(fl))
+                       locks_delete_lock(before, &dispose);
        }
 
-       if (i_have_this_lease || (mode & O_NONBLOCK)) {
+       fl = inode->i_flock;
+       if (!fl || !IS_LEASE(fl))
+               goto out;
+
+       if (mode & O_NONBLOCK) {
                trace_break_lease_noblock(inode, new_fl);
                error = -EWOULDBLOCK;
                goto out;
        }
 
 restart:
-       break_time = flock->fl_break_time;
+       break_time = inode->i_flock->fl_break_time;
        if (break_time != 0)
                break_time -= jiffies;
        if (break_time == 0)
                break_time++;
-       locks_insert_block(flock, new_fl);
+       locks_insert_block(inode->i_flock, new_fl);
        trace_break_lease_block(inode, new_fl);
        spin_unlock(&inode->i_lock);
+       locks_dispose_list(&dispose);
        error = wait_event_interruptible_timeout(new_fl->fl_wait,
                                                !new_fl->fl_next, break_time);
        spin_lock(&inode->i_lock);
        trace_break_lease_unblock(inode, new_fl);
        locks_delete_block(new_fl);
        if (error >= 0) {
-               if (error == 0)
-                       time_out_leases(inode);
                /*
                 * Wait for the next conflicting lease that has not been
                 * broken yet
                 */
-               for (flock = inode->i_flock; flock && IS_LEASE(flock);
-                               flock = flock->fl_next) {
-                       if (leases_conflict(new_fl, flock))
-                               goto restart;
-               }
+               if (error == 0)
+                       time_out_leases(inode, &dispose);
+               if (any_leases_conflict(inode, new_fl))
+                       goto restart;
+
                error = 0;
        }
 
 out:
        spin_unlock(&inode->i_lock);
+       locks_dispose_list(&dispose);
        locks_free_lock(new_fl);
        return error;
 }
@@ -1455,8 +1486,18 @@ EXPORT_SYMBOL(__break_lease);
  */
 void lease_get_mtime(struct inode *inode, struct timespec *time)
 {
-       struct file_lock *flock = inode->i_flock;
-       if (flock && IS_LEASE(flock) && (flock->fl_type == F_WRLCK))
+       bool has_lease = false;
+       struct file_lock *flock;
+
+       if (inode->i_flock) {
+               spin_lock(&inode->i_lock);
+               flock = inode->i_flock;
+               if (flock && IS_LEASE(flock) && (flock->fl_type == F_WRLCK))
+                       has_lease = true;
+               spin_unlock(&inode->i_lock);
+       }
+
+       if (has_lease)
                *time = current_fs_time(inode->i_sb);
        else
                *time = inode->i_mtime;
@@ -1492,9 +1533,10 @@ int fcntl_getlease(struct file *filp)
        struct file_lock *fl;
        struct inode *inode = file_inode(filp);
        int type = F_UNLCK;
+       LIST_HEAD(dispose);
 
        spin_lock(&inode->i_lock);
-       time_out_leases(file_inode(filp));
+       time_out_leases(file_inode(filp), &dispose);
        for (fl = file_inode(filp)->i_flock; fl && IS_LEASE(fl);
                        fl = fl->fl_next) {
                if (fl->fl_file == filp) {
@@ -1503,6 +1545,7 @@ int fcntl_getlease(struct file *filp)
                }
        }
        spin_unlock(&inode->i_lock);
+       locks_dispose_list(&dispose);
        return type;
 }
 
@@ -1532,13 +1575,15 @@ check_conflicting_open(const struct dentry *dentry, const long arg)
        return ret;
 }
 
-static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp)
+static int
+generic_add_lease(struct file *filp, long arg, struct file_lock **flp, void **priv)
 {
        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;
+       LIST_HEAD(dispose);
 
        lease = *flp;
        trace_generic_add_lease(inode, lease);
@@ -1561,6 +1606,8 @@ static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp
                return -EINVAL;
        }
 
+       spin_lock(&inode->i_lock);
+       time_out_leases(inode, &dispose);
        error = check_conflicting_open(dentry, arg);
        if (error)
                goto out;
@@ -1596,10 +1643,11 @@ static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp
        }
 
        if (my_before != NULL) {
-               error = lease->fl_lmops->lm_change(my_before, arg);
-               if (!error)
-                       *flp = *my_before;
-               goto out;
+               lease = *my_before;
+               error = lease->fl_lmops->lm_change(my_before, arg, &dispose);
+               if (error)
+                       goto out;
+               goto out_setup;
        }
 
        error = -EINVAL;
@@ -1619,43 +1667,61 @@ static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp
        smp_mb();
        error = check_conflicting_open(dentry, arg);
        if (error)
-               locks_unlink_lock(before);
+               goto out_unlink;
+
+out_setup:
+       if (lease->fl_lmops->lm_setup)
+               lease->fl_lmops->lm_setup(lease, priv);
 out:
+       spin_unlock(&inode->i_lock);
+       locks_dispose_list(&dispose);
        if (is_deleg)
                mutex_unlock(&inode->i_mutex);
+       if (!error && !my_before)
+               *flp = NULL;
        return error;
+out_unlink:
+       locks_unlink_lock(before);
+       goto out;
 }
 
-static int generic_delete_lease(struct file *filp, struct file_lock **flp)
+static int generic_delete_lease(struct file *filp)
 {
+       int error = -EAGAIN;
        struct file_lock *fl, **before;
        struct dentry *dentry = filp->f_path.dentry;
        struct inode *inode = dentry->d_inode;
+       LIST_HEAD(dispose);
 
-       trace_generic_delete_lease(inode, *flp);
-
+       spin_lock(&inode->i_lock);
+       time_out_leases(inode, &dispose);
        for (before = &inode->i_flock;
                        ((fl = *before) != NULL) && IS_LEASE(fl);
                        before = &fl->fl_next) {
-               if (fl->fl_file != filp)
-                       continue;
-               return (*flp)->fl_lmops->lm_change(before, F_UNLCK);
+               if (fl->fl_file == filp)
+                       break;
        }
-       return -EAGAIN;
+       trace_generic_delete_lease(inode, fl);
+       if (fl)
+               error = fl->fl_lmops->lm_change(before, F_UNLCK, &dispose);
+       spin_unlock(&inode->i_lock);
+       locks_dispose_list(&dispose);
+       return error;
 }
 
 /**
  *     generic_setlease        -       sets a lease on an open file
- *     @filp: file pointer
- *     @arg: type of lease to obtain
- *     @flp: input - file_lock to use, output - file_lock inserted
+ *     @filp:  file pointer
+ *     @arg:   type of lease to obtain
+ *     @flp:   input - file_lock to use, output - file_lock inserted
+ *     @priv:  private data for lm_setup (may be NULL if lm_setup
+ *             doesn't require it)
  *
  *     The (input) flp->fl_lmops->lm_break function is required
  *     by break_lease().
- *
- *     Called with inode->i_lock held.
  */
-int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
+int generic_setlease(struct file *filp, long arg, struct file_lock **flp,
+                       void **priv)
 {
        struct dentry *dentry = filp->f_path.dentry;
        struct inode *inode = dentry->d_inode;
@@ -1669,83 +1735,52 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
        if (error)
                return error;
 
-       time_out_leases(inode);
-
-       BUG_ON(!(*flp)->fl_lmops->lm_break);
-
        switch (arg) {
        case F_UNLCK:
-               return generic_delete_lease(filp, flp);
+               return generic_delete_lease(filp);
        case F_RDLCK:
        case F_WRLCK:
-               return generic_add_lease(filp, arg, flp);
+               if (!(*flp)->fl_lmops->lm_break) {
+                       WARN_ON_ONCE(1);
+                       return -ENOLCK;
+               }
+               return generic_add_lease(filp, arg, flp, priv);
        default:
                return -EINVAL;
        }
 }
 EXPORT_SYMBOL(generic_setlease);
 
-static int __vfs_setlease(struct file *filp, long arg, struct file_lock **lease)
-{
-       if (filp->f_op->setlease)
-               return filp->f_op->setlease(filp, arg, lease);
-       else
-               return generic_setlease(filp, arg, lease);
-}
-
 /**
- *     vfs_setlease        -       sets a lease on an open file
- *     @filp: file pointer
- *     @arg: type of lease to obtain
- *     @lease: file_lock to use
- *
- *     Call this to establish a lease on the file.
- *     The (*lease)->fl_lmops->lm_break operation must be set; if not,
- *     break_lease will oops!
- *
- *     This will call the filesystem's setlease file method, if
- *     defined.  Note that there is no getlease method; instead, the
- *     filesystem setlease method should call back to setlease() to
- *     add a lease to the inode's lease list, where fcntl_getlease() can
- *     find it.  Since fcntl_getlease() only reports whether the current
- *     task holds a lease, a cluster filesystem need only do this for
- *     leases held by processes on this node.
- *
- *     There is also no break_lease method; filesystems that
- *     handle their own leases should break leases themselves from the
- *     filesystem's open, create, and (on truncate) setattr methods.
- *
- *     Warning: the only current setlease methods exist only to disable
- *     leases in certain cases.  More vfs changes may be required to
- *     allow a full filesystem lease implementation.
+ * vfs_setlease        -       sets a lease on an open file
+ * @filp:      file pointer
+ * @arg:       type of lease to obtain
+ * @lease:     file_lock to use when adding a lease
+ * @priv:      private info for lm_setup when adding a lease (may be
+ *             NULL if lm_setup doesn't require it)
+ *
+ * Call this to establish a lease on the file. The "lease" argument is not
+ * used for F_UNLCK requests and may be NULL. For commands that set or alter
+ * an existing lease, the (*lease)->fl_lmops->lm_break operation must be set;
+ * if not, this function will return -ENOLCK (and generate a scary-looking
+ * stack trace).
+ *
+ * The "priv" pointer is passed directly to the lm_setup function as-is. It
+ * may be NULL if the lm_setup operation doesn't require it.
  */
-
-int vfs_setlease(struct file *filp, long arg, struct file_lock **lease)
+int
+vfs_setlease(struct file *filp, long arg, struct file_lock **lease, void **priv)
 {
-       struct inode *inode = file_inode(filp);
-       int error;
-
-       spin_lock(&inode->i_lock);
-       error = __vfs_setlease(filp, arg, lease);
-       spin_unlock(&inode->i_lock);
-
-       return error;
+       if (filp->f_op->setlease)
+               return filp->f_op->setlease(filp, arg, lease, priv);
+       else
+               return generic_setlease(filp, arg, lease, priv);
 }
 EXPORT_SYMBOL_GPL(vfs_setlease);
 
-static int do_fcntl_delete_lease(struct file *filp)
-{
-       struct file_lock fl, *flp = &fl;
-
-       lease_init(filp, F_UNLCK, flp);
-
-       return vfs_setlease(filp, F_UNLCK, &flp);
-}
-
 static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg)
 {
-       struct file_lock *fl, *ret;
-       struct inode *inode = file_inode(filp);
+       struct file_lock *fl;
        struct fasync_struct *new;
        int error;
 
@@ -1758,26 +1793,9 @@ static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg)
                locks_free_lock(fl);
                return -ENOMEM;
        }
-       ret = fl;
-       spin_lock(&inode->i_lock);
-       error = __vfs_setlease(filp, arg, &ret);
-       if (error)
-               goto out_unlock;
-       if (ret == fl)
-               fl = NULL;
-
-       /*
-        * fasync_insert_entry() returns the old entry if any.
-        * If there was no old entry, then it used 'new' and
-        * inserted it into the fasync list. Clear new so that
-        * we don't release it here.
-        */
-       if (!fasync_insert_entry(fd, filp, &ret->fl_fasync, new))
-               new = NULL;
+       new->fa_fd = fd;
 
-       error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
-out_unlock:
-       spin_unlock(&inode->i_lock);
+       error = vfs_setlease(filp, arg, &fl, (void **)&new);
        if (fl)
                locks_free_lock(fl);
        if (new)
@@ -1798,7 +1816,7 @@ out_unlock:
 int fcntl_setlease(unsigned int fd, struct file *filp, long arg)
 {
        if (arg == F_UNLCK)
-               return do_fcntl_delete_lease(filp);
+               return vfs_setlease(filp, F_UNLCK, NULL, NULL);
        return do_fcntl_add_lease(fd, filp, arg);
 }
 
@@ -1867,9 +1885,12 @@ SYSCALL_DEFINE2(flock, unsigned int, fd, unsigned int, cmd)
            !(f.file->f_mode & (FMODE_READ|FMODE_WRITE)))
                goto out_putf;
 
-       error = flock_make_lock(f.file, &lock, cmd);
-       if (error)
+       lock = flock_make_lock(f.file, cmd);
+       if (IS_ERR(lock)) {
+               error = PTR_ERR(lock);
                goto out_putf;
+       }
+
        if (can_sleep)
                lock->fl_flags |= FL_SLEEP;
 
@@ -1981,11 +2002,13 @@ int fcntl_getlk(struct file *filp, unsigned int cmd, struct flock __user *l)
        if (file_lock.fl_type != F_UNLCK) {
                error = posix_lock_to_flock(&flock, &file_lock);
                if (error)
-                       goto out;
+                       goto rel_priv;
        }
        error = -EFAULT;
        if (!copy_to_user(l, &flock, sizeof(flock)))
                error = 0;
+rel_priv:
+       locks_release_private(&file_lock);
 out:
        return error;
 }
@@ -2206,7 +2229,8 @@ int fcntl_getlk64(struct file *filp, unsigned int cmd, struct flock64 __user *l)
        error = -EFAULT;
        if (!copy_to_user(l, &flock, sizeof(flock)))
                error = 0;
-  
+
+       locks_release_private(&file_lock);
 out:
        return error;
 }
@@ -2369,7 +2393,7 @@ void locks_remove_file(struct file *filp)
        while ((fl = *before) != NULL) {
                if (fl->fl_file == filp) {
                        if (IS_LEASE(fl)) {
-                               lease_modify(before, F_UNLCK);
+                               lease_modify(before, F_UNLCK, &dispose);
                                continue;
                        }
 
@@ -2593,86 +2617,6 @@ static int __init proc_locks_init(void)
 module_init(proc_locks_init);
 #endif
 
-/**
- *     lock_may_read - checks that the region is free of locks
- *     @inode: the inode that is being read
- *     @start: the first byte to read
- *     @len: the number of bytes to read
- *
- *     Emulates Windows locking requirements.  Whole-file
- *     mandatory locks (share modes) can prohibit a read and
- *     byte-range POSIX locks can prohibit a read if they overlap.
- *
- *     N.B. this function is only ever called
- *     from knfsd and ownership of locks is never checked.
- */
-int lock_may_read(struct inode *inode, loff_t start, unsigned long len)
-{
-       struct file_lock *fl;
-       int result = 1;
-
-       spin_lock(&inode->i_lock);
-       for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
-               if (IS_POSIX(fl)) {
-                       if (fl->fl_type == F_RDLCK)
-                               continue;
-                       if ((fl->fl_end < start) || (fl->fl_start > (start + len)))
-                               continue;
-               } else if (IS_FLOCK(fl)) {
-                       if (!(fl->fl_type & LOCK_MAND))
-                               continue;
-                       if (fl->fl_type & LOCK_READ)
-                               continue;
-               } else
-                       continue;
-               result = 0;
-               break;
-       }
-       spin_unlock(&inode->i_lock);
-       return result;
-}
-
-EXPORT_SYMBOL(lock_may_read);
-
-/**
- *     lock_may_write - checks that the region is free of locks
- *     @inode: the inode that is being written
- *     @start: the first byte to write
- *     @len: the number of bytes to write
- *
- *     Emulates Windows locking requirements.  Whole-file
- *     mandatory locks (share modes) can prohibit a write and
- *     byte-range POSIX locks can prohibit a write if they overlap.
- *
- *     N.B. this function is only ever called
- *     from knfsd and ownership of locks is never checked.
- */
-int lock_may_write(struct inode *inode, loff_t start, unsigned long len)
-{
-       struct file_lock *fl;
-       int result = 1;
-
-       spin_lock(&inode->i_lock);
-       for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
-               if (IS_POSIX(fl)) {
-                       if ((fl->fl_end < start) || (fl->fl_start > (start + len)))
-                               continue;
-               } else if (IS_FLOCK(fl)) {
-                       if (!(fl->fl_type & LOCK_MAND))
-                               continue;
-                       if (fl->fl_type & LOCK_WRITE)
-                               continue;
-               } else
-                       continue;
-               result = 0;
-               break;
-       }
-       spin_unlock(&inode->i_lock);
-       return result;
-}
-
-EXPORT_SYMBOL(lock_may_write);
-
 static int __init filelock_init(void)
 {
        int i;
index 6920127..4ea92ce 100644 (file)
@@ -919,17 +919,6 @@ int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
 }
 EXPORT_SYMBOL_GPL(nfs_flock);
 
-/*
- * There is no protocol support for leases, so we have no way to implement
- * them correctly in the face of opens by other clients.
- */
-int nfs_setlease(struct file *file, long arg, struct file_lock **fl)
-{
-       dprintk("NFS: setlease(%pD2, arg=%ld)\n", file, arg);
-       return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(nfs_setlease);
-
 const struct file_operations nfs_file_operations = {
        .llseek         = nfs_file_llseek,
        .read           = new_sync_read,
@@ -946,6 +935,6 @@ const struct file_operations nfs_file_operations = {
        .splice_read    = nfs_file_splice_read,
        .splice_write   = iter_file_splice_write,
        .check_flags    = nfs_check_flags,
-       .setlease       = nfs_setlease,
+       .setlease       = simple_nosetlease,
 };
 EXPORT_SYMBOL_GPL(nfs_file_operations);
index 14ae6f2..efaa31c 100644 (file)
@@ -339,7 +339,6 @@ int nfs_file_release(struct inode *, struct file *);
 int nfs_lock(struct file *, int, struct file_lock *);
 int nfs_flock(struct file *, int, struct file_lock *);
 int nfs_check_flags(int);
-int nfs_setlease(struct file *, long, struct file_lock **);
 
 /* inode.c */
 extern struct workqueue_struct *nfsiod_workqueue;
index a816f06..3e987ad 100644 (file)
@@ -131,5 +131,5 @@ const struct file_operations nfs4_file_operations = {
        .splice_read    = nfs_file_splice_read,
        .splice_write   = iter_file_splice_write,
        .check_flags    = nfs_check_flags,
-       .setlease       = nfs_setlease,
+       .setlease       = simple_nosetlease,
 };
index 5c0cac1..e9c3afe 100644 (file)
@@ -218,6 +218,13 @@ static void nfsd4_put_session(struct nfsd4_session *ses)
        spin_unlock(&nn->client_lock);
 }
 
+static inline struct nfs4_stateowner *
+nfs4_get_stateowner(struct nfs4_stateowner *sop)
+{
+       atomic_inc(&sop->so_count);
+       return sop;
+}
+
 static int
 same_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner)
 {
@@ -237,10 +244,8 @@ find_openstateowner_str_locked(unsigned int hashval, struct nfsd4_open *open,
                            so_strhash) {
                if (!so->so_is_open_owner)
                        continue;
-               if (same_owner_str(so, &open->op_owner)) {
-                       atomic_inc(&so->so_count);
-                       return openowner(so);
-               }
+               if (same_owner_str(so, &open->op_owner))
+                       return openowner(nfs4_get_stateowner(so));
        }
        return NULL;
 }
@@ -678,18 +683,14 @@ nfs4_put_stid(struct nfs4_stid *s)
 static void nfs4_put_deleg_lease(struct nfs4_file *fp)
 {
        struct file *filp = NULL;
-       struct file_lock *fl;
 
        spin_lock(&fp->fi_lock);
-       if (fp->fi_lease && atomic_dec_and_test(&fp->fi_delegees)) {
+       if (fp->fi_deleg_file && atomic_dec_and_test(&fp->fi_delegees))
                swap(filp, fp->fi_deleg_file);
-               fl = fp->fi_lease;
-               fp->fi_lease = NULL;
-       }
        spin_unlock(&fp->fi_lock);
 
        if (filp) {
-               vfs_setlease(filp, F_UNLCK, &fl);
+               vfs_setlease(filp, F_UNLCK, NULL, NULL);
                fput(filp);
        }
 }
@@ -1655,7 +1656,7 @@ __destroy_client(struct nfs4_client *clp)
        }
        while (!list_empty(&clp->cl_openowners)) {
                oo = list_entry(clp->cl_openowners.next, struct nfs4_openowner, oo_perclient);
-               atomic_inc(&oo->oo_owner.so_count);
+               nfs4_get_stateowner(&oo->oo_owner);
                release_openowner(oo);
        }
        nfsd4_shutdown_callback(clp);
@@ -3067,8 +3068,8 @@ static void nfsd4_init_file(struct nfs4_file *fp, struct knfsd_fh *fh)
        INIT_LIST_HEAD(&fp->fi_stateids);
        INIT_LIST_HEAD(&fp->fi_delegations);
        fh_copy_shallow(&fp->fi_fhandle, fh);
+       fp->fi_deleg_file = NULL;
        fp->fi_had_conflict = false;
-       fp->fi_lease = NULL;
        fp->fi_share_deny = 0;
        memset(fp->fi_fds, 0, sizeof(fp->fi_fds));
        memset(fp->fi_access, 0, sizeof(fp->fi_access));
@@ -3136,8 +3137,7 @@ static void nfsd4_cstate_assign_replay(struct nfsd4_compound_state *cstate,
 {
        if (!nfsd4_has_session(cstate)) {
                mutex_lock(&so->so_replay.rp_mutex);
-               cstate->replay_owner = so;
-               atomic_inc(&so->so_count);
+               cstate->replay_owner = nfs4_get_stateowner(so);
        }
 }
 
@@ -3236,8 +3236,7 @@ static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp,
        atomic_inc(&stp->st_stid.sc_count);
        stp->st_stid.sc_type = NFS4_OPEN_STID;
        INIT_LIST_HEAD(&stp->st_locks);
-       stp->st_stateowner = &oo->oo_owner;
-       atomic_inc(&stp->st_stateowner->so_count);
+       stp->st_stateowner = nfs4_get_stateowner(&oo->oo_owner);
        get_nfs4_file(fp);
        stp->st_stid.sc_file = fp;
        stp->st_access_bmap = 0;
@@ -3434,18 +3433,20 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
 }
 
 /* Called from break_lease() with i_lock held. */
-static void nfsd_break_deleg_cb(struct file_lock *fl)
+static bool
+nfsd_break_deleg_cb(struct file_lock *fl)
 {
+       bool ret = false;
        struct nfs4_file *fp = (struct nfs4_file *)fl->fl_owner;
        struct nfs4_delegation *dp;
 
        if (!fp) {
                WARN(1, "(%p)->fl_owner NULL\n", fl);
-               return;
+               return ret;
        }
        if (fp->fi_had_conflict) {
                WARN(1, "duplicate break on %p\n", fp);
-               return;
+               return ret;
        }
        /*
         * We don't want the locks code to timeout the lease for us;
@@ -3457,24 +3458,23 @@ static void nfsd_break_deleg_cb(struct file_lock *fl)
        spin_lock(&fp->fi_lock);
        fp->fi_had_conflict = true;
        /*
-        * If there are no delegations on the list, then we can't count on this
-        * lease ever being cleaned up. Set the fl_break_time to jiffies so that
-        * time_out_leases will do it ASAP. The fact that fi_had_conflict is now
-        * true should keep any new delegations from being hashed.
+        * If there are no delegations on the list, then return true
+        * so that the lease code will go ahead and delete it.
         */
        if (list_empty(&fp->fi_delegations))
-               fl->fl_break_time = jiffies;
+               ret = true;
        else
                list_for_each_entry(dp, &fp->fi_delegations, dl_perfile)
                        nfsd_break_one_deleg(dp);
        spin_unlock(&fp->fi_lock);
+       return ret;
 }
 
-static
-int nfsd_change_deleg_cb(struct file_lock **onlist, int arg)
+static int
+nfsd_change_deleg_cb(struct file_lock **onlist, int arg, struct list_head *dispose)
 {
        if (arg & F_UNLCK)
-               return lease_modify(onlist, arg);
+               return lease_modify(onlist, arg, dispose);
        else
                return -EAGAIN;
 }
@@ -3820,7 +3820,7 @@ static struct file_lock *nfs4_alloc_init_lease(struct nfs4_file *fp, int flag)
 static int nfs4_setlease(struct nfs4_delegation *dp)
 {
        struct nfs4_file *fp = dp->dl_stid.sc_file;
-       struct file_lock *fl;
+       struct file_lock *fl, *ret;
        struct file *filp;
        int status = 0;
 
@@ -3834,11 +3834,12 @@ static int nfs4_setlease(struct nfs4_delegation *dp)
                return -EBADF;
        }
        fl->fl_file = filp;
-       status = vfs_setlease(filp, fl->fl_type, &fl);
-       if (status) {
+       ret = fl;
+       status = vfs_setlease(filp, fl->fl_type, &fl, NULL);
+       if (fl)
                locks_free_lock(fl);
+       if (status)
                goto out_fput;
-       }
        spin_lock(&state_lock);
        spin_lock(&fp->fi_lock);
        /* Did the lease get broken before we took the lock? */
@@ -3846,13 +3847,12 @@ static int nfs4_setlease(struct nfs4_delegation *dp)
        if (fp->fi_had_conflict)
                goto out_unlock;
        /* Race breaker */
-       if (fp->fi_lease) {
+       if (fp->fi_deleg_file) {
                status = 0;
                atomic_inc(&fp->fi_delegees);
                hash_delegation_locked(dp, fp);
                goto out_unlock;
        }
-       fp->fi_lease = fl;
        fp->fi_deleg_file = filp;
        atomic_set(&fp->fi_delegees, 1);
        hash_delegation_locked(dp, fp);
@@ -3885,7 +3885,7 @@ nfs4_set_delegation(struct nfs4_client *clp, struct svc_fh *fh,
        spin_lock(&state_lock);
        spin_lock(&fp->fi_lock);
        dp->dl_stid.sc_file = fp;
-       if (!fp->fi_lease) {
+       if (!fp->fi_deleg_file) {
                spin_unlock(&fp->fi_lock);
                spin_unlock(&state_lock);
                status = nfs4_setlease(dp);
@@ -4929,9 +4929,25 @@ nfs4_transform_lock_offset(struct file_lock *lock)
                lock->fl_end = OFFSET_MAX;
 }
 
-/* Hack!: For now, we're defining this just so we can use a pointer to it
- * as a unique cookie to identify our (NFSv4's) posix locks. */
+static void nfsd4_fl_get_owner(struct file_lock *dst, struct file_lock *src)
+{
+       struct nfs4_lockowner *lo = (struct nfs4_lockowner *)src->fl_owner;
+       dst->fl_owner = (fl_owner_t)lockowner(nfs4_get_stateowner(&lo->lo_owner));
+}
+
+static void nfsd4_fl_put_owner(struct file_lock *fl)
+{
+       struct nfs4_lockowner *lo = (struct nfs4_lockowner *)fl->fl_owner;
+
+       if (lo) {
+               nfs4_put_stateowner(&lo->lo_owner);
+               fl->fl_owner = NULL;
+       }
+}
+
 static const struct lock_manager_operations nfsd_posix_mng_ops  = {
+       .lm_get_owner = nfsd4_fl_get_owner,
+       .lm_put_owner = nfsd4_fl_put_owner,
 };
 
 static inline void
@@ -4977,10 +4993,8 @@ find_lockowner_str_locked(clientid_t *clid, struct xdr_netobj *owner,
                            so_strhash) {
                if (so->so_is_open_owner)
                        continue;
-               if (!same_owner_str(so, owner))
-                       continue;
-               atomic_inc(&so->so_count);
-               return lockowner(so);
+               if (same_owner_str(so, owner))
+                       return lockowner(nfs4_get_stateowner(so));
        }
        return NULL;
 }
@@ -5059,8 +5073,7 @@ init_lock_stateid(struct nfs4_ol_stateid *stp, struct nfs4_lockowner *lo,
 
        atomic_inc(&stp->st_stid.sc_count);
        stp->st_stid.sc_type = NFS4_LOCK_STID;
-       stp->st_stateowner = &lo->lo_owner;
-       atomic_inc(&lo->lo_owner.so_count);
+       stp->st_stateowner = nfs4_get_stateowner(&lo->lo_owner);
        get_nfs4_file(fp);
        stp->st_stid.sc_file = fp;
        stp->st_stid.sc_free = nfs4_free_lock_stateid;
@@ -5299,7 +5312,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                status = nfserr_openmode;
                goto out;
        }
-       file_lock->fl_owner = (fl_owner_t)lock_sop;
+
+       file_lock->fl_owner = (fl_owner_t)lockowner(nfs4_get_stateowner(&lock_sop->lo_owner));
        file_lock->fl_pid = current->tgid;
        file_lock->fl_file = filp;
        file_lock->fl_flags = FL_POSIX;
@@ -5495,7 +5509,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        }
 
        file_lock->fl_type = F_UNLCK;
-       file_lock->fl_owner = (fl_owner_t)lockowner(stp->st_stateowner);
+       file_lock->fl_owner = (fl_owner_t)lockowner(nfs4_get_stateowner(stp->st_stateowner));
        file_lock->fl_pid = current->tgid;
        file_lock->fl_file = filp;
        file_lock->fl_flags = FL_POSIX;
@@ -5602,7 +5616,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
                        }
                }
 
-               atomic_inc(&sop->so_count);
+               nfs4_get_stateowner(sop);
                break;
        }
        spin_unlock(&clp->cl_lock);
index 0a47c6a..2712042 100644 (file)
@@ -486,7 +486,6 @@ struct nfs4_file {
        atomic_t                fi_access[2];
        u32                     fi_share_deny;
        struct file             *fi_deleg_file;
-       struct file_lock        *fi_lease;
        atomic_t                fi_delegees;
        struct knfsd_fh         fi_fhandle;
        bool                    fi_had_conflict;
index abc8cbc..caaaf9d 100644 (file)
@@ -346,13 +346,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
                goto out;
        }
 
-       error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
-       if (error) {
-               /* if we added, we must shoot */
-               if (dn_mark == new_dn_mark)
-                       destroy = 1;
-               goto out;
-       }
+       __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
 
        error = attach_dn(dn, dn_mark, id, fd, filp, mask);
        /* !error means that we attached the dn to the dn_mark, so don't free it */
index 9418772..2023306 100644 (file)
@@ -851,13 +851,7 @@ static inline struct file *get_file(struct file *f)
  */
 #define FILE_LOCK_DEFERRED 1
 
-/*
- * The POSIX file lock owner is determined by
- * the "struct files_struct" in the thread group
- * (or NULL for no owner - BSD locks).
- *
- * Lockd stuffs a "host" pointer into this.
- */
+/* legacy typedef, should eventually be removed */
 typedef void *fl_owner_t;
 
 struct file_lock_operations {
@@ -868,10 +862,13 @@ struct file_lock_operations {
 struct lock_manager_operations {
        int (*lm_compare_owner)(struct file_lock *, struct file_lock *);
        unsigned long (*lm_owner_key)(struct file_lock *);
+       void (*lm_get_owner)(struct file_lock *, struct file_lock *);
+       void (*lm_put_owner)(struct file_lock *);
        void (*lm_notify)(struct file_lock *);  /* unblock callback */
-       int (*lm_grant)(struct file_lock *, struct file_lock *, int);
-       void (*lm_break)(struct file_lock *);
-       int (*lm_change)(struct file_lock **, int);
+       int (*lm_grant)(struct file_lock *, int);
+       bool (*lm_break)(struct file_lock *);
+       int (*lm_change)(struct file_lock **, int, struct list_head *);
+       void (*lm_setup)(struct file_lock *, void **);
 };
 
 struct lock_manager {
@@ -966,7 +963,7 @@ void locks_free_lock(struct file_lock *fl);
 extern void locks_init_lock(struct file_lock *);
 extern struct file_lock * locks_alloc_lock(void);
 extern void locks_copy_lock(struct file_lock *, struct file_lock *);
-extern void __locks_copy_lock(struct file_lock *, const struct file_lock *);
+extern void locks_copy_conflock(struct file_lock *, struct file_lock *);
 extern void locks_remove_posix(struct file *, fl_owner_t);
 extern void locks_remove_file(struct file *);
 extern void locks_release_private(struct file_lock *);
@@ -980,11 +977,9 @@ 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, 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 **);
-extern int lease_modify(struct file_lock **, int);
-extern int lock_may_read(struct inode *, loff_t start, unsigned long count);
-extern int lock_may_write(struct inode *, loff_t start, unsigned long count);
+extern int generic_setlease(struct file *, long, struct file_lock **, void **priv);
+extern int vfs_setlease(struct file *, long, struct file_lock **, void **);
+extern int lease_modify(struct file_lock **, int, struct list_head *);
 #else /* !CONFIG_FILE_LOCKING */
 static inline int fcntl_getlk(struct file *file, unsigned int cmd,
                              struct flock __user *user)
@@ -1013,12 +1008,12 @@ static inline int fcntl_setlk64(unsigned int fd, struct file *file,
 #endif
 static inline int fcntl_setlease(unsigned int fd, struct file *filp, long arg)
 {
-       return 0;
+       return -EINVAL;
 }
 
 static inline int fcntl_getlease(struct file *filp)
 {
-       return 0;
+       return F_UNLCK;
 }
 
 static inline void locks_init_lock(struct file_lock *fl)
@@ -1026,7 +1021,7 @@ static inline void locks_init_lock(struct file_lock *fl)
        return;
 }
 
-static inline void __locks_copy_lock(struct file_lock *new, struct file_lock *fl)
+static inline void locks_copy_conflock(struct file_lock *new, struct file_lock *fl)
 {
        return;
 }
@@ -1100,33 +1095,22 @@ static inline void lease_get_mtime(struct inode *inode, struct timespec *time)
 }
 
 static inline int generic_setlease(struct file *filp, long arg,
-                                   struct file_lock **flp)
+                                   struct file_lock **flp, void **priv)
 {
        return -EINVAL;
 }
 
 static inline int vfs_setlease(struct file *filp, long arg,
-                              struct file_lock **lease)
+                              struct file_lock **lease, void **priv)
 {
        return -EINVAL;
 }
 
-static inline int lease_modify(struct file_lock **before, int arg)
+static inline int lease_modify(struct file_lock **before, int arg,
+                              struct list_head *dispose)
 {
        return -EINVAL;
 }
-
-static inline int lock_may_read(struct inode *inode, loff_t start,
-                               unsigned long len)
-{
-       return 1;
-}
-
-static inline int lock_may_write(struct inode *inode, loff_t start,
-                                unsigned long len)
-{
-       return 1;
-}
 #endif /* !CONFIG_FILE_LOCKING */
 
 
@@ -1151,8 +1135,8 @@ extern void fasync_free(struct fasync_struct *);
 /* can be called from interrupts */
 extern void kill_fasync(struct fasync_struct **, int, int);
 
-extern int __f_setown(struct file *filp, struct pid *, enum pid_type, int force);
-extern int f_setown(struct file *filp, unsigned long arg, int force);
+extern void __f_setown(struct file *filp, struct pid *, enum pid_type, int force);
+extern void f_setown(struct file *filp, unsigned long arg, int force);
 extern void f_delown(struct file *filp);
 extern pid_t f_getown(struct file *filp);
 extern int send_sigurg(struct fown_struct *fown);
@@ -1506,7 +1490,7 @@ struct file_operations {
        int (*flock) (struct file *, int, struct file_lock *);
        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
-       int (*setlease)(struct file *, long, struct file_lock **);
+       int (*setlease)(struct file *, long, struct file_lock **, void **);
        long (*fallocate)(struct file *file, int mode, loff_t offset,
                          loff_t len);
        int (*show_fdinfo)(struct seq_file *m, struct file *f);
@@ -2611,6 +2595,7 @@ extern int simple_write_end(struct file *file, struct address_space *mapping,
                        struct page *page, void *fsdata);
 extern int always_delete_dentry(const struct dentry *);
 extern struct inode *alloc_anon_inode(struct super_block *);
+extern int simple_nosetlease(struct file *, long, struct file_lock **, void **);
 extern const struct dentry_operations simple_dentry_operations;
 
 extern struct dentry *simple_lookup(struct inode *, struct dentry *, unsigned int flags);
index 219d796..ff82a32 100644 (file)
@@ -178,7 +178,6 @@ struct nlm_block {
        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 */
index 623f90e..b10e7af 100644 (file)
@@ -1559,7 +1559,7 @@ struct security_operations {
        int (*file_lock) (struct file *file, unsigned int cmd);
        int (*file_fcntl) (struct file *file, unsigned int cmd,
                           unsigned long arg);
-       int (*file_set_fowner) (struct file *file);
+       void (*file_set_fowner) (struct file *file);
        int (*file_send_sigiotask) (struct task_struct *tsk,
                                    struct fown_struct *fown, int sig);
        int (*file_receive) (struct file *file);
@@ -1834,7 +1834,7 @@ int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
                           unsigned long prot);
 int security_file_lock(struct file *file, unsigned int cmd);
 int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg);
-int security_file_set_fowner(struct file *file);
+void security_file_set_fowner(struct file *file);
 int security_file_send_sigiotask(struct task_struct *tsk,
                                 struct fown_struct *fown, int sig);
 int security_file_receive(struct file *file);
@@ -2312,9 +2312,9 @@ static inline int security_file_fcntl(struct file *file, unsigned int cmd,
        return 0;
 }
 
-static inline int security_file_set_fowner(struct file *file)
+static inline void security_file_set_fowner(struct file *file)
 {
-       return 0;
+       return;
 }
 
 static inline int security_file_send_sigiotask(struct task_struct *tsk,
index 59d11c2..a0d0080 100644 (file)
@@ -53,15 +53,15 @@ DECLARE_EVENT_CLASS(filelock_lease,
        ),
 
        TP_fast_assign(
-               __entry->fl = fl;
+               __entry->fl = fl ? fl : NULL;
                __entry->s_dev = inode->i_sb->s_dev;
                __entry->i_ino = inode->i_ino;
-               __entry->fl_next = fl->fl_next;
-               __entry->fl_owner = fl->fl_owner;
-               __entry->fl_flags = fl->fl_flags;
-               __entry->fl_type = fl->fl_type;
-               __entry->fl_break_time = fl->fl_break_time;
-               __entry->fl_downgrade_time = fl->fl_downgrade_time;
+               __entry->fl_next = fl ? fl->fl_next : NULL;
+               __entry->fl_owner = fl ? fl->fl_owner : NULL;
+               __entry->fl_flags = fl ? fl->fl_flags : 0;
+               __entry->fl_type = fl ? fl->fl_type : 0;
+               __entry->fl_break_time = fl ? fl->fl_break_time : 0;
+               __entry->fl_downgrade_time = fl ? fl->fl_downgrade_time : 0;
        ),
 
        TP_printk("fl=0x%p dev=0x%x:0x%x ino=0x%lx fl_next=0x%p fl_owner=0x%p fl_flags=%s fl_type=%s fl_break_time=%lu fl_downgrade_time=%lu",
index ffd9cb4..fe20c31 100644 (file)
@@ -1065,7 +1065,8 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
                        err = -EFAULT;
                        if (get_user(pid, (int __user *)argp))
                                break;
-                       err = f_setown(sock->file, pid, 1);
+                       f_setown(sock->file, pid, 1);
+                       err = 0;
                        break;
                case FIOGETOWN:
                case SIOCGPGRP:
index a74fde6..d68c57a 100644 (file)
@@ -343,9 +343,9 @@ static int cap_file_fcntl(struct file *file, unsigned int cmd,
        return 0;
 }
 
-static int cap_file_set_fowner(struct file *file)
+static void cap_file_set_fowner(struct file *file)
 {
-       return 0;
+       return;
 }
 
 static int cap_file_send_sigiotask(struct task_struct *tsk,
index e41b1a8..18b35c6 100644 (file)
@@ -775,9 +775,9 @@ int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
        return security_ops->file_fcntl(file, cmd, arg);
 }
 
-int security_file_set_fowner(struct file *file)
+void security_file_set_fowner(struct file *file)
 {
-       return security_ops->file_set_fowner(file);
+       security_ops->file_set_fowner(file);
 }
 
 int security_file_send_sigiotask(struct task_struct *tsk,
index b0e9404..ada0d0b 100644 (file)
@@ -3346,14 +3346,12 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
        return err;
 }
 
-static int selinux_file_set_fowner(struct file *file)
+static void selinux_file_set_fowner(struct file *file)
 {
        struct file_security_struct *fsec;
 
        fsec = file->f_security;
        fsec->fown_sid = current_sid();
-
-       return 0;
 }
 
 static int selinux_file_send_sigiotask(struct task_struct *tsk,
index e6ab307..69e5635 100644 (file)
@@ -1390,12 +1390,11 @@ static int smack_mmap_file(struct file *file,
  * Returns 0
  * Further research may be required on this one.
  */
-static int smack_file_set_fowner(struct file *file)
+static void smack_file_set_fowner(struct file *file)
 {
        struct smack_known *skp = smk_of_current();
 
        file->f_security = skp->smk_known;
-       return 0;
 }
 
 /**