NFSv4.1: Allow parallel OPEN/OPEN_DOWNGRADE/CLOSE
[firefly-linux-kernel-4.4.55.git] / fs / nfs / nfs4proc.c
index e7f8d5ff2581a98269a262998beb43ccaca23e3c..0a279ad5421fffb18c4a3933c5bb9025f72c5eb2 100644 (file)
@@ -977,6 +977,7 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
        struct dentry *parent = dget_parent(dentry);
        struct inode *dir = parent->d_inode;
        struct nfs_server *server = NFS_SERVER(dir);
+       struct nfs_seqid *(*alloc_seqid)(struct nfs_seqid_counter *, gfp_t);
        struct nfs4_opendata *p;
 
        p = kzalloc(sizeof(*p), gfp_mask);
@@ -987,8 +988,9 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
        if (IS_ERR(p->f_label))
                goto err_free_p;
 
-       p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid, gfp_mask);
-       if (p->o_arg.seqid == NULL)
+       alloc_seqid = server->nfs_client->cl_mvops->alloc_seqid;
+       p->o_arg.seqid = alloc_seqid(&sp->so_seqid, gfp_mask);
+       if (IS_ERR(p->o_arg.seqid))
                goto err_free_label;
        nfs_sb_active(dentry->d_sb);
        p->dentry = dget(dentry);
@@ -1117,8 +1119,6 @@ static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode)
                return 0;
        if ((delegation->type & fmode) != fmode)
                return 0;
-       if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags))
-               return 0;
        if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
                return 0;
        nfs_mark_delegation_referenced(delegation);
@@ -1169,6 +1169,16 @@ static bool nfs_need_update_open_stateid(struct nfs4_state *state,
        return false;
 }
 
+static void nfs_resync_open_stateid_locked(struct nfs4_state *state)
+{
+       if (state->n_wronly)
+               set_bit(NFS_O_WRONLY_STATE, &state->flags);
+       if (state->n_rdonly)
+               set_bit(NFS_O_RDONLY_STATE, &state->flags);
+       if (state->n_rdwr)
+               set_bit(NFS_O_RDWR_STATE, &state->flags);
+}
+
 static void nfs_clear_open_stateid_locked(struct nfs4_state *state,
                nfs4_stateid *stateid, fmode_t fmode)
 {
@@ -1187,8 +1197,12 @@ static void nfs_clear_open_stateid_locked(struct nfs4_state *state,
        }
        if (stateid == NULL)
                return;
-       if (!nfs_need_update_open_stateid(state, stateid))
+       /* Handle races with OPEN */
+       if (!nfs4_stateid_match_other(stateid, &state->open_stateid) ||
+           !nfs4_stateid_is_newer(stateid, &state->open_stateid)) {
+               nfs_resync_open_stateid_locked(state);
                return;
+       }
        if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
                nfs4_stateid_copy(&state->stateid, stateid);
        nfs4_stateid_copy(&state->open_stateid, stateid);
@@ -2589,6 +2603,11 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
                case -NFS4ERR_OLD_STATEID:
                case -NFS4ERR_BAD_STATEID:
                case -NFS4ERR_EXPIRED:
+                       if (!nfs4_stateid_match(&calldata->arg.stateid,
+                                               &state->stateid)) {
+                               rpc_restart_call_prepare(task);
+                               goto out_release;
+                       }
                        if (calldata->arg.fmode == 0)
                                break;
                default:
@@ -2621,6 +2640,7 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
        is_rdwr = test_bit(NFS_O_RDWR_STATE, &state->flags);
        is_rdonly = test_bit(NFS_O_RDONLY_STATE, &state->flags);
        is_wronly = test_bit(NFS_O_WRONLY_STATE, &state->flags);
+       nfs4_stateid_copy(&calldata->arg.stateid, &state->stateid);
        /* Calculate the change in open mode */
        calldata->arg.fmode = 0;
        if (state->n_rdwr == 0) {
@@ -2733,6 +2753,7 @@ static bool nfs4_roc(struct inode *inode)
 int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait)
 {
        struct nfs_server *server = NFS_SERVER(state->inode);
+       struct nfs_seqid *(*alloc_seqid)(struct nfs_seqid_counter *, gfp_t);
        struct nfs4_closedata *calldata;
        struct nfs4_state_owner *sp = state->owner;
        struct rpc_task *task;
@@ -2759,10 +2780,10 @@ int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait)
        calldata->inode = state->inode;
        calldata->state = state;
        calldata->arg.fh = NFS_FH(state->inode);
-       calldata->arg.stateid = &state->open_stateid;
        /* Serialization for the sequence id */
-       calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid, gfp_mask);
-       if (calldata->arg.seqid == NULL)
+       alloc_seqid = server->nfs_client->cl_mvops->alloc_seqid;
+       calldata->arg.seqid = alloc_seqid(&state->owner->so_seqid, gfp_mask);
+       if (IS_ERR(calldata->arg.seqid))
                goto out_free_calldata;
        calldata->arg.fmode = 0;
        calldata->arg.bitmask = server->cache_consistency_bitmask;
@@ -4917,11 +4938,14 @@ static void nfs4_init_boot_verifier(const struct nfs_client *clp,
 }
 
 static unsigned int
