Merge tag 'nfs-for-5.11-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
[linux-2.6-microblaze.git] / net / sunrpc / xdr.c
index 01918e6..3964ff7 100644 (file)
@@ -123,8 +123,7 @@ EXPORT_SYMBOL_GPL(xdr_decode_string_inplace);
  * @len: length of string, in bytes
  *
  */
-void
-xdr_terminate_string(struct xdr_buf *buf, const u32 len)
+void xdr_terminate_string(const struct xdr_buf *buf, const u32 len)
 {
        char *kaddr;
 
@@ -134,8 +133,7 @@ xdr_terminate_string(struct xdr_buf *buf, const u32 len)
 }
 EXPORT_SYMBOL_GPL(xdr_terminate_string);
 
-size_t
-xdr_buf_pagecount(struct xdr_buf *buf)
+size_t xdr_buf_pagecount(const struct xdr_buf *buf)
 {
        if (!buf->page_len)
                return 0;
@@ -480,16 +478,47 @@ static void xdr_buf_pages_zero(const struct xdr_buf *buf, unsigned int pgbase,
        } while ((len -= zero) != 0);
 }
 
+static unsigned int xdr_buf_pages_fill_sparse(const struct xdr_buf *buf,
+                                             unsigned int buflen, gfp_t gfp)
+{
+       unsigned int i, npages, pagelen;
+
+       if (!(buf->flags & XDRBUF_SPARSE_PAGES))
+               return buflen;
+       if (buflen <= buf->head->iov_len)
+               return buflen;
+       pagelen = buflen - buf->head->iov_len;
+       if (pagelen > buf->page_len)
+               pagelen = buf->page_len;
+       npages = (pagelen + buf->page_base + PAGE_SIZE - 1) >> PAGE_SHIFT;
+       for (i = 0; i < npages; i++) {
+               if (!buf->pages[i])
+                       continue;
+               buf->pages[i] = alloc_page(gfp);
+               if (likely(buf->pages[i]))
+                       continue;
+               buflen -= pagelen;
+               pagelen = i << PAGE_SHIFT;
+               if (pagelen > buf->page_base)
+                       buflen += pagelen - buf->page_base;
+               break;
+       }
+       return buflen;
+}
+
 static void xdr_buf_try_expand(struct xdr_buf *buf, unsigned int len)
 {
        struct kvec *head = buf->head;
        struct kvec *tail = buf->tail;
        unsigned int sum = head->iov_len + buf->page_len + tail->iov_len;
-       unsigned int free_space;
+       unsigned int free_space, newlen;
 
        if (sum > buf->len) {
                free_space = min_t(unsigned int, sum - buf->len, len);
-               buf->len += free_space;
+               newlen = xdr_buf_pages_fill_sparse(buf, buf->len + free_space,
+                                                  GFP_KERNEL);
+               free_space = newlen - buf->len;
+               buf->len = newlen;
                len -= free_space;
                if (!len)
                        return;
@@ -868,7 +897,7 @@ void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p,
        struct kvec *iov = buf->head;
        int scratch_len = buf->buflen - buf->page_len - buf->tail[0].iov_len;
 
-       xdr_set_scratch_buffer(xdr, NULL, 0);
+       xdr_reset_scratch_buffer(xdr);
        BUG_ON(scratch_len < 0);
        xdr->buf = buf;
        xdr->iov = iov;
@@ -912,7 +941,7 @@ inline void xdr_commit_encode(struct xdr_stream *xdr)
        page = page_address(*xdr->page_ptr);
        memcpy(xdr->scratch.iov_base, page, shift);
        memmove(page, page + shift, (void *)xdr->p - page);
-       xdr->scratch.iov_len = 0;
+       xdr_reset_scratch_buffer(xdr);
 }
 EXPORT_SYMBOL_GPL(xdr_commit_encode);
 
@@ -942,8 +971,7 @@ static __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr,
         * the "scratch" iov to track any temporarily unused fragment of
         * space at the end of the previous buffer:
         */
-       xdr->scratch.iov_base = xdr->p;
-       xdr->scratch.iov_len = frag1bytes;
+       xdr_set_scratch_buffer(xdr, xdr->p, frag1bytes);
        p = page_address(*xdr->page_ptr);
        /*
         * Note this is where the next encode will start after we've
@@ -1183,6 +1211,15 @@ static unsigned int xdr_set_iov(struct xdr_stream *xdr, struct kvec *iov,
        return len - base;
 }
 
+static unsigned int xdr_set_tail_base(struct xdr_stream *xdr,
+                                     unsigned int base, unsigned int len)
+{
+       struct xdr_buf *buf = xdr->buf;
+
+       xdr_stream_set_pos(xdr, base + buf->page_len + buf->head->iov_len);
+       return xdr_set_iov(xdr, buf->tail, base, len);
+}
+
 static unsigned int xdr_set_page_base(struct xdr_stream *xdr,
                                      unsigned int base, unsigned int len)
 {
@@ -1201,6 +1238,7 @@ static unsigned int xdr_set_page_base(struct xdr_stream *xdr,
        if (len > maxlen)
                len = maxlen;
 
+       xdr_stream_page_set_pos(xdr, base);
        base += xdr->buf->page_base;
 
        pgnr = base >> PAGE_SHIFT;
@@ -1223,7 +1261,7 @@ static void xdr_set_page(struct xdr_stream *xdr, unsigned int base,
 {
        if (xdr_set_page_base(xdr, base, len) == 0) {
                base -= xdr->buf->page_len;
-               xdr_set_iov(xdr, xdr->buf->tail, base, len);
+               xdr_set_tail_base(xdr, base, len);
        }
 }
 
@@ -1236,7 +1274,7 @@ static void xdr_set_next_page(struct xdr_stream *xdr)
        if (newbase < xdr->buf->page_len)
                xdr_set_page_base(xdr, newbase, xdr_stream_remaining(xdr));
        else
-               xdr_set_iov(xdr, xdr->buf->tail, 0, xdr_stream_remaining(xdr));
+               xdr_set_tail_base(xdr, 0, xdr_stream_remaining(xdr));
 }
 
 static bool xdr_set_next_buffer(struct xdr_stream *xdr)
@@ -1259,8 +1297,7 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p,
                     struct rpc_rqst *rqst)
 {
        xdr->buf = buf;
-       xdr->scratch.iov_base = NULL;
-       xdr->scratch.iov_len = 0;
+       xdr_reset_scratch_buffer(xdr);
        xdr->nwords = XDR_QUADLEN(buf->len);
        if (xdr_set_iov(xdr, buf->head, 0, buf->len) == 0 &&
            xdr_set_page_base(xdr, 0, buf->len) == 0)
@@ -1305,24 +1342,6 @@ static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
        return p;
 }
 
-/**
- * xdr_set_scratch_buffer - Attach a scratch buffer for decoding data.
- * @xdr: pointer to xdr_stream struct
- * @buf: pointer to an empty buffer
- * @buflen: size of 'buf'
- *
- * The scratch buffer is used when decoding from an array of pages.
- * If an xdr_inline_decode() call spans across page boundaries, then
- * we copy the data into the scratch buffer in order to allow linear
- * access.
- */
-void xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen)
-{
-       xdr->scratch.iov_base = buf;
-       xdr->scratch.iov_len = buflen;
-}
-EXPORT_SYMBOL_GPL(xdr_set_scratch_buffer);
-
 static __be32 *xdr_copy_to_scratch(struct xdr_stream *xdr, size_t nbytes)
 {
        __be32 *p;
@@ -1388,7 +1407,7 @@ static void xdr_realign_pages(struct xdr_stream *xdr)
        if (iov->iov_len > cur) {
                copied = xdr_shrink_bufhead(buf, cur);
                trace_rpc_xdr_alignment(xdr, cur, copied);
-               xdr->nwords = XDR_QUADLEN(buf->len - cur);
+               xdr_set_page(xdr, 0, buf->page_len);
        }
 }
 
