patch-2.4.20 linux-2.4.20/net/sunrpc/xdr.c

Next file: linux-2.4.20/net/sunrpc/xprt.c
Previous file: linux-2.4.20/net/sunrpc/timer.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.19/net/sunrpc/xdr.c linux-2.4.20/net/sunrpc/xdr.c
@@ -10,6 +10,8 @@
 #include <linux/socket.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
+#include <linux/pagemap.h>
+#include <linux/errno.h>
 #include <linux/in.h>
 #include <linux/sunrpc/xdr.h>
 #include <linux/sunrpc/msg_prot.h>
@@ -100,6 +102,46 @@
 }
 
 
+void
+xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base,
+		 unsigned int len)
+{
+	xdr->pages = pages;
+	xdr->page_base = base;
+	xdr->page_len = len;
+
+	if (len & 3) {
+		struct iovec *iov = xdr->tail;
+		unsigned int pad = 4 - (len & 3);
+
+		iov->iov_base = (void *) "\0\0\0";
+		iov->iov_len  = pad;
+		len += pad;
+	}
+	xdr->len += len;
+}
+
+void
+xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
+		 struct page **pages, unsigned int base, unsigned int len)
+{
+	struct iovec *head = xdr->head;
+	struct iovec *tail = xdr->tail;
+	char *buf = (char *)head->iov_base;
+	unsigned int buflen = head->iov_len;
+
+	head->iov_len  = offset;
+
+	xdr->pages = pages;
+	xdr->page_base = base;
+	xdr->page_len = len;
+
+	tail->iov_base = buf + offset;
+	tail->iov_len = buflen - offset;
+
+	xdr->len += len;
+}
+
 /*
  * Realign the iovec if the server missed out some reply elements
  * (such as post-op attributes,...)
@@ -132,19 +174,157 @@
 }
 
 /*
- * Zero the last n bytes in an iovec array of 'nr' elements
+ * Map a struct xdr_buf into an iovec array.
  */
-void xdr_zero_iovec(struct iovec *iov, int nr, size_t n)
+int xdr_kmap(struct iovec *iov_base, struct xdr_buf *xdr, unsigned int base)
 {
-	struct iovec *pvec;
+	struct iovec	*iov = iov_base;
+	struct page	**ppage = xdr->pages;
+	unsigned int	len, pglen = xdr->page_len;
+
+	len = xdr->head[0].iov_len;
+	if (base < len) {
+		iov->iov_len = len - base;
+		iov->iov_base = (char *)xdr->head[0].iov_base + base;
+		iov++;
+		base = 0;
+	} else
+		base -= len;
+
+	if (pglen == 0)
+		goto map_tail;
+	if (base >= pglen) {
+		base -= pglen;
+		goto map_tail;
+	}
+	if (base || xdr->page_base) {
+		pglen -= base;
+		base  += xdr->page_base;
+		ppage += base >> PAGE_CACHE_SHIFT;
+		base &= ~PAGE_CACHE_MASK;
+	}
+	do {
+		len = PAGE_CACHE_SIZE;
+		iov->iov_base = kmap(*ppage);
+		if (base) {
+			iov->iov_base += base;
+			len -= base;
+			base = 0;
+		}
+		if (pglen < len)
+			len = pglen;
+		iov->iov_len = len;
+		iov++;
+		ppage++;
+	} while ((pglen -= len) != 0);
+map_tail:
+	if (xdr->tail[0].iov_len) {
+		iov->iov_len = xdr->tail[0].iov_len - base;
+		iov->iov_base = (char *)xdr->tail[0].iov_base + base;
+		iov++;
+	}
+	return (iov - iov_base);
+}
 
-	for (pvec = iov + nr - 1; n && nr > 0; nr--, pvec--) {
-		if (n < pvec->iov_len) {
-			memset((char *)pvec->iov_base + pvec->iov_len - n, 0, n);
-			n = 0;
+void xdr_kunmap(struct xdr_buf *xdr, unsigned int base)
+{
+	struct page	**ppage = xdr->pages;
+	unsigned int	pglen = xdr->page_len;
+
+	if (!pglen)
+		return;
+	if (base > xdr->head[0].iov_len)
+		base -= xdr->head[0].iov_len;
+	else
+		base = 0;
+
+	if (base >= pglen)
+		return;
+	if (base || xdr->page_base) {
+		pglen -= base;
+		base  += xdr->page_base;
+		ppage += base >> PAGE_CACHE_SHIFT;
+		/* Note: The offset means that the length of the first
+		 * page is really (PAGE_CACHE_SIZE - (base & ~PAGE_CACHE_MASK)).
+		 * In order to avoid an extra test inside the loop,
+		 * we bump pglen here, and just subtract PAGE_CACHE_SIZE... */
+		pglen += base & ~PAGE_CACHE_MASK;
+	}
+	for (;;) {
+		flush_dcache_page(*ppage);
+		kunmap(*ppage);
+		if (pglen <= PAGE_CACHE_SIZE)
+			break;
+		pglen -= PAGE_CACHE_SIZE;
+		ppage++;
+	}
+}
+
+void
+xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base,
+			  skb_reader_t *desc,
+			  skb_read_actor_t copy_actor)
+{
+	struct page	**ppage = xdr->pages;
+	unsigned int	len, pglen = xdr->page_len;
+	int		ret;
+
+	len = xdr->head[0].iov_len;
+	if (base < len) {
+		len -= base;
+		ret = copy_actor(desc, (char *)xdr->head[0].iov_base + base, len);
+		if (ret != len || !desc->count)
+			return;
+		base = 0;
+	} else
+		base -= len;
+
+	if (pglen == 0)
+		goto copy_tail;
+	if (base >= pglen) {
+		base -= pglen;
+		goto copy_tail;
+	}
+	if (base || xdr->page_base) {
+		pglen -= base;
+		base  += xdr->page_base;
+		ppage += base >> PAGE_CACHE_SHIFT;
+		base &= ~PAGE_CACHE_MASK;
+	}
+	do {
+		char *kaddr;
+
+		len = PAGE_CACHE_SIZE;
+		kaddr = kmap_atomic(*ppage, KM_SKB_SUNRPC_DATA);
+		if (base) {
+			len -= base;
+			if (pglen < len)
+				len = pglen;
+			ret = copy_actor(desc, kaddr + base, len);
+			base = 0;
 		} else {
-			memset(pvec->iov_base, 0, pvec->iov_len);
-			n -= pvec->iov_len;
+			if (pglen < len)
+				len = pglen;
+			ret = copy_actor(desc, kaddr, len);
 		}
-	}
+		kunmap_atomic(kaddr, KM_SKB_SUNRPC_DATA);
+		if (ret != len || !desc->count)
+			return;
+		ppage++;
+	} while ((pglen -= len) != 0);
+copy_tail:
+	len = xdr->tail[0].iov_len;
+	if (len)
+		copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len);
+}
+
+void
+xdr_shift_buf(struct xdr_buf *xdr, size_t len)
+{
+	struct iovec iov[MAX_IOVEC];
+	unsigned int nr;
+
+	nr = xdr_kmap(iov, xdr, 0);
+	xdr_shift_iovec(iov, nr, len);
+	xdr_kunmap(xdr, 0);
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)