diff -urNp nfs-ref/include/asm-i386/highmem.h nfs/include/asm-i386/highmem.h --- nfs-ref/include/asm-i386/highmem.h 2003-02-14 07:01:58.000000000 +0100 +++ nfs/include/asm-i386/highmem.h 2003-02-20 21:42:17.000000000 +0100 @@ -56,16 +56,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()) out_of_line_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 -urNp nfs-ref/include/linux/sunrpc/xdr.h nfs/include/linux/sunrpc/xdr.h --- nfs-ref/include/linux/sunrpc/xdr.h 2003-02-19 01:12:41.000000000 +0100 +++ nfs/include/linux/sunrpc/xdr.h 2003-02-20 21:39:51.000000000 +0100 @@ -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 *, size_t); /* diff -urNp nfs-ref/mm/highmem.c nfs/mm/highmem.c --- nfs-ref/mm/highmem.c 2002-11-29 02:23:18.000000000 +0100 +++ nfs/mm/highmem.c 2003-02-20 21:45:27.000000000 +0100 @@ -77,7 +77,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; @@ -96,6 +96,9 @@ start: if (--count) continue; + if (nonblocking) + return 0; + /* * Sleep for somebody else to unmap their entries */ @@ -126,7 +129,7 @@ start: return vaddr; } -void *kmap_high(struct page *page) +void *kmap_high(struct page *page, int nonblocking) { unsigned long vaddr; @@ -138,11 +141,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 -urNp nfs-ref/net/sunrpc/xdr.c nfs/net/sunrpc/xdr.c --- nfs-ref/net/sunrpc/xdr.c 2002-11-29 02:23:23.000000000 +0100 +++ nfs/net/sunrpc/xdr.c 2003-02-20 21:39:51.000000000 +0100 @@ -180,7 +180,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) { @@ -203,9 +203,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; @@ -223,20 +231,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; @@ -250,7 +261,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) @@ -322,9 +337,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 -urNp nfs-ref/net/sunrpc/xprt.c nfs/net/sunrpc/xprt.c --- nfs-ref/net/sunrpc/xprt.c 2003-01-29 06:14:32.000000000 +0100 +++ nfs/net/sunrpc/xprt.c 2003-02-20 21:39:51.000000000 +0100 @@ -226,23 +226,34 @@ 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()); - clear_bit(SOCK_ASYNC_NOSPACE, &sock->flags); - result = sock_sendmsg(sock, &msg, slen); + 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; + + clear_bit(SOCK_ASYNC_NOSPACE, &sock->flags); + 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); - xdr_kunmap(xdr, skip); - dprintk("RPC: xprt_sendmsg(%d) = %d\n", slen, result); if (result >= 0)