📄 xdr.c
字号:
} while ((len -= copy) != 0);}/* * _copy_to_pages * @pages: array of pages * @pgbase: page vector address of destination * @p: pointer to source data * @len: length * * Copies data from an arbitrary memory location into an array of pages * The copy is assumed to be non-overlapping. */static void_copy_to_pages(struct page **pages, size_t pgbase, const char *p, size_t len){ struct page **pgto; char *vto; size_t copy; pgto = pages + (pgbase >> PAGE_CACHE_SHIFT); pgbase &= ~PAGE_CACHE_MASK; do { copy = PAGE_CACHE_SIZE - pgbase; if (copy > len) copy = len; vto = kmap_atomic(*pgto, KM_USER0); memcpy(vto + pgbase, p, copy); kunmap_atomic(vto, KM_USER0); pgbase += copy; if (pgbase == PAGE_CACHE_SIZE) { pgbase = 0; pgto++; } p += copy; } while ((len -= copy) != 0);}/* * _copy_from_pages * @p: pointer to destination * @pages: array of pages * @pgbase: offset of source data * @len: length * * Copies data into an arbitrary memory location from an array of pages * The copy is assumed to be non-overlapping. */void_copy_from_pages(char *p, struct page **pages, size_t pgbase, size_t len){ struct page **pgfrom; char *vfrom; size_t copy; pgfrom = pages + (pgbase >> PAGE_CACHE_SHIFT); pgbase &= ~PAGE_CACHE_MASK; do { copy = PAGE_CACHE_SIZE - pgbase; if (copy > len) copy = len; vfrom = kmap_atomic(*pgfrom, KM_USER0); memcpy(p, vfrom + pgbase, copy); kunmap_atomic(vfrom, KM_USER0); pgbase += copy; if (pgbase == PAGE_CACHE_SIZE) { pgbase = 0; pgfrom++; } p += copy; } while ((len -= copy) != 0);}/* * xdr_shrink_bufhead * @buf: xdr_buf * @len: bytes to remove from buf->head[0] * * Shrinks XDR buffer's header kvec buf->head[0] by * 'len' bytes. The extra data is not lost, but is instead * moved into the inlined pages and/or the tail. */voidxdr_shrink_bufhead(struct xdr_buf *buf, size_t len){ struct kvec *head, *tail; size_t copy, offs; unsigned int pglen = buf->page_len; tail = buf->tail; head = buf->head; BUG_ON (len > head->iov_len); /* Shift the tail first */ if (tail->iov_len != 0) { if (tail->iov_len > len) { copy = tail->iov_len - len; memmove((char *)tail->iov_base + len, tail->iov_base, copy); } /* Copy from the inlined pages into the tail */ copy = len; if (copy > pglen) copy = pglen; offs = len - copy; if (offs >= tail->iov_len) copy = 0; else if (copy > tail->iov_len - offs) copy = tail->iov_len - offs; if (copy != 0) _copy_from_pages((char *)tail->iov_base + offs, buf->pages, buf->page_base + pglen + offs - len, copy); /* Do we also need to copy data from the head into the tail ? */ if (len > pglen) { offs = copy = len - pglen; if (copy > tail->iov_len) copy = tail->iov_len; memcpy(tail->iov_base, (char *)head->iov_base + head->iov_len - offs, copy); } } /* Now handle pages */ if (pglen != 0) { if (pglen > len) _shift_data_right_pages(buf->pages, buf->page_base + len, buf->page_base, pglen - len); copy = len; if (len > pglen) copy = pglen; _copy_to_pages(buf->pages, buf->page_base, (char *)head->iov_base + head->iov_len - len, copy); } head->iov_len -= len; buf->buflen -= len; /* Have we truncated the message? */ if (buf->len > buf->buflen) buf->len = buf->buflen;}/* * xdr_shrink_pagelen * @buf: xdr_buf * @len: bytes to remove from buf->pages * * Shrinks XDR buffer's page array buf->pages by * 'len' bytes. The extra data is not lost, but is instead * moved into the tail. */voidxdr_shrink_pagelen(struct xdr_buf *buf, size_t len){ struct kvec *tail; size_t copy; char *p; unsigned int pglen = buf->page_len; tail = buf->tail; BUG_ON (len > pglen); /* Shift the tail first */ if (tail->iov_len != 0) { p = (char *)tail->iov_base + len; if (tail->iov_len > len) { copy = tail->iov_len - len; memmove(p, tail->iov_base, copy); } else buf->buflen -= len; /* Copy from the inlined pages into the tail */ copy = len; if (copy > tail->iov_len) copy = tail->iov_len; _copy_from_pages((char *)tail->iov_base, buf->pages, buf->page_base + pglen - len, copy); } buf->page_len -= len; buf->buflen -= len; /* Have we truncated the message? */ if (buf->len > buf->buflen) buf->len = buf->buflen;}voidxdr_shift_buf(struct xdr_buf *buf, size_t len){ xdr_shrink_bufhead(buf, len);}/** * xdr_init_encode - Initialize a struct xdr_stream for sending data. * @xdr: pointer to xdr_stream struct * @buf: pointer to XDR buffer in which to encode data * @p: current pointer inside XDR buffer * * Note: at the moment the RPC client only passes the length of our * scratch buffer in the xdr_buf's header kvec. Previously this * meant we needed to call xdr_adjust_iovec() after encoding the * data. With the new scheme, the xdr_stream manages the details * of the buffer length, and takes care of adjusting the kvec * length for us. */void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p){ struct kvec *iov = buf->head; xdr->buf = buf; xdr->iov = iov; xdr->end = (uint32_t *)((char *)iov->iov_base + iov->iov_len); buf->len = iov->iov_len = (char *)p - (char *)iov->iov_base; xdr->p = p;}EXPORT_SYMBOL(xdr_init_encode);/** * xdr_reserve_space - Reserve buffer space for sending * @xdr: pointer to xdr_stream * @nbytes: number of bytes to reserve * * Checks that we have enough buffer space to encode 'nbytes' more * bytes of data. If so, update the total xdr_buf length, and * adjust the length of the current kvec. */uint32_t * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes){ uint32_t *p = xdr->p; uint32_t *q; /* align nbytes on the next 32-bit boundary */ nbytes += 3; nbytes &= ~3; q = p + (nbytes >> 2); if (unlikely(q > xdr->end || q < p)) return NULL; xdr->p = q; xdr->iov->iov_len += nbytes; xdr->buf->len += nbytes; return p;}EXPORT_SYMBOL(xdr_reserve_space);/** * xdr_write_pages - Insert a list of pages into an XDR buffer for sending * @xdr: pointer to xdr_stream * @pages: list of pages * @base: offset of first byte * @len: length of data in bytes * */void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int base, unsigned int len){ struct xdr_buf *buf = xdr->buf; struct kvec *iov = buf->tail; buf->pages = pages; buf->page_base = base; buf->page_len = len; iov->iov_base = (char *)xdr->p; iov->iov_len = 0; xdr->iov = iov; if (len & 3) { unsigned int pad = 4 - (len & 3); BUG_ON(xdr->p >= xdr->end); iov->iov_base = (char *)xdr->p + (len & 3); iov->iov_len += pad; len += pad; *xdr->p++ = 0; } buf->buflen += len; buf->len += len;}EXPORT_SYMBOL(xdr_write_pages);/** * xdr_init_decode - Initialize an xdr_stream for decoding data. * @xdr: pointer to xdr_stream struct * @buf: pointer to XDR buffer from which to decode data * @p: current pointer inside XDR buffer */void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p){ struct kvec *iov = buf->head; unsigned int len = iov->iov_len; if (len > buf->len) len = buf->len; xdr->buf = buf; xdr->iov = iov; xdr->p = p; xdr->end = (uint32_t *)((char *)iov->iov_base + len);}EXPORT_SYMBOL(xdr_init_decode);/** * xdr_inline_decode - Retrieve non-page XDR data to decode * @xdr: pointer to xdr_stream struct * @nbytes: number of bytes of data to decode * * Check if the input buffer is long enough to enable us to decode * 'nbytes' more bytes of data starting at the current position. * If so return the current pointer, then update the current * pointer position. */uint32_t * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes){ uint32_t *p = xdr->p; uint32_t *q = p + XDR_QUADLEN(nbytes); if (unlikely(q > xdr->end || q < p)) return NULL; xdr->p = q; return p;}EXPORT_SYMBOL(xdr_inline_decode);/** * xdr_read_pages - Ensure page-based XDR data to decode is aligned at current pointer position * @xdr: pointer to xdr_stream struct * @len: number of bytes of page data * * Moves data beyond the current pointer position from the XDR head[] buffer * into the page list. Any data that lies beyond current position + "len" * bytes is moved into the XDR tail[]. The current pointer is then * repositioned at the beginning of the XDR tail. */void xdr_read_pages(struct xdr_stream *xdr, unsigned int len){ struct xdr_buf *buf = xdr->buf; struct kvec *iov; ssize_t shift; unsigned int end; int padding; /* Realign pages to current pointer position */ iov = buf->head; shift = iov->iov_len + (char *)iov->iov_base - (char *)xdr->p; if (shift > 0) xdr_shrink_bufhead(buf, shift); /* Truncate page data and move it into the tail */ if (buf->page_len > len) xdr_shrink_pagelen(buf, buf->page_len - len); padding = (XDR_QUADLEN(len) << 2) - len; xdr->iov = iov = buf->tail; /* Compute remaining message length. */ end = iov->iov_len; shift = buf->buflen - buf->len; if (shift < end) end -= shift; else if (shift > 0) end = 0; /* * Position current pointer at beginning of tail, and * set remaining message length. */ xdr->p = (uint32_t *)((char *)iov->iov_base + padding); xdr->end = (uint32_t *)((char *)iov->iov_base + end);}EXPORT_SYMBOL(xdr_read_pages);static struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0};voidxdr_buf_from_iov(struct kvec *iov, struct xdr_buf *buf){ buf->head[0] = *iov; buf->tail[0] = empty_iov; buf->page_len = 0; buf->buflen = buf->len = iov->iov_len;}/* Sets subiov to the intersection of iov with the buffer of length len * starting base bytes after iov. Indicates empty intersection by setting * length of subiov to zero. Decrements len by length of subiov, sets base * to zero (or decrements it by length of iov if subiov is empty). */static voidiov_subsegment(struct kvec *iov, struct kvec *subiov, int *base, int *len){ if (*base > iov->iov_len) { subiov->iov_base = NULL; subiov->iov_len = 0; *base -= iov->iov_len; } else { subiov->iov_base = iov->iov_base + *base; subiov->iov_len = min(*len, (int)iov->iov_len - *base); *base = 0; } *len -= subiov->iov_len; }/* Sets subbuf to the portion of buf of length len beginning base bytes * from the start of buf. Returns -1 if base of length are out of bounds. */intxdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf, int base, int len){ int i; subbuf->buflen = subbuf->len = len; iov_subsegment(buf->head, subbuf->head, &base, &len); if (base < buf->page_len) { i = (base + buf->page_base) >> PAGE_CACHE_SHIFT; subbuf->pages = &buf->pages[i]; subbuf->page_base = (base + buf->page_base) & ~PAGE_CACHE_MASK; subbuf->page_len = min((int)buf->page_len - base, len); len -= subbuf->page_len; base = 0; } else { base -= buf->page_len; subbuf->page_len = 0; } iov_subsegment(buf->tail, subbuf->tail, &base, &len); if (base || len) return -1; return 0;}/* obj is assumed to point to allocated memory of size at least len: */intread_bytes_from_xdr_buf(struct xdr_buf *buf, int base, void *obj, int len){ struct xdr_buf subbuf; int this_len; int status; status = xdr_buf_subsegment(buf, &subbuf, base, len); if (status) goto out; this_len = min(len, (int)subbuf.head[0].iov_len); memcpy(obj, subbuf.head[0].iov_base, this_len); len -= this_len; obj += this_len; this_len = min(len, (int)subbuf.page_len); if (this_len) _copy_from_pages(obj, subbuf.pages, subbuf.page_base, this_len); len -= this_len; obj += this_len; this_len = min(len, (int)subbuf.tail[0].iov_len); memcpy(obj, subbuf.tail[0].iov_base, this_len);out: return status;}static intread_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj){ u32 raw; int status; status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj)); if (status) return status; *obj = ntohl(raw); return 0;}/* If the netobj starting offset bytes from the start of xdr_buf is contained * entirely in the head or the tail, set object to point to it; otherwise * try to find space for it at the end of the tail, copy it there, and * set obj to point to it. */intxdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, int offset){ u32 tail_offset = buf->head[0].iov_len + buf->page_len; u32 obj_end_offset; if (read_u32_from_xdr_buf(buf, offset, &obj->len)) goto out; obj_end_offset = offset + 4 + obj->len; if (obj_end_offset <= buf->head[0].iov_len) { /* The obj is contained entirely in the head: */ obj->data = buf->head[0].iov_base + offset + 4; } else if (offset + 4 >= tail_offset) { if (obj_end_offset - tail_offset > buf->tail[0].iov_len) goto out; /* The obj is contained entirely in the tail: */ obj->data = buf->tail[0].iov_base + offset - tail_offset + 4; } else { /* use end of tail as storage for obj: * (We don't copy to the beginning because then we'd have * to worry about doing a potentially overlapping copy. * This assumes the object is at most half the length of the * tail.) */ if (obj->len > buf->tail[0].iov_len) goto out; obj->data = buf->tail[0].iov_base + buf->tail[0].iov_len - obj->len; if (read_bytes_from_xdr_buf(buf, offset + 4, obj->data, obj->len)) goto out; } return 0;out: return -1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -