SUNRPC: xdr_read_pages should return the amount of XDR encoded page data
[deliverable/linux.git] / net / sunrpc / xdr.c
index fddcccfcdf768afd5fbad18d7c37380633bbd767..80d518644cf8273afd74426e0e816e2f16dc35b9 100644 (file)
@@ -554,13 +554,11 @@ void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int b
 EXPORT_SYMBOL_GPL(xdr_write_pages);
 
 static void xdr_set_iov(struct xdr_stream *xdr, struct kvec *iov,
-               __be32 *p, unsigned int len)
+               unsigned int len)
 {
        if (len > iov->iov_len)
                len = iov->iov_len;
-       if (p == NULL)
-               p = (__be32*)iov->iov_base;
-       xdr->p = p;
+       xdr->p = (__be32*)iov->iov_base;
        xdr->end = (__be32*)(iov->iov_base + len);
        xdr->iov = iov;
        xdr->page_ptr = NULL;
@@ -607,7 +605,7 @@ static void xdr_set_next_page(struct xdr_stream *xdr)
        newbase -= xdr->buf->page_base;
 
        if (xdr_set_page_base(xdr, newbase, PAGE_SIZE) < 0)
-               xdr_set_iov(xdr, xdr->buf->tail, NULL, xdr->buf->len);
+               xdr_set_iov(xdr, xdr->buf->tail, xdr->buf->len);
 }
 
 static bool xdr_set_next_buffer(struct xdr_stream *xdr)
@@ -616,7 +614,7 @@ static bool xdr_set_next_buffer(struct xdr_stream *xdr)
                xdr_set_next_page(xdr);
        else if (xdr->iov == xdr->buf->head) {
                if (xdr_set_page_base(xdr, 0, PAGE_SIZE) < 0)
-                       xdr_set_iov(xdr, xdr->buf->tail, NULL, xdr->buf->len);
+                       xdr_set_iov(xdr, xdr->buf->tail, xdr->buf->len);
        }
        return xdr->p != xdr->end;
 }
@@ -632,10 +630,15 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p)
        xdr->buf = buf;
        xdr->scratch.iov_base = NULL;
        xdr->scratch.iov_len = 0;
+       xdr->nwords = XDR_QUADLEN(buf->len);
        if (buf->head[0].iov_len != 0)
-               xdr_set_iov(xdr, buf->head, p, buf->len);
+               xdr_set_iov(xdr, buf->head, buf->len);
        else if (buf->page_len != 0)
                xdr_set_page_base(xdr, 0, buf->len);
+       if (p != NULL && p > xdr->p && xdr->end >= p) {
+               xdr->nwords -= p - xdr->p;
+               xdr->p = p;
+       }
 }
 EXPORT_SYMBOL_GPL(xdr_init_decode);
 
@@ -660,12 +663,14 @@ EXPORT_SYMBOL_GPL(xdr_init_decode_pages);
 
 static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
 {
+       unsigned int nwords = XDR_QUADLEN(nbytes);
        __be32 *p = xdr->p;
-       __be32 *q = p + XDR_QUADLEN(nbytes);
+       __be32 *q = p + nwords;
 
-       if (unlikely(q > xdr->end || q < p))
+       if (unlikely(nwords > xdr->nwords || q > xdr->end || q < p))
                return NULL;
        xdr->p = q;
+       xdr->nwords -= nwords;
        return p;
 }
 
@@ -740,15 +745,24 @@ EXPORT_SYMBOL_GPL(xdr_inline_decode);
  * 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[].
+ *
+ * Returns the number of XDR encoded bytes now contained in the pages
  */
-void xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
+unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
 {
        struct xdr_buf *buf = xdr->buf;
        struct kvec *iov;
        ssize_t shift;
+       unsigned int nwords = XDR_QUADLEN(len);
        unsigned int end;
        int padding;
 
+       if (xdr->nwords == 0)
+               return 0;
+       if (nwords > xdr->nwords) {
+               nwords = xdr->nwords;
+               len = nwords << 2;
+       }
        /* Realign pages to current pointer position */
        iov  = buf->head;
        shift = iov->iov_len + (char *)iov->iov_base - (char *)xdr->p;
@@ -758,21 +772,24 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
        /* 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;
+       padding = (nwords << 2) - len;
        xdr->iov = iov = buf->tail;
        /* Compute remaining message length.  */
        end = iov->iov_len;
        shift = buf->buflen - buf->len;
-       if (shift < end)
+       if (end > shift + padding)
                end -= shift;
-       else if (shift > 0)
-               end = 0;
+       else
+               end = padding;
        /*
         * Position current pointer at beginning of tail, and
         * set remaining message length.
         */
        xdr->p = (__be32 *)((char *)iov->iov_base + padding);
        xdr->end = (__be32 *)((char *)iov->iov_base + end);
+       xdr->page_ptr = NULL;
+       xdr->nwords = XDR_QUADLEN(end - padding);
+       return len;
 }
 EXPORT_SYMBOL_GPL(xdr_read_pages);
 
@@ -788,12 +805,13 @@ EXPORT_SYMBOL_GPL(xdr_read_pages);
  */
 void xdr_enter_page(struct xdr_stream *xdr, unsigned int len)
 {
-       xdr_read_pages(xdr, len);
+       len = xdr_read_pages(xdr, len);
        /*
         * Position current pointer at beginning of tail, and
         * set remaining message length.
         */
        xdr_set_page_base(xdr, 0, len);
+       xdr->nwords += XDR_QUADLEN(xdr->buf->page_len);
 }
 EXPORT_SYMBOL_GPL(xdr_enter_page);
 
This page took 0.025684 seconds and 5 git commands to generate.