fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / nfs / delegation.c
index 5b9c60f..841c99a 100644 (file)
@@ -6,8 +6,8 @@
  * NFS file delegation management
  *
  */
-#include <linux/config.h>
 #include <linux/completion.h>
+#include <linux/kthread.h>
 #include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/spinlock.h>
 #include <linux/nfs_fs.h>
 #include <linux/nfs_xdr.h>
 
+#include "nfs4_fs.h"
 #include "delegation.h"
-
-static struct nfs_delegation *nfs_alloc_delegation(void)
-{
-       return (struct nfs_delegation *)kmalloc(sizeof(struct nfs_delegation), GFP_KERNEL);
-}
+#include "internal.h"
 
 static void nfs_free_delegation(struct nfs_delegation *delegation)
 {
@@ -30,11 +27,42 @@ static void nfs_free_delegation(struct nfs_delegation *delegation)
        kfree(delegation);
 }
 
+static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state)
+{
+       struct inode *inode = state->inode;
+       struct file_lock *fl;
+       int status;
+
+       for (fl = inode->i_flock; fl != 0; fl = fl->fl_next) {
+               if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
+                       continue;
+               if ((struct nfs_open_context *)fl->fl_file->private_data != ctx)
+                       continue;
+               status = nfs4_lock_delegation_recall(state, fl);
+               if (status >= 0)
+                       continue;
+               switch (status) {
+                       default:
+                               printk(KERN_ERR "%s: unhandled error %d.\n",
+                                               __FUNCTION__, status);
+                       case -NFS4ERR_EXPIRED:
+                               /* kill_proc(fl->fl_pid, SIGLOST, 1); */
+                       case -NFS4ERR_STALE_CLIENTID:
+                               nfs4_schedule_state_recovery(NFS_SERVER(inode)->nfs_client);
+                               goto out_err;
+               }
+       }
+       return 0;
+out_err:
+       return status;
+}
+
 static void nfs_delegation_claim_opens(struct inode *inode)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
        struct nfs_open_context *ctx;
        struct nfs4_state *state;
+       int err;
 
 again:
        spin_lock(&inode->i_lock);
@@ -46,9 +74,12 @@ again:
                        continue;
                get_nfs_open_context(ctx);
                spin_unlock(&inode->i_lock);
-               if (nfs4_open_delegation_recall(ctx->dentry, state) < 0)
-                       return;
+               err = nfs4_open_delegation_recall(ctx->dentry, state);
+               if (err >= 0)
+                       err = nfs_delegation_claim_locks(ctx, state);
                put_nfs_open_context(ctx);
+               if (err != 0)
+                       return;
                goto again;
        }
        spin_unlock(&inode->i_lock);
@@ -79,18 +110,23 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, st
  */
 int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
 {
-       struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
+       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
        struct nfs_inode *nfsi = NFS_I(inode);
        struct nfs_delegation *delegation;
        int status = 0;
 
-       delegation = nfs_alloc_delegation();
+       /* Ensure we first revalidate the attributes and page cache! */
+       if ((nfsi->cache_validity & (NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_ATTR)))
+               __nfs_revalidate_inode(NFS_SERVER(inode), inode);
+
+       delegation = kmalloc(sizeof(*delegation), GFP_KERNEL);
        if (delegation == NULL)
                return -ENOMEM;
        memcpy(delegation->stateid.data, res->delegation.data,
                        sizeof(delegation->stateid.data));
        delegation->type = res->delegation_type;
        delegation->maxsize = res->maxsize;
+       delegation->change_attr = nfsi->change_attr;
        delegation->cred = get_rpccred(cred);
        delegation->inode = inode;
 
@@ -105,13 +141,12 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
                                        sizeof(delegation->stateid)) != 0 ||
                                delegation->type != nfsi->delegation->type) {
                        printk("%s: server %u.%u.%u.%u, handed out a duplicate delegation!\n",
-                                       __FUNCTION__, NIPQUAD(clp->cl_addr));
+                                       __FUNCTION__, NIPQUAD(clp->cl_addr.sin_addr));
                        status = -EIO;
                }
        }
        spin_unlock(&clp->cl_lock);
-       if (delegation != NULL)
-               kfree(delegation);
+       kfree(delegation);
        return status;
 }
 
@@ -119,8 +154,6 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *
 {
        int res = 0;
 
-       __nfs_revalidate_inode(NFS_SERVER(inode), inode);
-
        res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid);
        nfs_free_delegation(delegation);
        return res;
@@ -137,9 +170,9 @@ static void nfs_msync_inode(struct inode *inode)
 /*
  * Basic procedure for returning a delegation to the server
  */