@@ -1396,7 +1415,6 @@ static unsigned int xdr_align_pages(struct xdr_stream *xdr, unsigned int len)
 {
        struct xdr_buf *buf = xdr->buf;
        unsigned int nwords = XDR_QUADLEN(len);
-       unsigned int cur = xdr_stream_pos(xdr);
        unsigned int copied;
 
        if (xdr->nwords == 0)
@@ -1413,7 +1431,6 @@ static unsigned int xdr_align_pages(struct xdr_stream *xdr, unsigned int len)
                /* Truncate page data and move it into the tail */
                copied = xdr_shrink_pagelen(buf, len);
                trace_rpc_xdr_alignment(xdr, len, copied);
-               xdr->nwords = XDR_QUADLEN(buf->len - cur);
        }
        return len;
 }
@@ -1439,12 +1456,10 @@ unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
        if (pglen == 0)
                return 0;
 
-       xdr->nwords -= nwords;
        base = (nwords << 2) - pglen;
        end = xdr_stream_remaining(xdr) - pglen;
 
-       if (xdr_set_iov(xdr, xdr->buf->tail, base, end) == 0)
-               xdr->nwords = 0;
+       xdr_set_tail_base(xdr, base, end);
        return len <= pglen ? len : pglen;
 }
 EXPORT_SYMBOL_GPL(xdr_read_pages);
