NFSv4.2: Add client support for the generic 'layouterror' RPC call
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Fri, 8 Feb 2019 15:31:05 +0000 (10:31 -0500)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Fri, 1 Mar 2019 21:20:16 +0000 (16:20 -0500)
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
fs/nfs/nfs42.h
fs/nfs/nfs42proc.c
fs/nfs/nfs42xdr.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4xdr.c
include/linux/nfs4.h
include/linux/nfs_fs_sb.h
include/linux/nfs_xdr.h

index 19ec38f85ce0724fb11e8d879c9688a12b3929d4..901cca7542f944ce8829131d4262aee5529c0df0 100644 (file)
@@ -20,5 +20,8 @@ loff_t nfs42_proc_llseek(struct file *, loff_t, int);
 int nfs42_proc_layoutstats_generic(struct nfs_server *,
                                   struct nfs42_layoutstat_data *);
 int nfs42_proc_clone(struct file *, struct file *, loff_t, loff_t, loff_t);
+int nfs42_proc_layouterror(struct pnfs_layout_segment *lseg,
+                          const struct nfs42_layout_error *errors,
+                          size_t n);
 
 #endif /* __LINUX_FS_NFS_NFS4_2_H */
index fed06fd9998d322a202befd46f8c2546dedbba84..ff6f85fb676b7c1094878b269d35f2f127ca5fb5 100644 (file)
@@ -672,6 +672,170 @@ int nfs42_proc_layoutstats_generic(struct nfs_server *server,
        return 0;
 }
 
