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