linux/fs/nfsd/blocklayoutxdr.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2014-2016 Christoph Hellwig.
   3 */
   4#include <linux/sunrpc/svc.h>
   5#include <linux/exportfs.h>
   6#include <linux/iomap.h>
   7#include <linux/nfs4.h>
   8
   9#include "nfsd.h"
  10#include "blocklayoutxdr.h"
  11
  12#define NFSDDBG_FACILITY        NFSDDBG_PNFS
  13
  14
  15__be32
  16nfsd4_block_encode_layoutget(struct xdr_stream *xdr,
  17                struct nfsd4_layoutget *lgp)
  18{
  19        struct pnfs_block_extent *b = lgp->lg_content;
  20        int len = sizeof(__be32) + 5 * sizeof(__be64) + sizeof(__be32);
  21        __be32 *p;
  22
  23        p = xdr_reserve_space(xdr, sizeof(__be32) + len);
  24        if (!p)
  25                return nfserr_toosmall;
  26
  27        *p++ = cpu_to_be32(len);
  28        *p++ = cpu_to_be32(1);          /* we always return a single extent */
  29
  30        p = xdr_encode_opaque_fixed(p, &b->vol_id,
  31                        sizeof(struct nfsd4_deviceid));
  32        p = xdr_encode_hyper(p, b->foff);
  33        p = xdr_encode_hyper(p, b->len);
  34        p = xdr_encode_hyper(p, b->soff);
  35        *p++ = cpu_to_be32(b->es);
  36        return 0;
  37}
  38
  39static int
  40nfsd4_block_encode_volume(struct xdr_stream *xdr, struct pnfs_block_volume *b)
  41{
  42        __be32 *p;
  43        int len;
  44
  45        switch (b->type) {
  46        case PNFS_BLOCK_VOLUME_SIMPLE:
  47                len = 4 + 4 + 8 + 4 + b->simple.sig_len;
  48                p = xdr_reserve_space(xdr, len);
  49                if (!p)
  50                        return -ETOOSMALL;
  51
  52                *p++ = cpu_to_be32(b->type);
  53                *p++ = cpu_to_be32(1);  /* single signature */
  54                p = xdr_encode_hyper(p, b->simple.offset);
  55                p = xdr_encode_opaque(p, b->simple.sig, b->simple.sig_len);
  56                break;
  57        case PNFS_BLOCK_VOLUME_SCSI:
  58                len = 4 + 4 + 4 + 4 + b->scsi.designator_len + 8;
  59                p = xdr_reserve_space(xdr, len);
  60                if (!p)
  61                        return -ETOOSMALL;
  62
  63                *p++ = cpu_to_be32(b->type);
  64                *p++ = cpu_to_be32(b->scsi.code_set);
  65                *p++ = cpu_to_be32(b->scsi.designator_type);
  66                p = xdr_encode_opaque(p, b->scsi.designator, b->scsi.designator_len);
  67                p = xdr_encode_hyper(p, b->scsi.pr_key);
  68                break;
  69        default:
  70                return -ENOTSUPP;
  71        }
  72
  73        return len;
  74}
  75
  76__be32
  77nfsd4_block_encode_getdeviceinfo(struct xdr_stream *xdr,
  78                struct nfsd4_getdeviceinfo *gdp)
  79{
  80        struct pnfs_block_deviceaddr *dev = gdp->gd_device;
  81        int len = sizeof(__be32), ret, i;
  82        __be32 *p;
  83
  84        p = xdr_reserve_space(xdr, len + sizeof(__be32));
  85        if (!p)
  86                return nfserr_resource;
  87
  88        for (i = 0; i < dev->nr_volumes; i++) {
  89                ret = nfsd4_block_encode_volume(xdr, &dev->volumes[i]);
  90                if (ret < 0)
  91                        return nfserrno(ret);
  92                len += ret;
  93        }
  94
  95        /*
  96         * Fill in the overall length and number of volumes at the beginning
  97         * of the layout.
  98         */
  99        *p++ = cpu_to_be32(len);
 100        *p++ = cpu_to_be32(dev->nr_volumes);
 101        return 0;
 102}
 103
 104int
 105nfsd4_block_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp,
 106                u32 block_size)
 107{
 108        struct iomap *iomaps;
 109        u32 nr_iomaps, i;
 110
 111        if (len < sizeof(u32)) {
 112                dprintk("%s: extent array too small: %u\n", __func__, len);
 113                return -EINVAL;
 114        }
 115        len -= sizeof(u32);
 116        if (len % PNFS_BLOCK_EXTENT_SIZE) {
 117                dprintk("%s: extent array invalid: %u\n", __func__, len);
 118                return -EINVAL;
 119        }
 120
 121        nr_iomaps = be32_to_cpup(p++);
 122        if (nr_iomaps != len / PNFS_BLOCK_EXTENT_SIZE) {
 123                dprintk("%s: extent array size mismatch: %u/%u\n",
 124                        __func__, len, nr_iomaps);
 125                return -EINVAL;
 126        }
 127
 128        iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL);
 129        if (!iomaps) {
 130                dprintk("%s: failed to allocate extent array\n", __func__);
 131                return -ENOMEM;
 132        }
 133
 134        for (i = 0; i < nr_iomaps; i++) {
 135                struct pnfs_block_extent bex;
 136
 137                memcpy(&bex.vol_id, p, sizeof(struct nfsd4_deviceid));
 138                p += XDR_QUADLEN(sizeof(struct nfsd4_deviceid));
 139
 140                p = xdr_decode_hyper(p, &bex.foff);
 141                if (bex.foff & (block_size - 1)) {
 142                        dprintk("%s: unaligned offset 0x%llx\n",
 143                                __func__, bex.foff);
 144                        goto fail;
 145                }
 146                p = xdr_decode_hyper(p, &bex.len);
 147                if (bex.len & (block_size - 1)) {
 148                        dprintk("%s: unaligned length 0x%llx\n",
 149                                __func__, bex.foff);
 150                        goto fail;
 151                }
 152                p = xdr_decode_hyper(p, &bex.soff);
 153                if (bex.soff & (block_size - 1)) {
 154                        dprintk("%s: unaligned disk offset 0x%llx\n",
 155                                __func__, bex.soff);
 156                        goto fail;
 157                }
 158                bex.es = be32_to_cpup(p++);
 159                if (bex.es != PNFS_BLOCK_READWRITE_DATA) {
 160                        dprintk("%s: incorrect extent state %d\n",
 161                                __func__, bex.es);
 162                        goto fail;
 163                }
 164
 165                iomaps[i].offset = bex.foff;
 166                iomaps[i].length = bex.len;
 167        }
 168
 169        *iomapp = iomaps;
 170        return nr_iomaps;
 171fail:
 172        kfree(iomaps);
 173        return -EINVAL;
 174}
 175
 176int
 177nfsd4_scsi_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp,
 178                u32 block_size)
 179{
 180        struct iomap *iomaps;
 181        u32 nr_iomaps, expected, i;
 182
 183        if (len < sizeof(u32)) {
 184                dprintk("%s: extent array too small: %u\n", __func__, len);
 185                return -EINVAL;
 186        }
 187
 188        nr_iomaps = be32_to_cpup(p++);
 189        expected = sizeof(__be32) + nr_iomaps * PNFS_SCSI_RANGE_SIZE;
 190        if (len != expected) {
 191                dprintk("%s: extent array size mismatch: %u/%u\n",
 192                        __func__, len, expected);
 193                return -EINVAL;
 194        }
 195
 196        iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL);
 197        if (!iomaps) {
 198                dprintk("%s: failed to allocate extent array\n", __func__);
 199                return -ENOMEM;
 200        }
 201
 202        for (i = 0; i < nr_iomaps; i++) {
 203                u64 val;
 204
 205                p = xdr_decode_hyper(p, &val);
 206                if (val & (block_size - 1)) {
 207                        dprintk("%s: unaligned offset 0x%llx\n", __func__, val);
 208                        goto fail;
 209                }
 210                iomaps[i].offset = val;
 211
 212                p = xdr_decode_hyper(p, &val);
 213                if (val & (block_size - 1)) {
 214                        dprintk("%s: unaligned length 0x%llx\n", __func__, val);
 215                        goto fail;
 216                }
 217                iomaps[i].length = val;
 218        }
 219
 220        *iomapp = iomaps;
 221        return nr_iomaps;
 222fail:
 223        kfree(iomaps);
 224        return -EINVAL;
 225}
 226