+static struct nfs42_layouterror_data *
+nfs42_alloc_layouterror_data(struct pnfs_layout_segment *lseg, gfp_t gfp_flags)
+{
+       struct nfs42_layouterror_data *data;
+       struct inode *inode = lseg->pls_layout->plh_inode;
+
+       data = kzalloc(sizeof(*data), gfp_flags);
+       if (data) {
+               data->args.inode = data->inode = nfs_igrab_and_active(inode);
+               if (data->inode) {
+                       data->lseg = pnfs_get_lseg(lseg);
+                       if (data->lseg)
+                               return data;
+                       nfs_iput_and_deactive(data->inode);
+               }
+               kfree(data);
+       }
+       return NULL;
+}
+
+static void
+nfs42_free_layouterror_data(struct nfs42_layouterror_data *data)
+{
+       pnfs_put_lseg(data->lseg);
+       nfs_iput_and_deactive(data->inode);
+       kfree(data);
+}
+
+static void
+nfs42_layouterror_prepare(struct rpc_task *task, void *calldata)
+{
+       struct nfs42_layouterror_data *data = calldata;
+       struct inode *inode = data->inode;
+       struct nfs_server *server = NFS_SERVER(inode);
+       struct pnfs_layout_hdr *lo = data->lseg->pls_layout;
+       unsigned i;
+
+       spin_lock(&inode->i_lock);
+       if (!pnfs_layout_is_valid(lo)) {
+               spin_unlock(&inode->i_lock);
+               rpc_exit(task, 0);
+               return;
+       }
+       for (i = 0; i < data->args.num_errors; i++)
+               nfs4_stateid_copy(&data->args.errors[i].stateid,
+                               &lo->plh_stateid);
+       spin_unlock(&inode->i_lock);
+       nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
+                           &data->res.seq_res, task);
+}
+
+static void
+nfs42_layouterror_done(struct rpc_task *task, void *calldata)
+{
+       struct nfs42_layouterror_data *data = calldata;
+       struct inode *inode = data->inode;
+       struct pnfs_layout_hdr *lo = data->lseg->pls_layout;
+
+       if (!nfs4_sequence_done(task, &data->res.seq_res))
+               return;
+
+       switch (task->tk_status) {
+       case 0:
+               break;
+       case -NFS4ERR_BADHANDLE:
+       case -ESTALE:
+               pnfs_destroy_layout(NFS_I(inode));
+               break;
+       case -NFS4ERR_EXPIRED:
+       case -NFS4ERR_ADMIN_REVOKED:
+       case -NFS4ERR_DELEG_REVOKED:
+       case -NFS4ERR_STALE_STATEID:
+       case -NFS4ERR_BAD_STATEID:
+               spin_lock(&inode->i_lock);
+               if (pnfs_layout_is_valid(lo) &&
+                   nfs4_stateid_match(&data->args.errors[0].stateid,
+                                            &lo->plh_stateid)) {
+                       LIST_HEAD(head);
+
+                       /*
+                        * Mark the bad layout state as invalid, then retry
+                        * with the current stateid.
+                        */
+                       pnfs_mark_layout_stateid_invalid(lo, &head);
+                       spin_unlock(&inode->i_lock);
+                       pnfs_free_lseg_list(&head);
+                       nfs_commit_inode(inode, 0);
+               } else
+                       spin_unlock(&inode->i_lock);
+               break;
+       case -NFS4ERR_OLD_STATEID:
+               spin_lock(&inode->i_lock);
+               if (pnfs_layout_is_valid(lo) &&
+                   nfs4_stateid_match_other(&data->args.errors[0].stateid,
+                                       &lo->plh_stateid)) {
+                       /* Do we need to delay before resending? */
+                       if (!nfs4_stateid_is_newer(&lo->plh_stateid,
+                                               &data->args.errors[0].stateid))
+                               rpc_delay(task, HZ);
+                       rpc_restart_call_prepare(task);
+               }
+               spin_unlock(&inode->i_lock);
+               break;
+       case -ENOTSUPP:
+       case -EOPNOTSUPP:
+               NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTERROR;
+       }
+}
+
+static void
+nfs42_layouterror_release(void *calldata)
+{
+       struct nfs42_layouterror_data *data = calldata;
+
+       nfs42_free_layouterror_data(data);
+}
+
+static const struct rpc_call_ops nfs42_layouterror_ops = {
+       .rpc_call_prepare = nfs42_layouterror_prepare,
+       .rpc_call_done = nfs42_layouterror_done,
+       .rpc_release = nfs42_layouterror_release,
+};
+
+int nfs42_proc_layouterror(struct pnfs_layout_segment *lseg,
+               const struct nfs42_layout_error *errors, size_t n)
+{
+       struct inode *inode = lseg->pls_layout->plh_inode;
+       struct nfs42_layouterror_data *data;
+       struct rpc_task *task;
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTERROR],
+       };
+       struct rpc_task_setup task_setup = {
+               .rpc_message = &msg,
+               .callback_ops = &nfs42_layouterror_ops,
+               .flags = RPC_TASK_ASYNC,
+       };
+       unsigned int i;
+
+       if (!nfs_server_capable(inode, NFS_CAP_LAYOUTERROR))
+               return -EOPNOTSUPP;
+       if (n > NFS42_LAYOUTERROR_MAX)
+               return -EINVAL;
+       data = nfs42_alloc_layouterror_data(lseg, GFP_NOFS);
+       if (!data)
+               return -ENOMEM;
+       for (i = 0; i < n; i++) {
+               data->args.errors[i] = errors[i];
+               data->args.num_errors++;
+               data->res.num_errors++;
+       }
+       msg.rpc_argp = &data->args;
+       msg.rpc_resp = &data->res;
+       task_setup.callback_data = data;
+       task_setup.rpc_client = NFS_SERVER(inode)->client;
+       nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0);
+       task = rpc_run_task(&task_setup);
+       if (IS_ERR(task))
+               return PTR_ERR(task);
+       rpc_put_task(task);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(nfs42_proc_layouterror);
+
 static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
                struct file *dst_f, struct nfs_lock_context *src_lock,
                struct nfs_lock_context *dst_lock, loff_t src_offset,
index 7d596e8a0941add8f599949c675a52a93ebeb16e..aed865a8462963276e904d5782ab884743ff6a47 100644 (file)
                                        1 /* opaque devaddr4 length */ + \
                                        XDR_QUADLEN(PNFS_LAYOUTSTATS_MAXSIZE))
 #define decode_layoutstats_maxsz       (op_decode_hdr_maxsz)
