]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/nfs/delegation.c
i386: Remove strrchr assembler implementation
[linux-2.6-omap-h63xx.git] / fs / nfs / delegation.c
index 7f37d1bea83fc3f8c4bdadb968c4f295e1b32cd7..af8b235d405dba2ea74c260fd13aba31a5ac46f1 100644 (file)
 #include "delegation.h"
 #include "internal.h"
 
-static void nfs_free_delegation(struct nfs_delegation *delegation)
+static void nfs_do_free_delegation(struct nfs_delegation *delegation)
 {
-       if (delegation->cred)
-               put_rpccred(delegation->cred);
        kfree(delegation);
 }
 
+static void nfs_free_delegation_callback(struct rcu_head *head)
+{
+       struct nfs_delegation *delegation = container_of(head, struct nfs_delegation, rcu);
+
+       nfs_do_free_delegation(delegation);
+}
+
+static void nfs_free_delegation(struct nfs_delegation *delegation)
+{
+       struct rpc_cred *cred;
+
+       cred = rcu_dereference(delegation->cred);
+       rcu_assign_pointer(delegation->cred, NULL);
+       call_rcu(&delegation->rcu, nfs_free_delegation_callback);
+       if (cred)
+               put_rpccred(cred);
+}
+
 static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state)
 {
        struct inode *inode = state->inode;
@@ -36,7 +52,7 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_
        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)
+               if (nfs_file_open_context(fl->fl_file) != ctx)
                        continue;
                status = nfs4_lock_delegation_recall(state, fl);
                if (status >= 0)
@@ -57,7 +73,7 @@ out_err:
        return status;
 }
 
-static void nfs_delegation_claim_opens(struct inode *inode)
+static void nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
        struct nfs_open_context *ctx;
@@ -72,9 +88,11 @@ again:
                        continue;
                if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
                        continue;
+               if (memcmp(state->stateid.data, stateid->data, sizeof(state->stateid.data)) != 0)
+                       continue;
                get_nfs_open_context(ctx);
                spin_unlock(&inode->i_lock);
-               err = nfs4_open_delegation_recall(ctx->dentry, state);
+               err = nfs4_open_delegation_recall(ctx, state, stateid);
                if (err >= 0)
                        err = nfs_delegation_claim_locks(ctx, state);
                put_nfs_open_context(ctx);
@@ -91,6 +109,7 @@ again:
 void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
 {
        struct nfs_delegation *delegation = NFS_I(inode)->delegation;
+       struct rpc_cred *oldcred;
 
        if (delegation == NULL)
                return;
@@ -98,11 +117,12 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, st
                        sizeof(delegation->stateid.data));
        delegation->type = res->delegation_type;
        delegation->maxsize = res->maxsize;
-       put_rpccred(cred);
+       oldcred = delegation->cred;
        delegation->cred = get_rpccred(cred);
        delegation->flags &= ~NFS_DELEGATION_NEED_RECLAIM;
        NFS_I(inode)->delegation_state = delegation->type;
        smp_wmb();
+       put_rpccred(oldcred);
 }
 
 /*
@@ -115,10 +135,6 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
        struct nfs_delegation *delegation;
        int status = 0;
 
-       /* 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;
@@ -131,10 +147,10 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
        delegation->inode = inode;
 
        spin_lock(&clp->cl_lock);
-       if (nfsi->delegation == NULL) {
-               list_add(&delegation->super_list, &clp->cl_delegations);
-               nfsi->delegation = delegation;
+       if (rcu_dereference(nfsi->delegation) == NULL) {
+               list_add_rcu(&delegation->super_list, &clp->cl_delegations);
                nfsi->delegation_state = delegation->type;
+               rcu_assign_pointer(nfsi->delegation, delegation);
                delegation = NULL;
        } else {
                if (memcmp(&delegation->stateid, &nfsi->delegation->stateid,
@@ -145,6 +161,12 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
                        status = -EIO;
                }
        }
+
+       /* Ensure we revalidate the attributes and page cache! */
+       spin_lock(&inode->i_lock);
+       nfsi->cache_validity |= NFS_INO_REVAL_FORCED;
+       spin_unlock(&inode->i_lock);
+
        spin_unlock(&clp->cl_lock);
        kfree(delegation);
        return status;