-nfs4_init_nonuniform_client_string(const struct nfs_client *clp,
+nfs4_init_nonuniform_client_string(struct nfs_client *clp,
                                   char *buf, size_t len)
 {
        unsigned int result;
 
+       if (clp->cl_owner_id != NULL)
+               return strlcpy(buf, clp->cl_owner_id, len);
+
        rcu_read_lock();
        result = scnprintf(buf, len, "Linux NFSv4.0 %s/%s %s",
                                clp->cl_ipaddr,
@@ -4930,24 +4954,32 @@ nfs4_init_nonuniform_client_string(const struct nfs_client *clp,
                                rpc_peeraddr2str(clp->cl_rpcclient,
                                                        RPC_DISPLAY_PROTO));
        rcu_read_unlock();
+       clp->cl_owner_id = kstrdup(buf, GFP_KERNEL);
        return result;
 }
 
 static unsigned int
-nfs4_init_uniform_client_string(const struct nfs_client *clp,
+nfs4_init_uniform_client_string(struct nfs_client *clp,
                                char *buf, size_t len)
 {
        const char *nodename = clp->cl_rpcclient->cl_nodename;
+       unsigned int result;
+
+       if (clp->cl_owner_id != NULL)
+               return strlcpy(buf, clp->cl_owner_id, len);
 
        if (nfs4_client_id_uniquifier[0] != '\0')
-               return scnprintf(buf, len, "Linux NFSv%u.%u %s/%s",
+               result = scnprintf(buf, len, "Linux NFSv%u.%u %s/%s",
                                clp->rpc_ops->version,
                                clp->cl_minorversion,
                                nfs4_client_id_uniquifier,
                                nodename);
-       return scnprintf(buf, len, "Linux NFSv%u.%u %s",
+       else
+               result = scnprintf(buf, len, "Linux NFSv%u.%u %s",
                                clp->rpc_ops->version, clp->cl_minorversion,
                                nodename);
+       clp->cl_owner_id = kstrdup(buf, GFP_KERNEL);
+       return result;
 }
 
 /*
@@ -5489,7 +5521,7 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *
                goto out;
        seqid = nfs_alloc_seqid(&lsp->ls_seqid, GFP_KERNEL);
        status = -ENOMEM;
-       if (seqid == NULL)
+       if (IS_ERR(seqid))
                goto out;
        task = nfs4_do_unlck(request, nfs_file_open_context(request->fl_file), lsp, seqid);
        status = PTR_ERR(task);
@@ -5530,10 +5562,10 @@ static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl,
        p->arg.fh = NFS_FH(inode);
        p->arg.fl = &p->fl;
        p->arg.open_seqid = nfs_alloc_seqid(&lsp->ls_state->owner->so_seqid, gfp_mask);
-       if (p->arg.open_seqid == NULL)
+       if (IS_ERR(p->arg.open_seqid))
                goto out_free;
        p->arg.lock_seqid = nfs_alloc_seqid(&lsp->ls_seqid, gfp_mask);
-       if (p->arg.lock_seqid == NULL)
+       if (IS_ERR(p->arg.lock_seqid))
                goto out_free_seqid;
        p->arg.lock_stateid = &lsp->ls_stateid;
        p->arg.lock_owner.clientid = server->nfs_client->cl_clientid;
@@ -8386,6 +8418,7 @@ static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
        .match_stateid = nfs4_match_stateid,
        .find_root_sec = nfs4_find_root_sec,
        .free_lock_state = nfs4_release_lockowner,
+       .alloc_seqid = nfs_alloc_seqid,
        .call_sync_ops = &nfs40_call_sync_ops,
        .reboot_recovery_ops = &nfs40_reboot_recovery_ops,
        .nograce_recovery_ops = &nfs40_nograce_recovery_ops,
@@ -8394,6 +8427,12 @@ static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
 };
 
 #if defined(CONFIG_NFS_V4_1)
+static struct nfs_seqid *
+nfs_alloc_no_seqid(struct nfs_seqid_counter *arg1, gfp_t arg2)
+{
+       return NULL;
+}
+
 static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
        .minor_version = 1,
        .init_caps = NFS_CAP_READDIRPLUS
@@ -8407,6 +8446,7 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
        .match_stateid = nfs41_match_stateid,
        .find_root_sec = nfs41_find_root_sec,
        .free_lock_state = nfs41_free_lock_state,
+       .alloc_seqid = nfs_alloc_no_seqid,
        .call_sync_ops = &nfs41_call_sync_ops,
        .reboot_recovery_ops = &nfs41_reboot_recovery_ops,
        .nograce_recovery_ops = &nfs41_nograce_recovery_ops,
@@ -8433,6 +8473,7 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
        .find_root_sec = nfs41_find_root_sec,
        .free_lock_state = nfs41_free_lock_state,
        .call_sync_ops = &nfs41_call_sync_ops,
+       .alloc_seqid = nfs_alloc_no_seqid,
        .reboot_recovery_ops = &nfs41_reboot_recovery_ops,
        .nograce_recovery_ops = &nfs41_nograce_recovery_ops,
        .state_renewal_ops = &nfs41_state_renewal_ops,