+#define encode_device_error_maxsz      (XDR_QUADLEN(NFS4_DEVICEID4_SIZE) + \
+                                       1 /* status */ + 1 /* opnum */)
+#define encode_layouterror_maxsz       (op_decode_hdr_maxsz + \
+                                       2 /* offset */ + \
+                                       2 /* length */ + \
+                                       encode_stateid_maxsz + \
+                                       1 /* Array size */ + \
+                                       encode_device_error_maxsz)
+#define decode_layouterror_maxsz       (op_decode_hdr_maxsz)
 #define encode_clone_maxsz             (encode_stateid_maxsz + \
                                        encode_stateid_maxsz + \
                                        2 /* src offset */ + \
                                         decode_sequence_maxsz + \
                                         decode_putfh_maxsz + \
                                         PNFS_LAYOUTSTATS_MAXDEV * decode_layoutstats_maxsz)
+#define NFS4_enc_layouterror_sz                (compound_encode_hdr_maxsz + \
+                                        encode_sequence_maxsz + \
+                                        encode_putfh_maxsz + \
+                                        NFS42_LAYOUTERROR_MAX * \
+                                        encode_layouterror_maxsz)
+#define NFS4_dec_layouterror_sz                (compound_decode_hdr_maxsz + \
+                                        decode_sequence_maxsz + \
+                                        decode_putfh_maxsz + \
+                                        NFS42_LAYOUTERROR_MAX * \
+                                        decode_layouterror_maxsz)
 #define NFS4_enc_clone_sz              (compound_encode_hdr_maxsz + \
                                         encode_sequence_maxsz + \
                                         encode_putfh_maxsz + \
@@ -233,6 +252,34 @@ static void encode_clone(struct xdr_stream *xdr,
        xdr_encode_hyper(p, args->count);
 }
 
+static void encode_device_error(struct xdr_stream *xdr,
+                               const struct nfs42_device_error *error)
+{
+       __be32 *p;
+
+       p = reserve_space(xdr, NFS4_DEVICEID4_SIZE + 2*4);
+       p = xdr_encode_opaque_fixed(p, error->dev_id.data,
+                       NFS4_DEVICEID4_SIZE);
+       *p++ = cpu_to_be32(error->status);
+       *p = cpu_to_be32(error->opnum);
+}
+
+static void encode_layouterror(struct xdr_stream *xdr,
+                              const struct nfs42_layout_error *args,
+                              struct compound_hdr *hdr)
+{
+       __be32 *p;
+
+       encode_op_hdr(xdr, OP_LAYOUTERROR, decode_layouterror_maxsz, hdr);
+       p = reserve_space(xdr, 8 + 8);
+       p = xdr_encode_hyper(p, args->offset);
+       p = xdr_encode_hyper(p, args->length);
+       encode_nfs4_stateid(xdr, &args->stateid);
+       p = reserve_space(xdr, 4);
+       *p = cpu_to_be32(1);
+       encode_device_error(xdr, &args->errors[0]);
+}
+
 /*
  * Encode ALLOCATE request
  */
@@ -391,6 +438,27 @@ static void nfs4_xdr_enc_clone(struct rpc_rqst *req,
        encode_nops(&hdr);
 }
 
+/*
+ * Encode LAYOUTERROR request
+ */
+static void nfs4_xdr_enc_layouterror(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    const void *data)
+{
+       const struct nfs42_layouterror_args *args = data;
+       struct compound_hdr hdr = {
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
+       };
+       int i;
+
+       encode_compound_hdr(xdr, req, &hdr);
+       encode_sequence(xdr, &args->seq_args, &hdr);
+       encode_putfh(xdr, NFS_FH(args->inode), &hdr);
+       for (i = 0; i < args->num_errors; i++)
+               encode_layouterror(xdr, &args->errors[i], &hdr);
+       encode_nops(&hdr);
+}
+
 static int decode_allocate(struct xdr_stream *xdr, struct nfs42_falloc_res *res)
 {
        return decode_op_hdr(xdr, OP_ALLOCATE);
@@ -494,6 +562,11 @@ static int decode_clone(struct xdr_stream *xdr)
        return decode_op_hdr(xdr, OP_CLONE);
 }
 
+static int decode_layouterror(struct xdr_stream *xdr)
+{
+       return decode_op_hdr(xdr, OP_LAYOUTERROR);
+}
+
 /*
  * Decode ALLOCATE request
  */
@@ -703,4 +776,30 @@ out:
        return status;
 }
 
