blocklayoutxdr.c (5306B)
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 p = xdr_reserve_space(xdr, len + sizeof(__be32)); 86 if (!p) 87 return nfserr_resource; 88 89 for (i = 0; i < dev->nr_volumes; i++) { 90 ret = nfsd4_block_encode_volume(xdr, &dev->volumes[i]); 91 if (ret < 0) 92 return nfserrno(ret); 93 len += ret; 94 } 95 96 /* 97 * Fill in the overall length and number of volumes at the beginning 98 * of the layout. 99 */ 100 *p++ = cpu_to_be32(len); 101 *p++ = cpu_to_be32(dev->nr_volumes); 102 return 0; 103} 104 105int 106nfsd4_block_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, 107 u32 block_size) 108{ 109 struct iomap *iomaps; 110 u32 nr_iomaps, i; 111 112 if (len < sizeof(u32)) { 113 dprintk("%s: extent array too small: %u\n", __func__, len); 114 return -EINVAL; 115 } 116 len -= sizeof(u32); 117 if (len % PNFS_BLOCK_EXTENT_SIZE) { 118 dprintk("%s: extent array invalid: %u\n", __func__, len); 119 return -EINVAL; 120 } 121 122 nr_iomaps = be32_to_cpup(p++); 123 if (nr_iomaps != len / PNFS_BLOCK_EXTENT_SIZE) { 124 dprintk("%s: extent array size mismatch: %u/%u\n", 125 __func__, len, nr_iomaps); 126 return -EINVAL; 127 } 128 129 iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL); 130 if (!iomaps) { 131 dprintk("%s: failed to allocate extent array\n", __func__); 132 return -ENOMEM; 133 } 134 135 for (i = 0; i < nr_iomaps; i++) { 136 struct pnfs_block_extent bex; 137 138 memcpy(&bex.vol_id, p, sizeof(struct nfsd4_deviceid)); 139 p += XDR_QUADLEN(sizeof(struct nfsd4_deviceid)); 140 141 p = xdr_decode_hyper(p, &bex.foff); 142 if (bex.foff & (block_size - 1)) { 143 dprintk("%s: unaligned offset 0x%llx\n", 144 __func__, bex.foff); 145 goto fail; 146 } 147 p = xdr_decode_hyper(p, &bex.len); 148 if (bex.len & (block_size - 1)) { 149 dprintk("%s: unaligned length 0x%llx\n", 150 __func__, bex.foff); 151 goto fail; 152 } 153 p = xdr_decode_hyper(p, &bex.soff); 154 if (bex.soff & (block_size - 1)) { 155 dprintk("%s: unaligned disk offset 0x%llx\n", 156 __func__, bex.soff); 157 goto fail; 158 } 159 bex.es = be32_to_cpup(p++); 160 if (bex.es != PNFS_BLOCK_READWRITE_DATA) { 161 dprintk("%s: incorrect extent state %d\n", 162 __func__, bex.es); 163 goto fail; 164 } 165 166 iomaps[i].offset = bex.foff; 167 iomaps[i].length = bex.len; 168 } 169 170 *iomapp = iomaps; 171 return nr_iomaps; 172fail: 173 kfree(iomaps); 174 return -EINVAL; 175} 176 177int 178nfsd4_scsi_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp, 179 u32 block_size) 180{ 181 struct iomap *iomaps; 182 u32 nr_iomaps, expected, i; 183 184 if (len < sizeof(u32)) { 185 dprintk("%s: extent array too small: %u\n", __func__, len); 186 return -EINVAL; 187 } 188 189 nr_iomaps = be32_to_cpup(p++); 190 expected = sizeof(__be32) + nr_iomaps * PNFS_SCSI_RANGE_SIZE; 191 if (len != expected) { 192 dprintk("%s: extent array size mismatch: %u/%u\n", 193 __func__, len, expected); 194 return -EINVAL; 195 } 196 197 iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL); 198 if (!iomaps) { 199 dprintk("%s: failed to allocate extent array\n", __func__); 200 return -ENOMEM; 201 } 202 203 for (i = 0; i < nr_iomaps; i++) { 204 u64 val; 205 206 p = xdr_decode_hyper(p, &val); 207 if (val & (block_size - 1)) { 208 dprintk("%s: unaligned offset 0x%llx\n", __func__, val); 209 goto fail; 210 } 211 iomaps[i].offset = val; 212 213 p = xdr_decode_hyper(p, &val); 214 if (val & (block_size - 1)) { 215 dprintk("%s: unaligned length 0x%llx\n", __func__, val); 216 goto fail; 217 } 218 iomaps[i].length = val; 219 } 220 221 *iomapp = iomaps; 222 return nr_iomaps; 223fail: 224 kfree(iomaps); 225 return -EINVAL; 226}