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

Fred Isaman
2010-09-10T21:47:31Z

On Fri, Sep 10, 2010 at 1:11 PM, Trond Myklebust
<Trond.Myklebust@netapp.com> wrote:
> 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()?
>

Hmmm, since it is being called synchronously at the moment, we should
probably remove the nfs4_async_handle_error call.

>> +
>> +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.

I agree.

>
>>  #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.
>

OK, I'll rip this out and just set the bitmap to zero.

>> +     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.
>

OK.

>> +     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.

This is defined in pnfs.h as
PNFS_NFS_SERVER()->pnfs_curr_ld->ld_io_iops, mainly to save typing.

The macro that you had objected to was PNFS_EXISTS_LDIO_OP form
Benny's tree, which is now gone.

>
>>       /* 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?

OK, clearly the function and call to pnfs_layout_from_open_stateid
need to be reexamined.

Fred

>
>> +
>> +     /* 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.
>>   */
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>