+/*
+ * Decode LAYOUTERROR request
+ */
+static int nfs4_xdr_dec_layouterror(struct rpc_rqst *rqstp,
+                                   struct xdr_stream *xdr,
+                                   void *data)
+{
+       struct nfs42_layouterror_res *res = data;
+       struct compound_hdr hdr;
+       int status, i;
+
+       status = decode_compound_hdr(xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
+       if (status)
+               goto out;
+       status = decode_putfh(xdr);
+
+       for (i = 0; i < res->num_errors && status == 0; i++)
+               status = decode_layouterror(xdr);
+out:
+       res->rpc_status = status;
+       return status;
+}
+
 #endif /* __LINUX_FS_NFS_NFS4_2XDR_H */
index 5b980246b03533270c4c5d329e652f66c2e4248c..73889ea7d196a43fb4e4a4f6643eae9041edc63b 100644 (file)
@@ -9690,7 +9690,8 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
                | NFS_CAP_DEALLOCATE
                | NFS_CAP_SEEK
                | NFS_CAP_LAYOUTSTATS
-               | NFS_CAP_CLONE,
+               | NFS_CAP_CLONE
+               | NFS_CAP_LAYOUTERROR,
        .init_client = nfs41_init_client,
        .shutdown_client = nfs41_shutdown_client,
        .match_stateid = nfs41_match_stateid,
index 6d9d5e2f630804f41cfe99d192a1d62f6dff7ec5..cfcabc33e24d01136ba00c336f90497f657fb0a5 100644 (file)
@@ -7572,6 +7572,7 @@ const struct rpc_procinfo nfs4_procedures[] = {
        PROC42(COPY,            enc_copy,               dec_copy),
        PROC42(OFFLOAD_CANCEL,  enc_offload_cancel,     dec_offload_cancel),
        PROC(LOOKUPP,           enc_lookupp,            dec_lookupp),
+       PROC42(LAYOUTERROR,     enc_layouterror,        dec_layouterror),
 };
 
 static unsigned int nfs_version4_counts[ARRAY_SIZE(nfs4_procedures)];
index 1b06f0b284533b025031bd198d244ececfe9b675..22494d1706195d01715d6173c1385fc227eea1e4 100644 (file)
@@ -538,6 +538,7 @@ enum {
        NFSPROC4_CLNT_OFFLOAD_CANCEL,
 
        NFSPROC4_CLNT_LOOKUPP,
+       NFSPROC4_CLNT_LAYOUTERROR,
 };
 
 /* nfs41 types */
index 6aa8cc83c3b66317ca0fd20e26d591b7f29654f8..c827d31298cc379e3bdb07810f98913b24737301 100644 (file)
@@ -261,5 +261,6 @@ struct nfs_server {
 #define NFS_CAP_CLONE          (1U << 23)
 #define NFS_CAP_COPY           (1U << 24)
 #define NFS_CAP_OFFLOAD_CANCEL (1U << 25)
+#define NFS_CAP_LAYOUTERROR    (1U << 26)
 
 #endif
index b4bd2bf5f5857b8a78a853bfdc913c6a891bf394..9b8324ec08f3191f7ff95944ed0d0cb07df67194 100644 (file)
@@ -383,6 +383,41 @@ struct nfs42_layoutstat_data {
        struct nfs42_layoutstat_res res;
 };
 
+struct nfs42_device_error {
+       struct nfs4_deviceid dev_id;
+       int status;
+       enum nfs_opnum4 opnum;
+};
+
+struct nfs42_layout_error {
+       __u64 offset;
+       __u64 length;
+       nfs4_stateid stateid;
+       struct nfs42_device_error errors[1];
+};
+
+#define NFS42_LAYOUTERROR_MAX 5
+
+struct nfs42_layouterror_args {
+       struct nfs4_sequence_args seq_args;
+       struct inode *inode;
+       unsigned int num_errors;
+       struct nfs42_layout_error errors[NFS42_LAYOUTERROR_MAX];
+};
+
+struct nfs42_layouterror_res {
+       struct nfs4_sequence_res seq_res;
+       unsigned int num_errors;
+       int rpc_status;
+};
+
+struct nfs42_layouterror_data {
+       struct nfs42_layouterror_args args;
+       struct nfs42_layouterror_res res;
+       struct inode *inode;
+       struct pnfs_layout_segment *lseg;
+};
+
 struct nfs42_clone_args {
        struct nfs4_sequence_args       seq_args;
        struct nfs_fh                   *src_fh;