-int nfs_inode_return_delegation(struct inode *inode)
+int __nfs_inode_return_delegation(struct inode *inode)
 {
-       struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
+       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
        struct nfs_inode *nfsi = NFS_I(inode);
        struct nfs_delegation *delegation;
        int res = 0;
@@ -171,7 +204,7 @@ int nfs_inode_return_delegation(struct inode *inode)
  */
 void nfs_return_all_delegations(struct super_block *sb)
 {
-       struct nfs4_client *clp = NFS_SB(sb)->nfs4_state;
+       struct nfs_client *clp = NFS_SB(sb)->nfs_client;
        struct nfs_delegation *delegation;
        struct inode *inode;
 
@@ -193,10 +226,53 @@ restart:
        spin_unlock(&clp->cl_lock);
 }
 
+int nfs_do_expire_all_delegations(void *ptr)
+{
+       struct nfs_client *clp = ptr;
+       struct nfs_delegation *delegation;
+       struct inode *inode;
+
+       allow_signal(SIGKILL);
+restart:
+       spin_lock(&clp->cl_lock);
+       if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) != 0)
+               goto out;
+       if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0)
+               goto out;
+       list_for_each_entry(delegation, &clp->cl_delegations, super_list) {
+               inode = igrab(delegation->inode);
+               if (inode == NULL)
+                       continue;
+               spin_unlock(&clp->cl_lock);
+               nfs_inode_return_delegation(inode);
+               iput(inode);
+               goto restart;
+       }
+out:
+       spin_unlock(&clp->cl_lock);
+       nfs_put_client(clp);
+       module_put_and_exit(0);
+}
+
+void nfs_expire_all_delegations(struct nfs_client *clp)
+{
+       struct task_struct *task;
+
+       __module_get(THIS_MODULE);
+       atomic_inc(&clp->cl_count);
+       task = kthread_run(nfs_do_expire_all_delegations, clp,
+                       "%u.%u.%u.%u-delegreturn",
+                       NIPQUAD(clp->cl_addr.sin_addr));
+       if (!IS_ERR(task))
+               return;
+       nfs_put_client(clp);
+       module_put(THIS_MODULE);
+}
+
 /*
  * Return all delegations following an NFS4ERR_CB_PATH_DOWN error.
  */
-void nfs_handle_cb_pathdown(struct nfs4_client *clp)
+void nfs_handle_cb_pathdown(struct nfs_client *clp)
 {
        struct nfs_delegation *delegation;
        struct inode *inode;
@@ -219,7 +295,7 @@ restart:
 
 struct recall_threadargs {
        struct inode *inode;
-       struct nfs4_client *clp;
+       struct nfs_client *clp;
        const nfs4_stateid *stateid;
 
        struct completion started;
@@ -230,7 +306,7 @@ static int recall_thread(void *data)
 {
        struct recall_threadargs *args = (struct recall_threadargs *)data;
        struct inode *inode = igrab(args->inode);
-       struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
+       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
        struct nfs_inode *nfsi = NFS_I(inode);
        struct nfs_delegation *delegation;
 
@@ -291,7 +367,7 @@ out_module_put:
 /*
  * Retrieve the inode associated with a delegation
  */
-struct inode *nfs_delegation_find_inode(struct nfs4_client *clp, const struct nfs_fh *fhandle)
+struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle)
 {
        struct nfs_delegation *delegation;
        struct inode *res = NULL;
@@ -309,7 +385,7 @@ struct inode *nfs_delegation_find_inode(struct nfs4_client *clp, const struct nf
 /*
  * Mark all delegations as needing to be reclaimed
  */
-void nfs_delegation_mark_reclaim(struct nfs4_client *clp)
+void nfs_delegation_mark_reclaim(struct nfs_client *clp)
 {
        struct nfs_delegation *delegation;
        spin_lock(&clp->cl_lock);
@@ -321,7 +397,7 @@ void nfs_delegation_mark_reclaim(struct nfs4_client *clp)
 /*
  * Reap all unclaimed delegations after reboot recovery is done
  */
-void nfs_delegation_reap_unclaimed(struct nfs4_client *clp)
+void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
 {
        struct nfs_delegation *delegation, *n;
        LIST_HEAD(head);
@@ -340,3 +416,22 @@ void nfs_delegation_reap_unclaimed(struct nfs4_client *clp)
                nfs_free_delegation(delegation);
        }
 }
+
+int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode)
+{
+       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+       struct nfs_inode *nfsi = NFS_I(inode);
+       struct nfs_delegation *delegation;
+       int res = 0;
+
+       if (nfsi->delegation_state == 0)
+               return 0;
+       spin_lock(&clp->cl_lock);
+       delegation = nfsi->delegation;
+       if (delegation != NULL) {
+               memcpy(dst->data, delegation->stateid.data, sizeof(dst->data));
+               res = 1;
+       }
+       spin_unlock(&clp->cl_lock);
+       return res;
+}