@@ -1478,15 +1493,13 @@ unsigned int xdr_align_data(struct xdr_stream *xdr, unsigned int offset,
        /* Move page data to the left */
        shift = from - offset;
        xdr_buf_pages_shift_left(buf, from, len, shift);
-       xdr->buf->len -= shift;
-       xdr->nwords -= XDR_QUADLEN(shift);
 
        bytes = xdr_stream_remaining(xdr);
        if (length > bytes)
                length = bytes;
        bytes -= length;
 
-       xdr->nwords -= XDR_QUADLEN(length);
+       xdr->buf->len -= shift;
        xdr_set_page(xdr, offset + length, bytes);
        return length;
 }
@@ -1508,12 +1521,11 @@ unsigned int xdr_expand_hole(struct xdr_stream *xdr, unsigned int offset,
                shift = to - from;
                xdr_buf_try_expand(buf, shift);
                xdr_buf_pages_shift_right(buf, from, buflen, shift);
-               xdr_stream_page_set_pos(xdr, to);
+               xdr_set_page(xdr, to, xdr_stream_remaining(xdr));
        } else if (to != from)
                xdr_align_data(xdr, to, 0);
        xdr_buf_pages_zero(buf, offset, length);
 
-       xdr_set_page(xdr, to, xdr_stream_remaining(xdr));
        return length;
 }
 EXPORT_SYMBOL_GPL(xdr_expand_hole);
@@ -1542,8 +1554,7 @@ EXPORT_SYMBOL_GPL(xdr_enter_page);
 
 static const struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0};
 
-void
-xdr_buf_from_iov(struct kvec *iov, struct xdr_buf *buf)
+void xdr_buf_from_iov(const struct kvec *iov, struct xdr_buf *buf)
 {
        buf->head[0] = *iov;
        buf->tail[0] = empty_iov;
@@ -1566,9 +1577,8 @@ EXPORT_SYMBOL_GPL(xdr_buf_from_iov);
  *
  * Returns -1 if base of length are out of bounds.
  */
-int
-xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf,
-                       unsigned int base, unsigned int len)
+int xdr_buf_subsegment(const struct xdr_buf *buf, struct xdr_buf *subbuf,
+                      unsigned int base, unsigned int len)
 {
        subbuf->buflen = subbuf->len = len;
        if (base < buf->head[0].iov_len) {
@@ -1615,6 +1625,51 @@ xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf,
 }
 EXPORT_SYMBOL_GPL(xdr_buf_subsegment);
 
+/**
+ * xdr_stream_subsegment - set @subbuf to a portion of @xdr
+ * @xdr: an xdr_stream set up for decoding
+ * @subbuf: the result buffer
+ * @nbytes: length of @xdr to extract, in bytes
+ *
+ * Sets up @subbuf to represent a portion of @xdr. The portion
+ * starts at the current offset in @xdr, and extends for a length
+ * of @nbytes. If this is successful, @xdr is advanced to the next
+ * position following that portion.
+ *
+ * Return values:
+ *   %true: @subbuf has been initialized, and @xdr has been advanced.
+ *   %false: a bounds error has occurred
+ */
+bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf,
+                          unsigned int nbytes)
+{
+       unsigned int remaining, offset, len;
+
+       if (xdr_buf_subsegment(xdr->buf, subbuf, xdr_stream_pos(xdr), nbytes))
+               return false;
+
+       if (subbuf->head[0].iov_len)
+               if (!__xdr_inline_decode(xdr, subbuf->head[0].iov_len))
+                       return false;
+
+       remaining = subbuf->page_len;
+       offset = subbuf->page_base;
+       while (remaining) {
+               len = min_t(unsigned int, remaining, PAGE_SIZE) - offset;
+
+               if (xdr->p == xdr->end && !xdr_set_next_buffer(xdr))
+                       return false;
+               if (!__xdr_inline_decode(xdr, len))
+                       return false;
+
+               remaining -= len;
+               offset = 0;
+       }
+
+       return true;
+}
+EXPORT_SYMBOL_GPL(xdr_stream_subsegment);
+
 /**
  * xdr_buf_trim - lop at most "len" bytes off the end of "buf"
  * @buf: buf to be trimmed
@@ -1656,7 +1711,8 @@ fix_len:
 }
 EXPORT_SYMBOL_GPL(xdr_buf_trim);
 
-static void __read_bytes_from_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len)
+static void __read_bytes_from_xdr_buf(const struct xdr_buf *subbuf,
+                                     void *obj, unsigned int len)
 {
        unsigned int this_len;
 
@@ -1673,7 +1729,8 @@ static void __read_bytes_from_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigne
 }
 
 /* obj is assumed to point to allocated memory of size at least len: */
