162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/net/sunrpc/xdr.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Generic XDR support. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/string.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/pagemap.h> 1662306a36Sopenharmony_ci#include <linux/errno.h> 1762306a36Sopenharmony_ci#include <linux/sunrpc/xdr.h> 1862306a36Sopenharmony_ci#include <linux/sunrpc/msg_prot.h> 1962306a36Sopenharmony_ci#include <linux/bvec.h> 2062306a36Sopenharmony_ci#include <trace/events/sunrpc.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic void _copy_to_pages(struct page **, size_t, const char *, size_t); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * XDR functions for basic NFS types 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci__be32 * 2962306a36Sopenharmony_cixdr_encode_netobj(__be32 *p, const struct xdr_netobj *obj) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci unsigned int quadlen = XDR_QUADLEN(obj->len); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci p[quadlen] = 0; /* zero trailing bytes */ 3462306a36Sopenharmony_ci *p++ = cpu_to_be32(obj->len); 3562306a36Sopenharmony_ci memcpy(p, obj->data, obj->len); 3662306a36Sopenharmony_ci return p + XDR_QUADLEN(obj->len); 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_encode_netobj); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci__be32 * 4162306a36Sopenharmony_cixdr_decode_netobj(__be32 *p, struct xdr_netobj *obj) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci unsigned int len; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if ((len = be32_to_cpu(*p++)) > XDR_MAX_NETOBJ) 4662306a36Sopenharmony_ci return NULL; 4762306a36Sopenharmony_ci obj->len = len; 4862306a36Sopenharmony_ci obj->data = (u8 *) p; 4962306a36Sopenharmony_ci return p + XDR_QUADLEN(len); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_decode_netobj); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/** 5462306a36Sopenharmony_ci * xdr_encode_opaque_fixed - Encode fixed length opaque data 5562306a36Sopenharmony_ci * @p: pointer to current position in XDR buffer. 5662306a36Sopenharmony_ci * @ptr: pointer to data to encode (or NULL) 5762306a36Sopenharmony_ci * @nbytes: size of data. 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * Copy the array of data of length nbytes at ptr to the XDR buffer 6062306a36Sopenharmony_ci * at position p, then align to the next 32-bit boundary by padding 6162306a36Sopenharmony_ci * with zero bytes (see RFC1832). 6262306a36Sopenharmony_ci * Note: if ptr is NULL, only the padding is performed. 6362306a36Sopenharmony_ci * 6462306a36Sopenharmony_ci * Returns the updated current XDR buffer position 6562306a36Sopenharmony_ci * 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ci__be32 *xdr_encode_opaque_fixed(__be32 *p, const void *ptr, unsigned int nbytes) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci if (likely(nbytes != 0)) { 7062306a36Sopenharmony_ci unsigned int quadlen = XDR_QUADLEN(nbytes); 7162306a36Sopenharmony_ci unsigned int padding = (quadlen << 2) - nbytes; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (ptr != NULL) 7462306a36Sopenharmony_ci memcpy(p, ptr, nbytes); 7562306a36Sopenharmony_ci if (padding != 0) 7662306a36Sopenharmony_ci memset((char *)p + nbytes, 0, padding); 7762306a36Sopenharmony_ci p += quadlen; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci return p; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_encode_opaque_fixed); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/** 8462306a36Sopenharmony_ci * xdr_encode_opaque - Encode variable length opaque data 8562306a36Sopenharmony_ci * @p: pointer to current position in XDR buffer. 8662306a36Sopenharmony_ci * @ptr: pointer to data to encode (or NULL) 8762306a36Sopenharmony_ci * @nbytes: size of data. 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * Returns the updated current XDR buffer position 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci__be32 *xdr_encode_opaque(__be32 *p, const void *ptr, unsigned int nbytes) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci *p++ = cpu_to_be32(nbytes); 9462306a36Sopenharmony_ci return xdr_encode_opaque_fixed(p, ptr, nbytes); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_encode_opaque); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci__be32 * 9962306a36Sopenharmony_cixdr_encode_string(__be32 *p, const char *string) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci return xdr_encode_array(p, string, strlen(string)); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_encode_string); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci__be32 * 10662306a36Sopenharmony_cixdr_decode_string_inplace(__be32 *p, char **sp, 10762306a36Sopenharmony_ci unsigned int *lenp, unsigned int maxlen) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci u32 len; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci len = be32_to_cpu(*p++); 11262306a36Sopenharmony_ci if (len > maxlen) 11362306a36Sopenharmony_ci return NULL; 11462306a36Sopenharmony_ci *lenp = len; 11562306a36Sopenharmony_ci *sp = (char *) p; 11662306a36Sopenharmony_ci return p + XDR_QUADLEN(len); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_decode_string_inplace); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/** 12162306a36Sopenharmony_ci * xdr_terminate_string - '\0'-terminate a string residing in an xdr_buf 12262306a36Sopenharmony_ci * @buf: XDR buffer where string resides 12362306a36Sopenharmony_ci * @len: length of string, in bytes 12462306a36Sopenharmony_ci * 12562306a36Sopenharmony_ci */ 12662306a36Sopenharmony_civoid xdr_terminate_string(const struct xdr_buf *buf, const u32 len) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci char *kaddr; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci kaddr = kmap_atomic(buf->pages[0]); 13162306a36Sopenharmony_ci kaddr[buf->page_base + len] = '\0'; 13262306a36Sopenharmony_ci kunmap_atomic(kaddr); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_terminate_string); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cisize_t xdr_buf_pagecount(const struct xdr_buf *buf) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci if (!buf->page_len) 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci return (buf->page_base + buf->page_len + PAGE_SIZE - 1) >> PAGE_SHIFT; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ciint 14462306a36Sopenharmony_cixdr_alloc_bvec(struct xdr_buf *buf, gfp_t gfp) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci size_t i, n = xdr_buf_pagecount(buf); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (n != 0 && buf->bvec == NULL) { 14962306a36Sopenharmony_ci buf->bvec = kmalloc_array(n, sizeof(buf->bvec[0]), gfp); 15062306a36Sopenharmony_ci if (!buf->bvec) 15162306a36Sopenharmony_ci return -ENOMEM; 15262306a36Sopenharmony_ci for (i = 0; i < n; i++) { 15362306a36Sopenharmony_ci bvec_set_page(&buf->bvec[i], buf->pages[i], PAGE_SIZE, 15462306a36Sopenharmony_ci 0); 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_civoid 16162306a36Sopenharmony_cixdr_free_bvec(struct xdr_buf *buf) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci kfree(buf->bvec); 16462306a36Sopenharmony_ci buf->bvec = NULL; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci/** 16862306a36Sopenharmony_ci * xdr_buf_to_bvec - Copy components of an xdr_buf into a bio_vec array 16962306a36Sopenharmony_ci * @bvec: bio_vec array to populate 17062306a36Sopenharmony_ci * @bvec_size: element count of @bio_vec 17162306a36Sopenharmony_ci * @xdr: xdr_buf to be copied 17262306a36Sopenharmony_ci * 17362306a36Sopenharmony_ci * Returns the number of entries consumed in @bvec. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ciunsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size, 17662306a36Sopenharmony_ci const struct xdr_buf *xdr) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci const struct kvec *head = xdr->head; 17962306a36Sopenharmony_ci const struct kvec *tail = xdr->tail; 18062306a36Sopenharmony_ci unsigned int count = 0; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (head->iov_len) { 18362306a36Sopenharmony_ci bvec_set_virt(bvec++, head->iov_base, head->iov_len); 18462306a36Sopenharmony_ci ++count; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (xdr->page_len) { 18862306a36Sopenharmony_ci unsigned int offset, len, remaining; 18962306a36Sopenharmony_ci struct page **pages = xdr->pages; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci offset = offset_in_page(xdr->page_base); 19262306a36Sopenharmony_ci remaining = xdr->page_len; 19362306a36Sopenharmony_ci while (remaining > 0) { 19462306a36Sopenharmony_ci len = min_t(unsigned int, remaining, 19562306a36Sopenharmony_ci PAGE_SIZE - offset); 19662306a36Sopenharmony_ci bvec_set_page(bvec++, *pages++, len, offset); 19762306a36Sopenharmony_ci remaining -= len; 19862306a36Sopenharmony_ci offset = 0; 19962306a36Sopenharmony_ci if (unlikely(++count > bvec_size)) 20062306a36Sopenharmony_ci goto bvec_overflow; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (tail->iov_len) { 20562306a36Sopenharmony_ci bvec_set_virt(bvec, tail->iov_base, tail->iov_len); 20662306a36Sopenharmony_ci if (unlikely(++count > bvec_size)) 20762306a36Sopenharmony_ci goto bvec_overflow; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return count; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cibvec_overflow: 21362306a36Sopenharmony_ci pr_warn_once("%s: bio_vec array overflow\n", __func__); 21462306a36Sopenharmony_ci return count - 1; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/** 21862306a36Sopenharmony_ci * xdr_inline_pages - Prepare receive buffer for a large reply 21962306a36Sopenharmony_ci * @xdr: xdr_buf into which reply will be placed 22062306a36Sopenharmony_ci * @offset: expected offset where data payload will start, in bytes 22162306a36Sopenharmony_ci * @pages: vector of struct page pointers 22262306a36Sopenharmony_ci * @base: offset in first page where receive should start, in bytes 22362306a36Sopenharmony_ci * @len: expected size of the upper layer data payload, in bytes 22462306a36Sopenharmony_ci * 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_civoid 22762306a36Sopenharmony_cixdr_inline_pages(struct xdr_buf *xdr, unsigned int offset, 22862306a36Sopenharmony_ci struct page **pages, unsigned int base, unsigned int len) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct kvec *head = xdr->head; 23162306a36Sopenharmony_ci struct kvec *tail = xdr->tail; 23262306a36Sopenharmony_ci char *buf = (char *)head->iov_base; 23362306a36Sopenharmony_ci unsigned int buflen = head->iov_len; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci head->iov_len = offset; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci xdr->pages = pages; 23862306a36Sopenharmony_ci xdr->page_base = base; 23962306a36Sopenharmony_ci xdr->page_len = len; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci tail->iov_base = buf + offset; 24262306a36Sopenharmony_ci tail->iov_len = buflen - offset; 24362306a36Sopenharmony_ci xdr->buflen += len; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_inline_pages); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/* 24862306a36Sopenharmony_ci * Helper routines for doing 'memmove' like operations on a struct xdr_buf 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci/** 25262306a36Sopenharmony_ci * _shift_data_left_pages 25362306a36Sopenharmony_ci * @pages: vector of pages containing both the source and dest memory area. 25462306a36Sopenharmony_ci * @pgto_base: page vector address of destination 25562306a36Sopenharmony_ci * @pgfrom_base: page vector address of source 25662306a36Sopenharmony_ci * @len: number of bytes to copy 25762306a36Sopenharmony_ci * 25862306a36Sopenharmony_ci * Note: the addresses pgto_base and pgfrom_base are both calculated in 25962306a36Sopenharmony_ci * the same way: 26062306a36Sopenharmony_ci * if a memory area starts at byte 'base' in page 'pages[i]', 26162306a36Sopenharmony_ci * then its address is given as (i << PAGE_CACHE_SHIFT) + base 26262306a36Sopenharmony_ci * Alse note: pgto_base must be < pgfrom_base, but the memory areas 26362306a36Sopenharmony_ci * they point to may overlap. 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_cistatic void 26662306a36Sopenharmony_ci_shift_data_left_pages(struct page **pages, size_t pgto_base, 26762306a36Sopenharmony_ci size_t pgfrom_base, size_t len) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct page **pgfrom, **pgto; 27062306a36Sopenharmony_ci char *vfrom, *vto; 27162306a36Sopenharmony_ci size_t copy; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci BUG_ON(pgfrom_base <= pgto_base); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (!len) 27662306a36Sopenharmony_ci return; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci pgto = pages + (pgto_base >> PAGE_SHIFT); 27962306a36Sopenharmony_ci pgfrom = pages + (pgfrom_base >> PAGE_SHIFT); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci pgto_base &= ~PAGE_MASK; 28262306a36Sopenharmony_ci pgfrom_base &= ~PAGE_MASK; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci do { 28562306a36Sopenharmony_ci if (pgto_base >= PAGE_SIZE) { 28662306a36Sopenharmony_ci pgto_base = 0; 28762306a36Sopenharmony_ci pgto++; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci if (pgfrom_base >= PAGE_SIZE){ 29062306a36Sopenharmony_ci pgfrom_base = 0; 29162306a36Sopenharmony_ci pgfrom++; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci copy = len; 29562306a36Sopenharmony_ci if (copy > (PAGE_SIZE - pgto_base)) 29662306a36Sopenharmony_ci copy = PAGE_SIZE - pgto_base; 29762306a36Sopenharmony_ci if (copy > (PAGE_SIZE - pgfrom_base)) 29862306a36Sopenharmony_ci copy = PAGE_SIZE - pgfrom_base; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci vto = kmap_atomic(*pgto); 30162306a36Sopenharmony_ci if (*pgto != *pgfrom) { 30262306a36Sopenharmony_ci vfrom = kmap_atomic(*pgfrom); 30362306a36Sopenharmony_ci memcpy(vto + pgto_base, vfrom + pgfrom_base, copy); 30462306a36Sopenharmony_ci kunmap_atomic(vfrom); 30562306a36Sopenharmony_ci } else 30662306a36Sopenharmony_ci memmove(vto + pgto_base, vto + pgfrom_base, copy); 30762306a36Sopenharmony_ci flush_dcache_page(*pgto); 30862306a36Sopenharmony_ci kunmap_atomic(vto); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci pgto_base += copy; 31162306a36Sopenharmony_ci pgfrom_base += copy; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci } while ((len -= copy) != 0); 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci/** 31762306a36Sopenharmony_ci * _shift_data_right_pages 31862306a36Sopenharmony_ci * @pages: vector of pages containing both the source and dest memory area. 31962306a36Sopenharmony_ci * @pgto_base: page vector address of destination 32062306a36Sopenharmony_ci * @pgfrom_base: page vector address of source 32162306a36Sopenharmony_ci * @len: number of bytes to copy 32262306a36Sopenharmony_ci * 32362306a36Sopenharmony_ci * Note: the addresses pgto_base and pgfrom_base are both calculated in 32462306a36Sopenharmony_ci * the same way: 32562306a36Sopenharmony_ci * if a memory area starts at byte 'base' in page 'pages[i]', 32662306a36Sopenharmony_ci * then its address is given as (i << PAGE_SHIFT) + base 32762306a36Sopenharmony_ci * Also note: pgfrom_base must be < pgto_base, but the memory areas 32862306a36Sopenharmony_ci * they point to may overlap. 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_cistatic void 33162306a36Sopenharmony_ci_shift_data_right_pages(struct page **pages, size_t pgto_base, 33262306a36Sopenharmony_ci size_t pgfrom_base, size_t len) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct page **pgfrom, **pgto; 33562306a36Sopenharmony_ci char *vfrom, *vto; 33662306a36Sopenharmony_ci size_t copy; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci BUG_ON(pgto_base <= pgfrom_base); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (!len) 34162306a36Sopenharmony_ci return; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci pgto_base += len; 34462306a36Sopenharmony_ci pgfrom_base += len; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci pgto = pages + (pgto_base >> PAGE_SHIFT); 34762306a36Sopenharmony_ci pgfrom = pages + (pgfrom_base >> PAGE_SHIFT); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci pgto_base &= ~PAGE_MASK; 35062306a36Sopenharmony_ci pgfrom_base &= ~PAGE_MASK; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci do { 35362306a36Sopenharmony_ci /* Are any pointers crossing a page boundary? */ 35462306a36Sopenharmony_ci if (pgto_base == 0) { 35562306a36Sopenharmony_ci pgto_base = PAGE_SIZE; 35662306a36Sopenharmony_ci pgto--; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci if (pgfrom_base == 0) { 35962306a36Sopenharmony_ci pgfrom_base = PAGE_SIZE; 36062306a36Sopenharmony_ci pgfrom--; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci copy = len; 36462306a36Sopenharmony_ci if (copy > pgto_base) 36562306a36Sopenharmony_ci copy = pgto_base; 36662306a36Sopenharmony_ci if (copy > pgfrom_base) 36762306a36Sopenharmony_ci copy = pgfrom_base; 36862306a36Sopenharmony_ci pgto_base -= copy; 36962306a36Sopenharmony_ci pgfrom_base -= copy; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci vto = kmap_atomic(*pgto); 37262306a36Sopenharmony_ci if (*pgto != *pgfrom) { 37362306a36Sopenharmony_ci vfrom = kmap_atomic(*pgfrom); 37462306a36Sopenharmony_ci memcpy(vto + pgto_base, vfrom + pgfrom_base, copy); 37562306a36Sopenharmony_ci kunmap_atomic(vfrom); 37662306a36Sopenharmony_ci } else 37762306a36Sopenharmony_ci memmove(vto + pgto_base, vto + pgfrom_base, copy); 37862306a36Sopenharmony_ci flush_dcache_page(*pgto); 37962306a36Sopenharmony_ci kunmap_atomic(vto); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci } while ((len -= copy) != 0); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci/** 38562306a36Sopenharmony_ci * _copy_to_pages 38662306a36Sopenharmony_ci * @pages: array of pages 38762306a36Sopenharmony_ci * @pgbase: page vector address of destination 38862306a36Sopenharmony_ci * @p: pointer to source data 38962306a36Sopenharmony_ci * @len: length 39062306a36Sopenharmony_ci * 39162306a36Sopenharmony_ci * Copies data from an arbitrary memory location into an array of pages 39262306a36Sopenharmony_ci * The copy is assumed to be non-overlapping. 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_cistatic void 39562306a36Sopenharmony_ci_copy_to_pages(struct page **pages, size_t pgbase, const char *p, size_t len) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct page **pgto; 39862306a36Sopenharmony_ci char *vto; 39962306a36Sopenharmony_ci size_t copy; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (!len) 40262306a36Sopenharmony_ci return; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci pgto = pages + (pgbase >> PAGE_SHIFT); 40562306a36Sopenharmony_ci pgbase &= ~PAGE_MASK; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci for (;;) { 40862306a36Sopenharmony_ci copy = PAGE_SIZE - pgbase; 40962306a36Sopenharmony_ci if (copy > len) 41062306a36Sopenharmony_ci copy = len; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci vto = kmap_atomic(*pgto); 41362306a36Sopenharmony_ci memcpy(vto + pgbase, p, copy); 41462306a36Sopenharmony_ci kunmap_atomic(vto); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci len -= copy; 41762306a36Sopenharmony_ci if (len == 0) 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci pgbase += copy; 42162306a36Sopenharmony_ci if (pgbase == PAGE_SIZE) { 42262306a36Sopenharmony_ci flush_dcache_page(*pgto); 42362306a36Sopenharmony_ci pgbase = 0; 42462306a36Sopenharmony_ci pgto++; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci p += copy; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci flush_dcache_page(*pgto); 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci/** 43262306a36Sopenharmony_ci * _copy_from_pages 43362306a36Sopenharmony_ci * @p: pointer to destination 43462306a36Sopenharmony_ci * @pages: array of pages 43562306a36Sopenharmony_ci * @pgbase: offset of source data 43662306a36Sopenharmony_ci * @len: length 43762306a36Sopenharmony_ci * 43862306a36Sopenharmony_ci * Copies data into an arbitrary memory location from an array of pages 43962306a36Sopenharmony_ci * The copy is assumed to be non-overlapping. 44062306a36Sopenharmony_ci */ 44162306a36Sopenharmony_civoid 44262306a36Sopenharmony_ci_copy_from_pages(char *p, struct page **pages, size_t pgbase, size_t len) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci struct page **pgfrom; 44562306a36Sopenharmony_ci char *vfrom; 44662306a36Sopenharmony_ci size_t copy; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (!len) 44962306a36Sopenharmony_ci return; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci pgfrom = pages + (pgbase >> PAGE_SHIFT); 45262306a36Sopenharmony_ci pgbase &= ~PAGE_MASK; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci do { 45562306a36Sopenharmony_ci copy = PAGE_SIZE - pgbase; 45662306a36Sopenharmony_ci if (copy > len) 45762306a36Sopenharmony_ci copy = len; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci vfrom = kmap_atomic(*pgfrom); 46062306a36Sopenharmony_ci memcpy(p, vfrom + pgbase, copy); 46162306a36Sopenharmony_ci kunmap_atomic(vfrom); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci pgbase += copy; 46462306a36Sopenharmony_ci if (pgbase == PAGE_SIZE) { 46562306a36Sopenharmony_ci pgbase = 0; 46662306a36Sopenharmony_ci pgfrom++; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci p += copy; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci } while ((len -= copy) != 0); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(_copy_from_pages); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic void xdr_buf_iov_zero(const struct kvec *iov, unsigned int base, 47562306a36Sopenharmony_ci unsigned int len) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci if (base >= iov->iov_len) 47862306a36Sopenharmony_ci return; 47962306a36Sopenharmony_ci if (len > iov->iov_len - base) 48062306a36Sopenharmony_ci len = iov->iov_len - base; 48162306a36Sopenharmony_ci memset(iov->iov_base + base, 0, len); 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci/** 48562306a36Sopenharmony_ci * xdr_buf_pages_zero 48662306a36Sopenharmony_ci * @buf: xdr_buf 48762306a36Sopenharmony_ci * @pgbase: beginning offset 48862306a36Sopenharmony_ci * @len: length 48962306a36Sopenharmony_ci */ 49062306a36Sopenharmony_cistatic void xdr_buf_pages_zero(const struct xdr_buf *buf, unsigned int pgbase, 49162306a36Sopenharmony_ci unsigned int len) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci struct page **pages = buf->pages; 49462306a36Sopenharmony_ci struct page **page; 49562306a36Sopenharmony_ci char *vpage; 49662306a36Sopenharmony_ci unsigned int zero; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (!len) 49962306a36Sopenharmony_ci return; 50062306a36Sopenharmony_ci if (pgbase >= buf->page_len) { 50162306a36Sopenharmony_ci xdr_buf_iov_zero(buf->tail, pgbase - buf->page_len, len); 50262306a36Sopenharmony_ci return; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci if (pgbase + len > buf->page_len) { 50562306a36Sopenharmony_ci xdr_buf_iov_zero(buf->tail, 0, pgbase + len - buf->page_len); 50662306a36Sopenharmony_ci len = buf->page_len - pgbase; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci pgbase += buf->page_base; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci page = pages + (pgbase >> PAGE_SHIFT); 51262306a36Sopenharmony_ci pgbase &= ~PAGE_MASK; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci do { 51562306a36Sopenharmony_ci zero = PAGE_SIZE - pgbase; 51662306a36Sopenharmony_ci if (zero > len) 51762306a36Sopenharmony_ci zero = len; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci vpage = kmap_atomic(*page); 52062306a36Sopenharmony_ci memset(vpage + pgbase, 0, zero); 52162306a36Sopenharmony_ci kunmap_atomic(vpage); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci flush_dcache_page(*page); 52462306a36Sopenharmony_ci pgbase = 0; 52562306a36Sopenharmony_ci page++; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci } while ((len -= zero) != 0); 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic unsigned int xdr_buf_pages_fill_sparse(const struct xdr_buf *buf, 53162306a36Sopenharmony_ci unsigned int buflen, gfp_t gfp) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci unsigned int i, npages, pagelen; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (!(buf->flags & XDRBUF_SPARSE_PAGES)) 53662306a36Sopenharmony_ci return buflen; 53762306a36Sopenharmony_ci if (buflen <= buf->head->iov_len) 53862306a36Sopenharmony_ci return buflen; 53962306a36Sopenharmony_ci pagelen = buflen - buf->head->iov_len; 54062306a36Sopenharmony_ci if (pagelen > buf->page_len) 54162306a36Sopenharmony_ci pagelen = buf->page_len; 54262306a36Sopenharmony_ci npages = (pagelen + buf->page_base + PAGE_SIZE - 1) >> PAGE_SHIFT; 54362306a36Sopenharmony_ci for (i = 0; i < npages; i++) { 54462306a36Sopenharmony_ci if (!buf->pages[i]) 54562306a36Sopenharmony_ci continue; 54662306a36Sopenharmony_ci buf->pages[i] = alloc_page(gfp); 54762306a36Sopenharmony_ci if (likely(buf->pages[i])) 54862306a36Sopenharmony_ci continue; 54962306a36Sopenharmony_ci buflen -= pagelen; 55062306a36Sopenharmony_ci pagelen = i << PAGE_SHIFT; 55162306a36Sopenharmony_ci if (pagelen > buf->page_base) 55262306a36Sopenharmony_ci buflen += pagelen - buf->page_base; 55362306a36Sopenharmony_ci break; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci return buflen; 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic void xdr_buf_try_expand(struct xdr_buf *buf, unsigned int len) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci struct kvec *head = buf->head; 56162306a36Sopenharmony_ci struct kvec *tail = buf->tail; 56262306a36Sopenharmony_ci unsigned int sum = head->iov_len + buf->page_len + tail->iov_len; 56362306a36Sopenharmony_ci unsigned int free_space, newlen; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (sum > buf->len) { 56662306a36Sopenharmony_ci free_space = min_t(unsigned int, sum - buf->len, len); 56762306a36Sopenharmony_ci newlen = xdr_buf_pages_fill_sparse(buf, buf->len + free_space, 56862306a36Sopenharmony_ci GFP_KERNEL); 56962306a36Sopenharmony_ci free_space = newlen - buf->len; 57062306a36Sopenharmony_ci buf->len = newlen; 57162306a36Sopenharmony_ci len -= free_space; 57262306a36Sopenharmony_ci if (!len) 57362306a36Sopenharmony_ci return; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (buf->buflen > sum) { 57762306a36Sopenharmony_ci /* Expand the tail buffer */ 57862306a36Sopenharmony_ci free_space = min_t(unsigned int, buf->buflen - sum, len); 57962306a36Sopenharmony_ci tail->iov_len += free_space; 58062306a36Sopenharmony_ci buf->len += free_space; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic void xdr_buf_tail_copy_right(const struct xdr_buf *buf, 58562306a36Sopenharmony_ci unsigned int base, unsigned int len, 58662306a36Sopenharmony_ci unsigned int shift) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci const struct kvec *tail = buf->tail; 58962306a36Sopenharmony_ci unsigned int to = base + shift; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (to >= tail->iov_len) 59262306a36Sopenharmony_ci return; 59362306a36Sopenharmony_ci if (len + to > tail->iov_len) 59462306a36Sopenharmony_ci len = tail->iov_len - to; 59562306a36Sopenharmony_ci memmove(tail->iov_base + to, tail->iov_base + base, len); 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic void xdr_buf_pages_copy_right(const struct xdr_buf *buf, 59962306a36Sopenharmony_ci unsigned int base, unsigned int len, 60062306a36Sopenharmony_ci unsigned int shift) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci const struct kvec *tail = buf->tail; 60362306a36Sopenharmony_ci unsigned int to = base + shift; 60462306a36Sopenharmony_ci unsigned int pglen = 0; 60562306a36Sopenharmony_ci unsigned int talen = 0, tato = 0; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (base >= buf->page_len) 60862306a36Sopenharmony_ci return; 60962306a36Sopenharmony_ci if (len > buf->page_len - base) 61062306a36Sopenharmony_ci len = buf->page_len - base; 61162306a36Sopenharmony_ci if (to >= buf->page_len) { 61262306a36Sopenharmony_ci tato = to - buf->page_len; 61362306a36Sopenharmony_ci if (tail->iov_len >= len + tato) 61462306a36Sopenharmony_ci talen = len; 61562306a36Sopenharmony_ci else if (tail->iov_len > tato) 61662306a36Sopenharmony_ci talen = tail->iov_len - tato; 61762306a36Sopenharmony_ci } else if (len + to >= buf->page_len) { 61862306a36Sopenharmony_ci pglen = buf->page_len - to; 61962306a36Sopenharmony_ci talen = len - pglen; 62062306a36Sopenharmony_ci if (talen > tail->iov_len) 62162306a36Sopenharmony_ci talen = tail->iov_len; 62262306a36Sopenharmony_ci } else 62362306a36Sopenharmony_ci pglen = len; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci _copy_from_pages(tail->iov_base + tato, buf->pages, 62662306a36Sopenharmony_ci buf->page_base + base + pglen, talen); 62762306a36Sopenharmony_ci _shift_data_right_pages(buf->pages, buf->page_base + to, 62862306a36Sopenharmony_ci buf->page_base + base, pglen); 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic void xdr_buf_head_copy_right(const struct xdr_buf *buf, 63262306a36Sopenharmony_ci unsigned int base, unsigned int len, 63362306a36Sopenharmony_ci unsigned int shift) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci const struct kvec *head = buf->head; 63662306a36Sopenharmony_ci const struct kvec *tail = buf->tail; 63762306a36Sopenharmony_ci unsigned int to = base + shift; 63862306a36Sopenharmony_ci unsigned int pglen = 0, pgto = 0; 63962306a36Sopenharmony_ci unsigned int talen = 0, tato = 0; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (base >= head->iov_len) 64262306a36Sopenharmony_ci return; 64362306a36Sopenharmony_ci if (len > head->iov_len - base) 64462306a36Sopenharmony_ci len = head->iov_len - base; 64562306a36Sopenharmony_ci if (to >= buf->page_len + head->iov_len) { 64662306a36Sopenharmony_ci tato = to - buf->page_len - head->iov_len; 64762306a36Sopenharmony_ci talen = len; 64862306a36Sopenharmony_ci } else if (to >= head->iov_len) { 64962306a36Sopenharmony_ci pgto = to - head->iov_len; 65062306a36Sopenharmony_ci pglen = len; 65162306a36Sopenharmony_ci if (pgto + pglen > buf->page_len) { 65262306a36Sopenharmony_ci talen = pgto + pglen - buf->page_len; 65362306a36Sopenharmony_ci pglen -= talen; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci } else { 65662306a36Sopenharmony_ci pglen = len - to; 65762306a36Sopenharmony_ci if (pglen > buf->page_len) { 65862306a36Sopenharmony_ci talen = pglen - buf->page_len; 65962306a36Sopenharmony_ci pglen = buf->page_len; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci len -= talen; 66462306a36Sopenharmony_ci base += len; 66562306a36Sopenharmony_ci if (talen + tato > tail->iov_len) 66662306a36Sopenharmony_ci talen = tail->iov_len > tato ? tail->iov_len - tato : 0; 66762306a36Sopenharmony_ci memcpy(tail->iov_base + tato, head->iov_base + base, talen); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci len -= pglen; 67062306a36Sopenharmony_ci base -= pglen; 67162306a36Sopenharmony_ci _copy_to_pages(buf->pages, buf->page_base + pgto, head->iov_base + base, 67262306a36Sopenharmony_ci pglen); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci base -= len; 67562306a36Sopenharmony_ci memmove(head->iov_base + to, head->iov_base + base, len); 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cistatic void xdr_buf_tail_shift_right(const struct xdr_buf *buf, 67962306a36Sopenharmony_ci unsigned int base, unsigned int len, 68062306a36Sopenharmony_ci unsigned int shift) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci const struct kvec *tail = buf->tail; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (base >= tail->iov_len || !shift || !len) 68562306a36Sopenharmony_ci return; 68662306a36Sopenharmony_ci xdr_buf_tail_copy_right(buf, base, len, shift); 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic void xdr_buf_pages_shift_right(const struct xdr_buf *buf, 69062306a36Sopenharmony_ci unsigned int base, unsigned int len, 69162306a36Sopenharmony_ci unsigned int shift) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci if (!shift || !len) 69462306a36Sopenharmony_ci return; 69562306a36Sopenharmony_ci if (base >= buf->page_len) { 69662306a36Sopenharmony_ci xdr_buf_tail_shift_right(buf, base - buf->page_len, len, shift); 69762306a36Sopenharmony_ci return; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci if (base + len > buf->page_len) 70062306a36Sopenharmony_ci xdr_buf_tail_shift_right(buf, 0, base + len - buf->page_len, 70162306a36Sopenharmony_ci shift); 70262306a36Sopenharmony_ci xdr_buf_pages_copy_right(buf, base, len, shift); 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic void xdr_buf_head_shift_right(const struct xdr_buf *buf, 70662306a36Sopenharmony_ci unsigned int base, unsigned int len, 70762306a36Sopenharmony_ci unsigned int shift) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci const struct kvec *head = buf->head; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (!shift) 71262306a36Sopenharmony_ci return; 71362306a36Sopenharmony_ci if (base >= head->iov_len) { 71462306a36Sopenharmony_ci xdr_buf_pages_shift_right(buf, head->iov_len - base, len, 71562306a36Sopenharmony_ci shift); 71662306a36Sopenharmony_ci return; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci if (base + len > head->iov_len) 71962306a36Sopenharmony_ci xdr_buf_pages_shift_right(buf, 0, base + len - head->iov_len, 72062306a36Sopenharmony_ci shift); 72162306a36Sopenharmony_ci xdr_buf_head_copy_right(buf, base, len, shift); 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic void xdr_buf_tail_copy_left(const struct xdr_buf *buf, unsigned int base, 72562306a36Sopenharmony_ci unsigned int len, unsigned int shift) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci const struct kvec *tail = buf->tail; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (base >= tail->iov_len) 73062306a36Sopenharmony_ci return; 73162306a36Sopenharmony_ci if (len > tail->iov_len - base) 73262306a36Sopenharmony_ci len = tail->iov_len - base; 73362306a36Sopenharmony_ci /* Shift data into head */ 73462306a36Sopenharmony_ci if (shift > buf->page_len + base) { 73562306a36Sopenharmony_ci const struct kvec *head = buf->head; 73662306a36Sopenharmony_ci unsigned int hdto = 73762306a36Sopenharmony_ci head->iov_len + buf->page_len + base - shift; 73862306a36Sopenharmony_ci unsigned int hdlen = len; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (WARN_ONCE(shift > head->iov_len + buf->page_len + base, 74162306a36Sopenharmony_ci "SUNRPC: Misaligned data.\n")) 74262306a36Sopenharmony_ci return; 74362306a36Sopenharmony_ci if (hdto + hdlen > head->iov_len) 74462306a36Sopenharmony_ci hdlen = head->iov_len - hdto; 74562306a36Sopenharmony_ci memcpy(head->iov_base + hdto, tail->iov_base + base, hdlen); 74662306a36Sopenharmony_ci base += hdlen; 74762306a36Sopenharmony_ci len -= hdlen; 74862306a36Sopenharmony_ci if (!len) 74962306a36Sopenharmony_ci return; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci /* Shift data into pages */ 75262306a36Sopenharmony_ci if (shift > base) { 75362306a36Sopenharmony_ci unsigned int pgto = buf->page_len + base - shift; 75462306a36Sopenharmony_ci unsigned int pglen = len; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (pgto + pglen > buf->page_len) 75762306a36Sopenharmony_ci pglen = buf->page_len - pgto; 75862306a36Sopenharmony_ci _copy_to_pages(buf->pages, buf->page_base + pgto, 75962306a36Sopenharmony_ci tail->iov_base + base, pglen); 76062306a36Sopenharmony_ci base += pglen; 76162306a36Sopenharmony_ci len -= pglen; 76262306a36Sopenharmony_ci if (!len) 76362306a36Sopenharmony_ci return; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci memmove(tail->iov_base + base - shift, tail->iov_base + base, len); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic void xdr_buf_pages_copy_left(const struct xdr_buf *buf, 76962306a36Sopenharmony_ci unsigned int base, unsigned int len, 77062306a36Sopenharmony_ci unsigned int shift) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci unsigned int pgto; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (base >= buf->page_len) 77562306a36Sopenharmony_ci return; 77662306a36Sopenharmony_ci if (len > buf->page_len - base) 77762306a36Sopenharmony_ci len = buf->page_len - base; 77862306a36Sopenharmony_ci /* Shift data into head */ 77962306a36Sopenharmony_ci if (shift > base) { 78062306a36Sopenharmony_ci const struct kvec *head = buf->head; 78162306a36Sopenharmony_ci unsigned int hdto = head->iov_len + base - shift; 78262306a36Sopenharmony_ci unsigned int hdlen = len; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (WARN_ONCE(shift > head->iov_len + base, 78562306a36Sopenharmony_ci "SUNRPC: Misaligned data.\n")) 78662306a36Sopenharmony_ci return; 78762306a36Sopenharmony_ci if (hdto + hdlen > head->iov_len) 78862306a36Sopenharmony_ci hdlen = head->iov_len - hdto; 78962306a36Sopenharmony_ci _copy_from_pages(head->iov_base + hdto, buf->pages, 79062306a36Sopenharmony_ci buf->page_base + base, hdlen); 79162306a36Sopenharmony_ci base += hdlen; 79262306a36Sopenharmony_ci len -= hdlen; 79362306a36Sopenharmony_ci if (!len) 79462306a36Sopenharmony_ci return; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci pgto = base - shift; 79762306a36Sopenharmony_ci _shift_data_left_pages(buf->pages, buf->page_base + pgto, 79862306a36Sopenharmony_ci buf->page_base + base, len); 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_cistatic void xdr_buf_tail_shift_left(const struct xdr_buf *buf, 80262306a36Sopenharmony_ci unsigned int base, unsigned int len, 80362306a36Sopenharmony_ci unsigned int shift) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci if (!shift || !len) 80662306a36Sopenharmony_ci return; 80762306a36Sopenharmony_ci xdr_buf_tail_copy_left(buf, base, len, shift); 80862306a36Sopenharmony_ci} 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistatic void xdr_buf_pages_shift_left(const struct xdr_buf *buf, 81162306a36Sopenharmony_ci unsigned int base, unsigned int len, 81262306a36Sopenharmony_ci unsigned int shift) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci if (!shift || !len) 81562306a36Sopenharmony_ci return; 81662306a36Sopenharmony_ci if (base >= buf->page_len) { 81762306a36Sopenharmony_ci xdr_buf_tail_shift_left(buf, base - buf->page_len, len, shift); 81862306a36Sopenharmony_ci return; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci xdr_buf_pages_copy_left(buf, base, len, shift); 82162306a36Sopenharmony_ci len += base; 82262306a36Sopenharmony_ci if (len <= buf->page_len) 82362306a36Sopenharmony_ci return; 82462306a36Sopenharmony_ci xdr_buf_tail_copy_left(buf, 0, len - buf->page_len, shift); 82562306a36Sopenharmony_ci} 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cistatic void xdr_buf_head_shift_left(const struct xdr_buf *buf, 82862306a36Sopenharmony_ci unsigned int base, unsigned int len, 82962306a36Sopenharmony_ci unsigned int shift) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci const struct kvec *head = buf->head; 83262306a36Sopenharmony_ci unsigned int bytes; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (!shift || !len) 83562306a36Sopenharmony_ci return; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci if (shift > base) { 83862306a36Sopenharmony_ci bytes = (shift - base); 83962306a36Sopenharmony_ci if (bytes >= len) 84062306a36Sopenharmony_ci return; 84162306a36Sopenharmony_ci base += bytes; 84262306a36Sopenharmony_ci len -= bytes; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (base < head->iov_len) { 84662306a36Sopenharmony_ci bytes = min_t(unsigned int, len, head->iov_len - base); 84762306a36Sopenharmony_ci memmove(head->iov_base + (base - shift), 84862306a36Sopenharmony_ci head->iov_base + base, bytes); 84962306a36Sopenharmony_ci base += bytes; 85062306a36Sopenharmony_ci len -= bytes; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci xdr_buf_pages_shift_left(buf, base - head->iov_len, len, shift); 85362306a36Sopenharmony_ci} 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci/** 85662306a36Sopenharmony_ci * xdr_shrink_bufhead 85762306a36Sopenharmony_ci * @buf: xdr_buf 85862306a36Sopenharmony_ci * @len: new length of buf->head[0] 85962306a36Sopenharmony_ci * 86062306a36Sopenharmony_ci * Shrinks XDR buffer's header kvec buf->head[0], setting it to 86162306a36Sopenharmony_ci * 'len' bytes. The extra data is not lost, but is instead 86262306a36Sopenharmony_ci * moved into the inlined pages and/or the tail. 86362306a36Sopenharmony_ci */ 86462306a36Sopenharmony_cistatic unsigned int xdr_shrink_bufhead(struct xdr_buf *buf, unsigned int len) 86562306a36Sopenharmony_ci{ 86662306a36Sopenharmony_ci struct kvec *head = buf->head; 86762306a36Sopenharmony_ci unsigned int shift, buflen = max(buf->len, len); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci WARN_ON_ONCE(len > head->iov_len); 87062306a36Sopenharmony_ci if (head->iov_len > buflen) { 87162306a36Sopenharmony_ci buf->buflen -= head->iov_len - buflen; 87262306a36Sopenharmony_ci head->iov_len = buflen; 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci if (len >= head->iov_len) 87562306a36Sopenharmony_ci return 0; 87662306a36Sopenharmony_ci shift = head->iov_len - len; 87762306a36Sopenharmony_ci xdr_buf_try_expand(buf, shift); 87862306a36Sopenharmony_ci xdr_buf_head_shift_right(buf, len, buflen - len, shift); 87962306a36Sopenharmony_ci head->iov_len = len; 88062306a36Sopenharmony_ci buf->buflen -= shift; 88162306a36Sopenharmony_ci buf->len -= shift; 88262306a36Sopenharmony_ci return shift; 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci/** 88662306a36Sopenharmony_ci * xdr_shrink_pagelen - shrinks buf->pages to @len bytes 88762306a36Sopenharmony_ci * @buf: xdr_buf 88862306a36Sopenharmony_ci * @len: new page buffer length 88962306a36Sopenharmony_ci * 89062306a36Sopenharmony_ci * The extra data is not lost, but is instead moved into buf->tail. 89162306a36Sopenharmony_ci * Returns the actual number of bytes moved. 89262306a36Sopenharmony_ci */ 89362306a36Sopenharmony_cistatic unsigned int xdr_shrink_pagelen(struct xdr_buf *buf, unsigned int len) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci unsigned int shift, buflen = buf->len - buf->head->iov_len; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci WARN_ON_ONCE(len > buf->page_len); 89862306a36Sopenharmony_ci if (buf->head->iov_len >= buf->len || len > buflen) 89962306a36Sopenharmony_ci buflen = len; 90062306a36Sopenharmony_ci if (buf->page_len > buflen) { 90162306a36Sopenharmony_ci buf->buflen -= buf->page_len - buflen; 90262306a36Sopenharmony_ci buf->page_len = buflen; 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci if (len >= buf->page_len) 90562306a36Sopenharmony_ci return 0; 90662306a36Sopenharmony_ci shift = buf->page_len - len; 90762306a36Sopenharmony_ci xdr_buf_try_expand(buf, shift); 90862306a36Sopenharmony_ci xdr_buf_pages_shift_right(buf, len, buflen - len, shift); 90962306a36Sopenharmony_ci buf->page_len = len; 91062306a36Sopenharmony_ci buf->len -= shift; 91162306a36Sopenharmony_ci buf->buflen -= shift; 91262306a36Sopenharmony_ci return shift; 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci/** 91662306a36Sopenharmony_ci * xdr_stream_pos - Return the current offset from the start of the xdr_stream 91762306a36Sopenharmony_ci * @xdr: pointer to struct xdr_stream 91862306a36Sopenharmony_ci */ 91962306a36Sopenharmony_ciunsigned int xdr_stream_pos(const struct xdr_stream *xdr) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci return (unsigned int)(XDR_QUADLEN(xdr->buf->len) - xdr->nwords) << 2; 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_stream_pos); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic void xdr_stream_set_pos(struct xdr_stream *xdr, unsigned int pos) 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci unsigned int blen = xdr->buf->len; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci xdr->nwords = blen > pos ? XDR_QUADLEN(blen) - XDR_QUADLEN(pos) : 0; 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cistatic void xdr_stream_page_set_pos(struct xdr_stream *xdr, unsigned int pos) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci xdr_stream_set_pos(xdr, pos + xdr->buf->head[0].iov_len); 93562306a36Sopenharmony_ci} 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci/** 93862306a36Sopenharmony_ci * xdr_page_pos - Return the current offset from the start of the xdr pages 93962306a36Sopenharmony_ci * @xdr: pointer to struct xdr_stream 94062306a36Sopenharmony_ci */ 94162306a36Sopenharmony_ciunsigned int xdr_page_pos(const struct xdr_stream *xdr) 94262306a36Sopenharmony_ci{ 94362306a36Sopenharmony_ci unsigned int pos = xdr_stream_pos(xdr); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci WARN_ON(pos < xdr->buf->head[0].iov_len); 94662306a36Sopenharmony_ci return pos - xdr->buf->head[0].iov_len; 94762306a36Sopenharmony_ci} 94862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_page_pos); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci/** 95162306a36Sopenharmony_ci * xdr_init_encode - Initialize a struct xdr_stream for sending data. 95262306a36Sopenharmony_ci * @xdr: pointer to xdr_stream struct 95362306a36Sopenharmony_ci * @buf: pointer to XDR buffer in which to encode data 95462306a36Sopenharmony_ci * @p: current pointer inside XDR buffer 95562306a36Sopenharmony_ci * @rqst: pointer to controlling rpc_rqst, for debugging 95662306a36Sopenharmony_ci * 95762306a36Sopenharmony_ci * Note: at the moment the RPC client only passes the length of our 95862306a36Sopenharmony_ci * scratch buffer in the xdr_buf's header kvec. Previously this 95962306a36Sopenharmony_ci * meant we needed to call xdr_adjust_iovec() after encoding the 96062306a36Sopenharmony_ci * data. With the new scheme, the xdr_stream manages the details 96162306a36Sopenharmony_ci * of the buffer length, and takes care of adjusting the kvec 96262306a36Sopenharmony_ci * length for us. 96362306a36Sopenharmony_ci */ 96462306a36Sopenharmony_civoid xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p, 96562306a36Sopenharmony_ci struct rpc_rqst *rqst) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci struct kvec *iov = buf->head; 96862306a36Sopenharmony_ci int scratch_len = buf->buflen - buf->page_len - buf->tail[0].iov_len; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci xdr_reset_scratch_buffer(xdr); 97162306a36Sopenharmony_ci BUG_ON(scratch_len < 0); 97262306a36Sopenharmony_ci xdr->buf = buf; 97362306a36Sopenharmony_ci xdr->iov = iov; 97462306a36Sopenharmony_ci xdr->p = (__be32 *)((char *)iov->iov_base + iov->iov_len); 97562306a36Sopenharmony_ci xdr->end = (__be32 *)((char *)iov->iov_base + scratch_len); 97662306a36Sopenharmony_ci BUG_ON(iov->iov_len > scratch_len); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci if (p != xdr->p && p != NULL) { 97962306a36Sopenharmony_ci size_t len; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci BUG_ON(p < xdr->p || p > xdr->end); 98262306a36Sopenharmony_ci len = (char *)p - (char *)xdr->p; 98362306a36Sopenharmony_ci xdr->p = p; 98462306a36Sopenharmony_ci buf->len += len; 98562306a36Sopenharmony_ci iov->iov_len += len; 98662306a36Sopenharmony_ci } 98762306a36Sopenharmony_ci xdr->rqst = rqst; 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_init_encode); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci/** 99262306a36Sopenharmony_ci * xdr_init_encode_pages - Initialize an xdr_stream for encoding into pages 99362306a36Sopenharmony_ci * @xdr: pointer to xdr_stream struct 99462306a36Sopenharmony_ci * @buf: pointer to XDR buffer into which to encode data 99562306a36Sopenharmony_ci * @pages: list of pages to decode into 99662306a36Sopenharmony_ci * @rqst: pointer to controlling rpc_rqst, for debugging 99762306a36Sopenharmony_ci * 99862306a36Sopenharmony_ci */ 99962306a36Sopenharmony_civoid xdr_init_encode_pages(struct xdr_stream *xdr, struct xdr_buf *buf, 100062306a36Sopenharmony_ci struct page **pages, struct rpc_rqst *rqst) 100162306a36Sopenharmony_ci{ 100262306a36Sopenharmony_ci xdr_reset_scratch_buffer(xdr); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci xdr->buf = buf; 100562306a36Sopenharmony_ci xdr->page_ptr = pages; 100662306a36Sopenharmony_ci xdr->iov = NULL; 100762306a36Sopenharmony_ci xdr->p = page_address(*pages); 100862306a36Sopenharmony_ci xdr->end = (void *)xdr->p + min_t(u32, buf->buflen, PAGE_SIZE); 100962306a36Sopenharmony_ci xdr->rqst = rqst; 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_init_encode_pages); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci/** 101462306a36Sopenharmony_ci * __xdr_commit_encode - Ensure all data is written to buffer 101562306a36Sopenharmony_ci * @xdr: pointer to xdr_stream 101662306a36Sopenharmony_ci * 101762306a36Sopenharmony_ci * We handle encoding across page boundaries by giving the caller a 101862306a36Sopenharmony_ci * temporary location to write to, then later copying the data into 101962306a36Sopenharmony_ci * place; xdr_commit_encode does that copying. 102062306a36Sopenharmony_ci * 102162306a36Sopenharmony_ci * Normally the caller doesn't need to call this directly, as the 102262306a36Sopenharmony_ci * following xdr_reserve_space will do it. But an explicit call may be 102362306a36Sopenharmony_ci * required at the end of encoding, or any other time when the xdr_buf 102462306a36Sopenharmony_ci * data might be read. 102562306a36Sopenharmony_ci */ 102662306a36Sopenharmony_civoid __xdr_commit_encode(struct xdr_stream *xdr) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci size_t shift = xdr->scratch.iov_len; 102962306a36Sopenharmony_ci void *page; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci page = page_address(*xdr->page_ptr); 103262306a36Sopenharmony_ci memcpy(xdr->scratch.iov_base, page, shift); 103362306a36Sopenharmony_ci memmove(page, page + shift, (void *)xdr->p - page); 103462306a36Sopenharmony_ci xdr_reset_scratch_buffer(xdr); 103562306a36Sopenharmony_ci} 103662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__xdr_commit_encode); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci/* 103962306a36Sopenharmony_ci * The buffer space to be reserved crosses the boundary between 104062306a36Sopenharmony_ci * xdr->buf->head and xdr->buf->pages, or between two pages 104162306a36Sopenharmony_ci * in xdr->buf->pages. 104262306a36Sopenharmony_ci */ 104362306a36Sopenharmony_cistatic noinline __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr, 104462306a36Sopenharmony_ci size_t nbytes) 104562306a36Sopenharmony_ci{ 104662306a36Sopenharmony_ci int space_left; 104762306a36Sopenharmony_ci int frag1bytes, frag2bytes; 104862306a36Sopenharmony_ci void *p; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci if (nbytes > PAGE_SIZE) 105162306a36Sopenharmony_ci goto out_overflow; /* Bigger buffers require special handling */ 105262306a36Sopenharmony_ci if (xdr->buf->len + nbytes > xdr->buf->buflen) 105362306a36Sopenharmony_ci goto out_overflow; /* Sorry, we're totally out of space */ 105462306a36Sopenharmony_ci frag1bytes = (xdr->end - xdr->p) << 2; 105562306a36Sopenharmony_ci frag2bytes = nbytes - frag1bytes; 105662306a36Sopenharmony_ci if (xdr->iov) 105762306a36Sopenharmony_ci xdr->iov->iov_len += frag1bytes; 105862306a36Sopenharmony_ci else 105962306a36Sopenharmony_ci xdr->buf->page_len += frag1bytes; 106062306a36Sopenharmony_ci xdr->page_ptr++; 106162306a36Sopenharmony_ci xdr->iov = NULL; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci /* 106462306a36Sopenharmony_ci * If the last encode didn't end exactly on a page boundary, the 106562306a36Sopenharmony_ci * next one will straddle boundaries. Encode into the next 106662306a36Sopenharmony_ci * page, then copy it back later in xdr_commit_encode. We use 106762306a36Sopenharmony_ci * the "scratch" iov to track any temporarily unused fragment of 106862306a36Sopenharmony_ci * space at the end of the previous buffer: 106962306a36Sopenharmony_ci */ 107062306a36Sopenharmony_ci xdr_set_scratch_buffer(xdr, xdr->p, frag1bytes); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci /* 107362306a36Sopenharmony_ci * xdr->p is where the next encode will start after 107462306a36Sopenharmony_ci * xdr_commit_encode() has shifted this one back: 107562306a36Sopenharmony_ci */ 107662306a36Sopenharmony_ci p = page_address(*xdr->page_ptr); 107762306a36Sopenharmony_ci xdr->p = p + frag2bytes; 107862306a36Sopenharmony_ci space_left = xdr->buf->buflen - xdr->buf->len; 107962306a36Sopenharmony_ci if (space_left - frag1bytes >= PAGE_SIZE) 108062306a36Sopenharmony_ci xdr->end = p + PAGE_SIZE; 108162306a36Sopenharmony_ci else 108262306a36Sopenharmony_ci xdr->end = p + space_left - frag1bytes; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci xdr->buf->page_len += frag2bytes; 108562306a36Sopenharmony_ci xdr->buf->len += nbytes; 108662306a36Sopenharmony_ci return p; 108762306a36Sopenharmony_ciout_overflow: 108862306a36Sopenharmony_ci trace_rpc_xdr_overflow(xdr, nbytes); 108962306a36Sopenharmony_ci return NULL; 109062306a36Sopenharmony_ci} 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci/** 109362306a36Sopenharmony_ci * xdr_reserve_space - Reserve buffer space for sending 109462306a36Sopenharmony_ci * @xdr: pointer to xdr_stream 109562306a36Sopenharmony_ci * @nbytes: number of bytes to reserve 109662306a36Sopenharmony_ci * 109762306a36Sopenharmony_ci * Checks that we have enough buffer space to encode 'nbytes' more 109862306a36Sopenharmony_ci * bytes of data. If so, update the total xdr_buf length, and 109962306a36Sopenharmony_ci * adjust the length of the current kvec. 110062306a36Sopenharmony_ci */ 110162306a36Sopenharmony_ci__be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes) 110262306a36Sopenharmony_ci{ 110362306a36Sopenharmony_ci __be32 *p = xdr->p; 110462306a36Sopenharmony_ci __be32 *q; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci xdr_commit_encode(xdr); 110762306a36Sopenharmony_ci /* align nbytes on the next 32-bit boundary */ 110862306a36Sopenharmony_ci nbytes += 3; 110962306a36Sopenharmony_ci nbytes &= ~3; 111062306a36Sopenharmony_ci q = p + (nbytes >> 2); 111162306a36Sopenharmony_ci if (unlikely(q > xdr->end || q < p)) 111262306a36Sopenharmony_ci return xdr_get_next_encode_buffer(xdr, nbytes); 111362306a36Sopenharmony_ci xdr->p = q; 111462306a36Sopenharmony_ci if (xdr->iov) 111562306a36Sopenharmony_ci xdr->iov->iov_len += nbytes; 111662306a36Sopenharmony_ci else 111762306a36Sopenharmony_ci xdr->buf->page_len += nbytes; 111862306a36Sopenharmony_ci xdr->buf->len += nbytes; 111962306a36Sopenharmony_ci return p; 112062306a36Sopenharmony_ci} 112162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_reserve_space); 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci/** 112462306a36Sopenharmony_ci * xdr_reserve_space_vec - Reserves a large amount of buffer space for sending 112562306a36Sopenharmony_ci * @xdr: pointer to xdr_stream 112662306a36Sopenharmony_ci * @nbytes: number of bytes to reserve 112762306a36Sopenharmony_ci * 112862306a36Sopenharmony_ci * The size argument passed to xdr_reserve_space() is determined based 112962306a36Sopenharmony_ci * on the number of bytes remaining in the current page to avoid 113062306a36Sopenharmony_ci * invalidating iov_base pointers when xdr_commit_encode() is called. 113162306a36Sopenharmony_ci * 113262306a36Sopenharmony_ci * Return values: 113362306a36Sopenharmony_ci * %0: success 113462306a36Sopenharmony_ci * %-EMSGSIZE: not enough space is available in @xdr 113562306a36Sopenharmony_ci */ 113662306a36Sopenharmony_ciint xdr_reserve_space_vec(struct xdr_stream *xdr, size_t nbytes) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci size_t thislen; 113962306a36Sopenharmony_ci __be32 *p; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci /* 114262306a36Sopenharmony_ci * svcrdma requires every READ payload to start somewhere 114362306a36Sopenharmony_ci * in xdr->pages. 114462306a36Sopenharmony_ci */ 114562306a36Sopenharmony_ci if (xdr->iov == xdr->buf->head) { 114662306a36Sopenharmony_ci xdr->iov = NULL; 114762306a36Sopenharmony_ci xdr->end = xdr->p; 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci /* XXX: Let's find a way to make this more efficient */ 115162306a36Sopenharmony_ci while (nbytes) { 115262306a36Sopenharmony_ci thislen = xdr->buf->page_len % PAGE_SIZE; 115362306a36Sopenharmony_ci thislen = min_t(size_t, nbytes, PAGE_SIZE - thislen); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci p = xdr_reserve_space(xdr, thislen); 115662306a36Sopenharmony_ci if (!p) 115762306a36Sopenharmony_ci return -EMSGSIZE; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci nbytes -= thislen; 116062306a36Sopenharmony_ci } 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci return 0; 116362306a36Sopenharmony_ci} 116462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_reserve_space_vec); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci/** 116762306a36Sopenharmony_ci * xdr_truncate_encode - truncate an encode buffer 116862306a36Sopenharmony_ci * @xdr: pointer to xdr_stream 116962306a36Sopenharmony_ci * @len: new length of buffer 117062306a36Sopenharmony_ci * 117162306a36Sopenharmony_ci * Truncates the xdr stream, so that xdr->buf->len == len, 117262306a36Sopenharmony_ci * and xdr->p points at offset len from the start of the buffer, and 117362306a36Sopenharmony_ci * head, tail, and page lengths are adjusted to correspond. 117462306a36Sopenharmony_ci * 117562306a36Sopenharmony_ci * If this means moving xdr->p to a different buffer, we assume that 117662306a36Sopenharmony_ci * the end pointer should be set to the end of the current page, 117762306a36Sopenharmony_ci * except in the case of the head buffer when we assume the head 117862306a36Sopenharmony_ci * buffer's current length represents the end of the available buffer. 117962306a36Sopenharmony_ci * 118062306a36Sopenharmony_ci * This is *not* safe to use on a buffer that already has inlined page 118162306a36Sopenharmony_ci * cache pages (as in a zero-copy server read reply), except for the 118262306a36Sopenharmony_ci * simple case of truncating from one position in the tail to another. 118362306a36Sopenharmony_ci * 118462306a36Sopenharmony_ci */ 118562306a36Sopenharmony_civoid xdr_truncate_encode(struct xdr_stream *xdr, size_t len) 118662306a36Sopenharmony_ci{ 118762306a36Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 118862306a36Sopenharmony_ci struct kvec *head = buf->head; 118962306a36Sopenharmony_ci struct kvec *tail = buf->tail; 119062306a36Sopenharmony_ci int fraglen; 119162306a36Sopenharmony_ci int new; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci if (len > buf->len) { 119462306a36Sopenharmony_ci WARN_ON_ONCE(1); 119562306a36Sopenharmony_ci return; 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci xdr_commit_encode(xdr); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci fraglen = min_t(int, buf->len - len, tail->iov_len); 120062306a36Sopenharmony_ci tail->iov_len -= fraglen; 120162306a36Sopenharmony_ci buf->len -= fraglen; 120262306a36Sopenharmony_ci if (tail->iov_len) { 120362306a36Sopenharmony_ci xdr->p = tail->iov_base + tail->iov_len; 120462306a36Sopenharmony_ci WARN_ON_ONCE(!xdr->end); 120562306a36Sopenharmony_ci WARN_ON_ONCE(!xdr->iov); 120662306a36Sopenharmony_ci return; 120762306a36Sopenharmony_ci } 120862306a36Sopenharmony_ci WARN_ON_ONCE(fraglen); 120962306a36Sopenharmony_ci fraglen = min_t(int, buf->len - len, buf->page_len); 121062306a36Sopenharmony_ci buf->page_len -= fraglen; 121162306a36Sopenharmony_ci buf->len -= fraglen; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci new = buf->page_base + buf->page_len; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci xdr->page_ptr = buf->pages + (new >> PAGE_SHIFT); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci if (buf->page_len) { 121862306a36Sopenharmony_ci xdr->p = page_address(*xdr->page_ptr); 121962306a36Sopenharmony_ci xdr->end = (void *)xdr->p + PAGE_SIZE; 122062306a36Sopenharmony_ci xdr->p = (void *)xdr->p + (new % PAGE_SIZE); 122162306a36Sopenharmony_ci WARN_ON_ONCE(xdr->iov); 122262306a36Sopenharmony_ci return; 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci if (fraglen) 122562306a36Sopenharmony_ci xdr->end = head->iov_base + head->iov_len; 122662306a36Sopenharmony_ci /* (otherwise assume xdr->end is already set) */ 122762306a36Sopenharmony_ci xdr->page_ptr--; 122862306a36Sopenharmony_ci head->iov_len = len; 122962306a36Sopenharmony_ci buf->len = len; 123062306a36Sopenharmony_ci xdr->p = head->iov_base + head->iov_len; 123162306a36Sopenharmony_ci xdr->iov = buf->head; 123262306a36Sopenharmony_ci} 123362306a36Sopenharmony_ciEXPORT_SYMBOL(xdr_truncate_encode); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci/** 123662306a36Sopenharmony_ci * xdr_truncate_decode - Truncate a decoding stream 123762306a36Sopenharmony_ci * @xdr: pointer to struct xdr_stream 123862306a36Sopenharmony_ci * @len: Number of bytes to remove 123962306a36Sopenharmony_ci * 124062306a36Sopenharmony_ci */ 124162306a36Sopenharmony_civoid xdr_truncate_decode(struct xdr_stream *xdr, size_t len) 124262306a36Sopenharmony_ci{ 124362306a36Sopenharmony_ci unsigned int nbytes = xdr_align_size(len); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci xdr->buf->len -= nbytes; 124662306a36Sopenharmony_ci xdr->nwords -= XDR_QUADLEN(nbytes); 124762306a36Sopenharmony_ci} 124862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_truncate_decode); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci/** 125162306a36Sopenharmony_ci * xdr_restrict_buflen - decrease available buffer space 125262306a36Sopenharmony_ci * @xdr: pointer to xdr_stream 125362306a36Sopenharmony_ci * @newbuflen: new maximum number of bytes available 125462306a36Sopenharmony_ci * 125562306a36Sopenharmony_ci * Adjust our idea of how much space is available in the buffer. 125662306a36Sopenharmony_ci * If we've already used too much space in the buffer, returns -1. 125762306a36Sopenharmony_ci * If the available space is already smaller than newbuflen, returns 0 125862306a36Sopenharmony_ci * and does nothing. Otherwise, adjusts xdr->buf->buflen to newbuflen 125962306a36Sopenharmony_ci * and ensures xdr->end is set at most offset newbuflen from the start 126062306a36Sopenharmony_ci * of the buffer. 126162306a36Sopenharmony_ci */ 126262306a36Sopenharmony_ciint xdr_restrict_buflen(struct xdr_stream *xdr, int newbuflen) 126362306a36Sopenharmony_ci{ 126462306a36Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 126562306a36Sopenharmony_ci int left_in_this_buf = (void *)xdr->end - (void *)xdr->p; 126662306a36Sopenharmony_ci int end_offset = buf->len + left_in_this_buf; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci if (newbuflen < 0 || newbuflen < buf->len) 126962306a36Sopenharmony_ci return -1; 127062306a36Sopenharmony_ci if (newbuflen > buf->buflen) 127162306a36Sopenharmony_ci return 0; 127262306a36Sopenharmony_ci if (newbuflen < end_offset) 127362306a36Sopenharmony_ci xdr->end = (void *)xdr->end + newbuflen - end_offset; 127462306a36Sopenharmony_ci buf->buflen = newbuflen; 127562306a36Sopenharmony_ci return 0; 127662306a36Sopenharmony_ci} 127762306a36Sopenharmony_ciEXPORT_SYMBOL(xdr_restrict_buflen); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci/** 128062306a36Sopenharmony_ci * xdr_write_pages - Insert a list of pages into an XDR buffer for sending 128162306a36Sopenharmony_ci * @xdr: pointer to xdr_stream 128262306a36Sopenharmony_ci * @pages: array of pages to insert 128362306a36Sopenharmony_ci * @base: starting offset of first data byte in @pages 128462306a36Sopenharmony_ci * @len: number of data bytes in @pages to insert 128562306a36Sopenharmony_ci * 128662306a36Sopenharmony_ci * After the @pages are added, the tail iovec is instantiated pointing to 128762306a36Sopenharmony_ci * end of the head buffer, and the stream is set up to encode subsequent 128862306a36Sopenharmony_ci * items into the tail. 128962306a36Sopenharmony_ci */ 129062306a36Sopenharmony_civoid xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int base, 129162306a36Sopenharmony_ci unsigned int len) 129262306a36Sopenharmony_ci{ 129362306a36Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 129462306a36Sopenharmony_ci struct kvec *tail = buf->tail; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci buf->pages = pages; 129762306a36Sopenharmony_ci buf->page_base = base; 129862306a36Sopenharmony_ci buf->page_len = len; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci tail->iov_base = xdr->p; 130162306a36Sopenharmony_ci tail->iov_len = 0; 130262306a36Sopenharmony_ci xdr->iov = tail; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci if (len & 3) { 130562306a36Sopenharmony_ci unsigned int pad = 4 - (len & 3); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci BUG_ON(xdr->p >= xdr->end); 130862306a36Sopenharmony_ci tail->iov_base = (char *)xdr->p + (len & 3); 130962306a36Sopenharmony_ci tail->iov_len += pad; 131062306a36Sopenharmony_ci len += pad; 131162306a36Sopenharmony_ci *xdr->p++ = 0; 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci buf->buflen += len; 131462306a36Sopenharmony_ci buf->len += len; 131562306a36Sopenharmony_ci} 131662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_write_pages); 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_cistatic unsigned int xdr_set_iov(struct xdr_stream *xdr, struct kvec *iov, 131962306a36Sopenharmony_ci unsigned int base, unsigned int len) 132062306a36Sopenharmony_ci{ 132162306a36Sopenharmony_ci if (len > iov->iov_len) 132262306a36Sopenharmony_ci len = iov->iov_len; 132362306a36Sopenharmony_ci if (unlikely(base > len)) 132462306a36Sopenharmony_ci base = len; 132562306a36Sopenharmony_ci xdr->p = (__be32*)(iov->iov_base + base); 132662306a36Sopenharmony_ci xdr->end = (__be32*)(iov->iov_base + len); 132762306a36Sopenharmony_ci xdr->iov = iov; 132862306a36Sopenharmony_ci xdr->page_ptr = NULL; 132962306a36Sopenharmony_ci return len - base; 133062306a36Sopenharmony_ci} 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_cistatic unsigned int xdr_set_tail_base(struct xdr_stream *xdr, 133362306a36Sopenharmony_ci unsigned int base, unsigned int len) 133462306a36Sopenharmony_ci{ 133562306a36Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci xdr_stream_set_pos(xdr, base + buf->page_len + buf->head->iov_len); 133862306a36Sopenharmony_ci return xdr_set_iov(xdr, buf->tail, base, len); 133962306a36Sopenharmony_ci} 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_cistatic void xdr_stream_unmap_current_page(struct xdr_stream *xdr) 134262306a36Sopenharmony_ci{ 134362306a36Sopenharmony_ci if (xdr->page_kaddr) { 134462306a36Sopenharmony_ci kunmap_local(xdr->page_kaddr); 134562306a36Sopenharmony_ci xdr->page_kaddr = NULL; 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci} 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_cistatic unsigned int xdr_set_page_base(struct xdr_stream *xdr, 135062306a36Sopenharmony_ci unsigned int base, unsigned int len) 135162306a36Sopenharmony_ci{ 135262306a36Sopenharmony_ci unsigned int pgnr; 135362306a36Sopenharmony_ci unsigned int maxlen; 135462306a36Sopenharmony_ci unsigned int pgoff; 135562306a36Sopenharmony_ci unsigned int pgend; 135662306a36Sopenharmony_ci void *kaddr; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci maxlen = xdr->buf->page_len; 135962306a36Sopenharmony_ci if (base >= maxlen) 136062306a36Sopenharmony_ci return 0; 136162306a36Sopenharmony_ci else 136262306a36Sopenharmony_ci maxlen -= base; 136362306a36Sopenharmony_ci if (len > maxlen) 136462306a36Sopenharmony_ci len = maxlen; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci xdr_stream_unmap_current_page(xdr); 136762306a36Sopenharmony_ci xdr_stream_page_set_pos(xdr, base); 136862306a36Sopenharmony_ci base += xdr->buf->page_base; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci pgnr = base >> PAGE_SHIFT; 137162306a36Sopenharmony_ci xdr->page_ptr = &xdr->buf->pages[pgnr]; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci if (PageHighMem(*xdr->page_ptr)) { 137462306a36Sopenharmony_ci xdr->page_kaddr = kmap_local_page(*xdr->page_ptr); 137562306a36Sopenharmony_ci kaddr = xdr->page_kaddr; 137662306a36Sopenharmony_ci } else 137762306a36Sopenharmony_ci kaddr = page_address(*xdr->page_ptr); 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci pgoff = base & ~PAGE_MASK; 138062306a36Sopenharmony_ci xdr->p = (__be32*)(kaddr + pgoff); 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci pgend = pgoff + len; 138362306a36Sopenharmony_ci if (pgend > PAGE_SIZE) 138462306a36Sopenharmony_ci pgend = PAGE_SIZE; 138562306a36Sopenharmony_ci xdr->end = (__be32*)(kaddr + pgend); 138662306a36Sopenharmony_ci xdr->iov = NULL; 138762306a36Sopenharmony_ci return len; 138862306a36Sopenharmony_ci} 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_cistatic void xdr_set_page(struct xdr_stream *xdr, unsigned int base, 139162306a36Sopenharmony_ci unsigned int len) 139262306a36Sopenharmony_ci{ 139362306a36Sopenharmony_ci if (xdr_set_page_base(xdr, base, len) == 0) { 139462306a36Sopenharmony_ci base -= xdr->buf->page_len; 139562306a36Sopenharmony_ci xdr_set_tail_base(xdr, base, len); 139662306a36Sopenharmony_ci } 139762306a36Sopenharmony_ci} 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_cistatic void xdr_set_next_page(struct xdr_stream *xdr) 140062306a36Sopenharmony_ci{ 140162306a36Sopenharmony_ci unsigned int newbase; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci newbase = (1 + xdr->page_ptr - xdr->buf->pages) << PAGE_SHIFT; 140462306a36Sopenharmony_ci newbase -= xdr->buf->page_base; 140562306a36Sopenharmony_ci if (newbase < xdr->buf->page_len) 140662306a36Sopenharmony_ci xdr_set_page_base(xdr, newbase, xdr_stream_remaining(xdr)); 140762306a36Sopenharmony_ci else 140862306a36Sopenharmony_ci xdr_set_tail_base(xdr, 0, xdr_stream_remaining(xdr)); 140962306a36Sopenharmony_ci} 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_cistatic bool xdr_set_next_buffer(struct xdr_stream *xdr) 141262306a36Sopenharmony_ci{ 141362306a36Sopenharmony_ci if (xdr->page_ptr != NULL) 141462306a36Sopenharmony_ci xdr_set_next_page(xdr); 141562306a36Sopenharmony_ci else if (xdr->iov == xdr->buf->head) 141662306a36Sopenharmony_ci xdr_set_page(xdr, 0, xdr_stream_remaining(xdr)); 141762306a36Sopenharmony_ci return xdr->p != xdr->end; 141862306a36Sopenharmony_ci} 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci/** 142162306a36Sopenharmony_ci * xdr_init_decode - Initialize an xdr_stream for decoding data. 142262306a36Sopenharmony_ci * @xdr: pointer to xdr_stream struct 142362306a36Sopenharmony_ci * @buf: pointer to XDR buffer from which to decode data 142462306a36Sopenharmony_ci * @p: current pointer inside XDR buffer 142562306a36Sopenharmony_ci * @rqst: pointer to controlling rpc_rqst, for debugging 142662306a36Sopenharmony_ci */ 142762306a36Sopenharmony_civoid xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p, 142862306a36Sopenharmony_ci struct rpc_rqst *rqst) 142962306a36Sopenharmony_ci{ 143062306a36Sopenharmony_ci xdr->buf = buf; 143162306a36Sopenharmony_ci xdr->page_kaddr = NULL; 143262306a36Sopenharmony_ci xdr_reset_scratch_buffer(xdr); 143362306a36Sopenharmony_ci xdr->nwords = XDR_QUADLEN(buf->len); 143462306a36Sopenharmony_ci if (xdr_set_iov(xdr, buf->head, 0, buf->len) == 0 && 143562306a36Sopenharmony_ci xdr_set_page_base(xdr, 0, buf->len) == 0) 143662306a36Sopenharmony_ci xdr_set_iov(xdr, buf->tail, 0, buf->len); 143762306a36Sopenharmony_ci if (p != NULL && p > xdr->p && xdr->end >= p) { 143862306a36Sopenharmony_ci xdr->nwords -= p - xdr->p; 143962306a36Sopenharmony_ci xdr->p = p; 144062306a36Sopenharmony_ci } 144162306a36Sopenharmony_ci xdr->rqst = rqst; 144262306a36Sopenharmony_ci} 144362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_init_decode); 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci/** 144662306a36Sopenharmony_ci * xdr_init_decode_pages - Initialize an xdr_stream for decoding into pages 144762306a36Sopenharmony_ci * @xdr: pointer to xdr_stream struct 144862306a36Sopenharmony_ci * @buf: pointer to XDR buffer from which to decode data 144962306a36Sopenharmony_ci * @pages: list of pages to decode into 145062306a36Sopenharmony_ci * @len: length in bytes of buffer in pages 145162306a36Sopenharmony_ci */ 145262306a36Sopenharmony_civoid xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf, 145362306a36Sopenharmony_ci struct page **pages, unsigned int len) 145462306a36Sopenharmony_ci{ 145562306a36Sopenharmony_ci memset(buf, 0, sizeof(*buf)); 145662306a36Sopenharmony_ci buf->pages = pages; 145762306a36Sopenharmony_ci buf->page_len = len; 145862306a36Sopenharmony_ci buf->buflen = len; 145962306a36Sopenharmony_ci buf->len = len; 146062306a36Sopenharmony_ci xdr_init_decode(xdr, buf, NULL, NULL); 146162306a36Sopenharmony_ci} 146262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_init_decode_pages); 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci/** 146562306a36Sopenharmony_ci * xdr_finish_decode - Clean up the xdr_stream after decoding data. 146662306a36Sopenharmony_ci * @xdr: pointer to xdr_stream struct 146762306a36Sopenharmony_ci */ 146862306a36Sopenharmony_civoid xdr_finish_decode(struct xdr_stream *xdr) 146962306a36Sopenharmony_ci{ 147062306a36Sopenharmony_ci xdr_stream_unmap_current_page(xdr); 147162306a36Sopenharmony_ci} 147262306a36Sopenharmony_ciEXPORT_SYMBOL(xdr_finish_decode); 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_cistatic __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) 147562306a36Sopenharmony_ci{ 147662306a36Sopenharmony_ci unsigned int nwords = XDR_QUADLEN(nbytes); 147762306a36Sopenharmony_ci __be32 *p = xdr->p; 147862306a36Sopenharmony_ci __be32 *q = p + nwords; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci if (unlikely(nwords > xdr->nwords || q > xdr->end || q < p)) 148162306a36Sopenharmony_ci return NULL; 148262306a36Sopenharmony_ci xdr->p = q; 148362306a36Sopenharmony_ci xdr->nwords -= nwords; 148462306a36Sopenharmony_ci return p; 148562306a36Sopenharmony_ci} 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_cistatic __be32 *xdr_copy_to_scratch(struct xdr_stream *xdr, size_t nbytes) 148862306a36Sopenharmony_ci{ 148962306a36Sopenharmony_ci __be32 *p; 149062306a36Sopenharmony_ci char *cpdest = xdr->scratch.iov_base; 149162306a36Sopenharmony_ci size_t cplen = (char *)xdr->end - (char *)xdr->p; 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci if (nbytes > xdr->scratch.iov_len) 149462306a36Sopenharmony_ci goto out_overflow; 149562306a36Sopenharmony_ci p = __xdr_inline_decode(xdr, cplen); 149662306a36Sopenharmony_ci if (p == NULL) 149762306a36Sopenharmony_ci return NULL; 149862306a36Sopenharmony_ci memcpy(cpdest, p, cplen); 149962306a36Sopenharmony_ci if (!xdr_set_next_buffer(xdr)) 150062306a36Sopenharmony_ci goto out_overflow; 150162306a36Sopenharmony_ci cpdest += cplen; 150262306a36Sopenharmony_ci nbytes -= cplen; 150362306a36Sopenharmony_ci p = __xdr_inline_decode(xdr, nbytes); 150462306a36Sopenharmony_ci if (p == NULL) 150562306a36Sopenharmony_ci return NULL; 150662306a36Sopenharmony_ci memcpy(cpdest, p, nbytes); 150762306a36Sopenharmony_ci return xdr->scratch.iov_base; 150862306a36Sopenharmony_ciout_overflow: 150962306a36Sopenharmony_ci trace_rpc_xdr_overflow(xdr, nbytes); 151062306a36Sopenharmony_ci return NULL; 151162306a36Sopenharmony_ci} 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci/** 151462306a36Sopenharmony_ci * xdr_inline_decode - Retrieve XDR data to decode 151562306a36Sopenharmony_ci * @xdr: pointer to xdr_stream struct 151662306a36Sopenharmony_ci * @nbytes: number of bytes of data to decode 151762306a36Sopenharmony_ci * 151862306a36Sopenharmony_ci * Check if the input buffer is long enough to enable us to decode 151962306a36Sopenharmony_ci * 'nbytes' more bytes of data starting at the current position. 152062306a36Sopenharmony_ci * If so return the current pointer, then update the current 152162306a36Sopenharmony_ci * pointer position. 152262306a36Sopenharmony_ci */ 152362306a36Sopenharmony_ci__be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) 152462306a36Sopenharmony_ci{ 152562306a36Sopenharmony_ci __be32 *p; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci if (unlikely(nbytes == 0)) 152862306a36Sopenharmony_ci return xdr->p; 152962306a36Sopenharmony_ci if (xdr->p == xdr->end && !xdr_set_next_buffer(xdr)) 153062306a36Sopenharmony_ci goto out_overflow; 153162306a36Sopenharmony_ci p = __xdr_inline_decode(xdr, nbytes); 153262306a36Sopenharmony_ci if (p != NULL) 153362306a36Sopenharmony_ci return p; 153462306a36Sopenharmony_ci return xdr_copy_to_scratch(xdr, nbytes); 153562306a36Sopenharmony_ciout_overflow: 153662306a36Sopenharmony_ci trace_rpc_xdr_overflow(xdr, nbytes); 153762306a36Sopenharmony_ci return NULL; 153862306a36Sopenharmony_ci} 153962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_inline_decode); 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_cistatic void xdr_realign_pages(struct xdr_stream *xdr) 154262306a36Sopenharmony_ci{ 154362306a36Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 154462306a36Sopenharmony_ci struct kvec *iov = buf->head; 154562306a36Sopenharmony_ci unsigned int cur = xdr_stream_pos(xdr); 154662306a36Sopenharmony_ci unsigned int copied; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci /* Realign pages to current pointer position */ 154962306a36Sopenharmony_ci if (iov->iov_len > cur) { 155062306a36Sopenharmony_ci copied = xdr_shrink_bufhead(buf, cur); 155162306a36Sopenharmony_ci trace_rpc_xdr_alignment(xdr, cur, copied); 155262306a36Sopenharmony_ci xdr_set_page(xdr, 0, buf->page_len); 155362306a36Sopenharmony_ci } 155462306a36Sopenharmony_ci} 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_cistatic unsigned int xdr_align_pages(struct xdr_stream *xdr, unsigned int len) 155762306a36Sopenharmony_ci{ 155862306a36Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 155962306a36Sopenharmony_ci unsigned int nwords = XDR_QUADLEN(len); 156062306a36Sopenharmony_ci unsigned int copied; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci if (xdr->nwords == 0) 156362306a36Sopenharmony_ci return 0; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci xdr_realign_pages(xdr); 156662306a36Sopenharmony_ci if (nwords > xdr->nwords) { 156762306a36Sopenharmony_ci nwords = xdr->nwords; 156862306a36Sopenharmony_ci len = nwords << 2; 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci if (buf->page_len <= len) 157162306a36Sopenharmony_ci len = buf->page_len; 157262306a36Sopenharmony_ci else if (nwords < xdr->nwords) { 157362306a36Sopenharmony_ci /* Truncate page data and move it into the tail */ 157462306a36Sopenharmony_ci copied = xdr_shrink_pagelen(buf, len); 157562306a36Sopenharmony_ci trace_rpc_xdr_alignment(xdr, len, copied); 157662306a36Sopenharmony_ci } 157762306a36Sopenharmony_ci return len; 157862306a36Sopenharmony_ci} 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci/** 158162306a36Sopenharmony_ci * xdr_read_pages - align page-based XDR data to current pointer position 158262306a36Sopenharmony_ci * @xdr: pointer to xdr_stream struct 158362306a36Sopenharmony_ci * @len: number of bytes of page data 158462306a36Sopenharmony_ci * 158562306a36Sopenharmony_ci * Moves data beyond the current pointer position from the XDR head[] buffer 158662306a36Sopenharmony_ci * into the page list. Any data that lies beyond current position + @len 158762306a36Sopenharmony_ci * bytes is moved into the XDR tail[]. The xdr_stream current position is 158862306a36Sopenharmony_ci * then advanced past that data to align to the next XDR object in the tail. 158962306a36Sopenharmony_ci * 159062306a36Sopenharmony_ci * Returns the number of XDR encoded bytes now contained in the pages 159162306a36Sopenharmony_ci */ 159262306a36Sopenharmony_ciunsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len) 159362306a36Sopenharmony_ci{ 159462306a36Sopenharmony_ci unsigned int nwords = XDR_QUADLEN(len); 159562306a36Sopenharmony_ci unsigned int base, end, pglen; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci pglen = xdr_align_pages(xdr, nwords << 2); 159862306a36Sopenharmony_ci if (pglen == 0) 159962306a36Sopenharmony_ci return 0; 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci base = (nwords << 2) - pglen; 160262306a36Sopenharmony_ci end = xdr_stream_remaining(xdr) - pglen; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci xdr_set_tail_base(xdr, base, end); 160562306a36Sopenharmony_ci return len <= pglen ? len : pglen; 160662306a36Sopenharmony_ci} 160762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_read_pages); 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci/** 161062306a36Sopenharmony_ci * xdr_set_pagelen - Sets the length of the XDR pages 161162306a36Sopenharmony_ci * @xdr: pointer to xdr_stream struct 161262306a36Sopenharmony_ci * @len: new length of the XDR page data 161362306a36Sopenharmony_ci * 161462306a36Sopenharmony_ci * Either grows or shrinks the length of the xdr pages by setting pagelen to 161562306a36Sopenharmony_ci * @len bytes. When shrinking, any extra data is moved into buf->tail, whereas 161662306a36Sopenharmony_ci * when growing any data beyond the current pointer is moved into the tail. 161762306a36Sopenharmony_ci * 161862306a36Sopenharmony_ci * Returns True if the operation was successful, and False otherwise. 161962306a36Sopenharmony_ci */ 162062306a36Sopenharmony_civoid xdr_set_pagelen(struct xdr_stream *xdr, unsigned int len) 162162306a36Sopenharmony_ci{ 162262306a36Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 162362306a36Sopenharmony_ci size_t remaining = xdr_stream_remaining(xdr); 162462306a36Sopenharmony_ci size_t base = 0; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci if (len < buf->page_len) { 162762306a36Sopenharmony_ci base = buf->page_len - len; 162862306a36Sopenharmony_ci xdr_shrink_pagelen(buf, len); 162962306a36Sopenharmony_ci } else { 163062306a36Sopenharmony_ci xdr_buf_head_shift_right(buf, xdr_stream_pos(xdr), 163162306a36Sopenharmony_ci buf->page_len, remaining); 163262306a36Sopenharmony_ci if (len > buf->page_len) 163362306a36Sopenharmony_ci xdr_buf_try_expand(buf, len - buf->page_len); 163462306a36Sopenharmony_ci } 163562306a36Sopenharmony_ci xdr_set_tail_base(xdr, base, remaining); 163662306a36Sopenharmony_ci} 163762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_set_pagelen); 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci/** 164062306a36Sopenharmony_ci * xdr_enter_page - decode data from the XDR page 164162306a36Sopenharmony_ci * @xdr: pointer to xdr_stream struct 164262306a36Sopenharmony_ci * @len: number of bytes of page data 164362306a36Sopenharmony_ci * 164462306a36Sopenharmony_ci * Moves data beyond the current pointer position from the XDR head[] buffer 164562306a36Sopenharmony_ci * into the page list. Any data that lies beyond current position + "len" 164662306a36Sopenharmony_ci * bytes is moved into the XDR tail[]. The current pointer is then 164762306a36Sopenharmony_ci * repositioned at the beginning of the first XDR page. 164862306a36Sopenharmony_ci */ 164962306a36Sopenharmony_civoid xdr_enter_page(struct xdr_stream *xdr, unsigned int len) 165062306a36Sopenharmony_ci{ 165162306a36Sopenharmony_ci len = xdr_align_pages(xdr, len); 165262306a36Sopenharmony_ci /* 165362306a36Sopenharmony_ci * Position current pointer at beginning of tail, and 165462306a36Sopenharmony_ci * set remaining message length. 165562306a36Sopenharmony_ci */ 165662306a36Sopenharmony_ci if (len != 0) 165762306a36Sopenharmony_ci xdr_set_page_base(xdr, 0, len); 165862306a36Sopenharmony_ci} 165962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_enter_page); 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_cistatic const struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0}; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_civoid xdr_buf_from_iov(const struct kvec *iov, struct xdr_buf *buf) 166462306a36Sopenharmony_ci{ 166562306a36Sopenharmony_ci buf->head[0] = *iov; 166662306a36Sopenharmony_ci buf->tail[0] = empty_iov; 166762306a36Sopenharmony_ci buf->page_len = 0; 166862306a36Sopenharmony_ci buf->buflen = buf->len = iov->iov_len; 166962306a36Sopenharmony_ci} 167062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_buf_from_iov); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci/** 167362306a36Sopenharmony_ci * xdr_buf_subsegment - set subbuf to a portion of buf 167462306a36Sopenharmony_ci * @buf: an xdr buffer 167562306a36Sopenharmony_ci * @subbuf: the result buffer 167662306a36Sopenharmony_ci * @base: beginning of range in bytes 167762306a36Sopenharmony_ci * @len: length of range in bytes 167862306a36Sopenharmony_ci * 167962306a36Sopenharmony_ci * sets @subbuf to an xdr buffer representing the portion of @buf of 168062306a36Sopenharmony_ci * length @len starting at offset @base. 168162306a36Sopenharmony_ci * 168262306a36Sopenharmony_ci * @buf and @subbuf may be pointers to the same struct xdr_buf. 168362306a36Sopenharmony_ci * 168462306a36Sopenharmony_ci * Returns -1 if base or length are out of bounds. 168562306a36Sopenharmony_ci */ 168662306a36Sopenharmony_ciint xdr_buf_subsegment(const struct xdr_buf *buf, struct xdr_buf *subbuf, 168762306a36Sopenharmony_ci unsigned int base, unsigned int len) 168862306a36Sopenharmony_ci{ 168962306a36Sopenharmony_ci subbuf->buflen = subbuf->len = len; 169062306a36Sopenharmony_ci if (base < buf->head[0].iov_len) { 169162306a36Sopenharmony_ci subbuf->head[0].iov_base = buf->head[0].iov_base + base; 169262306a36Sopenharmony_ci subbuf->head[0].iov_len = min_t(unsigned int, len, 169362306a36Sopenharmony_ci buf->head[0].iov_len - base); 169462306a36Sopenharmony_ci len -= subbuf->head[0].iov_len; 169562306a36Sopenharmony_ci base = 0; 169662306a36Sopenharmony_ci } else { 169762306a36Sopenharmony_ci base -= buf->head[0].iov_len; 169862306a36Sopenharmony_ci subbuf->head[0].iov_base = buf->head[0].iov_base; 169962306a36Sopenharmony_ci subbuf->head[0].iov_len = 0; 170062306a36Sopenharmony_ci } 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci if (base < buf->page_len) { 170362306a36Sopenharmony_ci subbuf->page_len = min(buf->page_len - base, len); 170462306a36Sopenharmony_ci base += buf->page_base; 170562306a36Sopenharmony_ci subbuf->page_base = base & ~PAGE_MASK; 170662306a36Sopenharmony_ci subbuf->pages = &buf->pages[base >> PAGE_SHIFT]; 170762306a36Sopenharmony_ci len -= subbuf->page_len; 170862306a36Sopenharmony_ci base = 0; 170962306a36Sopenharmony_ci } else { 171062306a36Sopenharmony_ci base -= buf->page_len; 171162306a36Sopenharmony_ci subbuf->pages = buf->pages; 171262306a36Sopenharmony_ci subbuf->page_base = 0; 171362306a36Sopenharmony_ci subbuf->page_len = 0; 171462306a36Sopenharmony_ci } 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci if (base < buf->tail[0].iov_len) { 171762306a36Sopenharmony_ci subbuf->tail[0].iov_base = buf->tail[0].iov_base + base; 171862306a36Sopenharmony_ci subbuf->tail[0].iov_len = min_t(unsigned int, len, 171962306a36Sopenharmony_ci buf->tail[0].iov_len - base); 172062306a36Sopenharmony_ci len -= subbuf->tail[0].iov_len; 172162306a36Sopenharmony_ci base = 0; 172262306a36Sopenharmony_ci } else { 172362306a36Sopenharmony_ci base -= buf->tail[0].iov_len; 172462306a36Sopenharmony_ci subbuf->tail[0].iov_base = buf->tail[0].iov_base; 172562306a36Sopenharmony_ci subbuf->tail[0].iov_len = 0; 172662306a36Sopenharmony_ci } 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci if (base || len) 172962306a36Sopenharmony_ci return -1; 173062306a36Sopenharmony_ci return 0; 173162306a36Sopenharmony_ci} 173262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_buf_subsegment); 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci/** 173562306a36Sopenharmony_ci * xdr_stream_subsegment - set @subbuf to a portion of @xdr 173662306a36Sopenharmony_ci * @xdr: an xdr_stream set up for decoding 173762306a36Sopenharmony_ci * @subbuf: the result buffer 173862306a36Sopenharmony_ci * @nbytes: length of @xdr to extract, in bytes 173962306a36Sopenharmony_ci * 174062306a36Sopenharmony_ci * Sets up @subbuf to represent a portion of @xdr. The portion 174162306a36Sopenharmony_ci * starts at the current offset in @xdr, and extends for a length 174262306a36Sopenharmony_ci * of @nbytes. If this is successful, @xdr is advanced to the next 174362306a36Sopenharmony_ci * XDR data item following that portion. 174462306a36Sopenharmony_ci * 174562306a36Sopenharmony_ci * Return values: 174662306a36Sopenharmony_ci * %true: @subbuf has been initialized, and @xdr has been advanced. 174762306a36Sopenharmony_ci * %false: a bounds error has occurred 174862306a36Sopenharmony_ci */ 174962306a36Sopenharmony_cibool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf, 175062306a36Sopenharmony_ci unsigned int nbytes) 175162306a36Sopenharmony_ci{ 175262306a36Sopenharmony_ci unsigned int start = xdr_stream_pos(xdr); 175362306a36Sopenharmony_ci unsigned int remaining, len; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci /* Extract @subbuf and bounds-check the fn arguments */ 175662306a36Sopenharmony_ci if (xdr_buf_subsegment(xdr->buf, subbuf, start, nbytes)) 175762306a36Sopenharmony_ci return false; 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci /* Advance @xdr by @nbytes */ 176062306a36Sopenharmony_ci for (remaining = nbytes; remaining;) { 176162306a36Sopenharmony_ci if (xdr->p == xdr->end && !xdr_set_next_buffer(xdr)) 176262306a36Sopenharmony_ci return false; 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci len = (char *)xdr->end - (char *)xdr->p; 176562306a36Sopenharmony_ci if (remaining <= len) { 176662306a36Sopenharmony_ci xdr->p = (__be32 *)((char *)xdr->p + 176762306a36Sopenharmony_ci (remaining + xdr_pad_size(nbytes))); 176862306a36Sopenharmony_ci break; 176962306a36Sopenharmony_ci } 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci xdr->p = (__be32 *)((char *)xdr->p + len); 177262306a36Sopenharmony_ci xdr->end = xdr->p; 177362306a36Sopenharmony_ci remaining -= len; 177462306a36Sopenharmony_ci } 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci xdr_stream_set_pos(xdr, start + nbytes); 177762306a36Sopenharmony_ci return true; 177862306a36Sopenharmony_ci} 177962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_stream_subsegment); 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci/** 178262306a36Sopenharmony_ci * xdr_stream_move_subsegment - Move part of a stream to another position 178362306a36Sopenharmony_ci * @xdr: the source xdr_stream 178462306a36Sopenharmony_ci * @offset: the source offset of the segment 178562306a36Sopenharmony_ci * @target: the target offset of the segment 178662306a36Sopenharmony_ci * @length: the number of bytes to move 178762306a36Sopenharmony_ci * 178862306a36Sopenharmony_ci * Moves @length bytes from @offset to @target in the xdr_stream, overwriting 178962306a36Sopenharmony_ci * anything in its space. Returns the number of bytes in the segment. 179062306a36Sopenharmony_ci */ 179162306a36Sopenharmony_ciunsigned int xdr_stream_move_subsegment(struct xdr_stream *xdr, unsigned int offset, 179262306a36Sopenharmony_ci unsigned int target, unsigned int length) 179362306a36Sopenharmony_ci{ 179462306a36Sopenharmony_ci struct xdr_buf buf; 179562306a36Sopenharmony_ci unsigned int shift; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci if (offset < target) { 179862306a36Sopenharmony_ci shift = target - offset; 179962306a36Sopenharmony_ci if (xdr_buf_subsegment(xdr->buf, &buf, offset, shift + length) < 0) 180062306a36Sopenharmony_ci return 0; 180162306a36Sopenharmony_ci xdr_buf_head_shift_right(&buf, 0, length, shift); 180262306a36Sopenharmony_ci } else if (offset > target) { 180362306a36Sopenharmony_ci shift = offset - target; 180462306a36Sopenharmony_ci if (xdr_buf_subsegment(xdr->buf, &buf, target, shift + length) < 0) 180562306a36Sopenharmony_ci return 0; 180662306a36Sopenharmony_ci xdr_buf_head_shift_left(&buf, shift, length, shift); 180762306a36Sopenharmony_ci } 180862306a36Sopenharmony_ci return length; 180962306a36Sopenharmony_ci} 181062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_stream_move_subsegment); 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci/** 181362306a36Sopenharmony_ci * xdr_stream_zero - zero out a portion of an xdr_stream 181462306a36Sopenharmony_ci * @xdr: an xdr_stream to zero out 181562306a36Sopenharmony_ci * @offset: the starting point in the stream 181662306a36Sopenharmony_ci * @length: the number of bytes to zero 181762306a36Sopenharmony_ci */ 181862306a36Sopenharmony_ciunsigned int xdr_stream_zero(struct xdr_stream *xdr, unsigned int offset, 181962306a36Sopenharmony_ci unsigned int length) 182062306a36Sopenharmony_ci{ 182162306a36Sopenharmony_ci struct xdr_buf buf; 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci if (xdr_buf_subsegment(xdr->buf, &buf, offset, length) < 0) 182462306a36Sopenharmony_ci return 0; 182562306a36Sopenharmony_ci if (buf.head[0].iov_len) 182662306a36Sopenharmony_ci xdr_buf_iov_zero(buf.head, 0, buf.head[0].iov_len); 182762306a36Sopenharmony_ci if (buf.page_len > 0) 182862306a36Sopenharmony_ci xdr_buf_pages_zero(&buf, 0, buf.page_len); 182962306a36Sopenharmony_ci if (buf.tail[0].iov_len) 183062306a36Sopenharmony_ci xdr_buf_iov_zero(buf.tail, 0, buf.tail[0].iov_len); 183162306a36Sopenharmony_ci return length; 183262306a36Sopenharmony_ci} 183362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_stream_zero); 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci/** 183662306a36Sopenharmony_ci * xdr_buf_trim - lop at most "len" bytes off the end of "buf" 183762306a36Sopenharmony_ci * @buf: buf to be trimmed 183862306a36Sopenharmony_ci * @len: number of bytes to reduce "buf" by 183962306a36Sopenharmony_ci * 184062306a36Sopenharmony_ci * Trim an xdr_buf by the given number of bytes by fixing up the lengths. Note 184162306a36Sopenharmony_ci * that it's possible that we'll trim less than that amount if the xdr_buf is 184262306a36Sopenharmony_ci * too small, or if (for instance) it's all in the head and the parser has 184362306a36Sopenharmony_ci * already read too far into it. 184462306a36Sopenharmony_ci */ 184562306a36Sopenharmony_civoid xdr_buf_trim(struct xdr_buf *buf, unsigned int len) 184662306a36Sopenharmony_ci{ 184762306a36Sopenharmony_ci size_t cur; 184862306a36Sopenharmony_ci unsigned int trim = len; 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci if (buf->tail[0].iov_len) { 185162306a36Sopenharmony_ci cur = min_t(size_t, buf->tail[0].iov_len, trim); 185262306a36Sopenharmony_ci buf->tail[0].iov_len -= cur; 185362306a36Sopenharmony_ci trim -= cur; 185462306a36Sopenharmony_ci if (!trim) 185562306a36Sopenharmony_ci goto fix_len; 185662306a36Sopenharmony_ci } 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci if (buf->page_len) { 185962306a36Sopenharmony_ci cur = min_t(unsigned int, buf->page_len, trim); 186062306a36Sopenharmony_ci buf->page_len -= cur; 186162306a36Sopenharmony_ci trim -= cur; 186262306a36Sopenharmony_ci if (!trim) 186362306a36Sopenharmony_ci goto fix_len; 186462306a36Sopenharmony_ci } 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci if (buf->head[0].iov_len) { 186762306a36Sopenharmony_ci cur = min_t(size_t, buf->head[0].iov_len, trim); 186862306a36Sopenharmony_ci buf->head[0].iov_len -= cur; 186962306a36Sopenharmony_ci trim -= cur; 187062306a36Sopenharmony_ci } 187162306a36Sopenharmony_cifix_len: 187262306a36Sopenharmony_ci buf->len -= (len - trim); 187362306a36Sopenharmony_ci} 187462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_buf_trim); 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_cistatic void __read_bytes_from_xdr_buf(const struct xdr_buf *subbuf, 187762306a36Sopenharmony_ci void *obj, unsigned int len) 187862306a36Sopenharmony_ci{ 187962306a36Sopenharmony_ci unsigned int this_len; 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci this_len = min_t(unsigned int, len, subbuf->head[0].iov_len); 188262306a36Sopenharmony_ci memcpy(obj, subbuf->head[0].iov_base, this_len); 188362306a36Sopenharmony_ci len -= this_len; 188462306a36Sopenharmony_ci obj += this_len; 188562306a36Sopenharmony_ci this_len = min_t(unsigned int, len, subbuf->page_len); 188662306a36Sopenharmony_ci _copy_from_pages(obj, subbuf->pages, subbuf->page_base, this_len); 188762306a36Sopenharmony_ci len -= this_len; 188862306a36Sopenharmony_ci obj += this_len; 188962306a36Sopenharmony_ci this_len = min_t(unsigned int, len, subbuf->tail[0].iov_len); 189062306a36Sopenharmony_ci memcpy(obj, subbuf->tail[0].iov_base, this_len); 189162306a36Sopenharmony_ci} 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci/* obj is assumed to point to allocated memory of size at least len: */ 189462306a36Sopenharmony_ciint read_bytes_from_xdr_buf(const struct xdr_buf *buf, unsigned int base, 189562306a36Sopenharmony_ci void *obj, unsigned int len) 189662306a36Sopenharmony_ci{ 189762306a36Sopenharmony_ci struct xdr_buf subbuf; 189862306a36Sopenharmony_ci int status; 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci status = xdr_buf_subsegment(buf, &subbuf, base, len); 190162306a36Sopenharmony_ci if (status != 0) 190262306a36Sopenharmony_ci return status; 190362306a36Sopenharmony_ci __read_bytes_from_xdr_buf(&subbuf, obj, len); 190462306a36Sopenharmony_ci return 0; 190562306a36Sopenharmony_ci} 190662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(read_bytes_from_xdr_buf); 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_cistatic void __write_bytes_to_xdr_buf(const struct xdr_buf *subbuf, 190962306a36Sopenharmony_ci void *obj, unsigned int len) 191062306a36Sopenharmony_ci{ 191162306a36Sopenharmony_ci unsigned int this_len; 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci this_len = min_t(unsigned int, len, subbuf->head[0].iov_len); 191462306a36Sopenharmony_ci memcpy(subbuf->head[0].iov_base, obj, this_len); 191562306a36Sopenharmony_ci len -= this_len; 191662306a36Sopenharmony_ci obj += this_len; 191762306a36Sopenharmony_ci this_len = min_t(unsigned int, len, subbuf->page_len); 191862306a36Sopenharmony_ci _copy_to_pages(subbuf->pages, subbuf->page_base, obj, this_len); 191962306a36Sopenharmony_ci len -= this_len; 192062306a36Sopenharmony_ci obj += this_len; 192162306a36Sopenharmony_ci this_len = min_t(unsigned int, len, subbuf->tail[0].iov_len); 192262306a36Sopenharmony_ci memcpy(subbuf->tail[0].iov_base, obj, this_len); 192362306a36Sopenharmony_ci} 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci/* obj is assumed to point to allocated memory of size at least len: */ 192662306a36Sopenharmony_ciint write_bytes_to_xdr_buf(const struct xdr_buf *buf, unsigned int base, 192762306a36Sopenharmony_ci void *obj, unsigned int len) 192862306a36Sopenharmony_ci{ 192962306a36Sopenharmony_ci struct xdr_buf subbuf; 193062306a36Sopenharmony_ci int status; 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci status = xdr_buf_subsegment(buf, &subbuf, base, len); 193362306a36Sopenharmony_ci if (status != 0) 193462306a36Sopenharmony_ci return status; 193562306a36Sopenharmony_ci __write_bytes_to_xdr_buf(&subbuf, obj, len); 193662306a36Sopenharmony_ci return 0; 193762306a36Sopenharmony_ci} 193862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(write_bytes_to_xdr_buf); 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ciint xdr_decode_word(const struct xdr_buf *buf, unsigned int base, u32 *obj) 194162306a36Sopenharmony_ci{ 194262306a36Sopenharmony_ci __be32 raw; 194362306a36Sopenharmony_ci int status; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj)); 194662306a36Sopenharmony_ci if (status) 194762306a36Sopenharmony_ci return status; 194862306a36Sopenharmony_ci *obj = be32_to_cpu(raw); 194962306a36Sopenharmony_ci return 0; 195062306a36Sopenharmony_ci} 195162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_decode_word); 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ciint xdr_encode_word(const struct xdr_buf *buf, unsigned int base, u32 obj) 195462306a36Sopenharmony_ci{ 195562306a36Sopenharmony_ci __be32 raw = cpu_to_be32(obj); 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci return write_bytes_to_xdr_buf(buf, base, &raw, sizeof(obj)); 195862306a36Sopenharmony_ci} 195962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_encode_word); 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci/* Returns 0 on success, or else a negative error code. */ 196262306a36Sopenharmony_cistatic int xdr_xcode_array2(const struct xdr_buf *buf, unsigned int base, 196362306a36Sopenharmony_ci struct xdr_array2_desc *desc, int encode) 196462306a36Sopenharmony_ci{ 196562306a36Sopenharmony_ci char *elem = NULL, *c; 196662306a36Sopenharmony_ci unsigned int copied = 0, todo, avail_here; 196762306a36Sopenharmony_ci struct page **ppages = NULL; 196862306a36Sopenharmony_ci int err; 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci if (encode) { 197162306a36Sopenharmony_ci if (xdr_encode_word(buf, base, desc->array_len) != 0) 197262306a36Sopenharmony_ci return -EINVAL; 197362306a36Sopenharmony_ci } else { 197462306a36Sopenharmony_ci if (xdr_decode_word(buf, base, &desc->array_len) != 0 || 197562306a36Sopenharmony_ci desc->array_len > desc->array_maxlen || 197662306a36Sopenharmony_ci (unsigned long) base + 4 + desc->array_len * 197762306a36Sopenharmony_ci desc->elem_size > buf->len) 197862306a36Sopenharmony_ci return -EINVAL; 197962306a36Sopenharmony_ci } 198062306a36Sopenharmony_ci base += 4; 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci if (!desc->xcode) 198362306a36Sopenharmony_ci return 0; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci todo = desc->array_len * desc->elem_size; 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci /* process head */ 198862306a36Sopenharmony_ci if (todo && base < buf->head->iov_len) { 198962306a36Sopenharmony_ci c = buf->head->iov_base + base; 199062306a36Sopenharmony_ci avail_here = min_t(unsigned int, todo, 199162306a36Sopenharmony_ci buf->head->iov_len - base); 199262306a36Sopenharmony_ci todo -= avail_here; 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci while (avail_here >= desc->elem_size) { 199562306a36Sopenharmony_ci err = desc->xcode(desc, c); 199662306a36Sopenharmony_ci if (err) 199762306a36Sopenharmony_ci goto out; 199862306a36Sopenharmony_ci c += desc->elem_size; 199962306a36Sopenharmony_ci avail_here -= desc->elem_size; 200062306a36Sopenharmony_ci } 200162306a36Sopenharmony_ci if (avail_here) { 200262306a36Sopenharmony_ci if (!elem) { 200362306a36Sopenharmony_ci elem = kmalloc(desc->elem_size, GFP_KERNEL); 200462306a36Sopenharmony_ci err = -ENOMEM; 200562306a36Sopenharmony_ci if (!elem) 200662306a36Sopenharmony_ci goto out; 200762306a36Sopenharmony_ci } 200862306a36Sopenharmony_ci if (encode) { 200962306a36Sopenharmony_ci err = desc->xcode(desc, elem); 201062306a36Sopenharmony_ci if (err) 201162306a36Sopenharmony_ci goto out; 201262306a36Sopenharmony_ci memcpy(c, elem, avail_here); 201362306a36Sopenharmony_ci } else 201462306a36Sopenharmony_ci memcpy(elem, c, avail_here); 201562306a36Sopenharmony_ci copied = avail_here; 201662306a36Sopenharmony_ci } 201762306a36Sopenharmony_ci base = buf->head->iov_len; /* align to start of pages */ 201862306a36Sopenharmony_ci } 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci /* process pages array */ 202162306a36Sopenharmony_ci base -= buf->head->iov_len; 202262306a36Sopenharmony_ci if (todo && base < buf->page_len) { 202362306a36Sopenharmony_ci unsigned int avail_page; 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci avail_here = min(todo, buf->page_len - base); 202662306a36Sopenharmony_ci todo -= avail_here; 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci base += buf->page_base; 202962306a36Sopenharmony_ci ppages = buf->pages + (base >> PAGE_SHIFT); 203062306a36Sopenharmony_ci base &= ~PAGE_MASK; 203162306a36Sopenharmony_ci avail_page = min_t(unsigned int, PAGE_SIZE - base, 203262306a36Sopenharmony_ci avail_here); 203362306a36Sopenharmony_ci c = kmap(*ppages) + base; 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci while (avail_here) { 203662306a36Sopenharmony_ci avail_here -= avail_page; 203762306a36Sopenharmony_ci if (copied || avail_page < desc->elem_size) { 203862306a36Sopenharmony_ci unsigned int l = min(avail_page, 203962306a36Sopenharmony_ci desc->elem_size - copied); 204062306a36Sopenharmony_ci if (!elem) { 204162306a36Sopenharmony_ci elem = kmalloc(desc->elem_size, 204262306a36Sopenharmony_ci GFP_KERNEL); 204362306a36Sopenharmony_ci err = -ENOMEM; 204462306a36Sopenharmony_ci if (!elem) 204562306a36Sopenharmony_ci goto out; 204662306a36Sopenharmony_ci } 204762306a36Sopenharmony_ci if (encode) { 204862306a36Sopenharmony_ci if (!copied) { 204962306a36Sopenharmony_ci err = desc->xcode(desc, elem); 205062306a36Sopenharmony_ci if (err) 205162306a36Sopenharmony_ci goto out; 205262306a36Sopenharmony_ci } 205362306a36Sopenharmony_ci memcpy(c, elem + copied, l); 205462306a36Sopenharmony_ci copied += l; 205562306a36Sopenharmony_ci if (copied == desc->elem_size) 205662306a36Sopenharmony_ci copied = 0; 205762306a36Sopenharmony_ci } else { 205862306a36Sopenharmony_ci memcpy(elem + copied, c, l); 205962306a36Sopenharmony_ci copied += l; 206062306a36Sopenharmony_ci if (copied == desc->elem_size) { 206162306a36Sopenharmony_ci err = desc->xcode(desc, elem); 206262306a36Sopenharmony_ci if (err) 206362306a36Sopenharmony_ci goto out; 206462306a36Sopenharmony_ci copied = 0; 206562306a36Sopenharmony_ci } 206662306a36Sopenharmony_ci } 206762306a36Sopenharmony_ci avail_page -= l; 206862306a36Sopenharmony_ci c += l; 206962306a36Sopenharmony_ci } 207062306a36Sopenharmony_ci while (avail_page >= desc->elem_size) { 207162306a36Sopenharmony_ci err = desc->xcode(desc, c); 207262306a36Sopenharmony_ci if (err) 207362306a36Sopenharmony_ci goto out; 207462306a36Sopenharmony_ci c += desc->elem_size; 207562306a36Sopenharmony_ci avail_page -= desc->elem_size; 207662306a36Sopenharmony_ci } 207762306a36Sopenharmony_ci if (avail_page) { 207862306a36Sopenharmony_ci unsigned int l = min(avail_page, 207962306a36Sopenharmony_ci desc->elem_size - copied); 208062306a36Sopenharmony_ci if (!elem) { 208162306a36Sopenharmony_ci elem = kmalloc(desc->elem_size, 208262306a36Sopenharmony_ci GFP_KERNEL); 208362306a36Sopenharmony_ci err = -ENOMEM; 208462306a36Sopenharmony_ci if (!elem) 208562306a36Sopenharmony_ci goto out; 208662306a36Sopenharmony_ci } 208762306a36Sopenharmony_ci if (encode) { 208862306a36Sopenharmony_ci if (!copied) { 208962306a36Sopenharmony_ci err = desc->xcode(desc, elem); 209062306a36Sopenharmony_ci if (err) 209162306a36Sopenharmony_ci goto out; 209262306a36Sopenharmony_ci } 209362306a36Sopenharmony_ci memcpy(c, elem + copied, l); 209462306a36Sopenharmony_ci copied += l; 209562306a36Sopenharmony_ci if (copied == desc->elem_size) 209662306a36Sopenharmony_ci copied = 0; 209762306a36Sopenharmony_ci } else { 209862306a36Sopenharmony_ci memcpy(elem + copied, c, l); 209962306a36Sopenharmony_ci copied += l; 210062306a36Sopenharmony_ci if (copied == desc->elem_size) { 210162306a36Sopenharmony_ci err = desc->xcode(desc, elem); 210262306a36Sopenharmony_ci if (err) 210362306a36Sopenharmony_ci goto out; 210462306a36Sopenharmony_ci copied = 0; 210562306a36Sopenharmony_ci } 210662306a36Sopenharmony_ci } 210762306a36Sopenharmony_ci } 210862306a36Sopenharmony_ci if (avail_here) { 210962306a36Sopenharmony_ci kunmap(*ppages); 211062306a36Sopenharmony_ci ppages++; 211162306a36Sopenharmony_ci c = kmap(*ppages); 211262306a36Sopenharmony_ci } 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci avail_page = min(avail_here, 211562306a36Sopenharmony_ci (unsigned int) PAGE_SIZE); 211662306a36Sopenharmony_ci } 211762306a36Sopenharmony_ci base = buf->page_len; /* align to start of tail */ 211862306a36Sopenharmony_ci } 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci /* process tail */ 212162306a36Sopenharmony_ci base -= buf->page_len; 212262306a36Sopenharmony_ci if (todo) { 212362306a36Sopenharmony_ci c = buf->tail->iov_base + base; 212462306a36Sopenharmony_ci if (copied) { 212562306a36Sopenharmony_ci unsigned int l = desc->elem_size - copied; 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci if (encode) 212862306a36Sopenharmony_ci memcpy(c, elem + copied, l); 212962306a36Sopenharmony_ci else { 213062306a36Sopenharmony_ci memcpy(elem + copied, c, l); 213162306a36Sopenharmony_ci err = desc->xcode(desc, elem); 213262306a36Sopenharmony_ci if (err) 213362306a36Sopenharmony_ci goto out; 213462306a36Sopenharmony_ci } 213562306a36Sopenharmony_ci todo -= l; 213662306a36Sopenharmony_ci c += l; 213762306a36Sopenharmony_ci } 213862306a36Sopenharmony_ci while (todo) { 213962306a36Sopenharmony_ci err = desc->xcode(desc, c); 214062306a36Sopenharmony_ci if (err) 214162306a36Sopenharmony_ci goto out; 214262306a36Sopenharmony_ci c += desc->elem_size; 214362306a36Sopenharmony_ci todo -= desc->elem_size; 214462306a36Sopenharmony_ci } 214562306a36Sopenharmony_ci } 214662306a36Sopenharmony_ci err = 0; 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ciout: 214962306a36Sopenharmony_ci kfree(elem); 215062306a36Sopenharmony_ci if (ppages) 215162306a36Sopenharmony_ci kunmap(*ppages); 215262306a36Sopenharmony_ci return err; 215362306a36Sopenharmony_ci} 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ciint xdr_decode_array2(const struct xdr_buf *buf, unsigned int base, 215662306a36Sopenharmony_ci struct xdr_array2_desc *desc) 215762306a36Sopenharmony_ci{ 215862306a36Sopenharmony_ci if (base >= buf->len) 215962306a36Sopenharmony_ci return -EINVAL; 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci return xdr_xcode_array2(buf, base, desc, 0); 216262306a36Sopenharmony_ci} 216362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_decode_array2); 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ciint xdr_encode_array2(const struct xdr_buf *buf, unsigned int base, 216662306a36Sopenharmony_ci struct xdr_array2_desc *desc) 216762306a36Sopenharmony_ci{ 216862306a36Sopenharmony_ci if ((unsigned long) base + 4 + desc->array_len * desc->elem_size > 216962306a36Sopenharmony_ci buf->head->iov_len + buf->page_len + buf->tail->iov_len) 217062306a36Sopenharmony_ci return -EINVAL; 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci return xdr_xcode_array2(buf, base, desc, 1); 217362306a36Sopenharmony_ci} 217462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_encode_array2); 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ciint xdr_process_buf(const struct xdr_buf *buf, unsigned int offset, 217762306a36Sopenharmony_ci unsigned int len, 217862306a36Sopenharmony_ci int (*actor)(struct scatterlist *, void *), void *data) 217962306a36Sopenharmony_ci{ 218062306a36Sopenharmony_ci int i, ret = 0; 218162306a36Sopenharmony_ci unsigned int page_len, thislen, page_offset; 218262306a36Sopenharmony_ci struct scatterlist sg[1]; 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci sg_init_table(sg, 1); 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci if (offset >= buf->head[0].iov_len) { 218762306a36Sopenharmony_ci offset -= buf->head[0].iov_len; 218862306a36Sopenharmony_ci } else { 218962306a36Sopenharmony_ci thislen = buf->head[0].iov_len - offset; 219062306a36Sopenharmony_ci if (thislen > len) 219162306a36Sopenharmony_ci thislen = len; 219262306a36Sopenharmony_ci sg_set_buf(sg, buf->head[0].iov_base + offset, thislen); 219362306a36Sopenharmony_ci ret = actor(sg, data); 219462306a36Sopenharmony_ci if (ret) 219562306a36Sopenharmony_ci goto out; 219662306a36Sopenharmony_ci offset = 0; 219762306a36Sopenharmony_ci len -= thislen; 219862306a36Sopenharmony_ci } 219962306a36Sopenharmony_ci if (len == 0) 220062306a36Sopenharmony_ci goto out; 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci if (offset >= buf->page_len) { 220362306a36Sopenharmony_ci offset -= buf->page_len; 220462306a36Sopenharmony_ci } else { 220562306a36Sopenharmony_ci page_len = buf->page_len - offset; 220662306a36Sopenharmony_ci if (page_len > len) 220762306a36Sopenharmony_ci page_len = len; 220862306a36Sopenharmony_ci len -= page_len; 220962306a36Sopenharmony_ci page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1); 221062306a36Sopenharmony_ci i = (offset + buf->page_base) >> PAGE_SHIFT; 221162306a36Sopenharmony_ci thislen = PAGE_SIZE - page_offset; 221262306a36Sopenharmony_ci do { 221362306a36Sopenharmony_ci if (thislen > page_len) 221462306a36Sopenharmony_ci thislen = page_len; 221562306a36Sopenharmony_ci sg_set_page(sg, buf->pages[i], thislen, page_offset); 221662306a36Sopenharmony_ci ret = actor(sg, data); 221762306a36Sopenharmony_ci if (ret) 221862306a36Sopenharmony_ci goto out; 221962306a36Sopenharmony_ci page_len -= thislen; 222062306a36Sopenharmony_ci i++; 222162306a36Sopenharmony_ci page_offset = 0; 222262306a36Sopenharmony_ci thislen = PAGE_SIZE; 222362306a36Sopenharmony_ci } while (page_len != 0); 222462306a36Sopenharmony_ci offset = 0; 222562306a36Sopenharmony_ci } 222662306a36Sopenharmony_ci if (len == 0) 222762306a36Sopenharmony_ci goto out; 222862306a36Sopenharmony_ci if (offset < buf->tail[0].iov_len) { 222962306a36Sopenharmony_ci thislen = buf->tail[0].iov_len - offset; 223062306a36Sopenharmony_ci if (thislen > len) 223162306a36Sopenharmony_ci thislen = len; 223262306a36Sopenharmony_ci sg_set_buf(sg, buf->tail[0].iov_base + offset, thislen); 223362306a36Sopenharmony_ci ret = actor(sg, data); 223462306a36Sopenharmony_ci len -= thislen; 223562306a36Sopenharmony_ci } 223662306a36Sopenharmony_ci if (len != 0) 223762306a36Sopenharmony_ci ret = -EINVAL; 223862306a36Sopenharmony_ciout: 223962306a36Sopenharmony_ci return ret; 224062306a36Sopenharmony_ci} 224162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_process_buf); 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci/** 224462306a36Sopenharmony_ci * xdr_stream_decode_opaque - Decode variable length opaque 224562306a36Sopenharmony_ci * @xdr: pointer to xdr_stream 224662306a36Sopenharmony_ci * @ptr: location to store opaque data 224762306a36Sopenharmony_ci * @size: size of storage buffer @ptr 224862306a36Sopenharmony_ci * 224962306a36Sopenharmony_ci * Return values: 225062306a36Sopenharmony_ci * On success, returns size of object stored in *@ptr 225162306a36Sopenharmony_ci * %-EBADMSG on XDR buffer overflow 225262306a36Sopenharmony_ci * %-EMSGSIZE on overflow of storage buffer @ptr 225362306a36Sopenharmony_ci */ 225462306a36Sopenharmony_cissize_t xdr_stream_decode_opaque(struct xdr_stream *xdr, void *ptr, size_t size) 225562306a36Sopenharmony_ci{ 225662306a36Sopenharmony_ci ssize_t ret; 225762306a36Sopenharmony_ci void *p; 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci ret = xdr_stream_decode_opaque_inline(xdr, &p, size); 226062306a36Sopenharmony_ci if (ret <= 0) 226162306a36Sopenharmony_ci return ret; 226262306a36Sopenharmony_ci memcpy(ptr, p, ret); 226362306a36Sopenharmony_ci return ret; 226462306a36Sopenharmony_ci} 226562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_stream_decode_opaque); 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci/** 226862306a36Sopenharmony_ci * xdr_stream_decode_opaque_dup - Decode and duplicate variable length opaque 226962306a36Sopenharmony_ci * @xdr: pointer to xdr_stream 227062306a36Sopenharmony_ci * @ptr: location to store pointer to opaque data 227162306a36Sopenharmony_ci * @maxlen: maximum acceptable object size 227262306a36Sopenharmony_ci * @gfp_flags: GFP mask to use 227362306a36Sopenharmony_ci * 227462306a36Sopenharmony_ci * Return values: 227562306a36Sopenharmony_ci * On success, returns size of object stored in *@ptr 227662306a36Sopenharmony_ci * %-EBADMSG on XDR buffer overflow 227762306a36Sopenharmony_ci * %-EMSGSIZE if the size of the object would exceed @maxlen 227862306a36Sopenharmony_ci * %-ENOMEM on memory allocation failure 227962306a36Sopenharmony_ci */ 228062306a36Sopenharmony_cissize_t xdr_stream_decode_opaque_dup(struct xdr_stream *xdr, void **ptr, 228162306a36Sopenharmony_ci size_t maxlen, gfp_t gfp_flags) 228262306a36Sopenharmony_ci{ 228362306a36Sopenharmony_ci ssize_t ret; 228462306a36Sopenharmony_ci void *p; 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_ci ret = xdr_stream_decode_opaque_inline(xdr, &p, maxlen); 228762306a36Sopenharmony_ci if (ret > 0) { 228862306a36Sopenharmony_ci *ptr = kmemdup(p, ret, gfp_flags); 228962306a36Sopenharmony_ci if (*ptr != NULL) 229062306a36Sopenharmony_ci return ret; 229162306a36Sopenharmony_ci ret = -ENOMEM; 229262306a36Sopenharmony_ci } 229362306a36Sopenharmony_ci *ptr = NULL; 229462306a36Sopenharmony_ci return ret; 229562306a36Sopenharmony_ci} 229662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_stream_decode_opaque_dup); 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci/** 229962306a36Sopenharmony_ci * xdr_stream_decode_string - Decode variable length string 230062306a36Sopenharmony_ci * @xdr: pointer to xdr_stream 230162306a36Sopenharmony_ci * @str: location to store string 230262306a36Sopenharmony_ci * @size: size of storage buffer @str 230362306a36Sopenharmony_ci * 230462306a36Sopenharmony_ci * Return values: 230562306a36Sopenharmony_ci * On success, returns length of NUL-terminated string stored in *@str 230662306a36Sopenharmony_ci * %-EBADMSG on XDR buffer overflow 230762306a36Sopenharmony_ci * %-EMSGSIZE on overflow of storage buffer @str 230862306a36Sopenharmony_ci */ 230962306a36Sopenharmony_cissize_t xdr_stream_decode_string(struct xdr_stream *xdr, char *str, size_t size) 231062306a36Sopenharmony_ci{ 231162306a36Sopenharmony_ci ssize_t ret; 231262306a36Sopenharmony_ci void *p; 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci ret = xdr_stream_decode_opaque_inline(xdr, &p, size); 231562306a36Sopenharmony_ci if (ret > 0) { 231662306a36Sopenharmony_ci memcpy(str, p, ret); 231762306a36Sopenharmony_ci str[ret] = '\0'; 231862306a36Sopenharmony_ci return strlen(str); 231962306a36Sopenharmony_ci } 232062306a36Sopenharmony_ci *str = '\0'; 232162306a36Sopenharmony_ci return ret; 232262306a36Sopenharmony_ci} 232362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_stream_decode_string); 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_ci/** 232662306a36Sopenharmony_ci * xdr_stream_decode_string_dup - Decode and duplicate variable length string 232762306a36Sopenharmony_ci * @xdr: pointer to xdr_stream 232862306a36Sopenharmony_ci * @str: location to store pointer to string 232962306a36Sopenharmony_ci * @maxlen: maximum acceptable string length 233062306a36Sopenharmony_ci * @gfp_flags: GFP mask to use 233162306a36Sopenharmony_ci * 233262306a36Sopenharmony_ci * Return values: 233362306a36Sopenharmony_ci * On success, returns length of NUL-terminated string stored in *@ptr 233462306a36Sopenharmony_ci * %-EBADMSG on XDR buffer overflow 233562306a36Sopenharmony_ci * %-EMSGSIZE if the size of the string would exceed @maxlen 233662306a36Sopenharmony_ci * %-ENOMEM on memory allocation failure 233762306a36Sopenharmony_ci */ 233862306a36Sopenharmony_cissize_t xdr_stream_decode_string_dup(struct xdr_stream *xdr, char **str, 233962306a36Sopenharmony_ci size_t maxlen, gfp_t gfp_flags) 234062306a36Sopenharmony_ci{ 234162306a36Sopenharmony_ci void *p; 234262306a36Sopenharmony_ci ssize_t ret; 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci ret = xdr_stream_decode_opaque_inline(xdr, &p, maxlen); 234562306a36Sopenharmony_ci if (ret > 0) { 234662306a36Sopenharmony_ci char *s = kmemdup_nul(p, ret, gfp_flags); 234762306a36Sopenharmony_ci if (s != NULL) { 234862306a36Sopenharmony_ci *str = s; 234962306a36Sopenharmony_ci return strlen(s); 235062306a36Sopenharmony_ci } 235162306a36Sopenharmony_ci ret = -ENOMEM; 235262306a36Sopenharmony_ci } 235362306a36Sopenharmony_ci *str = NULL; 235462306a36Sopenharmony_ci return ret; 235562306a36Sopenharmony_ci} 235662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_stream_decode_string_dup); 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci/** 235962306a36Sopenharmony_ci * xdr_stream_decode_opaque_auth - Decode struct opaque_auth (RFC5531 S8.2) 236062306a36Sopenharmony_ci * @xdr: pointer to xdr_stream 236162306a36Sopenharmony_ci * @flavor: location to store decoded flavor 236262306a36Sopenharmony_ci * @body: location to store decode body 236362306a36Sopenharmony_ci * @body_len: location to store length of decoded body 236462306a36Sopenharmony_ci * 236562306a36Sopenharmony_ci * Return values: 236662306a36Sopenharmony_ci * On success, returns the number of buffer bytes consumed 236762306a36Sopenharmony_ci * %-EBADMSG on XDR buffer overflow 236862306a36Sopenharmony_ci * %-EMSGSIZE if the decoded size of the body field exceeds 400 octets 236962306a36Sopenharmony_ci */ 237062306a36Sopenharmony_cissize_t xdr_stream_decode_opaque_auth(struct xdr_stream *xdr, u32 *flavor, 237162306a36Sopenharmony_ci void **body, unsigned int *body_len) 237262306a36Sopenharmony_ci{ 237362306a36Sopenharmony_ci ssize_t ret, len; 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci len = xdr_stream_decode_u32(xdr, flavor); 237662306a36Sopenharmony_ci if (unlikely(len < 0)) 237762306a36Sopenharmony_ci return len; 237862306a36Sopenharmony_ci ret = xdr_stream_decode_opaque_inline(xdr, body, RPC_MAX_AUTH_SIZE); 237962306a36Sopenharmony_ci if (unlikely(ret < 0)) 238062306a36Sopenharmony_ci return ret; 238162306a36Sopenharmony_ci *body_len = ret; 238262306a36Sopenharmony_ci return len + ret; 238362306a36Sopenharmony_ci} 238462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_stream_decode_opaque_auth); 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci/** 238762306a36Sopenharmony_ci * xdr_stream_encode_opaque_auth - Encode struct opaque_auth (RFC5531 S8.2) 238862306a36Sopenharmony_ci * @xdr: pointer to xdr_stream 238962306a36Sopenharmony_ci * @flavor: verifier flavor to encode 239062306a36Sopenharmony_ci * @body: content of body to encode 239162306a36Sopenharmony_ci * @body_len: length of body to encode 239262306a36Sopenharmony_ci * 239362306a36Sopenharmony_ci * Return values: 239462306a36Sopenharmony_ci * On success, returns length in bytes of XDR buffer consumed 239562306a36Sopenharmony_ci * %-EBADMSG on XDR buffer overflow 239662306a36Sopenharmony_ci * %-EMSGSIZE if the size of @body exceeds 400 octets 239762306a36Sopenharmony_ci */ 239862306a36Sopenharmony_cissize_t xdr_stream_encode_opaque_auth(struct xdr_stream *xdr, u32 flavor, 239962306a36Sopenharmony_ci void *body, unsigned int body_len) 240062306a36Sopenharmony_ci{ 240162306a36Sopenharmony_ci ssize_t ret, len; 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci if (unlikely(body_len > RPC_MAX_AUTH_SIZE)) 240462306a36Sopenharmony_ci return -EMSGSIZE; 240562306a36Sopenharmony_ci len = xdr_stream_encode_u32(xdr, flavor); 240662306a36Sopenharmony_ci if (unlikely(len < 0)) 240762306a36Sopenharmony_ci return len; 240862306a36Sopenharmony_ci ret = xdr_stream_encode_opaque(xdr, body, body_len); 240962306a36Sopenharmony_ci if (unlikely(ret < 0)) 241062306a36Sopenharmony_ci return ret; 241162306a36Sopenharmony_ci return len + ret; 241262306a36Sopenharmony_ci} 241362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xdr_stream_encode_opaque_auth); 2414