Blame view

net/sunrpc/xdr.c 30.9 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
  /*
   * linux/net/sunrpc/xdr.c
   *
   * Generic XDR support.
   *
   * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
   */
a246b0105   Chuck Lever   [PATCH] RPC: intr...
8
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
9
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
  #include <linux/types.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
13
14
  #include <linux/string.h>
  #include <linux/kernel.h>
  #include <linux/pagemap.h>
  #include <linux/errno.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
17
18
19
20
  #include <linux/sunrpc/xdr.h>
  #include <linux/sunrpc/msg_prot.h>
  
  /*
   * XDR functions for basic NFS types
   */
d8ed029d6   Alexey Dobriyan   [SUNRPC]: trivial...
21
22
  __be32 *
  xdr_encode_netobj(__be32 *p, const struct xdr_netobj *obj)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
  {
  	unsigned int	quadlen = XDR_QUADLEN(obj->len);
  
  	p[quadlen] = 0;		/* zero trailing bytes */
9f162d2a8   Benny Halevy   sunrpc: hton -> c...
27
  	*p++ = cpu_to_be32(obj->len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
30
  	memcpy(p, obj->data, obj->len);
  	return p + XDR_QUADLEN(obj->len);
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
31
  EXPORT_SYMBOL_GPL(xdr_encode_netobj);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32

d8ed029d6   Alexey Dobriyan   [SUNRPC]: trivial...
33
34
  __be32 *
  xdr_decode_netobj(__be32 *p, struct xdr_netobj *obj)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
36
  {
  	unsigned int	len;
98866b5ab   Benny Halevy   sunrpc: ntoh -> b...
37
  	if ((len = be32_to_cpu(*p++)) > XDR_MAX_NETOBJ)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
40
41
42
  		return NULL;
  	obj->len  = len;
  	obj->data = (u8 *) p;
  	return p + XDR_QUADLEN(len);
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
43
  EXPORT_SYMBOL_GPL(xdr_decode_netobj);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
45
46
  
  /**
   * xdr_encode_opaque_fixed - Encode fixed length opaque data
4dc3b16ba   Pavel Pisa   [PATCH] DocBook: ...
47
48
49
   * @p: pointer to current position in XDR buffer.
   * @ptr: pointer to data to encode (or NULL)
   * @nbytes: size of data.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
51
52
53
54
55
56
57
58
   *
   * Copy the array of data of length nbytes at ptr to the XDR buffer
   * at position p, then align to the next 32-bit boundary by padding
   * with zero bytes (see RFC1832).
   * Note: if ptr is NULL, only the padding is performed.
   *
   * Returns the updated current XDR buffer position
   *
   */
d8ed029d6   Alexey Dobriyan   [SUNRPC]: trivial...
59
  __be32 *xdr_encode_opaque_fixed(__be32 *p, const void *ptr, unsigned int nbytes)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
61
62
63
64
65
66
67
68
69
70
71
72
  {
  	if (likely(nbytes != 0)) {
  		unsigned int quadlen = XDR_QUADLEN(nbytes);
  		unsigned int padding = (quadlen << 2) - nbytes;
  
  		if (ptr != NULL)
  			memcpy(p, ptr, nbytes);
  		if (padding != 0)
  			memset((char *)p + nbytes, 0, padding);
  		p += quadlen;
  	}
  	return p;
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
73
  EXPORT_SYMBOL_GPL(xdr_encode_opaque_fixed);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
76
  
  /**
   * xdr_encode_opaque - Encode variable length opaque data
4dc3b16ba   Pavel Pisa   [PATCH] DocBook: ...
77
78
79
   * @p: pointer to current position in XDR buffer.
   * @ptr: pointer to data to encode (or NULL)
   * @nbytes: size of data.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
82
   *
   * Returns the updated current XDR buffer position
   */
d8ed029d6   Alexey Dobriyan   [SUNRPC]: trivial...
83
  __be32 *xdr_encode_opaque(__be32 *p, const void *ptr, unsigned int nbytes)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
  {
9f162d2a8   Benny Halevy   sunrpc: hton -> c...
85
  	*p++ = cpu_to_be32(nbytes);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
  	return xdr_encode_opaque_fixed(p, ptr, nbytes);
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
88
  EXPORT_SYMBOL_GPL(xdr_encode_opaque);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89

d8ed029d6   Alexey Dobriyan   [SUNRPC]: trivial...
90
91
  __be32 *
  xdr_encode_string(__be32 *p, const char *string)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
93
94
  {
  	return xdr_encode_array(p, string, strlen(string));
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
95
  EXPORT_SYMBOL_GPL(xdr_encode_string);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96

d8ed029d6   Alexey Dobriyan   [SUNRPC]: trivial...
97
  __be32 *
e5cff482c   Chuck Lever   SUNRPC: Use unsig...
98
99
  xdr_decode_string_inplace(__be32 *p, char **sp,
  			  unsigned int *lenp, unsigned int maxlen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
  {
e5cff482c   Chuck Lever   SUNRPC: Use unsig...
101
  	u32 len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102

98866b5ab   Benny Halevy   sunrpc: ntoh -> b...
103
  	len = be32_to_cpu(*p++);
e5cff482c   Chuck Lever   SUNRPC: Use unsig...
104
  	if (len > maxlen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
106
107
108
109
  		return NULL;
  	*lenp = len;
  	*sp = (char *) p;
  	return p + XDR_QUADLEN(len);
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
110
  EXPORT_SYMBOL_GPL(xdr_decode_string_inplace);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111

b4687da7f   Chuck Lever   SUNRPC: Refactor ...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  /**
   * xdr_terminate_string - '\0'-terminate a string residing in an xdr_buf
   * @buf: XDR buffer where string resides
   * @len: length of string, in bytes
   *
   */
  void
  xdr_terminate_string(struct xdr_buf *buf, const u32 len)
  {
  	char *kaddr;
  
  	kaddr = kmap_atomic(buf->pages[0], KM_USER0);
  	kaddr[buf->page_base + len] = '\0';
  	kunmap_atomic(kaddr, KM_USER0);
  }
0d961aa93   Trond Myklebust   SUNRPC: Convert t...
127
  EXPORT_SYMBOL_GPL(xdr_terminate_string);
b4687da7f   Chuck Lever   SUNRPC: Refactor ...
128

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
  void
  xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base,
  		 unsigned int len)
  {
  	struct kvec *tail = xdr->tail;
  	u32 *p;
  
  	xdr->pages = pages;
  	xdr->page_base = base;
  	xdr->page_len = len;
  
  	p = (u32 *)xdr->head[0].iov_base + XDR_QUADLEN(xdr->head[0].iov_len);
  	tail->iov_base = p;
  	tail->iov_len = 0;
  
  	if (len & 3) {
  		unsigned int pad = 4 - (len & 3);
  
  		*p = 0;
  		tail->iov_base = (char *)p + (len & 3);
  		tail->iov_len  = pad;
  		len += pad;
  	}
  	xdr->buflen += len;
  	xdr->len += len;
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
155
  EXPORT_SYMBOL_GPL(xdr_encode_pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
  
  void
  xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
  		 struct page **pages, unsigned int base, unsigned int len)
  {
  	struct kvec *head = xdr->head;
  	struct kvec *tail = xdr->tail;
  	char *buf = (char *)head->iov_base;
  	unsigned int buflen = head->iov_len;
  
  	head->iov_len  = offset;
  
  	xdr->pages = pages;
  	xdr->page_base = base;
  	xdr->page_len = len;
  
  	tail->iov_base = buf + offset;
  	tail->iov_len = buflen - offset;
  
  	xdr->buflen += len;
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
177
  EXPORT_SYMBOL_GPL(xdr_inline_pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
180
181
182
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
  /*
   * Helper routines for doing 'memmove' like operations on a struct xdr_buf
   *
   * _shift_data_right_pages
   * @pages: vector of pages containing both the source and dest memory area.
   * @pgto_base: page vector address of destination
   * @pgfrom_base: page vector address of source
   * @len: number of bytes to copy
   *
   * Note: the addresses pgto_base and pgfrom_base are both calculated in
   *       the same way:
   *            if a memory area starts at byte 'base' in page 'pages[i]',
   *            then its address is given as (i << PAGE_CACHE_SHIFT) + base
   * Also note: pgfrom_base must be < pgto_base, but the memory areas
   * 	they point to may overlap.
   */
  static void
  _shift_data_right_pages(struct page **pages, size_t pgto_base,
  		size_t pgfrom_base, size_t len)
  {
  	struct page **pgfrom, **pgto;
  	char *vfrom, *vto;
  	size_t copy;
  
  	BUG_ON(pgto_base <= pgfrom_base);
  
  	pgto_base += len;
  	pgfrom_base += len;
  
  	pgto = pages + (pgto_base >> PAGE_CACHE_SHIFT);
  	pgfrom = pages + (pgfrom_base >> PAGE_CACHE_SHIFT);
  
  	pgto_base &= ~PAGE_CACHE_MASK;
  	pgfrom_base &= ~PAGE_CACHE_MASK;
  
  	do {
  		/* Are any pointers crossing a page boundary? */
  		if (pgto_base == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
  			pgto_base = PAGE_CACHE_SIZE;
  			pgto--;
  		}
  		if (pgfrom_base == 0) {
  			pgfrom_base = PAGE_CACHE_SIZE;
  			pgfrom--;
  		}
  
  		copy = len;
  		if (copy > pgto_base)
  			copy = pgto_base;
  		if (copy > pgfrom_base)
  			copy = pgfrom_base;
  		pgto_base -= copy;
  		pgfrom_base -= copy;
  
  		vto = kmap_atomic(*pgto, KM_USER0);
  		vfrom = kmap_atomic(*pgfrom, KM_USER1);
  		memmove(vto + pgto_base, vfrom + pgfrom_base, copy);
bce3481c9   Trond Myklebust   This fixes a pani...
236
  		flush_dcache_page(*pgto);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
238
239
240
  		kunmap_atomic(vfrom, KM_USER1);
  		kunmap_atomic(vto, KM_USER0);
  
  	} while ((len -= copy) != 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
  }
  
  /*
   * _copy_to_pages
   * @pages: array of pages
   * @pgbase: page vector address of destination
   * @p: pointer to source data
   * @len: length
   *
   * Copies data from an arbitrary memory location into an array of pages
   * The copy is assumed to be non-overlapping.
   */
  static void
  _copy_to_pages(struct page **pages, size_t pgbase, const char *p, size_t len)
  {
  	struct page **pgto;
  	char *vto;
  	size_t copy;
  
  	pgto = pages + (pgbase >> PAGE_CACHE_SHIFT);
  	pgbase &= ~PAGE_CACHE_MASK;
daeba89d4   Trond Myklebust   SUNRPC: don't cal...
262
  	for (;;) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
264
265
266
267
268
269
  		copy = PAGE_CACHE_SIZE - pgbase;
  		if (copy > len)
  			copy = len;
  
  		vto = kmap_atomic(*pgto, KM_USER0);
  		memcpy(vto + pgbase, p, copy);
  		kunmap_atomic(vto, KM_USER0);
daeba89d4   Trond Myklebust   SUNRPC: don't cal...
270
271
272
  		len -= copy;
  		if (len == 0)
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
274
275
276
277
278
279
  		pgbase += copy;
  		if (pgbase == PAGE_CACHE_SIZE) {
  			flush_dcache_page(*pgto);
  			pgbase = 0;
  			pgto++;
  		}
  		p += copy;
daeba89d4   Trond Myklebust   SUNRPC: don't cal...
280
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
283
284
285
286
287
288
289
290
291
292
293
  	flush_dcache_page(*pgto);
  }
  
  /*
   * _copy_from_pages
   * @p: pointer to destination
   * @pages: array of pages
   * @pgbase: offset of source data
   * @len: length
   *
   * Copies data into an arbitrary memory location from an array of pages
   * The copy is assumed to be non-overlapping.
   */
bf118a342   Andy Adamson   NFSv4: include bi...
294
  void
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
  _copy_from_pages(char *p, struct page **pages, size_t pgbase, size_t len)
  {
  	struct page **pgfrom;
  	char *vfrom;
  	size_t copy;
  
  	pgfrom = pages + (pgbase >> PAGE_CACHE_SHIFT);
  	pgbase &= ~PAGE_CACHE_MASK;
  
  	do {
  		copy = PAGE_CACHE_SIZE - pgbase;
  		if (copy > len)
  			copy = len;
  
  		vfrom = kmap_atomic(*pgfrom, KM_USER0);
  		memcpy(p, vfrom + pgbase, copy);
  		kunmap_atomic(vfrom, KM_USER0);
  
  		pgbase += copy;
  		if (pgbase == PAGE_CACHE_SIZE) {
  			pgbase = 0;
  			pgfrom++;
  		}
  		p += copy;
  
  	} while ((len -= copy) != 0);
  }
bf118a342   Andy Adamson   NFSv4: include bi...
322
  EXPORT_SYMBOL_GPL(_copy_from_pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
324
325
326
327
328
  
  /*
   * xdr_shrink_bufhead
   * @buf: xdr_buf
   * @len: bytes to remove from buf->head[0]
   *
cca5172a7   YOSHIFUJI Hideaki   [NET] SUNRPC: Fix...
329
   * Shrinks XDR buffer's header kvec buf->head[0] by
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
   * 'len' bytes. The extra data is not lost, but is instead
   * moved into the inlined pages and/or the tail.
   */
  static void
  xdr_shrink_bufhead(struct xdr_buf *buf, size_t len)
  {
  	struct kvec *head, *tail;
  	size_t copy, offs;
  	unsigned int pglen = buf->page_len;
  
  	tail = buf->tail;
  	head = buf->head;
  	BUG_ON (len > head->iov_len);
  
  	/* Shift the tail first */
  	if (tail->iov_len != 0) {
  		if (tail->iov_len > len) {
  			copy = tail->iov_len - len;
  			memmove((char *)tail->iov_base + len,
  					tail->iov_base, copy);
  		}
  		/* Copy from the inlined pages into the tail */
  		copy = len;
  		if (copy > pglen)
  			copy = pglen;
  		offs = len - copy;
  		if (offs >= tail->iov_len)
  			copy = 0;
  		else if (copy > tail->iov_len - offs)
  			copy = tail->iov_len - offs;
  		if (copy != 0)
  			_copy_from_pages((char *)tail->iov_base + offs,
  					buf->pages,
  					buf->page_base + pglen + offs - len,
  					copy);
  		/* Do we also need to copy data from the head into the tail ? */
  		if (len > pglen) {
  			offs = copy = len - pglen;
  			if (copy > tail->iov_len)
  				copy = tail->iov_len;
  			memcpy(tail->iov_base,
  					(char *)head->iov_base +
  					head->iov_len - offs,
  					copy);
  		}
  	}
  	/* Now handle pages */
  	if (pglen != 0) {
  		if (pglen > len)
  			_shift_data_right_pages(buf->pages,
  					buf->page_base + len,
  					buf->page_base,
  					pglen - len);
  		copy = len;
  		if (len > pglen)
  			copy = pglen;
  		_copy_to_pages(buf->pages, buf->page_base,
  				(char *)head->iov_base + head->iov_len - len,
  				copy);
  	}
  	head->iov_len -= len;
  	buf->buflen -= len;
  	/* Have we truncated the message? */
  	if (buf->len > buf->buflen)
  		buf->len = buf->buflen;
  }
  
  /*
   * xdr_shrink_pagelen
   * @buf: xdr_buf
   * @len: bytes to remove from buf->pages
   *
cca5172a7   YOSHIFUJI Hideaki   [NET] SUNRPC: Fix...
402
   * Shrinks XDR buffer's page array buf->pages by
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
404
405
406
407
408
409
410
   * 'len' bytes. The extra data is not lost, but is instead
   * moved into the tail.
   */
  static void
  xdr_shrink_pagelen(struct xdr_buf *buf, size_t len)
  {
  	struct kvec *tail;
  	size_t copy;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411
  	unsigned int pglen = buf->page_len;
cf187c2d7   Trond Myklebust   SUNRPC: Don't tru...
412
  	unsigned int tailbuf_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
415
  
  	tail = buf->tail;
  	BUG_ON (len > pglen);
cf187c2d7   Trond Myklebust   SUNRPC: Don't tru...
416
  	tailbuf_len = buf->buflen - buf->head->iov_len - buf->page_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
417
  	/* Shift the tail first */
cf187c2d7   Trond Myklebust   SUNRPC: Don't tru...
418
419
420
421
422
423
  	if (tailbuf_len != 0) {
  		unsigned int free_space = tailbuf_len - tail->iov_len;
  
  		if (len < free_space)
  			free_space = len;
  		tail->iov_len += free_space;
42d6d8ab5   Benny Halevy   sunrpc: simplify ...
424
  		copy = len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
  		if (tail->iov_len > len) {
0fe62a359   Benny Halevy   sunrpc: clean up ...
426
  			char *p = (char *)tail->iov_base + len;
2e29ebb81   Benny Halevy   sunrpc: don't use...
427
  			memmove(p, tail->iov_base, tail->iov_len - len);
42d6d8ab5   Benny Halevy   sunrpc: simplify ...
428
  		} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429
  			copy = tail->iov_len;
42d6d8ab5   Benny Halevy   sunrpc: simplify ...
430
  		/* Copy from the inlined pages into the tail */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
  		_copy_from_pages((char *)tail->iov_base,
  				buf->pages, buf->page_base + pglen - len,
  				copy);
  	}
  	buf->page_len -= len;
  	buf->buflen -= len;
  	/* Have we truncated the message? */
  	if (buf->len > buf->buflen)
  		buf->len = buf->buflen;
  }
  
  void
  xdr_shift_buf(struct xdr_buf *buf, size_t len)
  {
  	xdr_shrink_bufhead(buf, len);
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
447
  EXPORT_SYMBOL_GPL(xdr_shift_buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448
449
450
451
452
453
454
455
456
457
458
459
460
461
  
  /**
   * xdr_init_encode - Initialize a struct xdr_stream for sending data.
   * @xdr: pointer to xdr_stream struct
   * @buf: pointer to XDR buffer in which to encode data
   * @p: current pointer inside XDR buffer
   *
   * Note: at the moment the RPC client only passes the length of our
   *	 scratch buffer in the xdr_buf's header kvec. Previously this
   *	 meant we needed to call xdr_adjust_iovec() after encoding the
   *	 data. With the new scheme, the xdr_stream manages the details
   *	 of the buffer length, and takes care of adjusting the kvec
   *	 length for us.
   */
d8ed029d6   Alexey Dobriyan   [SUNRPC]: trivial...
462
  void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
464
  {
  	struct kvec *iov = buf->head;
334ccfd54   Trond Myklebust   [PATCH] RPC: Ensu...
465
  	int scratch_len = buf->buflen - buf->page_len - buf->tail[0].iov_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
466

334ccfd54   Trond Myklebust   [PATCH] RPC: Ensu...
467
  	BUG_ON(scratch_len < 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
469
  	xdr->buf = buf;
  	xdr->iov = iov;
d8ed029d6   Alexey Dobriyan   [SUNRPC]: trivial...
470
471
  	xdr->p = (__be32 *)((char *)iov->iov_base + iov->iov_len);
  	xdr->end = (__be32 *)((char *)iov->iov_base + scratch_len);
334ccfd54   Trond Myklebust   [PATCH] RPC: Ensu...
472
473
474
475
476
477
478
479
480
481
482
  	BUG_ON(iov->iov_len > scratch_len);
  
  	if (p != xdr->p && p != NULL) {
  		size_t len;
  
  		BUG_ON(p < xdr->p || p > xdr->end);
  		len = (char *)p - (char *)xdr->p;
  		xdr->p = p;
  		buf->len += len;
  		iov->iov_len += len;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
484
  EXPORT_SYMBOL_GPL(xdr_init_encode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
486
487
488
489
490
491
492
493
494
  
  /**
   * xdr_reserve_space - Reserve buffer space for sending
   * @xdr: pointer to xdr_stream
   * @nbytes: number of bytes to reserve
   *
   * Checks that we have enough buffer space to encode 'nbytes' more
   * bytes of data. If so, update the total xdr_buf length, and
   * adjust the length of the current kvec.
   */
d8ed029d6   Alexey Dobriyan   [SUNRPC]: trivial...
495
  __be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496
  {
d8ed029d6   Alexey Dobriyan   [SUNRPC]: trivial...
497
498
  	__be32 *p = xdr->p;
  	__be32 *q;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
500
501
502
503
504
505
506
507
508
509
510
  
  	/* align nbytes on the next 32-bit boundary */
  	nbytes += 3;
  	nbytes &= ~3;
  	q = p + (nbytes >> 2);
  	if (unlikely(q > xdr->end || q < p))
  		return NULL;
  	xdr->p = q;
  	xdr->iov->iov_len += nbytes;
  	xdr->buf->len += nbytes;
  	return p;
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
511
  EXPORT_SYMBOL_GPL(xdr_reserve_space);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
  
  /**
   * xdr_write_pages - Insert a list of pages into an XDR buffer for sending
   * @xdr: pointer to xdr_stream
   * @pages: list of pages
   * @base: offset of first byte
   * @len: length of data in bytes
   *
   */
  void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int base,
  		 unsigned int len)
  {
  	struct xdr_buf *buf = xdr->buf;
  	struct kvec *iov = buf->tail;
  	buf->pages = pages;
  	buf->page_base = base;
  	buf->page_len = len;
  
  	iov->iov_base = (char *)xdr->p;
  	iov->iov_len  = 0;
  	xdr->iov = iov;
  
  	if (len & 3) {
  		unsigned int pad = 4 - (len & 3);
  
  		BUG_ON(xdr->p >= xdr->end);
  		iov->iov_base = (char *)xdr->p + (len & 3);
  		iov->iov_len  += pad;
  		len += pad;
  		*xdr->p++ = 0;
  	}
  	buf->buflen += len;
  	buf->len += len;
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
546
  EXPORT_SYMBOL_GPL(xdr_write_pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547

6650239a4   Trond Myklebust   NFS: Don't use vm...
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
  static void xdr_set_iov(struct xdr_stream *xdr, struct kvec *iov,
  		__be32 *p, unsigned int len)
  {
  	if (len > iov->iov_len)
  		len = iov->iov_len;
  	if (p == NULL)
  		p = (__be32*)iov->iov_base;
  	xdr->p = p;
  	xdr->end = (__be32*)(iov->iov_base + len);
  	xdr->iov = iov;
  	xdr->page_ptr = NULL;
  }
  
  static int xdr_set_page_base(struct xdr_stream *xdr,
  		unsigned int base, unsigned int len)
  {
  	unsigned int pgnr;
  	unsigned int maxlen;
  	unsigned int pgoff;
  	unsigned int pgend;
  	void *kaddr;
  
  	maxlen = xdr->buf->page_len;
  	if (base >= maxlen)
  		return -EINVAL;
  	maxlen -= base;
  	if (len > maxlen)
  		len = maxlen;
  
  	base += xdr->buf->page_base;
  
  	pgnr = base >> PAGE_SHIFT;
  	xdr->page_ptr = &xdr->buf->pages[pgnr];
  	kaddr = page_address(*xdr->page_ptr);
  
  	pgoff = base & ~PAGE_MASK;
  	xdr->p = (__be32*)(kaddr + pgoff);
  
  	pgend = pgoff + len;
  	if (pgend > PAGE_SIZE)
  		pgend = PAGE_SIZE;
  	xdr->end = (__be32*)(kaddr + pgend);
  	xdr->iov = NULL;
  	return 0;
  }
  
  static void xdr_set_next_page(struct xdr_stream *xdr)
  {
  	unsigned int newbase;
  
  	newbase = (1 + xdr->page_ptr - xdr->buf->pages) << PAGE_SHIFT;
  	newbase -= xdr->buf->page_base;
  
  	if (xdr_set_page_base(xdr, newbase, PAGE_SIZE) < 0)
  		xdr_set_iov(xdr, xdr->buf->tail, NULL, xdr->buf->len);
  }
  
  static bool xdr_set_next_buffer(struct xdr_stream *xdr)
  {
  	if (xdr->page_ptr != NULL)
  		xdr_set_next_page(xdr);
  	else if (xdr->iov == xdr->buf->head) {
  		if (xdr_set_page_base(xdr, 0, PAGE_SIZE) < 0)
  			xdr_set_iov(xdr, xdr->buf->tail, NULL, xdr->buf->len);
  	}
  	return xdr->p != xdr->end;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
615
616
617
618
619
620
  /**
   * xdr_init_decode - Initialize an xdr_stream for decoding data.
   * @xdr: pointer to xdr_stream struct
   * @buf: pointer to XDR buffer from which to decode data
   * @p: current pointer inside XDR buffer
   */
d8ed029d6   Alexey Dobriyan   [SUNRPC]: trivial...
621
  void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
623
  	xdr->buf = buf;
6650239a4   Trond Myklebust   NFS: Don't use vm...
624
625
626
627
628
629
  	xdr->scratch.iov_base = NULL;
  	xdr->scratch.iov_len = 0;
  	if (buf->head[0].iov_len != 0)
  		xdr_set_iov(xdr, buf->head, p, buf->len);
  	else if (buf->page_len != 0)
  		xdr_set_page_base(xdr, 0, buf->len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
630
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
631
  EXPORT_SYMBOL_GPL(xdr_init_decode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
632

f7da7a129   Benny Halevy   SUNRPC: introduce...
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
  /**
   * xdr_init_decode - Initialize an xdr_stream for decoding data.
   * @xdr: pointer to xdr_stream struct
   * @buf: pointer to XDR buffer from which to decode data
   * @pages: list of pages to decode into
   * @len: length in bytes of buffer in pages
   */
  void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf,
  			   struct page **pages, unsigned int len)
  {
  	memset(buf, 0, sizeof(*buf));
  	buf->pages =  pages;
  	buf->page_len =  len;
  	buf->buflen =  len;
  	buf->len = len;
  	xdr_init_decode(xdr, buf, NULL);
  }
  EXPORT_SYMBOL_GPL(xdr_init_decode_pages);
6650239a4   Trond Myklebust   NFS: Don't use vm...
651
  static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
ba8e452a4   Trond Myklebust   SUNRPC: Add a hel...
652
653
654
655
656
657
  {
  	__be32 *p = xdr->p;
  	__be32 *q = p + XDR_QUADLEN(nbytes);
  
  	if (unlikely(q > xdr->end || q < p))
  		return NULL;
6650239a4   Trond Myklebust   NFS: Don't use vm...
658
  	xdr->p = q;
ba8e452a4   Trond Myklebust   SUNRPC: Add a hel...
659
660
  	return p;
  }
ba8e452a4   Trond Myklebust   SUNRPC: Add a hel...
661
662
  
  /**
6650239a4   Trond Myklebust   NFS: Don't use vm...
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
   * xdr_set_scratch_buffer - Attach a scratch buffer for decoding data.
   * @xdr: pointer to xdr_stream struct
   * @buf: pointer to an empty buffer
   * @buflen: size of 'buf'
   *
   * The scratch buffer is used when decoding from an array of pages.
   * If an xdr_inline_decode() call spans across page boundaries, then
   * we copy the data into the scratch buffer in order to allow linear
   * access.
   */
  void xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen)
  {
  	xdr->scratch.iov_base = buf;
  	xdr->scratch.iov_len = buflen;
  }
  EXPORT_SYMBOL_GPL(xdr_set_scratch_buffer);
  
  static __be32 *xdr_copy_to_scratch(struct xdr_stream *xdr, size_t nbytes)
  {
  	__be32 *p;
  	void *cpdest = xdr->scratch.iov_base;
  	size_t cplen = (char *)xdr->end - (char *)xdr->p;
  
  	if (nbytes > xdr->scratch.iov_len)
  		return NULL;
  	memcpy(cpdest, xdr->p, cplen);
  	cpdest += cplen;
  	nbytes -= cplen;
  	if (!xdr_set_next_buffer(xdr))
  		return NULL;
  	p = __xdr_inline_decode(xdr, nbytes);
  	if (p == NULL)
  		return NULL;
  	memcpy(cpdest, p, nbytes);
  	return xdr->scratch.iov_base;
  }
  
  /**
   * xdr_inline_decode - Retrieve XDR data to decode
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
702
703
704
705
706
707
708
709
   * @xdr: pointer to xdr_stream struct
   * @nbytes: number of bytes of data to decode
   *
   * Check if the input buffer is long enough to enable us to decode
   * 'nbytes' more bytes of data starting at the current position.
   * If so return the current pointer, then update the current
   * pointer position.
   */
d8ed029d6   Alexey Dobriyan   [SUNRPC]: trivial...
710
  __be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
  {
6650239a4   Trond Myklebust   NFS: Don't use vm...
712
  	__be32 *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
713

6650239a4   Trond Myklebust   NFS: Don't use vm...
714
715
716
  	if (nbytes == 0)
  		return xdr->p;
  	if (xdr->p == xdr->end && !xdr_set_next_buffer(xdr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
  		return NULL;
6650239a4   Trond Myklebust   NFS: Don't use vm...
718
719
720
721
  	p = __xdr_inline_decode(xdr, nbytes);
  	if (p != NULL)
  		return p;
  	return xdr_copy_to_scratch(xdr, nbytes);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
722
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
723
  EXPORT_SYMBOL_GPL(xdr_inline_decode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
724
725
726
727
728
729
730
731
  
  /**
   * xdr_read_pages - Ensure page-based XDR data to decode is aligned at current pointer position
   * @xdr: pointer to xdr_stream struct
   * @len: number of bytes of page data
   *
   * Moves data beyond the current pointer position from the XDR head[] buffer
   * into the page list. Any data that lies beyond current position + "len"
8b23ea7be   Trond Myklebust   RPC: Allow struc ...
732
   * bytes is moved into the XDR tail[].
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
   */
  void xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
  {
  	struct xdr_buf *buf = xdr->buf;
  	struct kvec *iov;
  	ssize_t shift;
  	unsigned int end;
  	int padding;
  
  	/* Realign pages to current pointer position */
  	iov  = buf->head;
  	shift = iov->iov_len + (char *)iov->iov_base - (char *)xdr->p;
  	if (shift > 0)
  		xdr_shrink_bufhead(buf, shift);
  
  	/* Truncate page data and move it into the tail */
  	if (buf->page_len > len)
  		xdr_shrink_pagelen(buf, buf->page_len - len);
  	padding = (XDR_QUADLEN(len) << 2) - len;
  	xdr->iov = iov = buf->tail;
  	/* Compute remaining message length.  */
  	end = iov->iov_len;
  	shift = buf->buflen - buf->len;
  	if (shift < end)
  		end -= shift;
  	else if (shift > 0)
  		end = 0;
  	/*
  	 * Position current pointer at beginning of tail, and
  	 * set remaining message length.
  	 */
d8ed029d6   Alexey Dobriyan   [SUNRPC]: trivial...
764
765
  	xdr->p = (__be32 *)((char *)iov->iov_base + padding);
  	xdr->end = (__be32 *)((char *)iov->iov_base + end);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
766
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
767
  EXPORT_SYMBOL_GPL(xdr_read_pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
768

8b23ea7be   Trond Myklebust   RPC: Allow struc ...
769
770
771
772
773
774
775
776
777
778
779
780
  /**
   * xdr_enter_page - decode data from the XDR page
   * @xdr: pointer to xdr_stream struct
   * @len: number of bytes of page data
   *
   * Moves data beyond the current pointer position from the XDR head[] buffer
   * into the page list. Any data that lies beyond current position + "len"
   * bytes is moved into the XDR tail[]. The current pointer is then
   * repositioned at the beginning of the first XDR page.
   */
  void xdr_enter_page(struct xdr_stream *xdr, unsigned int len)
  {
8b23ea7be   Trond Myklebust   RPC: Allow struc ...
781
782
783
784
785
  	xdr_read_pages(xdr, len);
  	/*
  	 * Position current pointer at beginning of tail, and
  	 * set remaining message length.
  	 */
6650239a4   Trond Myklebust   NFS: Don't use vm...
786
  	xdr_set_page_base(xdr, 0, len);
8b23ea7be   Trond Myklebust   RPC: Allow struc ...
787
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
788
  EXPORT_SYMBOL_GPL(xdr_enter_page);
8b23ea7be   Trond Myklebust   RPC: Allow struc ...
789

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
790
791
792
793
794
795
796
797
798
799
  static struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0};
  
  void
  xdr_buf_from_iov(struct kvec *iov, struct xdr_buf *buf)
  {
  	buf->head[0] = *iov;
  	buf->tail[0] = empty_iov;
  	buf->page_len = 0;
  	buf->buflen = buf->len = iov->iov_len;
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
800
  EXPORT_SYMBOL_GPL(xdr_buf_from_iov);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
801

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
802
803
804
805
  /* Sets subbuf to the portion of buf of length len beginning base bytes
   * from the start of buf. Returns -1 if base of length are out of bounds. */
  int
  xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf,
1e78957e0   Trond Myklebust   SUNRPC: Clean up ...
806
  			unsigned int base, unsigned int len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
807
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
808
  	subbuf->buflen = subbuf->len = len;
1e78957e0   Trond Myklebust   SUNRPC: Clean up ...
809
810
811
812
813
814
815
816
817
818
819
  	if (base < buf->head[0].iov_len) {
  		subbuf->head[0].iov_base = buf->head[0].iov_base + base;
  		subbuf->head[0].iov_len = min_t(unsigned int, len,
  						buf->head[0].iov_len - base);
  		len -= subbuf->head[0].iov_len;
  		base = 0;
  	} else {
  		subbuf->head[0].iov_base = NULL;
  		subbuf->head[0].iov_len = 0;
  		base -= buf->head[0].iov_len;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
820
821
  
  	if (base < buf->page_len) {
1e78957e0   Trond Myklebust   SUNRPC: Clean up ...
822
823
824
825
  		subbuf->page_len = min(buf->page_len - base, len);
  		base += buf->page_base;
  		subbuf->page_base = base & ~PAGE_CACHE_MASK;
  		subbuf->pages = &buf->pages[base >> PAGE_CACHE_SHIFT];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
826
827
828
829
830
831
  		len -= subbuf->page_len;
  		base = 0;
  	} else {
  		base -= buf->page_len;
  		subbuf->page_len = 0;
  	}
1e78957e0   Trond Myklebust   SUNRPC: Clean up ...
832
833
834
835
836
837
838
839
840
841
842
  	if (base < buf->tail[0].iov_len) {
  		subbuf->tail[0].iov_base = buf->tail[0].iov_base + base;
  		subbuf->tail[0].iov_len = min_t(unsigned int, len,
  						buf->tail[0].iov_len - base);
  		len -= subbuf->tail[0].iov_len;
  		base = 0;
  	} else {
  		subbuf->tail[0].iov_base = NULL;
  		subbuf->tail[0].iov_len = 0;
  		base -= buf->tail[0].iov_len;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
843
844
845
846
  	if (base || len)
  		return -1;
  	return 0;
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
847
  EXPORT_SYMBOL_GPL(xdr_buf_subsegment);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
848

4e3e43ad1   Trond Myklebust   SUNRPC: Add __(re...
849
  static void __read_bytes_from_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
850
  {
1e78957e0   Trond Myklebust   SUNRPC: Clean up ...
851
  	unsigned int this_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
852

4e3e43ad1   Trond Myklebust   SUNRPC: Add __(re...
853
854
  	this_len = min_t(unsigned int, len, subbuf->head[0].iov_len);
  	memcpy(obj, subbuf->head[0].iov_base, this_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
855
856
  	len -= this_len;
  	obj += this_len;
4e3e43ad1   Trond Myklebust   SUNRPC: Add __(re...
857
  	this_len = min_t(unsigned int, len, subbuf->page_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
858
  	if (this_len)
4e3e43ad1   Trond Myklebust   SUNRPC: Add __(re...
859
  		_copy_from_pages(obj, subbuf->pages, subbuf->page_base, this_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
860
861
  	len -= this_len;
  	obj += this_len;
4e3e43ad1   Trond Myklebust   SUNRPC: Add __(re...
862
863
  	this_len = min_t(unsigned int, len, subbuf->tail[0].iov_len);
  	memcpy(obj, subbuf->tail[0].iov_base, this_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
864
  }
bd8100e7e   Andreas Gruenbacher   [PATCH] RPC: Enco...
865
  /* obj is assumed to point to allocated memory of size at least len: */
4e3e43ad1   Trond Myklebust   SUNRPC: Add __(re...
866
  int read_bytes_from_xdr_buf(struct xdr_buf *buf, unsigned int base, void *obj, unsigned int len)
bd8100e7e   Andreas Gruenbacher   [PATCH] RPC: Enco...
867
868
  {
  	struct xdr_buf subbuf;
bd8100e7e   Andreas Gruenbacher   [PATCH] RPC: Enco...
869
870
871
  	int status;
  
  	status = xdr_buf_subsegment(buf, &subbuf, base, len);
4e3e43ad1   Trond Myklebust   SUNRPC: Add __(re...
872
873
874
875
876
  	if (status != 0)
  		return status;
  	__read_bytes_from_xdr_buf(&subbuf, obj, len);
  	return 0;
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
877
  EXPORT_SYMBOL_GPL(read_bytes_from_xdr_buf);
4e3e43ad1   Trond Myklebust   SUNRPC: Add __(re...
878
879
880
881
882
883
884
  
  static void __write_bytes_to_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len)
  {
  	unsigned int this_len;
  
  	this_len = min_t(unsigned int, len, subbuf->head[0].iov_len);
  	memcpy(subbuf->head[0].iov_base, obj, this_len);
bd8100e7e   Andreas Gruenbacher   [PATCH] RPC: Enco...
885
886
  	len -= this_len;
  	obj += this_len;
4e3e43ad1   Trond Myklebust   SUNRPC: Add __(re...
887
  	this_len = min_t(unsigned int, len, subbuf->page_len);
bd8100e7e   Andreas Gruenbacher   [PATCH] RPC: Enco...
888
  	if (this_len)
4e3e43ad1   Trond Myklebust   SUNRPC: Add __(re...
889
  		_copy_to_pages(subbuf->pages, subbuf->page_base, obj, this_len);
bd8100e7e   Andreas Gruenbacher   [PATCH] RPC: Enco...
890
891
  	len -= this_len;
  	obj += this_len;
4e3e43ad1   Trond Myklebust   SUNRPC: Add __(re...
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
  	this_len = min_t(unsigned int, len, subbuf->tail[0].iov_len);
  	memcpy(subbuf->tail[0].iov_base, obj, this_len);
  }
  
  /* obj is assumed to point to allocated memory of size at least len: */
  int write_bytes_to_xdr_buf(struct xdr_buf *buf, unsigned int base, void *obj, unsigned int len)
  {
  	struct xdr_buf subbuf;
  	int status;
  
  	status = xdr_buf_subsegment(buf, &subbuf, base, len);
  	if (status != 0)
  		return status;
  	__write_bytes_to_xdr_buf(&subbuf, obj, len);
  	return 0;
bd8100e7e   Andreas Gruenbacher   [PATCH] RPC: Enco...
907
  }
c43abaeda   Kevin Coffman   xdr: Add an expor...
908
  EXPORT_SYMBOL_GPL(write_bytes_to_xdr_buf);
bd8100e7e   Andreas Gruenbacher   [PATCH] RPC: Enco...
909
910
  
  int
1e78957e0   Trond Myklebust   SUNRPC: Clean up ...
911
  xdr_decode_word(struct xdr_buf *buf, unsigned int base, u32 *obj)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
912
  {
d8ed029d6   Alexey Dobriyan   [SUNRPC]: trivial...
913
  	__be32	raw;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
914
915
916
917
918
  	int	status;
  
  	status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj));
  	if (status)
  		return status;
98866b5ab   Benny Halevy   sunrpc: ntoh -> b...
919
  	*obj = be32_to_cpu(raw);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
920
921
  	return 0;
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
922
  EXPORT_SYMBOL_GPL(xdr_decode_word);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
923

bd8100e7e   Andreas Gruenbacher   [PATCH] RPC: Enco...
924
  int
1e78957e0   Trond Myklebust   SUNRPC: Clean up ...
925
  xdr_encode_word(struct xdr_buf *buf, unsigned int base, u32 obj)
bd8100e7e   Andreas Gruenbacher   [PATCH] RPC: Enco...
926
  {
9f162d2a8   Benny Halevy   sunrpc: hton -> c...
927
  	__be32	raw = cpu_to_be32(obj);
bd8100e7e   Andreas Gruenbacher   [PATCH] RPC: Enco...
928
929
930
  
  	return write_bytes_to_xdr_buf(buf, base, &raw, sizeof(obj));
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
931
  EXPORT_SYMBOL_GPL(xdr_encode_word);
bd8100e7e   Andreas Gruenbacher   [PATCH] RPC: Enco...
932

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
933
934
935
936
  /* If the netobj starting offset bytes from the start of xdr_buf is contained
   * entirely in the head or the tail, set object to point to it; otherwise
   * try to find space for it at the end of the tail, copy it there, and
   * set obj to point to it. */
bee57c99c   Trond Myklebust   SUNRPC: Ensure xd...
937
  int xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, unsigned int offset)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
938
  {
bee57c99c   Trond Myklebust   SUNRPC: Ensure xd...
939
  	struct xdr_buf subbuf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
940

bd8100e7e   Andreas Gruenbacher   [PATCH] RPC: Enco...
941
  	if (xdr_decode_word(buf, offset, &obj->len))
bee57c99c   Trond Myklebust   SUNRPC: Ensure xd...
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
  		return -EFAULT;
  	if (xdr_buf_subsegment(buf, &subbuf, offset + 4, obj->len))
  		return -EFAULT;
  
  	/* Is the obj contained entirely in the head? */
  	obj->data = subbuf.head[0].iov_base;
  	if (subbuf.head[0].iov_len == obj->len)
  		return 0;
  	/* ..or is the obj contained entirely in the tail? */
  	obj->data = subbuf.tail[0].iov_base;
  	if (subbuf.tail[0].iov_len == obj->len)
  		return 0;
  
  	/* use end of tail as storage for obj:
  	 * (We don't copy to the beginning because then we'd have
  	 * to worry about doing a potentially overlapping copy.
  	 * This assumes the object is at most half the length of the
  	 * tail.) */
  	if (obj->len > buf->buflen - buf->len)
  		return -ENOMEM;
  	if (buf->tail[0].iov_len != 0)
  		obj->data = buf->tail[0].iov_base + buf->tail[0].iov_len;
  	else
  		obj->data = buf->head[0].iov_base + buf->head[0].iov_len;
  	__read_bytes_from_xdr_buf(&subbuf, obj->data, obj->len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
967
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
968
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
969
  EXPORT_SYMBOL_GPL(xdr_buf_read_netobj);
bd8100e7e   Andreas Gruenbacher   [PATCH] RPC: Enco...
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
  
  /* Returns 0 on success, or else a negative error code. */
  static int
  xdr_xcode_array2(struct xdr_buf *buf, unsigned int base,
  		 struct xdr_array2_desc *desc, int encode)
  {
  	char *elem = NULL, *c;
  	unsigned int copied = 0, todo, avail_here;
  	struct page **ppages = NULL;
  	int err;
  
  	if (encode) {
  		if (xdr_encode_word(buf, base, desc->array_len) != 0)
  			return -EINVAL;
  	} else {
  		if (xdr_decode_word(buf, base, &desc->array_len) != 0 ||
58fcb8df0   Trond Myklebust   [PATCH] NFS: Ensu...
986
  		    desc->array_len > desc->array_maxlen ||
bd8100e7e   Andreas Gruenbacher   [PATCH] RPC: Enco...
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
  		    (unsigned long) base + 4 + desc->array_len *
  				    desc->elem_size > buf->len)
  			return -EINVAL;
  	}
  	base += 4;
  
  	if (!desc->xcode)
  		return 0;
  
  	todo = desc->array_len * desc->elem_size;
  
  	/* process head */
  	if (todo && base < buf->head->iov_len) {
  		c = buf->head->iov_base + base;
  		avail_here = min_t(unsigned int, todo,
  				   buf->head->iov_len - base);
  		todo -= avail_here;
  
  		while (avail_here >= desc->elem_size) {
  			err = desc->xcode(desc, c);
  			if (err)
  				goto out;
  			c += desc->elem_size;
  			avail_here -= desc->elem_size;
  		}
  		if (avail_here) {
  			if (!elem) {
  				elem = kmalloc(desc->elem_size, GFP_KERNEL);
  				err = -ENOMEM;
  				if (!elem)
  					goto out;
  			}
  			if (encode) {
  				err = desc->xcode(desc, elem);
  				if (err)
  					goto out;
  				memcpy(c, elem, avail_here);
  			} else
  				memcpy(elem, c, avail_here);
  			copied = avail_here;
  		}
  		base = buf->head->iov_len;  /* align to start of pages */
  	}
  
  	/* process pages array */
  	base -= buf->head->iov_len;
  	if (todo && base < buf->page_len) {
  		unsigned int avail_page;
  
  		avail_here = min(todo, buf->page_len - base);
  		todo -= avail_here;
  
  		base += buf->page_base;
  		ppages = buf->pages + (base >> PAGE_CACHE_SHIFT);
  		base &= ~PAGE_CACHE_MASK;
  		avail_page = min_t(unsigned int, PAGE_CACHE_SIZE - base,
  					avail_here);
  		c = kmap(*ppages) + base;
  
  		while (avail_here) {
  			avail_here -= avail_page;
  			if (copied || avail_page < desc->elem_size) {
  				unsigned int l = min(avail_page,
  					desc->elem_size - copied);
  				if (!elem) {
  					elem = kmalloc(desc->elem_size,
  						       GFP_KERNEL);
  					err = -ENOMEM;
  					if (!elem)
  						goto out;
  				}
  				if (encode) {
  					if (!copied) {
  						err = desc->xcode(desc, elem);
  						if (err)
  							goto out;
  					}
  					memcpy(c, elem + copied, l);
  					copied += l;
  					if (copied == desc->elem_size)
  						copied = 0;
  				} else {
  					memcpy(elem + copied, c, l);
  					copied += l;
  					if (copied == desc->elem_size) {
  						err = desc->xcode(desc, elem);
  						if (err)
  							goto out;
  						copied = 0;
  					}
  				}
  				avail_page -= l;
  				c += l;
  			}
  			while (avail_page >= desc->elem_size) {
  				err = desc->xcode(desc, c);
  				if (err)
  					goto out;
  				c += desc->elem_size;
  				avail_page -= desc->elem_size;
  			}
  			if (avail_page) {
  				unsigned int l = min(avail_page,
  					    desc->elem_size - copied);
  				if (!elem) {
  					elem = kmalloc(desc->elem_size,
  						       GFP_KERNEL);
  					err = -ENOMEM;
  					if (!elem)
  						goto out;
  				}
  				if (encode) {
  					if (!copied) {
  						err = desc->xcode(desc, elem);
  						if (err)
  							goto out;
  					}
  					memcpy(c, elem + copied, l);
  					copied += l;
  					if (copied == desc->elem_size)
  						copied = 0;
  				} else {
  					memcpy(elem + copied, c, l);
  					copied += l;
  					if (copied == desc->elem_size) {
  						err = desc->xcode(desc, elem);
  						if (err)
  							goto out;
  						copied = 0;
  					}
  				}
  			}
  			if (avail_here) {
  				kunmap(*ppages);
  				ppages++;
  				c = kmap(*ppages);
  			}
  
  			avail_page = min(avail_here,
  				 (unsigned int) PAGE_CACHE_SIZE);
  		}
  		base = buf->page_len;  /* align to start of tail */
  	}
  
  	/* process tail */
  	base -= buf->page_len;
  	if (todo) {
  		c = buf->tail->iov_base + base;
  		if (copied) {
  			unsigned int l = desc->elem_size - copied;
  
  			if (encode)
  				memcpy(c, elem + copied, l);
  			else {
  				memcpy(elem + copied, c, l);
  				err = desc->xcode(desc, elem);
  				if (err)
  					goto out;
  			}
  			todo -= l;
  			c += l;
  		}
  		while (todo) {
  			err = desc->xcode(desc, c);
  			if (err)
  				goto out;
  			c += desc->elem_size;
  			todo -= desc->elem_size;
  		}
  	}
  	err = 0;
  
  out:
a51482bde   Jesper Juhl   [NET]: kfree cleanup
1160
  	kfree(elem);
bd8100e7e   Andreas Gruenbacher   [PATCH] RPC: Enco...
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
  	if (ppages)
  		kunmap(*ppages);
  	return err;
  }
  
  int
  xdr_decode_array2(struct xdr_buf *buf, unsigned int base,
  		  struct xdr_array2_desc *desc)
  {
  	if (base >= buf->len)
  		return -EINVAL;
  
  	return xdr_xcode_array2(buf, base, desc, 0);
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
1175
  EXPORT_SYMBOL_GPL(xdr_decode_array2);
bd8100e7e   Andreas Gruenbacher   [PATCH] RPC: Enco...
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
  
  int
  xdr_encode_array2(struct xdr_buf *buf, unsigned int base,
  		  struct xdr_array2_desc *desc)
  {
  	if ((unsigned long) base + 4 + desc->array_len * desc->elem_size >
  	    buf->head->iov_len + buf->page_len + buf->tail->iov_len)
  		return -EINVAL;
  
  	return xdr_xcode_array2(buf, base, desc, 1);
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
1187
  EXPORT_SYMBOL_GPL(xdr_encode_array2);
37a4e6cb0   Olga Kornievskaia   rpc: move process...
1188
1189
1190
  
  int
  xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len,
cca5172a7   YOSHIFUJI Hideaki   [NET] SUNRPC: Fix...
1191
  		int (*actor)(struct scatterlist *, void *), void *data)
37a4e6cb0   Olga Kornievskaia   rpc: move process...
1192
1193
1194
1195
  {
  	int i, ret = 0;
  	unsigned page_len, thislen, page_offset;
  	struct scatterlist      sg[1];
68e3f5dd4   Herbert Xu   [CRYPTO] users: F...
1196
  	sg_init_table(sg, 1);
37a4e6cb0   Olga Kornievskaia   rpc: move process...
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
  	if (offset >= buf->head[0].iov_len) {
  		offset -= buf->head[0].iov_len;
  	} else {
  		thislen = buf->head[0].iov_len - offset;
  		if (thislen > len)
  			thislen = len;
  		sg_set_buf(sg, buf->head[0].iov_base + offset, thislen);
  		ret = actor(sg, data);
  		if (ret)
  			goto out;
  		offset = 0;
  		len -= thislen;
  	}
  	if (len == 0)
  		goto out;
  
  	if (offset >= buf->page_len) {
  		offset -= buf->page_len;
  	} else {
  		page_len = buf->page_len - offset;
  		if (page_len > len)
  			page_len = len;
  		len -= page_len;
  		page_offset = (offset + buf->page_base) & (PAGE_CACHE_SIZE - 1);
  		i = (offset + buf->page_base) >> PAGE_CACHE_SHIFT;
  		thislen = PAGE_CACHE_SIZE - page_offset;
  		do {
  			if (thislen > page_len)
  				thislen = page_len;
642f14903   Jens Axboe   SG: Change sg_set...
1226
  			sg_set_page(sg, buf->pages[i], thislen, page_offset);
37a4e6cb0   Olga Kornievskaia   rpc: move process...
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
  			ret = actor(sg, data);
  			if (ret)
  				goto out;
  			page_len -= thislen;
  			i++;
  			page_offset = 0;
  			thislen = PAGE_CACHE_SIZE;
  		} while (page_len != 0);
  		offset = 0;
  	}
  	if (len == 0)
  		goto out;
  	if (offset < buf->tail[0].iov_len) {
  		thislen = buf->tail[0].iov_len - offset;
  		if (thislen > len)
  			thislen = len;
  		sg_set_buf(sg, buf->tail[0].iov_base + offset, thislen);
  		ret = actor(sg, data);
  		len -= thislen;
  	}
  	if (len != 0)
  		ret = -EINVAL;
  out:
  	return ret;
  }
468039ee4   Trond Myklebust   SUNRPC: Convert t...
1252
  EXPORT_SYMBOL_GPL(xdr_process_buf);
37a4e6cb0   Olga Kornievskaia   rpc: move process...
1253