Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  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}