@@ -170,33 +192,55 @@ 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)
+static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation)
 {
        struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
        struct nfs_inode *nfsi = NFS_I(inode);
-       struct nfs_delegation *delegation;
-       int res = 0;
 
        nfs_msync_inode(inode);
        down_read(&clp->cl_sem);
        /* Guard against new delegated open calls */
        down_write(&nfsi->rwsem);
-       spin_lock(&clp->cl_lock);
-       delegation = nfsi->delegation;
-       if (delegation != NULL) {
-               list_del_init(&delegation->super_list);
-               nfsi->delegation = NULL;
-               nfsi->delegation_state = 0;
-       }
-       spin_unlock(&clp->cl_lock);
-       nfs_delegation_claim_opens(inode);
+       nfs_delegation_claim_opens(inode, &delegation->stateid);
        up_write(&nfsi->rwsem);
        up_read(&clp->cl_sem);
        nfs_msync_inode(inode);
 
-       if (delegation != NULL)
-               res = nfs_do_return_delegation(inode, delegation);
-       return res;
+       return nfs_do_return_delegation(inode, delegation);
+}
+
+static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid)
+{
+       struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);
+
+       if (delegation == NULL)
+               goto nomatch;
+       if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data,
+                               sizeof(delegation->stateid.data)) != 0)
+               goto nomatch;
+       list_del_rcu(&delegation->super_list);
+       nfsi->delegation_state = 0;
+       rcu_assign_pointer(nfsi->delegation, NULL);
+       return delegation;
+nomatch:
+       return NULL;
+}
+
+int nfs_inode_return_delegation(struct inode *inode)
+{
+       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+       struct nfs_inode *nfsi = NFS_I(inode);
+       struct nfs_delegation *delegation;
+       int err = 0;
+
+       if (rcu_dereference(nfsi->delegation) != NULL) {
+               spin_lock(&clp->cl_lock);
+               delegation = nfs_detach_delegation_locked(nfsi, NULL);
+               spin_unlock(&clp->cl_lock);
+               if (delegation != NULL)
+                       err = __nfs_inode_return_delegation(inode, delegation);
+       }
+       return err;
 }
 
 /*
@@ -211,19 +255,23 @@ void nfs_return_all_delegations(struct super_block *sb)
        if (clp == NULL)
                return;
 restart:
-       spin_lock(&clp->cl_lock);
-       list_for_each_entry(delegation, &clp->cl_delegations, super_list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
                if (delegation->inode->i_sb != sb)
                        continue;
                inode = igrab(delegation->inode);
                if (inode == NULL)
                        continue;
+               spin_lock(&clp->cl_lock);
+               delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
                spin_unlock(&clp->cl_lock);
-               nfs_inode_return_delegation(inode);
+               rcu_read_unlock();
+               if (delegation != NULL)
+                       __nfs_inode_return_delegation(inode, delegation);
                iput(inode);
                goto restart;
        }
-       spin_unlock(&clp->cl_lock);
+       rcu_read_unlock();
 }
 
 static int nfs_do_expire_all_delegations(void *ptr)
@@ -234,22 +282,26 @@ static int nfs_do_expire_all_delegations(void *ptr)
 
        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) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
                inode = igrab(delegation->inode);
                if (inode == NULL)
                        continue;
+               spin_lock(&clp->cl_lock);
+               delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
                spin_unlock(&clp->cl_lock);
-               nfs_inode_return_delegation(inode);
+               rcu_read_unlock();
+               if (delegation)
+                       __nfs_inode_return_delegation(inode, delegation);
                iput(inode);
                goto restart;
        }
+       rcu_read_unlock();
 out:
-       spin_unlock(&clp->cl_lock);
        nfs_put_client(clp);
        module_put_and_exit(0);
 }
@@ -280,17 +332,21 @@ void nfs_handle_cb_pathdown(struct nfs_client *clp)
        if (clp == NULL)
                return;
 restart:
-       spin_lock(&clp->cl_lock);
-       list_for_each_entry(delegation, &clp->cl_delegations, super_list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
                inode = igrab(delegation->inode);
                if (inode == NULL)
                        continue;
+               spin_lock(&clp->cl_lock);
+               delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
                spin_unlock(&clp->cl_lock);
-               nfs_inode_return_delegation(inode);
+               rcu_read_unlock();
+               if (delegation != NULL)
+                       __nfs_inode_return_delegation(inode, delegation);
                iput(inode);
                goto restart;
        }
-       spin_unlock(&clp->cl_lock);
+       rcu_read_unlock();
 }
 
 struct recall_threadargs {
@@ -316,21 +372,14 @@ static int recall_thread(void *data)
        down_read(&clp->cl_sem);
        down_write(&nfsi->rwsem);
        spin_lock(&clp->cl_lock);
-       delegation = nfsi->delegation;
-       if (delegation != NULL && memcmp(delegation->stateid.data,
-                               args->stateid->data,
-                               sizeof(delegation->stateid.data)) == 0) {
-               list_del_init(&delegation->super_list);
-               nfsi->delegation = NULL;
-               nfsi->delegation_state = 0;
+       delegation = nfs_detach_delegation_locked(nfsi, args->stateid);
+       if (delegation != NULL)
                args->result = 0;
-       } else {
-               delegation = NULL;
+       else
                args->result = -ENOENT;
-       }
        spin_unlock(&clp->cl_lock);
        complete(&args->started);
-       nfs_delegation_claim_opens(inode);
+       nfs_delegation_claim_opens(inode, args->stateid);
        up_write(&nfsi->rwsem);
        up_read(&clp->cl_sem);
        nfs_msync_inode(inode);
@@ -371,14 +420,14 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs
 {
        struct nfs_delegation *delegation;
        struct inode *res = NULL;
-       spin_lock(&clp->cl_lock);
-       list_for_each_entry(delegation, &clp->cl_delegations, super_list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
                if (nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
                        res = igrab(delegation->inode);
                        break;
                }
        }
-       spin_unlock(&clp->cl_lock);
+       rcu_read_unlock();
        return res;
 }
 
@@ -388,10 +437,10 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs
 void nfs_delegation_mark_reclaim(struct nfs_client *clp)
 {
        struct nfs_delegation *delegation;
-       spin_lock(&clp->cl_lock);
-       list_for_each_entry(delegation, &clp->cl_delegations, super_list)
+       rcu_read_lock();
+       list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list)
                delegation->flags |= NFS_DELEGATION_NEED_RECLAIM;
-       spin_unlock(&clp->cl_lock);
+       rcu_read_unlock();
 }
 
 /*
@@ -399,39 +448,35 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp)
  */
 void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
 {
-       struct nfs_delegation *delegation, *n;
-       LIST_HEAD(head);
-       spin_lock(&clp->cl_lock);
-       list_for_each_entry_safe(delegation, n, &clp->cl_delegations, super_list) {
+       struct nfs_delegation *delegation;
+restart:
+       rcu_read_lock();
+       list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
                if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0)
                        continue;
-               list_move(&delegation->super_list, &head);
-               NFS_I(delegation->inode)->delegation = NULL;
-               NFS_I(delegation->inode)->delegation_state = 0;
-       }
-       spin_unlock(&clp->cl_lock);
-       while(!list_empty(&head)) {
-               delegation = list_entry(head.next, struct nfs_delegation, super_list);
-               list_del(&delegation->super_list);
-               nfs_free_delegation(delegation);
+               spin_lock(&clp->cl_lock);
+               delegation = nfs_detach_delegation_locked(NFS_I(delegation->inode), NULL);
+               spin_unlock(&clp->cl_lock);
+               rcu_read_unlock();
+               if (delegation != NULL)
+                       nfs_free_delegation(delegation);
+               goto restart;
        }
+       rcu_read_unlock();
 }
 
 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;
+       int ret = 0;
 
-       if (nfsi->delegation_state == 0)
-               return 0;
-       spin_lock(&clp->cl_lock);
-       delegation = nfsi->delegation;
+       rcu_read_lock();
+       delegation = rcu_dereference(nfsi->delegation);
        if (delegation != NULL) {
                memcpy(dst->data, delegation->stateid.data, sizeof(dst->data));
-               res = 1;
+               ret = 1;
        }
-       spin_unlock(&clp->cl_lock);
-       return res;
+       rcu_read_unlock();
+       return ret;
 }