diff -X /home/cel/src/linux/dont-diff -Naurp old/include/asm-i386/highmem.h new/include/asm-i386/highmem.h --- old/include/asm-i386/highmem.h Tue Oct 28 17:52:53 2003 +++ new/include/asm-i386/highmem.h Tue Oct 28 18:04:46 2003 @@ -49,16 +49,19 @@ extern void kmap_init(void) __init; #define PKMAP_NR(virt) ((virt-PKMAP_BASE) >> PAGE_SHIFT) #define PKMAP_ADDR(nr) (PKMAP_BASE + ((nr) << PAGE_SHIFT)) -extern void * FASTCALL(kmap_high(struct page *page)); +extern void * FASTCALL(kmap_high(struct page *page, int nonblocking)); extern void FASTCALL(kunmap_high(struct page *page)); -static inline void *kmap(struct page *page) +#define kmap(page) __kmap(page, 0) +#define kmap_nonblock(page) __kmap(page, 1) + +static inline void *__kmap(struct page *page, int nonblocking) { if (in_interrupt()) BUG(); if (page < highmem_start_page) return page_address(page); - return kmap_high(page); + return kmap_high(page, nonblocking); } static inline void kunmap(struct page *page) diff -X /home/cel/src/linux/dont-diff -Naurp old/include/linux/sunrpc/xdr.h new/include/linux/sunrpc/xdr.h --- old/include/linux/sunrpc/xdr.h Tue Oct 28 17:52:57 2003 +++ new/include/linux/sunrpc/xdr.h Tue Oct 28 18:06:10 2003 @@ -137,7 +137,7 @@ void xdr_zero_iovec(struct iovec *, int, * XDR buffer helper functions */ extern int xdr_kmap(struct iovec *, struct xdr_buf *, unsigned int); -extern void xdr_kunmap(struct xdr_buf *, unsigned int); +extern void xdr_kunmap(struct xdr_buf *, unsigned int, int); extern void xdr_shift_buf(struct xdr_buf *, unsigned int); extern void xdr_zero_buf(struct xdr_buf *, unsigned int); diff -X /home/cel/src/linux/dont-diff -Naurp old/mm/highmem.c new/mm/highmem.c --- old/mm/highmem.c Tue Oct 28 17:52:57 2003 +++ new/mm/highmem.c Tue Oct 28 18:09:41 2003 @@ -68,7 +68,7 @@ static void flush_all_zero_pkmaps(void) flush_tlb_all(); } -static inline unsigned long map_new_virtual(struct page *page) +static inline unsigned long map_new_virtual(struct page *page, int nonblocking) { unsigned long vaddr; int count; @@ -87,6 +87,9 @@ start: if (--count) continue; + if (nonblocking) + return 0; + /* * Sleep for somebody else to unmap their entries */ @@ -117,7 +120,7 @@ start: return vaddr; } -void *kmap_high(struct page *page) +void *kmap_high(struct page *page, int nonblocking) { unsigned long vaddr; @@ -129,11 +132,15 @@ void *kmap_high(struct page *page) */ spin_lock(&kmap_lock); vaddr = (unsigned long) page->virtual; - if (!vaddr) - vaddr = map_new_virtual(page); + if (!vaddr) { + vaddr = map_new_virtual(page, nonblocking); + if (!vaddr) + goto out; + } pkmap_count[PKMAP_NR(vaddr)]++; if (pkmap_count[PKMAP_NR(vaddr)] < 2) BUG(); + out: spin_unlock(&kmap_lock); return (void*) vaddr; } diff -X /home/cel/src/linux/dont-diff -Naurp old/net/sunrpc/xdr.c new/net/sunrpc/xdr.c --- old/net/sunrpc/xdr.c Tue Oct 28 17:52:57 2003 +++ new/net/sunrpc/xdr.c Tue Oct 28 18:17:24 2003 @@ -215,7 +215,7 @@ int xdr_kmap(struct iovec *iov_base, str { struct iovec *iov = iov_base; struct page **ppage = xdr->pages; - unsigned int len, pglen = xdr->page_len; + unsigned int len, pglen = xdr->page_len, first_kmap; len = xdr->head[0].iov_len; if (base < len) { @@ -238,9 +238,17 @@ int xdr_kmap(struct iovec *iov_base, str ppage += base >> PAGE_CACHE_SHIFT; base &= ~PAGE_CACHE_MASK; } + first_kmap = 1; do { len = PAGE_CACHE_SIZE; - iov->iov_base = kmap(*ppage); + if (first_kmap) { + first_kmap = 0; + iov->iov_base = kmap(*ppage); + } else { + iov->iov_base = kmap_nonblock(*ppage); + if (!iov->iov_base) + goto out; + } if (base) { iov->iov_base += base; len -= base; @@ -258,20 +266,23 @@ map_tail: iov->iov_base = (char *)xdr->tail[0].iov_base + base; iov++; } + out: return (iov - iov_base); } -void xdr_kunmap(struct xdr_buf *xdr, unsigned int base) +void xdr_kunmap(struct xdr_buf *xdr, unsigned int base, int niov) { struct page **ppage = xdr->pages; unsigned int pglen = xdr->page_len; if (!pglen) return; - if (base > xdr->head[0].iov_len) + if (base >= xdr->head[0].iov_len) base -= xdr->head[0].iov_len; - else + else { + niov--; base = 0; + } if (base >= pglen) return; @@ -285,7 +296,11 @@ void xdr_kunmap(struct xdr_buf *xdr, uns * we bump pglen here, and just subtract PAGE_CACHE_SIZE... */ pglen += base & ~PAGE_CACHE_MASK; } - for (;;) { + /* + * In case we could only do a partial xdr_kmap, all remaining iovecs + * refer to pages. Otherwise we detect the end through pglen. + */ + for (; niov; niov--) { flush_dcache_page(*ppage); kunmap(*ppage); if (pglen <= PAGE_CACHE_SIZE) @@ -357,9 +372,22 @@ void xdr_shift_buf(struct xdr_buf *xdr, size_t len) { struct iovec iov[MAX_IOVEC]; - unsigned int nr; + unsigned int nr, len_part, n, skip; + + skip = 0; + do { + + nr = xdr_kmap(iov, xdr, skip); + + len_part = 0; + for (n = 0; n < nr; n++) + len_part += iov[n].iov_len; + + xdr_shift_iovec(iov, nr, len_part); + + xdr_kunmap(xdr, skip, nr); - nr = xdr_kmap(iov, xdr, 0); - xdr_shift_iovec(iov, nr, len); - xdr_kunmap(xdr, 0); + skip += len_part; + len -= len_part; + } while (len); } diff -X /home/cel/src/linux/dont-diff -Naurp old/net/sunrpc/xprt.c new/net/sunrpc/xprt.c --- old/net/sunrpc/xprt.c Tue Oct 28 17:52:57 2003 +++ new/net/sunrpc/xprt.c Tue Oct 28 18:22:15 2003 @@ -169,21 +169,32 @@ xprt_sendmsg(struct rpc_xprt *xprt, stru /* Dont repeat bytes */ skip = req->rq_bytes_sent; slen = xdr->len - skip; - niov = xdr_kmap(niv, xdr, skip); + oldfs = get_fs(); set_fs(get_ds()); + do { + unsigned int slen_part, n; - msg.msg_flags = MSG_DONTWAIT|MSG_NOSIGNAL; - msg.msg_iov = niv; - msg.msg_iovlen = niov; - msg.msg_name = (struct sockaddr *) &xprt->addr; - msg.msg_namelen = sizeof(xprt->addr); - msg.msg_control = NULL; - msg.msg_controllen = 0; + niov = xdr_kmap(niv, xdr, skip); - oldfs = get_fs(); set_fs(get_ds()); - result = sock_sendmsg(sock, &msg, slen); - set_fs(oldfs); + msg.msg_flags = MSG_DONTWAIT|MSG_NOSIGNAL; + msg.msg_iov = niv; + msg.msg_iovlen = niov; + msg.msg_name = (struct sockaddr *) &xprt->addr; + msg.msg_namelen = sizeof(xprt->addr); + msg.msg_control = NULL; + msg.msg_controllen = 0; + + slen_part = 0; + for (n = 0; n < niov; n++) + slen_part += niv[n].iov_len; - xdr_kunmap(xdr, skip); + result = sock_sendmsg(sock, &msg, slen_part); + + xdr_kunmap(xdr, skip, niov); + + skip += slen_part; + slen -= slen_part; + } while (result >= 0 && slen); + set_fs(oldfs); dprintk("RPC: xprt_sendmsg(%d) = %d\n", slen, result);