Blame view

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