18650b8a0SChristoph Hellwig /* 2f99d4fbdSChristoph Hellwig * Copyright (c) 2014-2016 Christoph Hellwig. 38650b8a0SChristoph Hellwig */ 48650b8a0SChristoph Hellwig #include <linux/sunrpc/svc.h> 58650b8a0SChristoph Hellwig #include <linux/exportfs.h> 68650b8a0SChristoph Hellwig #include <linux/nfs4.h> 78650b8a0SChristoph Hellwig 88650b8a0SChristoph Hellwig #include "nfsd.h" 98650b8a0SChristoph Hellwig #include "blocklayoutxdr.h" 108650b8a0SChristoph Hellwig 118650b8a0SChristoph Hellwig #define NFSDDBG_FACILITY NFSDDBG_PNFS 128650b8a0SChristoph Hellwig 138650b8a0SChristoph Hellwig 148650b8a0SChristoph Hellwig __be32 158650b8a0SChristoph Hellwig nfsd4_block_encode_layoutget(struct xdr_stream *xdr, 168650b8a0SChristoph Hellwig struct nfsd4_layoutget *lgp) 178650b8a0SChristoph Hellwig { 188650b8a0SChristoph Hellwig struct pnfs_block_extent *b = lgp->lg_content; 198650b8a0SChristoph Hellwig int len = sizeof(__be32) + 5 * sizeof(__be64) + sizeof(__be32); 208650b8a0SChristoph Hellwig __be32 *p; 218650b8a0SChristoph Hellwig 228650b8a0SChristoph Hellwig p = xdr_reserve_space(xdr, sizeof(__be32) + len); 238650b8a0SChristoph Hellwig if (!p) 248650b8a0SChristoph Hellwig return nfserr_toosmall; 258650b8a0SChristoph Hellwig 268650b8a0SChristoph Hellwig *p++ = cpu_to_be32(len); 278650b8a0SChristoph Hellwig *p++ = cpu_to_be32(1); /* we always return a single extent */ 288650b8a0SChristoph Hellwig 298650b8a0SChristoph Hellwig p = xdr_encode_opaque_fixed(p, &b->vol_id, 308650b8a0SChristoph Hellwig sizeof(struct nfsd4_deviceid)); 318650b8a0SChristoph Hellwig p = xdr_encode_hyper(p, b->foff); 328650b8a0SChristoph Hellwig p = xdr_encode_hyper(p, b->len); 338650b8a0SChristoph Hellwig p = xdr_encode_hyper(p, b->soff); 348650b8a0SChristoph Hellwig *p++ = cpu_to_be32(b->es); 358650b8a0SChristoph Hellwig return 0; 368650b8a0SChristoph Hellwig } 378650b8a0SChristoph Hellwig 388650b8a0SChristoph Hellwig static int 398650b8a0SChristoph Hellwig nfsd4_block_encode_volume(struct xdr_stream *xdr, struct pnfs_block_volume *b) 408650b8a0SChristoph Hellwig { 418650b8a0SChristoph Hellwig __be32 *p; 428650b8a0SChristoph Hellwig int len; 438650b8a0SChristoph Hellwig 448650b8a0SChristoph Hellwig switch (b->type) { 458650b8a0SChristoph Hellwig case PNFS_BLOCK_VOLUME_SIMPLE: 468650b8a0SChristoph Hellwig len = 4 + 4 + 8 + 4 + b->simple.sig_len; 478650b8a0SChristoph Hellwig p = xdr_reserve_space(xdr, len); 488650b8a0SChristoph Hellwig if (!p) 498650b8a0SChristoph Hellwig return -ETOOSMALL; 508650b8a0SChristoph Hellwig 518650b8a0SChristoph Hellwig *p++ = cpu_to_be32(b->type); 528650b8a0SChristoph Hellwig *p++ = cpu_to_be32(1); /* single signature */ 538650b8a0SChristoph Hellwig p = xdr_encode_hyper(p, b->simple.offset); 548650b8a0SChristoph Hellwig p = xdr_encode_opaque(p, b->simple.sig, b->simple.sig_len); 558650b8a0SChristoph Hellwig break; 56f99d4fbdSChristoph Hellwig case PNFS_BLOCK_VOLUME_SCSI: 57f99d4fbdSChristoph Hellwig len = 4 + 4 + 4 + 4 + b->scsi.designator_len + 8; 58f99d4fbdSChristoph Hellwig p = xdr_reserve_space(xdr, len); 59f99d4fbdSChristoph Hellwig if (!p) 60f99d4fbdSChristoph Hellwig return -ETOOSMALL; 61f99d4fbdSChristoph Hellwig 62f99d4fbdSChristoph Hellwig *p++ = cpu_to_be32(b->type); 63f99d4fbdSChristoph Hellwig *p++ = cpu_to_be32(b->scsi.code_set); 64f99d4fbdSChristoph Hellwig *p++ = cpu_to_be32(b->scsi.designator_type); 65f99d4fbdSChristoph Hellwig p = xdr_encode_opaque(p, b->scsi.designator, b->scsi.designator_len); 66f99d4fbdSChristoph Hellwig p = xdr_encode_hyper(p, b->scsi.pr_key); 67f99d4fbdSChristoph Hellwig break; 688650b8a0SChristoph Hellwig default: 698650b8a0SChristoph Hellwig return -ENOTSUPP; 708650b8a0SChristoph Hellwig } 718650b8a0SChristoph Hellwig 728650b8a0SChristoph Hellwig return len; 738650b8a0SChristoph Hellwig } 748650b8a0SChristoph Hellwig 758650b8a0SChristoph Hellwig __be32 768650b8a0SChristoph Hellwig nfsd4_block_encode_getdeviceinfo(struct xdr_stream *xdr, 778650b8a0SChristoph Hellwig struct nfsd4_getdeviceinfo *gdp) 788650b8a0SChristoph Hellwig { 798650b8a0SChristoph Hellwig struct pnfs_block_deviceaddr *dev = gdp->gd_device; 808650b8a0SChristoph Hellwig int len = sizeof(__be32), ret, i; 818650b8a0SChristoph Hellwig __be32 *p; 828650b8a0SChristoph Hellwig 838650b8a0SChristoph Hellwig p = xdr_reserve_space(xdr, len + sizeof(__be32)); 848650b8a0SChristoph Hellwig if (!p) 858650b8a0SChristoph Hellwig return nfserr_resource; 868650b8a0SChristoph Hellwig 878650b8a0SChristoph Hellwig for (i = 0; i < dev->nr_volumes; i++) { 888650b8a0SChristoph Hellwig ret = nfsd4_block_encode_volume(xdr, &dev->volumes[i]); 898650b8a0SChristoph Hellwig if (ret < 0) 908650b8a0SChristoph Hellwig return nfserrno(ret); 918650b8a0SChristoph Hellwig len += ret; 928650b8a0SChristoph Hellwig } 938650b8a0SChristoph Hellwig 948650b8a0SChristoph Hellwig /* 958650b8a0SChristoph Hellwig * Fill in the overall length and number of volumes at the beginning 968650b8a0SChristoph Hellwig * of the layout. 978650b8a0SChristoph Hellwig */ 988650b8a0SChristoph Hellwig *p++ = cpu_to_be32(len); 998650b8a0SChristoph Hellwig *p++ = cpu_to_be32(dev->nr_volumes); 1008650b8a0SChristoph Hellwig return 0; 1018650b8a0SChristoph Hellwig } 1028650b8a0SChristoph Hellwig 1038650b8a0SChristoph Hellwig int 1048650b8a0SChristoph Hellwig nfsd4_block_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, 1058650b8a0SChristoph Hellwig u32 block_size) 1068650b8a0SChristoph Hellwig { 1078650b8a0SChristoph Hellwig struct iomap *iomaps; 108*4b15da44SJ. Bruce Fields u32 nr_iomaps, i; 1098650b8a0SChristoph Hellwig 1108650b8a0SChristoph Hellwig if (len < sizeof(u32)) { 1118650b8a0SChristoph Hellwig dprintk("%s: extent array too small: %u\n", __func__, len); 1128650b8a0SChristoph Hellwig return -EINVAL; 1138650b8a0SChristoph Hellwig } 114*4b15da44SJ. Bruce Fields len -= sizeof(u32); 115*4b15da44SJ. Bruce Fields if (len % PNFS_BLOCK_EXTENT_SIZE) { 116*4b15da44SJ. Bruce Fields dprintk("%s: extent array invalid: %u\n", __func__, len); 117*4b15da44SJ. Bruce Fields return -EINVAL; 118*4b15da44SJ. Bruce Fields } 1198650b8a0SChristoph Hellwig 1208650b8a0SChristoph Hellwig nr_iomaps = be32_to_cpup(p++); 121*4b15da44SJ. Bruce Fields if (nr_iomaps != len / PNFS_BLOCK_EXTENT_SIZE) { 1228650b8a0SChristoph Hellwig dprintk("%s: extent array size mismatch: %u/%u\n", 123*4b15da44SJ. Bruce Fields __func__, len, nr_iomaps); 1248650b8a0SChristoph Hellwig return -EINVAL; 1258650b8a0SChristoph Hellwig } 1268650b8a0SChristoph Hellwig 1278650b8a0SChristoph Hellwig iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL); 1288650b8a0SChristoph Hellwig if (!iomaps) { 1298650b8a0SChristoph Hellwig dprintk("%s: failed to allocate extent array\n", __func__); 1308650b8a0SChristoph Hellwig return -ENOMEM; 1318650b8a0SChristoph Hellwig } 1328650b8a0SChristoph Hellwig 1338650b8a0SChristoph Hellwig for (i = 0; i < nr_iomaps; i++) { 1348650b8a0SChristoph Hellwig struct pnfs_block_extent bex; 1358650b8a0SChristoph Hellwig 1368650b8a0SChristoph Hellwig memcpy(&bex.vol_id, p, sizeof(struct nfsd4_deviceid)); 1378650b8a0SChristoph Hellwig p += XDR_QUADLEN(sizeof(struct nfsd4_deviceid)); 1388650b8a0SChristoph Hellwig 1398650b8a0SChristoph Hellwig p = xdr_decode_hyper(p, &bex.foff); 1408650b8a0SChristoph Hellwig if (bex.foff & (block_size - 1)) { 14185369523SKinglong Mee dprintk("%s: unaligned offset 0x%llx\n", 1428650b8a0SChristoph Hellwig __func__, bex.foff); 1438650b8a0SChristoph Hellwig goto fail; 1448650b8a0SChristoph Hellwig } 1458650b8a0SChristoph Hellwig p = xdr_decode_hyper(p, &bex.len); 1468650b8a0SChristoph Hellwig if (bex.len & (block_size - 1)) { 14785369523SKinglong Mee dprintk("%s: unaligned length 0x%llx\n", 1488650b8a0SChristoph Hellwig __func__, bex.foff); 1498650b8a0SChristoph Hellwig goto fail; 1508650b8a0SChristoph Hellwig } 1518650b8a0SChristoph Hellwig p = xdr_decode_hyper(p, &bex.soff); 1528650b8a0SChristoph Hellwig if (bex.soff & (block_size - 1)) { 15385369523SKinglong Mee dprintk("%s: unaligned disk offset 0x%llx\n", 1548650b8a0SChristoph Hellwig __func__, bex.soff); 1558650b8a0SChristoph Hellwig goto fail; 1568650b8a0SChristoph Hellwig } 1578650b8a0SChristoph Hellwig bex.es = be32_to_cpup(p++); 1588650b8a0SChristoph Hellwig if (bex.es != PNFS_BLOCK_READWRITE_DATA) { 1598650b8a0SChristoph Hellwig dprintk("%s: incorrect extent state %d\n", 1608650b8a0SChristoph Hellwig __func__, bex.es); 1618650b8a0SChristoph Hellwig goto fail; 1628650b8a0SChristoph Hellwig } 1638650b8a0SChristoph Hellwig 1648650b8a0SChristoph Hellwig iomaps[i].offset = bex.foff; 1658650b8a0SChristoph Hellwig iomaps[i].length = bex.len; 1668650b8a0SChristoph Hellwig } 1678650b8a0SChristoph Hellwig 1688650b8a0SChristoph Hellwig *iomapp = iomaps; 1698650b8a0SChristoph Hellwig return nr_iomaps; 1708650b8a0SChristoph Hellwig fail: 1718650b8a0SChristoph Hellwig kfree(iomaps); 1728650b8a0SChristoph Hellwig return -EINVAL; 1738650b8a0SChristoph Hellwig } 174f99d4fbdSChristoph Hellwig 175f99d4fbdSChristoph Hellwig int 176f99d4fbdSChristoph Hellwig nfsd4_scsi_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, 177f99d4fbdSChristoph Hellwig u32 block_size) 178f99d4fbdSChristoph Hellwig { 179f99d4fbdSChristoph Hellwig struct iomap *iomaps; 180f99d4fbdSChristoph Hellwig u32 nr_iomaps, expected, i; 181f99d4fbdSChristoph Hellwig 182f99d4fbdSChristoph Hellwig if (len < sizeof(u32)) { 183f99d4fbdSChristoph Hellwig dprintk("%s: extent array too small: %u\n", __func__, len); 184f99d4fbdSChristoph Hellwig return -EINVAL; 185f99d4fbdSChristoph Hellwig } 186f99d4fbdSChristoph Hellwig 187f99d4fbdSChristoph Hellwig nr_iomaps = be32_to_cpup(p++); 188f99d4fbdSChristoph Hellwig expected = sizeof(__be32) + nr_iomaps * PNFS_SCSI_RANGE_SIZE; 189f99d4fbdSChristoph Hellwig if (len != expected) { 190f99d4fbdSChristoph Hellwig dprintk("%s: extent array size mismatch: %u/%u\n", 191f99d4fbdSChristoph Hellwig __func__, len, expected); 192f99d4fbdSChristoph Hellwig return -EINVAL; 193f99d4fbdSChristoph Hellwig } 194f99d4fbdSChristoph Hellwig 195f99d4fbdSChristoph Hellwig iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL); 196f99d4fbdSChristoph Hellwig if (!iomaps) { 197f99d4fbdSChristoph Hellwig dprintk("%s: failed to allocate extent array\n", __func__); 198f99d4fbdSChristoph Hellwig return -ENOMEM; 199f99d4fbdSChristoph Hellwig } 200f99d4fbdSChristoph Hellwig 201f99d4fbdSChristoph Hellwig for (i = 0; i < nr_iomaps; i++) { 202f99d4fbdSChristoph Hellwig u64 val; 203f99d4fbdSChristoph Hellwig 204f99d4fbdSChristoph Hellwig p = xdr_decode_hyper(p, &val); 205f99d4fbdSChristoph Hellwig if (val & (block_size - 1)) { 206f99d4fbdSChristoph Hellwig dprintk("%s: unaligned offset 0x%llx\n", __func__, val); 207f99d4fbdSChristoph Hellwig goto fail; 208f99d4fbdSChristoph Hellwig } 209f99d4fbdSChristoph Hellwig iomaps[i].offset = val; 210f99d4fbdSChristoph Hellwig 211f99d4fbdSChristoph Hellwig p = xdr_decode_hyper(p, &val); 212f99d4fbdSChristoph Hellwig if (val & (block_size - 1)) { 213f99d4fbdSChristoph Hellwig dprintk("%s: unaligned length 0x%llx\n", __func__, val); 214f99d4fbdSChristoph Hellwig goto fail; 215f99d4fbdSChristoph Hellwig } 216f99d4fbdSChristoph Hellwig iomaps[i].length = val; 217f99d4fbdSChristoph Hellwig } 218f99d4fbdSChristoph Hellwig 219f99d4fbdSChristoph Hellwig *iomapp = iomaps; 220f99d4fbdSChristoph Hellwig return nr_iomaps; 221f99d4fbdSChristoph Hellwig fail: 222f99d4fbdSChristoph Hellwig kfree(iomaps); 223f99d4fbdSChristoph Hellwig return -EINVAL; 224f99d4fbdSChristoph Hellwig } 225