Merge tag '5.10-rc-smb3-fixes-part1' of git://git.samba.org/sfrench/cifs-2.6
[linux-2.6-microblaze.git] / fs / nfs / nfs4proc.c
index 6e95c85..9e0ca9b 100644 (file)
@@ -63,6 +63,7 @@
 #include "callback.h"
 #include "pnfs.h"
 #include "netns.h"
+#include "sysfs.h"
 #include "nfs4idmap.h"
 #include "nfs4session.h"
 #include "fscache.h"
 
 #include "nfs4trace.h"
 
+#ifdef CONFIG_NFS_V4_2
+#include "nfs42.h"
+#endif /* CONFIG_NFS_V4_2 */
+
 #define NFSDBG_FACILITY                NFSDBG_PROC
 
 #define NFS4_BITMASK_SZ                3
@@ -107,6 +112,9 @@ static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *,
 static int nfs41_free_stateid(struct nfs_server *, const nfs4_stateid *,
                const struct cred *, bool);
 #endif
+static void nfs4_bitmask_adjust(__u32 *bitmask, struct inode *inode,
+               struct nfs_server *server,
+               struct nfs4_label *label);
 
 #ifdef CONFIG_NFS_V4_SECURITY_LABEL
 static inline struct nfs4_label *
@@ -1547,19 +1555,6 @@ static void nfs_state_log_update_open_stateid(struct nfs4_state *state)
                wake_up_all(&state->waitq);
 }
 
