NFS: Ensure we always hold the BKL when dereferencing inode->i_flock
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 17 Jun 2009 20:23:00 +0000 (13:23 -0700)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 17 Jun 2009 20:23:00 +0000 (13:23 -0700)
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
fs/nfs/delegation.c
fs/nfs/nfs4state.c

index d4f669f..af05b91 100644 (file)
@@ -70,15 +70,24 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_
        struct file_lock *fl;
        int status = 0;
 
+       if (inode->i_flock == NULL)
+               goto out;
+
+       /* Protect inode->i_flock using the BKL */
+       lock_kernel();
        for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
                if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
                        continue;
                if (nfs_file_open_context(fl->fl_file) != ctx)
                        continue;
+               unlock_kernel();
                status = nfs4_lock_delegation_recall(state, fl);
                if (status < 0)
-                       break;
+                       goto out;
+               lock_kernel();
        }
+       unlock_kernel();
+out:
        return status;
 }
 
@@ -256,7 +265,10 @@ static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegat
        struct nfs_inode *nfsi = NFS_I(inode);
 
        nfs_msync_inode(inode);
-       /* Guard against new delegated open calls */
+       /*
+        * Guard against new delegated open/lock/unlock calls and against
+        * state recovery
+        */
        down_write(&nfsi->rwsem);
        nfs_delegation_claim_opens(inode, &delegation->stateid);
        up_write(&nfsi->rwsem);
index 6e094e9..0b2a19b 100644 (file)
@@ -847,12 +847,19 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_
        struct file_lock *fl;
        int status = 0;
 
+       if (inode->i_flock == NULL)
+               return 0;
+
+       /* Guard against delegation returns and new lock/unlock calls */
        down_write(&nfsi->rwsem);
+       /* Protect inode->i_flock using the BKL */
+       lock_kernel();
        for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
                if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
                        continue;
                if (nfs_file_open_context(fl->fl_file)->state != state)
                        continue;
+               unlock_kernel();
                status = ops->recover_lock(state, fl);
                switch (status) {
                        case 0:
@@ -875,7 +882,9 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_
                                /* kill_proc(fl->fl_pid, SIGLOST, 1); */
                                status = 0;
                }
+               lock_kernel();
        }
+       unlock_kernel();
 out:
        up_write(&nfsi->rwsem);
        return status;