diff -u --recursive --new-file linux-2.2.21-pre4/fs/nfs/dir.c linux-2.2.21-NFS/fs/nfs/dir.c --- linux-2.2.21-pre4/fs/nfs/dir.c Fri Nov 2 17:39:08 2001 +++ linux-2.2.21-NFS/fs/nfs/dir.c Mon Mar 11 18:12:02 2002 @@ -623,8 +623,10 @@ */ static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) { - if (dentry->d_flags & DCACHE_NFSFS_RENAMED) + if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { + inode->i_nlink--; nfs_complete_unlink(dentry); + } iput(inode); } diff -u --recursive --new-file linux-2.2.21-pre4/fs/nfs/inode.c linux-2.2.21-NFS/fs/nfs/inode.c --- linux-2.2.21-pre4/fs/nfs/inode.c Fri Nov 2 17:39:08 2001 +++ linux-2.2.21-NFS/fs/nfs/inode.c Sun Mar 17 23:30:20 2002 @@ -117,16 +117,10 @@ inode->i_op = NULL; /* We can't support UPDATE_ATIME(), since the server will reset it */ inode->i_flags |= MS_NOATIME; - NFS_FILEID(inode) = 0; - NFS_FSID(inode) = 0; INIT_LIST_HEAD(&inode->u.nfs_i.read); INIT_LIST_HEAD(&inode->u.nfs_i.dirty); INIT_LIST_HEAD(&inode->u.nfs_i.commit); INIT_LIST_HEAD(&inode->u.nfs_i.writeback); - inode->u.nfs_i.nread = 0; - inode->u.nfs_i.ndirty = 0; - inode->u.nfs_i.ncommit = 0; - inode->u.nfs_i.npages = 0; NFS_CACHEINV(inode); NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); NFS_ATTRTIMEO_UPDATE(inode) = jiffies; @@ -635,19 +629,6 @@ init_fifo(inode); else inode->i_op = NULL; - /* - * Preset the size and mtime, as there's no need - * to invalidate the caches. - */ - inode->i_size = nfs_size_to_off_t(fattr->size); - inode->i_mtime = nfs_time_to_secs(fattr->mtime); - inode->i_atime = nfs_time_to_secs(fattr->atime); - inode->i_ctime = nfs_time_to_secs(fattr->ctime); - NFS_CACHE_CTIME(inode) = fattr->ctime; - NFS_CACHE_MTIME(inode) = fattr->mtime; - NFS_CACHE_ISIZE(inode) = fattr->size; - NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); - NFS_ATTRTIMEO_UPDATE(inode) = jiffies; memcpy(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh)); } nfs_refresh_inode(inode, fattr); @@ -677,6 +658,8 @@ return 0; if (memcmp(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh)) != 0) return 0; + if (!inode->i_count) + NFS_CACHEINV(inode); return 1; } @@ -691,7 +674,7 @@ if ((fattr->mode & S_IFMT) != (inode->i_mode & S_IFMT)) return 1; - if (is_bad_inode(inode)) + if (is_bad_inode(inode) || NFS_STALE(inode)) return 1; /* Has the filehandle changed? If so is the old one stale? */ @@ -793,6 +776,8 @@ fattr.pre_ctime = NFS_CACHE_CTIME(inode); fattr.valid |= NFS_ATTR_WCC; } + /* Force an attribute cache update */ + NFS_CACHEINV(inode); error = nfs_refresh_inode(inode, &fattr); out: return error; @@ -897,6 +882,34 @@ } /* + * nfs_fattr_obsolete - Test if attribute data is newer than cached data + * @inode: inode + * @fattr: attributes to test + * + * Avoid stuffing the attribute cache with obsolete information. + * We always accept updates if the attribute cache timed out, or if + * fattr->ctime is newer than our cached value. + * If fattr->ctime matches the cached value, we still accept the update + * if it increases the file size. + */ +static inline +int nfs_fattr_obsolete(struct inode *inode, struct nfs_fattr *fattr) +{ + s64 cdif; + + if (time_after(jiffies, NFS_READTIME(inode)+NFS_ATTRTIMEO(inode))) + goto out_valid; + if ((cdif = (s64)fattr->ctime - (s64)NFS_CACHE_CTIME(inode)) > 0) + goto out_valid; + /* Ugh... */ + if (cdif == 0 && fattr->size > NFS_CACHE_ISIZE(inode)) + goto out_valid; + return -1; +out_valid: + return 0; +} + +/* * Many nfs protocol calls return the new file attributes after * an operation. Here we update the inode to reflect the state * of the server's inode. @@ -914,6 +927,7 @@ { off_t new_size, new_isize; __u64 new_mtime; + time_t new_atime; int invalid = 0; int error = -EIO; @@ -962,6 +976,11 @@ new_size = fattr->size; new_isize = nfs_size_to_off_t(fattr->size); + new_atime = nfs_time_to_secs(fattr->atime); + /* Avoid races */ + if (nfs_fattr_obsolete(inode, fattr)) + goto out_nochange; + error = 0; /* @@ -1013,7 +1032,7 @@ NFS_CACHE_CTIME(inode) = fattr->ctime; inode->i_ctime = nfs_time_to_secs(fattr->ctime); - inode->i_atime = nfs_time_to_secs(fattr->atime); + inode->i_atime = new_atime; NFS_CACHE_MTIME(inode) = new_mtime; inode->i_mtime = nfs_time_to_secs(new_mtime); @@ -1052,6 +1071,10 @@ out: return error; +out_nochange: + if (new_atime - inode->i_atime > 0) + inode->i_atime = new_atime; + return 0; out_changed: /* diff -u --recursive --new-file linux-2.2.21-pre4/fs/nfs/nfs2xdr.c linux-2.2.21-NFS/fs/nfs/nfs2xdr.c --- linux-2.2.21-pre4/fs/nfs/nfs2xdr.c Sun Mar 25 18:30:58 2001 +++ linux-2.2.21-NFS/fs/nfs/nfs2xdr.c Mon Mar 11 18:01:21 2002 @@ -269,16 +269,18 @@ count = ntohl(*p++); hdrlen = (u8 *) p - (u8 *) iov->iov_base; - recvd = req->rq_rlen - hdrlen; - if (hdrlen > iov[0].iov_len) { - printk(KERN_WARNING "NFS: Odd RPC header size in read reply: %d\n", hdrlen); - return -errno_NFSERR_IO; - } - if (hdrlen != iov[0].iov_len) { + if (iov->iov_len > hdrlen) { dprintk("NFS: Short READ header. iovec will be shifted.\n"); xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen); } + recvd = req->rq_rlen - hdrlen; + if (count > recvd) { + printk(KERN_WARNING "NFS: server cheating in read reply: " + "count %d > recvd %d\n", count, recvd); + count = recvd; + } + dprintk("RPC: readres OK count %d\n", count); if (count < res->count) { xdr_zero_iovec(iov+1, req->rq_rnr-1, res->count - count); @@ -447,11 +449,7 @@ if ((status = ntohl(*p++))) return -nfs_stat_to_errno(status); hdrlen = (u8*)p - (u8*) iov->iov_base; - if (hdrlen > iov[0].iov_len) { - printk(KERN_WARNING "NFS: Odd RPC header size in READDIR reply: %d\n", hdrlen); - return -errno_NFSERR_IO; - } - if (hdrlen != iov[0].iov_len) { + if (iov->iov_len > hdrlen) { dprintk("NFS: Short READDIR header. iovec will be shifted.\n"); xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen); } @@ -460,12 +458,6 @@ p = (u32 *) iov[1].iov_base; end = (u32 *) ((u8 *) p + iov[1].iov_len); - /* Get start and end of dirent buffer */ - if (res->buffer != p) { - printk(KERN_ERR "NFS: Bad result buffer in readdir\n"); - return -errno_NFSERR_IO; - } - for (nr = 0; *p++; nr++) { entry = p - 1; if (p + 2 > end) @@ -602,14 +594,11 @@ if ((status = ntohl(*p++))) return -nfs_stat_to_errno(status); hdrlen = (u8*)p - (u8*) iov->iov_base; - if (hdrlen > iov[0].iov_len) { - printk(KERN_WARNING "NFS: Odd RPC header size in READLINK reply: %d\n", hdrlen); - return -errno_NFSERR_IO; - } - if (hdrlen != iov[0].iov_len) { + if (iov->iov_len > hdrlen) { dprintk("NFS: Short READLINK header. iovec will be shifted.\n"); xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen); } + strlen = (u32*)res->buffer; /* Convert length of symlink */ len = ntohl(*strlen); diff -u --recursive --new-file linux-2.2.21-pre4/fs/nfs/nfs3xdr.c linux-2.2.21-NFS/fs/nfs/nfs3xdr.c --- linux-2.2.21-pre4/fs/nfs/nfs3xdr.c Sun Mar 25 18:30:58 2001 +++ linux-2.2.21-NFS/fs/nfs/nfs3xdr.c Mon Mar 11 18:10:24 2002 @@ -579,11 +579,7 @@ } hdrlen = (u8 *) p - (u8 *) iov->iov_base; - if (hdrlen > iov[0].iov_len) { - printk(KERN_WARNING "NFS: Odd RPC header size in READDIR reply: %d\n", hdrlen); - return -errno_NFSERR_IO; - } - if (hdrlen != iov->iov_len) { + if (iov->iov_len > hdrlen) { dprintk("NFS: Short READDIR header. iovec will be shifted.\n"); xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen); } @@ -801,11 +797,7 @@ return -nfs_stat_to_errno(status); hdrlen = (u8 *) p - (u8 *) iov->iov_base; - if (hdrlen > iov[0].iov_len) { - printk(KERN_WARNING "NFS: Odd RPC header size in READLINK reply: %d\n", hdrlen); - return -errno_NFSERR_IO; - } - if (hdrlen != iov->iov_len) { + if (iov->iov_len > hdrlen) { dprintk("NFS: Short READLINK header. iovec will be shifted.\n"); xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen); } @@ -851,11 +843,7 @@ } hdrlen = (u8 *) p - (u8 *) iov->iov_base; - if (hdrlen > iov[0].iov_len) { - printk(KERN_WARNING "NFS: Odd RPC header size in read reply: %d\n", hdrlen); - return -errno_NFSERR_IO; - } - if (hdrlen != iov->iov_len) { + if (iov->iov_len > hdrlen) { dprintk("NFS: Short READ header. iovec will be shifted.\n"); xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen); } diff -u --recursive --new-file linux-2.2.21-pre4/fs/nfs/read.c linux-2.2.21-NFS/fs/nfs/read.c --- linux-2.2.21-pre4/fs/nfs/read.c Mon Mar 11 17:42:26 2002 +++ linux-2.2.21-NFS/fs/nfs/read.c Sun Mar 17 20:55:22 2002 @@ -150,32 +150,6 @@ return result; } -static inline struct nfs_page * -_nfs_find_read(struct inode *inode, struct page *page) -{ - struct list_head *head, *next; - - head = &inode->u.nfs_i.read; - next = head->next; - while (next != head) { - struct nfs_page *req = nfs_list_entry(next); - next = next->next; - if (page_index(req->wb_page) != page_index(page)) - continue; - req->wb_count++; - return req; - } - return NULL; -} - -static struct nfs_page * -nfs_find_read(struct inode *inode, struct page *page) -{ - struct nfs_page *req; - req = _nfs_find_read(inode, page); - return req; -} - /* * Add a request to the inode's asynchronous read list. */ @@ -194,46 +168,17 @@ static int nfs_readpage_async(struct file *file, struct inode *inode, struct page *page) { - struct nfs_page *req, *new = NULL; - int result; - - for (;;) { - result = 0; - if (PageUptodate(page)) - break; + struct nfs_page *new; - req = nfs_find_read(inode, page); - if (req) { - if (page != req->wb_page) { - nfs_release_request(req); - nfs_pagein_inode(inode, page_index(page), 0); - continue; - } - nfs_release_request(req); - break; - } - - if (new) { - nfs_lock_request(new); - new->wb_timeout = jiffies + NFS_READ_DELAY; - nfs_mark_request_read(new); - nfs_unlock_request(new); - new = NULL; - break; - } - - result = -ENOMEM; - new = nfs_create_request(file, inode, page, 0, PAGE_CACHE_SIZE); - if (!new) - break; - } + new = nfs_create_request(nfs_file_cred(file), inode, page, 0, PAGE_CACHE_SIZE); + if (!new) + return -ENOMEM; + nfs_mark_request_read(new); if (inode->u.nfs_i.nread >= NFS_SERVER(inode)->rpages || page_index(page) == (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT) nfs_pagein_inode(inode, 0, 0); - if (new) - nfs_release_request(new); - return result; + return 0; } /* @@ -281,6 +226,7 @@ nfs_list_remove_request(req); set_bit(PG_error, &page->flags); nfs_unlock_page(page); + nfs_clear_request(req); nfs_unlock_request(req); nfs_release_request(req); } @@ -432,7 +378,7 @@ nfs_list_remove_request(req); if (task->tk_status >= 0) { - char *p = page_address(page); + char *p = (char *)page_address(page); if (count < PAGE_CACHE_SIZE) { memset(p + count, 0, PAGE_CACHE_SIZE - count); count = 0; @@ -450,6 +396,7 @@ req->wb_bytes, (nfs_page_offset(page) + req->wb_offset)); nfs_unlock_request(req); + nfs_clear_request(req); nfs_release_request(req); } } diff -u --recursive --new-file linux-2.2.21-pre4/fs/nfs/write.c linux-2.2.21-NFS/fs/nfs/write.c --- linux-2.2.21-pre4/fs/nfs/write.c Sun Mar 25 18:37:38 2001 +++ linux-2.2.21-NFS/fs/nfs/write.c Mon Mar 18 00:25:15 2002 @@ -286,6 +286,7 @@ iput(inode); if (!nfs_have_writebacks(inode) && !nfs_have_read(inode)) inode_remove_flushd(inode); + nfs_clear_request(req); nfs_release_request(req); } @@ -330,8 +331,6 @@ printk(KERN_ERR "NFS: Add to list failed!\n"); return; } - if (!NFS_WBACK_BUSY(req)) - printk(KERN_ERR "NFS: unlocked request attempted added to list!\n"); prev = head->prev; while (prev != head) { struct nfs_page *p = nfs_list_entry(prev); @@ -405,7 +404,7 @@ * two different requests for the same page, and avoids possible deadlock * when we reach the hard limit on the number of dirty pages. */ -struct nfs_page *nfs_create_request(struct file *file, struct inode *inode, +struct nfs_page *nfs_create_request(struct rpc_cred *cred, struct inode *inode, struct page *page, unsigned int offset, unsigned int count) { @@ -459,12 +458,8 @@ atomic_inc(&page->count); req->wb_offset = offset; req->wb_bytes = count; - req->wb_file = file; - if (file) { - file->f_count++; - req->wb_cred = nfs_file_cred(file); - } else - req->wb_cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); + if (cred) + req->wb_cred = get_rpccred(cred); req->wb_inode = inode; req->wb_count = 1; @@ -474,6 +469,35 @@ return req; } +/** + * nfs_clear_request - Free up all resources allocated to the request + * @req: + * + * Release all resources associated with a write request after it + * has completed. + */ +void nfs_clear_request(struct nfs_page *req) +{ + struct inode *inode = req->wb_inode; + struct nfs_reqlist *cache = NFS_REQUESTLIST(inode); + + /* Release struct file or cached credential */ + if (req->wb_file) { + fput(req->wb_file); + req->wb_file = NULL; + } + if (req->wb_cred) { + rpcauth_releasecred(NFS_CLIENT(inode)->cl_auth, req->wb_cred); + req->wb_cred = NULL; + } + if (req->wb_page) { + page_cache_release(req->wb_page); + req->wb_page = NULL; + cache->nr_requests--; + nfs_nr_requests--; + wake_up(&cache->request_wait); + } +} /* * Release all resources associated with a write request after it @@ -482,10 +506,6 @@ void nfs_release_request(struct nfs_page *req) { - struct inode *inode = req->wb_inode; - struct nfs_reqlist *cache = NFS_REQUESTLIST(inode); - struct page *page = req->wb_page; - if (--req->wb_count) return; @@ -500,16 +520,8 @@ if (NFS_WBACK_BUSY(req)) printk(KERN_ERR "NFS: Request released while still locked!\n"); - if (req->wb_file) - fput(req->wb_file); - else - rpcauth_releasecred(NFS_CLIENT(inode)->cl_auth, req->wb_cred); - page_cache_release(page); + nfs_clear_request(req); nfs_page_free(req); - /* wake up anyone waiting to allocate a request */ - cache->nr_requests--; - nfs_nr_requests--; - wake_up(&cache->request_wait); } /* @@ -702,7 +714,7 @@ req = nfs_list_entry(src->next); if (prev) { - if (req->wb_file != prev->wb_file) + if (req->wb_cred != prev->wb_cred) break; if (page_index(req->wb_page) != page_index(prev->wb_page)+1) @@ -767,9 +779,13 @@ */ if (inode->u.nfs_i.npages >= MAX_REQUEST_SOFT) nfs_wb_file(inode, file); - new = nfs_create_request(file, inode, page, offset, bytes); + new = nfs_create_request(nfs_file_cred(file), inode, page, offset, bytes); if (!new) return ERR_PTR(-ENOMEM); + if (file) { + new->wb_file = file; + file->f_count++; + } /* If the region is locked, adjust the timeout */ if (region_locked(inode, new)) new->wb_timeout = jiffies + NFS_WRITEBACK_LOCKDELAY; @@ -861,6 +877,7 @@ int nfs_flush_incompatible(struct file *file, struct page *page) { + struct rpc_cred *cred = nfs_file_cred(file); struct inode *inode = file->f_dentry->d_inode; struct nfs_page *req; int status = 0; @@ -874,7 +891,7 @@ */ req = nfs_find_request(inode,page); if (req) { - if (req->wb_file != file || req->wb_page != page) + if (req->wb_file != file || req->wb_cred != cred || req->wb_page != page) status = nfs_wb_page(inode, page); nfs_release_request(req); } diff -u --recursive --new-file linux-2.2.21-pre4/include/linux/nfs_page.h linux-2.2.21-NFS/include/linux/nfs_page.h --- linux-2.2.21-pre4/include/linux/nfs_page.h Sun Mar 25 18:37:40 2001 +++ linux-2.2.21-NFS/include/linux/nfs_page.h Sun Mar 17 21:35:17 2002 @@ -40,11 +40,12 @@ #define NFS_WBACK_BUSY(req) ((req)->wb_flags & PG_BUSY) -extern struct nfs_page *nfs_create_request(struct file *file, +extern struct nfs_page *nfs_create_request(struct rpc_cred *cred, struct inode *inode, struct page *page, unsigned int offset, unsigned int count); +extern void nfs_clear_request(struct nfs_page *req); extern void nfs_release_request(struct nfs_page *req); diff -u --recursive --new-file linux-2.2.21-pre4/include/linux/sunrpc/auth.h linux-2.2.21-NFS/include/linux/sunrpc/auth.h --- linux-2.2.21-pre4/include/linux/sunrpc/auth.h Sun Mar 25 18:31:04 2001 +++ linux-2.2.21-NFS/include/linux/sunrpc/auth.h Sun Mar 17 21:35:11 2002 @@ -102,5 +102,12 @@ void rpcauth_insert_credcache(struct rpc_auth *, struct rpc_cred *); +static inline +struct rpc_cred * get_rpccred(struct rpc_cred *cred) +{ + cred->cr_count++; + return cred; +} + #endif /* __KERNEL__ */ #endif /* _LINUX_SUNRPC_AUTH_H */ diff -u --recursive --new-file linux-2.2.21-pre4/include/linux/sunrpc/debug.h linux-2.2.21-NFS/include/linux/sunrpc/debug.h --- linux-2.2.21-pre4/include/linux/sunrpc/debug.h Sun Mar 17 20:27:12 2002 +++ linux-2.2.21-NFS/include/linux/sunrpc/debug.h Sun Mar 17 21:30:28 2002 @@ -50,7 +50,7 @@ #undef ifdebug #ifdef RPC_DEBUG # define ifdebug(fac) if (rpc_debug & RPCDBG_##fac) -# define dfprintk(fac, args...) do { ifdebug(fac) printk(## args); } while(0) +# define dfprintk(fac, args...) do { ifdebug(fac) printk(args); } while(0) # define RPC_IFDEBUG(x) x #else # define dfprintk(fac, args...) do ; while (0)