Re: [PATCH 12/13] RFC: pnfs: add LAYOUTGET and GETDEVICEINFO infrastructure

Trond Myklebust
2010-09-10T20:11:45Z

On Thu, 2010-09-02 at 14:00 -0400, Fred Isaman wrote:
> From: The pNFS Team <linux-nfs@vger.kernel.org>
>
> Add the ability to actually send LAYOUTGET and GETDEVICEINFO. This also adds
> in the machinery to handle layout state and the deviceid cache. Note that
> GETDEVICEINFO is not called directly by the generic layer. Instead it
> is called by the drivers while parsing the LAYOUTGET opaque data in response
> to an unknown device id embedded therein. Annoyingly, RFC 5661 only encodes
> device ids within the driver-specific opaque data.
>
> Signed-off-by: TBD - melding/reorganization of several patches
> ---
> fs/nfs/nfs4proc.c | 134 ++++++++++++++++
> fs/nfs/nfs4xdr.c | 302 +++++++++++++++++++++++++++++++++++
> fs/nfs/pnfs.c | 382 ++++++++++++++++++++++++++++++++++++++++++---
> fs/nfs/pnfs.h | 91 +++++++++++-
> include/linux/nfs4.h | 2 +
> include/linux/nfs_fs_sb.h | 1 +
> include/linux/nfs_xdr.h | 49 ++++++
> 7 files changed, 935 insertions(+), 26 deletions(-)
>
> diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
> index c7c7277..7eeea0e 100644
> --- a/fs/nfs/nfs4proc.c
> +++ b/fs/nfs/nfs4proc.c
> @@ -55,6 +55,7 @@
> #include "internal.h"
> #include "iostat.h"
> #include "callback.h"
> +#include "pnfs.h"
>
> #define NFSDBG_FACILITY NFSDBG_PROC
>
> @@ -5335,6 +5336,139 @@ out:
> dprintk("<-- %s status=%d\n", __func__, status);
> return status;
> }
> +
> +static void
> +nfs4_layoutget_prepare(struct rpc_task *task, void *calldata)
> +{
> + struct nfs4_layoutget *lgp = calldata;
> + struct inode *ino = lgp->args.inode;
> + struct nfs_server *server = NFS_SERVER(ino);
> +
> + dprintk("--> %s\n", __func__);
> + if (nfs4_setup_sequence(server, &lgp->args.seq_args,
> + &lgp->res.seq_res, 0, task))
> + return;
> + rpc_call_start(task);
> +}
> +
> +static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)
> +{
> + struct nfs4_layoutget *lgp = calldata;
> + struct inode *ino = lgp->args.inode;
> + struct nfs_server *server = NFS_SERVER(ino);
> +
> + dprintk("--> %s\n", __func__);
> +
> + if (!nfs4_sequence_done(task, &lgp->res.seq_res))
> + return;
> +
> + if (RPC_ASSASSINATED(task))
> + return;
> +
> + if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN)
> + nfs_restart_rpc(task, server->nfs_client);
> +
> + lgp->status = task->tk_status;
> + dprintk("<-- %s\n", __func__);
> +}
> +
> +static void nfs4_layoutget_release(void *calldata)
> +{
> + struct nfs4_layoutget *lgp = calldata;
> +
> + dprintk("--> %s\n", __func__);
> + put_layout_hdr(lgp->args.inode);
> + if (lgp->res.layout.buf != NULL)
> + free_page((unsigned long) lgp->res.layout.buf);
> + put_nfs_open_context(lgp->args.ctx);
> + kfree(calldata);
> + dprintk("<-- %s\n", __func__);
> +}
> +
> +static const struct rpc_call_ops nfs4_layoutget_call_ops = {
> + .rpc_call_prepare = nfs4_layoutget_prepare,
> + .rpc_call_done = nfs4_layoutget_done,
> + .rpc_release = nfs4_layoutget_release,
> +};
> +
> +static int _nfs4_proc_layoutget(struct nfs4_layoutget *lgp)
> +{
> + struct nfs_server *server = NFS_SERVER(lgp->args.inode);
> + struct rpc_task *task;
> + struct rpc_message msg = {
> + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTGET],
> + .rpc_argp = &lgp->args,
> + .rpc_resp = &lgp->res,
> + };
> + struct rpc_task_setup task_setup_data = {
> + .rpc_client = server->client,
> + .rpc_message = &msg,
> + .callback_ops = &nfs4_layoutget_call_ops,
> + .callback_data = lgp,
> + .flags = RPC_TASK_ASYNC,
> + };
> + int status = 0;
> +
> + dprintk("--> %s\n", __func__);
> +
> + lgp->res.layout.buf = (void *)__get_free_page(GFP_NOFS);
> + if (lgp->res.layout.buf == NULL) {
> + nfs4_layoutget_release(lgp);
> + return -ENOMEM;
> + }
> +
> + lgp->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
> + task = rpc_run_task(&task_setup_data);
> + if (IS_ERR(task))
> + return PTR_ERR(task);
> + status = nfs4_wait_for_completion_rpc_task(task);
> + if (status != 0)
> + goto out;
> + status = lgp->status;
> + if (status != 0)
> + goto out;
> + status = pnfs_layout_process(lgp);
> +out:
> + rpc_put_task(task);
> + dprintk("<-- %s status=%d\n", __func__, status);
> + return status;
> +}
> +
> +int nfs4_proc_layoutget(struct nfs4_layoutget *lgp)
> +{
> + struct nfs_server *server = NFS_SERVER(lgp->args.inode);
> + struct nfs4_exception exception = { };
> + int err;
> + do {
> + err = nfs4_handle_exception(server, _nfs4_proc_layoutget(lgp),
> + &exception);
> + } while (exception.retry);
> + return err;
> +}

Since nfs4_layoutget_done() already calls nfs4_async_handle_error(), do
you really need to call nfs4_handle_exception()?

> +
> +int nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev)
> +{
> + struct nfs4_getdeviceinfo_args args = {
> + .pdev = pdev,
> + };
> + struct nfs4_getdeviceinfo_res res = {
> + .pdev = pdev,
> + };
> + struct rpc_message msg = {
> + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETDEVICEINFO],
> + .rpc_argp = &args,
> + .rpc_resp = &res,
> + };
> + int status;
> +
> + dprintk("--> %s\n", __func__);
> + status = nfs4_call_sync(server, &msg, &args, &res, 0);
> + dprintk("<-- %s status=%d\n", __func__, status);
> +
> + return status;
> +}
> +EXPORT_SYMBOL_GPL(nfs4_proc_getdeviceinfo);
> +

This, on the other hand, might need a 'handle exception' wrapper.

> #endif /* CONFIG_NFS_V4_1 */
>
> struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = {
> diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
> index 60233ae..aaf6fe5 100644
> --- a/fs/nfs/nfs4xdr.c
> +++ b/fs/nfs/nfs4xdr.c
> @@ -52,6 +52,7 @@
> #include <linux/nfs_idmap.h>
> #include "nfs4_fs.h"
> #include "internal.h"
> +#include "pnfs.h"
>
> #define NFSDBG_FACILITY NFSDBG_XDR
>
> @@ -310,6 +311,19 @@ static int nfs4_stat_to_errno(int);
> XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + 5)
> #define encode_reclaim_complete_maxsz (op_encode_hdr_maxsz + 4)
> #define decode_reclaim_complete_maxsz (op_decode_hdr_maxsz + 4)
> +#define encode_getdeviceinfo_maxsz (op_encode_hdr_maxsz + 4 + \
> + XDR_QUADLEN(NFS4_PNFS_DEVICEID4_SIZE))
> +#define decode_getdeviceinfo_maxsz (op_decode_hdr_maxsz + \
> + 1 /* layout type */ + \
> + 1 /* opaque devaddr4 length */ + \
> + /* devaddr4 payload is read into page */ \
> + 1 /* notification bitmap length */ + \
> + 1 /* notification bitmap */)
> +#define encode_layoutget_maxsz (op_encode_hdr_maxsz + 10 + \
> + encode_stateid_maxsz)
> +#define decode_layoutget_maxsz (op_decode_hdr_maxsz + 8 + \
> + decode_stateid_maxsz + \
> + XDR_QUADLEN(PNFS_LAYOUT_MAXSIZE))
> #else /* CONFIG_NFS_V4_1 */
> #define encode_sequence_maxsz 0
> #define decode_sequence_maxsz 0
> @@ -699,6 +713,20 @@ static int nfs4_stat_to_errno(int);
> #define NFS4_dec_reclaim_complete_sz (compound_decode_hdr_maxsz + \
> decode_sequence_maxsz + \
> decode_reclaim_complete_maxsz)
> +#define NFS4_enc_getdeviceinfo_sz (compound_encode_hdr_maxsz + \
> + encode_sequence_maxsz +\
> + encode_getdeviceinfo_maxsz)
> +#define NFS4_dec_getdeviceinfo_sz (compound_decode_hdr_maxsz + \
> + decode_sequence_maxsz + \
> + decode_getdeviceinfo_maxsz)
> +#define NFS4_enc_layoutget_sz (compound_encode_hdr_maxsz + \
> + encode_sequence_maxsz + \
> + encode_putfh_maxsz + \
> + encode_layoutget_maxsz)
> +#define NFS4_dec_layoutget_sz (compound_decode_hdr_maxsz + \
> + decode_sequence_maxsz + \
> + decode_putfh_maxsz + \
> + decode_layoutget_maxsz)
>
> const u32 nfs41_maxwrite_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
> compound_encode_hdr_maxsz +
> @@ -1726,6 +1754,61 @@ static void encode_sequence(struct xdr_stream *xdr,
> #endif /* CONFIG_NFS_V4_1 */
> }
>
> +#ifdef CONFIG_NFS_V4_1
> +static void
> +encode_getdeviceinfo(struct xdr_stream *xdr,
> + const struct nfs4_getdeviceinfo_args *args,
> + struct compound_hdr *hdr)
> +{
> + int has_bitmap = (args->pdev->dev_notify_types != 0);
> + int len = 16 + NFS4_PNFS_DEVICEID4_SIZE + (has_bitmap * 4);
> + __be32 *p;
> +
> + p = reserve_space(xdr, len);
> + *p++ = cpu_to_be32(OP_GETDEVICEINFO);
> + p = xdr_encode_opaque_fixed(p, args->pdev->dev_id.data,
> + NFS4_PNFS_DEVICEID4_SIZE);
> + *p++ = cpu_to_be32(args->pdev->layout_type);
> + *p++ = cpu_to_be32(args->pdev->pglen); /* gdia_maxcount */
> + *p++ = cpu_to_be32(has_bitmap); /* bitmap length [01] */
> + if (has_bitmap)
> + *p = cpu_to_be32(args->pdev->dev_notify_types);

We don't support notification callbacks yet.

> + hdr->nops++;
> + hdr->replen += decode_getdeviceinfo_maxsz;
> +}
> +
> +static void
> +encode_layoutget(struct xdr_stream *xdr,
> + const struct nfs4_layoutget_args *args,
> + struct compound_hdr *hdr)
> +{
> + nfs4_stateid stateid;
> + __be32 *p;
> +
> + p = reserve_space(xdr, 44 + NFS4_STATEID_SIZE);
> + *p++ = cpu_to_be32(OP_LAYOUTGET);
> + *p++ = cpu_to_be32(0); /* Signal layout available */
> + *p++ = cpu_to_be32(args->type);
> + *p++ = cpu_to_be32(args->range.iomode);
> + p = xdr_encode_hyper(p, args->range.offset);
> + p = xdr_encode_hyper(p, args->range.length);
> + p = xdr_encode_hyper(p, args->minlength);
> + pnfs_get_layout_stateid(&stateid, NFS_I(args->inode)->layout);
> + p = xdr_encode_opaque_fixed(p, &stateid.data, NFS4_STATEID_SIZE);
> + *p = cpu_to_be32(args->maxcount);
> +
> + dprintk("%s: 1st type:0x%x iomode:%d off:%lu len:%lu mc:%d\n",
> + __func__,
> + args->type,
> + args->range.iomode,
> + (unsigned long)args->range.offset,
> + (unsigned long)args->range.length,
> + args->maxcount);
> + hdr->nops++;
> + hdr->replen += decode_layoutget_maxsz;
> +}
> +#endif /* CONFIG_NFS_V4_1 */
> +
> /*
> * END OF "GENERIC" ENCODE ROUTINES.
> */
> @@ -2543,6 +2626,51 @@ static int nfs4_xdr_enc_reclaim_complete(struct rpc_rqst *req, uint32_t *p,
> return 0;
> }
>
> +/*
> + * Encode GETDEVICEINFO request
> + */
> +static int nfs4_xdr_enc_getdeviceinfo(struct rpc_rqst *req, uint32_t *p,
> + struct nfs4_getdeviceinfo_args *args)
> +{
> + struct xdr_stream xdr;
> + struct compound_hdr hdr = {
> + .minorversion = nfs4_xdr_minorversion(&args->seq_args),
> + };
> +
> + xdr_init_encode(&xdr, &req->rq_snd_buf, p);
> + encode_compound_hdr(&xdr, req, &hdr);
> + encode_sequence(&xdr, &args->seq_args, &hdr);
> + encode_getdeviceinfo(&xdr, args, &hdr);
> +
> + /* set up reply kvec. Subtract notification bitmap max size (2)
> + * so that notification bitmap is put in xdr_buf tail */
> + xdr_inline_pages(&req->rq_rcv_buf, (hdr.replen - 2) << 2,
> + args->pdev->pages, args->pdev->pgbase,
> + args->pdev->pglen);
> +
> + encode_nops(&hdr);
> + return 0;
> +}
> +
> +/*
> + * Encode LAYOUTGET request
> + */
> +static int nfs4_xdr_enc_layoutget(struct rpc_rqst *req, uint32_t *p,
> + struct nfs4_layoutget_args *args)
> +{
> + struct xdr_stream xdr;
> + struct compound_hdr hdr = {
> + .minorversion = nfs4_xdr_minorversion(&args->seq_args),
> + };
> +
> + xdr_init_encode(&xdr, &req->rq_snd_buf, p);
> + encode_compound_hdr(&xdr, req, &hdr);
> + encode_sequence(&xdr, &args->seq_args, &hdr);
> + encode_putfh(&xdr, NFS_FH(args->inode), &hdr);
> + encode_layoutget(&xdr, args, &hdr);
> + encode_nops(&hdr);
> + return 0;
> +}
> #endif /* CONFIG_NFS_V4_1 */
>
> static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
> @@ -4788,6 +4916,131 @@ out_overflow:
> #endif /* CONFIG_NFS_V4_1 */
> }
>
> +#if defined(CONFIG_NFS_V4_1)
> +
> +static int decode_getdeviceinfo(struct xdr_stream *xdr,
> + struct pnfs_device *pdev)
> +{
> + __be32 *p;
> + uint32_t len, type;
> + int status;
> +
> + status = decode_op_hdr(xdr, OP_GETDEVICEINFO);
> + if (status) {
> + if (status == -ETOOSMALL) {
> + p = xdr_inline_decode(xdr, 4);
> + if (unlikely(!p))
> + goto out_overflow;
> + pdev->mincount = be32_to_cpup(p);
> + dprintk("%s: Min count too small. mincnt = %u\n",
> + __func__, pdev->mincount);
> + }
> + return status;
> + }
> +
> + p = xdr_inline_decode(xdr, 8);
> + if (unlikely(!p))
> + goto out_overflow;
> + type = be32_to_cpup(p++);
> + if (type != pdev->layout_type) {
> + dprintk("%s: layout mismatch req: %u pdev: %u\n",
> + __func__, pdev->layout_type, type);
> + return -EINVAL;
> + }
> + /*
> + * Get the length of the opaque device_addr4. xdr_read_pages places
> + * the opaque device_addr4 in the xdr_buf->pages (pnfs_device->pages)
> + * and places the remaining xdr data in xdr_buf->tail
> + */
> + pdev->mincount = be32_to_cpup(p);
> + xdr_read_pages(xdr, pdev->mincount); /* include space for the length */
> +
> + /*
> + * At most one bitmap word. If the server returns a bitmap of more
> + * than one word we ignore the extra invalid words given that
> + * getdeviceinfo is the final operation in the compound.
> + */
> + p = xdr_inline_decode(xdr, 4);
> + if (unlikely(!p))
> + goto out_overflow;
> + len = be32_to_cpup(p);
> + if (len) {
> + p = xdr_inline_decode(xdr, 4);
> + if (unlikely(!p))
> + goto out_overflow;
> + pdev->dev_notify_types = be32_to_cpup(p);
> + } else
> + pdev->dev_notify_types = 0;

Again, we don't support notifications.

> + return 0;
> +out_overflow:
> + print_overflow_msg(__func__, xdr);
> + return -EIO;
> +}
> +
> +static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
> + struct nfs4_layoutget_res *res)
> +{
> + __be32 *p;
> + int status;
> + u32 layout_count;
> +
> + status = decode_op_hdr(xdr, OP_LAYOUTGET);
> + if (status)
> + return status;
> + p = xdr_inline_decode(xdr, 8 + NFS4_STATEID_SIZE);
> + if (unlikely(!p))
> + goto out_overflow;
> + res->return_on_close = be32_to_cpup(p++);
> + p = xdr_decode_opaque_fixed(p, res->stateid.data, NFS4_STATEID_SIZE);
> + layout_count = be32_to_cpup(p);
> + if (!layout_count) {
> + dprintk("%s: server responded with empty layout array\n",
> + __func__);
> + return -EINVAL;
> + }
> +
> + p = xdr_inline_decode(xdr, 24);
> + if (unlikely(!p))
> + goto out_overflow;
> + p = xdr_decode_hyper(p, &res->range.offset);
> + p = xdr_decode_hyper(p, &res->range.length);
> + res->range.iomode = be32_to_cpup(p++);
> + res->type = be32_to_cpup(p++);
> +
> + status = decode_opaque_inline(xdr, &res->layout.len, (char **)&p);
> + if (unlikely(status))
> + return status;
> +
> + dprintk("%s roff:%lu rlen:%lu riomode:%d, lo_type:0x%x, lo.len:%d\n",
> + __func__,
> + (unsigned long)res->range.offset,
> + (unsigned long)res->range.length,
> + res->range.iomode,
> + res->type,
> + res->layout.len);
> +
> + /* nfs4_proc_layoutget allocated a single page */
> + if (res->layout.len > PAGE_SIZE)
> + return -ENOMEM;
> + memcpy(res->layout.buf, p, res->layout.len);
> +
> + if (layout_count > 1) {
> + /* We only handle a length one array at the moment. Any
> + * further entries are just ignored. Note that this means
> + * the client may see a response that is less than the
> + * minimum it requested.
> + */
> + dprintk("%s: server responded with %d layouts, dropping tail\n",
> + __func__, layout_count);
> + }
> +
> + return 0;
> +out_overflow:
> + print_overflow_msg(__func__, xdr);
> + return -EIO;
> +}
> +#endif /* CONFIG_NFS_V4_1 */
> +
> /*
> * END OF "GENERIC" DECODE ROUTINES.
> */
> @@ -5815,6 +6068,53 @@ static int nfs4_xdr_dec_reclaim_complete(struct rpc_rqst *rqstp, uint32_t *p,
> status = decode_reclaim_complete(&xdr, (void *)NULL);
> return status;
> }
> +
> +/*
> + * Decode GETDEVINFO response
> + */
> +static int nfs4_xdr_dec_getdeviceinfo(struct rpc_rqst *rqstp, uint32_t *p,
> + struct nfs4_getdeviceinfo_res *res)
> +{
> + struct xdr_stream xdr;
> + struct compound_hdr hdr;
> + int status;
> +
> + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
> + status = decode_compound_hdr(&xdr, &hdr);
> + if (status != 0)
> + goto out;
> + status = decode_sequence(&xdr, &res->seq_res, rqstp);
> + if (status != 0)
> + goto out;
> + status = decode_getdeviceinfo(&xdr, res->pdev);
> +out:
> + return status;
> +}
> +
> +/*
> + * Decode LAYOUTGET response
> + */
> +static int nfs4_xdr_dec_layoutget(struct rpc_rqst *rqstp, uint32_t *p,
> + struct nfs4_layoutget_res *res)
> +{
> + struct xdr_stream xdr;
> + struct compound_hdr hdr;
> + int status;
> +
> + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
> + 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);
> + if (status)
> + goto out;
> + status = decode_layoutget(&xdr, rqstp, res);
> +out:
> + return status;
> +}
> #endif /* CONFIG_NFS_V4_1 */
>
> __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
> @@ -5993,6 +6293,8 @@ struct rpc_procinfo nfs4_procedures[] = {
> PROC(SEQUENCE, enc_sequence, dec_sequence),
> PROC(GET_LEASE_TIME, enc_get_lease_time, dec_get_lease_time),
> PROC(RECLAIM_COMPLETE, enc_reclaim_complete, dec_reclaim_complete),
> + PROC(GETDEVICEINFO, enc_getdeviceinfo, dec_getdeviceinfo),
> + PROC(LAYOUTGET, enc_layoutget, dec_layoutget),
> #endif /* CONFIG_NFS_V4_1 */
> };
>
> diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
> index cbce942..faf6c4c 100644
> --- a/fs/nfs/pnfs.c
> +++ b/fs/nfs/pnfs.c
> @@ -128,6 +128,12 @@ pnfs_register_layoutdriver(struct pnfs_layoutdriver_type *ld_type)
> return status;
> }
>
> + if (!io_ops->alloc_lseg || !io_ops->free_lseg) {
> + printk(KERN_ERR "%s Layout driver must provide "
> + "alloc_lseg and free_lseg.\n", __func__);
> + return status;
> + }
> +
> spin_lock(&pnfs_spinlock);
> if (!find_pnfs_driver_locked(ld_type->id)) {
> list_add(&ld_type->pnfs_tblid, &pnfs_modules_tbl);
> @@ -153,6 +159,10 @@ pnfs_unregister_layoutdriver(struct pnfs_layoutdriver_type *ld_type)
> }
> EXPORT_SYMBOL(pnfs_unregister_layoutdriver);
>
> +/*
> + * pNFS client layout cache
> + */
> +
> static void
> get_layout_hdr_locked(struct pnfs_layout_hdr *lo)
> {
> @@ -175,6 +185,15 @@ put_layout_hdr_locked(struct pnfs_layout_hdr *lo)
> }
> }
>
> +void
> +put_layout_hdr(struct inode *inode)
> +{
> + spin_lock(&inode->i_lock);
> + put_layout_hdr_locked(NFS_I(inode)->layout);
> + spin_unlock(&inode->i_lock);
> +
> +}
> +
> static void
> init_lseg(struct pnfs_layout_hdr *lo, struct pnfs_layout_segment *lseg)
> {
> @@ -191,7 +210,7 @@ destroy_lseg(struct kref *kref)
> struct pnfs_layout_hdr *local = lseg->layout;
>
> dprintk("--> %s\n", __func__);
> - kfree(lseg);
> + PNFS_LD_IO_OPS(local)->free_lseg(lseg);

Where is PNFS_LD_IO_OPS() defined? Besides, I thought we agreed to get
rid of that.

> /* Matched by get_layout_hdr_locked in pnfs_insert_layout */
> put_layout_hdr_locked(local);
> }
> @@ -226,6 +245,7 @@ pnfs_clear_lseg_list(struct pnfs_layout_hdr *lo)
> /* List does not take a reference, so no need for put here */
> list_del_init(&lo->layouts);
> spin_unlock(&clp->cl_lock);
> + pnfs_set_layout_stateid(lo, &zero_stateid);
>
> dprintk("%s:Return\n", __func__);
> }
> @@ -268,40 +288,120 @@ pnfs_destroy_all_layouts(struct nfs_client *clp)
> }
> }
>
> -static void pnfs_insert_layout(struct pnfs_layout_hdr *lo,
> - struct pnfs_layout_segment *lseg);
> +void
> +pnfs_set_layout_stateid(struct pnfs_layout_hdr *lo,
> + const nfs4_stateid *stateid)
> +{
> + write_seqlock(&lo->seqlock);
> + memcpy(lo->stateid.data, stateid->data, sizeof(lo->stateid.data));
> + write_sequnlock(&lo->seqlock);
> +}
> +
> +void
> +pnfs_get_layout_stateid(nfs4_stateid *dst, struct pnfs_layout_hdr *lo)
> +{
> + int seq;
>
> -/* Get layout from server. */
> + dprintk("--> %s\n", __func__);
> +
> + do {
> + seq = read_seqbegin(&lo->seqlock);
> + memcpy(dst->data, lo->stateid.data,
> + sizeof(lo->stateid.data));
> + } while (read_seqretry(&lo->seqlock, seq));
> +
> + dprintk("<-- %s\n", __func__);
> +}
> +
> +static void
> +pnfs_layout_from_open_stateid(struct pnfs_layout_hdr *lo,
> + struct nfs4_state *state)
> +{
> + int seq;
> +
> + dprintk("--> %s\n", __func__);
> +
> + write_seqlock(&lo->seqlock);
> + /* Zero stateid, which is illegal to use in layout, is our
> + * marker for an un-initialized stateid.
> + */

Isn't it easier just to have a flag in the layout?

> + if (!memcmp(lo->stateid.data, &zero_stateid, NFS4_STATEID_SIZE))
> + do {
> + seq = read_seqbegin(&state->seqlock);
> + memcpy(lo->stateid.data, state->stateid.data,
> + sizeof(state->stateid.data));
> + } while (read_seqretry(&state->seqlock, seq));
> + write_sequnlock(&lo->seqlock);

...and if memcmp(), is the caller supposed to detect that nothing was
done?

> + dprintk("<-- %s\n", __func__);
> +}
> +
> +/*
> +* Get layout from server.
> +* for now, assume that whole file layouts are requested.
> +* arg->offset: 0
> +* arg->length: all ones
> +*/
> static struct pnfs_layout_segment *
> send_layoutget(struct pnfs_layout_hdr *lo,
> struct nfs_open_context *ctx,
> u32 iomode)
> {
> struct inode *ino = lo->inode;
> - struct pnfs_layout_segment *lseg;
> + struct nfs_server *server = NFS_SERVER(ino);
> + struct nfs4_layoutget *lgp;
> + struct pnfs_layout_segment *lseg = NULL;
>
> - /* Lets pretend we sent LAYOUTGET and got a response */
> - lseg = kzalloc(sizeof(*lseg), GFP_KERNEL);
> + dprintk("--> %s\n", __func__);
> +
> + BUG_ON(ctx == NULL);
> + lgp = kzalloc(sizeof(*lgp), GFP_KERNEL);
> + if (lgp == NULL) {
> + put_layout_hdr(lo->inode);
> + return NULL;
> + }
> + lgp->args.minlength = NFS4_MAX_UINT64;
> + lgp->args.maxcount = PNFS_LAYOUT_MAXSIZE;
> + lgp->args.range.iomode = iomode;
> + lgp->args.range.offset = 0;
> + lgp->args.range.length = NFS4_MAX_UINT64;
> + lgp->args.type = server->pnfs_curr_ld->id;
> + lgp->args.inode = ino;
> + lgp->args.ctx = get_nfs_open_context(ctx);
> + lgp->lsegpp = &lseg;
> +
> + if (!memcmp(lo->stateid.data, &zero_stateid, NFS4_STATEID_SIZE))
> + pnfs_layout_from_open_stateid(NFS_I(ino)->layout, ctx->state);

Why do an extra memcmp() here?

> +
> + /* Synchronously retrieve layout information from server and
> + * store in lseg.
> + */
> + nfs4_proc_layoutget(lgp);
> if (!lseg) {
> + /* remember that LAYOUTGET failed and suspend trying */
> set_bit(lo_fail_bit(iomode), &lo->state);
> - spin_lock(&ino->i_lock);
> - put_layout_hdr_locked(lo);
> - spin_unlock(&ino->i_lock);
> - return NULL;
> }
> - init_lseg(lo, lseg);
> - lseg->iomode = IOMODE_RW;
> - spin_lock(&ino->i_lock);
> - pnfs_insert_layout(lo, lseg);
> - put_layout_hdr_locked(lo);
> - spin_unlock(&ino->i_lock);
> return lseg;
> }
>
> +/*
> + * Compare two layout segments for sorting into layout cache.
> + * We want to preferentially return RW over RO layouts, so ensure those
> + * are seen first.
> + */
> +static s64
> +cmp_layout(u32 iomode1, u32 iomode2)
> +{
> + /* read > read/write */
> + return (int)(iomode2 == IOMODE_READ) - (int)(iomode1 == IOMODE_READ);
> +}
> +
> static void
> pnfs_insert_layout(struct pnfs_layout_hdr *lo,
> struct pnfs_layout_segment *lseg)
> {
> + struct pnfs_layout_segment *lp;
> + int found = 0;
> +
> dprintk("%s:Begin\n", __func__);
>
> assert_spin_locked(&lo->inode->i_lock);
> @@ -313,13 +413,28 @@ pnfs_insert_layout(struct pnfs_layout_hdr *lo,
> list_add_tail(&lo->layouts, &clp->cl_layouts);
> spin_unlock(&clp->cl_lock);
> }
> - /* STUB - add the constructed lseg if necessary */
> - if (list_empty(&lo->segs)) {
> + list_for_each_entry(lp, &lo->segs, fi_list) {
> + if (cmp_layout(lp->range.iomode, lseg->range.iomode) > 0)
> + continue;
> + list_add_tail(&lseg->fi_list, &lp->fi_list);
> + dprintk("%s: inserted lseg %p "
> + "iomode %d offset %llu length %llu before "
> + "lp %p iomode %d offset %llu length %llu\n",
> + __func__, lseg, lseg->range.iomode,
> + lseg->range.offset, lseg->range.length,
> + lp, lp->range.iomode, lp->range.offset,
> + lp->range.length);
> + found = 1;
> + break;
> + }
> + if (!found) {
> list_add_tail(&lseg->fi_list, &lo->segs);
> - get_layout_hdr_locked(lo);
> - dprintk("%s: inserted lseg %p iomode %d at tail\n",
> - __func__, lseg, lseg->iomode);
> + dprintk("%s: inserted lseg %p "
> + "iomode %d offset %llu length %llu at tail\n",
> + __func__, lseg, lseg->range.iomode,
> + lseg->range.offset, lseg->range.length);
> }
> + get_layout_hdr_locked(lo);
>
> dprintk("%s:Return\n", __func__);
> }
> @@ -335,6 +450,7 @@ alloc_init_layout_hdr(struct inode *ino)
> lo->refcount = 1;
> INIT_LIST_HEAD(&lo->layouts);
> INIT_LIST_HEAD(&lo->segs);
> + seqlock_init(&lo->seqlock);
> lo->inode = ino;
> return lo;
> }
> @@ -362,11 +478,46 @@ pnfs_find_alloc_layout(struct inode *ino)
> return nfsi->layout;
> }
>
> -/* STUB - LAYOUTGET never succeeds, so cache is empty */
> +/*
> + * iomode matching rules:
> + * iomode lseg match
> + * ----- ----- -----
> + * ANY READ true
> + * ANY RW true
> + * RW READ false
> + * RW RW true
> + * READ READ true
> + * READ RW true
> + */
> +static int
> +is_matching_lseg(struct pnfs_layout_segment *lseg, u32 iomode)
> +{
> + return (iomode != IOMODE_RW || lseg->range.iomode == IOMODE_RW);
> +}
> +
> +/*
> + * lookup range in layout
> + */
> static struct pnfs_layout_segment *
> pnfs_has_layout(struct pnfs_layout_hdr *lo, u32 iomode)
> {
> - return NULL;
> + struct pnfs_layout_segment *lseg, *ret = NULL;
> +
> + dprintk("%s:Begin\n", __func__);
> +
> + assert_spin_locked(&lo->inode->i_lock);
> + list_for_each_entry(lseg, &lo->segs, fi_list) {
> + if (is_matching_lseg(lseg, iomode)) {
> + ret = lseg;
> + break;
> + }
> + if (cmp_layout(iomode, lseg->range.iomode) > 0)
> + break;
> + }
> +
> + dprintk("%s:Return lseg %p ref %d\n",
> + __func__, ret, ret ? atomic_read(&ret->kref.refcount) : 0);
> + return ret;
> }
>
> /*
> @@ -403,7 +554,7 @@ pnfs_update_layout(struct inode *ino,
> if (test_bit(lo_fail_bit(iomode), &nfsi->layout->state))
> goto out_unlock;
>
> - get_layout_hdr_locked(lo);
> + get_layout_hdr_locked(lo); /* Matched in nfs4_layoutget_release */
> spin_unlock(&ino->i_lock);
>
> lseg = send_layoutget(lo, ctx, iomode);
> @@ -415,3 +566,184 @@ out_unlock:
> spin_unlock(&ino->i_lock);
> goto out;
> }
> +
> +int
> +pnfs_layout_process(struct nfs4_layoutget *lgp)
> +{
> + struct pnfs_layout_hdr *lo = NFS_I(lgp->args.inode)->layout;
> + struct nfs4_layoutget_res *res = &lgp->res;
> + struct pnfs_layout_segment *lseg;
> + struct inode *ino = lo->inode;
> + int status = 0;
> +
> + /* Inject layout blob into I/O device driver */
> + lseg = PNFS_LD_IO_OPS(lo)->alloc_lseg(lo, res);
^^^^^^^^^^^^^^

> + if (!lseg || IS_ERR(lseg)) {
> + if (!lseg)
> + status = -ENOMEM;
> + else
> + status = PTR_ERR(lseg);
> + dprintk("%s: Could not allocate layout: error %d\n",
> + __func__, status);
> + goto out;
> + }
> +
> + spin_lock(&ino->i_lock);
> + init_lseg(lo, lseg);
> + lseg->range = res->range;
> + *lgp->lsegpp = lseg;
> + pnfs_insert_layout(lo, lseg);
> +
> + /* Done processing layoutget. Set the layout stateid */
> + pnfs_set_layout_stateid(lo, &res->stateid);
> + spin_unlock(&ino->i_lock);
> +out:
> + return status;
> +}
> +
> +/*
> + * Device ID cache. Currently supports one layout type per struct nfs_client.
> + * Add layout type to the lookup key to expand to support multiple types.
> + */
> +int
> +nfs4_alloc_init_deviceid_cache(struct nfs_client *clp,
> + void (*free_callback)(struct nfs4_deviceid *))
> +{
> + struct nfs4_deviceid_cache *c;
> +
> + c = kzalloc(sizeof(struct nfs4_deviceid_cache), GFP_KERNEL);
> + if (!c)
> + return -ENOMEM;
> + spin_lock(&clp->cl_lock);
> + if (clp->cl_devid_cache != NULL) {
> + atomic_inc(&clp->cl_devid_cache->dc_ref);
> + dprintk("%s [kref [%d]]\n", __func__,
> + atomic_read(&clp->cl_devid_cache->dc_ref));
> + kfree(c);
> + } else {
> + /* kzalloc initializes hlists */
> + spin_lock_init(&c->dc_lock);
> + atomic_set(&c->dc_ref, 1);
> + c->dc_free_callback = free_callback;
> + clp->cl_devid_cache = c;
> + dprintk("%s [new]\n", __func__);
> + }
> + spin_unlock(&clp->cl_lock);
> + return 0;
> +}
> +EXPORT_SYMBOL(nfs4_alloc_init_deviceid_cache);
> +
> +void
> +nfs4_init_deviceid_node(struct nfs4_deviceid *d)
> +{
> + INIT_HLIST_NODE(&d->de_node);
> + atomic_set(&d->de_ref, 1);
> +}
> +EXPORT_SYMBOL(nfs4_init_deviceid_node);
> +
> +/* Called from layoutdriver_io_operations->alloc_lseg */
> +void
> +nfs4_set_layout_deviceid(struct pnfs_layout_segment *l, struct nfs4_deviceid *d)
> +{
> + dprintk("%s [%d]\n", __func__, atomic_read(&d->de_ref));
> + l->deviceid = d;
> +}
> +EXPORT_SYMBOL(nfs4_set_layout_deviceid);
> +
> +/*
> + * Called from layoutdriver_io_operations->free_lseg
> + * last layout segment reference frees deviceid
> + */
> +void
> +nfs4_put_layout_deviceid(struct pnfs_layout_segment *l)
> +{
> + struct nfs4_deviceid_cache *c =
> + NFS_SERVER(l->layout->inode)->nfs_client->cl_devid_cache;
> + struct pnfs_deviceid *id = &l->deviceid->de_id;
> + struct nfs4_deviceid *d;
> + struct hlist_node *n;
> + long h = nfs4_deviceid_hash(id);
> +
> + dprintk("%s [%d]\n", __func__, atomic_read(&l->deviceid->de_ref));
> + if (!atomic_dec_and_lock(&l->deviceid->de_ref, &c->dc_lock))
> + return;
> +
> + hlist_for_each_entry_rcu(d, n, &c->dc_deviceids[h], de_node)
> + if (!memcmp(&d->de_id, id, sizeof(*id))) {
> + hlist_del_rcu(&d->de_node);
> + spin_unlock(&c->dc_lock);
> + synchronize_rcu();
> + c->dc_free_callback(l->deviceid);
> + return;
> + }
> + spin_unlock(&c->dc_lock);
> +}
> +EXPORT_SYMBOL(nfs4_put_layout_deviceid);
> +
> +/* Find and reference a deviceid */
> +struct nfs4_deviceid *
> +nfs4_find_get_deviceid(struct nfs4_deviceid_cache *c, struct pnfs_deviceid *id)
> +{
> + struct nfs4_deviceid *d;
> + struct hlist_node *n;
> + long hash = nfs4_deviceid_hash(id);
> +
> + dprintk("--> %s hash %ld\n", __func__, hash);
> + rcu_read_lock();
> + hlist_for_each_entry_rcu(d, n, &c->dc_deviceids[hash], de_node) {
> + if (!memcmp(&d->de_id, id, sizeof(*id))) {
> + if (!atomic_inc_not_zero(&d->de_ref)) {
> + goto fail;
> + } else {
> + rcu_read_unlock();
> + return d;
> + }
> + }
> + }
> +fail:
> + rcu_read_unlock();
> + return NULL;
> +}
> +EXPORT_SYMBOL(nfs4_find_get_deviceid);
> +
> +/*
> + * Add a deviceid to the cache.
> + * GETDEVICEINFOs for same deviceid can race. If deviceid is found, discard new
> + */
> +struct nfs4_deviceid *
> +nfs4_add_deviceid(struct nfs4_deviceid_cache *c, struct nfs4_deviceid *new)
> +{
> + struct nfs4_deviceid *d;
> + struct hlist_node *n;
> + long hash = nfs4_deviceid_hash(&new->de_id);
> +
> + dprintk("--> %s hash %ld\n", __func__, hash);
> + spin_lock(&c->dc_lock);
> + hlist_for_each_entry_rcu(d, n, &c->dc_deviceids[hash], de_node) {
> + if (!memcmp(&d->de_id, &new->de_id, sizeof(new->de_id))) {
> + spin_unlock(&c->dc_lock);
> + dprintk("%s [discard]\n", __func__);
> + c->dc_free_callback(new);
> + return d;
> + }
> + }
> + hlist_add_head_rcu(&new->de_node, &c->dc_deviceids[hash]);
> + spin_unlock(&c->dc_lock);
> + dprintk("%s [new]\n", __func__);
> + return new;
> +}
> +EXPORT_SYMBOL(nfs4_add_deviceid);
> +
> +void
> +nfs4_put_deviceid_cache(struct nfs_client *clp)
> +{
> + struct nfs4_deviceid_cache *local = clp->cl_devid_cache;
> +
> + dprintk("--> %s cl_devid_cache %p\n", __func__, clp->cl_devid_cache);
> + if (atomic_dec_and_lock(&local->dc_ref, &clp->cl_lock)) {
> + clp->cl_devid_cache = NULL;
> + spin_unlock(&clp->cl_lock);
> + kfree(local);
> + }
> +}
> +EXPORT_SYMBOL(nfs4_put_deviceid_cache);
> diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h
> index dac6a72..d343f83 100644
> --- a/fs/nfs/pnfs.h
> +++ b/fs/nfs/pnfs.h
> @@ -12,11 +12,14 @@
>
> struct pnfs_layout_segment {
> struct list_head fi_list;
> - u32 iomode;
> + struct pnfs_layout_range range;
> struct kref kref;
> struct pnfs_layout_hdr *layout;
> + struct nfs4_deviceid *deviceid;
> };
>
> +#define NFS4_PNFS_DEVICEID4_SIZE 16
> +
> #ifdef CONFIG_NFS_V4_1
>
> #define LAYOUT_NFSV4_1_MODULE_PREFIX "nfs-layouttype4"
> @@ -38,17 +41,86 @@ struct pnfs_layout_hdr {
> int refcount;
> struct list_head layouts; /* other client layouts */
> struct list_head segs; /* layout segments list */
> + seqlock_t seqlock; /* Protects the stateid */
> + nfs4_stateid stateid;
> unsigned long state;
> struct inode *inode;
> };
>
> /* Layout driver I/O operations. */
> struct layoutdriver_io_operations {
> + struct pnfs_layout_segment * (*alloc_lseg) (struct pnfs_layout_hdr *layoutid, struct nfs4_layoutget_res *lgr);
> + void (*free_lseg) (struct pnfs_layout_segment *lseg);
> +
> /* Registration information for a new mounted file system */
> int (*initialize_mountpoint) (struct nfs_client *);
> int (*uninitialize_mountpoint) (struct nfs_client *);
> };
>
> +struct pnfs_deviceid {
> + char data[NFS4_PNFS_DEVICEID4_SIZE];
> +};
> +
> +struct pnfs_device {
> + struct pnfs_deviceid dev_id;
> + unsigned int layout_type;
> + unsigned int mincount;
> + struct page **pages;
> + void *area;
> + unsigned int pgbase;
> + unsigned int pglen;
> + unsigned int dev_notify_types;
> +};
> +
> +/*
> + * Device ID RCU cache. A device ID is unique per client ID and layout type.
> + */
> +#define NFS4_DEVICE_ID_HASH_BITS 5
> +#define NFS4_DEVICE_ID_HASH_SIZE (1 << NFS4_DEVICE_ID_HASH_BITS)
> +#define NFS4_DEVICE_ID_HASH_MASK (NFS4_DEVICE_ID_HASH_SIZE - 1)
> +
> +static inline u32
> +nfs4_deviceid_hash(struct pnfs_deviceid *id)
> +{
> + unsigned char *cptr = (unsigned char *)id->data;
> + unsigned int nbytes = NFS4_PNFS_DEVICEID4_SIZE;
> + u32 x = 0;
> +
> + while (nbytes--) {
> + x *= 37;
> + x += *cptr++;
> + }
> + return x & NFS4_DEVICE_ID_HASH_MASK;
> +}
> +
> +struct nfs4_deviceid_cache {
> + spinlock_t dc_lock;
> + atomic_t dc_ref;
> + void (*dc_free_callback)(struct nfs4_deviceid *);
> + struct hlist_head dc_deviceids[NFS4_DEVICE_ID_HASH_SIZE];
> + struct hlist_head dc_to_free;
> +};
> +
> +/* Device ID cache node */
> +struct nfs4_deviceid {
> + struct hlist_node de_node;
> + struct pnfs_deviceid de_id;
> + atomic_t de_ref;
> +};
> +
> +extern int nfs4_alloc_init_deviceid_cache(struct nfs_client *,
> + void (*free_callback)(struct nfs4_deviceid *));
> +extern void nfs4_put_deviceid_cache(struct nfs_client *);
> +extern void nfs4_init_deviceid_node(struct nfs4_deviceid *);
> +extern struct nfs4_deviceid *nfs4_find_get_deviceid(
> + struct nfs4_deviceid_cache *,
> + struct pnfs_deviceid *);
> +extern struct nfs4_deviceid *nfs4_add_deviceid(struct nfs4_deviceid_cache *,
> + struct nfs4_deviceid *);
> +extern void nfs4_set_layout_deviceid(struct pnfs_layout_segment *,
> + struct nfs4_deviceid *);
> +extern void nfs4_put_layout_deviceid(struct pnfs_layout_segment *);
> +
> extern int pnfs_register_layoutdriver(struct pnfs_layoutdriver_type *);
> extern void pnfs_unregister_layoutdriver(struct pnfs_layoutdriver_type *);
>
> @@ -58,13 +130,30 @@ PNFS_NFS_SERVER(struct pnfs_layout_hdr *lo)
> return NFS_SERVER(lo->inode);
> }
>
> +static inline struct layoutdriver_io_operations *
> +PNFS_LD_IO_OPS(struct pnfs_layout_hdr *lo)
> +{
> + return PNFS_NFS_SERVER(lo)->pnfs_curr_ld->ld_io_ops;
> +}
> +
> +/* nfs4proc.c */
> +extern int nfs4_proc_getdeviceinfo(struct nfs_server *server,
> + struct pnfs_device *dev);
> +extern int nfs4_proc_layoutget(struct nfs4_layoutget *lgp);
> +
> +/* pnfs.c */
> struct pnfs_layout_segment *
> pnfs_update_layout(struct inode *ino, struct nfs_open_context *ctx,
> enum pnfs_iomode access_type);
> void set_pnfs_layoutdriver(struct nfs_server *, u32 id);
> void unset_pnfs_layoutdriver(struct nfs_server *);
> +int pnfs_layout_process(struct nfs4_layoutget *lgp);
> +void pnfs_set_layout_stateid(struct pnfs_layout_hdr *lo,
> + const nfs4_stateid *stateid);
> void pnfs_destroy_layout(struct nfs_inode *);
> void pnfs_destroy_all_layouts(struct nfs_client *);
> +void put_layout_hdr(struct inode *inode);
> +void pnfs_get_layout_stateid(nfs4_stateid *dst, struct pnfs_layout_hdr *lo);
>
>
> static inline int lo_fail_bit(u32 iomode)
> diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
> index 2dde7c8..dcdd11c 100644
> --- a/include/linux/nfs4.h
> +++ b/include/linux/nfs4.h
> @@ -545,6 +545,8 @@ enum {
> NFSPROC4_CLNT_SEQUENCE,
> NFSPROC4_CLNT_GET_LEASE_TIME,
> NFSPROC4_CLNT_RECLAIM_COMPLETE,
> + NFSPROC4_CLNT_LAYOUTGET,
> + NFSPROC4_CLNT_GETDEVICEINFO,
> };
>
> /* nfs41 types */
> diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
> index e670a9c..7512886 100644
> --- a/include/linux/nfs_fs_sb.h
> +++ b/include/linux/nfs_fs_sb.h
> @@ -83,6 +83,7 @@ struct nfs_client {
> u32 cl_exchange_flags;
> struct nfs4_session *cl_session; /* sharred session */
> struct list_head cl_layouts;
> + struct nfs4_deviceid_cache *cl_devid_cache; /* pNFS deviceid cache */
> #endif /* CONFIG_NFS_V4_1 */
>
> #ifdef CONFIG_NFS_FSCACHE
> diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
> index 8a2c228..c4c6a61 100644
> --- a/include/linux/nfs_xdr.h
> +++ b/include/linux/nfs_xdr.h
> @@ -186,6 +186,55 @@ struct nfs4_get_lease_time_res {
> struct nfs4_sequence_res lr_seq_res;
> };
>
> +#define PNFS_LAYOUT_MAXSIZE 4096
> +
> +struct nfs4_layoutdriver_data {
> + __u32 len;
> + void *buf;
> +};
> +
> +struct pnfs_layout_range {
> + u32 iomode;
> + u64 offset;
> + u64 length;
> +};
> +
> +struct nfs4_layoutget_args {
> + __u32 type;
> + struct pnfs_layout_range range;
> + __u64 minlength;
> + __u32 maxcount;
> + struct inode *inode;
> + struct nfs_open_context *ctx;
> + struct nfs4_sequence_args seq_args;
> +};
> +
> +struct nfs4_layoutget_res {
> + __u32 return_on_close;
> + struct pnfs_layout_range range;
> + __u32 type;
> + nfs4_stateid stateid;
> + struct nfs4_layoutdriver_data layout;
> + struct nfs4_sequence_res seq_res;
> +};
> +
> +struct nfs4_layoutget {
> + struct nfs4_layoutget_args args;
> + struct nfs4_layoutget_res res;
> + struct pnfs_layout_segment **lsegpp;
> + int status;
> +};
> +
> +struct nfs4_getdeviceinfo_args {
> + struct pnfs_device *pdev;
> + struct nfs4_sequence_args seq_args;
> +};
> +
> +struct nfs4_getdeviceinfo_res {
> + struct pnfs_device *pdev;
> + struct nfs4_sequence_res seq_res;
> +};
> +
> /*
> * Arguments to the open call.
> */