-static void nfs_state_log_out_of_order_open_stateid(struct nfs4_state *state,
-               const nfs4_stateid *stateid)
-{
-       u32 state_seqid = be32_to_cpu(state->open_stateid.seqid);
-       u32 stateid_seqid = be32_to_cpu(stateid->seqid);
-
-       if (stateid_seqid == state_seqid + 1U ||
-           (stateid_seqid == 1U && state_seqid == 0xffffffffU))
-               nfs_state_log_update_open_stateid(state);
-       else
-               set_bit(NFS_STATE_CHANGE_WAIT, &state->flags);
-}
-
 static void nfs_test_and_clear_all_open_stateid(struct nfs4_state *state)
 {
        struct nfs_client *clp = state->owner->so_server->nfs_client;
@@ -1585,21 +1580,19 @@ static void nfs_test_and_clear_all_open_stateid(struct nfs4_state *state)
  * i.e. The stateid seqids have to be initialised to 1, and
  * are then incremented on every state transition.
  */
-static bool nfs_need_update_open_stateid(struct nfs4_state *state,
+static bool nfs_stateid_is_sequential(struct nfs4_state *state,
                const nfs4_stateid *stateid)
 {
-       if (test_bit(NFS_OPEN_STATE, &state->flags) == 0 ||
-           !nfs4_stateid_match_other(stateid, &state->open_stateid)) {
+       if (test_bit(NFS_OPEN_STATE, &state->flags)) {
+               /* The common case - we're updating to a new sequence number */
+               if (nfs4_stateid_match_other(stateid, &state->open_stateid) &&
+                       nfs4_stateid_is_next(&state->open_stateid, stateid)) {
+                       return true;
+               }
+       } else {
+               /* This is the first OPEN in this generation */
                if (stateid->seqid == cpu_to_be32(1))
-                       nfs_state_log_update_open_stateid(state);
-               else
-                       set_bit(NFS_STATE_CHANGE_WAIT, &state->flags);
-               return true;
-       }
-
-       if (nfs4_stateid_is_newer(stateid, &state->open_stateid)) {
-               nfs_state_log_out_of_order_open_stateid(state, stateid);
-               return true;
+                       return true;
        }
        return false;
 }
@@ -1673,16 +1666,16 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state,
        int status = 0;
        for (;;) {
 
-               if (!nfs_need_update_open_stateid(state, stateid))
-                       return;
-               if (!test_bit(NFS_STATE_CHANGE_WAIT, &state->flags))
+               if (nfs_stateid_is_sequential(state, stateid))
                        break;
+
                if (status)
                        break;
                /* Rely on seqids for serialisation with NFSv4.0 */
                if (!nfs4_has_session(NFS_SERVER(state->inode)->nfs_client))
                        break;
 
+               set_bit(NFS_STATE_CHANGE_WAIT, &state->flags);
                prepare_to_wait(&state->waitq, &wait, TASK_KILLABLE);
                /*
                 * Ensure we process the state changes in the same order
@@ -1693,6 +1686,7 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state,
                spin_unlock(&state->owner->so_lock);
                rcu_read_unlock();
                trace_nfs4_open_stateid_update_wait(state->inode, stateid, 0);
+
                if (!signal_pending(current)) {
                        if (schedule_timeout(5*HZ) == 0)
                                status = -EAGAIN;
@@ -3435,7 +3429,8 @@ static bool nfs4_refresh_open_old_stateid(nfs4_stateid *dst,
        __be32 seqid_open;
        u32 dst_seqid;
        bool ret;
-       int seq;
+       int seq, status = -EAGAIN;
+       DEFINE_WAIT(wait);
 
        for (;;) {
                ret = false;
@@ -3447,15 +3442,41 @@ static bool nfs4_refresh_open_old_stateid(nfs4_stateid *dst,
                                continue;
                        break;
                }
+
+               write_seqlock(&state->seqlock);
                seqid_open = state->open_stateid.seqid;
-               if (read_seqretry(&state->seqlock, seq))
-                       continue;
 
                dst_seqid = be32_to_cpu(dst->seqid);
-               if ((s32)(dst_seqid - be32_to_cpu(seqid_open)) >= 0)
-                       dst->seqid = cpu_to_be32(dst_seqid + 1);
-               else
+
+               /* Did another OPEN bump the state's seqid?  try again: */
+               if ((s32)(be32_to_cpu(seqid_open) - dst_seqid) > 0) {
                        dst->seqid = seqid_open;
+                       write_sequnlock(&state->seqlock);
+                       ret = true;
+                       break;
+               }
+
+               /* server says we're behind but we haven't seen the update yet */
+               set_bit(NFS_STATE_CHANGE_WAIT, &state->flags);
+               prepare_to_wait(&state->waitq, &wait, TASK_KILLABLE);
+               write_sequnlock(&state->seqlock);
+               trace_nfs4_close_stateid_update_wait(state->inode, dst, 0);
+
+               if (signal_pending(current))
+                       status = -EINTR;
+               else
+                       if (schedule_timeout(5*HZ) != 0)
+                               status = 0;
+
+               finish_wait(&state->waitq, &wait);
+
+               if (!status)
+                       continue;
+               if (status == -EINTR)
+                       break;
+
+               /* we slept the whole 5 seconds, we must have lost a seqid */
+               dst->seqid = cpu_to_be32(dst_seqid + 1);
                ret = true;
                break;
        }
@@ -3632,9 +3653,10 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
 
        if (calldata->arg.fmode == 0 || calldata->arg.fmode == FMODE_READ) {
                /* Close-to-open cache consistency revalidation */
-               if (!nfs4_have_delegation(inode, FMODE_READ))
+               if (!nfs4_have_delegation(inode, FMODE_READ)) {
                        calldata->arg.bitmask = NFS_SERVER(inode)->cache_consistency_bitmask;
-               else
+                       nfs4_bitmask_adjust(calldata->arg.bitmask, inode, NFS_SERVER(inode), NULL);
+               } else
                        calldata->arg.bitmask = NULL;
        }
 
@@ -5255,28 +5277,60 @@ static bool nfs4_read_stateid_changed(struct rpc_task *task,
        return true;
 }
 
-static int nfs4_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
+static bool nfs4_read_plus_not_supported(struct rpc_task *task,
+                                        struct nfs_pgio_header *hdr)
 {
+       struct nfs_server *server = NFS_SERVER(hdr->inode);
+       struct rpc_message *msg = &task->tk_msg;
 
+       if (msg->rpc_proc == &nfs4_procedures[NFSPROC4_CLNT_READ_PLUS] &&
+           server->caps & NFS_CAP_READ_PLUS && task->tk_status == -ENOTSUPP) {
+               server->caps &= ~NFS_CAP_READ_PLUS;
+               msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ];
+               rpc_restart_call_prepare(task);
+               return true;
+       }
+       return false;
+}
+
+static int nfs4_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
+{
        dprintk("--> %s\n", __func__);
 
        if (!nfs4_sequence_done(task, &hdr->res.seq_res))
                return -EAGAIN;
        if (nfs4_read_stateid_changed(task, &hdr->args))
                return -EAGAIN;
+       if (nfs4_read_plus_not_supported(task, hdr))
+               return -EAGAIN;
        if (task->tk_status > 0)
                nfs_invalidate_atime(hdr->inode);
        return hdr->pgio_done_cb ? hdr->pgio_done_cb(task, hdr) :
                                    nfs4_read_done_cb(task, hdr);
 }
 
+#ifdef CONFIG_NFS_V4_2
+static void nfs42_read_plus_support(struct nfs_server *server, struct rpc_message *msg)
+{
+       if (server->caps & NFS_CAP_READ_PLUS)
+               msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ_PLUS];
+       else
+               msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ];
+}
+#else
+static void nfs42_read_plus_support(struct nfs_server *server, struct rpc_message *msg)
+{
+       msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ];
+}
+#endif /* CONFIG_NFS_V4_2 */
+
 static void nfs4_proc_read_setup(struct nfs_pgio_header *hdr,
                                 struct rpc_message *msg)
 {
        hdr->timestamp   = jiffies;
        if (!hdr->pgio_done_cb)
                hdr->pgio_done_cb = nfs4_read_done_cb;
-       msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ];
+       nfs42_read_plus_support(NFS_SERVER(hdr->inode), msg);
        nfs4_init_sequence(&hdr->args.seq_args, &hdr->res.seq_res, 0, 0);
 }
 
