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