Merge branch 'akpm' (patches from Andrew)
[linux-2.6-microblaze.git] / fs / nfs / callback_proc.c
index 64c214f..fa515d5 100644 (file)
@@ -215,9 +215,9 @@ static u32 pnfs_check_callback_stateid(struct pnfs_layout_hdr *lo,
 {
        u32 oldseq, newseq;
 
-       /* Is the stateid still not initialised? */
+       /* Is the stateid not initialised? */
        if (!pnfs_layout_is_valid(lo))
-               return NFS4ERR_DELAY;
+               return NFS4ERR_NOMATCHING_LAYOUT;
 
        /* Mismatched stateid? */
        if (!nfs4_stateid_match_other(&lo->plh_stateid, new))
@@ -273,7 +273,6 @@ static u32 initiate_file_draining(struct nfs_client *clp,
        rv = pnfs_check_callback_stateid(lo, &args->cbl_stateid);
        if (rv != NFS_OK)
                goto unlock;
-       pnfs_set_layout_stateid(lo, &args->cbl_stateid, true);
 
        /*
         * Enforce RFC5661 Section 12.5.5.2.1.5 (Bulk Recall and Return)
@@ -283,19 +282,23 @@ static u32 initiate_file_draining(struct nfs_client *clp,
                goto unlock;
        }
 
-       if (pnfs_mark_matching_lsegs_return(lo, &free_me_list,
+       pnfs_set_layout_stateid(lo, &args->cbl_stateid, true);
+       switch (pnfs_mark_matching_lsegs_return(lo, &free_me_list,
                                &args->cbl_range,
                                be32_to_cpu(args->cbl_stateid.seqid))) {
+       case 0:
+       case -EBUSY:
+               /* There are layout segments that need to be returned */
                rv = NFS4_OK;
-               goto unlock;
-       }
-
-       /* Embrace your forgetfulness! */
-       rv = NFS4ERR_NOMATCHING_LAYOUT;
+               break;
+       case -ENOENT:
+               /* Embrace your forgetfulness! */
+               rv = NFS4ERR_NOMATCHING_LAYOUT;
 
-       if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) {
-               NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo,
-                       &args->cbl_range);
+               if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) {
+                       NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo,
+                               &args->cbl_range);
+               }
        }
 unlock:
        spin_unlock(&ino->i_lock);
@@ -328,8 +331,6 @@ static u32 initiate_bulk_draining(struct nfs_client *clp,
 static u32 do_callback_layoutrecall(struct nfs_client *clp,
                                    struct cb_layoutrecallargs *args)
 {
-       write_seqcount_begin(&clp->cl_callback_count);
-       write_seqcount_end(&clp->cl_callback_count);
        if (args->cbl_recall_type == RETURN_FILE)
                return initiate_file_draining(clp, args);
        return initiate_bulk_draining(clp, args);
@@ -441,11 +442,14 @@ validate_seqid(const struct nfs4_slot_table *tbl, const struct nfs4_slot *slot,
  * a match.  If the slot is in use and the sequence numbers match, the
  * client is still waiting for a response to the original request.
  */
-static bool referring_call_exists(struct nfs_client *clp,
+static int referring_call_exists(struct nfs_client *clp,
                                  uint32_t nrclists,
-                                 struct referring_call_list *rclists)
+                                 struct referring_call_list *rclists,
+                                 spinlock_t *lock)
+       __releases(lock)
+       __acquires(lock)
 {
-       bool status = false;
+       int status = 0;
        int i, j;
        struct nfs4_session *session;
        struct nfs4_slot_table *tbl;
@@ -468,8 +472,10 @@ static bool referring_call_exists(struct nfs_client *clp,
 
                for (j = 0; j < rclist->rcl_nrefcalls; j++) {
                        ref = &rclist->rcl_refcalls[j];
+                       spin_unlock(lock);
                        status = nfs4_slot_wait_on_seqid(tbl, ref->rc_slotid,
                                        ref->rc_sequenceid, HZ >> 1) < 0;
+                       spin_lock(lock);
                        if (status)
                                goto out;
                }
@@ -546,7 +552,8 @@ __be32 nfs4_callback_sequence(void *argp, void *resp,
         * related callback was received before the response to the original
         * call.
         */
-       if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists)) {
+       if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists,
+                               &tbl->slot_tbl_lock) < 0) {
                status = htonl(NFS4ERR_DELAY);
                goto out_unlock;
        }
@@ -660,3 +667,57 @@ __be32 nfs4_callback_notify_lock(void *argp, void *resp,
        return htonl(NFS4_OK);
 }
 #endif /* CONFIG_NFS_V4_1 */
+#ifdef CONFIG_NFS_V4_2
+static void nfs4_copy_cb_args(struct nfs4_copy_state *cp_state,
+                               struct cb_offloadargs *args)
+{
+       cp_state->count = args->wr_count;
+       cp_state->error = args->error;
+       if (!args->error) {
+               cp_state->verf.committed = args->wr_writeverf.committed;
+               memcpy(&cp_state->verf.verifier.data[0],
+                       &args->wr_writeverf.verifier.data[0],
+                       NFS4_VERIFIER_SIZE);
+       }
+}
+
+__be32 nfs4_callback_offload(void *data, void *dummy,
+                            struct cb_process_state *cps)
+{
+       struct cb_offloadargs *args = data;
+       struct nfs_server *server;
+       struct nfs4_copy_state *copy;
+       bool found = false;
+
+       spin_lock(&cps->clp->cl_lock);
+       rcu_read_lock();
+       list_for_each_entry_rcu(server, &cps->clp->cl_superblocks,
+                               client_link) {
+               list_for_each_entry(copy, &server->ss_copies, copies) {
+                       if (memcmp(args->coa_stateid.other,
+                                       copy->stateid.other,
+                                       sizeof(args->coa_stateid.other)))
+                               continue;
+                       nfs4_copy_cb_args(copy, args);
+                       complete(&copy->completion);
+                       found = true;
+                       goto out;
+               }
+       }
+out:
+       rcu_read_unlock();
+       if (!found) {
+               copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
+               if (!copy) {
+                       spin_unlock(&cps->clp->cl_lock);
+                       return htonl(NFS4ERR_SERVERFAULT);
+               }
+               memcpy(&copy->stateid, &args->coa_stateid, NFS4_STATEID_SIZE);
+               nfs4_copy_cb_args(copy, args);
+               list_add_tail(&copy->copies, &cps->clp->pending_cb_stateids);
+       }
+       spin_unlock(&cps->clp->cl_lock);
+
+       return 0;
+}
+#endif /* CONFIG_NFS_V4_2 */