@@ -5360,6 +5414,38 @@ bool nfs4_write_need_cache_consistency_data(struct nfs_pgio_header *hdr)
        return nfs4_have_delegation(hdr->inode, FMODE_READ) == 0;
 }
 
+static void nfs4_bitmask_adjust(__u32 *bitmask, struct inode *inode,
+                               struct nfs_server *server,
+                               struct nfs4_label *label)
+{
+
+       unsigned long cache_validity = READ_ONCE(NFS_I(inode)->cache_validity);
+
+       if ((cache_validity & NFS_INO_INVALID_DATA) ||
+               (cache_validity & NFS_INO_REVAL_PAGECACHE) ||
+               (cache_validity & NFS_INO_REVAL_FORCED) ||
+               (cache_validity & NFS_INO_INVALID_OTHER))
+               nfs4_bitmap_copy_adjust(bitmask, nfs4_bitmask(server, label), inode);
+
+       if (cache_validity & NFS_INO_INVALID_ATIME)
+               bitmask[1] |= FATTR4_WORD1_TIME_ACCESS;
+       if (cache_validity & NFS_INO_INVALID_ACCESS)
+               bitmask[0] |= FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER |
+                               FATTR4_WORD1_OWNER_GROUP;
+       if (cache_validity & NFS_INO_INVALID_ACL)
+               bitmask[0] |= FATTR4_WORD0_ACL;
+       if (cache_validity & NFS_INO_INVALID_LABEL)
+               bitmask[2] |= FATTR4_WORD2_SECURITY_LABEL;
+       if (cache_validity & NFS_INO_INVALID_CTIME)
+               bitmask[0] |= FATTR4_WORD0_CHANGE;
+       if (cache_validity & NFS_INO_INVALID_MTIME)
+               bitmask[1] |= FATTR4_WORD1_TIME_MODIFY;
+       if (cache_validity & NFS_INO_INVALID_SIZE)
+               bitmask[0] |= FATTR4_WORD0_SIZE;
+       if (cache_validity & NFS_INO_INVALID_BLOCKS)
+               bitmask[1] |= FATTR4_WORD1_SPACE_USED;
+}
+
 static void nfs4_proc_write_setup(struct nfs_pgio_header *hdr,
                                  struct rpc_message *msg,
                                  struct rpc_clnt **clnt)
@@ -5369,8 +5455,10 @@ static void nfs4_proc_write_setup(struct nfs_pgio_header *hdr,
        if (!nfs4_write_need_cache_consistency_data(hdr)) {
                hdr->args.bitmask = NULL;
                hdr->res.fattr = NULL;
-       } else
+       } else {
                hdr->args.bitmask = server->cache_consistency_bitmask;
+               nfs4_bitmask_adjust(hdr->args.bitmask, hdr->inode, server, NULL);
+       }
 
        if (!hdr->pgio_done_cb)
                hdr->pgio_done_cb = nfs4_write_done_cb;
