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 16 nfsd4_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 39 static int 40 nfsd4_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 + (XDR_QUADLEN(b->simple.sig_len) << 2); 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 + (XDR_QUADLEN(b->scsi.designator_len) << 2) + 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 77 nfsd4_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 104 int 105 nfsd4_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; 171 fail: 172 kfree(iomaps); 173 return -EINVAL; 174 } 175 176 int 177 nfsd4_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; 222 fail: 223 kfree(iomaps); 224 return -EINVAL; 225 } 226