xref: /openbmc/linux/fs/nfsd/blocklayoutxdr.c (revision 4b15da44e742871206582f6aa2997b009648f02f)
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