-int read_bytes_from_xdr_buf(struct xdr_buf *buf, unsigned int base, void *obj, unsigned int len)
+int read_bytes_from_xdr_buf(const struct xdr_buf *buf, unsigned int base,
+                           void *obj, unsigned int len)
 {
        struct xdr_buf subbuf;
        int status;
@@ -1686,7 +1743,8 @@ int read_bytes_from_xdr_buf(struct xdr_buf *buf, unsigned int base, void *obj, u
 }
 EXPORT_SYMBOL_GPL(read_bytes_from_xdr_buf);
 
-static void __write_bytes_to_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len)
+static void __write_bytes_to_xdr_buf(const struct xdr_buf *subbuf,
+                                    void *obj, unsigned int len)
 {
        unsigned int this_len;
 
@@ -1703,7 +1761,8 @@ static void __write_bytes_to_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned
 }
 
 /* obj is assumed to point to allocated memory of size at least len: */
-int write_bytes_to_xdr_buf(struct xdr_buf *buf, unsigned int base, void *obj, unsigned int len)
+int write_bytes_to_xdr_buf(const struct xdr_buf *buf, unsigned int base,
+                          void *obj, unsigned int len)
 {
        struct xdr_buf subbuf;
        int status;
@@ -1716,8 +1775,7 @@ int write_bytes_to_xdr_buf(struct xdr_buf *buf, unsigned int base, void *obj, un
 }
 EXPORT_SYMBOL_GPL(write_bytes_to_xdr_buf);
 
-int
-xdr_decode_word(struct xdr_buf *buf, unsigned int base, u32 *obj)
+int xdr_decode_word(const struct xdr_buf *buf, unsigned int base, u32 *obj)
 {
        __be32  raw;
        int     status;
@@ -1730,8 +1788,7 @@ xdr_decode_word(struct xdr_buf *buf, unsigned int base, u32 *obj)
 }
 EXPORT_SYMBOL_GPL(xdr_decode_word);
 
-int
-xdr_encode_word(struct xdr_buf *buf, unsigned int base, u32 obj)
+int xdr_encode_word(const struct xdr_buf *buf, unsigned int base, u32 obj)
 {
        __be32  raw = cpu_to_be32(obj);
 
@@ -1740,9 +1797,8 @@ xdr_encode_word(struct xdr_buf *buf, unsigned int base, u32 obj)
 EXPORT_SYMBOL_GPL(xdr_encode_word);
 
 /* Returns 0 on success, or else a negative error code. */
-static int
-xdr_xcode_array2(struct xdr_buf *buf, unsigned int base,
-                struct xdr_array2_desc *desc, int encode)
+static int xdr_xcode_array2(const struct xdr_buf *buf, unsigned int base,
+                           struct xdr_array2_desc *desc, int encode)
 {
        char *elem = NULL, *c;
        unsigned int copied = 0, todo, avail_here;
@@ -1934,9 +1990,8 @@ out:
        return err;
 }
 
-int
-xdr_decode_array2(struct xdr_buf *buf, unsigned int base,
-                 struct xdr_array2_desc *desc)
+int xdr_decode_array2(const struct xdr_buf *buf, unsigned int base,
+                     struct xdr_array2_desc *desc)
 {
        if (base >= buf->len)
                return -EINVAL;
@@ -1945,9 +2000,8 @@ xdr_decode_array2(struct xdr_buf *buf, unsigned int base,
 }
 EXPORT_SYMBOL_GPL(xdr_decode_array2);
 
-int
-xdr_encode_array2(struct xdr_buf *buf, unsigned int base,
-                 struct xdr_array2_desc *desc)
+int xdr_encode_array2(const struct xdr_buf *buf, unsigned int base,
+                     struct xdr_array2_desc *desc)
 {
        if ((unsigned long) base + 4 + desc->array_len * desc->elem_size >
            buf->head->iov_len + buf->page_len + buf->tail->iov_len)
@@ -1957,9 +2011,9 @@ xdr_encode_array2(struct xdr_buf *buf, unsigned int base,
 }
 EXPORT_SYMBOL_GPL(xdr_encode_array2);
 
-int
-xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len,
-               int (*actor)(struct scatterlist *, void *), void *data)
+int xdr_process_buf(const struct xdr_buf *buf, unsigned int offset,
+                   unsigned int len,
+                   int (*actor)(struct scatterlist *, void *), void *data)
 {
        int i, ret = 0;
        unsigned int page_len, thislen, page_offset;