@@ -6006,9 +6094,34 @@ static void nfs4_init_boot_verifier(const struct nfs_client *clp,
        memcpy(bootverf->data, verf, sizeof(bootverf->data));
 }
 
+static size_t
+nfs4_get_uniquifier(struct nfs_client *clp, char *buf, size_t buflen)
+{
+       struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
+       struct nfs_netns_client *nn_clp = nn->nfs_client;
+       const char *id;
+
+       buf[0] = '\0';
+
+       if (nn_clp) {
+               rcu_read_lock();
+               id = rcu_dereference(nn_clp->identifier);
+               if (id)
+                       strscpy(buf, id, buflen);
+               rcu_read_unlock();
+       }
+
+       if (nfs4_client_id_uniquifier[0] != '\0' && buf[0] == '\0')
+               strscpy(buf, nfs4_client_id_uniquifier, buflen);
+
+       return strlen(buf);
+}
+
 static int
 nfs4_init_nonuniform_client_string(struct nfs_client *clp)
 {
+       char buf[NFS4_CLIENT_ID_UNIQ_LEN];
+       size_t buflen;
        size_t len;
        char *str;
 
@@ -6022,8 +6135,11 @@ nfs4_init_nonuniform_client_string(struct nfs_client *clp)
                strlen(rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)) +
                1;
        rcu_read_unlock();
-       if (nfs4_client_id_uniquifier[0] != '\0')
-               len += strlen(nfs4_client_id_uniquifier) + 1;
+
+       buflen = nfs4_get_uniquifier(clp, buf, sizeof(buf));
+       if (buflen)
+               len += buflen + 1;
+
        if (len > NFS4_OPAQUE_LIMIT + 1)
                return -EINVAL;
 
@@ -6037,10 +6153,9 @@ nfs4_init_nonuniform_client_string(struct nfs_client *clp)
                return -ENOMEM;
 
        rcu_read_lock();
-       if (nfs4_client_id_uniquifier[0] != '\0')
+       if (buflen)
                scnprintf(str, len, "Linux NFSv4.0 %s/%s/%s",
-                         clp->cl_rpcclient->cl_nodename,
-                         nfs4_client_id_uniquifier,
+                         clp->cl_rpcclient->cl_nodename, buf,
                          rpc_peeraddr2str(clp->cl_rpcclient,
                                           RPC_DISPLAY_ADDR));
        else
@@ -6054,51 +6169,24 @@ nfs4_init_nonuniform_client_string(struct nfs_client *clp)
        return 0;
 }
 
-static int
-nfs4_init_uniquifier_client_string(struct nfs_client *clp)
-{
-       size_t len;
-       char *str;
-
-       len = 10 + 10 + 1 + 10 + 1 +
-               strlen(nfs4_client_id_uniquifier) + 1 +
-               strlen(clp->cl_rpcclient->cl_nodename) + 1;
-
-       if (len > NFS4_OPAQUE_LIMIT + 1)
-               return -EINVAL;
-
-       /*
-        * Since this string is allocated at mount time, and held until the
-        * nfs_client is destroyed, we can use GFP_KERNEL here w/o worrying
-        * about a memory-reclaim deadlock.
-        */
-       str = kmalloc(len, GFP_KERNEL);
-       if (!str)
-               return -ENOMEM;
-
-       scnprintf(str, len, "Linux NFSv%u.%u %s/%s",
-                       clp->rpc_ops->version, clp->cl_minorversion,
-                       nfs4_client_id_uniquifier,
-                       clp->cl_rpcclient->cl_nodename);
-       clp->cl_owner_id = str;
-       return 0;
-}
-
 static int
 nfs4_init_uniform_client_string(struct nfs_client *clp)
 {
+       char buf[NFS4_CLIENT_ID_UNIQ_LEN];
+       size_t buflen;
        size_t len;
        char *str;
 
        if (clp->cl_owner_id != NULL)
                return 0;
 
-       if (nfs4_client_id_uniquifier[0] != '\0')
-               return nfs4_init_uniquifier_client_string(clp);
-
        len = 10 + 10 + 1 + 10 + 1 +
                strlen(clp->cl_rpcclient->cl_nodename) + 1;
 
+       buflen = nfs4_get_uniquifier(clp, buf, sizeof(buf));
+       if (buflen)
+               len += buflen + 1;
+
        if (len > NFS4_OPAQUE_LIMIT + 1)
                return -EINVAL;
 
@@ -6111,9 +6199,14 @@ nfs4_init_uniform_client_string(struct nfs_client *clp)
        if (!str)
                return -ENOMEM;
 
-       scnprintf(str, len, "Linux NFSv%u.%u %s",
-                       clp->rpc_ops->version, clp->cl_minorversion,
-                       clp->cl_rpcclient->cl_nodename);
+       if (buflen)
+               scnprintf(str, len, "Linux NFSv%u.%u %s/%s",
+                         clp->rpc_ops->version, clp->cl_minorversion,
+                         buf, clp->cl_rpcclient->cl_nodename);
+       else
+               scnprintf(str, len, "Linux NFSv%u.%u %s",
+                         clp->rpc_ops->version, clp->cl_minorversion,
+                         clp->cl_rpcclient->cl_nodename);
        clp->cl_owner_id = str;
        return 0;
 }
@@ -6406,6 +6499,7 @@ static int _nfs4_proc_delegreturn(struct inode *inode, const struct cred *cred,
        data->args.fhandle = &data->fh;
        data->args.stateid = &data->stateid;
        data->args.bitmask = server->cache_consistency_bitmask;
+       nfs4_bitmask_adjust(data->args.bitmask, inode, server, NULL);
        nfs_copy_fh(&data->fh, NFS_FH(inode));
        nfs4_stateid_copy(&data->stateid, stateid);
        data->res.fattr = &data->fattr;
@@ -7440,7 +7534,7 @@ nfs4_listxattr_nfs4_label(struct inode *inode, char *list, size_t list_len)
 
        if (nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL)) {
                len = security_inode_listsecurity(inode, list, list_len);
-               if (list_len && len > list_len)
+               if (len >= 0 && list_len && len > list_len)
                        return -ERANGE;
        }
        return len;
@@ -8039,9 +8133,11 @@ int nfs4_proc_secinfo(struct inode *dir, const struct qstr *name,
  * both PNFS and NON_PNFS flags set, and not having one of NON_PNFS, PNFS, or
  * DS flags set.
  */
-static int nfs4_check_cl_exchange_flags(u32 flags)
+static int nfs4_check_cl_exchange_flags(u32 flags, u32 version)
 {
-       if (flags & ~EXCHGID4_FLAG_MASK_R)
+       if (version >= 2 && (flags & ~EXCHGID4_2_FLAG_MASK_R))
+               goto out_inval;
+       else if (version < 2 && (flags & ~EXCHGID4_FLAG_MASK_R))
                goto out_inval;
        if ((flags & EXCHGID4_FLAG_USE_PNFS_MDS) &&
            (flags & EXCHGID4_FLAG_USE_NON_PNFS))
@@ -8454,7 +8550,8 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, const struct cred *cre
        if (status  != 0)
                goto out;
 
-       status = nfs4_check_cl_exchange_flags(resp->flags);
+       status = nfs4_check_cl_exchange_flags(resp->flags,
+                       clp->cl_mvops->minor_version);
        if (status  != 0)
                goto out;
 
@@ -9693,7 +9790,6 @@ _nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,
                .rpc_argp = &args,
                .rpc_resp = &res,
        };
-       struct rpc_clnt *clnt = server->client;
        struct nfs4_call_sync_data data = {
                .seq_server = server,
                .seq_args = &args.seq_args,
@@ -9710,8 +9806,7 @@ _nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,
        int status;
 
        if (use_integrity) {
-               clnt = server->nfs_client->cl_rpcclient;
-               task_setup.rpc_client = clnt;
+               task_setup.rpc_client = server->nfs_client->cl_rpcclient;
 
                cred = nfs4_get_clid_cred(server->nfs_client);
                msg.rpc_cred = cred;
@@ -10165,7 +10260,8 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
                | NFS_CAP_SEEK
                | NFS_CAP_LAYOUTSTATS
                | NFS_CAP_CLONE
-               | NFS_CAP_LAYOUTERROR,
+               | NFS_CAP_LAYOUTERROR
+               | NFS_CAP_READ_PLUS,
        .init_client = nfs41_init_client,
        .shutdown_client = nfs41_shutdown_client,
        .match_stateid = nfs41_match_stateid,