xref: /openbmc/linux/net/sunrpc/xdr.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * linux/net/sunrpc/xdr.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Generic XDR support.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
10a246b010SChuck Lever #include <linux/module.h>
115a0e3ad6STejun Heo #include <linux/slab.h>
121da177e4SLinus Torvalds #include <linux/types.h>
131da177e4SLinus Torvalds #include <linux/string.h>
141da177e4SLinus Torvalds #include <linux/kernel.h>
151da177e4SLinus Torvalds #include <linux/pagemap.h>
161da177e4SLinus Torvalds #include <linux/errno.h>
171da177e4SLinus Torvalds #include <linux/sunrpc/xdr.h>
181da177e4SLinus Torvalds #include <linux/sunrpc/msg_prot.h>
199d96acbcSTrond Myklebust #include <linux/bvec.h>
205582863fSChuck Lever #include <trace/events/sunrpc.h>
211da177e4SLinus Torvalds 
22e6ac0accSAnna Schumaker static void _copy_to_pages(struct page **, size_t, const char *, size_t);
23e6ac0accSAnna Schumaker 
24e6ac0accSAnna Schumaker 
251da177e4SLinus Torvalds /*
261da177e4SLinus Torvalds  * XDR functions for basic NFS types
271da177e4SLinus Torvalds  */
28d8ed029dSAlexey Dobriyan __be32 *
xdr_encode_netobj(__be32 * p,const struct xdr_netobj * obj)29d8ed029dSAlexey Dobriyan xdr_encode_netobj(__be32 *p, const struct xdr_netobj *obj)
301da177e4SLinus Torvalds {
311da177e4SLinus Torvalds 	unsigned int	quadlen = XDR_QUADLEN(obj->len);
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds 	p[quadlen] = 0;		/* zero trailing bytes */
349f162d2aSBenny Halevy 	*p++ = cpu_to_be32(obj->len);
351da177e4SLinus Torvalds 	memcpy(p, obj->data, obj->len);
361da177e4SLinus Torvalds 	return p + XDR_QUADLEN(obj->len);
371da177e4SLinus Torvalds }
38468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_encode_netobj);
391da177e4SLinus Torvalds 
40d8ed029dSAlexey Dobriyan __be32 *
xdr_decode_netobj(__be32 * p,struct xdr_netobj * obj)41d8ed029dSAlexey Dobriyan xdr_decode_netobj(__be32 *p, struct xdr_netobj *obj)
421da177e4SLinus Torvalds {
431da177e4SLinus Torvalds 	unsigned int	len;
441da177e4SLinus Torvalds 
4598866b5aSBenny Halevy 	if ((len = be32_to_cpu(*p++)) > XDR_MAX_NETOBJ)
461da177e4SLinus Torvalds 		return NULL;
471da177e4SLinus Torvalds 	obj->len  = len;
481da177e4SLinus Torvalds 	obj->data = (u8 *) p;
491da177e4SLinus Torvalds 	return p + XDR_QUADLEN(len);
501da177e4SLinus Torvalds }
51468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_decode_netobj);
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds /**
541da177e4SLinus Torvalds  * xdr_encode_opaque_fixed - Encode fixed length opaque data
554dc3b16bSPavel Pisa  * @p: pointer to current position in XDR buffer.
564dc3b16bSPavel Pisa  * @ptr: pointer to data to encode (or NULL)
574dc3b16bSPavel Pisa  * @nbytes: size of data.
581da177e4SLinus Torvalds  *
591da177e4SLinus Torvalds  * Copy the array of data of length nbytes at ptr to the XDR buffer
601da177e4SLinus Torvalds  * at position p, then align to the next 32-bit boundary by padding
611da177e4SLinus Torvalds  * with zero bytes (see RFC1832).
621da177e4SLinus Torvalds  * Note: if ptr is NULL, only the padding is performed.
631da177e4SLinus Torvalds  *
641da177e4SLinus Torvalds  * Returns the updated current XDR buffer position
651da177e4SLinus Torvalds  *
661da177e4SLinus Torvalds  */
xdr_encode_opaque_fixed(__be32 * p,const void * ptr,unsigned int nbytes)67d8ed029dSAlexey Dobriyan __be32 *xdr_encode_opaque_fixed(__be32 *p, const void *ptr, unsigned int nbytes)
681da177e4SLinus Torvalds {
691da177e4SLinus Torvalds 	if (likely(nbytes != 0)) {
701da177e4SLinus Torvalds 		unsigned int quadlen = XDR_QUADLEN(nbytes);
711da177e4SLinus Torvalds 		unsigned int padding = (quadlen << 2) - nbytes;
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds 		if (ptr != NULL)
741da177e4SLinus Torvalds 			memcpy(p, ptr, nbytes);
751da177e4SLinus Torvalds 		if (padding != 0)
761da177e4SLinus Torvalds 			memset((char *)p + nbytes, 0, padding);
771da177e4SLinus Torvalds 		p += quadlen;
781da177e4SLinus Torvalds 	}
791da177e4SLinus Torvalds 	return p;
801da177e4SLinus Torvalds }
81468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_encode_opaque_fixed);
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds /**
841da177e4SLinus Torvalds  * xdr_encode_opaque - Encode variable length opaque data
854dc3b16bSPavel Pisa  * @p: pointer to current position in XDR buffer.
864dc3b16bSPavel Pisa  * @ptr: pointer to data to encode (or NULL)
874dc3b16bSPavel Pisa  * @nbytes: size of data.
881da177e4SLinus Torvalds  *
891da177e4SLinus Torvalds  * Returns the updated current XDR buffer position
901da177e4SLinus Torvalds  */
xdr_encode_opaque(__be32 * p,const void * ptr,unsigned int nbytes)91d8ed029dSAlexey Dobriyan __be32 *xdr_encode_opaque(__be32 *p, const void *ptr, unsigned int nbytes)
921da177e4SLinus Torvalds {
939f162d2aSBenny Halevy 	*p++ = cpu_to_be32(nbytes);
941da177e4SLinus Torvalds 	return xdr_encode_opaque_fixed(p, ptr, nbytes);
951da177e4SLinus Torvalds }
96468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_encode_opaque);
971da177e4SLinus Torvalds 
98d8ed029dSAlexey Dobriyan __be32 *
xdr_encode_string(__be32 * p,const char * string)99d8ed029dSAlexey Dobriyan xdr_encode_string(__be32 *p, const char *string)
1001da177e4SLinus Torvalds {
1011da177e4SLinus Torvalds 	return xdr_encode_array(p, string, strlen(string));
1021da177e4SLinus Torvalds }
103468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_encode_string);
1041da177e4SLinus Torvalds 
105d8ed029dSAlexey Dobriyan __be32 *
xdr_decode_string_inplace(__be32 * p,char ** sp,unsigned int * lenp,unsigned int maxlen)106e5cff482SChuck Lever xdr_decode_string_inplace(__be32 *p, char **sp,
107e5cff482SChuck Lever 			  unsigned int *lenp, unsigned int maxlen)
1081da177e4SLinus Torvalds {
109e5cff482SChuck Lever 	u32 len;
1101da177e4SLinus Torvalds 
11198866b5aSBenny Halevy 	len = be32_to_cpu(*p++);
112e5cff482SChuck Lever 	if (len > maxlen)
1131da177e4SLinus Torvalds 		return NULL;
1141da177e4SLinus Torvalds 	*lenp = len;
1151da177e4SLinus Torvalds 	*sp = (char *) p;
1161da177e4SLinus Torvalds 	return p + XDR_QUADLEN(len);
1171da177e4SLinus Torvalds }
118468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_decode_string_inplace);
1191da177e4SLinus Torvalds 
120b4687da7SChuck Lever /**
121b4687da7SChuck Lever  * xdr_terminate_string - '\0'-terminate a string residing in an xdr_buf
122b4687da7SChuck Lever  * @buf: XDR buffer where string resides
123b4687da7SChuck Lever  * @len: length of string, in bytes
124b4687da7SChuck Lever  *
125b4687da7SChuck Lever  */
xdr_terminate_string(const struct xdr_buf * buf,const u32 len)126f8d0e60fSTrond Myklebust void xdr_terminate_string(const struct xdr_buf *buf, const u32 len)
127b4687da7SChuck Lever {
128b4687da7SChuck Lever 	char *kaddr;
129b4687da7SChuck Lever 
130b8541786SCong Wang 	kaddr = kmap_atomic(buf->pages[0]);
131b4687da7SChuck Lever 	kaddr[buf->page_base + len] = '\0';
132b8541786SCong Wang 	kunmap_atomic(kaddr);
133b4687da7SChuck Lever }
1340d961aa9STrond Myklebust EXPORT_SYMBOL_GPL(xdr_terminate_string);
135b4687da7SChuck Lever 
xdr_buf_pagecount(const struct xdr_buf * buf)136f8d0e60fSTrond Myklebust size_t xdr_buf_pagecount(const struct xdr_buf *buf)
1379d96acbcSTrond Myklebust {
1389d96acbcSTrond Myklebust 	if (!buf->page_len)
1399d96acbcSTrond Myklebust 		return 0;
1409d96acbcSTrond Myklebust 	return (buf->page_base + buf->page_len + PAGE_SIZE - 1) >> PAGE_SHIFT;
1419d96acbcSTrond Myklebust }
1429d96acbcSTrond Myklebust 
1439d96acbcSTrond Myklebust int
xdr_alloc_bvec(struct xdr_buf * buf,gfp_t gfp)1449d96acbcSTrond Myklebust xdr_alloc_bvec(struct xdr_buf *buf, gfp_t gfp)
1459d96acbcSTrond Myklebust {
1469d96acbcSTrond Myklebust 	size_t i, n = xdr_buf_pagecount(buf);
1479d96acbcSTrond Myklebust 
1489d96acbcSTrond Myklebust 	if (n != 0 && buf->bvec == NULL) {
1499d96acbcSTrond Myklebust 		buf->bvec = kmalloc_array(n, sizeof(buf->bvec[0]), gfp);
1509d96acbcSTrond Myklebust 		if (!buf->bvec)
1519d96acbcSTrond Myklebust 			return -ENOMEM;
1529d96acbcSTrond Myklebust 		for (i = 0; i < n; i++) {
1539088151fSChristoph Hellwig 			bvec_set_page(&buf->bvec[i], buf->pages[i], PAGE_SIZE,
1549088151fSChristoph Hellwig 				      0);
1559d96acbcSTrond Myklebust 		}
1569d96acbcSTrond Myklebust 	}
1579d96acbcSTrond Myklebust 	return 0;
1589d96acbcSTrond Myklebust }
1599d96acbcSTrond Myklebust 
1609d96acbcSTrond Myklebust void
xdr_free_bvec(struct xdr_buf * buf)1619d96acbcSTrond Myklebust xdr_free_bvec(struct xdr_buf *buf)
1629d96acbcSTrond Myklebust {
1639d96acbcSTrond Myklebust 	kfree(buf->bvec);
1649d96acbcSTrond Myklebust 	buf->bvec = NULL;
1659d96acbcSTrond Myklebust }
1669d96acbcSTrond Myklebust 
167cf500bacSChuck Lever /**
168*2eb2b935SChuck Lever  * xdr_buf_to_bvec - Copy components of an xdr_buf into a bio_vec array
169*2eb2b935SChuck Lever  * @bvec: bio_vec array to populate
170*2eb2b935SChuck Lever  * @bvec_size: element count of @bio_vec
171*2eb2b935SChuck Lever  * @xdr: xdr_buf to be copied
172*2eb2b935SChuck Lever  *
173*2eb2b935SChuck Lever  * Returns the number of entries consumed in @bvec.
174*2eb2b935SChuck Lever  */
xdr_buf_to_bvec(struct bio_vec * bvec,unsigned int bvec_size,const struct xdr_buf * xdr)175*2eb2b935SChuck Lever unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
176*2eb2b935SChuck Lever 			     const struct xdr_buf *xdr)
177*2eb2b935SChuck Lever {
178*2eb2b935SChuck Lever 	const struct kvec *head = xdr->head;
179*2eb2b935SChuck Lever 	const struct kvec *tail = xdr->tail;
180*2eb2b935SChuck Lever 	unsigned int count = 0;
181*2eb2b935SChuck Lever 
182*2eb2b935SChuck Lever 	if (head->iov_len) {
183*2eb2b935SChuck Lever 		bvec_set_virt(bvec++, head->iov_base, head->iov_len);
184*2eb2b935SChuck Lever 		++count;
185*2eb2b935SChuck Lever 	}
186*2eb2b935SChuck Lever 
187*2eb2b935SChuck Lever 	if (xdr->page_len) {
188*2eb2b935SChuck Lever 		unsigned int offset, len, remaining;
189*2eb2b935SChuck Lever 		struct page **pages = xdr->pages;
190*2eb2b935SChuck Lever 
191*2eb2b935SChuck Lever 		offset = offset_in_page(xdr->page_base);
192*2eb2b935SChuck Lever 		remaining = xdr->page_len;
193*2eb2b935SChuck Lever 		while (remaining > 0) {
194*2eb2b935SChuck Lever 			len = min_t(unsigned int, remaining,
195*2eb2b935SChuck Lever 				    PAGE_SIZE - offset);
196*2eb2b935SChuck Lever 			bvec_set_page(bvec++, *pages++, len, offset);
197*2eb2b935SChuck Lever 			remaining -= len;
198*2eb2b935SChuck Lever 			offset = 0;
199*2eb2b935SChuck Lever 			if (unlikely(++count > bvec_size))
200*2eb2b935SChuck Lever 				goto bvec_overflow;
201*2eb2b935SChuck Lever 		}
202*2eb2b935SChuck Lever 	}
203*2eb2b935SChuck Lever 
204*2eb2b935SChuck Lever 	if (tail->iov_len) {
205*2eb2b935SChuck Lever 		bvec_set_virt(bvec, tail->iov_base, tail->iov_len);
206*2eb2b935SChuck Lever 		if (unlikely(++count > bvec_size))
207*2eb2b935SChuck Lever 			goto bvec_overflow;
208*2eb2b935SChuck Lever 	}
209*2eb2b935SChuck Lever 
210*2eb2b935SChuck Lever 	return count;
211*2eb2b935SChuck Lever 
212*2eb2b935SChuck Lever bvec_overflow:
213*2eb2b935SChuck Lever 	pr_warn_once("%s: bio_vec array overflow\n", __func__);
214*2eb2b935SChuck Lever 	return count - 1;
215*2eb2b935SChuck Lever }
216*2eb2b935SChuck Lever 
217*2eb2b935SChuck Lever /**
218cf500bacSChuck Lever  * xdr_inline_pages - Prepare receive buffer for a large reply
219cf500bacSChuck Lever  * @xdr: xdr_buf into which reply will be placed
220cf500bacSChuck Lever  * @offset: expected offset where data payload will start, in bytes
221cf500bacSChuck Lever  * @pages: vector of struct page pointers
222cf500bacSChuck Lever  * @base: offset in first page where receive should start, in bytes
223cf500bacSChuck Lever  * @len: expected size of the upper layer data payload, in bytes
224cf500bacSChuck Lever  *
225cf500bacSChuck Lever  */
2261da177e4SLinus Torvalds void
xdr_inline_pages(struct xdr_buf * xdr,unsigned int offset,struct page ** pages,unsigned int base,unsigned int len)2271da177e4SLinus Torvalds xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
2281da177e4SLinus Torvalds 		 struct page **pages, unsigned int base, unsigned int len)
2291da177e4SLinus Torvalds {
2301da177e4SLinus Torvalds 	struct kvec *head = xdr->head;
2311da177e4SLinus Torvalds 	struct kvec *tail = xdr->tail;
2321da177e4SLinus Torvalds 	char *buf = (char *)head->iov_base;
2331da177e4SLinus Torvalds 	unsigned int buflen = head->iov_len;
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds 	head->iov_len  = offset;
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds 	xdr->pages = pages;
2381da177e4SLinus Torvalds 	xdr->page_base = base;
2391da177e4SLinus Torvalds 	xdr->page_len = len;
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds 	tail->iov_base = buf + offset;
2421da177e4SLinus Torvalds 	tail->iov_len = buflen - offset;
2431da177e4SLinus Torvalds 	xdr->buflen += len;
2441da177e4SLinus Torvalds }
245468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_inline_pages);
2461da177e4SLinus Torvalds 
2471da177e4SLinus Torvalds /*
2481da177e4SLinus Torvalds  * Helper routines for doing 'memmove' like operations on a struct xdr_buf
2492c53040fSBen Hutchings  */
2502c53040fSBen Hutchings 
2512c53040fSBen Hutchings /**
252e6ac0accSAnna Schumaker  * _shift_data_left_pages
253e6ac0accSAnna Schumaker  * @pages: vector of pages containing both the source and dest memory area.
254e6ac0accSAnna Schumaker  * @pgto_base: page vector address of destination
255e6ac0accSAnna Schumaker  * @pgfrom_base: page vector address of source
256e6ac0accSAnna Schumaker  * @len: number of bytes to copy
257e6ac0accSAnna Schumaker  *
258e6ac0accSAnna Schumaker  * Note: the addresses pgto_base and pgfrom_base are both calculated in
259e6ac0accSAnna Schumaker  *       the same way:
260e6ac0accSAnna Schumaker  *            if a memory area starts at byte 'base' in page 'pages[i]',
261e6ac0accSAnna Schumaker  *            then its address is given as (i << PAGE_CACHE_SHIFT) + base
262e6ac0accSAnna Schumaker  * Alse note: pgto_base must be < pgfrom_base, but the memory areas
263e6ac0accSAnna Schumaker  * 	they point to may overlap.
264e6ac0accSAnna Schumaker  */
265e6ac0accSAnna Schumaker static void
_shift_data_left_pages(struct page ** pages,size_t pgto_base,size_t pgfrom_base,size_t len)266e6ac0accSAnna Schumaker _shift_data_left_pages(struct page **pages, size_t pgto_base,
267e6ac0accSAnna Schumaker 			size_t pgfrom_base, size_t len)
268e6ac0accSAnna Schumaker {
269e6ac0accSAnna Schumaker 	struct page **pgfrom, **pgto;
270e6ac0accSAnna Schumaker 	char *vfrom, *vto;
271e6ac0accSAnna Schumaker 	size_t copy;
272e6ac0accSAnna Schumaker 
273e6ac0accSAnna Schumaker 	BUG_ON(pgfrom_base <= pgto_base);
274e6ac0accSAnna Schumaker 
275c54e959bSTrond Myklebust 	if (!len)
276c54e959bSTrond Myklebust 		return;
277c54e959bSTrond Myklebust 
278e6ac0accSAnna Schumaker 	pgto = pages + (pgto_base >> PAGE_SHIFT);
279e6ac0accSAnna Schumaker 	pgfrom = pages + (pgfrom_base >> PAGE_SHIFT);
280e6ac0accSAnna Schumaker 
281e6ac0accSAnna Schumaker 	pgto_base &= ~PAGE_MASK;
282e6ac0accSAnna Schumaker 	pgfrom_base &= ~PAGE_MASK;
283e6ac0accSAnna Schumaker 
284e6ac0accSAnna Schumaker 	do {
285e6ac0accSAnna Schumaker 		if (pgto_base >= PAGE_SIZE) {
286e6ac0accSAnna Schumaker 			pgto_base = 0;
287e6ac0accSAnna Schumaker 			pgto++;
288e6ac0accSAnna Schumaker 		}
289e6ac0accSAnna Schumaker 		if (pgfrom_base >= PAGE_SIZE){
290e6ac0accSAnna Schumaker 			pgfrom_base = 0;
291e6ac0accSAnna Schumaker 			pgfrom++;
292e6ac0accSAnna Schumaker 		}
293e6ac0accSAnna Schumaker 
294e6ac0accSAnna Schumaker 		copy = len;
295e6ac0accSAnna Schumaker 		if (copy > (PAGE_SIZE - pgto_base))
296e6ac0accSAnna Schumaker 			copy = PAGE_SIZE - pgto_base;
297e6ac0accSAnna Schumaker 		if (copy > (PAGE_SIZE - pgfrom_base))
298e6ac0accSAnna Schumaker 			copy = PAGE_SIZE - pgfrom_base;
299e6ac0accSAnna Schumaker 
300e6ac0accSAnna Schumaker 		vto = kmap_atomic(*pgto);
301e6ac0accSAnna Schumaker 		if (*pgto != *pgfrom) {
302e6ac0accSAnna Schumaker 			vfrom = kmap_atomic(*pgfrom);
303e6ac0accSAnna Schumaker 			memcpy(vto + pgto_base, vfrom + pgfrom_base, copy);
304e6ac0accSAnna Schumaker 			kunmap_atomic(vfrom);
305e6ac0accSAnna Schumaker 		} else
306e6ac0accSAnna Schumaker 			memmove(vto + pgto_base, vto + pgfrom_base, copy);
307e6ac0accSAnna Schumaker 		flush_dcache_page(*pgto);
308e6ac0accSAnna Schumaker 		kunmap_atomic(vto);
309e6ac0accSAnna Schumaker 
310e6ac0accSAnna Schumaker 		pgto_base += copy;
311e6ac0accSAnna Schumaker 		pgfrom_base += copy;
312e6ac0accSAnna Schumaker 
313e6ac0accSAnna Schumaker 	} while ((len -= copy) != 0);
314e6ac0accSAnna Schumaker }
315e6ac0accSAnna Schumaker 
316e6ac0accSAnna Schumaker /**
3171da177e4SLinus Torvalds  * _shift_data_right_pages
3181da177e4SLinus Torvalds  * @pages: vector of pages containing both the source and dest memory area.
3191da177e4SLinus Torvalds  * @pgto_base: page vector address of destination
3201da177e4SLinus Torvalds  * @pgfrom_base: page vector address of source
3211da177e4SLinus Torvalds  * @len: number of bytes to copy
3221da177e4SLinus Torvalds  *
3231da177e4SLinus Torvalds  * Note: the addresses pgto_base and pgfrom_base are both calculated in
3241da177e4SLinus Torvalds  *       the same way:
3251da177e4SLinus Torvalds  *            if a memory area starts at byte 'base' in page 'pages[i]',
326ea1754a0SKirill A. Shutemov  *            then its address is given as (i << PAGE_SHIFT) + base
3271da177e4SLinus Torvalds  * Also note: pgfrom_base must be < pgto_base, but the memory areas
3281da177e4SLinus Torvalds  * 	they point to may overlap.
3291da177e4SLinus Torvalds  */
3301da177e4SLinus Torvalds static void
_shift_data_right_pages(struct page ** pages,size_t pgto_base,size_t pgfrom_base,size_t len)3311da177e4SLinus Torvalds _shift_data_right_pages(struct page **pages, size_t pgto_base,
3321da177e4SLinus Torvalds 		size_t pgfrom_base, size_t len)
3331da177e4SLinus Torvalds {
3341da177e4SLinus Torvalds 	struct page **pgfrom, **pgto;
3351da177e4SLinus Torvalds 	char *vfrom, *vto;
3361da177e4SLinus Torvalds 	size_t copy;
3371da177e4SLinus Torvalds 
3381da177e4SLinus Torvalds 	BUG_ON(pgto_base <= pgfrom_base);
3391da177e4SLinus Torvalds 
340c54e959bSTrond Myklebust 	if (!len)
341c54e959bSTrond Myklebust 		return;
342c54e959bSTrond Myklebust 
3431da177e4SLinus Torvalds 	pgto_base += len;
3441da177e4SLinus Torvalds 	pgfrom_base += len;
3451da177e4SLinus Torvalds 
34609cbfeafSKirill A. Shutemov 	pgto = pages + (pgto_base >> PAGE_SHIFT);
34709cbfeafSKirill A. Shutemov 	pgfrom = pages + (pgfrom_base >> PAGE_SHIFT);
3481da177e4SLinus Torvalds 
34909cbfeafSKirill A. Shutemov 	pgto_base &= ~PAGE_MASK;
35009cbfeafSKirill A. Shutemov 	pgfrom_base &= ~PAGE_MASK;
3511da177e4SLinus Torvalds 
3521da177e4SLinus Torvalds 	do {
3531da177e4SLinus Torvalds 		/* Are any pointers crossing a page boundary? */
3541da177e4SLinus Torvalds 		if (pgto_base == 0) {
35509cbfeafSKirill A. Shutemov 			pgto_base = PAGE_SIZE;
3561da177e4SLinus Torvalds 			pgto--;
3571da177e4SLinus Torvalds 		}
3581da177e4SLinus Torvalds 		if (pgfrom_base == 0) {
35909cbfeafSKirill A. Shutemov 			pgfrom_base = PAGE_SIZE;
3601da177e4SLinus Torvalds 			pgfrom--;
3611da177e4SLinus Torvalds 		}
3621da177e4SLinus Torvalds 
3631da177e4SLinus Torvalds 		copy = len;
3641da177e4SLinus Torvalds 		if (copy > pgto_base)
3651da177e4SLinus Torvalds 			copy = pgto_base;
3661da177e4SLinus Torvalds 		if (copy > pgfrom_base)
3671da177e4SLinus Torvalds 			copy = pgfrom_base;
3681da177e4SLinus Torvalds 		pgto_base -= copy;
3691da177e4SLinus Torvalds 		pgfrom_base -= copy;
3701da177e4SLinus Torvalds 
371b8541786SCong Wang 		vto = kmap_atomic(*pgto);
372347e2233STrond Myklebust 		if (*pgto != *pgfrom) {
373b8541786SCong Wang 			vfrom = kmap_atomic(*pgfrom);
374347e2233STrond Myklebust 			memcpy(vto + pgto_base, vfrom + pgfrom_base, copy);
375b8541786SCong Wang 			kunmap_atomic(vfrom);
376347e2233STrond Myklebust 		} else
377347e2233STrond Myklebust 			memmove(vto + pgto_base, vto + pgfrom_base, copy);
378347e2233STrond Myklebust 		flush_dcache_page(*pgto);
379b8541786SCong Wang 		kunmap_atomic(vto);
3801da177e4SLinus Torvalds 
3811da177e4SLinus Torvalds 	} while ((len -= copy) != 0);
3821da177e4SLinus Torvalds }
3831da177e4SLinus Torvalds 
3842c53040fSBen Hutchings /**
3851da177e4SLinus Torvalds  * _copy_to_pages
3861da177e4SLinus Torvalds  * @pages: array of pages
3871da177e4SLinus Torvalds  * @pgbase: page vector address of destination
3881da177e4SLinus Torvalds  * @p: pointer to source data
3891da177e4SLinus Torvalds  * @len: length
3901da177e4SLinus Torvalds  *
3911da177e4SLinus Torvalds  * Copies data from an arbitrary memory location into an array of pages
3921da177e4SLinus Torvalds  * The copy is assumed to be non-overlapping.
3931da177e4SLinus Torvalds  */
3941da177e4SLinus Torvalds static void
_copy_to_pages(struct page ** pages,size_t pgbase,const char * p,size_t len)3951da177e4SLinus Torvalds _copy_to_pages(struct page **pages, size_t pgbase, const char *p, size_t len)
3961da177e4SLinus Torvalds {
3971da177e4SLinus Torvalds 	struct page **pgto;
3981da177e4SLinus Torvalds 	char *vto;
3991da177e4SLinus Torvalds 	size_t copy;
4001da177e4SLinus Torvalds 
401c54e959bSTrond Myklebust 	if (!len)
402c54e959bSTrond Myklebust 		return;
403c54e959bSTrond Myklebust 
40409cbfeafSKirill A. Shutemov 	pgto = pages + (pgbase >> PAGE_SHIFT);
40509cbfeafSKirill A. Shutemov 	pgbase &= ~PAGE_MASK;
4061da177e4SLinus Torvalds 
407daeba89dSTrond Myklebust 	for (;;) {
40809cbfeafSKirill A. Shutemov 		copy = PAGE_SIZE - pgbase;
4091da177e4SLinus Torvalds 		if (copy > len)
4101da177e4SLinus Torvalds 			copy = len;
4111da177e4SLinus Torvalds 
412b8541786SCong Wang 		vto = kmap_atomic(*pgto);
4131da177e4SLinus Torvalds 		memcpy(vto + pgbase, p, copy);
414b8541786SCong Wang 		kunmap_atomic(vto);
4151da177e4SLinus Torvalds 
416daeba89dSTrond Myklebust 		len -= copy;
417daeba89dSTrond Myklebust 		if (len == 0)
418daeba89dSTrond Myklebust 			break;
419daeba89dSTrond Myklebust 
4201da177e4SLinus Torvalds 		pgbase += copy;
42109cbfeafSKirill A. Shutemov 		if (pgbase == PAGE_SIZE) {
4221da177e4SLinus Torvalds 			flush_dcache_page(*pgto);
4231da177e4SLinus Torvalds 			pgbase = 0;
4241da177e4SLinus Torvalds 			pgto++;
4251da177e4SLinus Torvalds 		}
4261da177e4SLinus Torvalds 		p += copy;
427daeba89dSTrond Myklebust 	}
4281da177e4SLinus Torvalds 	flush_dcache_page(*pgto);
4291da177e4SLinus Torvalds }
4301da177e4SLinus Torvalds 
4312c53040fSBen Hutchings /**
4321da177e4SLinus Torvalds  * _copy_from_pages
4331da177e4SLinus Torvalds  * @p: pointer to destination
4341da177e4SLinus Torvalds  * @pages: array of pages
4351da177e4SLinus Torvalds  * @pgbase: offset of source data
4361da177e4SLinus Torvalds  * @len: length
4371da177e4SLinus Torvalds  *
4381da177e4SLinus Torvalds  * Copies data into an arbitrary memory location from an array of pages
4391da177e4SLinus Torvalds  * The copy is assumed to be non-overlapping.
4401da177e4SLinus Torvalds  */
441bf118a34SAndy Adamson void
_copy_from_pages(char * p,struct page ** pages,size_t pgbase,size_t len)4421da177e4SLinus Torvalds _copy_from_pages(char *p, struct page **pages, size_t pgbase, size_t len)
4431da177e4SLinus Torvalds {
4441da177e4SLinus Torvalds 	struct page **pgfrom;
4451da177e4SLinus Torvalds 	char *vfrom;
4461da177e4SLinus Torvalds 	size_t copy;
4471da177e4SLinus Torvalds 
448c54e959bSTrond Myklebust 	if (!len)
449c54e959bSTrond Myklebust 		return;
450c54e959bSTrond Myklebust 
45109cbfeafSKirill A. Shutemov 	pgfrom = pages + (pgbase >> PAGE_SHIFT);
45209cbfeafSKirill A. Shutemov 	pgbase &= ~PAGE_MASK;
4531da177e4SLinus Torvalds 
4541da177e4SLinus Torvalds 	do {
45509cbfeafSKirill A. Shutemov 		copy = PAGE_SIZE - pgbase;
4561da177e4SLinus Torvalds 		if (copy > len)
4571da177e4SLinus Torvalds 			copy = len;
4581da177e4SLinus Torvalds 
459b8541786SCong Wang 		vfrom = kmap_atomic(*pgfrom);
4601da177e4SLinus Torvalds 		memcpy(p, vfrom + pgbase, copy);
461b8541786SCong Wang 		kunmap_atomic(vfrom);
4621da177e4SLinus Torvalds 
4631da177e4SLinus Torvalds 		pgbase += copy;
46409cbfeafSKirill A. Shutemov 		if (pgbase == PAGE_SIZE) {
4651da177e4SLinus Torvalds 			pgbase = 0;
4661da177e4SLinus Torvalds 			pgfrom++;
4671da177e4SLinus Torvalds 		}
4681da177e4SLinus Torvalds 		p += copy;
4691da177e4SLinus Torvalds 
4701da177e4SLinus Torvalds 	} while ((len -= copy) != 0);
4711da177e4SLinus Torvalds }
472bf118a34SAndy Adamson EXPORT_SYMBOL_GPL(_copy_from_pages);
4731da177e4SLinus Torvalds 
xdr_buf_iov_zero(const struct kvec * iov,unsigned int base,unsigned int len)474c4f2f591STrond Myklebust static void xdr_buf_iov_zero(const struct kvec *iov, unsigned int base,
475c4f2f591STrond Myklebust 			     unsigned int len)
476c4f2f591STrond Myklebust {
477c4f2f591STrond Myklebust 	if (base >= iov->iov_len)
478c4f2f591STrond Myklebust 		return;
479c4f2f591STrond Myklebust 	if (len > iov->iov_len - base)
480c4f2f591STrond Myklebust 		len = iov->iov_len - base;
481c4f2f591STrond Myklebust 	memset(iov->iov_base + base, 0, len);
482c4f2f591STrond Myklebust }
483c4f2f591STrond Myklebust 
4842c53040fSBen Hutchings /**
485c4f2f591STrond Myklebust  * xdr_buf_pages_zero
486c4f2f591STrond Myklebust  * @buf: xdr_buf
487c4f2f591STrond Myklebust  * @pgbase: beginning offset
48884ce182aSAnna Schumaker  * @len: length
48984ce182aSAnna Schumaker  */
xdr_buf_pages_zero(const struct xdr_buf * buf,unsigned int pgbase,unsigned int len)490c4f2f591STrond Myklebust static void xdr_buf_pages_zero(const struct xdr_buf *buf, unsigned int pgbase,
491c4f2f591STrond Myklebust 			       unsigned int len)
49284ce182aSAnna Schumaker {
493c4f2f591STrond Myklebust 	struct page **pages = buf->pages;
49484ce182aSAnna Schumaker 	struct page **page;
49584ce182aSAnna Schumaker 	char *vpage;
496c4f2f591STrond Myklebust 	unsigned int zero;
497c4f2f591STrond Myklebust 
498c4f2f591STrond Myklebust 	if (!len)
499c4f2f591STrond Myklebust 		return;
500c4f2f591STrond Myklebust 	if (pgbase >= buf->page_len) {
501c4f2f591STrond Myklebust 		xdr_buf_iov_zero(buf->tail, pgbase - buf->page_len, len);
502c4f2f591STrond Myklebust 		return;
503c4f2f591STrond Myklebust 	}
504c4f2f591STrond Myklebust 	if (pgbase + len > buf->page_len) {
505c4f2f591STrond Myklebust 		xdr_buf_iov_zero(buf->tail, 0, pgbase + len - buf->page_len);
506c4f2f591STrond Myklebust 		len = buf->page_len - pgbase;
507c4f2f591STrond Myklebust 	}
508c4f2f591STrond Myklebust 
509c4f2f591STrond Myklebust 	pgbase += buf->page_base;
51084ce182aSAnna Schumaker 
51184ce182aSAnna Schumaker 	page = pages + (pgbase >> PAGE_SHIFT);
51284ce182aSAnna Schumaker 	pgbase &= ~PAGE_MASK;
51384ce182aSAnna Schumaker 
51484ce182aSAnna Schumaker 	do {
51584ce182aSAnna Schumaker 		zero = PAGE_SIZE - pgbase;
51684ce182aSAnna Schumaker 		if (zero > len)
51784ce182aSAnna Schumaker 			zero = len;
51884ce182aSAnna Schumaker 
51984ce182aSAnna Schumaker 		vpage = kmap_atomic(*page);
52084ce182aSAnna Schumaker 		memset(vpage + pgbase, 0, zero);
52184ce182aSAnna Schumaker 		kunmap_atomic(vpage);
52284ce182aSAnna Schumaker 
52384ce182aSAnna Schumaker 		flush_dcache_page(*page);
52484ce182aSAnna Schumaker 		pgbase = 0;
52584ce182aSAnna Schumaker 		page++;
52684ce182aSAnna Schumaker 
52784ce182aSAnna Schumaker 	} while ((len -= zero) != 0);
52884ce182aSAnna Schumaker }
52984ce182aSAnna Schumaker 
xdr_buf_pages_fill_sparse(const struct xdr_buf * buf,unsigned int buflen,gfp_t gfp)5305802f7c2STrond Myklebust static unsigned int xdr_buf_pages_fill_sparse(const struct xdr_buf *buf,
5315802f7c2STrond Myklebust 					      unsigned int buflen, gfp_t gfp)
5325802f7c2STrond Myklebust {
5335802f7c2STrond Myklebust 	unsigned int i, npages, pagelen;
5345802f7c2STrond Myklebust 
5355802f7c2STrond Myklebust 	if (!(buf->flags & XDRBUF_SPARSE_PAGES))
5365802f7c2STrond Myklebust 		return buflen;
5375802f7c2STrond Myklebust 	if (buflen <= buf->head->iov_len)
5385802f7c2STrond Myklebust 		return buflen;
5395802f7c2STrond Myklebust 	pagelen = buflen - buf->head->iov_len;
5405802f7c2STrond Myklebust 	if (pagelen > buf->page_len)
5415802f7c2STrond Myklebust 		pagelen = buf->page_len;
5425802f7c2STrond Myklebust 	npages = (pagelen + buf->page_base + PAGE_SIZE - 1) >> PAGE_SHIFT;
5435802f7c2STrond Myklebust 	for (i = 0; i < npages; i++) {
5445802f7c2STrond Myklebust 		if (!buf->pages[i])
5455802f7c2STrond Myklebust 			continue;
5465802f7c2STrond Myklebust 		buf->pages[i] = alloc_page(gfp);
5475802f7c2STrond Myklebust 		if (likely(buf->pages[i]))
5485802f7c2STrond Myklebust 			continue;
5495802f7c2STrond Myklebust 		buflen -= pagelen;
5505802f7c2STrond Myklebust 		pagelen = i << PAGE_SHIFT;
5515802f7c2STrond Myklebust 		if (pagelen > buf->page_base)
5525802f7c2STrond Myklebust 			buflen += pagelen - buf->page_base;
5535802f7c2STrond Myklebust 		break;
5545802f7c2STrond Myklebust 	}
5555802f7c2STrond Myklebust 	return buflen;
5565802f7c2STrond Myklebust }
5575802f7c2STrond Myklebust 
xdr_buf_try_expand(struct xdr_buf * buf,unsigned int len)558c4f2f591STrond Myklebust static void xdr_buf_try_expand(struct xdr_buf *buf, unsigned int len)
559c4f2f591STrond Myklebust {
560c4f2f591STrond Myklebust 	struct kvec *head = buf->head;
561c4f2f591STrond Myklebust 	struct kvec *tail = buf->tail;
562c4f2f591STrond Myklebust 	unsigned int sum = head->iov_len + buf->page_len + tail->iov_len;
5635802f7c2STrond Myklebust 	unsigned int free_space, newlen;
564c4f2f591STrond Myklebust 
565c4f2f591STrond Myklebust 	if (sum > buf->len) {
566c4f2f591STrond Myklebust 		free_space = min_t(unsigned int, sum - buf->len, len);
5675802f7c2STrond Myklebust 		newlen = xdr_buf_pages_fill_sparse(buf, buf->len + free_space,
5685802f7c2STrond Myklebust 						   GFP_KERNEL);
5695802f7c2STrond Myklebust 		free_space = newlen - buf->len;
5705802f7c2STrond Myklebust 		buf->len = newlen;
571c4f2f591STrond Myklebust 		len -= free_space;
572c4f2f591STrond Myklebust 		if (!len)
573c4f2f591STrond Myklebust 			return;
574c4f2f591STrond Myklebust 	}
575c4f2f591STrond Myklebust 
576c4f2f591STrond Myklebust 	if (buf->buflen > sum) {
577c4f2f591STrond Myklebust 		/* Expand the tail buffer */
578c4f2f591STrond Myklebust 		free_space = min_t(unsigned int, buf->buflen - sum, len);
579c4f2f591STrond Myklebust 		tail->iov_len += free_space;
580c4f2f591STrond Myklebust 		buf->len += free_space;
581c4f2f591STrond Myklebust 	}
582c4f2f591STrond Myklebust }
583c4f2f591STrond Myklebust 
xdr_buf_tail_copy_right(const struct xdr_buf * buf,unsigned int base,unsigned int len,unsigned int shift)584c4f2f591STrond Myklebust static void xdr_buf_tail_copy_right(const struct xdr_buf *buf,
585c4f2f591STrond Myklebust 				    unsigned int base, unsigned int len,
586c4f2f591STrond Myklebust 				    unsigned int shift)
587c4f2f591STrond Myklebust {
588c4f2f591STrond Myklebust 	const struct kvec *tail = buf->tail;
589c4f2f591STrond Myklebust 	unsigned int to = base + shift;
590c4f2f591STrond Myklebust 
591c4f2f591STrond Myklebust 	if (to >= tail->iov_len)
592c4f2f591STrond Myklebust 		return;
593c4f2f591STrond Myklebust 	if (len + to > tail->iov_len)
594c4f2f591STrond Myklebust 		len = tail->iov_len - to;
595c4f2f591STrond Myklebust 	memmove(tail->iov_base + to, tail->iov_base + base, len);
596c4f2f591STrond Myklebust }
597c4f2f591STrond Myklebust 
xdr_buf_pages_copy_right(const struct xdr_buf * buf,unsigned int base,unsigned int len,unsigned int shift)598c4f2f591STrond Myklebust static void xdr_buf_pages_copy_right(const struct xdr_buf *buf,
599c4f2f591STrond Myklebust 				     unsigned int base, unsigned int len,
600c4f2f591STrond Myklebust 				     unsigned int shift)
601c4f2f591STrond Myklebust {
602c4f2f591STrond Myklebust 	const struct kvec *tail = buf->tail;
603c4f2f591STrond Myklebust 	unsigned int to = base + shift;
604c4f2f591STrond Myklebust 	unsigned int pglen = 0;
605c4f2f591STrond Myklebust 	unsigned int talen = 0, tato = 0;
606c4f2f591STrond Myklebust 
607c4f2f591STrond Myklebust 	if (base >= buf->page_len)
608c4f2f591STrond Myklebust 		return;
609c4f2f591STrond Myklebust 	if (len > buf->page_len - base)
610c4f2f591STrond Myklebust 		len = buf->page_len - base;
611c4f2f591STrond Myklebust 	if (to >= buf->page_len) {
612c4f2f591STrond Myklebust 		tato = to - buf->page_len;
613c4f2f591STrond Myklebust 		if (tail->iov_len >= len + tato)
614c4f2f591STrond Myklebust 			talen = len;
615c4f2f591STrond Myklebust 		else if (tail->iov_len > tato)
616c4f2f591STrond Myklebust 			talen = tail->iov_len - tato;
617c4f2f591STrond Myklebust 	} else if (len + to >= buf->page_len) {
618c4f2f591STrond Myklebust 		pglen = buf->page_len - to;
619c4f2f591STrond Myklebust 		talen = len - pglen;
620c4f2f591STrond Myklebust 		if (talen > tail->iov_len)
621c4f2f591STrond Myklebust 			talen = tail->iov_len;
622c4f2f591STrond Myklebust 	} else
623c4f2f591STrond Myklebust 		pglen = len;
624c4f2f591STrond Myklebust 
625c4f2f591STrond Myklebust 	_copy_from_pages(tail->iov_base + tato, buf->pages,
626c4f2f591STrond Myklebust 			 buf->page_base + base + pglen, talen);
627c4f2f591STrond Myklebust 	_shift_data_right_pages(buf->pages, buf->page_base + to,
628c4f2f591STrond Myklebust 				buf->page_base + base, pglen);
629c4f2f591STrond Myklebust }
630c4f2f591STrond Myklebust 
xdr_buf_head_copy_right(const struct xdr_buf * buf,unsigned int base,unsigned int len,unsigned int shift)6316707fbd7STrond Myklebust static void xdr_buf_head_copy_right(const struct xdr_buf *buf,
6326707fbd7STrond Myklebust 				    unsigned int base, unsigned int len,
6336707fbd7STrond Myklebust 				    unsigned int shift)
6346707fbd7STrond Myklebust {
6356707fbd7STrond Myklebust 	const struct kvec *head = buf->head;
6366707fbd7STrond Myklebust 	const struct kvec *tail = buf->tail;
6376707fbd7STrond Myklebust 	unsigned int to = base + shift;
6386707fbd7STrond Myklebust 	unsigned int pglen = 0, pgto = 0;
6396707fbd7STrond Myklebust 	unsigned int talen = 0, tato = 0;
6406707fbd7STrond Myklebust 
6416707fbd7STrond Myklebust 	if (base >= head->iov_len)
6426707fbd7STrond Myklebust 		return;
6436707fbd7STrond Myklebust 	if (len > head->iov_len - base)
6446707fbd7STrond Myklebust 		len = head->iov_len - base;
6456707fbd7STrond Myklebust 	if (to >= buf->page_len + head->iov_len) {
6466707fbd7STrond Myklebust 		tato = to - buf->page_len - head->iov_len;
6476707fbd7STrond Myklebust 		talen = len;
6486707fbd7STrond Myklebust 	} else if (to >= head->iov_len) {
6496707fbd7STrond Myklebust 		pgto = to - head->iov_len;
6506707fbd7STrond Myklebust 		pglen = len;
6516707fbd7STrond Myklebust 		if (pgto + pglen > buf->page_len) {
6526707fbd7STrond Myklebust 			talen = pgto + pglen - buf->page_len;
6536707fbd7STrond Myklebust 			pglen -= talen;
6546707fbd7STrond Myklebust 		}
6556707fbd7STrond Myklebust 	} else {
6566707fbd7STrond Myklebust 		pglen = len - to;
6576707fbd7STrond Myklebust 		if (pglen > buf->page_len) {
6586707fbd7STrond Myklebust 			talen = pglen - buf->page_len;
6596707fbd7STrond Myklebust 			pglen = buf->page_len;
6606707fbd7STrond Myklebust 		}
6616707fbd7STrond Myklebust 	}
6626707fbd7STrond Myklebust 
6636707fbd7STrond Myklebust 	len -= talen;
6646707fbd7STrond Myklebust 	base += len;
6656707fbd7STrond Myklebust 	if (talen + tato > tail->iov_len)
6666707fbd7STrond Myklebust 		talen = tail->iov_len > tato ? tail->iov_len - tato : 0;
6676707fbd7STrond Myklebust 	memcpy(tail->iov_base + tato, head->iov_base + base, talen);
6686707fbd7STrond Myklebust 
6696707fbd7STrond Myklebust 	len -= pglen;
6706707fbd7STrond Myklebust 	base -= pglen;
6716707fbd7STrond Myklebust 	_copy_to_pages(buf->pages, buf->page_base + pgto, head->iov_base + base,
6726707fbd7STrond Myklebust 		       pglen);
6736707fbd7STrond Myklebust 
6746707fbd7STrond Myklebust 	base -= len;
6756707fbd7STrond Myklebust 	memmove(head->iov_base + to, head->iov_base + base, len);
6766707fbd7STrond Myklebust }
6776707fbd7STrond Myklebust 
xdr_buf_tail_shift_right(const struct xdr_buf * buf,unsigned int base,unsigned int len,unsigned int shift)678c4f2f591STrond Myklebust static void xdr_buf_tail_shift_right(const struct xdr_buf *buf,
679c4f2f591STrond Myklebust 				     unsigned int base, unsigned int len,
680c4f2f591STrond Myklebust 				     unsigned int shift)
681c4f2f591STrond Myklebust {
682c4f2f591STrond Myklebust 	const struct kvec *tail = buf->tail;
683c4f2f591STrond Myklebust 
684c4f2f591STrond Myklebust 	if (base >= tail->iov_len || !shift || !len)
685c4f2f591STrond Myklebust 		return;
686c4f2f591STrond Myklebust 	xdr_buf_tail_copy_right(buf, base, len, shift);
687c4f2f591STrond Myklebust }
688c4f2f591STrond Myklebust 
xdr_buf_pages_shift_right(const struct xdr_buf * buf,unsigned int base,unsigned int len,unsigned int shift)689c4f2f591STrond Myklebust static void xdr_buf_pages_shift_right(const struct xdr_buf *buf,
690c4f2f591STrond Myklebust 				      unsigned int base, unsigned int len,
691c4f2f591STrond Myklebust 				      unsigned int shift)
692c4f2f591STrond Myklebust {
693c4f2f591STrond Myklebust 	if (!shift || !len)
694c4f2f591STrond Myklebust 		return;
695c4f2f591STrond Myklebust 	if (base >= buf->page_len) {
696c4f2f591STrond Myklebust 		xdr_buf_tail_shift_right(buf, base - buf->page_len, len, shift);
697c4f2f591STrond Myklebust 		return;
698c4f2f591STrond Myklebust 	}
699c4f2f591STrond Myklebust 	if (base + len > buf->page_len)
700c4f2f591STrond Myklebust 		xdr_buf_tail_shift_right(buf, 0, base + len - buf->page_len,
701c4f2f591STrond Myklebust 					 shift);
702c4f2f591STrond Myklebust 	xdr_buf_pages_copy_right(buf, base, len, shift);
703c4f2f591STrond Myklebust }
704c4f2f591STrond Myklebust 
xdr_buf_head_shift_right(const struct xdr_buf * buf,unsigned int base,unsigned int len,unsigned int shift)7056707fbd7STrond Myklebust static void xdr_buf_head_shift_right(const struct xdr_buf *buf,
7066707fbd7STrond Myklebust 				     unsigned int base, unsigned int len,
7076707fbd7STrond Myklebust 				     unsigned int shift)
7086707fbd7STrond Myklebust {
7096707fbd7STrond Myklebust 	const struct kvec *head = buf->head;
7106707fbd7STrond Myklebust 
7116707fbd7STrond Myklebust 	if (!shift)
7126707fbd7STrond Myklebust 		return;
7136707fbd7STrond Myklebust 	if (base >= head->iov_len) {
7146707fbd7STrond Myklebust 		xdr_buf_pages_shift_right(buf, head->iov_len - base, len,
7156707fbd7STrond Myklebust 					  shift);
7166707fbd7STrond Myklebust 		return;
7176707fbd7STrond Myklebust 	}
7186707fbd7STrond Myklebust 	if (base + len > head->iov_len)
7196707fbd7STrond Myklebust 		xdr_buf_pages_shift_right(buf, 0, base + len - head->iov_len,
7206707fbd7STrond Myklebust 					  shift);
7216707fbd7STrond Myklebust 	xdr_buf_head_copy_right(buf, base, len, shift);
7226707fbd7STrond Myklebust }
7236707fbd7STrond Myklebust 
xdr_buf_tail_copy_left(const struct xdr_buf * buf,unsigned int base,unsigned int len,unsigned int shift)7249a20f6f4STrond Myklebust static void xdr_buf_tail_copy_left(const struct xdr_buf *buf, unsigned int base,
7259a20f6f4STrond Myklebust 				   unsigned int len, unsigned int shift)
7269a20f6f4STrond Myklebust {
7279a20f6f4STrond Myklebust 	const struct kvec *tail = buf->tail;
7289a20f6f4STrond Myklebust 
7299a20f6f4STrond Myklebust 	if (base >= tail->iov_len)
7309a20f6f4STrond Myklebust 		return;
7319a20f6f4STrond Myklebust 	if (len > tail->iov_len - base)
7329a20f6f4STrond Myklebust 		len = tail->iov_len - base;
7339a20f6f4STrond Myklebust 	/* Shift data into head */
7349a20f6f4STrond Myklebust 	if (shift > buf->page_len + base) {
7359a20f6f4STrond Myklebust 		const struct kvec *head = buf->head;
7369a20f6f4STrond Myklebust 		unsigned int hdto =
7379a20f6f4STrond Myklebust 			head->iov_len + buf->page_len + base - shift;
7389a20f6f4STrond Myklebust 		unsigned int hdlen = len;
7399a20f6f4STrond Myklebust 
7409a20f6f4STrond Myklebust 		if (WARN_ONCE(shift > head->iov_len + buf->page_len + base,
7419a20f6f4STrond Myklebust 			      "SUNRPC: Misaligned data.\n"))
7429a20f6f4STrond Myklebust 			return;
7439a20f6f4STrond Myklebust 		if (hdto + hdlen > head->iov_len)
7449a20f6f4STrond Myklebust 			hdlen = head->iov_len - hdto;
7459a20f6f4STrond Myklebust 		memcpy(head->iov_base + hdto, tail->iov_base + base, hdlen);
7469a20f6f4STrond Myklebust 		base += hdlen;
7479a20f6f4STrond Myklebust 		len -= hdlen;
7489a20f6f4STrond Myklebust 		if (!len)
7499a20f6f4STrond Myklebust 			return;
7509a20f6f4STrond Myklebust 	}
7519a20f6f4STrond Myklebust 	/* Shift data into pages */
7529a20f6f4STrond Myklebust 	if (shift > base) {
7539a20f6f4STrond Myklebust 		unsigned int pgto = buf->page_len + base - shift;
7549a20f6f4STrond Myklebust 		unsigned int pglen = len;
7559a20f6f4STrond Myklebust 
7569a20f6f4STrond Myklebust 		if (pgto + pglen > buf->page_len)
7579a20f6f4STrond Myklebust 			pglen = buf->page_len - pgto;
7589a20f6f4STrond Myklebust 		_copy_to_pages(buf->pages, buf->page_base + pgto,
7599a20f6f4STrond Myklebust 			       tail->iov_base + base, pglen);
7609a20f6f4STrond Myklebust 		base += pglen;
7619a20f6f4STrond Myklebust 		len -= pglen;
7629a20f6f4STrond Myklebust 		if (!len)
7639a20f6f4STrond Myklebust 			return;
7649a20f6f4STrond Myklebust 	}
7659a20f6f4STrond Myklebust 	memmove(tail->iov_base + base - shift, tail->iov_base + base, len);
7669a20f6f4STrond Myklebust }
7679a20f6f4STrond Myklebust 
xdr_buf_pages_copy_left(const struct xdr_buf * buf,unsigned int base,unsigned int len,unsigned int shift)7689a20f6f4STrond Myklebust static void xdr_buf_pages_copy_left(const struct xdr_buf *buf,
7699a20f6f4STrond Myklebust 				    unsigned int base, unsigned int len,
7709a20f6f4STrond Myklebust 				    unsigned int shift)
7719a20f6f4STrond Myklebust {
7729a20f6f4STrond Myklebust 	unsigned int pgto;
7739a20f6f4STrond Myklebust 
7749a20f6f4STrond Myklebust 	if (base >= buf->page_len)
7759a20f6f4STrond Myklebust 		return;
7769a20f6f4STrond Myklebust 	if (len > buf->page_len - base)
7779a20f6f4STrond Myklebust 		len = buf->page_len - base;
7789a20f6f4STrond Myklebust 	/* Shift data into head */
7799a20f6f4STrond Myklebust 	if (shift > base) {
7809a20f6f4STrond Myklebust 		const struct kvec *head = buf->head;
7819a20f6f4STrond Myklebust 		unsigned int hdto = head->iov_len + base - shift;
7829a20f6f4STrond Myklebust 		unsigned int hdlen = len;
7839a20f6f4STrond Myklebust 
7849a20f6f4STrond Myklebust 		if (WARN_ONCE(shift > head->iov_len + base,
7859a20f6f4STrond Myklebust 			      "SUNRPC: Misaligned data.\n"))
7869a20f6f4STrond Myklebust 			return;
7879a20f6f4STrond Myklebust 		if (hdto + hdlen > head->iov_len)
7889a20f6f4STrond Myklebust 			hdlen = head->iov_len - hdto;
7899a20f6f4STrond Myklebust 		_copy_from_pages(head->iov_base + hdto, buf->pages,
7909a20f6f4STrond Myklebust 				 buf->page_base + base, hdlen);
7919a20f6f4STrond Myklebust 		base += hdlen;
7929a20f6f4STrond Myklebust 		len -= hdlen;
7939a20f6f4STrond Myklebust 		if (!len)
7949a20f6f4STrond Myklebust 			return;
7959a20f6f4STrond Myklebust 	}
7969a20f6f4STrond Myklebust 	pgto = base - shift;
7979a20f6f4STrond Myklebust 	_shift_data_left_pages(buf->pages, buf->page_base + pgto,
7989a20f6f4STrond Myklebust 			       buf->page_base + base, len);
7999a20f6f4STrond Myklebust }
8009a20f6f4STrond Myklebust 
xdr_buf_tail_shift_left(const struct xdr_buf * buf,unsigned int base,unsigned int len,unsigned int shift)8019a20f6f4STrond Myklebust static void xdr_buf_tail_shift_left(const struct xdr_buf *buf,
8029a20f6f4STrond Myklebust 				    unsigned int base, unsigned int len,
8039a20f6f4STrond Myklebust 				    unsigned int shift)
8049a20f6f4STrond Myklebust {
8059a20f6f4STrond Myklebust 	if (!shift || !len)
8069a20f6f4STrond Myklebust 		return;
8079a20f6f4STrond Myklebust 	xdr_buf_tail_copy_left(buf, base, len, shift);
8089a20f6f4STrond Myklebust }
8099a20f6f4STrond Myklebust 
xdr_buf_pages_shift_left(const struct xdr_buf * buf,unsigned int base,unsigned int len,unsigned int shift)8109a20f6f4STrond Myklebust static void xdr_buf_pages_shift_left(const struct xdr_buf *buf,
8119a20f6f4STrond Myklebust 				     unsigned int base, unsigned int len,
8129a20f6f4STrond Myklebust 				     unsigned int shift)
8139a20f6f4STrond Myklebust {
8149a20f6f4STrond Myklebust 	if (!shift || !len)
8159a20f6f4STrond Myklebust 		return;
8169a20f6f4STrond Myklebust 	if (base >= buf->page_len) {
8179a20f6f4STrond Myklebust 		xdr_buf_tail_shift_left(buf, base - buf->page_len, len, shift);
8189a20f6f4STrond Myklebust 		return;
8199a20f6f4STrond Myklebust 	}
8209a20f6f4STrond Myklebust 	xdr_buf_pages_copy_left(buf, base, len, shift);
8219a20f6f4STrond Myklebust 	len += base;
8229a20f6f4STrond Myklebust 	if (len <= buf->page_len)
8239a20f6f4STrond Myklebust 		return;
8249a20f6f4STrond Myklebust 	xdr_buf_tail_copy_left(buf, 0, len - buf->page_len, shift);
8259a20f6f4STrond Myklebust }
8269a20f6f4STrond Myklebust 
xdr_buf_head_shift_left(const struct xdr_buf * buf,unsigned int base,unsigned int len,unsigned int shift)8274f5f3b60SAnna Schumaker static void xdr_buf_head_shift_left(const struct xdr_buf *buf,
8284f5f3b60SAnna Schumaker 				    unsigned int base, unsigned int len,
8294f5f3b60SAnna Schumaker 				    unsigned int shift)
8304f5f3b60SAnna Schumaker {
8314f5f3b60SAnna Schumaker 	const struct kvec *head = buf->head;
8324f5f3b60SAnna Schumaker 	unsigned int bytes;
8334f5f3b60SAnna Schumaker 
8344f5f3b60SAnna Schumaker 	if (!shift || !len)
8354f5f3b60SAnna Schumaker 		return;
8364f5f3b60SAnna Schumaker 
8374f5f3b60SAnna Schumaker 	if (shift > base) {
8384f5f3b60SAnna Schumaker 		bytes = (shift - base);
8394f5f3b60SAnna Schumaker 		if (bytes >= len)
8404f5f3b60SAnna Schumaker 			return;
8414f5f3b60SAnna Schumaker 		base += bytes;
8424f5f3b60SAnna Schumaker 		len -= bytes;
8434f5f3b60SAnna Schumaker 	}
8444f5f3b60SAnna Schumaker 
8454f5f3b60SAnna Schumaker 	if (base < head->iov_len) {
8464f5f3b60SAnna Schumaker 		bytes = min_t(unsigned int, len, head->iov_len - base);
8474f5f3b60SAnna Schumaker 		memmove(head->iov_base + (base - shift),
8484f5f3b60SAnna Schumaker 			head->iov_base + base, bytes);
8494f5f3b60SAnna Schumaker 		base += bytes;
8504f5f3b60SAnna Schumaker 		len -= bytes;
8514f5f3b60SAnna Schumaker 	}
8524f5f3b60SAnna Schumaker 	xdr_buf_pages_shift_left(buf, base - head->iov_len, len, shift);
8534f5f3b60SAnna Schumaker }
8544f5f3b60SAnna Schumaker 
85584ce182aSAnna Schumaker /**
8561da177e4SLinus Torvalds  * xdr_shrink_bufhead
8571da177e4SLinus Torvalds  * @buf: xdr_buf
8586707fbd7STrond Myklebust  * @len: new length of buf->head[0]
8591da177e4SLinus Torvalds  *
8606707fbd7STrond Myklebust  * Shrinks XDR buffer's header kvec buf->head[0], setting it to
8611da177e4SLinus Torvalds  * 'len' bytes. The extra data is not lost, but is instead
8621da177e4SLinus Torvalds  * moved into the inlined pages and/or the tail.
8631da177e4SLinus Torvalds  */
xdr_shrink_bufhead(struct xdr_buf * buf,unsigned int len)8646707fbd7STrond Myklebust static unsigned int xdr_shrink_bufhead(struct xdr_buf *buf, unsigned int len)
8651da177e4SLinus Torvalds {
8666707fbd7STrond Myklebust 	struct kvec *head = buf->head;
8676707fbd7STrond Myklebust 	unsigned int shift, buflen = max(buf->len, len);
86818e624adSWeston Andros Adamson 
86918e624adSWeston Andros Adamson 	WARN_ON_ONCE(len > head->iov_len);
8706707fbd7STrond Myklebust 	if (head->iov_len > buflen) {
8716707fbd7STrond Myklebust 		buf->buflen -= head->iov_len - buflen;
8726707fbd7STrond Myklebust 		head->iov_len = buflen;
8731da177e4SLinus Torvalds 	}
8746707fbd7STrond Myklebust 	if (len >= head->iov_len)
8756707fbd7STrond Myklebust 		return 0;
8766707fbd7STrond Myklebust 	shift = head->iov_len - len;
8776707fbd7STrond Myklebust 	xdr_buf_try_expand(buf, shift);
8786707fbd7STrond Myklebust 	xdr_buf_head_shift_right(buf, len, buflen - len, shift);
8796707fbd7STrond Myklebust 	head->iov_len = len;
8806707fbd7STrond Myklebust 	buf->buflen -= shift;
8816707fbd7STrond Myklebust 	buf->len -= shift;
8826707fbd7STrond Myklebust 	return shift;
8831da177e4SLinus Torvalds }
8841da177e4SLinus Torvalds 
8852c53040fSBen Hutchings /**
886c4f2f591STrond Myklebust  * xdr_shrink_pagelen - shrinks buf->pages to @len bytes
8871da177e4SLinus Torvalds  * @buf: xdr_buf
888c4f2f591STrond Myklebust  * @len: new page buffer length
8891da177e4SLinus Torvalds  *
890e8d70b32SChuck Lever  * The extra data is not lost, but is instead moved into buf->tail.
891e8d70b32SChuck Lever  * Returns the actual number of bytes moved.
8921da177e4SLinus Torvalds  */
xdr_shrink_pagelen(struct xdr_buf * buf,unsigned int len)893c4f2f591STrond Myklebust static unsigned int xdr_shrink_pagelen(struct xdr_buf *buf, unsigned int len)
8941da177e4SLinus Torvalds {
895c4f2f591STrond Myklebust 	unsigned int shift, buflen = buf->len - buf->head->iov_len;
8961da177e4SLinus Torvalds 
897c4f2f591STrond Myklebust 	WARN_ON_ONCE(len > buf->page_len);
898c4f2f591STrond Myklebust 	if (buf->head->iov_len >= buf->len || len > buflen)
899c4f2f591STrond Myklebust 		buflen = len;
900c4f2f591STrond Myklebust 	if (buf->page_len > buflen) {
901c4f2f591STrond Myklebust 		buf->buflen -= buf->page_len - buflen;
902c4f2f591STrond Myklebust 		buf->page_len = buflen;
903c4f2f591STrond Myklebust 	}
904c4f2f591STrond Myklebust 	if (len >= buf->page_len)
905c4f2f591STrond Myklebust 		return 0;
906c4f2f591STrond Myklebust 	shift = buf->page_len - len;
907c4f2f591STrond Myklebust 	xdr_buf_try_expand(buf, shift);
908c4f2f591STrond Myklebust 	xdr_buf_pages_shift_right(buf, len, buflen - len, shift);
909c4f2f591STrond Myklebust 	buf->page_len = len;
910c4f2f591STrond Myklebust 	buf->len -= shift;
911c4f2f591STrond Myklebust 	buf->buflen -= shift;
912c4f2f591STrond Myklebust 	return shift;
9131da177e4SLinus Torvalds }
9141da177e4SLinus Torvalds 
9151da177e4SLinus Torvalds /**
9164517d526STrond Myklebust  * xdr_stream_pos - Return the current offset from the start of the xdr_stream
9174517d526STrond Myklebust  * @xdr: pointer to struct xdr_stream
9184517d526STrond Myklebust  */
xdr_stream_pos(const struct xdr_stream * xdr)9194517d526STrond Myklebust unsigned int xdr_stream_pos(const struct xdr_stream *xdr)
9204517d526STrond Myklebust {
9214517d526STrond Myklebust 	return (unsigned int)(XDR_QUADLEN(xdr->buf->len) - xdr->nwords) << 2;
9224517d526STrond Myklebust }
9234517d526STrond Myklebust EXPORT_SYMBOL_GPL(xdr_stream_pos);
9244517d526STrond Myklebust 
xdr_stream_set_pos(struct xdr_stream * xdr,unsigned int pos)925c4f2f591STrond Myklebust static void xdr_stream_set_pos(struct xdr_stream *xdr, unsigned int pos)
926c4f2f591STrond Myklebust {
927c4f2f591STrond Myklebust 	unsigned int blen = xdr->buf->len;
928c4f2f591STrond Myklebust 
929c4f2f591STrond Myklebust 	xdr->nwords = blen > pos ? XDR_QUADLEN(blen) - XDR_QUADLEN(pos) : 0;
930c4f2f591STrond Myklebust }
931c4f2f591STrond Myklebust 
xdr_stream_page_set_pos(struct xdr_stream * xdr,unsigned int pos)932c4f2f591STrond Myklebust static void xdr_stream_page_set_pos(struct xdr_stream *xdr, unsigned int pos)
933c4f2f591STrond Myklebust {
934c4f2f591STrond Myklebust 	xdr_stream_set_pos(xdr, pos + xdr->buf->head[0].iov_len);
935c4f2f591STrond Myklebust }
936c4f2f591STrond Myklebust 
9374517d526STrond Myklebust /**
938cf1f08caSAnna Schumaker  * xdr_page_pos - Return the current offset from the start of the xdr pages
939cf1f08caSAnna Schumaker  * @xdr: pointer to struct xdr_stream
940cf1f08caSAnna Schumaker  */
xdr_page_pos(const struct xdr_stream * xdr)941cf1f08caSAnna Schumaker unsigned int xdr_page_pos(const struct xdr_stream *xdr)
942cf1f08caSAnna Schumaker {
943cf1f08caSAnna Schumaker 	unsigned int pos = xdr_stream_pos(xdr);
944cf1f08caSAnna Schumaker 
945cf1f08caSAnna Schumaker 	WARN_ON(pos < xdr->buf->head[0].iov_len);
946cf1f08caSAnna Schumaker 	return pos - xdr->buf->head[0].iov_len;
947cf1f08caSAnna Schumaker }
948cf1f08caSAnna Schumaker EXPORT_SYMBOL_GPL(xdr_page_pos);
949cf1f08caSAnna Schumaker 
950cf1f08caSAnna Schumaker /**
9511da177e4SLinus Torvalds  * xdr_init_encode - Initialize a struct xdr_stream for sending data.
9521da177e4SLinus Torvalds  * @xdr: pointer to xdr_stream struct
9531da177e4SLinus Torvalds  * @buf: pointer to XDR buffer in which to encode data
9541da177e4SLinus Torvalds  * @p: current pointer inside XDR buffer
9550ccc61b1SChuck Lever  * @rqst: pointer to controlling rpc_rqst, for debugging
9561da177e4SLinus Torvalds  *
9571da177e4SLinus Torvalds  * Note: at the moment the RPC client only passes the length of our
9581da177e4SLinus Torvalds  *	 scratch buffer in the xdr_buf's header kvec. Previously this
9591da177e4SLinus Torvalds  *	 meant we needed to call xdr_adjust_iovec() after encoding the
9601da177e4SLinus Torvalds  *	 data. With the new scheme, the xdr_stream manages the details
9611da177e4SLinus Torvalds  *	 of the buffer length, and takes care of adjusting the kvec
9621da177e4SLinus Torvalds  *	 length for us.
9631da177e4SLinus Torvalds  */
xdr_init_encode(struct xdr_stream * xdr,struct xdr_buf * buf,__be32 * p,struct rpc_rqst * rqst)9640ccc61b1SChuck Lever void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p,
9650ccc61b1SChuck Lever 		     struct rpc_rqst *rqst)
9661da177e4SLinus Torvalds {
9671da177e4SLinus Torvalds 	struct kvec *iov = buf->head;
968334ccfd5STrond Myklebust 	int scratch_len = buf->buflen - buf->page_len - buf->tail[0].iov_len;
9691da177e4SLinus Torvalds 
9700ae4c3e8SChuck Lever 	xdr_reset_scratch_buffer(xdr);
971334ccfd5STrond Myklebust 	BUG_ON(scratch_len < 0);
9721da177e4SLinus Torvalds 	xdr->buf = buf;
9731da177e4SLinus Torvalds 	xdr->iov = iov;
974d8ed029dSAlexey Dobriyan 	xdr->p = (__be32 *)((char *)iov->iov_base + iov->iov_len);
975d8ed029dSAlexey Dobriyan 	xdr->end = (__be32 *)((char *)iov->iov_base + scratch_len);
976334ccfd5STrond Myklebust 	BUG_ON(iov->iov_len > scratch_len);
977334ccfd5STrond Myklebust 
978334ccfd5STrond Myklebust 	if (p != xdr->p && p != NULL) {
979334ccfd5STrond Myklebust 		size_t len;
980334ccfd5STrond Myklebust 
981334ccfd5STrond Myklebust 		BUG_ON(p < xdr->p || p > xdr->end);
982334ccfd5STrond Myklebust 		len = (char *)p - (char *)xdr->p;
9831da177e4SLinus Torvalds 		xdr->p = p;
984334ccfd5STrond Myklebust 		buf->len += len;
985334ccfd5STrond Myklebust 		iov->iov_len += len;
986334ccfd5STrond Myklebust 	}
9870ccc61b1SChuck Lever 	xdr->rqst = rqst;
9881da177e4SLinus Torvalds }
989468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_init_encode);
9901da177e4SLinus Torvalds 
9911da177e4SLinus Torvalds /**
99298124f5bSChuck Lever  * xdr_init_encode_pages - Initialize an xdr_stream for encoding into pages
99398124f5bSChuck Lever  * @xdr: pointer to xdr_stream struct
99498124f5bSChuck Lever  * @buf: pointer to XDR buffer into which to encode data
99598124f5bSChuck Lever  * @pages: list of pages to decode into
99698124f5bSChuck Lever  * @rqst: pointer to controlling rpc_rqst, for debugging
99798124f5bSChuck Lever  *
99898124f5bSChuck Lever  */
xdr_init_encode_pages(struct xdr_stream * xdr,struct xdr_buf * buf,struct page ** pages,struct rpc_rqst * rqst)99998124f5bSChuck Lever void xdr_init_encode_pages(struct xdr_stream *xdr, struct xdr_buf *buf,
100098124f5bSChuck Lever 			   struct page **pages, struct rpc_rqst *rqst)
100198124f5bSChuck Lever {
100298124f5bSChuck Lever 	xdr_reset_scratch_buffer(xdr);
100398124f5bSChuck Lever 
100498124f5bSChuck Lever 	xdr->buf = buf;
100598124f5bSChuck Lever 	xdr->page_ptr = pages;
100698124f5bSChuck Lever 	xdr->iov = NULL;
100798124f5bSChuck Lever 	xdr->p = page_address(*pages);
100898124f5bSChuck Lever 	xdr->end = (void *)xdr->p + min_t(u32, buf->buflen, PAGE_SIZE);
100998124f5bSChuck Lever 	xdr->rqst = rqst;
101098124f5bSChuck Lever }
101198124f5bSChuck Lever EXPORT_SYMBOL_GPL(xdr_init_encode_pages);
101298124f5bSChuck Lever 
101398124f5bSChuck Lever /**
101462ed448cSChuck Lever  * __xdr_commit_encode - Ensure all data is written to buffer
10152825a7f9SJ. Bruce Fields  * @xdr: pointer to xdr_stream
10162825a7f9SJ. Bruce Fields  *
10172825a7f9SJ. Bruce Fields  * We handle encoding across page boundaries by giving the caller a
10182825a7f9SJ. Bruce Fields  * temporary location to write to, then later copying the data into
10192825a7f9SJ. Bruce Fields  * place; xdr_commit_encode does that copying.
10202825a7f9SJ. Bruce Fields  *
10212825a7f9SJ. Bruce Fields  * Normally the caller doesn't need to call this directly, as the
10222825a7f9SJ. Bruce Fields  * following xdr_reserve_space will do it.  But an explicit call may be
10232825a7f9SJ. Bruce Fields  * required at the end of encoding, or any other time when the xdr_buf
10242825a7f9SJ. Bruce Fields  * data might be read.
10252825a7f9SJ. Bruce Fields  */
__xdr_commit_encode(struct xdr_stream * xdr)102662ed448cSChuck Lever void __xdr_commit_encode(struct xdr_stream *xdr)
10272825a7f9SJ. Bruce Fields {
102890d871b3SChuck Lever 	size_t shift = xdr->scratch.iov_len;
10292825a7f9SJ. Bruce Fields 	void *page;
10302825a7f9SJ. Bruce Fields 
10312825a7f9SJ. Bruce Fields 	page = page_address(*xdr->page_ptr);
10322825a7f9SJ. Bruce Fields 	memcpy(xdr->scratch.iov_base, page, shift);
10332825a7f9SJ. Bruce Fields 	memmove(page, page + shift, (void *)xdr->p - page);
10340ae4c3e8SChuck Lever 	xdr_reset_scratch_buffer(xdr);
10352825a7f9SJ. Bruce Fields }
103662ed448cSChuck Lever EXPORT_SYMBOL_GPL(__xdr_commit_encode);
10372825a7f9SJ. Bruce Fields 
103862ed448cSChuck Lever /*
103962ed448cSChuck Lever  * The buffer space to be reserved crosses the boundary between
104062ed448cSChuck Lever  * xdr->buf->head and xdr->buf->pages, or between two pages
104162ed448cSChuck Lever  * in xdr->buf->pages.
104262ed448cSChuck Lever  */
xdr_get_next_encode_buffer(struct xdr_stream * xdr,size_t nbytes)104362ed448cSChuck Lever static noinline __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr,
104422cb4385STrond Myklebust 						   size_t nbytes)
10452825a7f9SJ. Bruce Fields {
10462825a7f9SJ. Bruce Fields 	int space_left;
10472825a7f9SJ. Bruce Fields 	int frag1bytes, frag2bytes;
1048da9e94feSChuck Lever 	void *p;
10492825a7f9SJ. Bruce Fields 
10502825a7f9SJ. Bruce Fields 	if (nbytes > PAGE_SIZE)
10515582863fSChuck Lever 		goto out_overflow; /* Bigger buffers require special handling */
10522825a7f9SJ. Bruce Fields 	if (xdr->buf->len + nbytes > xdr->buf->buflen)
10535582863fSChuck Lever 		goto out_overflow; /* Sorry, we're totally out of space */
10542825a7f9SJ. Bruce Fields 	frag1bytes = (xdr->end - xdr->p) << 2;
10552825a7f9SJ. Bruce Fields 	frag2bytes = nbytes - frag1bytes;
10562825a7f9SJ. Bruce Fields 	if (xdr->iov)
10572825a7f9SJ. Bruce Fields 		xdr->iov->iov_len += frag1bytes;
105805638dc7SJ. Bruce Fields 	else
10592825a7f9SJ. Bruce Fields 		xdr->buf->page_len += frag1bytes;
10602825a7f9SJ. Bruce Fields 	xdr->page_ptr++;
10612825a7f9SJ. Bruce Fields 	xdr->iov = NULL;
1062bd07a641SChuck Lever 
10632825a7f9SJ. Bruce Fields 	/*
10642825a7f9SJ. Bruce Fields 	 * If the last encode didn't end exactly on a page boundary, the
10652825a7f9SJ. Bruce Fields 	 * next one will straddle boundaries.  Encode into the next
10662825a7f9SJ. Bruce Fields 	 * page, then copy it back later in xdr_commit_encode.  We use
10672825a7f9SJ. Bruce Fields 	 * the "scratch" iov to track any temporarily unused fragment of
10682825a7f9SJ. Bruce Fields 	 * space at the end of the previous buffer:
10692825a7f9SJ. Bruce Fields 	 */
10700ae4c3e8SChuck Lever 	xdr_set_scratch_buffer(xdr, xdr->p, frag1bytes);
1071bd07a641SChuck Lever 
10722825a7f9SJ. Bruce Fields 	/*
1073bd07a641SChuck Lever 	 * xdr->p is where the next encode will start after
1074bd07a641SChuck Lever 	 * xdr_commit_encode() has shifted this one back:
10752825a7f9SJ. Bruce Fields 	 */
1076bd07a641SChuck Lever 	p = page_address(*xdr->page_ptr);
1077da9e94feSChuck Lever 	xdr->p = p + frag2bytes;
10782825a7f9SJ. Bruce Fields 	space_left = xdr->buf->buflen - xdr->buf->len;
1079a23dd544SChuck Lever 	if (space_left - frag1bytes >= PAGE_SIZE)
1080da9e94feSChuck Lever 		xdr->end = p + PAGE_SIZE;
10816c254bf3SChuck Lever 	else
1082da9e94feSChuck Lever 		xdr->end = p + space_left - frag1bytes;
10836c254bf3SChuck Lever 
10842825a7f9SJ. Bruce Fields 	xdr->buf->page_len += frag2bytes;
10852825a7f9SJ. Bruce Fields 	xdr->buf->len += nbytes;
10862825a7f9SJ. Bruce Fields 	return p;
10875582863fSChuck Lever out_overflow:
10885582863fSChuck Lever 	trace_rpc_xdr_overflow(xdr, nbytes);
10895582863fSChuck Lever 	return NULL;
10902825a7f9SJ. Bruce Fields }
10912825a7f9SJ. Bruce Fields 
10922825a7f9SJ. Bruce Fields /**
10931da177e4SLinus Torvalds  * xdr_reserve_space - Reserve buffer space for sending
10941da177e4SLinus Torvalds  * @xdr: pointer to xdr_stream
10951da177e4SLinus Torvalds  * @nbytes: number of bytes to reserve
10961da177e4SLinus Torvalds  *
10971da177e4SLinus Torvalds  * Checks that we have enough buffer space to encode 'nbytes' more
10981da177e4SLinus Torvalds  * bytes of data. If so, update the total xdr_buf length, and
10991da177e4SLinus Torvalds  * adjust the length of the current kvec.
11001da177e4SLinus Torvalds  */
xdr_reserve_space(struct xdr_stream * xdr,size_t nbytes)1101d8ed029dSAlexey Dobriyan __be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes)
11021da177e4SLinus Torvalds {
1103d8ed029dSAlexey Dobriyan 	__be32 *p = xdr->p;
1104d8ed029dSAlexey Dobriyan 	__be32 *q;
11051da177e4SLinus Torvalds 
11062825a7f9SJ. Bruce Fields 	xdr_commit_encode(xdr);
11071da177e4SLinus Torvalds 	/* align nbytes on the next 32-bit boundary */
11081da177e4SLinus Torvalds 	nbytes += 3;
11091da177e4SLinus Torvalds 	nbytes &= ~3;
11101da177e4SLinus Torvalds 	q = p + (nbytes >> 2);
11111da177e4SLinus Torvalds 	if (unlikely(q > xdr->end || q < p))
11122825a7f9SJ. Bruce Fields 		return xdr_get_next_encode_buffer(xdr, nbytes);
11131da177e4SLinus Torvalds 	xdr->p = q;
11142825a7f9SJ. Bruce Fields 	if (xdr->iov)
11151da177e4SLinus Torvalds 		xdr->iov->iov_len += nbytes;
11162825a7f9SJ. Bruce Fields 	else
11172825a7f9SJ. Bruce Fields 		xdr->buf->page_len += nbytes;
11181da177e4SLinus Torvalds 	xdr->buf->len += nbytes;
11191da177e4SLinus Torvalds 	return p;
11201da177e4SLinus Torvalds }
1121468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_reserve_space);
11221da177e4SLinus Torvalds 
1123403217f3SAnna Schumaker /**
1124403217f3SAnna Schumaker  * xdr_reserve_space_vec - Reserves a large amount of buffer space for sending
1125403217f3SAnna Schumaker  * @xdr: pointer to xdr_stream
1126403217f3SAnna Schumaker  * @nbytes: number of bytes to reserve
1127403217f3SAnna Schumaker  *
1128703d7521SChuck Lever  * The size argument passed to xdr_reserve_space() is determined based
1129703d7521SChuck Lever  * on the number of bytes remaining in the current page to avoid
1130703d7521SChuck Lever  * invalidating iov_base pointers when xdr_commit_encode() is called.
1131703d7521SChuck Lever  *
1132703d7521SChuck Lever  * Return values:
1133703d7521SChuck Lever  *   %0: success
1134703d7521SChuck Lever  *   %-EMSGSIZE: not enough space is available in @xdr
1135403217f3SAnna Schumaker  */
xdr_reserve_space_vec(struct xdr_stream * xdr,size_t nbytes)1136703d7521SChuck Lever int xdr_reserve_space_vec(struct xdr_stream *xdr, size_t nbytes)
1137403217f3SAnna Schumaker {
1138703d7521SChuck Lever 	size_t thislen;
1139403217f3SAnna Schumaker 	__be32 *p;
1140403217f3SAnna Schumaker 
1141403217f3SAnna Schumaker 	/*
1142403217f3SAnna Schumaker 	 * svcrdma requires every READ payload to start somewhere
1143403217f3SAnna Schumaker 	 * in xdr->pages.
1144403217f3SAnna Schumaker 	 */
1145403217f3SAnna Schumaker 	if (xdr->iov == xdr->buf->head) {
1146403217f3SAnna Schumaker 		xdr->iov = NULL;
1147403217f3SAnna Schumaker 		xdr->end = xdr->p;
1148403217f3SAnna Schumaker 	}
1149403217f3SAnna Schumaker 
1150703d7521SChuck Lever 	/* XXX: Let's find a way to make this more efficient */
1151403217f3SAnna Schumaker 	while (nbytes) {
1152403217f3SAnna Schumaker 		thislen = xdr->buf->page_len % PAGE_SIZE;
1153403217f3SAnna Schumaker 		thislen = min_t(size_t, nbytes, PAGE_SIZE - thislen);
1154403217f3SAnna Schumaker 
1155403217f3SAnna Schumaker 		p = xdr_reserve_space(xdr, thislen);
1156403217f3SAnna Schumaker 		if (!p)
1157703d7521SChuck Lever 			return -EMSGSIZE;
1158403217f3SAnna Schumaker 
1159403217f3SAnna Schumaker 		nbytes -= thislen;
1160403217f3SAnna Schumaker 	}
1161403217f3SAnna Schumaker 
1162703d7521SChuck Lever 	return 0;
1163403217f3SAnna Schumaker }
1164403217f3SAnna Schumaker EXPORT_SYMBOL_GPL(xdr_reserve_space_vec);
1165403217f3SAnna Schumaker 
11661da177e4SLinus Torvalds /**
11673e19ce76SJ. Bruce Fields  * xdr_truncate_encode - truncate an encode buffer
11683e19ce76SJ. Bruce Fields  * @xdr: pointer to xdr_stream
11693e19ce76SJ. Bruce Fields  * @len: new length of buffer
11703e19ce76SJ. Bruce Fields  *
11713e19ce76SJ. Bruce Fields  * Truncates the xdr stream, so that xdr->buf->len == len,
11723e19ce76SJ. Bruce Fields  * and xdr->p points at offset len from the start of the buffer, and
11733e19ce76SJ. Bruce Fields  * head, tail, and page lengths are adjusted to correspond.
11743e19ce76SJ. Bruce Fields  *
11753e19ce76SJ. Bruce Fields  * If this means moving xdr->p to a different buffer, we assume that
11761cc5213bSRandy Dunlap  * the end pointer should be set to the end of the current page,
11773e19ce76SJ. Bruce Fields  * except in the case of the head buffer when we assume the head
11783e19ce76SJ. Bruce Fields  * buffer's current length represents the end of the available buffer.
11793e19ce76SJ. Bruce Fields  *
11803e19ce76SJ. Bruce Fields  * This is *not* safe to use on a buffer that already has inlined page
11813e19ce76SJ. Bruce Fields  * cache pages (as in a zero-copy server read reply), except for the
11823e19ce76SJ. Bruce Fields  * simple case of truncating from one position in the tail to another.
11833e19ce76SJ. Bruce Fields  *
11843e19ce76SJ. Bruce Fields  */
xdr_truncate_encode(struct xdr_stream * xdr,size_t len)11853e19ce76SJ. Bruce Fields void xdr_truncate_encode(struct xdr_stream *xdr, size_t len)
11863e19ce76SJ. Bruce Fields {
11873e19ce76SJ. Bruce Fields 	struct xdr_buf *buf = xdr->buf;
11883e19ce76SJ. Bruce Fields 	struct kvec *head = buf->head;
11893e19ce76SJ. Bruce Fields 	struct kvec *tail = buf->tail;
11903e19ce76SJ. Bruce Fields 	int fraglen;
119149a068f8SJ. Bruce Fields 	int new;
11923e19ce76SJ. Bruce Fields 
11933e19ce76SJ. Bruce Fields 	if (len > buf->len) {
11943e19ce76SJ. Bruce Fields 		WARN_ON_ONCE(1);
11953e19ce76SJ. Bruce Fields 		return;
11963e19ce76SJ. Bruce Fields 	}
11972825a7f9SJ. Bruce Fields 	xdr_commit_encode(xdr);
11983e19ce76SJ. Bruce Fields 
11993e19ce76SJ. Bruce Fields 	fraglen = min_t(int, buf->len - len, tail->iov_len);
12003e19ce76SJ. Bruce Fields 	tail->iov_len -= fraglen;
12013e19ce76SJ. Bruce Fields 	buf->len -= fraglen;
1202ed38c069SJ. Bruce Fields 	if (tail->iov_len) {
12033e19ce76SJ. Bruce Fields 		xdr->p = tail->iov_base + tail->iov_len;
1204280caac0SJ. Bruce Fields 		WARN_ON_ONCE(!xdr->end);
1205280caac0SJ. Bruce Fields 		WARN_ON_ONCE(!xdr->iov);
12063e19ce76SJ. Bruce Fields 		return;
12073e19ce76SJ. Bruce Fields 	}
12083e19ce76SJ. Bruce Fields 	WARN_ON_ONCE(fraglen);
12093e19ce76SJ. Bruce Fields 	fraglen = min_t(int, buf->len - len, buf->page_len);
12103e19ce76SJ. Bruce Fields 	buf->page_len -= fraglen;
12113e19ce76SJ. Bruce Fields 	buf->len -= fraglen;
12123e19ce76SJ. Bruce Fields 
12133e19ce76SJ. Bruce Fields 	new = buf->page_base + buf->page_len;
121449a068f8SJ. Bruce Fields 
121549a068f8SJ. Bruce Fields 	xdr->page_ptr = buf->pages + (new >> PAGE_SHIFT);
12163e19ce76SJ. Bruce Fields 
1217ed38c069SJ. Bruce Fields 	if (buf->page_len) {
12183e19ce76SJ. Bruce Fields 		xdr->p = page_address(*xdr->page_ptr);
12193e19ce76SJ. Bruce Fields 		xdr->end = (void *)xdr->p + PAGE_SIZE;
12203e19ce76SJ. Bruce Fields 		xdr->p = (void *)xdr->p + (new % PAGE_SIZE);
1221280caac0SJ. Bruce Fields 		WARN_ON_ONCE(xdr->iov);
12223e19ce76SJ. Bruce Fields 		return;
12233e19ce76SJ. Bruce Fields 	}
12245d7a5bcbSFrank Sorenson 	if (fraglen)
12253e19ce76SJ. Bruce Fields 		xdr->end = head->iov_base + head->iov_len;
12263e19ce76SJ. Bruce Fields 	/* (otherwise assume xdr->end is already set) */
12275d7a5bcbSFrank Sorenson 	xdr->page_ptr--;
12283e19ce76SJ. Bruce Fields 	head->iov_len = len;
12293e19ce76SJ. Bruce Fields 	buf->len = len;
12303e19ce76SJ. Bruce Fields 	xdr->p = head->iov_base + head->iov_len;
12313e19ce76SJ. Bruce Fields 	xdr->iov = buf->head;
12323e19ce76SJ. Bruce Fields }
12333e19ce76SJ. Bruce Fields EXPORT_SYMBOL(xdr_truncate_encode);
12343e19ce76SJ. Bruce Fields 
12353e19ce76SJ. Bruce Fields /**
1236b68e4c5cSChuck Lever  * xdr_truncate_decode - Truncate a decoding stream
1237b68e4c5cSChuck Lever  * @xdr: pointer to struct xdr_stream
1238b68e4c5cSChuck Lever  * @len: Number of bytes to remove
1239b68e4c5cSChuck Lever  *
1240b68e4c5cSChuck Lever  */
xdr_truncate_decode(struct xdr_stream * xdr,size_t len)1241b68e4c5cSChuck Lever void xdr_truncate_decode(struct xdr_stream *xdr, size_t len)
1242b68e4c5cSChuck Lever {
1243b68e4c5cSChuck Lever 	unsigned int nbytes = xdr_align_size(len);
1244b68e4c5cSChuck Lever 
1245b68e4c5cSChuck Lever 	xdr->buf->len -= nbytes;
1246b68e4c5cSChuck Lever 	xdr->nwords -= XDR_QUADLEN(nbytes);
1247b68e4c5cSChuck Lever }
1248b68e4c5cSChuck Lever EXPORT_SYMBOL_GPL(xdr_truncate_decode);
1249b68e4c5cSChuck Lever 
1250b68e4c5cSChuck Lever /**
1251db3f58a9SJ. Bruce Fields  * xdr_restrict_buflen - decrease available buffer space
1252db3f58a9SJ. Bruce Fields  * @xdr: pointer to xdr_stream
1253db3f58a9SJ. Bruce Fields  * @newbuflen: new maximum number of bytes available
1254db3f58a9SJ. Bruce Fields  *
1255db3f58a9SJ. Bruce Fields  * Adjust our idea of how much space is available in the buffer.
1256db3f58a9SJ. Bruce Fields  * If we've already used too much space in the buffer, returns -1.
1257db3f58a9SJ. Bruce Fields  * If the available space is already smaller than newbuflen, returns 0
1258db3f58a9SJ. Bruce Fields  * and does nothing.  Otherwise, adjusts xdr->buf->buflen to newbuflen
1259db3f58a9SJ. Bruce Fields  * and ensures xdr->end is set at most offset newbuflen from the start
1260db3f58a9SJ. Bruce Fields  * of the buffer.
1261db3f58a9SJ. Bruce Fields  */
xdr_restrict_buflen(struct xdr_stream * xdr,int newbuflen)1262db3f58a9SJ. Bruce Fields int xdr_restrict_buflen(struct xdr_stream *xdr, int newbuflen)
1263db3f58a9SJ. Bruce Fields {
1264db3f58a9SJ. Bruce Fields 	struct xdr_buf *buf = xdr->buf;
1265db3f58a9SJ. Bruce Fields 	int left_in_this_buf = (void *)xdr->end - (void *)xdr->p;
1266db3f58a9SJ. Bruce Fields 	int end_offset = buf->len + left_in_this_buf;
1267db3f58a9SJ. Bruce Fields 
1268db3f58a9SJ. Bruce Fields 	if (newbuflen < 0 || newbuflen < buf->len)
1269db3f58a9SJ. Bruce Fields 		return -1;
1270db3f58a9SJ. Bruce Fields 	if (newbuflen > buf->buflen)
1271db3f58a9SJ. Bruce Fields 		return 0;
1272db3f58a9SJ. Bruce Fields 	if (newbuflen < end_offset)
1273db3f58a9SJ. Bruce Fields 		xdr->end = (void *)xdr->end + newbuflen - end_offset;
1274db3f58a9SJ. Bruce Fields 	buf->buflen = newbuflen;
1275db3f58a9SJ. Bruce Fields 	return 0;
1276db3f58a9SJ. Bruce Fields }
1277db3f58a9SJ. Bruce Fields EXPORT_SYMBOL(xdr_restrict_buflen);
1278db3f58a9SJ. Bruce Fields 
1279db3f58a9SJ. Bruce Fields /**
12801da177e4SLinus Torvalds  * xdr_write_pages - Insert a list of pages into an XDR buffer for sending
12811da177e4SLinus Torvalds  * @xdr: pointer to xdr_stream
1282ad3d24c5SChuck Lever  * @pages: array of pages to insert
1283ad3d24c5SChuck Lever  * @base: starting offset of first data byte in @pages
1284ad3d24c5SChuck Lever  * @len: number of data bytes in @pages to insert
12851da177e4SLinus Torvalds  *
1286ad3d24c5SChuck Lever  * After the @pages are added, the tail iovec is instantiated pointing to
1287ad3d24c5SChuck Lever  * end of the head buffer, and the stream is set up to encode subsequent
1288ad3d24c5SChuck Lever  * items into the tail.
12891da177e4SLinus Torvalds  */
xdr_write_pages(struct xdr_stream * xdr,struct page ** pages,unsigned int base,unsigned int len)12901da177e4SLinus Torvalds void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int base,
12911da177e4SLinus Torvalds 		 unsigned int len)
12921da177e4SLinus Torvalds {
12931da177e4SLinus Torvalds 	struct xdr_buf *buf = xdr->buf;
1294ad3d24c5SChuck Lever 	struct kvec *tail = buf->tail;
1295ad3d24c5SChuck Lever 
12961da177e4SLinus Torvalds 	buf->pages = pages;
12971da177e4SLinus Torvalds 	buf->page_base = base;
12981da177e4SLinus Torvalds 	buf->page_len = len;
12991da177e4SLinus Torvalds 
1300ad3d24c5SChuck Lever 	tail->iov_base = xdr->p;
1301ad3d24c5SChuck Lever 	tail->iov_len = 0;
1302ad3d24c5SChuck Lever 	xdr->iov = tail;
13031da177e4SLinus Torvalds 
13041da177e4SLinus Torvalds 	if (len & 3) {
13051da177e4SLinus Torvalds 		unsigned int pad = 4 - (len & 3);
13061da177e4SLinus Torvalds 
13071da177e4SLinus Torvalds 		BUG_ON(xdr->p >= xdr->end);
1308ad3d24c5SChuck Lever 		tail->iov_base = (char *)xdr->p + (len & 3);
1309ad3d24c5SChuck Lever 		tail->iov_len += pad;
13101da177e4SLinus Torvalds 		len += pad;
13111da177e4SLinus Torvalds 		*xdr->p++ = 0;
13121da177e4SLinus Torvalds 	}
13131da177e4SLinus Torvalds 	buf->buflen += len;
13141da177e4SLinus Torvalds 	buf->len += len;
13151da177e4SLinus Torvalds }
1316468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_write_pages);
13171da177e4SLinus Torvalds 
xdr_set_iov(struct xdr_stream * xdr,struct kvec * iov,unsigned int base,unsigned int len)13188d86e373STrond Myklebust static unsigned int xdr_set_iov(struct xdr_stream *xdr, struct kvec *iov,
13198d86e373STrond Myklebust 				unsigned int base, unsigned int len)
13206650239aSTrond Myklebust {
13216650239aSTrond Myklebust 	if (len > iov->iov_len)
13226650239aSTrond Myklebust 		len = iov->iov_len;
13238d86e373STrond Myklebust 	if (unlikely(base > len))
13248d86e373STrond Myklebust 		base = len;
13258d86e373STrond Myklebust 	xdr->p = (__be32*)(iov->iov_base + base);
13266650239aSTrond Myklebust 	xdr->end = (__be32*)(iov->iov_base + len);
13276650239aSTrond Myklebust 	xdr->iov = iov;
13286650239aSTrond Myklebust 	xdr->page_ptr = NULL;
13298d86e373STrond Myklebust 	return len - base;
13306650239aSTrond Myklebust }
13316650239aSTrond Myklebust 
xdr_set_tail_base(struct xdr_stream * xdr,unsigned int base,unsigned int len)13325a5f1c2cSTrond Myklebust static unsigned int xdr_set_tail_base(struct xdr_stream *xdr,
13335a5f1c2cSTrond Myklebust 				      unsigned int base, unsigned int len)
13345a5f1c2cSTrond Myklebust {
13355a5f1c2cSTrond Myklebust 	struct xdr_buf *buf = xdr->buf;
13365a5f1c2cSTrond Myklebust 
13375a5f1c2cSTrond Myklebust 	xdr_stream_set_pos(xdr, base + buf->page_len + buf->head->iov_len);
13385a5f1c2cSTrond Myklebust 	return xdr_set_iov(xdr, buf->tail, base, len);
13395a5f1c2cSTrond Myklebust }
13405a5f1c2cSTrond Myklebust 
xdr_stream_unmap_current_page(struct xdr_stream * xdr)134161182c79SAnna Schumaker static void xdr_stream_unmap_current_page(struct xdr_stream *xdr)
134261182c79SAnna Schumaker {
134361182c79SAnna Schumaker 	if (xdr->page_kaddr) {
134461182c79SAnna Schumaker 		kunmap_local(xdr->page_kaddr);
134561182c79SAnna Schumaker 		xdr->page_kaddr = NULL;
134661182c79SAnna Schumaker 	}
134761182c79SAnna Schumaker }
134861182c79SAnna Schumaker 
xdr_set_page_base(struct xdr_stream * xdr,unsigned int base,unsigned int len)13498d86e373STrond Myklebust static unsigned int xdr_set_page_base(struct xdr_stream *xdr,
13506650239aSTrond Myklebust 				      unsigned int base, unsigned int len)
13516650239aSTrond Myklebust {
13526650239aSTrond Myklebust 	unsigned int pgnr;
13536650239aSTrond Myklebust 	unsigned int maxlen;
13546650239aSTrond Myklebust 	unsigned int pgoff;
13556650239aSTrond Myklebust 	unsigned int pgend;
13566650239aSTrond Myklebust 	void *kaddr;
13576650239aSTrond Myklebust 
13586650239aSTrond Myklebust 	maxlen = xdr->buf->page_len;
13596d1c0f3dSAnna Schumaker 	if (base >= maxlen)
13606d1c0f3dSAnna Schumaker 		return 0;
13616d1c0f3dSAnna Schumaker 	else
13626650239aSTrond Myklebust 		maxlen -= base;
13636650239aSTrond Myklebust 	if (len > maxlen)
13646650239aSTrond Myklebust 		len = maxlen;
13656650239aSTrond Myklebust 
136661182c79SAnna Schumaker 	xdr_stream_unmap_current_page(xdr);
13675a5f1c2cSTrond Myklebust 	xdr_stream_page_set_pos(xdr, base);
13686650239aSTrond Myklebust 	base += xdr->buf->page_base;
13696650239aSTrond Myklebust 
13706650239aSTrond Myklebust 	pgnr = base >> PAGE_SHIFT;
13716650239aSTrond Myklebust 	xdr->page_ptr = &xdr->buf->pages[pgnr];
137261182c79SAnna Schumaker 
137361182c79SAnna Schumaker 	if (PageHighMem(*xdr->page_ptr)) {
137461182c79SAnna Schumaker 		xdr->page_kaddr = kmap_local_page(*xdr->page_ptr);
137561182c79SAnna Schumaker 		kaddr = xdr->page_kaddr;
137661182c79SAnna Schumaker 	} else
13776650239aSTrond Myklebust 		kaddr = page_address(*xdr->page_ptr);
13786650239aSTrond Myklebust 
13796650239aSTrond Myklebust 	pgoff = base & ~PAGE_MASK;
13806650239aSTrond Myklebust 	xdr->p = (__be32*)(kaddr + pgoff);
13816650239aSTrond Myklebust 
13826650239aSTrond Myklebust 	pgend = pgoff + len;
13836650239aSTrond Myklebust 	if (pgend > PAGE_SIZE)
13846650239aSTrond Myklebust 		pgend = PAGE_SIZE;
13856650239aSTrond Myklebust 	xdr->end = (__be32*)(kaddr + pgend);
13866650239aSTrond Myklebust 	xdr->iov = NULL;
13878d86e373STrond Myklebust 	return len;
13886650239aSTrond Myklebust }
13896650239aSTrond Myklebust 
xdr_set_page(struct xdr_stream * xdr,unsigned int base,unsigned int len)1390f7d61ee4SAnna Schumaker static void xdr_set_page(struct xdr_stream *xdr, unsigned int base,
1391f7d61ee4SAnna Schumaker 			 unsigned int len)
1392f7d61ee4SAnna Schumaker {
13930279024fSTrond Myklebust 	if (xdr_set_page_base(xdr, base, len) == 0) {
13940279024fSTrond Myklebust 		base -= xdr->buf->page_len;
13955a5f1c2cSTrond Myklebust 		xdr_set_tail_base(xdr, base, len);
13960279024fSTrond Myklebust 	}
1397f7d61ee4SAnna Schumaker }
1398f7d61ee4SAnna Schumaker 
xdr_set_next_page(struct xdr_stream * xdr)13996650239aSTrond Myklebust static void xdr_set_next_page(struct xdr_stream *xdr)
14006650239aSTrond Myklebust {
14016650239aSTrond Myklebust 	unsigned int newbase;
14026650239aSTrond Myklebust 
14036650239aSTrond Myklebust 	newbase = (1 + xdr->page_ptr - xdr->buf->pages) << PAGE_SHIFT;
14046650239aSTrond Myklebust 	newbase -= xdr->buf->page_base;
14050279024fSTrond Myklebust 	if (newbase < xdr->buf->page_len)
14060279024fSTrond Myklebust 		xdr_set_page_base(xdr, newbase, xdr_stream_remaining(xdr));
14070279024fSTrond Myklebust 	else
14085a5f1c2cSTrond Myklebust 		xdr_set_tail_base(xdr, 0, xdr_stream_remaining(xdr));
14096650239aSTrond Myklebust }
14106650239aSTrond Myklebust 
xdr_set_next_buffer(struct xdr_stream * xdr)14116650239aSTrond Myklebust static bool xdr_set_next_buffer(struct xdr_stream *xdr)
14126650239aSTrond Myklebust {
14136650239aSTrond Myklebust 	if (xdr->page_ptr != NULL)
14146650239aSTrond Myklebust 		xdr_set_next_page(xdr);
14150279024fSTrond Myklebust 	else if (xdr->iov == xdr->buf->head)
14160279024fSTrond Myklebust 		xdr_set_page(xdr, 0, xdr_stream_remaining(xdr));
14176650239aSTrond Myklebust 	return xdr->p != xdr->end;
14186650239aSTrond Myklebust }
14196650239aSTrond Myklebust 
14201da177e4SLinus Torvalds /**
14211da177e4SLinus Torvalds  * xdr_init_decode - Initialize an xdr_stream for decoding data.
14221da177e4SLinus Torvalds  * @xdr: pointer to xdr_stream struct
14231da177e4SLinus Torvalds  * @buf: pointer to XDR buffer from which to decode data
14241da177e4SLinus Torvalds  * @p: current pointer inside XDR buffer
14250ccc61b1SChuck Lever  * @rqst: pointer to controlling rpc_rqst, for debugging
14261da177e4SLinus Torvalds  */
xdr_init_decode(struct xdr_stream * xdr,struct xdr_buf * buf,__be32 * p,struct rpc_rqst * rqst)14270ccc61b1SChuck Lever void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p,
14280ccc61b1SChuck Lever 		     struct rpc_rqst *rqst)
14291da177e4SLinus Torvalds {
14301da177e4SLinus Torvalds 	xdr->buf = buf;
143161182c79SAnna Schumaker 	xdr->page_kaddr = NULL;
14320ae4c3e8SChuck Lever 	xdr_reset_scratch_buffer(xdr);
1433bfeea1dcSTrond Myklebust 	xdr->nwords = XDR_QUADLEN(buf->len);
14348d86e373STrond Myklebust 	if (xdr_set_iov(xdr, buf->head, 0, buf->len) == 0 &&
14358d86e373STrond Myklebust 	    xdr_set_page_base(xdr, 0, buf->len) == 0)
14368d86e373STrond Myklebust 		xdr_set_iov(xdr, buf->tail, 0, buf->len);
1437bfeea1dcSTrond Myklebust 	if (p != NULL && p > xdr->p && xdr->end >= p) {
1438bfeea1dcSTrond Myklebust 		xdr->nwords -= p - xdr->p;
14391537693cSTrond Myklebust 		xdr->p = p;
14401da177e4SLinus Torvalds 	}
14410ccc61b1SChuck Lever 	xdr->rqst = rqst;
14421da177e4SLinus Torvalds }
1443468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_init_decode);
14441da177e4SLinus Torvalds 
1445f7da7a12SBenny Halevy /**
14467ecce75fSChuck Lever  * xdr_init_decode_pages - Initialize an xdr_stream for decoding into pages
1447f7da7a12SBenny Halevy  * @xdr: pointer to xdr_stream struct
1448f7da7a12SBenny Halevy  * @buf: pointer to XDR buffer from which to decode data
1449f7da7a12SBenny Halevy  * @pages: list of pages to decode into
1450f7da7a12SBenny Halevy  * @len: length in bytes of buffer in pages
1451f7da7a12SBenny Halevy  */
xdr_init_decode_pages(struct xdr_stream * xdr,struct xdr_buf * buf,struct page ** pages,unsigned int len)1452f7da7a12SBenny Halevy void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf,
1453f7da7a12SBenny Halevy 			   struct page **pages, unsigned int len)
1454f7da7a12SBenny Halevy {
1455f7da7a12SBenny Halevy 	memset(buf, 0, sizeof(*buf));
1456f7da7a12SBenny Halevy 	buf->pages =  pages;
1457f7da7a12SBenny Halevy 	buf->page_len =  len;
1458f7da7a12SBenny Halevy 	buf->buflen =  len;
1459f7da7a12SBenny Halevy 	buf->len = len;
14600ccc61b1SChuck Lever 	xdr_init_decode(xdr, buf, NULL, NULL);
1461f7da7a12SBenny Halevy }
1462f7da7a12SBenny Halevy EXPORT_SYMBOL_GPL(xdr_init_decode_pages);
1463f7da7a12SBenny Halevy 
146461182c79SAnna Schumaker /**
146561182c79SAnna Schumaker  * xdr_finish_decode - Clean up the xdr_stream after decoding data.
146661182c79SAnna Schumaker  * @xdr: pointer to xdr_stream struct
146761182c79SAnna Schumaker  */
xdr_finish_decode(struct xdr_stream * xdr)146861182c79SAnna Schumaker void xdr_finish_decode(struct xdr_stream *xdr)
146961182c79SAnna Schumaker {
147061182c79SAnna Schumaker 	xdr_stream_unmap_current_page(xdr);
147161182c79SAnna Schumaker }
147261182c79SAnna Schumaker EXPORT_SYMBOL(xdr_finish_decode);
147361182c79SAnna Schumaker 
__xdr_inline_decode(struct xdr_stream * xdr,size_t nbytes)14746650239aSTrond Myklebust static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
1475ba8e452aSTrond Myklebust {
1476bfeea1dcSTrond Myklebust 	unsigned int nwords = XDR_QUADLEN(nbytes);
1477ba8e452aSTrond Myklebust 	__be32 *p = xdr->p;
1478bfeea1dcSTrond Myklebust 	__be32 *q = p + nwords;
1479ba8e452aSTrond Myklebust 
1480bfeea1dcSTrond Myklebust 	if (unlikely(nwords > xdr->nwords || q > xdr->end || q < p))
1481ba8e452aSTrond Myklebust 		return NULL;
14826650239aSTrond Myklebust 	xdr->p = q;
1483bfeea1dcSTrond Myklebust 	xdr->nwords -= nwords;
1484ba8e452aSTrond Myklebust 	return p;
1485ba8e452aSTrond Myklebust }
1486ba8e452aSTrond Myklebust 
xdr_copy_to_scratch(struct xdr_stream * xdr,size_t nbytes)14876650239aSTrond Myklebust static __be32 *xdr_copy_to_scratch(struct xdr_stream *xdr, size_t nbytes)
14886650239aSTrond Myklebust {
14896650239aSTrond Myklebust 	__be32 *p;
1490ace0e14fSTrond Myklebust 	char *cpdest = xdr->scratch.iov_base;
14916650239aSTrond Myklebust 	size_t cplen = (char *)xdr->end - (char *)xdr->p;
14926650239aSTrond Myklebust 
14936650239aSTrond Myklebust 	if (nbytes > xdr->scratch.iov_len)
14945582863fSChuck Lever 		goto out_overflow;
1495ace0e14fSTrond Myklebust 	p = __xdr_inline_decode(xdr, cplen);
1496ace0e14fSTrond Myklebust 	if (p == NULL)
1497ace0e14fSTrond Myklebust 		return NULL;
1498ace0e14fSTrond Myklebust 	memcpy(cpdest, p, cplen);
14995582863fSChuck Lever 	if (!xdr_set_next_buffer(xdr))
15005582863fSChuck Lever 		goto out_overflow;
15016650239aSTrond Myklebust 	cpdest += cplen;
15026650239aSTrond Myklebust 	nbytes -= cplen;
15036650239aSTrond Myklebust 	p = __xdr_inline_decode(xdr, nbytes);
15046650239aSTrond Myklebust 	if (p == NULL)
15056650239aSTrond Myklebust 		return NULL;
15066650239aSTrond Myklebust 	memcpy(cpdest, p, nbytes);
15076650239aSTrond Myklebust 	return xdr->scratch.iov_base;
15085582863fSChuck Lever out_overflow:
15095582863fSChuck Lever 	trace_rpc_xdr_overflow(xdr, nbytes);
15105582863fSChuck Lever 	return NULL;
15116650239aSTrond Myklebust }
15126650239aSTrond Myklebust 
15136650239aSTrond Myklebust /**
15146650239aSTrond Myklebust  * xdr_inline_decode - Retrieve XDR data to decode
15151da177e4SLinus Torvalds  * @xdr: pointer to xdr_stream struct
15161da177e4SLinus Torvalds  * @nbytes: number of bytes of data to decode
15171da177e4SLinus Torvalds  *
15181da177e4SLinus Torvalds  * Check if the input buffer is long enough to enable us to decode
15191da177e4SLinus Torvalds  * 'nbytes' more bytes of data starting at the current position.
15201da177e4SLinus Torvalds  * If so return the current pointer, then update the current
15211da177e4SLinus Torvalds  * pointer position.
15221da177e4SLinus Torvalds  */
xdr_inline_decode(struct xdr_stream * xdr,size_t nbytes)1523d8ed029dSAlexey Dobriyan __be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
15241da177e4SLinus Torvalds {
15256650239aSTrond Myklebust 	__be32 *p;
15261da177e4SLinus Torvalds 
15275582863fSChuck Lever 	if (unlikely(nbytes == 0))
15286650239aSTrond Myklebust 		return xdr->p;
15296650239aSTrond Myklebust 	if (xdr->p == xdr->end && !xdr_set_next_buffer(xdr))
15305582863fSChuck Lever 		goto out_overflow;
15316650239aSTrond Myklebust 	p = __xdr_inline_decode(xdr, nbytes);
15326650239aSTrond Myklebust 	if (p != NULL)
15331da177e4SLinus Torvalds 		return p;
15346650239aSTrond Myklebust 	return xdr_copy_to_scratch(xdr, nbytes);
15355582863fSChuck Lever out_overflow:
15365582863fSChuck Lever 	trace_rpc_xdr_overflow(xdr, nbytes);
15375582863fSChuck Lever 	return NULL;
15381da177e4SLinus Torvalds }
1539468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_inline_decode);
15401da177e4SLinus Torvalds 
xdr_realign_pages(struct xdr_stream * xdr)154106216ecbSAnna Schumaker static void xdr_realign_pages(struct xdr_stream *xdr)
154206216ecbSAnna Schumaker {
154306216ecbSAnna Schumaker 	struct xdr_buf *buf = xdr->buf;
154406216ecbSAnna Schumaker 	struct kvec *iov = buf->head;
154506216ecbSAnna Schumaker 	unsigned int cur = xdr_stream_pos(xdr);
15466707fbd7STrond Myklebust 	unsigned int copied;
154706216ecbSAnna Schumaker 
154806216ecbSAnna Schumaker 	/* Realign pages to current pointer position */
154906216ecbSAnna Schumaker 	if (iov->iov_len > cur) {
15506707fbd7STrond Myklebust 		copied = xdr_shrink_bufhead(buf, cur);
15516707fbd7STrond Myklebust 		trace_rpc_xdr_alignment(xdr, cur, copied);
15525a5f1c2cSTrond Myklebust 		xdr_set_page(xdr, 0, buf->page_len);
155306216ecbSAnna Schumaker 	}
155406216ecbSAnna Schumaker }
155506216ecbSAnna Schumaker 
xdr_align_pages(struct xdr_stream * xdr,unsigned int len)15563994ee6fSTrond Myklebust static unsigned int xdr_align_pages(struct xdr_stream *xdr, unsigned int len)
15571da177e4SLinus Torvalds {
15581da177e4SLinus Torvalds 	struct xdr_buf *buf = xdr->buf;
1559bfeea1dcSTrond Myklebust 	unsigned int nwords = XDR_QUADLEN(len);
1560c4f2f591STrond Myklebust 	unsigned int copied;
15611da177e4SLinus Torvalds 
1562bfeea1dcSTrond Myklebust 	if (xdr->nwords == 0)
1563c337d365STrond Myklebust 		return 0;
15647be9cea3SChuck Lever 
156506216ecbSAnna Schumaker 	xdr_realign_pages(xdr);
1566bfeea1dcSTrond Myklebust 	if (nwords > xdr->nwords) {
1567bfeea1dcSTrond Myklebust 		nwords = xdr->nwords;
1568bfeea1dcSTrond Myklebust 		len = nwords << 2;
1569bfeea1dcSTrond Myklebust 	}
1570a11a2bf4STrond Myklebust 	if (buf->page_len <= len)
15718a9a8b83STrond Myklebust 		len = buf->page_len;
1572a11a2bf4STrond Myklebust 	else if (nwords < xdr->nwords) {
1573a11a2bf4STrond Myklebust 		/* Truncate page data and move it into the tail */
1574c4f2f591STrond Myklebust 		copied = xdr_shrink_pagelen(buf, len);
1575c4f2f591STrond Myklebust 		trace_rpc_xdr_alignment(xdr, len, copied);
1576a11a2bf4STrond Myklebust 	}
15773994ee6fSTrond Myklebust 	return len;
15783994ee6fSTrond Myklebust }
1579bd00f84bSTrond Myklebust 
15801da177e4SLinus Torvalds /**
15811d973166STrond Myklebust  * xdr_read_pages - align page-based XDR data to current pointer position
15821da177e4SLinus Torvalds  * @xdr: pointer to xdr_stream struct
15831da177e4SLinus Torvalds  * @len: number of bytes of page data
15841da177e4SLinus Torvalds  *
15851da177e4SLinus Torvalds  * Moves data beyond the current pointer position from the XDR head[] buffer
15861d973166STrond Myklebust  * into the page list. Any data that lies beyond current position + @len
15871d973166STrond Myklebust  * bytes is moved into the XDR tail[]. The xdr_stream current position is
15881d973166STrond Myklebust  * then advanced past that data to align to the next XDR object in the tail.
15893994ee6fSTrond Myklebust  *
15903994ee6fSTrond Myklebust  * Returns the number of XDR encoded bytes now contained in the pages
15911da177e4SLinus Torvalds  */
xdr_read_pages(struct xdr_stream * xdr,unsigned int len)15923994ee6fSTrond Myklebust unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
15931da177e4SLinus Torvalds {
15941d973166STrond Myklebust 	unsigned int nwords = XDR_QUADLEN(len);
15951d973166STrond Myklebust 	unsigned int base, end, pglen;
15961da177e4SLinus Torvalds 
15971d973166STrond Myklebust 	pglen = xdr_align_pages(xdr, nwords << 2);
15981d973166STrond Myklebust 	if (pglen == 0)
15993994ee6fSTrond Myklebust 		return 0;
1600bd00f84bSTrond Myklebust 
16011d973166STrond Myklebust 	base = (nwords << 2) - pglen;
16021d973166STrond Myklebust 	end = xdr_stream_remaining(xdr) - pglen;
16031d973166STrond Myklebust 
16045a5f1c2cSTrond Myklebust 	xdr_set_tail_base(xdr, base, end);
16051d973166STrond Myklebust 	return len <= pglen ? len : pglen;
16061da177e4SLinus Torvalds }
1607468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_read_pages);
16081da177e4SLinus Torvalds 
16097c4cd5f4SAnna Schumaker /**
16107c4cd5f4SAnna Schumaker  * xdr_set_pagelen - Sets the length of the XDR pages
16117c4cd5f4SAnna Schumaker  * @xdr: pointer to xdr_stream struct
16127c4cd5f4SAnna Schumaker  * @len: new length of the XDR page data
16137c4cd5f4SAnna Schumaker  *
16147c4cd5f4SAnna Schumaker  * Either grows or shrinks the length of the xdr pages by setting pagelen to
16157c4cd5f4SAnna Schumaker  * @len bytes. When shrinking, any extra data is moved into buf->tail, whereas
16167c4cd5f4SAnna Schumaker  * when growing any data beyond the current pointer is moved into the tail.
16177c4cd5f4SAnna Schumaker  *
16187c4cd5f4SAnna Schumaker  * Returns True if the operation was successful, and False otherwise.
16197c4cd5f4SAnna Schumaker  */
xdr_set_pagelen(struct xdr_stream * xdr,unsigned int len)16207c4cd5f4SAnna Schumaker void xdr_set_pagelen(struct xdr_stream *xdr, unsigned int len)
16217c4cd5f4SAnna Schumaker {
16227c4cd5f4SAnna Schumaker 	struct xdr_buf *buf = xdr->buf;
16237c4cd5f4SAnna Schumaker 	size_t remaining = xdr_stream_remaining(xdr);
16247c4cd5f4SAnna Schumaker 	size_t base = 0;
16257c4cd5f4SAnna Schumaker 
16267c4cd5f4SAnna Schumaker 	if (len < buf->page_len) {
16277c4cd5f4SAnna Schumaker 		base = buf->page_len - len;
16287c4cd5f4SAnna Schumaker 		xdr_shrink_pagelen(buf, len);
16297c4cd5f4SAnna Schumaker 	} else {
16307c4cd5f4SAnna Schumaker 		xdr_buf_head_shift_right(buf, xdr_stream_pos(xdr),
16317c4cd5f4SAnna Schumaker 					 buf->page_len, remaining);
16327c4cd5f4SAnna Schumaker 		if (len > buf->page_len)
16337c4cd5f4SAnna Schumaker 			xdr_buf_try_expand(buf, len - buf->page_len);
16347c4cd5f4SAnna Schumaker 	}
16357c4cd5f4SAnna Schumaker 	xdr_set_tail_base(xdr, base, remaining);
16367c4cd5f4SAnna Schumaker }
16377c4cd5f4SAnna Schumaker EXPORT_SYMBOL_GPL(xdr_set_pagelen);
16387c4cd5f4SAnna Schumaker 
16398b23ea7bSTrond Myklebust /**
16408b23ea7bSTrond Myklebust  * xdr_enter_page - decode data from the XDR page
16418b23ea7bSTrond Myklebust  * @xdr: pointer to xdr_stream struct
16428b23ea7bSTrond Myklebust  * @len: number of bytes of page data
16438b23ea7bSTrond Myklebust  *
16448b23ea7bSTrond Myklebust  * Moves data beyond the current pointer position from the XDR head[] buffer
16458b23ea7bSTrond Myklebust  * into the page list. Any data that lies beyond current position + "len"
16468b23ea7bSTrond Myklebust  * bytes is moved into the XDR tail[]. The current pointer is then
16478b23ea7bSTrond Myklebust  * repositioned at the beginning of the first XDR page.
16488b23ea7bSTrond Myklebust  */
xdr_enter_page(struct xdr_stream * xdr,unsigned int len)16498b23ea7bSTrond Myklebust void xdr_enter_page(struct xdr_stream *xdr, unsigned int len)
16508b23ea7bSTrond Myklebust {
1651f8bb7f08STrond Myklebust 	len = xdr_align_pages(xdr, len);
16528b23ea7bSTrond Myklebust 	/*
16538b23ea7bSTrond Myklebust 	 * Position current pointer at beginning of tail, and
16548b23ea7bSTrond Myklebust 	 * set remaining message length.
16558b23ea7bSTrond Myklebust 	 */
1656f8bb7f08STrond Myklebust 	if (len != 0)
16576650239aSTrond Myklebust 		xdr_set_page_base(xdr, 0, len);
16588b23ea7bSTrond Myklebust }
1659468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_enter_page);
16608b23ea7bSTrond Myklebust 
1661c2bd2c0aSJulia Lawall static const struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0};
16621da177e4SLinus Torvalds 
xdr_buf_from_iov(const struct kvec * iov,struct xdr_buf * buf)1663f8d0e60fSTrond Myklebust void xdr_buf_from_iov(const struct kvec *iov, struct xdr_buf *buf)
16641da177e4SLinus Torvalds {
16651da177e4SLinus Torvalds 	buf->head[0] = *iov;
16661da177e4SLinus Torvalds 	buf->tail[0] = empty_iov;
16671da177e4SLinus Torvalds 	buf->page_len = 0;
16681da177e4SLinus Torvalds 	buf->buflen = buf->len = iov->iov_len;
16691da177e4SLinus Torvalds }
1670468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_buf_from_iov);
16711da177e4SLinus Torvalds 
1672de4aee2eSJ. Bruce Fields /**
1673de4aee2eSJ. Bruce Fields  * xdr_buf_subsegment - set subbuf to a portion of buf
1674de4aee2eSJ. Bruce Fields  * @buf: an xdr buffer
1675de4aee2eSJ. Bruce Fields  * @subbuf: the result buffer
1676de4aee2eSJ. Bruce Fields  * @base: beginning of range in bytes
1677de4aee2eSJ. Bruce Fields  * @len: length of range in bytes
1678de4aee2eSJ. Bruce Fields  *
1679de4aee2eSJ. Bruce Fields  * sets @subbuf to an xdr buffer representing the portion of @buf of
1680de4aee2eSJ. Bruce Fields  * length @len starting at offset @base.
1681de4aee2eSJ. Bruce Fields  *
1682de4aee2eSJ. Bruce Fields  * @buf and @subbuf may be pointers to the same struct xdr_buf.
1683de4aee2eSJ. Bruce Fields  *
1684b8ab2a6fSChuck Lever  * Returns -1 if base or length are out of bounds.
1685de4aee2eSJ. Bruce Fields  */
xdr_buf_subsegment(const struct xdr_buf * buf,struct xdr_buf * subbuf,unsigned int base,unsigned int len)16865a7e7026SChuck Lever int xdr_buf_subsegment(const struct xdr_buf *buf, struct xdr_buf *subbuf,
16871e78957eSTrond Myklebust 		       unsigned int base, unsigned int len)
16881da177e4SLinus Torvalds {
16891da177e4SLinus Torvalds 	subbuf->buflen = subbuf->len = len;
16901e78957eSTrond Myklebust 	if (base < buf->head[0].iov_len) {
16911e78957eSTrond Myklebust 		subbuf->head[0].iov_base = buf->head[0].iov_base + base;
16921e78957eSTrond Myklebust 		subbuf->head[0].iov_len = min_t(unsigned int, len,
16931e78957eSTrond Myklebust 						buf->head[0].iov_len - base);
16941e78957eSTrond Myklebust 		len -= subbuf->head[0].iov_len;
16951e78957eSTrond Myklebust 		base = 0;
16961e78957eSTrond Myklebust 	} else {
16971e78957eSTrond Myklebust 		base -= buf->head[0].iov_len;
169889a3c9f5SChuck Lever 		subbuf->head[0].iov_base = buf->head[0].iov_base;
1699de4aee2eSJ. Bruce Fields 		subbuf->head[0].iov_len = 0;
17001e78957eSTrond Myklebust 	}
17011da177e4SLinus Torvalds 
17021da177e4SLinus Torvalds 	if (base < buf->page_len) {
17031e78957eSTrond Myklebust 		subbuf->page_len = min(buf->page_len - base, len);
17041e78957eSTrond Myklebust 		base += buf->page_base;
170509cbfeafSKirill A. Shutemov 		subbuf->page_base = base & ~PAGE_MASK;
170609cbfeafSKirill A. Shutemov 		subbuf->pages = &buf->pages[base >> PAGE_SHIFT];
17071da177e4SLinus Torvalds 		len -= subbuf->page_len;
17081da177e4SLinus Torvalds 		base = 0;
17091da177e4SLinus Torvalds 	} else {
17101da177e4SLinus Torvalds 		base -= buf->page_len;
171189a3c9f5SChuck Lever 		subbuf->pages = buf->pages;
171289a3c9f5SChuck Lever 		subbuf->page_base = 0;
17131da177e4SLinus Torvalds 		subbuf->page_len = 0;
17141da177e4SLinus Torvalds 	}
17151da177e4SLinus Torvalds 
17161e78957eSTrond Myklebust 	if (base < buf->tail[0].iov_len) {
17171e78957eSTrond Myklebust 		subbuf->tail[0].iov_base = buf->tail[0].iov_base + base;
17181e78957eSTrond Myklebust 		subbuf->tail[0].iov_len = min_t(unsigned int, len,
17191e78957eSTrond Myklebust 						buf->tail[0].iov_len - base);
17201e78957eSTrond Myklebust 		len -= subbuf->tail[0].iov_len;
17211e78957eSTrond Myklebust 		base = 0;
17221e78957eSTrond Myklebust 	} else {
17231e78957eSTrond Myklebust 		base -= buf->tail[0].iov_len;
172489a3c9f5SChuck Lever 		subbuf->tail[0].iov_base = buf->tail[0].iov_base;
1725de4aee2eSJ. Bruce Fields 		subbuf->tail[0].iov_len = 0;
17261e78957eSTrond Myklebust 	}
17271e78957eSTrond Myklebust 
17281da177e4SLinus Torvalds 	if (base || len)
17291da177e4SLinus Torvalds 		return -1;
17301da177e4SLinus Torvalds 	return 0;
17311da177e4SLinus Torvalds }
1732468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_buf_subsegment);
17331da177e4SLinus Torvalds 
17340a8e7b7dSChuck Lever /**
1735c1346a12SChuck Lever  * xdr_stream_subsegment - set @subbuf to a portion of @xdr
1736c1346a12SChuck Lever  * @xdr: an xdr_stream set up for decoding
1737c1346a12SChuck Lever  * @subbuf: the result buffer
1738c1346a12SChuck Lever  * @nbytes: length of @xdr to extract, in bytes
1739c1346a12SChuck Lever  *
1740c1346a12SChuck Lever  * Sets up @subbuf to represent a portion of @xdr. The portion
1741c1346a12SChuck Lever  * starts at the current offset in @xdr, and extends for a length
1742c1346a12SChuck Lever  * of @nbytes. If this is successful, @xdr is advanced to the next
1743f49b68ddSChuck Lever  * XDR data item following that portion.
1744c1346a12SChuck Lever  *
1745c1346a12SChuck Lever  * Return values:
1746c1346a12SChuck Lever  *   %true: @subbuf has been initialized, and @xdr has been advanced.
1747c1346a12SChuck Lever  *   %false: a bounds error has occurred
1748c1346a12SChuck Lever  */
xdr_stream_subsegment(struct xdr_stream * xdr,struct xdr_buf * subbuf,unsigned int nbytes)1749c1346a12SChuck Lever bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf,
1750c1346a12SChuck Lever 			   unsigned int nbytes)
1751c1346a12SChuck Lever {
1752f49b68ddSChuck Lever 	unsigned int start = xdr_stream_pos(xdr);
1753f49b68ddSChuck Lever 	unsigned int remaining, len;
1754c1346a12SChuck Lever 
1755f49b68ddSChuck Lever 	/* Extract @subbuf and bounds-check the fn arguments */
1756f49b68ddSChuck Lever 	if (xdr_buf_subsegment(xdr->buf, subbuf, start, nbytes))
1757c1346a12SChuck Lever 		return false;
1758c1346a12SChuck Lever 
1759f49b68ddSChuck Lever 	/* Advance @xdr by @nbytes */
1760f49b68ddSChuck Lever 	for (remaining = nbytes; remaining;) {
1761c1346a12SChuck Lever 		if (xdr->p == xdr->end && !xdr_set_next_buffer(xdr))
1762c1346a12SChuck Lever 			return false;
1763c1346a12SChuck Lever 
1764f49b68ddSChuck Lever 		len = (char *)xdr->end - (char *)xdr->p;
1765f49b68ddSChuck Lever 		if (remaining <= len) {
1766f49b68ddSChuck Lever 			xdr->p = (__be32 *)((char *)xdr->p +
1767f49b68ddSChuck Lever 					(remaining + xdr_pad_size(nbytes)));
1768f49b68ddSChuck Lever 			break;
1769c1346a12SChuck Lever 		}
1770c1346a12SChuck Lever 
1771f49b68ddSChuck Lever 		xdr->p = (__be32 *)((char *)xdr->p + len);
1772f49b68ddSChuck Lever 		xdr->end = xdr->p;
1773f49b68ddSChuck Lever 		remaining -= len;
1774f49b68ddSChuck Lever 	}
1775f49b68ddSChuck Lever 
1776f49b68ddSChuck Lever 	xdr_stream_set_pos(xdr, start + nbytes);
1777c1346a12SChuck Lever 	return true;
1778c1346a12SChuck Lever }
1779c1346a12SChuck Lever EXPORT_SYMBOL_GPL(xdr_stream_subsegment);
1780c1346a12SChuck Lever 
1781c1346a12SChuck Lever /**
17824f5f3b60SAnna Schumaker  * xdr_stream_move_subsegment - Move part of a stream to another position
17834f5f3b60SAnna Schumaker  * @xdr: the source xdr_stream
17844f5f3b60SAnna Schumaker  * @offset: the source offset of the segment
17854f5f3b60SAnna Schumaker  * @target: the target offset of the segment
17864f5f3b60SAnna Schumaker  * @length: the number of bytes to move
17874f5f3b60SAnna Schumaker  *
17884f5f3b60SAnna Schumaker  * Moves @length bytes from @offset to @target in the xdr_stream, overwriting
17894f5f3b60SAnna Schumaker  * anything in its space. Returns the number of bytes in the segment.
17904f5f3b60SAnna Schumaker  */
xdr_stream_move_subsegment(struct xdr_stream * xdr,unsigned int offset,unsigned int target,unsigned int length)17914f5f3b60SAnna Schumaker unsigned int xdr_stream_move_subsegment(struct xdr_stream *xdr, unsigned int offset,
17924f5f3b60SAnna Schumaker 					unsigned int target, unsigned int length)
17934f5f3b60SAnna Schumaker {
17944f5f3b60SAnna Schumaker 	struct xdr_buf buf;
17954f5f3b60SAnna Schumaker 	unsigned int shift;
17964f5f3b60SAnna Schumaker 
17974f5f3b60SAnna Schumaker 	if (offset < target) {
17984f5f3b60SAnna Schumaker 		shift = target - offset;
17994f5f3b60SAnna Schumaker 		if (xdr_buf_subsegment(xdr->buf, &buf, offset, shift + length) < 0)
18004f5f3b60SAnna Schumaker 			return 0;
18014f5f3b60SAnna Schumaker 		xdr_buf_head_shift_right(&buf, 0, length, shift);
18024f5f3b60SAnna Schumaker 	} else if (offset > target) {
18034f5f3b60SAnna Schumaker 		shift = offset - target;
18044f5f3b60SAnna Schumaker 		if (xdr_buf_subsegment(xdr->buf, &buf, target, shift + length) < 0)
18054f5f3b60SAnna Schumaker 			return 0;
18064f5f3b60SAnna Schumaker 		xdr_buf_head_shift_left(&buf, shift, length, shift);
18074f5f3b60SAnna Schumaker 	}
18084f5f3b60SAnna Schumaker 	return length;
18094f5f3b60SAnna Schumaker }
18104f5f3b60SAnna Schumaker EXPORT_SYMBOL_GPL(xdr_stream_move_subsegment);
18114f5f3b60SAnna Schumaker 
18124f5f3b60SAnna Schumaker /**
1813e1bd8760SAnna Schumaker  * xdr_stream_zero - zero out a portion of an xdr_stream
1814e1bd8760SAnna Schumaker  * @xdr: an xdr_stream to zero out
1815e1bd8760SAnna Schumaker  * @offset: the starting point in the stream
1816e1bd8760SAnna Schumaker  * @length: the number of bytes to zero
1817e1bd8760SAnna Schumaker  */
xdr_stream_zero(struct xdr_stream * xdr,unsigned int offset,unsigned int length)1818e1bd8760SAnna Schumaker unsigned int xdr_stream_zero(struct xdr_stream *xdr, unsigned int offset,
1819e1bd8760SAnna Schumaker 			     unsigned int length)
1820e1bd8760SAnna Schumaker {
1821e1bd8760SAnna Schumaker 	struct xdr_buf buf;
1822e1bd8760SAnna Schumaker 
1823e1bd8760SAnna Schumaker 	if (xdr_buf_subsegment(xdr->buf, &buf, offset, length) < 0)
1824e1bd8760SAnna Schumaker 		return 0;
1825e1bd8760SAnna Schumaker 	if (buf.head[0].iov_len)
1826e1bd8760SAnna Schumaker 		xdr_buf_iov_zero(buf.head, 0, buf.head[0].iov_len);
1827e1bd8760SAnna Schumaker 	if (buf.page_len > 0)
1828e1bd8760SAnna Schumaker 		xdr_buf_pages_zero(&buf, 0, buf.page_len);
1829e1bd8760SAnna Schumaker 	if (buf.tail[0].iov_len)
1830e1bd8760SAnna Schumaker 		xdr_buf_iov_zero(buf.tail, 0, buf.tail[0].iov_len);
1831e1bd8760SAnna Schumaker 	return length;
1832e1bd8760SAnna Schumaker }
1833e1bd8760SAnna Schumaker EXPORT_SYMBOL_GPL(xdr_stream_zero);
1834e1bd8760SAnna Schumaker 
1835e1bd8760SAnna Schumaker /**
18360a8e7b7dSChuck Lever  * xdr_buf_trim - lop at most "len" bytes off the end of "buf"
18370a8e7b7dSChuck Lever  * @buf: buf to be trimmed
18380a8e7b7dSChuck Lever  * @len: number of bytes to reduce "buf" by
18390a8e7b7dSChuck Lever  *
18400a8e7b7dSChuck Lever  * Trim an xdr_buf by the given number of bytes by fixing up the lengths. Note
18410a8e7b7dSChuck Lever  * that it's possible that we'll trim less than that amount if the xdr_buf is
18420a8e7b7dSChuck Lever  * too small, or if (for instance) it's all in the head and the parser has
18430a8e7b7dSChuck Lever  * already read too far into it.
18440a8e7b7dSChuck Lever  */
xdr_buf_trim(struct xdr_buf * buf,unsigned int len)18450a8e7b7dSChuck Lever void xdr_buf_trim(struct xdr_buf *buf, unsigned int len)
18460a8e7b7dSChuck Lever {
18470a8e7b7dSChuck Lever 	size_t cur;
18480a8e7b7dSChuck Lever 	unsigned int trim = len;
18490a8e7b7dSChuck Lever 
18500a8e7b7dSChuck Lever 	if (buf->tail[0].iov_len) {
18510a8e7b7dSChuck Lever 		cur = min_t(size_t, buf->tail[0].iov_len, trim);
18520a8e7b7dSChuck Lever 		buf->tail[0].iov_len -= cur;
18530a8e7b7dSChuck Lever 		trim -= cur;
18540a8e7b7dSChuck Lever 		if (!trim)
18550a8e7b7dSChuck Lever 			goto fix_len;
18560a8e7b7dSChuck Lever 	}
18570a8e7b7dSChuck Lever 
18580a8e7b7dSChuck Lever 	if (buf->page_len) {
18590a8e7b7dSChuck Lever 		cur = min_t(unsigned int, buf->page_len, trim);
18600a8e7b7dSChuck Lever 		buf->page_len -= cur;
18610a8e7b7dSChuck Lever 		trim -= cur;
18620a8e7b7dSChuck Lever 		if (!trim)
18630a8e7b7dSChuck Lever 			goto fix_len;
18640a8e7b7dSChuck Lever 	}
18650a8e7b7dSChuck Lever 
18660a8e7b7dSChuck Lever 	if (buf->head[0].iov_len) {
18670a8e7b7dSChuck Lever 		cur = min_t(size_t, buf->head[0].iov_len, trim);
18680a8e7b7dSChuck Lever 		buf->head[0].iov_len -= cur;
18690a8e7b7dSChuck Lever 		trim -= cur;
18700a8e7b7dSChuck Lever 	}
18710a8e7b7dSChuck Lever fix_len:
18720a8e7b7dSChuck Lever 	buf->len -= (len - trim);
18730a8e7b7dSChuck Lever }
18740a8e7b7dSChuck Lever EXPORT_SYMBOL_GPL(xdr_buf_trim);
18750a8e7b7dSChuck Lever 
__read_bytes_from_xdr_buf(const struct xdr_buf * subbuf,void * obj,unsigned int len)1876f8d0e60fSTrond Myklebust static void __read_bytes_from_xdr_buf(const struct xdr_buf *subbuf,
1877f8d0e60fSTrond Myklebust 				      void *obj, unsigned int len)
18781da177e4SLinus Torvalds {
18791e78957eSTrond Myklebust 	unsigned int this_len;
18801da177e4SLinus Torvalds 
18814e3e43adSTrond Myklebust 	this_len = min_t(unsigned int, len, subbuf->head[0].iov_len);
18824e3e43adSTrond Myklebust 	memcpy(obj, subbuf->head[0].iov_base, this_len);
18831da177e4SLinus Torvalds 	len -= this_len;
18841da177e4SLinus Torvalds 	obj += this_len;
18854e3e43adSTrond Myklebust 	this_len = min_t(unsigned int, len, subbuf->page_len);
18864e3e43adSTrond Myklebust 	_copy_from_pages(obj, subbuf->pages, subbuf->page_base, this_len);
18871da177e4SLinus Torvalds 	len -= this_len;
18881da177e4SLinus Torvalds 	obj += this_len;
18894e3e43adSTrond Myklebust 	this_len = min_t(unsigned int, len, subbuf->tail[0].iov_len);
18904e3e43adSTrond Myklebust 	memcpy(obj, subbuf->tail[0].iov_base, this_len);
18911da177e4SLinus Torvalds }
18921da177e4SLinus Torvalds 
1893bd8100e7SAndreas Gruenbacher /* obj is assumed to point to allocated memory of size at least len: */
read_bytes_from_xdr_buf(const struct xdr_buf * buf,unsigned int base,void * obj,unsigned int len)1894f8d0e60fSTrond Myklebust int read_bytes_from_xdr_buf(const struct xdr_buf *buf, unsigned int base,
1895f8d0e60fSTrond Myklebust 			    void *obj, unsigned int len)
1896bd8100e7SAndreas Gruenbacher {
1897bd8100e7SAndreas Gruenbacher 	struct xdr_buf subbuf;
1898bd8100e7SAndreas Gruenbacher 	int status;
1899bd8100e7SAndreas Gruenbacher 
1900bd8100e7SAndreas Gruenbacher 	status = xdr_buf_subsegment(buf, &subbuf, base, len);
19014e3e43adSTrond Myklebust 	if (status != 0)
1902bd8100e7SAndreas Gruenbacher 		return status;
19034e3e43adSTrond Myklebust 	__read_bytes_from_xdr_buf(&subbuf, obj, len);
19044e3e43adSTrond Myklebust 	return 0;
19054e3e43adSTrond Myklebust }
1906468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(read_bytes_from_xdr_buf);
19074e3e43adSTrond Myklebust 
__write_bytes_to_xdr_buf(const struct xdr_buf * subbuf,void * obj,unsigned int len)1908f8d0e60fSTrond Myklebust static void __write_bytes_to_xdr_buf(const struct xdr_buf *subbuf,
1909f8d0e60fSTrond Myklebust 				     void *obj, unsigned int len)
19104e3e43adSTrond Myklebust {
19114e3e43adSTrond Myklebust 	unsigned int this_len;
19124e3e43adSTrond Myklebust 
19134e3e43adSTrond Myklebust 	this_len = min_t(unsigned int, len, subbuf->head[0].iov_len);
19144e3e43adSTrond Myklebust 	memcpy(subbuf->head[0].iov_base, obj, this_len);
19154e3e43adSTrond Myklebust 	len -= this_len;
19164e3e43adSTrond Myklebust 	obj += this_len;
19174e3e43adSTrond Myklebust 	this_len = min_t(unsigned int, len, subbuf->page_len);
19184e3e43adSTrond Myklebust 	_copy_to_pages(subbuf->pages, subbuf->page_base, obj, this_len);
19194e3e43adSTrond Myklebust 	len -= this_len;
19204e3e43adSTrond Myklebust 	obj += this_len;
19214e3e43adSTrond Myklebust 	this_len = min_t(unsigned int, len, subbuf->tail[0].iov_len);
19224e3e43adSTrond Myklebust 	memcpy(subbuf->tail[0].iov_base, obj, this_len);
19234e3e43adSTrond Myklebust }
19244e3e43adSTrond Myklebust 
19254e3e43adSTrond Myklebust /* obj is assumed to point to allocated memory of size at least len: */
write_bytes_to_xdr_buf(const struct xdr_buf * buf,unsigned int base,void * obj,unsigned int len)1926f8d0e60fSTrond Myklebust int write_bytes_to_xdr_buf(const struct xdr_buf *buf, unsigned int base,
1927f8d0e60fSTrond Myklebust 			   void *obj, unsigned int len)
19284e3e43adSTrond Myklebust {
19294e3e43adSTrond Myklebust 	struct xdr_buf subbuf;
19304e3e43adSTrond Myklebust 	int status;
19314e3e43adSTrond Myklebust 
19324e3e43adSTrond Myklebust 	status = xdr_buf_subsegment(buf, &subbuf, base, len);
19334e3e43adSTrond Myklebust 	if (status != 0)
19344e3e43adSTrond Myklebust 		return status;
19354e3e43adSTrond Myklebust 	__write_bytes_to_xdr_buf(&subbuf, obj, len);
19364e3e43adSTrond Myklebust 	return 0;
1937bd8100e7SAndreas Gruenbacher }
1938c43abaedSKevin Coffman EXPORT_SYMBOL_GPL(write_bytes_to_xdr_buf);
1939bd8100e7SAndreas Gruenbacher 
xdr_decode_word(const struct xdr_buf * buf,unsigned int base,u32 * obj)1940f8d0e60fSTrond Myklebust int xdr_decode_word(const struct xdr_buf *buf, unsigned int base, u32 *obj)
19411da177e4SLinus Torvalds {
1942d8ed029dSAlexey Dobriyan 	__be32	raw;
19431da177e4SLinus Torvalds 	int	status;
19441da177e4SLinus Torvalds 
19451da177e4SLinus Torvalds 	status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj));
19461da177e4SLinus Torvalds 	if (status)
19471da177e4SLinus Torvalds 		return status;
194898866b5aSBenny Halevy 	*obj = be32_to_cpu(raw);
19491da177e4SLinus Torvalds 	return 0;
19501da177e4SLinus Torvalds }
1951468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_decode_word);
19521da177e4SLinus Torvalds 
xdr_encode_word(const struct xdr_buf * buf,unsigned int base,u32 obj)1953f8d0e60fSTrond Myklebust int xdr_encode_word(const struct xdr_buf *buf, unsigned int base, u32 obj)
1954bd8100e7SAndreas Gruenbacher {
19559f162d2aSBenny Halevy 	__be32	raw = cpu_to_be32(obj);
1956bd8100e7SAndreas Gruenbacher 
1957bd8100e7SAndreas Gruenbacher 	return write_bytes_to_xdr_buf(buf, base, &raw, sizeof(obj));
1958bd8100e7SAndreas Gruenbacher }
1959468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_encode_word);
1960bd8100e7SAndreas Gruenbacher 
1961bd8100e7SAndreas Gruenbacher /* Returns 0 on success, or else a negative error code. */
xdr_xcode_array2(const struct xdr_buf * buf,unsigned int base,struct xdr_array2_desc * desc,int encode)1962f8d0e60fSTrond Myklebust static int xdr_xcode_array2(const struct xdr_buf *buf, unsigned int base,
1963bd8100e7SAndreas Gruenbacher 			    struct xdr_array2_desc *desc, int encode)
1964bd8100e7SAndreas Gruenbacher {
1965bd8100e7SAndreas Gruenbacher 	char *elem = NULL, *c;
1966bd8100e7SAndreas Gruenbacher 	unsigned int copied = 0, todo, avail_here;
1967bd8100e7SAndreas Gruenbacher 	struct page **ppages = NULL;
1968bd8100e7SAndreas Gruenbacher 	int err;
1969bd8100e7SAndreas Gruenbacher 
1970bd8100e7SAndreas Gruenbacher 	if (encode) {
1971bd8100e7SAndreas Gruenbacher 		if (xdr_encode_word(buf, base, desc->array_len) != 0)
1972bd8100e7SAndreas Gruenbacher 			return -EINVAL;
1973bd8100e7SAndreas Gruenbacher 	} else {
1974bd8100e7SAndreas Gruenbacher 		if (xdr_decode_word(buf, base, &desc->array_len) != 0 ||
197558fcb8dfSTrond Myklebust 		    desc->array_len > desc->array_maxlen ||
1976bd8100e7SAndreas Gruenbacher 		    (unsigned long) base + 4 + desc->array_len *
1977bd8100e7SAndreas Gruenbacher 				    desc->elem_size > buf->len)
1978bd8100e7SAndreas Gruenbacher 			return -EINVAL;
1979bd8100e7SAndreas Gruenbacher 	}
1980bd8100e7SAndreas Gruenbacher 	base += 4;
1981bd8100e7SAndreas Gruenbacher 
1982bd8100e7SAndreas Gruenbacher 	if (!desc->xcode)
1983bd8100e7SAndreas Gruenbacher 		return 0;
1984bd8100e7SAndreas Gruenbacher 
1985bd8100e7SAndreas Gruenbacher 	todo = desc->array_len * desc->elem_size;
1986bd8100e7SAndreas Gruenbacher 
1987bd8100e7SAndreas Gruenbacher 	/* process head */
1988bd8100e7SAndreas Gruenbacher 	if (todo && base < buf->head->iov_len) {
1989bd8100e7SAndreas Gruenbacher 		c = buf->head->iov_base + base;
1990bd8100e7SAndreas Gruenbacher 		avail_here = min_t(unsigned int, todo,
1991bd8100e7SAndreas Gruenbacher 				   buf->head->iov_len - base);
1992bd8100e7SAndreas Gruenbacher 		todo -= avail_here;
1993bd8100e7SAndreas Gruenbacher 
1994bd8100e7SAndreas Gruenbacher 		while (avail_here >= desc->elem_size) {
1995bd8100e7SAndreas Gruenbacher 			err = desc->xcode(desc, c);
1996bd8100e7SAndreas Gruenbacher 			if (err)
1997bd8100e7SAndreas Gruenbacher 				goto out;
1998bd8100e7SAndreas Gruenbacher 			c += desc->elem_size;
1999bd8100e7SAndreas Gruenbacher 			avail_here -= desc->elem_size;
2000bd8100e7SAndreas Gruenbacher 		}
2001bd8100e7SAndreas Gruenbacher 		if (avail_here) {
2002bd8100e7SAndreas Gruenbacher 			if (!elem) {
2003bd8100e7SAndreas Gruenbacher 				elem = kmalloc(desc->elem_size, GFP_KERNEL);
2004bd8100e7SAndreas Gruenbacher 				err = -ENOMEM;
2005bd8100e7SAndreas Gruenbacher 				if (!elem)
2006bd8100e7SAndreas Gruenbacher 					goto out;
2007bd8100e7SAndreas Gruenbacher 			}
2008bd8100e7SAndreas Gruenbacher 			if (encode) {
2009bd8100e7SAndreas Gruenbacher 				err = desc->xcode(desc, elem);
2010bd8100e7SAndreas Gruenbacher 				if (err)
2011bd8100e7SAndreas Gruenbacher 					goto out;
2012bd8100e7SAndreas Gruenbacher 				memcpy(c, elem, avail_here);
2013bd8100e7SAndreas Gruenbacher 			} else
2014bd8100e7SAndreas Gruenbacher 				memcpy(elem, c, avail_here);
2015bd8100e7SAndreas Gruenbacher 			copied = avail_here;
2016bd8100e7SAndreas Gruenbacher 		}
2017bd8100e7SAndreas Gruenbacher 		base = buf->head->iov_len;  /* align to start of pages */
2018bd8100e7SAndreas Gruenbacher 	}
2019bd8100e7SAndreas Gruenbacher 
2020bd8100e7SAndreas Gruenbacher 	/* process pages array */
2021bd8100e7SAndreas Gruenbacher 	base -= buf->head->iov_len;
2022bd8100e7SAndreas Gruenbacher 	if (todo && base < buf->page_len) {
2023bd8100e7SAndreas Gruenbacher 		unsigned int avail_page;
2024bd8100e7SAndreas Gruenbacher 
2025bd8100e7SAndreas Gruenbacher 		avail_here = min(todo, buf->page_len - base);
2026bd8100e7SAndreas Gruenbacher 		todo -= avail_here;
2027bd8100e7SAndreas Gruenbacher 
2028bd8100e7SAndreas Gruenbacher 		base += buf->page_base;
202909cbfeafSKirill A. Shutemov 		ppages = buf->pages + (base >> PAGE_SHIFT);
203009cbfeafSKirill A. Shutemov 		base &= ~PAGE_MASK;
203109cbfeafSKirill A. Shutemov 		avail_page = min_t(unsigned int, PAGE_SIZE - base,
2032bd8100e7SAndreas Gruenbacher 					avail_here);
2033bd8100e7SAndreas Gruenbacher 		c = kmap(*ppages) + base;
2034bd8100e7SAndreas Gruenbacher 
2035bd8100e7SAndreas Gruenbacher 		while (avail_here) {
2036bd8100e7SAndreas Gruenbacher 			avail_here -= avail_page;
2037bd8100e7SAndreas Gruenbacher 			if (copied || avail_page < desc->elem_size) {
2038bd8100e7SAndreas Gruenbacher 				unsigned int l = min(avail_page,
2039bd8100e7SAndreas Gruenbacher 					desc->elem_size - copied);
2040bd8100e7SAndreas Gruenbacher 				if (!elem) {
2041bd8100e7SAndreas Gruenbacher 					elem = kmalloc(desc->elem_size,
2042bd8100e7SAndreas Gruenbacher 						       GFP_KERNEL);
2043bd8100e7SAndreas Gruenbacher 					err = -ENOMEM;
2044bd8100e7SAndreas Gruenbacher 					if (!elem)
2045bd8100e7SAndreas Gruenbacher 						goto out;
2046bd8100e7SAndreas Gruenbacher 				}
2047bd8100e7SAndreas Gruenbacher 				if (encode) {
2048bd8100e7SAndreas Gruenbacher 					if (!copied) {
2049bd8100e7SAndreas Gruenbacher 						err = desc->xcode(desc, elem);
2050bd8100e7SAndreas Gruenbacher 						if (err)
2051bd8100e7SAndreas Gruenbacher 							goto out;
2052bd8100e7SAndreas Gruenbacher 					}
2053bd8100e7SAndreas Gruenbacher 					memcpy(c, elem + copied, l);
2054bd8100e7SAndreas Gruenbacher 					copied += l;
2055bd8100e7SAndreas Gruenbacher 					if (copied == desc->elem_size)
2056bd8100e7SAndreas Gruenbacher 						copied = 0;
2057bd8100e7SAndreas Gruenbacher 				} else {
2058bd8100e7SAndreas Gruenbacher 					memcpy(elem + copied, c, l);
2059bd8100e7SAndreas Gruenbacher 					copied += l;
2060bd8100e7SAndreas Gruenbacher 					if (copied == desc->elem_size) {
2061bd8100e7SAndreas Gruenbacher 						err = desc->xcode(desc, elem);
2062bd8100e7SAndreas Gruenbacher 						if (err)
2063bd8100e7SAndreas Gruenbacher 							goto out;
2064bd8100e7SAndreas Gruenbacher 						copied = 0;
2065bd8100e7SAndreas Gruenbacher 					}
2066bd8100e7SAndreas Gruenbacher 				}
2067bd8100e7SAndreas Gruenbacher 				avail_page -= l;
2068bd8100e7SAndreas Gruenbacher 				c += l;
2069bd8100e7SAndreas Gruenbacher 			}
2070bd8100e7SAndreas Gruenbacher 			while (avail_page >= desc->elem_size) {
2071bd8100e7SAndreas Gruenbacher 				err = desc->xcode(desc, c);
2072bd8100e7SAndreas Gruenbacher 				if (err)
2073bd8100e7SAndreas Gruenbacher 					goto out;
2074bd8100e7SAndreas Gruenbacher 				c += desc->elem_size;
2075bd8100e7SAndreas Gruenbacher 				avail_page -= desc->elem_size;
2076bd8100e7SAndreas Gruenbacher 			}
2077bd8100e7SAndreas Gruenbacher 			if (avail_page) {
2078bd8100e7SAndreas Gruenbacher 				unsigned int l = min(avail_page,
2079bd8100e7SAndreas Gruenbacher 					    desc->elem_size - copied);
2080bd8100e7SAndreas Gruenbacher 				if (!elem) {
2081bd8100e7SAndreas Gruenbacher 					elem = kmalloc(desc->elem_size,
2082bd8100e7SAndreas Gruenbacher 						       GFP_KERNEL);
2083bd8100e7SAndreas Gruenbacher 					err = -ENOMEM;
2084bd8100e7SAndreas Gruenbacher 					if (!elem)
2085bd8100e7SAndreas Gruenbacher 						goto out;
2086bd8100e7SAndreas Gruenbacher 				}
2087bd8100e7SAndreas Gruenbacher 				if (encode) {
2088bd8100e7SAndreas Gruenbacher 					if (!copied) {
2089bd8100e7SAndreas Gruenbacher 						err = desc->xcode(desc, elem);
2090bd8100e7SAndreas Gruenbacher 						if (err)
2091bd8100e7SAndreas Gruenbacher 							goto out;
2092bd8100e7SAndreas Gruenbacher 					}
2093bd8100e7SAndreas Gruenbacher 					memcpy(c, elem + copied, l);
2094bd8100e7SAndreas Gruenbacher 					copied += l;
2095bd8100e7SAndreas Gruenbacher 					if (copied == desc->elem_size)
2096bd8100e7SAndreas Gruenbacher 						copied = 0;
2097bd8100e7SAndreas Gruenbacher 				} else {
2098bd8100e7SAndreas Gruenbacher 					memcpy(elem + copied, c, l);
2099bd8100e7SAndreas Gruenbacher 					copied += l;
2100bd8100e7SAndreas Gruenbacher 					if (copied == desc->elem_size) {
2101bd8100e7SAndreas Gruenbacher 						err = desc->xcode(desc, elem);
2102bd8100e7SAndreas Gruenbacher 						if (err)
2103bd8100e7SAndreas Gruenbacher 							goto out;
2104bd8100e7SAndreas Gruenbacher 						copied = 0;
2105bd8100e7SAndreas Gruenbacher 					}
2106bd8100e7SAndreas Gruenbacher 				}
2107bd8100e7SAndreas Gruenbacher 			}
2108bd8100e7SAndreas Gruenbacher 			if (avail_here) {
2109bd8100e7SAndreas Gruenbacher 				kunmap(*ppages);
2110bd8100e7SAndreas Gruenbacher 				ppages++;
2111bd8100e7SAndreas Gruenbacher 				c = kmap(*ppages);
2112bd8100e7SAndreas Gruenbacher 			}
2113bd8100e7SAndreas Gruenbacher 
2114bd8100e7SAndreas Gruenbacher 			avail_page = min(avail_here,
211509cbfeafSKirill A. Shutemov 				 (unsigned int) PAGE_SIZE);
2116bd8100e7SAndreas Gruenbacher 		}
2117bd8100e7SAndreas Gruenbacher 		base = buf->page_len;  /* align to start of tail */
2118bd8100e7SAndreas Gruenbacher 	}
2119bd8100e7SAndreas Gruenbacher 
2120bd8100e7SAndreas Gruenbacher 	/* process tail */
2121bd8100e7SAndreas Gruenbacher 	base -= buf->page_len;
2122bd8100e7SAndreas Gruenbacher 	if (todo) {
2123bd8100e7SAndreas Gruenbacher 		c = buf->tail->iov_base + base;
2124bd8100e7SAndreas Gruenbacher 		if (copied) {
2125bd8100e7SAndreas Gruenbacher 			unsigned int l = desc->elem_size - copied;
2126bd8100e7SAndreas Gruenbacher 
2127bd8100e7SAndreas Gruenbacher 			if (encode)
2128bd8100e7SAndreas Gruenbacher 				memcpy(c, elem + copied, l);
2129bd8100e7SAndreas Gruenbacher 			else {
2130bd8100e7SAndreas Gruenbacher 				memcpy(elem + copied, c, l);
2131bd8100e7SAndreas Gruenbacher 				err = desc->xcode(desc, elem);
2132bd8100e7SAndreas Gruenbacher 				if (err)
2133bd8100e7SAndreas Gruenbacher 					goto out;
2134bd8100e7SAndreas Gruenbacher 			}
2135bd8100e7SAndreas Gruenbacher 			todo -= l;
2136bd8100e7SAndreas Gruenbacher 			c += l;
2137bd8100e7SAndreas Gruenbacher 		}
2138bd8100e7SAndreas Gruenbacher 		while (todo) {
2139bd8100e7SAndreas Gruenbacher 			err = desc->xcode(desc, c);
2140bd8100e7SAndreas Gruenbacher 			if (err)
2141bd8100e7SAndreas Gruenbacher 				goto out;
2142bd8100e7SAndreas Gruenbacher 			c += desc->elem_size;
2143bd8100e7SAndreas Gruenbacher 			todo -= desc->elem_size;
2144bd8100e7SAndreas Gruenbacher 		}
2145bd8100e7SAndreas Gruenbacher 	}
2146bd8100e7SAndreas Gruenbacher 	err = 0;
2147bd8100e7SAndreas Gruenbacher 
2148bd8100e7SAndreas Gruenbacher out:
2149bd8100e7SAndreas Gruenbacher 	kfree(elem);
2150bd8100e7SAndreas Gruenbacher 	if (ppages)
2151bd8100e7SAndreas Gruenbacher 		kunmap(*ppages);
2152bd8100e7SAndreas Gruenbacher 	return err;
2153bd8100e7SAndreas Gruenbacher }
2154bd8100e7SAndreas Gruenbacher 
xdr_decode_array2(const struct xdr_buf * buf,unsigned int base,struct xdr_array2_desc * desc)2155f8d0e60fSTrond Myklebust int xdr_decode_array2(const struct xdr_buf *buf, unsigned int base,
2156bd8100e7SAndreas Gruenbacher 		      struct xdr_array2_desc *desc)
2157bd8100e7SAndreas Gruenbacher {
2158bd8100e7SAndreas Gruenbacher 	if (base >= buf->len)
2159bd8100e7SAndreas Gruenbacher 		return -EINVAL;
2160bd8100e7SAndreas Gruenbacher 
2161bd8100e7SAndreas Gruenbacher 	return xdr_xcode_array2(buf, base, desc, 0);
2162bd8100e7SAndreas Gruenbacher }
2163468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_decode_array2);
2164bd8100e7SAndreas Gruenbacher 
xdr_encode_array2(const struct xdr_buf * buf,unsigned int base,struct xdr_array2_desc * desc)2165f8d0e60fSTrond Myklebust int xdr_encode_array2(const struct xdr_buf *buf, unsigned int base,
2166bd8100e7SAndreas Gruenbacher 		      struct xdr_array2_desc *desc)
2167bd8100e7SAndreas Gruenbacher {
2168bd8100e7SAndreas Gruenbacher 	if ((unsigned long) base + 4 + desc->array_len * desc->elem_size >
2169bd8100e7SAndreas Gruenbacher 	    buf->head->iov_len + buf->page_len + buf->tail->iov_len)
2170bd8100e7SAndreas Gruenbacher 		return -EINVAL;
2171bd8100e7SAndreas Gruenbacher 
2172bd8100e7SAndreas Gruenbacher 	return xdr_xcode_array2(buf, base, desc, 1);
2173bd8100e7SAndreas Gruenbacher }
2174468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_encode_array2);
217537a4e6cbSOlga Kornievskaia 
xdr_process_buf(const struct xdr_buf * buf,unsigned int offset,unsigned int len,int (* actor)(struct scatterlist *,void *),void * data)2176f8d0e60fSTrond Myklebust int xdr_process_buf(const struct xdr_buf *buf, unsigned int offset,
2177f8d0e60fSTrond Myklebust 		    unsigned int len,
217837a4e6cbSOlga Kornievskaia 		    int (*actor)(struct scatterlist *, void *), void *data)
217937a4e6cbSOlga Kornievskaia {
218037a4e6cbSOlga Kornievskaia 	int i, ret = 0;
218195c96174SEric Dumazet 	unsigned int page_len, thislen, page_offset;
218237a4e6cbSOlga Kornievskaia 	struct scatterlist      sg[1];
218337a4e6cbSOlga Kornievskaia 
218468e3f5ddSHerbert Xu 	sg_init_table(sg, 1);
218568e3f5ddSHerbert Xu 
218637a4e6cbSOlga Kornievskaia 	if (offset >= buf->head[0].iov_len) {
218737a4e6cbSOlga Kornievskaia 		offset -= buf->head[0].iov_len;
218837a4e6cbSOlga Kornievskaia 	} else {
218937a4e6cbSOlga Kornievskaia 		thislen = buf->head[0].iov_len - offset;
219037a4e6cbSOlga Kornievskaia 		if (thislen > len)
219137a4e6cbSOlga Kornievskaia 			thislen = len;
219237a4e6cbSOlga Kornievskaia 		sg_set_buf(sg, buf->head[0].iov_base + offset, thislen);
219337a4e6cbSOlga Kornievskaia 		ret = actor(sg, data);
219437a4e6cbSOlga Kornievskaia 		if (ret)
219537a4e6cbSOlga Kornievskaia 			goto out;
219637a4e6cbSOlga Kornievskaia 		offset = 0;
219737a4e6cbSOlga Kornievskaia 		len -= thislen;
219837a4e6cbSOlga Kornievskaia 	}
219937a4e6cbSOlga Kornievskaia 	if (len == 0)
220037a4e6cbSOlga Kornievskaia 		goto out;
220137a4e6cbSOlga Kornievskaia 
220237a4e6cbSOlga Kornievskaia 	if (offset >= buf->page_len) {
220337a4e6cbSOlga Kornievskaia 		offset -= buf->page_len;
220437a4e6cbSOlga Kornievskaia 	} else {
220537a4e6cbSOlga Kornievskaia 		page_len = buf->page_len - offset;
220637a4e6cbSOlga Kornievskaia 		if (page_len > len)
220737a4e6cbSOlga Kornievskaia 			page_len = len;
220837a4e6cbSOlga Kornievskaia 		len -= page_len;
220909cbfeafSKirill A. Shutemov 		page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1);
221009cbfeafSKirill A. Shutemov 		i = (offset + buf->page_base) >> PAGE_SHIFT;
221109cbfeafSKirill A. Shutemov 		thislen = PAGE_SIZE - page_offset;
221237a4e6cbSOlga Kornievskaia 		do {
221337a4e6cbSOlga Kornievskaia 			if (thislen > page_len)
221437a4e6cbSOlga Kornievskaia 				thislen = page_len;
2215642f1490SJens Axboe 			sg_set_page(sg, buf->pages[i], thislen, page_offset);
221637a4e6cbSOlga Kornievskaia 			ret = actor(sg, data);
221737a4e6cbSOlga Kornievskaia 			if (ret)
221837a4e6cbSOlga Kornievskaia 				goto out;
221937a4e6cbSOlga Kornievskaia 			page_len -= thislen;
222037a4e6cbSOlga Kornievskaia 			i++;
222137a4e6cbSOlga Kornievskaia 			page_offset = 0;
222209cbfeafSKirill A. Shutemov 			thislen = PAGE_SIZE;
222337a4e6cbSOlga Kornievskaia 		} while (page_len != 0);
222437a4e6cbSOlga Kornievskaia 		offset = 0;
222537a4e6cbSOlga Kornievskaia 	}
222637a4e6cbSOlga Kornievskaia 	if (len == 0)
222737a4e6cbSOlga Kornievskaia 		goto out;
222837a4e6cbSOlga Kornievskaia 	if (offset < buf->tail[0].iov_len) {
222937a4e6cbSOlga Kornievskaia 		thislen = buf->tail[0].iov_len - offset;
223037a4e6cbSOlga Kornievskaia 		if (thislen > len)
223137a4e6cbSOlga Kornievskaia 			thislen = len;
223237a4e6cbSOlga Kornievskaia 		sg_set_buf(sg, buf->tail[0].iov_base + offset, thislen);
223337a4e6cbSOlga Kornievskaia 		ret = actor(sg, data);
223437a4e6cbSOlga Kornievskaia 		len -= thislen;
223537a4e6cbSOlga Kornievskaia 	}
223637a4e6cbSOlga Kornievskaia 	if (len != 0)
223737a4e6cbSOlga Kornievskaia 		ret = -EINVAL;
223837a4e6cbSOlga Kornievskaia out:
223937a4e6cbSOlga Kornievskaia 	return ret;
224037a4e6cbSOlga Kornievskaia }
2241468039eeSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_process_buf);
224237a4e6cbSOlga Kornievskaia 
22435c741d4fSTrond Myklebust /**
22440e779aa7STrond Myklebust  * xdr_stream_decode_opaque - Decode variable length opaque
22450e779aa7STrond Myklebust  * @xdr: pointer to xdr_stream
22460e779aa7STrond Myklebust  * @ptr: location to store opaque data
22470e779aa7STrond Myklebust  * @size: size of storage buffer @ptr
22480e779aa7STrond Myklebust  *
22490e779aa7STrond Myklebust  * Return values:
22500e779aa7STrond Myklebust  *   On success, returns size of object stored in *@ptr
22510e779aa7STrond Myklebust  *   %-EBADMSG on XDR buffer overflow
22520e779aa7STrond Myklebust  *   %-EMSGSIZE on overflow of storage buffer @ptr
22530e779aa7STrond Myklebust  */
xdr_stream_decode_opaque(struct xdr_stream * xdr,void * ptr,size_t size)22540e779aa7STrond Myklebust ssize_t xdr_stream_decode_opaque(struct xdr_stream *xdr, void *ptr, size_t size)
22550e779aa7STrond Myklebust {
22560e779aa7STrond Myklebust 	ssize_t ret;
22570e779aa7STrond Myklebust 	void *p;
22580e779aa7STrond Myklebust 
22590e779aa7STrond Myklebust 	ret = xdr_stream_decode_opaque_inline(xdr, &p, size);
22600e779aa7STrond Myklebust 	if (ret <= 0)
22610e779aa7STrond Myklebust 		return ret;
22620e779aa7STrond Myklebust 	memcpy(ptr, p, ret);
22630e779aa7STrond Myklebust 	return ret;
22640e779aa7STrond Myklebust }
22650e779aa7STrond Myklebust EXPORT_SYMBOL_GPL(xdr_stream_decode_opaque);
22660e779aa7STrond Myklebust 
22670e779aa7STrond Myklebust /**
22680e779aa7STrond Myklebust  * xdr_stream_decode_opaque_dup - Decode and duplicate variable length opaque
22690e779aa7STrond Myklebust  * @xdr: pointer to xdr_stream
22700e779aa7STrond Myklebust  * @ptr: location to store pointer to opaque data
22710e779aa7STrond Myklebust  * @maxlen: maximum acceptable object size
22720e779aa7STrond Myklebust  * @gfp_flags: GFP mask to use
22730e779aa7STrond Myklebust  *
22740e779aa7STrond Myklebust  * Return values:
22750e779aa7STrond Myklebust  *   On success, returns size of object stored in *@ptr
22760e779aa7STrond Myklebust  *   %-EBADMSG on XDR buffer overflow
22770e779aa7STrond Myklebust  *   %-EMSGSIZE if the size of the object would exceed @maxlen
22780e779aa7STrond Myklebust  *   %-ENOMEM on memory allocation failure
22790e779aa7STrond Myklebust  */
xdr_stream_decode_opaque_dup(struct xdr_stream * xdr,void ** ptr,size_t maxlen,gfp_t gfp_flags)22800e779aa7STrond Myklebust ssize_t xdr_stream_decode_opaque_dup(struct xdr_stream *xdr, void **ptr,
22810e779aa7STrond Myklebust 		size_t maxlen, gfp_t gfp_flags)
22820e779aa7STrond Myklebust {
22830e779aa7STrond Myklebust 	ssize_t ret;
22840e779aa7STrond Myklebust 	void *p;
22850e779aa7STrond Myklebust 
22860e779aa7STrond Myklebust 	ret = xdr_stream_decode_opaque_inline(xdr, &p, maxlen);
22870e779aa7STrond Myklebust 	if (ret > 0) {
22880e779aa7STrond Myklebust 		*ptr = kmemdup(p, ret, gfp_flags);
22890e779aa7STrond Myklebust 		if (*ptr != NULL)
22900e779aa7STrond Myklebust 			return ret;
22910e779aa7STrond Myklebust 		ret = -ENOMEM;
22920e779aa7STrond Myklebust 	}
22930e779aa7STrond Myklebust 	*ptr = NULL;
22940e779aa7STrond Myklebust 	return ret;
22950e779aa7STrond Myklebust }
22960e779aa7STrond Myklebust EXPORT_SYMBOL_GPL(xdr_stream_decode_opaque_dup);
22970e779aa7STrond Myklebust 
22980e779aa7STrond Myklebust /**
22990e779aa7STrond Myklebust  * xdr_stream_decode_string - Decode variable length string
23000e779aa7STrond Myklebust  * @xdr: pointer to xdr_stream
23010e779aa7STrond Myklebust  * @str: location to store string
23020e779aa7STrond Myklebust  * @size: size of storage buffer @str
23030e779aa7STrond Myklebust  *
23040e779aa7STrond Myklebust  * Return values:
23050e779aa7STrond Myklebust  *   On success, returns length of NUL-terminated string stored in *@str
23060e779aa7STrond Myklebust  *   %-EBADMSG on XDR buffer overflow
23070e779aa7STrond Myklebust  *   %-EMSGSIZE on overflow of storage buffer @str
23080e779aa7STrond Myklebust  */
xdr_stream_decode_string(struct xdr_stream * xdr,char * str,size_t size)23090e779aa7STrond Myklebust ssize_t xdr_stream_decode_string(struct xdr_stream *xdr, char *str, size_t size)
23100e779aa7STrond Myklebust {
23110e779aa7STrond Myklebust 	ssize_t ret;
23120e779aa7STrond Myklebust 	void *p;
23130e779aa7STrond Myklebust 
23140e779aa7STrond Myklebust 	ret = xdr_stream_decode_opaque_inline(xdr, &p, size);
23150e779aa7STrond Myklebust 	if (ret > 0) {
23160e779aa7STrond Myklebust 		memcpy(str, p, ret);
23170e779aa7STrond Myklebust 		str[ret] = '\0';
23180e779aa7STrond Myklebust 		return strlen(str);
23190e779aa7STrond Myklebust 	}
23200e779aa7STrond Myklebust 	*str = '\0';
23210e779aa7STrond Myklebust 	return ret;
23220e779aa7STrond Myklebust }
23230e779aa7STrond Myklebust EXPORT_SYMBOL_GPL(xdr_stream_decode_string);
23240e779aa7STrond Myklebust 
23250e779aa7STrond Myklebust /**
23265c741d4fSTrond Myklebust  * xdr_stream_decode_string_dup - Decode and duplicate variable length string
23275c741d4fSTrond Myklebust  * @xdr: pointer to xdr_stream
23285c741d4fSTrond Myklebust  * @str: location to store pointer to string
23295c741d4fSTrond Myklebust  * @maxlen: maximum acceptable string length
23305c741d4fSTrond Myklebust  * @gfp_flags: GFP mask to use
23315c741d4fSTrond Myklebust  *
23325c741d4fSTrond Myklebust  * Return values:
23335c741d4fSTrond Myklebust  *   On success, returns length of NUL-terminated string stored in *@ptr
23345c741d4fSTrond Myklebust  *   %-EBADMSG on XDR buffer overflow
23355c741d4fSTrond Myklebust  *   %-EMSGSIZE if the size of the string would exceed @maxlen
23365c741d4fSTrond Myklebust  *   %-ENOMEM on memory allocation failure
23375c741d4fSTrond Myklebust  */
xdr_stream_decode_string_dup(struct xdr_stream * xdr,char ** str,size_t maxlen,gfp_t gfp_flags)23385c741d4fSTrond Myklebust ssize_t xdr_stream_decode_string_dup(struct xdr_stream *xdr, char **str,
23395c741d4fSTrond Myklebust 		size_t maxlen, gfp_t gfp_flags)
23405c741d4fSTrond Myklebust {
23415c741d4fSTrond Myklebust 	void *p;
23425c741d4fSTrond Myklebust 	ssize_t ret;
23435c741d4fSTrond Myklebust 
23445c741d4fSTrond Myklebust 	ret = xdr_stream_decode_opaque_inline(xdr, &p, maxlen);
23455c741d4fSTrond Myklebust 	if (ret > 0) {
23464aceaaeaSTrond Myklebust 		char *s = kmemdup_nul(p, ret, gfp_flags);
23475c741d4fSTrond Myklebust 		if (s != NULL) {
23485c741d4fSTrond Myklebust 			*str = s;
23495c741d4fSTrond Myklebust 			return strlen(s);
23505c741d4fSTrond Myklebust 		}
23515c741d4fSTrond Myklebust 		ret = -ENOMEM;
23525c741d4fSTrond Myklebust 	}
23535c741d4fSTrond Myklebust 	*str = NULL;
23545c741d4fSTrond Myklebust 	return ret;
23555c741d4fSTrond Myklebust }
23565c741d4fSTrond Myklebust EXPORT_SYMBOL_GPL(xdr_stream_decode_string_dup);
2357846b5756SChuck Lever 
2358846b5756SChuck Lever /**
2359846b5756SChuck Lever  * xdr_stream_decode_opaque_auth - Decode struct opaque_auth (RFC5531 S8.2)
2360846b5756SChuck Lever  * @xdr: pointer to xdr_stream
2361846b5756SChuck Lever  * @flavor: location to store decoded flavor
2362846b5756SChuck Lever  * @body: location to store decode body
2363846b5756SChuck Lever  * @body_len: location to store length of decoded body
2364846b5756SChuck Lever  *
2365846b5756SChuck Lever  * Return values:
2366846b5756SChuck Lever  *   On success, returns the number of buffer bytes consumed
2367846b5756SChuck Lever  *   %-EBADMSG on XDR buffer overflow
2368846b5756SChuck Lever  *   %-EMSGSIZE if the decoded size of the body field exceeds 400 octets
2369846b5756SChuck Lever  */
xdr_stream_decode_opaque_auth(struct xdr_stream * xdr,u32 * flavor,void ** body,unsigned int * body_len)2370846b5756SChuck Lever ssize_t xdr_stream_decode_opaque_auth(struct xdr_stream *xdr, u32 *flavor,
2371846b5756SChuck Lever 				      void **body, unsigned int *body_len)
2372846b5756SChuck Lever {
2373846b5756SChuck Lever 	ssize_t ret, len;
2374846b5756SChuck Lever 
2375846b5756SChuck Lever 	len = xdr_stream_decode_u32(xdr, flavor);
2376846b5756SChuck Lever 	if (unlikely(len < 0))
2377846b5756SChuck Lever 		return len;
2378846b5756SChuck Lever 	ret = xdr_stream_decode_opaque_inline(xdr, body, RPC_MAX_AUTH_SIZE);
2379846b5756SChuck Lever 	if (unlikely(ret < 0))
2380846b5756SChuck Lever 		return ret;
2381846b5756SChuck Lever 	*body_len = ret;
2382846b5756SChuck Lever 	return len + ret;
2383846b5756SChuck Lever }
2384846b5756SChuck Lever EXPORT_SYMBOL_GPL(xdr_stream_decode_opaque_auth);
23857b402c8dSChuck Lever 
23867b402c8dSChuck Lever /**
23877b402c8dSChuck Lever  * xdr_stream_encode_opaque_auth - Encode struct opaque_auth (RFC5531 S8.2)
23887b402c8dSChuck Lever  * @xdr: pointer to xdr_stream
23897b402c8dSChuck Lever  * @flavor: verifier flavor to encode
23907b402c8dSChuck Lever  * @body: content of body to encode
23917b402c8dSChuck Lever  * @body_len: length of body to encode
23927b402c8dSChuck Lever  *
23937b402c8dSChuck Lever  * Return values:
23947b402c8dSChuck Lever  *   On success, returns length in bytes of XDR buffer consumed
23957b402c8dSChuck Lever  *   %-EBADMSG on XDR buffer overflow
23967b402c8dSChuck Lever  *   %-EMSGSIZE if the size of @body exceeds 400 octets
23977b402c8dSChuck Lever  */
xdr_stream_encode_opaque_auth(struct xdr_stream * xdr,u32 flavor,void * body,unsigned int body_len)23987b402c8dSChuck Lever ssize_t xdr_stream_encode_opaque_auth(struct xdr_stream *xdr, u32 flavor,
23997b402c8dSChuck Lever 				      void *body, unsigned int body_len)
24007b402c8dSChuck Lever {
24017b402c8dSChuck Lever 	ssize_t ret, len;
24027b402c8dSChuck Lever 
24037b402c8dSChuck Lever 	if (unlikely(body_len > RPC_MAX_AUTH_SIZE))
24047b402c8dSChuck Lever 		return -EMSGSIZE;
24057b402c8dSChuck Lever 	len = xdr_stream_encode_u32(xdr, flavor);
24067b402c8dSChuck Lever 	if (unlikely(len < 0))
24077b402c8dSChuck Lever 		return len;
24087b402c8dSChuck Lever 	ret = xdr_stream_encode_opaque(xdr, body, body_len);
24097b402c8dSChuck Lever 	if (unlikely(ret < 0))
24107b402c8dSChuck Lever 		return ret;
24117b402c8dSChuck Lever 	return len + ret;
24127b402c8dSChuck Lever }
24137b402c8dSChuck Lever EXPORT_SYMBOL_GPL(xdr_stream_encode_opaque_auth);
2414