18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/net/sunrpc/xdr.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Generic XDR support. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/types.h> 138c2ecf20Sopenharmony_ci#include <linux/string.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 168c2ecf20Sopenharmony_ci#include <linux/errno.h> 178c2ecf20Sopenharmony_ci#include <linux/sunrpc/xdr.h> 188c2ecf20Sopenharmony_ci#include <linux/sunrpc/msg_prot.h> 198c2ecf20Sopenharmony_ci#include <linux/bvec.h> 208c2ecf20Sopenharmony_ci#include <trace/events/sunrpc.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic void _copy_to_pages(struct page **, size_t, const char *, size_t); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * XDR functions for basic NFS types 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci__be32 * 298c2ecf20Sopenharmony_cixdr_encode_netobj(__be32 *p, const struct xdr_netobj *obj) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci unsigned int quadlen = XDR_QUADLEN(obj->len); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci p[quadlen] = 0; /* zero trailing bytes */ 348c2ecf20Sopenharmony_ci *p++ = cpu_to_be32(obj->len); 358c2ecf20Sopenharmony_ci memcpy(p, obj->data, obj->len); 368c2ecf20Sopenharmony_ci return p + XDR_QUADLEN(obj->len); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_encode_netobj); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci__be32 * 418c2ecf20Sopenharmony_cixdr_decode_netobj(__be32 *p, struct xdr_netobj *obj) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci unsigned int len; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if ((len = be32_to_cpu(*p++)) > XDR_MAX_NETOBJ) 468c2ecf20Sopenharmony_ci return NULL; 478c2ecf20Sopenharmony_ci obj->len = len; 488c2ecf20Sopenharmony_ci obj->data = (u8 *) p; 498c2ecf20Sopenharmony_ci return p + XDR_QUADLEN(len); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_decode_netobj); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/** 548c2ecf20Sopenharmony_ci * xdr_encode_opaque_fixed - Encode fixed length opaque data 558c2ecf20Sopenharmony_ci * @p: pointer to current position in XDR buffer. 568c2ecf20Sopenharmony_ci * @ptr: pointer to data to encode (or NULL) 578c2ecf20Sopenharmony_ci * @nbytes: size of data. 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * Copy the array of data of length nbytes at ptr to the XDR buffer 608c2ecf20Sopenharmony_ci * at position p, then align to the next 32-bit boundary by padding 618c2ecf20Sopenharmony_ci * with zero bytes (see RFC1832). 628c2ecf20Sopenharmony_ci * Note: if ptr is NULL, only the padding is performed. 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * Returns the updated current XDR buffer position 658c2ecf20Sopenharmony_ci * 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_ci__be32 *xdr_encode_opaque_fixed(__be32 *p, const void *ptr, unsigned int nbytes) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci if (likely(nbytes != 0)) { 708c2ecf20Sopenharmony_ci unsigned int quadlen = XDR_QUADLEN(nbytes); 718c2ecf20Sopenharmony_ci unsigned int padding = (quadlen << 2) - nbytes; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (ptr != NULL) 748c2ecf20Sopenharmony_ci memcpy(p, ptr, nbytes); 758c2ecf20Sopenharmony_ci if (padding != 0) 768c2ecf20Sopenharmony_ci memset((char *)p + nbytes, 0, padding); 778c2ecf20Sopenharmony_ci p += quadlen; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci return p; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_encode_opaque_fixed); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/** 848c2ecf20Sopenharmony_ci * xdr_encode_opaque - Encode variable length opaque data 858c2ecf20Sopenharmony_ci * @p: pointer to current position in XDR buffer. 868c2ecf20Sopenharmony_ci * @ptr: pointer to data to encode (or NULL) 878c2ecf20Sopenharmony_ci * @nbytes: size of data. 888c2ecf20Sopenharmony_ci * 898c2ecf20Sopenharmony_ci * Returns the updated current XDR buffer position 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ci__be32 *xdr_encode_opaque(__be32 *p, const void *ptr, unsigned int nbytes) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci *p++ = cpu_to_be32(nbytes); 948c2ecf20Sopenharmony_ci return xdr_encode_opaque_fixed(p, ptr, nbytes); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_encode_opaque); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci__be32 * 998c2ecf20Sopenharmony_cixdr_encode_string(__be32 *p, const char *string) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci return xdr_encode_array(p, string, strlen(string)); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_encode_string); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci__be32 * 1068c2ecf20Sopenharmony_cixdr_decode_string_inplace(__be32 *p, char **sp, 1078c2ecf20Sopenharmony_ci unsigned int *lenp, unsigned int maxlen) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci u32 len; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci len = be32_to_cpu(*p++); 1128c2ecf20Sopenharmony_ci if (len > maxlen) 1138c2ecf20Sopenharmony_ci return NULL; 1148c2ecf20Sopenharmony_ci *lenp = len; 1158c2ecf20Sopenharmony_ci *sp = (char *) p; 1168c2ecf20Sopenharmony_ci return p + XDR_QUADLEN(len); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_decode_string_inplace); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/** 1218c2ecf20Sopenharmony_ci * xdr_terminate_string - '\0'-terminate a string residing in an xdr_buf 1228c2ecf20Sopenharmony_ci * @buf: XDR buffer where string resides 1238c2ecf20Sopenharmony_ci * @len: length of string, in bytes 1248c2ecf20Sopenharmony_ci * 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_civoid 1278c2ecf20Sopenharmony_cixdr_terminate_string(struct xdr_buf *buf, const u32 len) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci char *kaddr; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci kaddr = kmap_atomic(buf->pages[0]); 1328c2ecf20Sopenharmony_ci kaddr[buf->page_base + len] = '\0'; 1338c2ecf20Sopenharmony_ci kunmap_atomic(kaddr); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_terminate_string); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cisize_t 1388c2ecf20Sopenharmony_cixdr_buf_pagecount(struct xdr_buf *buf) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci if (!buf->page_len) 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci return (buf->page_base + buf->page_len + PAGE_SIZE - 1) >> PAGE_SHIFT; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ciint 1468c2ecf20Sopenharmony_cixdr_alloc_bvec(struct xdr_buf *buf, gfp_t gfp) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci size_t i, n = xdr_buf_pagecount(buf); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (n != 0 && buf->bvec == NULL) { 1518c2ecf20Sopenharmony_ci buf->bvec = kmalloc_array(n, sizeof(buf->bvec[0]), gfp); 1528c2ecf20Sopenharmony_ci if (!buf->bvec) 1538c2ecf20Sopenharmony_ci return -ENOMEM; 1548c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 1558c2ecf20Sopenharmony_ci buf->bvec[i].bv_page = buf->pages[i]; 1568c2ecf20Sopenharmony_ci buf->bvec[i].bv_len = PAGE_SIZE; 1578c2ecf20Sopenharmony_ci buf->bvec[i].bv_offset = 0; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_civoid 1648c2ecf20Sopenharmony_cixdr_free_bvec(struct xdr_buf *buf) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci kfree(buf->bvec); 1678c2ecf20Sopenharmony_ci buf->bvec = NULL; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci/** 1718c2ecf20Sopenharmony_ci * xdr_inline_pages - Prepare receive buffer for a large reply 1728c2ecf20Sopenharmony_ci * @xdr: xdr_buf into which reply will be placed 1738c2ecf20Sopenharmony_ci * @offset: expected offset where data payload will start, in bytes 1748c2ecf20Sopenharmony_ci * @pages: vector of struct page pointers 1758c2ecf20Sopenharmony_ci * @base: offset in first page where receive should start, in bytes 1768c2ecf20Sopenharmony_ci * @len: expected size of the upper layer data payload, in bytes 1778c2ecf20Sopenharmony_ci * 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_civoid 1808c2ecf20Sopenharmony_cixdr_inline_pages(struct xdr_buf *xdr, unsigned int offset, 1818c2ecf20Sopenharmony_ci struct page **pages, unsigned int base, unsigned int len) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct kvec *head = xdr->head; 1848c2ecf20Sopenharmony_ci struct kvec *tail = xdr->tail; 1858c2ecf20Sopenharmony_ci char *buf = (char *)head->iov_base; 1868c2ecf20Sopenharmony_ci unsigned int buflen = head->iov_len; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci head->iov_len = offset; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci xdr->pages = pages; 1918c2ecf20Sopenharmony_ci xdr->page_base = base; 1928c2ecf20Sopenharmony_ci xdr->page_len = len; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci tail->iov_base = buf + offset; 1958c2ecf20Sopenharmony_ci tail->iov_len = buflen - offset; 1968c2ecf20Sopenharmony_ci if ((xdr->page_len & 3) == 0) 1978c2ecf20Sopenharmony_ci tail->iov_len -= sizeof(__be32); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci xdr->buflen += len; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_inline_pages); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci/* 2048c2ecf20Sopenharmony_ci * Helper routines for doing 'memmove' like operations on a struct xdr_buf 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/** 2088c2ecf20Sopenharmony_ci * _shift_data_left_pages 2098c2ecf20Sopenharmony_ci * @pages: vector of pages containing both the source and dest memory area. 2108c2ecf20Sopenharmony_ci * @pgto_base: page vector address of destination 2118c2ecf20Sopenharmony_ci * @pgfrom_base: page vector address of source 2128c2ecf20Sopenharmony_ci * @len: number of bytes to copy 2138c2ecf20Sopenharmony_ci * 2148c2ecf20Sopenharmony_ci * Note: the addresses pgto_base and pgfrom_base are both calculated in 2158c2ecf20Sopenharmony_ci * the same way: 2168c2ecf20Sopenharmony_ci * if a memory area starts at byte 'base' in page 'pages[i]', 2178c2ecf20Sopenharmony_ci * then its address is given as (i << PAGE_CACHE_SHIFT) + base 2188c2ecf20Sopenharmony_ci * Alse note: pgto_base must be < pgfrom_base, but the memory areas 2198c2ecf20Sopenharmony_ci * they point to may overlap. 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_cistatic void 2228c2ecf20Sopenharmony_ci_shift_data_left_pages(struct page **pages, size_t pgto_base, 2238c2ecf20Sopenharmony_ci size_t pgfrom_base, size_t len) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct page **pgfrom, **pgto; 2268c2ecf20Sopenharmony_ci char *vfrom, *vto; 2278c2ecf20Sopenharmony_ci size_t copy; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci BUG_ON(pgfrom_base <= pgto_base); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci pgto = pages + (pgto_base >> PAGE_SHIFT); 2328c2ecf20Sopenharmony_ci pgfrom = pages + (pgfrom_base >> PAGE_SHIFT); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci pgto_base &= ~PAGE_MASK; 2358c2ecf20Sopenharmony_ci pgfrom_base &= ~PAGE_MASK; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci do { 2388c2ecf20Sopenharmony_ci if (pgto_base >= PAGE_SIZE) { 2398c2ecf20Sopenharmony_ci pgto_base = 0; 2408c2ecf20Sopenharmony_ci pgto++; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci if (pgfrom_base >= PAGE_SIZE){ 2438c2ecf20Sopenharmony_ci pgfrom_base = 0; 2448c2ecf20Sopenharmony_ci pgfrom++; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci copy = len; 2488c2ecf20Sopenharmony_ci if (copy > (PAGE_SIZE - pgto_base)) 2498c2ecf20Sopenharmony_ci copy = PAGE_SIZE - pgto_base; 2508c2ecf20Sopenharmony_ci if (copy > (PAGE_SIZE - pgfrom_base)) 2518c2ecf20Sopenharmony_ci copy = PAGE_SIZE - pgfrom_base; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci vto = kmap_atomic(*pgto); 2548c2ecf20Sopenharmony_ci if (*pgto != *pgfrom) { 2558c2ecf20Sopenharmony_ci vfrom = kmap_atomic(*pgfrom); 2568c2ecf20Sopenharmony_ci memcpy(vto + pgto_base, vfrom + pgfrom_base, copy); 2578c2ecf20Sopenharmony_ci kunmap_atomic(vfrom); 2588c2ecf20Sopenharmony_ci } else 2598c2ecf20Sopenharmony_ci memmove(vto + pgto_base, vto + pgfrom_base, copy); 2608c2ecf20Sopenharmony_ci flush_dcache_page(*pgto); 2618c2ecf20Sopenharmony_ci kunmap_atomic(vto); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci pgto_base += copy; 2648c2ecf20Sopenharmony_ci pgfrom_base += copy; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci } while ((len -= copy) != 0); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic void 2708c2ecf20Sopenharmony_ci_shift_data_left_tail(struct xdr_buf *buf, unsigned int pgto, size_t len) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct kvec *tail = buf->tail; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (len > tail->iov_len) 2758c2ecf20Sopenharmony_ci len = tail->iov_len; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci _copy_to_pages(buf->pages, 2788c2ecf20Sopenharmony_ci buf->page_base + pgto, 2798c2ecf20Sopenharmony_ci (char *)tail->iov_base, 2808c2ecf20Sopenharmony_ci len); 2818c2ecf20Sopenharmony_ci tail->iov_len -= len; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (tail->iov_len > 0) 2848c2ecf20Sopenharmony_ci memmove((char *)tail->iov_base, 2858c2ecf20Sopenharmony_ci tail->iov_base + len, 2868c2ecf20Sopenharmony_ci tail->iov_len); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci/** 2908c2ecf20Sopenharmony_ci * _shift_data_right_pages 2918c2ecf20Sopenharmony_ci * @pages: vector of pages containing both the source and dest memory area. 2928c2ecf20Sopenharmony_ci * @pgto_base: page vector address of destination 2938c2ecf20Sopenharmony_ci * @pgfrom_base: page vector address of source 2948c2ecf20Sopenharmony_ci * @len: number of bytes to copy 2958c2ecf20Sopenharmony_ci * 2968c2ecf20Sopenharmony_ci * Note: the addresses pgto_base and pgfrom_base are both calculated in 2978c2ecf20Sopenharmony_ci * the same way: 2988c2ecf20Sopenharmony_ci * if a memory area starts at byte 'base' in page 'pages[i]', 2998c2ecf20Sopenharmony_ci * then its address is given as (i << PAGE_SHIFT) + base 3008c2ecf20Sopenharmony_ci * Also note: pgfrom_base must be < pgto_base, but the memory areas 3018c2ecf20Sopenharmony_ci * they point to may overlap. 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_cistatic void 3048c2ecf20Sopenharmony_ci_shift_data_right_pages(struct page **pages, size_t pgto_base, 3058c2ecf20Sopenharmony_ci size_t pgfrom_base, size_t len) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct page **pgfrom, **pgto; 3088c2ecf20Sopenharmony_ci char *vfrom, *vto; 3098c2ecf20Sopenharmony_ci size_t copy; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci BUG_ON(pgto_base <= pgfrom_base); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci pgto_base += len; 3148c2ecf20Sopenharmony_ci pgfrom_base += len; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci pgto = pages + (pgto_base >> PAGE_SHIFT); 3178c2ecf20Sopenharmony_ci pgfrom = pages + (pgfrom_base >> PAGE_SHIFT); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci pgto_base &= ~PAGE_MASK; 3208c2ecf20Sopenharmony_ci pgfrom_base &= ~PAGE_MASK; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci do { 3238c2ecf20Sopenharmony_ci /* Are any pointers crossing a page boundary? */ 3248c2ecf20Sopenharmony_ci if (pgto_base == 0) { 3258c2ecf20Sopenharmony_ci pgto_base = PAGE_SIZE; 3268c2ecf20Sopenharmony_ci pgto--; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci if (pgfrom_base == 0) { 3298c2ecf20Sopenharmony_ci pgfrom_base = PAGE_SIZE; 3308c2ecf20Sopenharmony_ci pgfrom--; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci copy = len; 3348c2ecf20Sopenharmony_ci if (copy > pgto_base) 3358c2ecf20Sopenharmony_ci copy = pgto_base; 3368c2ecf20Sopenharmony_ci if (copy > pgfrom_base) 3378c2ecf20Sopenharmony_ci copy = pgfrom_base; 3388c2ecf20Sopenharmony_ci pgto_base -= copy; 3398c2ecf20Sopenharmony_ci pgfrom_base -= copy; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci vto = kmap_atomic(*pgto); 3428c2ecf20Sopenharmony_ci if (*pgto != *pgfrom) { 3438c2ecf20Sopenharmony_ci vfrom = kmap_atomic(*pgfrom); 3448c2ecf20Sopenharmony_ci memcpy(vto + pgto_base, vfrom + pgfrom_base, copy); 3458c2ecf20Sopenharmony_ci kunmap_atomic(vfrom); 3468c2ecf20Sopenharmony_ci } else 3478c2ecf20Sopenharmony_ci memmove(vto + pgto_base, vto + pgfrom_base, copy); 3488c2ecf20Sopenharmony_ci flush_dcache_page(*pgto); 3498c2ecf20Sopenharmony_ci kunmap_atomic(vto); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci } while ((len -= copy) != 0); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic unsigned int 3558c2ecf20Sopenharmony_ci_shift_data_right_tail(struct xdr_buf *buf, unsigned int pgfrom, size_t len) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct kvec *tail = buf->tail; 3588c2ecf20Sopenharmony_ci unsigned int tailbuf_len; 3598c2ecf20Sopenharmony_ci unsigned int result = 0; 3608c2ecf20Sopenharmony_ci size_t copy; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci tailbuf_len = buf->buflen - buf->head->iov_len - buf->page_len; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* Shift the tail first */ 3658c2ecf20Sopenharmony_ci if (tailbuf_len != 0) { 3668c2ecf20Sopenharmony_ci unsigned int free_space = tailbuf_len - tail->iov_len; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (len < free_space) 3698c2ecf20Sopenharmony_ci free_space = len; 3708c2ecf20Sopenharmony_ci if (len > free_space) 3718c2ecf20Sopenharmony_ci len = free_space; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci tail->iov_len += free_space; 3748c2ecf20Sopenharmony_ci copy = len; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (tail->iov_len > len) { 3778c2ecf20Sopenharmony_ci char *p = (char *)tail->iov_base + len; 3788c2ecf20Sopenharmony_ci memmove(p, tail->iov_base, tail->iov_len - free_space); 3798c2ecf20Sopenharmony_ci result += tail->iov_len - free_space; 3808c2ecf20Sopenharmony_ci } else 3818c2ecf20Sopenharmony_ci copy = tail->iov_len; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* Copy from the inlined pages into the tail */ 3848c2ecf20Sopenharmony_ci _copy_from_pages((char *)tail->iov_base, 3858c2ecf20Sopenharmony_ci buf->pages, 3868c2ecf20Sopenharmony_ci buf->page_base + pgfrom, 3878c2ecf20Sopenharmony_ci copy); 3888c2ecf20Sopenharmony_ci result += copy; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return result; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci/** 3958c2ecf20Sopenharmony_ci * _copy_to_pages 3968c2ecf20Sopenharmony_ci * @pages: array of pages 3978c2ecf20Sopenharmony_ci * @pgbase: page vector address of destination 3988c2ecf20Sopenharmony_ci * @p: pointer to source data 3998c2ecf20Sopenharmony_ci * @len: length 4008c2ecf20Sopenharmony_ci * 4018c2ecf20Sopenharmony_ci * Copies data from an arbitrary memory location into an array of pages 4028c2ecf20Sopenharmony_ci * The copy is assumed to be non-overlapping. 4038c2ecf20Sopenharmony_ci */ 4048c2ecf20Sopenharmony_cistatic void 4058c2ecf20Sopenharmony_ci_copy_to_pages(struct page **pages, size_t pgbase, const char *p, size_t len) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct page **pgto; 4088c2ecf20Sopenharmony_ci char *vto; 4098c2ecf20Sopenharmony_ci size_t copy; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci pgto = pages + (pgbase >> PAGE_SHIFT); 4128c2ecf20Sopenharmony_ci pgbase &= ~PAGE_MASK; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci for (;;) { 4158c2ecf20Sopenharmony_ci copy = PAGE_SIZE - pgbase; 4168c2ecf20Sopenharmony_ci if (copy > len) 4178c2ecf20Sopenharmony_ci copy = len; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci vto = kmap_atomic(*pgto); 4208c2ecf20Sopenharmony_ci memcpy(vto + pgbase, p, copy); 4218c2ecf20Sopenharmony_ci kunmap_atomic(vto); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci len -= copy; 4248c2ecf20Sopenharmony_ci if (len == 0) 4258c2ecf20Sopenharmony_ci break; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci pgbase += copy; 4288c2ecf20Sopenharmony_ci if (pgbase == PAGE_SIZE) { 4298c2ecf20Sopenharmony_ci flush_dcache_page(*pgto); 4308c2ecf20Sopenharmony_ci pgbase = 0; 4318c2ecf20Sopenharmony_ci pgto++; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci p += copy; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci flush_dcache_page(*pgto); 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci/** 4398c2ecf20Sopenharmony_ci * _copy_from_pages 4408c2ecf20Sopenharmony_ci * @p: pointer to destination 4418c2ecf20Sopenharmony_ci * @pages: array of pages 4428c2ecf20Sopenharmony_ci * @pgbase: offset of source data 4438c2ecf20Sopenharmony_ci * @len: length 4448c2ecf20Sopenharmony_ci * 4458c2ecf20Sopenharmony_ci * Copies data into an arbitrary memory location from an array of pages 4468c2ecf20Sopenharmony_ci * The copy is assumed to be non-overlapping. 4478c2ecf20Sopenharmony_ci */ 4488c2ecf20Sopenharmony_civoid 4498c2ecf20Sopenharmony_ci_copy_from_pages(char *p, struct page **pages, size_t pgbase, size_t len) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci struct page **pgfrom; 4528c2ecf20Sopenharmony_ci char *vfrom; 4538c2ecf20Sopenharmony_ci size_t copy; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci pgfrom = pages + (pgbase >> PAGE_SHIFT); 4568c2ecf20Sopenharmony_ci pgbase &= ~PAGE_MASK; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci do { 4598c2ecf20Sopenharmony_ci copy = PAGE_SIZE - pgbase; 4608c2ecf20Sopenharmony_ci if (copy > len) 4618c2ecf20Sopenharmony_ci copy = len; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci vfrom = kmap_atomic(*pgfrom); 4648c2ecf20Sopenharmony_ci memcpy(p, vfrom + pgbase, copy); 4658c2ecf20Sopenharmony_ci kunmap_atomic(vfrom); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci pgbase += copy; 4688c2ecf20Sopenharmony_ci if (pgbase == PAGE_SIZE) { 4698c2ecf20Sopenharmony_ci pgbase = 0; 4708c2ecf20Sopenharmony_ci pgfrom++; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci p += copy; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci } while ((len -= copy) != 0); 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(_copy_from_pages); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci/** 4798c2ecf20Sopenharmony_ci * _zero_pages 4808c2ecf20Sopenharmony_ci * @pages: array of pages 4818c2ecf20Sopenharmony_ci * @pgbase: beginning page vector address 4828c2ecf20Sopenharmony_ci * @len: length 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_cistatic void 4858c2ecf20Sopenharmony_ci_zero_pages(struct page **pages, size_t pgbase, size_t len) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct page **page; 4888c2ecf20Sopenharmony_ci char *vpage; 4898c2ecf20Sopenharmony_ci size_t zero; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci page = pages + (pgbase >> PAGE_SHIFT); 4928c2ecf20Sopenharmony_ci pgbase &= ~PAGE_MASK; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci do { 4958c2ecf20Sopenharmony_ci zero = PAGE_SIZE - pgbase; 4968c2ecf20Sopenharmony_ci if (zero > len) 4978c2ecf20Sopenharmony_ci zero = len; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci vpage = kmap_atomic(*page); 5008c2ecf20Sopenharmony_ci memset(vpage + pgbase, 0, zero); 5018c2ecf20Sopenharmony_ci kunmap_atomic(vpage); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci flush_dcache_page(*page); 5048c2ecf20Sopenharmony_ci pgbase = 0; 5058c2ecf20Sopenharmony_ci page++; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci } while ((len -= zero) != 0); 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci/** 5118c2ecf20Sopenharmony_ci * xdr_shrink_bufhead 5128c2ecf20Sopenharmony_ci * @buf: xdr_buf 5138c2ecf20Sopenharmony_ci * @len: bytes to remove from buf->head[0] 5148c2ecf20Sopenharmony_ci * 5158c2ecf20Sopenharmony_ci * Shrinks XDR buffer's header kvec buf->head[0] by 5168c2ecf20Sopenharmony_ci * 'len' bytes. The extra data is not lost, but is instead 5178c2ecf20Sopenharmony_ci * moved into the inlined pages and/or the tail. 5188c2ecf20Sopenharmony_ci */ 5198c2ecf20Sopenharmony_cistatic unsigned int 5208c2ecf20Sopenharmony_cixdr_shrink_bufhead(struct xdr_buf *buf, size_t len) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci struct kvec *head, *tail; 5238c2ecf20Sopenharmony_ci size_t copy, offs; 5248c2ecf20Sopenharmony_ci unsigned int pglen = buf->page_len; 5258c2ecf20Sopenharmony_ci unsigned int result; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci result = 0; 5288c2ecf20Sopenharmony_ci tail = buf->tail; 5298c2ecf20Sopenharmony_ci head = buf->head; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci WARN_ON_ONCE(len > head->iov_len); 5328c2ecf20Sopenharmony_ci if (len > head->iov_len) 5338c2ecf20Sopenharmony_ci len = head->iov_len; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* Shift the tail first */ 5368c2ecf20Sopenharmony_ci if (tail->iov_len != 0) { 5378c2ecf20Sopenharmony_ci if (tail->iov_len > len) { 5388c2ecf20Sopenharmony_ci copy = tail->iov_len - len; 5398c2ecf20Sopenharmony_ci memmove((char *)tail->iov_base + len, 5408c2ecf20Sopenharmony_ci tail->iov_base, copy); 5418c2ecf20Sopenharmony_ci result += copy; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci /* Copy from the inlined pages into the tail */ 5448c2ecf20Sopenharmony_ci copy = len; 5458c2ecf20Sopenharmony_ci if (copy > pglen) 5468c2ecf20Sopenharmony_ci copy = pglen; 5478c2ecf20Sopenharmony_ci offs = len - copy; 5488c2ecf20Sopenharmony_ci if (offs >= tail->iov_len) 5498c2ecf20Sopenharmony_ci copy = 0; 5508c2ecf20Sopenharmony_ci else if (copy > tail->iov_len - offs) 5518c2ecf20Sopenharmony_ci copy = tail->iov_len - offs; 5528c2ecf20Sopenharmony_ci if (copy != 0) { 5538c2ecf20Sopenharmony_ci _copy_from_pages((char *)tail->iov_base + offs, 5548c2ecf20Sopenharmony_ci buf->pages, 5558c2ecf20Sopenharmony_ci buf->page_base + pglen + offs - len, 5568c2ecf20Sopenharmony_ci copy); 5578c2ecf20Sopenharmony_ci result += copy; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci /* Do we also need to copy data from the head into the tail ? */ 5608c2ecf20Sopenharmony_ci if (len > pglen) { 5618c2ecf20Sopenharmony_ci offs = copy = len - pglen; 5628c2ecf20Sopenharmony_ci if (copy > tail->iov_len) 5638c2ecf20Sopenharmony_ci copy = tail->iov_len; 5648c2ecf20Sopenharmony_ci memcpy(tail->iov_base, 5658c2ecf20Sopenharmony_ci (char *)head->iov_base + 5668c2ecf20Sopenharmony_ci head->iov_len - offs, 5678c2ecf20Sopenharmony_ci copy); 5688c2ecf20Sopenharmony_ci result += copy; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci /* Now handle pages */ 5728c2ecf20Sopenharmony_ci if (pglen != 0) { 5738c2ecf20Sopenharmony_ci if (pglen > len) 5748c2ecf20Sopenharmony_ci _shift_data_right_pages(buf->pages, 5758c2ecf20Sopenharmony_ci buf->page_base + len, 5768c2ecf20Sopenharmony_ci buf->page_base, 5778c2ecf20Sopenharmony_ci pglen - len); 5788c2ecf20Sopenharmony_ci copy = len; 5798c2ecf20Sopenharmony_ci if (len > pglen) 5808c2ecf20Sopenharmony_ci copy = pglen; 5818c2ecf20Sopenharmony_ci _copy_to_pages(buf->pages, buf->page_base, 5828c2ecf20Sopenharmony_ci (char *)head->iov_base + head->iov_len - len, 5838c2ecf20Sopenharmony_ci copy); 5848c2ecf20Sopenharmony_ci result += copy; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci head->iov_len -= len; 5878c2ecf20Sopenharmony_ci buf->buflen -= len; 5888c2ecf20Sopenharmony_ci /* Have we truncated the message? */ 5898c2ecf20Sopenharmony_ci if (buf->len > buf->buflen) 5908c2ecf20Sopenharmony_ci buf->len = buf->buflen; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci return result; 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci/** 5968c2ecf20Sopenharmony_ci * xdr_shrink_pagelen - shrinks buf->pages by up to @len bytes 5978c2ecf20Sopenharmony_ci * @buf: xdr_buf 5988c2ecf20Sopenharmony_ci * @len: bytes to remove from buf->pages 5998c2ecf20Sopenharmony_ci * 6008c2ecf20Sopenharmony_ci * The extra data is not lost, but is instead moved into buf->tail. 6018c2ecf20Sopenharmony_ci * Returns the actual number of bytes moved. 6028c2ecf20Sopenharmony_ci */ 6038c2ecf20Sopenharmony_cistatic unsigned int 6048c2ecf20Sopenharmony_cixdr_shrink_pagelen(struct xdr_buf *buf, size_t len) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci unsigned int pglen = buf->page_len; 6078c2ecf20Sopenharmony_ci unsigned int result; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (len > buf->page_len) 6108c2ecf20Sopenharmony_ci len = buf-> page_len; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci result = _shift_data_right_tail(buf, pglen - len, len); 6138c2ecf20Sopenharmony_ci buf->page_len -= len; 6148c2ecf20Sopenharmony_ci buf->buflen -= len; 6158c2ecf20Sopenharmony_ci /* Have we truncated the message? */ 6168c2ecf20Sopenharmony_ci if (buf->len > buf->buflen) 6178c2ecf20Sopenharmony_ci buf->len = buf->buflen; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci return result; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_civoid 6238c2ecf20Sopenharmony_cixdr_shift_buf(struct xdr_buf *buf, size_t len) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci xdr_shrink_bufhead(buf, len); 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_shift_buf); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci/** 6308c2ecf20Sopenharmony_ci * xdr_stream_pos - Return the current offset from the start of the xdr_stream 6318c2ecf20Sopenharmony_ci * @xdr: pointer to struct xdr_stream 6328c2ecf20Sopenharmony_ci */ 6338c2ecf20Sopenharmony_ciunsigned int xdr_stream_pos(const struct xdr_stream *xdr) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci return (unsigned int)(XDR_QUADLEN(xdr->buf->len) - xdr->nwords) << 2; 6368c2ecf20Sopenharmony_ci} 6378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_stream_pos); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci/** 6408c2ecf20Sopenharmony_ci * xdr_page_pos - Return the current offset from the start of the xdr pages 6418c2ecf20Sopenharmony_ci * @xdr: pointer to struct xdr_stream 6428c2ecf20Sopenharmony_ci */ 6438c2ecf20Sopenharmony_ciunsigned int xdr_page_pos(const struct xdr_stream *xdr) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci unsigned int pos = xdr_stream_pos(xdr); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci WARN_ON(pos < xdr->buf->head[0].iov_len); 6488c2ecf20Sopenharmony_ci return pos - xdr->buf->head[0].iov_len; 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_page_pos); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci/** 6538c2ecf20Sopenharmony_ci * xdr_init_encode - Initialize a struct xdr_stream for sending data. 6548c2ecf20Sopenharmony_ci * @xdr: pointer to xdr_stream struct 6558c2ecf20Sopenharmony_ci * @buf: pointer to XDR buffer in which to encode data 6568c2ecf20Sopenharmony_ci * @p: current pointer inside XDR buffer 6578c2ecf20Sopenharmony_ci * @rqst: pointer to controlling rpc_rqst, for debugging 6588c2ecf20Sopenharmony_ci * 6598c2ecf20Sopenharmony_ci * Note: at the moment the RPC client only passes the length of our 6608c2ecf20Sopenharmony_ci * scratch buffer in the xdr_buf's header kvec. Previously this 6618c2ecf20Sopenharmony_ci * meant we needed to call xdr_adjust_iovec() after encoding the 6628c2ecf20Sopenharmony_ci * data. With the new scheme, the xdr_stream manages the details 6638c2ecf20Sopenharmony_ci * of the buffer length, and takes care of adjusting the kvec 6648c2ecf20Sopenharmony_ci * length for us. 6658c2ecf20Sopenharmony_ci */ 6668c2ecf20Sopenharmony_civoid xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p, 6678c2ecf20Sopenharmony_ci struct rpc_rqst *rqst) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci struct kvec *iov = buf->head; 6708c2ecf20Sopenharmony_ci int scratch_len = buf->buflen - buf->page_len - buf->tail[0].iov_len; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci xdr_set_scratch_buffer(xdr, NULL, 0); 6738c2ecf20Sopenharmony_ci BUG_ON(scratch_len < 0); 6748c2ecf20Sopenharmony_ci xdr->buf = buf; 6758c2ecf20Sopenharmony_ci xdr->iov = iov; 6768c2ecf20Sopenharmony_ci xdr->p = (__be32 *)((char *)iov->iov_base + iov->iov_len); 6778c2ecf20Sopenharmony_ci xdr->end = (__be32 *)((char *)iov->iov_base + scratch_len); 6788c2ecf20Sopenharmony_ci BUG_ON(iov->iov_len > scratch_len); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (p != xdr->p && p != NULL) { 6818c2ecf20Sopenharmony_ci size_t len; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci BUG_ON(p < xdr->p || p > xdr->end); 6848c2ecf20Sopenharmony_ci len = (char *)p - (char *)xdr->p; 6858c2ecf20Sopenharmony_ci xdr->p = p; 6868c2ecf20Sopenharmony_ci buf->len += len; 6878c2ecf20Sopenharmony_ci iov->iov_len += len; 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci xdr->rqst = rqst; 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_init_encode); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci/** 6948c2ecf20Sopenharmony_ci * xdr_commit_encode - Ensure all data is written to buffer 6958c2ecf20Sopenharmony_ci * @xdr: pointer to xdr_stream 6968c2ecf20Sopenharmony_ci * 6978c2ecf20Sopenharmony_ci * We handle encoding across page boundaries by giving the caller a 6988c2ecf20Sopenharmony_ci * temporary location to write to, then later copying the data into 6998c2ecf20Sopenharmony_ci * place; xdr_commit_encode does that copying. 7008c2ecf20Sopenharmony_ci * 7018c2ecf20Sopenharmony_ci * Normally the caller doesn't need to call this directly, as the 7028c2ecf20Sopenharmony_ci * following xdr_reserve_space will do it. But an explicit call may be 7038c2ecf20Sopenharmony_ci * required at the end of encoding, or any other time when the xdr_buf 7048c2ecf20Sopenharmony_ci * data might be read. 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_ciinline void xdr_commit_encode(struct xdr_stream *xdr) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci int shift = xdr->scratch.iov_len; 7098c2ecf20Sopenharmony_ci void *page; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci if (shift == 0) 7128c2ecf20Sopenharmony_ci return; 7138c2ecf20Sopenharmony_ci page = page_address(*xdr->page_ptr); 7148c2ecf20Sopenharmony_ci memcpy(xdr->scratch.iov_base, page, shift); 7158c2ecf20Sopenharmony_ci memmove(page, page + shift, (void *)xdr->p - page); 7168c2ecf20Sopenharmony_ci xdr->scratch.iov_len = 0; 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_commit_encode); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr, 7218c2ecf20Sopenharmony_ci size_t nbytes) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci __be32 *p; 7248c2ecf20Sopenharmony_ci int space_left; 7258c2ecf20Sopenharmony_ci int frag1bytes, frag2bytes; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (nbytes > PAGE_SIZE) 7288c2ecf20Sopenharmony_ci goto out_overflow; /* Bigger buffers require special handling */ 7298c2ecf20Sopenharmony_ci if (xdr->buf->len + nbytes > xdr->buf->buflen) 7308c2ecf20Sopenharmony_ci goto out_overflow; /* Sorry, we're totally out of space */ 7318c2ecf20Sopenharmony_ci frag1bytes = (xdr->end - xdr->p) << 2; 7328c2ecf20Sopenharmony_ci frag2bytes = nbytes - frag1bytes; 7338c2ecf20Sopenharmony_ci if (xdr->iov) 7348c2ecf20Sopenharmony_ci xdr->iov->iov_len += frag1bytes; 7358c2ecf20Sopenharmony_ci else 7368c2ecf20Sopenharmony_ci xdr->buf->page_len += frag1bytes; 7378c2ecf20Sopenharmony_ci xdr->page_ptr++; 7388c2ecf20Sopenharmony_ci xdr->iov = NULL; 7398c2ecf20Sopenharmony_ci /* 7408c2ecf20Sopenharmony_ci * If the last encode didn't end exactly on a page boundary, the 7418c2ecf20Sopenharmony_ci * next one will straddle boundaries. Encode into the next 7428c2ecf20Sopenharmony_ci * page, then copy it back later in xdr_commit_encode. We use 7438c2ecf20Sopenharmony_ci * the "scratch" iov to track any temporarily unused fragment of 7448c2ecf20Sopenharmony_ci * space at the end of the previous buffer: 7458c2ecf20Sopenharmony_ci */ 7468c2ecf20Sopenharmony_ci xdr->scratch.iov_base = xdr->p; 7478c2ecf20Sopenharmony_ci xdr->scratch.iov_len = frag1bytes; 7488c2ecf20Sopenharmony_ci p = page_address(*xdr->page_ptr); 7498c2ecf20Sopenharmony_ci /* 7508c2ecf20Sopenharmony_ci * Note this is where the next encode will start after we've 7518c2ecf20Sopenharmony_ci * shifted this one back: 7528c2ecf20Sopenharmony_ci */ 7538c2ecf20Sopenharmony_ci xdr->p = (void *)p + frag2bytes; 7548c2ecf20Sopenharmony_ci space_left = xdr->buf->buflen - xdr->buf->len; 7558c2ecf20Sopenharmony_ci if (space_left - frag1bytes >= PAGE_SIZE) 7568c2ecf20Sopenharmony_ci xdr->end = (void *)p + PAGE_SIZE; 7578c2ecf20Sopenharmony_ci else 7588c2ecf20Sopenharmony_ci xdr->end = (void *)p + space_left - frag1bytes; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci xdr->buf->page_len += frag2bytes; 7618c2ecf20Sopenharmony_ci xdr->buf->len += nbytes; 7628c2ecf20Sopenharmony_ci return p; 7638c2ecf20Sopenharmony_ciout_overflow: 7648c2ecf20Sopenharmony_ci trace_rpc_xdr_overflow(xdr, nbytes); 7658c2ecf20Sopenharmony_ci return NULL; 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci/** 7698c2ecf20Sopenharmony_ci * xdr_reserve_space - Reserve buffer space for sending 7708c2ecf20Sopenharmony_ci * @xdr: pointer to xdr_stream 7718c2ecf20Sopenharmony_ci * @nbytes: number of bytes to reserve 7728c2ecf20Sopenharmony_ci * 7738c2ecf20Sopenharmony_ci * Checks that we have enough buffer space to encode 'nbytes' more 7748c2ecf20Sopenharmony_ci * bytes of data. If so, update the total xdr_buf length, and 7758c2ecf20Sopenharmony_ci * adjust the length of the current kvec. 7768c2ecf20Sopenharmony_ci */ 7778c2ecf20Sopenharmony_ci__be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci __be32 *p = xdr->p; 7808c2ecf20Sopenharmony_ci __be32 *q; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci xdr_commit_encode(xdr); 7838c2ecf20Sopenharmony_ci /* align nbytes on the next 32-bit boundary */ 7848c2ecf20Sopenharmony_ci nbytes += 3; 7858c2ecf20Sopenharmony_ci nbytes &= ~3; 7868c2ecf20Sopenharmony_ci q = p + (nbytes >> 2); 7878c2ecf20Sopenharmony_ci if (unlikely(q > xdr->end || q < p)) 7888c2ecf20Sopenharmony_ci return xdr_get_next_encode_buffer(xdr, nbytes); 7898c2ecf20Sopenharmony_ci xdr->p = q; 7908c2ecf20Sopenharmony_ci if (xdr->iov) 7918c2ecf20Sopenharmony_ci xdr->iov->iov_len += nbytes; 7928c2ecf20Sopenharmony_ci else 7938c2ecf20Sopenharmony_ci xdr->buf->page_len += nbytes; 7948c2ecf20Sopenharmony_ci xdr->buf->len += nbytes; 7958c2ecf20Sopenharmony_ci return p; 7968c2ecf20Sopenharmony_ci} 7978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_reserve_space); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci/** 8018c2ecf20Sopenharmony_ci * xdr_reserve_space_vec - Reserves a large amount of buffer space for sending 8028c2ecf20Sopenharmony_ci * @xdr: pointer to xdr_stream 8038c2ecf20Sopenharmony_ci * @vec: pointer to a kvec array 8048c2ecf20Sopenharmony_ci * @nbytes: number of bytes to reserve 8058c2ecf20Sopenharmony_ci * 8068c2ecf20Sopenharmony_ci * Reserves enough buffer space to encode 'nbytes' of data and stores the 8078c2ecf20Sopenharmony_ci * pointers in 'vec'. The size argument passed to xdr_reserve_space() is 8088c2ecf20Sopenharmony_ci * determined based on the number of bytes remaining in the current page to 8098c2ecf20Sopenharmony_ci * avoid invalidating iov_base pointers when xdr_commit_encode() is called. 8108c2ecf20Sopenharmony_ci */ 8118c2ecf20Sopenharmony_ciint xdr_reserve_space_vec(struct xdr_stream *xdr, struct kvec *vec, size_t nbytes) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci int thislen; 8148c2ecf20Sopenharmony_ci int v = 0; 8158c2ecf20Sopenharmony_ci __be32 *p; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci /* 8188c2ecf20Sopenharmony_ci * svcrdma requires every READ payload to start somewhere 8198c2ecf20Sopenharmony_ci * in xdr->pages. 8208c2ecf20Sopenharmony_ci */ 8218c2ecf20Sopenharmony_ci if (xdr->iov == xdr->buf->head) { 8228c2ecf20Sopenharmony_ci xdr->iov = NULL; 8238c2ecf20Sopenharmony_ci xdr->end = xdr->p; 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci while (nbytes) { 8278c2ecf20Sopenharmony_ci thislen = xdr->buf->page_len % PAGE_SIZE; 8288c2ecf20Sopenharmony_ci thislen = min_t(size_t, nbytes, PAGE_SIZE - thislen); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci p = xdr_reserve_space(xdr, thislen); 8318c2ecf20Sopenharmony_ci if (!p) 8328c2ecf20Sopenharmony_ci return -EIO; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci vec[v].iov_base = p; 8358c2ecf20Sopenharmony_ci vec[v].iov_len = thislen; 8368c2ecf20Sopenharmony_ci v++; 8378c2ecf20Sopenharmony_ci nbytes -= thislen; 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci return v; 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_reserve_space_vec); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci/** 8458c2ecf20Sopenharmony_ci * xdr_truncate_encode - truncate an encode buffer 8468c2ecf20Sopenharmony_ci * @xdr: pointer to xdr_stream 8478c2ecf20Sopenharmony_ci * @len: new length of buffer 8488c2ecf20Sopenharmony_ci * 8498c2ecf20Sopenharmony_ci * Truncates the xdr stream, so that xdr->buf->len == len, 8508c2ecf20Sopenharmony_ci * and xdr->p points at offset len from the start of the buffer, and 8518c2ecf20Sopenharmony_ci * head, tail, and page lengths are adjusted to correspond. 8528c2ecf20Sopenharmony_ci * 8538c2ecf20Sopenharmony_ci * If this means moving xdr->p to a different buffer, we assume that 8548c2ecf20Sopenharmony_ci * the end pointer should be set to the end of the current page, 8558c2ecf20Sopenharmony_ci * except in the case of the head buffer when we assume the head 8568c2ecf20Sopenharmony_ci * buffer's current length represents the end of the available buffer. 8578c2ecf20Sopenharmony_ci * 8588c2ecf20Sopenharmony_ci * This is *not* safe to use on a buffer that already has inlined page 8598c2ecf20Sopenharmony_ci * cache pages (as in a zero-copy server read reply), except for the 8608c2ecf20Sopenharmony_ci * simple case of truncating from one position in the tail to another. 8618c2ecf20Sopenharmony_ci * 8628c2ecf20Sopenharmony_ci */ 8638c2ecf20Sopenharmony_civoid xdr_truncate_encode(struct xdr_stream *xdr, size_t len) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 8668c2ecf20Sopenharmony_ci struct kvec *head = buf->head; 8678c2ecf20Sopenharmony_ci struct kvec *tail = buf->tail; 8688c2ecf20Sopenharmony_ci int fraglen; 8698c2ecf20Sopenharmony_ci int new; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci if (len > buf->len) { 8728c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 8738c2ecf20Sopenharmony_ci return; 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci xdr_commit_encode(xdr); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci fraglen = min_t(int, buf->len - len, tail->iov_len); 8788c2ecf20Sopenharmony_ci tail->iov_len -= fraglen; 8798c2ecf20Sopenharmony_ci buf->len -= fraglen; 8808c2ecf20Sopenharmony_ci if (tail->iov_len) { 8818c2ecf20Sopenharmony_ci xdr->p = tail->iov_base + tail->iov_len; 8828c2ecf20Sopenharmony_ci WARN_ON_ONCE(!xdr->end); 8838c2ecf20Sopenharmony_ci WARN_ON_ONCE(!xdr->iov); 8848c2ecf20Sopenharmony_ci return; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci WARN_ON_ONCE(fraglen); 8878c2ecf20Sopenharmony_ci fraglen = min_t(int, buf->len - len, buf->page_len); 8888c2ecf20Sopenharmony_ci buf->page_len -= fraglen; 8898c2ecf20Sopenharmony_ci buf->len -= fraglen; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci new = buf->page_base + buf->page_len; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci xdr->page_ptr = buf->pages + (new >> PAGE_SHIFT); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci if (buf->page_len) { 8968c2ecf20Sopenharmony_ci xdr->p = page_address(*xdr->page_ptr); 8978c2ecf20Sopenharmony_ci xdr->end = (void *)xdr->p + PAGE_SIZE; 8988c2ecf20Sopenharmony_ci xdr->p = (void *)xdr->p + (new % PAGE_SIZE); 8998c2ecf20Sopenharmony_ci WARN_ON_ONCE(xdr->iov); 9008c2ecf20Sopenharmony_ci return; 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci if (fraglen) 9038c2ecf20Sopenharmony_ci xdr->end = head->iov_base + head->iov_len; 9048c2ecf20Sopenharmony_ci /* (otherwise assume xdr->end is already set) */ 9058c2ecf20Sopenharmony_ci xdr->page_ptr--; 9068c2ecf20Sopenharmony_ci head->iov_len = len; 9078c2ecf20Sopenharmony_ci buf->len = len; 9088c2ecf20Sopenharmony_ci xdr->p = head->iov_base + head->iov_len; 9098c2ecf20Sopenharmony_ci xdr->iov = buf->head; 9108c2ecf20Sopenharmony_ci} 9118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xdr_truncate_encode); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci/** 9148c2ecf20Sopenharmony_ci * xdr_restrict_buflen - decrease available buffer space 9158c2ecf20Sopenharmony_ci * @xdr: pointer to xdr_stream 9168c2ecf20Sopenharmony_ci * @newbuflen: new maximum number of bytes available 9178c2ecf20Sopenharmony_ci * 9188c2ecf20Sopenharmony_ci * Adjust our idea of how much space is available in the buffer. 9198c2ecf20Sopenharmony_ci * If we've already used too much space in the buffer, returns -1. 9208c2ecf20Sopenharmony_ci * If the available space is already smaller than newbuflen, returns 0 9218c2ecf20Sopenharmony_ci * and does nothing. Otherwise, adjusts xdr->buf->buflen to newbuflen 9228c2ecf20Sopenharmony_ci * and ensures xdr->end is set at most offset newbuflen from the start 9238c2ecf20Sopenharmony_ci * of the buffer. 9248c2ecf20Sopenharmony_ci */ 9258c2ecf20Sopenharmony_ciint xdr_restrict_buflen(struct xdr_stream *xdr, int newbuflen) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 9288c2ecf20Sopenharmony_ci int left_in_this_buf = (void *)xdr->end - (void *)xdr->p; 9298c2ecf20Sopenharmony_ci int end_offset = buf->len + left_in_this_buf; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci if (newbuflen < 0 || newbuflen < buf->len) 9328c2ecf20Sopenharmony_ci return -1; 9338c2ecf20Sopenharmony_ci if (newbuflen > buf->buflen) 9348c2ecf20Sopenharmony_ci return 0; 9358c2ecf20Sopenharmony_ci if (newbuflen < end_offset) 9368c2ecf20Sopenharmony_ci xdr->end = (void *)xdr->end + newbuflen - end_offset; 9378c2ecf20Sopenharmony_ci buf->buflen = newbuflen; 9388c2ecf20Sopenharmony_ci return 0; 9398c2ecf20Sopenharmony_ci} 9408c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xdr_restrict_buflen); 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci/** 9438c2ecf20Sopenharmony_ci * xdr_write_pages - Insert a list of pages into an XDR buffer for sending 9448c2ecf20Sopenharmony_ci * @xdr: pointer to xdr_stream 9458c2ecf20Sopenharmony_ci * @pages: list of pages 9468c2ecf20Sopenharmony_ci * @base: offset of first byte 9478c2ecf20Sopenharmony_ci * @len: length of data in bytes 9488c2ecf20Sopenharmony_ci * 9498c2ecf20Sopenharmony_ci */ 9508c2ecf20Sopenharmony_civoid xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int base, 9518c2ecf20Sopenharmony_ci unsigned int len) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 9548c2ecf20Sopenharmony_ci struct kvec *iov = buf->tail; 9558c2ecf20Sopenharmony_ci buf->pages = pages; 9568c2ecf20Sopenharmony_ci buf->page_base = base; 9578c2ecf20Sopenharmony_ci buf->page_len = len; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci iov->iov_base = (char *)xdr->p; 9608c2ecf20Sopenharmony_ci iov->iov_len = 0; 9618c2ecf20Sopenharmony_ci xdr->iov = iov; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci if (len & 3) { 9648c2ecf20Sopenharmony_ci unsigned int pad = 4 - (len & 3); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci BUG_ON(xdr->p >= xdr->end); 9678c2ecf20Sopenharmony_ci iov->iov_base = (char *)xdr->p + (len & 3); 9688c2ecf20Sopenharmony_ci iov->iov_len += pad; 9698c2ecf20Sopenharmony_ci len += pad; 9708c2ecf20Sopenharmony_ci *xdr->p++ = 0; 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci buf->buflen += len; 9738c2ecf20Sopenharmony_ci buf->len += len; 9748c2ecf20Sopenharmony_ci} 9758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_write_pages); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_cistatic void xdr_set_iov(struct xdr_stream *xdr, struct kvec *iov, 9788c2ecf20Sopenharmony_ci unsigned int len) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci if (len > iov->iov_len) 9818c2ecf20Sopenharmony_ci len = iov->iov_len; 9828c2ecf20Sopenharmony_ci xdr->p = (__be32*)iov->iov_base; 9838c2ecf20Sopenharmony_ci xdr->end = (__be32*)(iov->iov_base + len); 9848c2ecf20Sopenharmony_ci xdr->iov = iov; 9858c2ecf20Sopenharmony_ci xdr->page_ptr = NULL; 9868c2ecf20Sopenharmony_ci} 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_cistatic int xdr_set_page_base(struct xdr_stream *xdr, 9898c2ecf20Sopenharmony_ci unsigned int base, unsigned int len) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci unsigned int pgnr; 9928c2ecf20Sopenharmony_ci unsigned int maxlen; 9938c2ecf20Sopenharmony_ci unsigned int pgoff; 9948c2ecf20Sopenharmony_ci unsigned int pgend; 9958c2ecf20Sopenharmony_ci void *kaddr; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci maxlen = xdr->buf->page_len; 9988c2ecf20Sopenharmony_ci if (base >= maxlen) 9998c2ecf20Sopenharmony_ci return -EINVAL; 10008c2ecf20Sopenharmony_ci maxlen -= base; 10018c2ecf20Sopenharmony_ci if (len > maxlen) 10028c2ecf20Sopenharmony_ci len = maxlen; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci base += xdr->buf->page_base; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci pgnr = base >> PAGE_SHIFT; 10078c2ecf20Sopenharmony_ci xdr->page_ptr = &xdr->buf->pages[pgnr]; 10088c2ecf20Sopenharmony_ci kaddr = page_address(*xdr->page_ptr); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci pgoff = base & ~PAGE_MASK; 10118c2ecf20Sopenharmony_ci xdr->p = (__be32*)(kaddr + pgoff); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci pgend = pgoff + len; 10148c2ecf20Sopenharmony_ci if (pgend > PAGE_SIZE) 10158c2ecf20Sopenharmony_ci pgend = PAGE_SIZE; 10168c2ecf20Sopenharmony_ci xdr->end = (__be32*)(kaddr + pgend); 10178c2ecf20Sopenharmony_ci xdr->iov = NULL; 10188c2ecf20Sopenharmony_ci return 0; 10198c2ecf20Sopenharmony_ci} 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_cistatic void xdr_set_page(struct xdr_stream *xdr, unsigned int base, 10228c2ecf20Sopenharmony_ci unsigned int len) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci if (xdr_set_page_base(xdr, base, len) < 0) 10258c2ecf20Sopenharmony_ci xdr_set_iov(xdr, xdr->buf->tail, xdr->nwords << 2); 10268c2ecf20Sopenharmony_ci} 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_cistatic void xdr_set_next_page(struct xdr_stream *xdr) 10298c2ecf20Sopenharmony_ci{ 10308c2ecf20Sopenharmony_ci unsigned int newbase; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci newbase = (1 + xdr->page_ptr - xdr->buf->pages) << PAGE_SHIFT; 10338c2ecf20Sopenharmony_ci newbase -= xdr->buf->page_base; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci xdr_set_page(xdr, newbase, PAGE_SIZE); 10368c2ecf20Sopenharmony_ci} 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_cistatic bool xdr_set_next_buffer(struct xdr_stream *xdr) 10398c2ecf20Sopenharmony_ci{ 10408c2ecf20Sopenharmony_ci if (xdr->page_ptr != NULL) 10418c2ecf20Sopenharmony_ci xdr_set_next_page(xdr); 10428c2ecf20Sopenharmony_ci else if (xdr->iov == xdr->buf->head) { 10438c2ecf20Sopenharmony_ci xdr_set_page(xdr, 0, PAGE_SIZE); 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci return xdr->p != xdr->end; 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci/** 10498c2ecf20Sopenharmony_ci * xdr_init_decode - Initialize an xdr_stream for decoding data. 10508c2ecf20Sopenharmony_ci * @xdr: pointer to xdr_stream struct 10518c2ecf20Sopenharmony_ci * @buf: pointer to XDR buffer from which to decode data 10528c2ecf20Sopenharmony_ci * @p: current pointer inside XDR buffer 10538c2ecf20Sopenharmony_ci * @rqst: pointer to controlling rpc_rqst, for debugging 10548c2ecf20Sopenharmony_ci */ 10558c2ecf20Sopenharmony_civoid xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p, 10568c2ecf20Sopenharmony_ci struct rpc_rqst *rqst) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci xdr->buf = buf; 10598c2ecf20Sopenharmony_ci xdr->scratch.iov_base = NULL; 10608c2ecf20Sopenharmony_ci xdr->scratch.iov_len = 0; 10618c2ecf20Sopenharmony_ci xdr->nwords = XDR_QUADLEN(buf->len); 10628c2ecf20Sopenharmony_ci if (buf->head[0].iov_len != 0) 10638c2ecf20Sopenharmony_ci xdr_set_iov(xdr, buf->head, buf->len); 10648c2ecf20Sopenharmony_ci else if (buf->page_len != 0) 10658c2ecf20Sopenharmony_ci xdr_set_page_base(xdr, 0, buf->len); 10668c2ecf20Sopenharmony_ci else 10678c2ecf20Sopenharmony_ci xdr_set_iov(xdr, buf->head, buf->len); 10688c2ecf20Sopenharmony_ci if (p != NULL && p > xdr->p && xdr->end >= p) { 10698c2ecf20Sopenharmony_ci xdr->nwords -= p - xdr->p; 10708c2ecf20Sopenharmony_ci xdr->p = p; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci xdr->rqst = rqst; 10738c2ecf20Sopenharmony_ci} 10748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_init_decode); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci/** 10778c2ecf20Sopenharmony_ci * xdr_init_decode_pages - Initialize an xdr_stream for decoding into pages 10788c2ecf20Sopenharmony_ci * @xdr: pointer to xdr_stream struct 10798c2ecf20Sopenharmony_ci * @buf: pointer to XDR buffer from which to decode data 10808c2ecf20Sopenharmony_ci * @pages: list of pages to decode into 10818c2ecf20Sopenharmony_ci * @len: length in bytes of buffer in pages 10828c2ecf20Sopenharmony_ci */ 10838c2ecf20Sopenharmony_civoid xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf, 10848c2ecf20Sopenharmony_ci struct page **pages, unsigned int len) 10858c2ecf20Sopenharmony_ci{ 10868c2ecf20Sopenharmony_ci memset(buf, 0, sizeof(*buf)); 10878c2ecf20Sopenharmony_ci buf->pages = pages; 10888c2ecf20Sopenharmony_ci buf->page_len = len; 10898c2ecf20Sopenharmony_ci buf->buflen = len; 10908c2ecf20Sopenharmony_ci buf->len = len; 10918c2ecf20Sopenharmony_ci xdr_init_decode(xdr, buf, NULL, NULL); 10928c2ecf20Sopenharmony_ci} 10938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_init_decode_pages); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistatic __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci unsigned int nwords = XDR_QUADLEN(nbytes); 10988c2ecf20Sopenharmony_ci __be32 *p = xdr->p; 10998c2ecf20Sopenharmony_ci __be32 *q = p + nwords; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci if (unlikely(nwords > xdr->nwords || q > xdr->end || q < p)) 11028c2ecf20Sopenharmony_ci return NULL; 11038c2ecf20Sopenharmony_ci xdr->p = q; 11048c2ecf20Sopenharmony_ci xdr->nwords -= nwords; 11058c2ecf20Sopenharmony_ci return p; 11068c2ecf20Sopenharmony_ci} 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci/** 11098c2ecf20Sopenharmony_ci * xdr_set_scratch_buffer - Attach a scratch buffer for decoding data. 11108c2ecf20Sopenharmony_ci * @xdr: pointer to xdr_stream struct 11118c2ecf20Sopenharmony_ci * @buf: pointer to an empty buffer 11128c2ecf20Sopenharmony_ci * @buflen: size of 'buf' 11138c2ecf20Sopenharmony_ci * 11148c2ecf20Sopenharmony_ci * The scratch buffer is used when decoding from an array of pages. 11158c2ecf20Sopenharmony_ci * If an xdr_inline_decode() call spans across page boundaries, then 11168c2ecf20Sopenharmony_ci * we copy the data into the scratch buffer in order to allow linear 11178c2ecf20Sopenharmony_ci * access. 11188c2ecf20Sopenharmony_ci */ 11198c2ecf20Sopenharmony_civoid xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci xdr->scratch.iov_base = buf; 11228c2ecf20Sopenharmony_ci xdr->scratch.iov_len = buflen; 11238c2ecf20Sopenharmony_ci} 11248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_set_scratch_buffer); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_cistatic __be32 *xdr_copy_to_scratch(struct xdr_stream *xdr, size_t nbytes) 11278c2ecf20Sopenharmony_ci{ 11288c2ecf20Sopenharmony_ci __be32 *p; 11298c2ecf20Sopenharmony_ci char *cpdest = xdr->scratch.iov_base; 11308c2ecf20Sopenharmony_ci size_t cplen = (char *)xdr->end - (char *)xdr->p; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci if (nbytes > xdr->scratch.iov_len) 11338c2ecf20Sopenharmony_ci goto out_overflow; 11348c2ecf20Sopenharmony_ci p = __xdr_inline_decode(xdr, cplen); 11358c2ecf20Sopenharmony_ci if (p == NULL) 11368c2ecf20Sopenharmony_ci return NULL; 11378c2ecf20Sopenharmony_ci memcpy(cpdest, p, cplen); 11388c2ecf20Sopenharmony_ci if (!xdr_set_next_buffer(xdr)) 11398c2ecf20Sopenharmony_ci goto out_overflow; 11408c2ecf20Sopenharmony_ci cpdest += cplen; 11418c2ecf20Sopenharmony_ci nbytes -= cplen; 11428c2ecf20Sopenharmony_ci p = __xdr_inline_decode(xdr, nbytes); 11438c2ecf20Sopenharmony_ci if (p == NULL) 11448c2ecf20Sopenharmony_ci return NULL; 11458c2ecf20Sopenharmony_ci memcpy(cpdest, p, nbytes); 11468c2ecf20Sopenharmony_ci return xdr->scratch.iov_base; 11478c2ecf20Sopenharmony_ciout_overflow: 11488c2ecf20Sopenharmony_ci trace_rpc_xdr_overflow(xdr, nbytes); 11498c2ecf20Sopenharmony_ci return NULL; 11508c2ecf20Sopenharmony_ci} 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci/** 11538c2ecf20Sopenharmony_ci * xdr_inline_decode - Retrieve XDR data to decode 11548c2ecf20Sopenharmony_ci * @xdr: pointer to xdr_stream struct 11558c2ecf20Sopenharmony_ci * @nbytes: number of bytes of data to decode 11568c2ecf20Sopenharmony_ci * 11578c2ecf20Sopenharmony_ci * Check if the input buffer is long enough to enable us to decode 11588c2ecf20Sopenharmony_ci * 'nbytes' more bytes of data starting at the current position. 11598c2ecf20Sopenharmony_ci * If so return the current pointer, then update the current 11608c2ecf20Sopenharmony_ci * pointer position. 11618c2ecf20Sopenharmony_ci */ 11628c2ecf20Sopenharmony_ci__be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) 11638c2ecf20Sopenharmony_ci{ 11648c2ecf20Sopenharmony_ci __be32 *p; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci if (unlikely(nbytes == 0)) 11678c2ecf20Sopenharmony_ci return xdr->p; 11688c2ecf20Sopenharmony_ci if (xdr->p == xdr->end && !xdr_set_next_buffer(xdr)) 11698c2ecf20Sopenharmony_ci goto out_overflow; 11708c2ecf20Sopenharmony_ci p = __xdr_inline_decode(xdr, nbytes); 11718c2ecf20Sopenharmony_ci if (p != NULL) 11728c2ecf20Sopenharmony_ci return p; 11738c2ecf20Sopenharmony_ci return xdr_copy_to_scratch(xdr, nbytes); 11748c2ecf20Sopenharmony_ciout_overflow: 11758c2ecf20Sopenharmony_ci trace_rpc_xdr_overflow(xdr, nbytes); 11768c2ecf20Sopenharmony_ci return NULL; 11778c2ecf20Sopenharmony_ci} 11788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_inline_decode); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_cistatic void xdr_realign_pages(struct xdr_stream *xdr) 11818c2ecf20Sopenharmony_ci{ 11828c2ecf20Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 11838c2ecf20Sopenharmony_ci struct kvec *iov = buf->head; 11848c2ecf20Sopenharmony_ci unsigned int cur = xdr_stream_pos(xdr); 11858c2ecf20Sopenharmony_ci unsigned int copied, offset; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci /* Realign pages to current pointer position */ 11888c2ecf20Sopenharmony_ci if (iov->iov_len > cur) { 11898c2ecf20Sopenharmony_ci offset = iov->iov_len - cur; 11908c2ecf20Sopenharmony_ci copied = xdr_shrink_bufhead(buf, offset); 11918c2ecf20Sopenharmony_ci trace_rpc_xdr_alignment(xdr, offset, copied); 11928c2ecf20Sopenharmony_ci xdr->nwords = XDR_QUADLEN(buf->len - cur); 11938c2ecf20Sopenharmony_ci } 11948c2ecf20Sopenharmony_ci} 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_cistatic unsigned int xdr_align_pages(struct xdr_stream *xdr, unsigned int len) 11978c2ecf20Sopenharmony_ci{ 11988c2ecf20Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 11998c2ecf20Sopenharmony_ci unsigned int nwords = XDR_QUADLEN(len); 12008c2ecf20Sopenharmony_ci unsigned int cur = xdr_stream_pos(xdr); 12018c2ecf20Sopenharmony_ci unsigned int copied, offset; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci if (xdr->nwords == 0) 12048c2ecf20Sopenharmony_ci return 0; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci xdr_realign_pages(xdr); 12078c2ecf20Sopenharmony_ci if (nwords > xdr->nwords) { 12088c2ecf20Sopenharmony_ci nwords = xdr->nwords; 12098c2ecf20Sopenharmony_ci len = nwords << 2; 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci if (buf->page_len <= len) 12128c2ecf20Sopenharmony_ci len = buf->page_len; 12138c2ecf20Sopenharmony_ci else if (nwords < xdr->nwords) { 12148c2ecf20Sopenharmony_ci /* Truncate page data and move it into the tail */ 12158c2ecf20Sopenharmony_ci offset = buf->page_len - len; 12168c2ecf20Sopenharmony_ci copied = xdr_shrink_pagelen(buf, offset); 12178c2ecf20Sopenharmony_ci trace_rpc_xdr_alignment(xdr, offset, copied); 12188c2ecf20Sopenharmony_ci xdr->nwords = XDR_QUADLEN(buf->len - cur); 12198c2ecf20Sopenharmony_ci } 12208c2ecf20Sopenharmony_ci return len; 12218c2ecf20Sopenharmony_ci} 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci/** 12248c2ecf20Sopenharmony_ci * xdr_read_pages - Ensure page-based XDR data to decode is aligned at current pointer position 12258c2ecf20Sopenharmony_ci * @xdr: pointer to xdr_stream struct 12268c2ecf20Sopenharmony_ci * @len: number of bytes of page data 12278c2ecf20Sopenharmony_ci * 12288c2ecf20Sopenharmony_ci * Moves data beyond the current pointer position from the XDR head[] buffer 12298c2ecf20Sopenharmony_ci * into the page list. Any data that lies beyond current position + "len" 12308c2ecf20Sopenharmony_ci * bytes is moved into the XDR tail[]. 12318c2ecf20Sopenharmony_ci * 12328c2ecf20Sopenharmony_ci * Returns the number of XDR encoded bytes now contained in the pages 12338c2ecf20Sopenharmony_ci */ 12348c2ecf20Sopenharmony_ciunsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len) 12358c2ecf20Sopenharmony_ci{ 12368c2ecf20Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 12378c2ecf20Sopenharmony_ci struct kvec *iov; 12388c2ecf20Sopenharmony_ci unsigned int nwords; 12398c2ecf20Sopenharmony_ci unsigned int end; 12408c2ecf20Sopenharmony_ci unsigned int padding; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci len = xdr_align_pages(xdr, len); 12438c2ecf20Sopenharmony_ci if (len == 0) 12448c2ecf20Sopenharmony_ci return 0; 12458c2ecf20Sopenharmony_ci nwords = XDR_QUADLEN(len); 12468c2ecf20Sopenharmony_ci padding = (nwords << 2) - len; 12478c2ecf20Sopenharmony_ci xdr->iov = iov = buf->tail; 12488c2ecf20Sopenharmony_ci /* Compute remaining message length. */ 12498c2ecf20Sopenharmony_ci end = ((xdr->nwords - nwords) << 2) + padding; 12508c2ecf20Sopenharmony_ci if (end > iov->iov_len) 12518c2ecf20Sopenharmony_ci end = iov->iov_len; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci /* 12548c2ecf20Sopenharmony_ci * Position current pointer at beginning of tail, and 12558c2ecf20Sopenharmony_ci * set remaining message length. 12568c2ecf20Sopenharmony_ci */ 12578c2ecf20Sopenharmony_ci xdr->p = (__be32 *)((char *)iov->iov_base + padding); 12588c2ecf20Sopenharmony_ci xdr->end = (__be32 *)((char *)iov->iov_base + end); 12598c2ecf20Sopenharmony_ci xdr->page_ptr = NULL; 12608c2ecf20Sopenharmony_ci xdr->nwords = XDR_QUADLEN(end - padding); 12618c2ecf20Sopenharmony_ci return len; 12628c2ecf20Sopenharmony_ci} 12638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_read_pages); 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ciuint64_t xdr_align_data(struct xdr_stream *xdr, uint64_t offset, uint32_t length) 12668c2ecf20Sopenharmony_ci{ 12678c2ecf20Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 12688c2ecf20Sopenharmony_ci unsigned int from, bytes; 12698c2ecf20Sopenharmony_ci unsigned int shift = 0; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci if ((offset + length) < offset || 12728c2ecf20Sopenharmony_ci (offset + length) > buf->page_len) 12738c2ecf20Sopenharmony_ci length = buf->page_len - offset; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci xdr_realign_pages(xdr); 12768c2ecf20Sopenharmony_ci from = xdr_page_pos(xdr); 12778c2ecf20Sopenharmony_ci bytes = xdr->nwords << 2; 12788c2ecf20Sopenharmony_ci if (length < bytes) 12798c2ecf20Sopenharmony_ci bytes = length; 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci /* Move page data to the left */ 12828c2ecf20Sopenharmony_ci if (from > offset) { 12838c2ecf20Sopenharmony_ci shift = min_t(unsigned int, bytes, buf->page_len - from); 12848c2ecf20Sopenharmony_ci _shift_data_left_pages(buf->pages, 12858c2ecf20Sopenharmony_ci buf->page_base + offset, 12868c2ecf20Sopenharmony_ci buf->page_base + from, 12878c2ecf20Sopenharmony_ci shift); 12888c2ecf20Sopenharmony_ci bytes -= shift; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci /* Move tail data into the pages, if necessary */ 12918c2ecf20Sopenharmony_ci if (bytes > 0) 12928c2ecf20Sopenharmony_ci _shift_data_left_tail(buf, offset + shift, bytes); 12938c2ecf20Sopenharmony_ci } 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci xdr->nwords -= XDR_QUADLEN(length); 12968c2ecf20Sopenharmony_ci xdr_set_page(xdr, from + length, PAGE_SIZE); 12978c2ecf20Sopenharmony_ci return length; 12988c2ecf20Sopenharmony_ci} 12998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_align_data); 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ciuint64_t xdr_expand_hole(struct xdr_stream *xdr, uint64_t offset, uint64_t length) 13028c2ecf20Sopenharmony_ci{ 13038c2ecf20Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 13048c2ecf20Sopenharmony_ci unsigned int bytes; 13058c2ecf20Sopenharmony_ci unsigned int from; 13068c2ecf20Sopenharmony_ci unsigned int truncated = 0; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci if ((offset + length) < offset || 13098c2ecf20Sopenharmony_ci (offset + length) > buf->page_len) 13108c2ecf20Sopenharmony_ci length = buf->page_len - offset; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci xdr_realign_pages(xdr); 13138c2ecf20Sopenharmony_ci from = xdr_page_pos(xdr); 13148c2ecf20Sopenharmony_ci bytes = xdr->nwords << 2; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci if (offset + length + bytes > buf->page_len) { 13178c2ecf20Sopenharmony_ci unsigned int shift = (offset + length + bytes) - buf->page_len; 13188c2ecf20Sopenharmony_ci unsigned int res = _shift_data_right_tail(buf, from + bytes - shift, shift); 13198c2ecf20Sopenharmony_ci truncated = shift - res; 13208c2ecf20Sopenharmony_ci xdr->nwords -= XDR_QUADLEN(truncated); 13218c2ecf20Sopenharmony_ci bytes -= shift; 13228c2ecf20Sopenharmony_ci } 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci /* Now move the page data over and zero pages */ 13258c2ecf20Sopenharmony_ci if (bytes > 0) 13268c2ecf20Sopenharmony_ci _shift_data_right_pages(buf->pages, 13278c2ecf20Sopenharmony_ci buf->page_base + offset + length, 13288c2ecf20Sopenharmony_ci buf->page_base + from, 13298c2ecf20Sopenharmony_ci bytes); 13308c2ecf20Sopenharmony_ci _zero_pages(buf->pages, buf->page_base + offset, length); 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci buf->len += length - (from - offset) - truncated; 13338c2ecf20Sopenharmony_ci xdr_set_page(xdr, offset + length, PAGE_SIZE); 13348c2ecf20Sopenharmony_ci return length; 13358c2ecf20Sopenharmony_ci} 13368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_expand_hole); 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci/** 13398c2ecf20Sopenharmony_ci * xdr_enter_page - decode data from the XDR page 13408c2ecf20Sopenharmony_ci * @xdr: pointer to xdr_stream struct 13418c2ecf20Sopenharmony_ci * @len: number of bytes of page data 13428c2ecf20Sopenharmony_ci * 13438c2ecf20Sopenharmony_ci * Moves data beyond the current pointer position from the XDR head[] buffer 13448c2ecf20Sopenharmony_ci * into the page list. Any data that lies beyond current position + "len" 13458c2ecf20Sopenharmony_ci * bytes is moved into the XDR tail[]. The current pointer is then 13468c2ecf20Sopenharmony_ci * repositioned at the beginning of the first XDR page. 13478c2ecf20Sopenharmony_ci */ 13488c2ecf20Sopenharmony_civoid xdr_enter_page(struct xdr_stream *xdr, unsigned int len) 13498c2ecf20Sopenharmony_ci{ 13508c2ecf20Sopenharmony_ci len = xdr_align_pages(xdr, len); 13518c2ecf20Sopenharmony_ci /* 13528c2ecf20Sopenharmony_ci * Position current pointer at beginning of tail, and 13538c2ecf20Sopenharmony_ci * set remaining message length. 13548c2ecf20Sopenharmony_ci */ 13558c2ecf20Sopenharmony_ci if (len != 0) 13568c2ecf20Sopenharmony_ci xdr_set_page_base(xdr, 0, len); 13578c2ecf20Sopenharmony_ci} 13588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_enter_page); 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_cistatic const struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0}; 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_civoid 13638c2ecf20Sopenharmony_cixdr_buf_from_iov(struct kvec *iov, struct xdr_buf *buf) 13648c2ecf20Sopenharmony_ci{ 13658c2ecf20Sopenharmony_ci buf->head[0] = *iov; 13668c2ecf20Sopenharmony_ci buf->tail[0] = empty_iov; 13678c2ecf20Sopenharmony_ci buf->page_len = 0; 13688c2ecf20Sopenharmony_ci buf->buflen = buf->len = iov->iov_len; 13698c2ecf20Sopenharmony_ci} 13708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_buf_from_iov); 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci/** 13738c2ecf20Sopenharmony_ci * xdr_buf_subsegment - set subbuf to a portion of buf 13748c2ecf20Sopenharmony_ci * @buf: an xdr buffer 13758c2ecf20Sopenharmony_ci * @subbuf: the result buffer 13768c2ecf20Sopenharmony_ci * @base: beginning of range in bytes 13778c2ecf20Sopenharmony_ci * @len: length of range in bytes 13788c2ecf20Sopenharmony_ci * 13798c2ecf20Sopenharmony_ci * sets @subbuf to an xdr buffer representing the portion of @buf of 13808c2ecf20Sopenharmony_ci * length @len starting at offset @base. 13818c2ecf20Sopenharmony_ci * 13828c2ecf20Sopenharmony_ci * @buf and @subbuf may be pointers to the same struct xdr_buf. 13838c2ecf20Sopenharmony_ci * 13848c2ecf20Sopenharmony_ci * Returns -1 if base of length are out of bounds. 13858c2ecf20Sopenharmony_ci */ 13868c2ecf20Sopenharmony_ciint 13878c2ecf20Sopenharmony_cixdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf, 13888c2ecf20Sopenharmony_ci unsigned int base, unsigned int len) 13898c2ecf20Sopenharmony_ci{ 13908c2ecf20Sopenharmony_ci subbuf->buflen = subbuf->len = len; 13918c2ecf20Sopenharmony_ci if (base < buf->head[0].iov_len) { 13928c2ecf20Sopenharmony_ci subbuf->head[0].iov_base = buf->head[0].iov_base + base; 13938c2ecf20Sopenharmony_ci subbuf->head[0].iov_len = min_t(unsigned int, len, 13948c2ecf20Sopenharmony_ci buf->head[0].iov_len - base); 13958c2ecf20Sopenharmony_ci len -= subbuf->head[0].iov_len; 13968c2ecf20Sopenharmony_ci base = 0; 13978c2ecf20Sopenharmony_ci } else { 13988c2ecf20Sopenharmony_ci base -= buf->head[0].iov_len; 13998c2ecf20Sopenharmony_ci subbuf->head[0].iov_base = buf->head[0].iov_base; 14008c2ecf20Sopenharmony_ci subbuf->head[0].iov_len = 0; 14018c2ecf20Sopenharmony_ci } 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci if (base < buf->page_len) { 14048c2ecf20Sopenharmony_ci subbuf->page_len = min(buf->page_len - base, len); 14058c2ecf20Sopenharmony_ci base += buf->page_base; 14068c2ecf20Sopenharmony_ci subbuf->page_base = base & ~PAGE_MASK; 14078c2ecf20Sopenharmony_ci subbuf->pages = &buf->pages[base >> PAGE_SHIFT]; 14088c2ecf20Sopenharmony_ci len -= subbuf->page_len; 14098c2ecf20Sopenharmony_ci base = 0; 14108c2ecf20Sopenharmony_ci } else { 14118c2ecf20Sopenharmony_ci base -= buf->page_len; 14128c2ecf20Sopenharmony_ci subbuf->pages = buf->pages; 14138c2ecf20Sopenharmony_ci subbuf->page_base = 0; 14148c2ecf20Sopenharmony_ci subbuf->page_len = 0; 14158c2ecf20Sopenharmony_ci } 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci if (base < buf->tail[0].iov_len) { 14188c2ecf20Sopenharmony_ci subbuf->tail[0].iov_base = buf->tail[0].iov_base + base; 14198c2ecf20Sopenharmony_ci subbuf->tail[0].iov_len = min_t(unsigned int, len, 14208c2ecf20Sopenharmony_ci buf->tail[0].iov_len - base); 14218c2ecf20Sopenharmony_ci len -= subbuf->tail[0].iov_len; 14228c2ecf20Sopenharmony_ci base = 0; 14238c2ecf20Sopenharmony_ci } else { 14248c2ecf20Sopenharmony_ci base -= buf->tail[0].iov_len; 14258c2ecf20Sopenharmony_ci subbuf->tail[0].iov_base = buf->tail[0].iov_base; 14268c2ecf20Sopenharmony_ci subbuf->tail[0].iov_len = 0; 14278c2ecf20Sopenharmony_ci } 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci if (base || len) 14308c2ecf20Sopenharmony_ci return -1; 14318c2ecf20Sopenharmony_ci return 0; 14328c2ecf20Sopenharmony_ci} 14338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_buf_subsegment); 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci/** 14368c2ecf20Sopenharmony_ci * xdr_buf_trim - lop at most "len" bytes off the end of "buf" 14378c2ecf20Sopenharmony_ci * @buf: buf to be trimmed 14388c2ecf20Sopenharmony_ci * @len: number of bytes to reduce "buf" by 14398c2ecf20Sopenharmony_ci * 14408c2ecf20Sopenharmony_ci * Trim an xdr_buf by the given number of bytes by fixing up the lengths. Note 14418c2ecf20Sopenharmony_ci * that it's possible that we'll trim less than that amount if the xdr_buf is 14428c2ecf20Sopenharmony_ci * too small, or if (for instance) it's all in the head and the parser has 14438c2ecf20Sopenharmony_ci * already read too far into it. 14448c2ecf20Sopenharmony_ci */ 14458c2ecf20Sopenharmony_civoid xdr_buf_trim(struct xdr_buf *buf, unsigned int len) 14468c2ecf20Sopenharmony_ci{ 14478c2ecf20Sopenharmony_ci size_t cur; 14488c2ecf20Sopenharmony_ci unsigned int trim = len; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci if (buf->tail[0].iov_len) { 14518c2ecf20Sopenharmony_ci cur = min_t(size_t, buf->tail[0].iov_len, trim); 14528c2ecf20Sopenharmony_ci buf->tail[0].iov_len -= cur; 14538c2ecf20Sopenharmony_ci trim -= cur; 14548c2ecf20Sopenharmony_ci if (!trim) 14558c2ecf20Sopenharmony_ci goto fix_len; 14568c2ecf20Sopenharmony_ci } 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci if (buf->page_len) { 14598c2ecf20Sopenharmony_ci cur = min_t(unsigned int, buf->page_len, trim); 14608c2ecf20Sopenharmony_ci buf->page_len -= cur; 14618c2ecf20Sopenharmony_ci trim -= cur; 14628c2ecf20Sopenharmony_ci if (!trim) 14638c2ecf20Sopenharmony_ci goto fix_len; 14648c2ecf20Sopenharmony_ci } 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci if (buf->head[0].iov_len) { 14678c2ecf20Sopenharmony_ci cur = min_t(size_t, buf->head[0].iov_len, trim); 14688c2ecf20Sopenharmony_ci buf->head[0].iov_len -= cur; 14698c2ecf20Sopenharmony_ci trim -= cur; 14708c2ecf20Sopenharmony_ci } 14718c2ecf20Sopenharmony_cifix_len: 14728c2ecf20Sopenharmony_ci buf->len -= (len - trim); 14738c2ecf20Sopenharmony_ci} 14748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_buf_trim); 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_cistatic void __read_bytes_from_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len) 14778c2ecf20Sopenharmony_ci{ 14788c2ecf20Sopenharmony_ci unsigned int this_len; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci this_len = min_t(unsigned int, len, subbuf->head[0].iov_len); 14818c2ecf20Sopenharmony_ci memcpy(obj, subbuf->head[0].iov_base, this_len); 14828c2ecf20Sopenharmony_ci len -= this_len; 14838c2ecf20Sopenharmony_ci obj += this_len; 14848c2ecf20Sopenharmony_ci this_len = min_t(unsigned int, len, subbuf->page_len); 14858c2ecf20Sopenharmony_ci if (this_len) 14868c2ecf20Sopenharmony_ci _copy_from_pages(obj, subbuf->pages, subbuf->page_base, this_len); 14878c2ecf20Sopenharmony_ci len -= this_len; 14888c2ecf20Sopenharmony_ci obj += this_len; 14898c2ecf20Sopenharmony_ci this_len = min_t(unsigned int, len, subbuf->tail[0].iov_len); 14908c2ecf20Sopenharmony_ci memcpy(obj, subbuf->tail[0].iov_base, this_len); 14918c2ecf20Sopenharmony_ci} 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci/* obj is assumed to point to allocated memory of size at least len: */ 14948c2ecf20Sopenharmony_ciint read_bytes_from_xdr_buf(struct xdr_buf *buf, unsigned int base, void *obj, unsigned int len) 14958c2ecf20Sopenharmony_ci{ 14968c2ecf20Sopenharmony_ci struct xdr_buf subbuf; 14978c2ecf20Sopenharmony_ci int status; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci status = xdr_buf_subsegment(buf, &subbuf, base, len); 15008c2ecf20Sopenharmony_ci if (status != 0) 15018c2ecf20Sopenharmony_ci return status; 15028c2ecf20Sopenharmony_ci __read_bytes_from_xdr_buf(&subbuf, obj, len); 15038c2ecf20Sopenharmony_ci return 0; 15048c2ecf20Sopenharmony_ci} 15058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(read_bytes_from_xdr_buf); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_cistatic void __write_bytes_to_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len) 15088c2ecf20Sopenharmony_ci{ 15098c2ecf20Sopenharmony_ci unsigned int this_len; 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci this_len = min_t(unsigned int, len, subbuf->head[0].iov_len); 15128c2ecf20Sopenharmony_ci memcpy(subbuf->head[0].iov_base, obj, this_len); 15138c2ecf20Sopenharmony_ci len -= this_len; 15148c2ecf20Sopenharmony_ci obj += this_len; 15158c2ecf20Sopenharmony_ci this_len = min_t(unsigned int, len, subbuf->page_len); 15168c2ecf20Sopenharmony_ci if (this_len) 15178c2ecf20Sopenharmony_ci _copy_to_pages(subbuf->pages, subbuf->page_base, obj, this_len); 15188c2ecf20Sopenharmony_ci len -= this_len; 15198c2ecf20Sopenharmony_ci obj += this_len; 15208c2ecf20Sopenharmony_ci this_len = min_t(unsigned int, len, subbuf->tail[0].iov_len); 15218c2ecf20Sopenharmony_ci memcpy(subbuf->tail[0].iov_base, obj, this_len); 15228c2ecf20Sopenharmony_ci} 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci/* obj is assumed to point to allocated memory of size at least len: */ 15258c2ecf20Sopenharmony_ciint write_bytes_to_xdr_buf(struct xdr_buf *buf, unsigned int base, void *obj, unsigned int len) 15268c2ecf20Sopenharmony_ci{ 15278c2ecf20Sopenharmony_ci struct xdr_buf subbuf; 15288c2ecf20Sopenharmony_ci int status; 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci status = xdr_buf_subsegment(buf, &subbuf, base, len); 15318c2ecf20Sopenharmony_ci if (status != 0) 15328c2ecf20Sopenharmony_ci return status; 15338c2ecf20Sopenharmony_ci __write_bytes_to_xdr_buf(&subbuf, obj, len); 15348c2ecf20Sopenharmony_ci return 0; 15358c2ecf20Sopenharmony_ci} 15368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(write_bytes_to_xdr_buf); 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ciint 15398c2ecf20Sopenharmony_cixdr_decode_word(struct xdr_buf *buf, unsigned int base, u32 *obj) 15408c2ecf20Sopenharmony_ci{ 15418c2ecf20Sopenharmony_ci __be32 raw; 15428c2ecf20Sopenharmony_ci int status; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj)); 15458c2ecf20Sopenharmony_ci if (status) 15468c2ecf20Sopenharmony_ci return status; 15478c2ecf20Sopenharmony_ci *obj = be32_to_cpu(raw); 15488c2ecf20Sopenharmony_ci return 0; 15498c2ecf20Sopenharmony_ci} 15508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_decode_word); 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ciint 15538c2ecf20Sopenharmony_cixdr_encode_word(struct xdr_buf *buf, unsigned int base, u32 obj) 15548c2ecf20Sopenharmony_ci{ 15558c2ecf20Sopenharmony_ci __be32 raw = cpu_to_be32(obj); 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci return write_bytes_to_xdr_buf(buf, base, &raw, sizeof(obj)); 15588c2ecf20Sopenharmony_ci} 15598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_encode_word); 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci/* Returns 0 on success, or else a negative error code. */ 15628c2ecf20Sopenharmony_cistatic int 15638c2ecf20Sopenharmony_cixdr_xcode_array2(struct xdr_buf *buf, unsigned int base, 15648c2ecf20Sopenharmony_ci struct xdr_array2_desc *desc, int encode) 15658c2ecf20Sopenharmony_ci{ 15668c2ecf20Sopenharmony_ci char *elem = NULL, *c; 15678c2ecf20Sopenharmony_ci unsigned int copied = 0, todo, avail_here; 15688c2ecf20Sopenharmony_ci struct page **ppages = NULL; 15698c2ecf20Sopenharmony_ci int err; 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci if (encode) { 15728c2ecf20Sopenharmony_ci if (xdr_encode_word(buf, base, desc->array_len) != 0) 15738c2ecf20Sopenharmony_ci return -EINVAL; 15748c2ecf20Sopenharmony_ci } else { 15758c2ecf20Sopenharmony_ci if (xdr_decode_word(buf, base, &desc->array_len) != 0 || 15768c2ecf20Sopenharmony_ci desc->array_len > desc->array_maxlen || 15778c2ecf20Sopenharmony_ci (unsigned long) base + 4 + desc->array_len * 15788c2ecf20Sopenharmony_ci desc->elem_size > buf->len) 15798c2ecf20Sopenharmony_ci return -EINVAL; 15808c2ecf20Sopenharmony_ci } 15818c2ecf20Sopenharmony_ci base += 4; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci if (!desc->xcode) 15848c2ecf20Sopenharmony_ci return 0; 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci todo = desc->array_len * desc->elem_size; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci /* process head */ 15898c2ecf20Sopenharmony_ci if (todo && base < buf->head->iov_len) { 15908c2ecf20Sopenharmony_ci c = buf->head->iov_base + base; 15918c2ecf20Sopenharmony_ci avail_here = min_t(unsigned int, todo, 15928c2ecf20Sopenharmony_ci buf->head->iov_len - base); 15938c2ecf20Sopenharmony_ci todo -= avail_here; 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci while (avail_here >= desc->elem_size) { 15968c2ecf20Sopenharmony_ci err = desc->xcode(desc, c); 15978c2ecf20Sopenharmony_ci if (err) 15988c2ecf20Sopenharmony_ci goto out; 15998c2ecf20Sopenharmony_ci c += desc->elem_size; 16008c2ecf20Sopenharmony_ci avail_here -= desc->elem_size; 16018c2ecf20Sopenharmony_ci } 16028c2ecf20Sopenharmony_ci if (avail_here) { 16038c2ecf20Sopenharmony_ci if (!elem) { 16048c2ecf20Sopenharmony_ci elem = kmalloc(desc->elem_size, GFP_KERNEL); 16058c2ecf20Sopenharmony_ci err = -ENOMEM; 16068c2ecf20Sopenharmony_ci if (!elem) 16078c2ecf20Sopenharmony_ci goto out; 16088c2ecf20Sopenharmony_ci } 16098c2ecf20Sopenharmony_ci if (encode) { 16108c2ecf20Sopenharmony_ci err = desc->xcode(desc, elem); 16118c2ecf20Sopenharmony_ci if (err) 16128c2ecf20Sopenharmony_ci goto out; 16138c2ecf20Sopenharmony_ci memcpy(c, elem, avail_here); 16148c2ecf20Sopenharmony_ci } else 16158c2ecf20Sopenharmony_ci memcpy(elem, c, avail_here); 16168c2ecf20Sopenharmony_ci copied = avail_here; 16178c2ecf20Sopenharmony_ci } 16188c2ecf20Sopenharmony_ci base = buf->head->iov_len; /* align to start of pages */ 16198c2ecf20Sopenharmony_ci } 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci /* process pages array */ 16228c2ecf20Sopenharmony_ci base -= buf->head->iov_len; 16238c2ecf20Sopenharmony_ci if (todo && base < buf->page_len) { 16248c2ecf20Sopenharmony_ci unsigned int avail_page; 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci avail_here = min(todo, buf->page_len - base); 16278c2ecf20Sopenharmony_ci todo -= avail_here; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci base += buf->page_base; 16308c2ecf20Sopenharmony_ci ppages = buf->pages + (base >> PAGE_SHIFT); 16318c2ecf20Sopenharmony_ci base &= ~PAGE_MASK; 16328c2ecf20Sopenharmony_ci avail_page = min_t(unsigned int, PAGE_SIZE - base, 16338c2ecf20Sopenharmony_ci avail_here); 16348c2ecf20Sopenharmony_ci c = kmap(*ppages) + base; 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci while (avail_here) { 16378c2ecf20Sopenharmony_ci avail_here -= avail_page; 16388c2ecf20Sopenharmony_ci if (copied || avail_page < desc->elem_size) { 16398c2ecf20Sopenharmony_ci unsigned int l = min(avail_page, 16408c2ecf20Sopenharmony_ci desc->elem_size - copied); 16418c2ecf20Sopenharmony_ci if (!elem) { 16428c2ecf20Sopenharmony_ci elem = kmalloc(desc->elem_size, 16438c2ecf20Sopenharmony_ci GFP_KERNEL); 16448c2ecf20Sopenharmony_ci err = -ENOMEM; 16458c2ecf20Sopenharmony_ci if (!elem) 16468c2ecf20Sopenharmony_ci goto out; 16478c2ecf20Sopenharmony_ci } 16488c2ecf20Sopenharmony_ci if (encode) { 16498c2ecf20Sopenharmony_ci if (!copied) { 16508c2ecf20Sopenharmony_ci err = desc->xcode(desc, elem); 16518c2ecf20Sopenharmony_ci if (err) 16528c2ecf20Sopenharmony_ci goto out; 16538c2ecf20Sopenharmony_ci } 16548c2ecf20Sopenharmony_ci memcpy(c, elem + copied, l); 16558c2ecf20Sopenharmony_ci copied += l; 16568c2ecf20Sopenharmony_ci if (copied == desc->elem_size) 16578c2ecf20Sopenharmony_ci copied = 0; 16588c2ecf20Sopenharmony_ci } else { 16598c2ecf20Sopenharmony_ci memcpy(elem + copied, c, l); 16608c2ecf20Sopenharmony_ci copied += l; 16618c2ecf20Sopenharmony_ci if (copied == desc->elem_size) { 16628c2ecf20Sopenharmony_ci err = desc->xcode(desc, elem); 16638c2ecf20Sopenharmony_ci if (err) 16648c2ecf20Sopenharmony_ci goto out; 16658c2ecf20Sopenharmony_ci copied = 0; 16668c2ecf20Sopenharmony_ci } 16678c2ecf20Sopenharmony_ci } 16688c2ecf20Sopenharmony_ci avail_page -= l; 16698c2ecf20Sopenharmony_ci c += l; 16708c2ecf20Sopenharmony_ci } 16718c2ecf20Sopenharmony_ci while (avail_page >= desc->elem_size) { 16728c2ecf20Sopenharmony_ci err = desc->xcode(desc, c); 16738c2ecf20Sopenharmony_ci if (err) 16748c2ecf20Sopenharmony_ci goto out; 16758c2ecf20Sopenharmony_ci c += desc->elem_size; 16768c2ecf20Sopenharmony_ci avail_page -= desc->elem_size; 16778c2ecf20Sopenharmony_ci } 16788c2ecf20Sopenharmony_ci if (avail_page) { 16798c2ecf20Sopenharmony_ci unsigned int l = min(avail_page, 16808c2ecf20Sopenharmony_ci desc->elem_size - copied); 16818c2ecf20Sopenharmony_ci if (!elem) { 16828c2ecf20Sopenharmony_ci elem = kmalloc(desc->elem_size, 16838c2ecf20Sopenharmony_ci GFP_KERNEL); 16848c2ecf20Sopenharmony_ci err = -ENOMEM; 16858c2ecf20Sopenharmony_ci if (!elem) 16868c2ecf20Sopenharmony_ci goto out; 16878c2ecf20Sopenharmony_ci } 16888c2ecf20Sopenharmony_ci if (encode) { 16898c2ecf20Sopenharmony_ci if (!copied) { 16908c2ecf20Sopenharmony_ci err = desc->xcode(desc, elem); 16918c2ecf20Sopenharmony_ci if (err) 16928c2ecf20Sopenharmony_ci goto out; 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci memcpy(c, elem + copied, l); 16958c2ecf20Sopenharmony_ci copied += l; 16968c2ecf20Sopenharmony_ci if (copied == desc->elem_size) 16978c2ecf20Sopenharmony_ci copied = 0; 16988c2ecf20Sopenharmony_ci } else { 16998c2ecf20Sopenharmony_ci memcpy(elem + copied, c, l); 17008c2ecf20Sopenharmony_ci copied += l; 17018c2ecf20Sopenharmony_ci if (copied == desc->elem_size) { 17028c2ecf20Sopenharmony_ci err = desc->xcode(desc, elem); 17038c2ecf20Sopenharmony_ci if (err) 17048c2ecf20Sopenharmony_ci goto out; 17058c2ecf20Sopenharmony_ci copied = 0; 17068c2ecf20Sopenharmony_ci } 17078c2ecf20Sopenharmony_ci } 17088c2ecf20Sopenharmony_ci } 17098c2ecf20Sopenharmony_ci if (avail_here) { 17108c2ecf20Sopenharmony_ci kunmap(*ppages); 17118c2ecf20Sopenharmony_ci ppages++; 17128c2ecf20Sopenharmony_ci c = kmap(*ppages); 17138c2ecf20Sopenharmony_ci } 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci avail_page = min(avail_here, 17168c2ecf20Sopenharmony_ci (unsigned int) PAGE_SIZE); 17178c2ecf20Sopenharmony_ci } 17188c2ecf20Sopenharmony_ci base = buf->page_len; /* align to start of tail */ 17198c2ecf20Sopenharmony_ci } 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci /* process tail */ 17228c2ecf20Sopenharmony_ci base -= buf->page_len; 17238c2ecf20Sopenharmony_ci if (todo) { 17248c2ecf20Sopenharmony_ci c = buf->tail->iov_base + base; 17258c2ecf20Sopenharmony_ci if (copied) { 17268c2ecf20Sopenharmony_ci unsigned int l = desc->elem_size - copied; 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci if (encode) 17298c2ecf20Sopenharmony_ci memcpy(c, elem + copied, l); 17308c2ecf20Sopenharmony_ci else { 17318c2ecf20Sopenharmony_ci memcpy(elem + copied, c, l); 17328c2ecf20Sopenharmony_ci err = desc->xcode(desc, elem); 17338c2ecf20Sopenharmony_ci if (err) 17348c2ecf20Sopenharmony_ci goto out; 17358c2ecf20Sopenharmony_ci } 17368c2ecf20Sopenharmony_ci todo -= l; 17378c2ecf20Sopenharmony_ci c += l; 17388c2ecf20Sopenharmony_ci } 17398c2ecf20Sopenharmony_ci while (todo) { 17408c2ecf20Sopenharmony_ci err = desc->xcode(desc, c); 17418c2ecf20Sopenharmony_ci if (err) 17428c2ecf20Sopenharmony_ci goto out; 17438c2ecf20Sopenharmony_ci c += desc->elem_size; 17448c2ecf20Sopenharmony_ci todo -= desc->elem_size; 17458c2ecf20Sopenharmony_ci } 17468c2ecf20Sopenharmony_ci } 17478c2ecf20Sopenharmony_ci err = 0; 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ciout: 17508c2ecf20Sopenharmony_ci kfree(elem); 17518c2ecf20Sopenharmony_ci if (ppages) 17528c2ecf20Sopenharmony_ci kunmap(*ppages); 17538c2ecf20Sopenharmony_ci return err; 17548c2ecf20Sopenharmony_ci} 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ciint 17578c2ecf20Sopenharmony_cixdr_decode_array2(struct xdr_buf *buf, unsigned int base, 17588c2ecf20Sopenharmony_ci struct xdr_array2_desc *desc) 17598c2ecf20Sopenharmony_ci{ 17608c2ecf20Sopenharmony_ci if (base >= buf->len) 17618c2ecf20Sopenharmony_ci return -EINVAL; 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci return xdr_xcode_array2(buf, base, desc, 0); 17648c2ecf20Sopenharmony_ci} 17658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_decode_array2); 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ciint 17688c2ecf20Sopenharmony_cixdr_encode_array2(struct xdr_buf *buf, unsigned int base, 17698c2ecf20Sopenharmony_ci struct xdr_array2_desc *desc) 17708c2ecf20Sopenharmony_ci{ 17718c2ecf20Sopenharmony_ci if ((unsigned long) base + 4 + desc->array_len * desc->elem_size > 17728c2ecf20Sopenharmony_ci buf->head->iov_len + buf->page_len + buf->tail->iov_len) 17738c2ecf20Sopenharmony_ci return -EINVAL; 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci return xdr_xcode_array2(buf, base, desc, 1); 17768c2ecf20Sopenharmony_ci} 17778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_encode_array2); 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ciint 17808c2ecf20Sopenharmony_cixdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len, 17818c2ecf20Sopenharmony_ci int (*actor)(struct scatterlist *, void *), void *data) 17828c2ecf20Sopenharmony_ci{ 17838c2ecf20Sopenharmony_ci int i, ret = 0; 17848c2ecf20Sopenharmony_ci unsigned int page_len, thislen, page_offset; 17858c2ecf20Sopenharmony_ci struct scatterlist sg[1]; 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci sg_init_table(sg, 1); 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci if (offset >= buf->head[0].iov_len) { 17908c2ecf20Sopenharmony_ci offset -= buf->head[0].iov_len; 17918c2ecf20Sopenharmony_ci } else { 17928c2ecf20Sopenharmony_ci thislen = buf->head[0].iov_len - offset; 17938c2ecf20Sopenharmony_ci if (thislen > len) 17948c2ecf20Sopenharmony_ci thislen = len; 17958c2ecf20Sopenharmony_ci sg_set_buf(sg, buf->head[0].iov_base + offset, thislen); 17968c2ecf20Sopenharmony_ci ret = actor(sg, data); 17978c2ecf20Sopenharmony_ci if (ret) 17988c2ecf20Sopenharmony_ci goto out; 17998c2ecf20Sopenharmony_ci offset = 0; 18008c2ecf20Sopenharmony_ci len -= thislen; 18018c2ecf20Sopenharmony_ci } 18028c2ecf20Sopenharmony_ci if (len == 0) 18038c2ecf20Sopenharmony_ci goto out; 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci if (offset >= buf->page_len) { 18068c2ecf20Sopenharmony_ci offset -= buf->page_len; 18078c2ecf20Sopenharmony_ci } else { 18088c2ecf20Sopenharmony_ci page_len = buf->page_len - offset; 18098c2ecf20Sopenharmony_ci if (page_len > len) 18108c2ecf20Sopenharmony_ci page_len = len; 18118c2ecf20Sopenharmony_ci len -= page_len; 18128c2ecf20Sopenharmony_ci page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1); 18138c2ecf20Sopenharmony_ci i = (offset + buf->page_base) >> PAGE_SHIFT; 18148c2ecf20Sopenharmony_ci thislen = PAGE_SIZE - page_offset; 18158c2ecf20Sopenharmony_ci do { 18168c2ecf20Sopenharmony_ci if (thislen > page_len) 18178c2ecf20Sopenharmony_ci thislen = page_len; 18188c2ecf20Sopenharmony_ci sg_set_page(sg, buf->pages[i], thislen, page_offset); 18198c2ecf20Sopenharmony_ci ret = actor(sg, data); 18208c2ecf20Sopenharmony_ci if (ret) 18218c2ecf20Sopenharmony_ci goto out; 18228c2ecf20Sopenharmony_ci page_len -= thislen; 18238c2ecf20Sopenharmony_ci i++; 18248c2ecf20Sopenharmony_ci page_offset = 0; 18258c2ecf20Sopenharmony_ci thislen = PAGE_SIZE; 18268c2ecf20Sopenharmony_ci } while (page_len != 0); 18278c2ecf20Sopenharmony_ci offset = 0; 18288c2ecf20Sopenharmony_ci } 18298c2ecf20Sopenharmony_ci if (len == 0) 18308c2ecf20Sopenharmony_ci goto out; 18318c2ecf20Sopenharmony_ci if (offset < buf->tail[0].iov_len) { 18328c2ecf20Sopenharmony_ci thislen = buf->tail[0].iov_len - offset; 18338c2ecf20Sopenharmony_ci if (thislen > len) 18348c2ecf20Sopenharmony_ci thislen = len; 18358c2ecf20Sopenharmony_ci sg_set_buf(sg, buf->tail[0].iov_base + offset, thislen); 18368c2ecf20Sopenharmony_ci ret = actor(sg, data); 18378c2ecf20Sopenharmony_ci len -= thislen; 18388c2ecf20Sopenharmony_ci } 18398c2ecf20Sopenharmony_ci if (len != 0) 18408c2ecf20Sopenharmony_ci ret = -EINVAL; 18418c2ecf20Sopenharmony_ciout: 18428c2ecf20Sopenharmony_ci return ret; 18438c2ecf20Sopenharmony_ci} 18448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_process_buf); 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci/** 18478c2ecf20Sopenharmony_ci * xdr_stream_decode_opaque - Decode variable length opaque 18488c2ecf20Sopenharmony_ci * @xdr: pointer to xdr_stream 18498c2ecf20Sopenharmony_ci * @ptr: location to store opaque data 18508c2ecf20Sopenharmony_ci * @size: size of storage buffer @ptr 18518c2ecf20Sopenharmony_ci * 18528c2ecf20Sopenharmony_ci * Return values: 18538c2ecf20Sopenharmony_ci * On success, returns size of object stored in *@ptr 18548c2ecf20Sopenharmony_ci * %-EBADMSG on XDR buffer overflow 18558c2ecf20Sopenharmony_ci * %-EMSGSIZE on overflow of storage buffer @ptr 18568c2ecf20Sopenharmony_ci */ 18578c2ecf20Sopenharmony_cissize_t xdr_stream_decode_opaque(struct xdr_stream *xdr, void *ptr, size_t size) 18588c2ecf20Sopenharmony_ci{ 18598c2ecf20Sopenharmony_ci ssize_t ret; 18608c2ecf20Sopenharmony_ci void *p; 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci ret = xdr_stream_decode_opaque_inline(xdr, &p, size); 18638c2ecf20Sopenharmony_ci if (ret <= 0) 18648c2ecf20Sopenharmony_ci return ret; 18658c2ecf20Sopenharmony_ci memcpy(ptr, p, ret); 18668c2ecf20Sopenharmony_ci return ret; 18678c2ecf20Sopenharmony_ci} 18688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_stream_decode_opaque); 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci/** 18718c2ecf20Sopenharmony_ci * xdr_stream_decode_opaque_dup - Decode and duplicate variable length opaque 18728c2ecf20Sopenharmony_ci * @xdr: pointer to xdr_stream 18738c2ecf20Sopenharmony_ci * @ptr: location to store pointer to opaque data 18748c2ecf20Sopenharmony_ci * @maxlen: maximum acceptable object size 18758c2ecf20Sopenharmony_ci * @gfp_flags: GFP mask to use 18768c2ecf20Sopenharmony_ci * 18778c2ecf20Sopenharmony_ci * Return values: 18788c2ecf20Sopenharmony_ci * On success, returns size of object stored in *@ptr 18798c2ecf20Sopenharmony_ci * %-EBADMSG on XDR buffer overflow 18808c2ecf20Sopenharmony_ci * %-EMSGSIZE if the size of the object would exceed @maxlen 18818c2ecf20Sopenharmony_ci * %-ENOMEM on memory allocation failure 18828c2ecf20Sopenharmony_ci */ 18838c2ecf20Sopenharmony_cissize_t xdr_stream_decode_opaque_dup(struct xdr_stream *xdr, void **ptr, 18848c2ecf20Sopenharmony_ci size_t maxlen, gfp_t gfp_flags) 18858c2ecf20Sopenharmony_ci{ 18868c2ecf20Sopenharmony_ci ssize_t ret; 18878c2ecf20Sopenharmony_ci void *p; 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci ret = xdr_stream_decode_opaque_inline(xdr, &p, maxlen); 18908c2ecf20Sopenharmony_ci if (ret > 0) { 18918c2ecf20Sopenharmony_ci *ptr = kmemdup(p, ret, gfp_flags); 18928c2ecf20Sopenharmony_ci if (*ptr != NULL) 18938c2ecf20Sopenharmony_ci return ret; 18948c2ecf20Sopenharmony_ci ret = -ENOMEM; 18958c2ecf20Sopenharmony_ci } 18968c2ecf20Sopenharmony_ci *ptr = NULL; 18978c2ecf20Sopenharmony_ci return ret; 18988c2ecf20Sopenharmony_ci} 18998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_stream_decode_opaque_dup); 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci/** 19028c2ecf20Sopenharmony_ci * xdr_stream_decode_string - Decode variable length string 19038c2ecf20Sopenharmony_ci * @xdr: pointer to xdr_stream 19048c2ecf20Sopenharmony_ci * @str: location to store string 19058c2ecf20Sopenharmony_ci * @size: size of storage buffer @str 19068c2ecf20Sopenharmony_ci * 19078c2ecf20Sopenharmony_ci * Return values: 19088c2ecf20Sopenharmony_ci * On success, returns length of NUL-terminated string stored in *@str 19098c2ecf20Sopenharmony_ci * %-EBADMSG on XDR buffer overflow 19108c2ecf20Sopenharmony_ci * %-EMSGSIZE on overflow of storage buffer @str 19118c2ecf20Sopenharmony_ci */ 19128c2ecf20Sopenharmony_cissize_t xdr_stream_decode_string(struct xdr_stream *xdr, char *str, size_t size) 19138c2ecf20Sopenharmony_ci{ 19148c2ecf20Sopenharmony_ci ssize_t ret; 19158c2ecf20Sopenharmony_ci void *p; 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci ret = xdr_stream_decode_opaque_inline(xdr, &p, size); 19188c2ecf20Sopenharmony_ci if (ret > 0) { 19198c2ecf20Sopenharmony_ci memcpy(str, p, ret); 19208c2ecf20Sopenharmony_ci str[ret] = '\0'; 19218c2ecf20Sopenharmony_ci return strlen(str); 19228c2ecf20Sopenharmony_ci } 19238c2ecf20Sopenharmony_ci *str = '\0'; 19248c2ecf20Sopenharmony_ci return ret; 19258c2ecf20Sopenharmony_ci} 19268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_stream_decode_string); 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci/** 19298c2ecf20Sopenharmony_ci * xdr_stream_decode_string_dup - Decode and duplicate variable length string 19308c2ecf20Sopenharmony_ci * @xdr: pointer to xdr_stream 19318c2ecf20Sopenharmony_ci * @str: location to store pointer to string 19328c2ecf20Sopenharmony_ci * @maxlen: maximum acceptable string length 19338c2ecf20Sopenharmony_ci * @gfp_flags: GFP mask to use 19348c2ecf20Sopenharmony_ci * 19358c2ecf20Sopenharmony_ci * Return values: 19368c2ecf20Sopenharmony_ci * On success, returns length of NUL-terminated string stored in *@ptr 19378c2ecf20Sopenharmony_ci * %-EBADMSG on XDR buffer overflow 19388c2ecf20Sopenharmony_ci * %-EMSGSIZE if the size of the string would exceed @maxlen 19398c2ecf20Sopenharmony_ci * %-ENOMEM on memory allocation failure 19408c2ecf20Sopenharmony_ci */ 19418c2ecf20Sopenharmony_cissize_t xdr_stream_decode_string_dup(struct xdr_stream *xdr, char **str, 19428c2ecf20Sopenharmony_ci size_t maxlen, gfp_t gfp_flags) 19438c2ecf20Sopenharmony_ci{ 19448c2ecf20Sopenharmony_ci void *p; 19458c2ecf20Sopenharmony_ci ssize_t ret; 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci ret = xdr_stream_decode_opaque_inline(xdr, &p, maxlen); 19488c2ecf20Sopenharmony_ci if (ret > 0) { 19498c2ecf20Sopenharmony_ci char *s = kmalloc(ret + 1, gfp_flags); 19508c2ecf20Sopenharmony_ci if (s != NULL) { 19518c2ecf20Sopenharmony_ci memcpy(s, p, ret); 19528c2ecf20Sopenharmony_ci s[ret] = '\0'; 19538c2ecf20Sopenharmony_ci *str = s; 19548c2ecf20Sopenharmony_ci return strlen(s); 19558c2ecf20Sopenharmony_ci } 19568c2ecf20Sopenharmony_ci ret = -ENOMEM; 19578c2ecf20Sopenharmony_ci } 19588c2ecf20Sopenharmony_ci *str = NULL; 19598c2ecf20Sopenharmony_ci return ret; 19608c2ecf20Sopenharmony_ci} 19618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_stream_decode_string_dup); 1962