locks: move freeing of leases outside of i_lock
authorJeff Layton <jlayton@primarydata.com>
Mon, 1 Sep 2014 11:12:07 +0000 (07:12 -0400)
committerJeff Layton <jlayton@primarydata.com>
Tue, 7 Oct 2014 18:06:13 +0000 (14:06 -0400)
There was only one place where we still could free a file_lock while
holding the i_lock -- lease_modify. Add a new list_head argument to the
lm_change operation, pass in a private list when calling it, and fix
those callers to dispose of the list once the lock has been dropped.

Signed-off-by: Jeff Layton <jlayton@primarydata.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Documentation/filesystems/Locking
fs/locks.c
fs/nfsd/nfs4state.c
include/linux/fs.h

index 4af288e38f138f0864865b01096bdd38fc5ad76e..94d93b1f8b530d44ab3855cdbbdc220fa8a8c60f 100644 (file)
@@ -469,8 +469,7 @@ prototypes:
 };
 
 locking rules:
-       All may block except for ->setlease.
-       No VFS locks held on entry except for ->setlease.
+       All may block.
 
 ->llseek() locking has moved from llseek to the individual llseek
 implementations.  If your fs is not using generic_file_llseek, you
index eb463257f8678aa5f5e81f4be80dae28d3f43266..c0f789dfa655c0c1f3ee522de0bc3b941ee09c1c 100644 (file)
@@ -1292,7 +1292,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);
@@ -1311,7 +1311,7 @@ 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;
 }
@@ -1325,7 +1325,7 @@ 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;
@@ -1336,9 +1336,9 @@ static void time_out_leases(struct inode *inode)
        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;
        }
@@ -1373,6 +1373,7 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
        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))
@@ -1381,7 +1382,7 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
 
        spin_lock(&inode->i_lock);
 
-       time_out_leases(inode);
+       time_out_leases(inode, &dispose);
 
        flock = inode->i_flock;
        if ((flock == NULL) || !IS_LEASE(flock))
@@ -1436,6 +1437,7 @@ restart:
        locks_insert_block(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);
@@ -1443,7 +1445,7 @@ restart:
        locks_delete_block(new_fl);
        if (error >= 0) {
                if (error == 0)
-                       time_out_leases(inode);
+                       time_out_leases(inode, &dispose);
                /*
                 * Wait for the next conflicting lease that has not been
                 * broken yet
@@ -1458,6 +1460,7 @@ restart:
 
 out:
        spin_unlock(&inode->i_lock);
+       locks_dispose_list(&dispose);
        locks_free_lock(new_fl);
        return error;
 }
@@ -1522,9 +1525,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) {
@@ -1533,6 +1537,7 @@ int fcntl_getlease(struct file *filp)
                }
        }
        spin_unlock(&inode->i_lock);
+       locks_dispose_list(&dispose);
        return type;
 }
 
@@ -1570,6 +1575,7 @@ generic_add_lease(struct file *filp, long arg, struct file_lock **flp, void **pr
        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);
@@ -1593,7 +1599,7 @@ generic_add_lease(struct file *filp, long arg, struct file_lock **flp, void **pr
        }
 
        spin_lock(&inode->i_lock);
-       time_out_leases(inode);
+       time_out_leases(inode, &dispose);
        error = check_conflicting_open(dentry, arg);
        if (error)
                goto out;
@@ -1630,7 +1636,7 @@ generic_add_lease(struct file *filp, long arg, struct file_lock **flp, void **pr
 
        if (my_before != NULL) {
                lease = *my_before;
-               error = lease->fl_lmops->lm_change(my_before, arg);
+               error = lease->fl_lmops->lm_change(my_before, arg, &dispose);
                if (error)
                        goto out;
                goto out_setup;
@@ -1660,6 +1666,7 @@ out_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)
@@ -1676,8 +1683,10 @@ static int generic_delete_lease(struct file *filp)
        struct file_lock *fl, **before;
        struct dentry *dentry = filp->f_path.dentry;
        struct inode *inode = dentry->d_inode;
+       LIST_HEAD(dispose);
 
        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) {
@@ -1686,8 +1695,9 @@ static int generic_delete_lease(struct file *filp)
        }
        trace_generic_delete_lease(inode, fl);
        if (fl)
-               error = fl->fl_lmops->lm_change(before, F_UNLCK);
+               error = fl->fl_lmops->lm_change(before, F_UNLCK, &dispose);
        spin_unlock(&inode->i_lock);
+       locks_dispose_list(&dispose);
        return error;
 }
 
@@ -2372,7 +2382,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;
                        }
 
index 5349528136e2c4f14623e3674d0684a49d1a704f..604ab6decd28a7684012ffa891db97fac7e37e5b 100644 (file)
@@ -3427,11 +3427,11 @@ static void nfsd_break_deleg_cb(struct file_lock *fl)
        spin_unlock(&fp->fi_lock);
 }
 
-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;
 }
index 9a6d56154dd54d24bedaa62d17631382ceff34e8..f419f718e447e365893f4d9ccd8a03cb8b16d41a 100644 (file)
@@ -873,7 +873,7 @@ struct lock_manager_operations {
        void (*lm_notify)(struct file_lock *);  /* unblock callback */
        int (*lm_grant)(struct file_lock *, int);
        void (*lm_break)(struct file_lock *);
-       int (*lm_change)(struct file_lock **, int);
+       int (*lm_change)(struct file_lock **, int, struct list_head *);
        void (*lm_setup)(struct file_lock *, void **);
 };
 
@@ -985,7 +985,7 @@ extern int __break_lease(struct inode *inode, unsigned int flags, unsigned int t
 extern void lease_get_mtime(struct inode *, struct timespec *time);
 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);
+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)
@@ -1112,7 +1112,8 @@ static inline int vfs_setlease(struct file *filp, long arg,
        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;
 }