diff options
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/dir.c | 129 | ||||
-rw-r--r-- | fs/nfs/file.c | 58 | ||||
-rw-r--r-- | fs/nfs/inode.c | 29 | ||||
-rw-r--r-- | fs/nfs/read.c | 15 | ||||
-rw-r--r-- | fs/nfs/symlink.c | 6 | ||||
-rw-r--r-- | fs/nfs/write.c | 531 |
6 files changed, 247 insertions, 521 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 7cc5ae160..b1dee7366 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -371,6 +371,15 @@ nfs_free_dircache(void) nfs_invalidate_dircache_sb(NULL); } +/* + * Whenever an NFS operation succeeds, we know that the dentry + * is valid, so we update the revalidation timestamp. + */ +static inline void nfs_renew_times(struct dentry * dentry) +{ + dentry->d_time = jiffies; +} + #define NFS_REVALIDATE_INTERVAL (5*HZ) /* * This is called every time the dcache has a lookup hit, @@ -416,23 +425,19 @@ parent->d_name.name, dentry->d_name.name); */ error = nfs_proc_lookup(NFS_DSERVER(parent), NFS_FH(parent), dentry->d_name.name, &fhandle, &fattr); - if (error) { -printk("nfs_lookup_revalidate: error=%d\n", error); + if (error) goto out_bad; - } /* Inode number matches? */ - if (fattr.fileid != inode->i_ino) { -printk("nfs_lookup_revalidate: %s/%s inode mismatch, old=%ld, new=%u\n", -parent->d_name.name, dentry->d_name.name, inode->i_ino, fattr.fileid); + if (fattr.fileid != inode->i_ino) goto out_bad; - } + /* Filehandle matches? */ - if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) { -printk("nfs_lookup_revalidate: %s/%s fh changed\n", -parent->d_name.name, dentry->d_name.name); + if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) goto out_bad; - } + + /* Ok, remeber that we successfully checked it.. */ + nfs_renew_times(dentry); out_valid: return 1; @@ -481,32 +486,6 @@ inode->i_ino, inode->i_count, inode->i_nlink); } /* - * Called to free the inode from the dentry. We must flush - * any pending writes for this dentry before freeing the inode. - */ -static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) -{ - dfprintk(VFS, "NFS: dentry_iput(%s/%s, cnt=%d, ino=%ld)\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - dentry->d_count, inode->i_ino); - - if (NFS_WRITEBACK(inode)) { -#ifdef NFS_PARANOIA -printk("nfs_dentry_iput: pending writes for %s/%s, i_count=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_count); -#endif - while (nfs_find_dentry_request(inode, dentry)) { -#ifdef NFS_PARANOIA -printk("nfs_dentry_iput: flushing %s/%s\n", -dentry->d_parent->d_name.name, dentry->d_name.name); -#endif - nfs_flush_dirty_pages(inode, 0, 0, 0); - } - } - iput(inode); -} - -/* * Called when the dentry is being freed to release private memory. */ static void nfs_dentry_release(struct dentry *dentry) @@ -521,7 +500,7 @@ struct dentry_operations nfs_dentry_operations = { NULL, /* d_compare */ nfs_dentry_delete, /* d_delete(struct dentry *) */ nfs_dentry_release, /* d_release(struct dentry *) */ - nfs_dentry_iput /* d_iput(struct dentry *, struct inode *) */ + NULL /* d_iput */ }; #ifdef NFS_PARANOIA @@ -547,15 +526,6 @@ static void show_dentry(struct list_head * dlist) } #endif -/* - * Whenever an NFS operation succeeds, we know that the dentry - * is valid, so we update the revalidation timestamp. - */ -static inline void nfs_renew_times(struct dentry * dentry) -{ - dentry->d_time = jiffies; -} - static int nfs_lookup(struct inode *dir, struct dentry * dentry) { struct inode *inode; @@ -751,7 +721,7 @@ out: */ static int nfs_rmdir(struct inode *dir, struct dentry *dentry) { - int error, rehash = 0; + int error; dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n", dir->i_dev, dir->i_ino, dentry->d_name.name); @@ -761,39 +731,24 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry) goto out; error = -EBUSY; - if (dentry->d_count > 1) { - /* Attempt to shrink child dentries ... */ - shrink_dcache_parent(dentry); - if (dentry->d_count > 1) - goto out; - } + if (!list_empty(&dentry->d_hash)) + goto out; + #ifdef NFS_PARANOIA if (dentry->d_inode->i_count > 1) printk("nfs_rmdir: %s/%s inode busy?? i_count=%d, i_nlink=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_inode->i_count, dentry->d_inode->i_nlink); #endif - /* - * Unhash the dentry while we remove the directory. - */ - if (!list_empty(&dentry->d_hash)) { - d_drop(dentry); - rehash = 1; - } + /* * Update i_nlink and free the inode before unlinking. */ if (dentry->d_inode->i_nlink) dentry->d_inode->i_nlink --; - d_delete(dentry); nfs_invalidate_dircache(dir); error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name); - if (!error) { - if (rehash) - d_add(dentry, NULL); - nfs_renew_times(dentry); - } out: return error; } @@ -945,18 +900,7 @@ static int nfs_safe_remove(struct dentry *dentry) /* N.B. not needed now that d_delete is done in advance? */ error = -EBUSY; - if (inode) { - if (NFS_WRITEBACK(inode)) { - nfs_flush_dirty_pages(inode, 0, 0, 0); - if (NFS_WRITEBACK(inode)) { -#ifdef NFS_PARANOIA -printk("nfs_safe_remove: %s/%s writes pending, d_count=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); -#endif - goto out; - } - } - } else { + if (!inode) { #ifdef NFS_PARANOIA printk("nfs_safe_remove: %s/%s already negative??\n", dentry->d_parent->d_name.name, dentry->d_name.name); @@ -1203,29 +1147,14 @@ new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count); if (new_dir == old_dir) goto do_rename; /* - * Cross-directory move ... check whether it's a file. - */ - if (S_ISREG(old_inode->i_mode)) { - if (NFS_WRITEBACK(old_inode)) { -#ifdef NFS_PARANOIA -printk("nfs_rename: %s/%s has pending writes\n", -old_dentry->d_parent->d_name.name, old_dentry->d_name.name); -#endif - nfs_flush_dirty_pages(old_inode, 0, 0, 0); - if (NFS_WRITEBACK(old_inode)) { -#ifdef NFS_PARANOIA -printk("nfs_rename: %s/%s has pending writes after flush\n", -old_dentry->d_parent->d_name.name, old_dentry->d_name.name); -#endif - goto out; - } - } - } - /* - * Moving a directory ... prune child dentries if needed. + * Cross-directory move ... + * + * ... prune child dentries and writebacks if needed. */ - else if (old_dentry->d_count > 1) + if (old_dentry->d_count > 1) { + nfs_wb_all(old_inode); shrink_dcache_parent(old_dentry); + } /* * Now check the use counts ... we can't safely do the diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 973ad52ec..25caa03d3 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -91,14 +91,13 @@ struct inode_operations nfs_file_inode_operations = { static int nfs_file_close(struct inode *inode, struct file *file) { - int status, error; + int status; dfprintk(VFS, "nfs: close(%x/%ld)\n", inode->i_dev, inode->i_ino); - status = nfs_flush_dirty_pages(inode, 0, 0, 0); - error = nfs_write_error(inode); + status = nfs_wb_all(inode); if (!status) - status = error; + status = nfs_write_error(inode); return status; } @@ -158,17 +157,17 @@ static int nfs_fsync(struct file *file, struct dentry *dentry) { struct inode *inode = dentry->d_inode; - int status, error; + int status; dfprintk(VFS, "nfs: fsync(%x/%ld)\n", inode->i_dev, inode->i_ino); - status = nfs_flush_dirty_pages(inode, current->pid, 0, 0); - error = nfs_write_error(inode); + status = nfs_wb_pid(inode, current->pid); if (!status) - status = error; + status = nfs_write_error(inode); return status; } + /* * Write to a file (through the page cache). */ @@ -179,14 +178,10 @@ nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) struct inode * inode = dentry->d_inode; ssize_t result; - dfprintk(VFS, "nfs: write(%s/%s (%d), %lu@%lu)\n", + dfprintk(VFS, "nfs: write(%s/%s(%ld), %lu@%lu)\n", dentry->d_parent->d_name.name, dentry->d_name.name, - inode->i_count, (unsigned long) count, (unsigned long) *ppos); + inode->i_ino, (unsigned long) count, (unsigned long) *ppos); - if (!inode) { - printk("nfs_file_write: inode = NULL\n"); - return -EINVAL; - } result = -EBUSY; if (IS_SWAPFILE(inode)) goto out_swapfile; @@ -194,14 +189,6 @@ nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) if (result) goto out; -#ifdef NFS_PARANOIA -/* N.B. This should be impossible now -- inodes can't change mode */ -if (!S_ISREG(inode->i_mode)) { - printk("nfs_file_write: write to non-file, mode %07o\n", - inode->i_mode); - return -EINVAL; -} -#endif result = count; if (!count) goto out; @@ -214,7 +201,7 @@ out: return result; out_swapfile: - printk(KERN_ERR "NFS: attempt to write to active swap file!\n"); + printk(KERN_INFO "NFS: attempt to write to active swap file!\n"); goto out; } @@ -253,22 +240,21 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl) if (!fl->fl_owner || (fl->fl_flags & (FL_POSIX|FL_BROKEN)) != FL_POSIX) return -ENOLCK; - /* If unlocking a file region, flush dirty pages (unless we've - * been killed by a signal, that is). */ - if (cmd == F_SETLK && fl->fl_type == F_UNLCK - && !signal_pending(current)) { - status = nfs_flush_dirty_pages(inode, current->pid, - fl->fl_start, fl->fl_end == NLM_OFFSET_MAX? 0 : - fl->fl_end - fl->fl_start + 1); - if (status < 0) - return status; - } + /* + * Flush all pending writes before doing anything + * with locks.. + */ + status = nfs_wb_all(inode); + if (status < 0) + return status; if ((status = nlmclnt_proc(inode, cmd, fl)) < 0) return status; - /* Here, we could turn off write-back of pages in the - * locked file region */ - + /* + * Make sure we re-validate anything we've got cached. + * This makes locking act as a cache coherency point. + */ + NFS_CACHEINV(inode); return 0; } diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 597821270..d11c20ab6 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -105,11 +105,11 @@ nfs_delete_inode(struct inode * inode) #ifdef NFS_DEBUG_VERBOSE printk("nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino); #endif - nfs_invalidate_pages(inode); - while (NFS_WRITEBACK(inode) != NULL && jiffies < timeout) { + nfs_inval(inode); + while (NFS_WRITEBACK(inode) != NULL && + time_before(jiffies, timeout)) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + HZ/10; - schedule(); + schedule_timeout(HZ/10); } current->state = TASK_RUNNING; if (NFS_WRITEBACK(inode) != NULL) @@ -322,18 +322,20 @@ out_no_fh: goto out_shutdown; out_no_iod: - printk("NFS: couldn't start rpciod!\n"); + printk(KERN_WARNING "NFS: couldn't start rpciod!\n"); out_shutdown: rpc_shutdown_client(server->client); - goto out_unlock; + goto out_free_host; out_no_client: - printk("NFS: cannot create RPC client.\n"); + printk(KERN_WARNING "NFS: cannot create RPC client.\n"); xprt_destroy(xprt); - goto out_unlock; + goto out_free_host; out_no_xprt: - printk("NFS: cannot create RPC transport.\n"); + printk(KERN_WARNING "NFS: cannot create RPC transport.\n"); + +out_free_host: kfree(server->hostname); out_unlock: unlock_super(sb); @@ -393,9 +395,10 @@ restart: tmp = head; while ((tmp = tmp->next) != head) { struct dentry *dentry = list_entry(tmp, struct dentry, d_alias); +printk("nfs_free_dentries: found %s/%s, d_count=%d, hashed=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, +dentry->d_count, !list_empty(&dentry->d_hash)); if (!dentry->d_count) { -printk("nfs_free_dentries: freeing %s/%s, i_count=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_count); dget(dentry); d_drop(dentry); dput(dentry); @@ -478,7 +481,7 @@ nfs_fhget(struct dentry *dentry, struct nfs_fh *fhandle, goto out; inode->i_sb = sb; inode->i_dev = sb->s_dev; - inode->i_flags = sb->s_flags; + inode->i_flags = 0; inode->i_ino = fattr->fileid; nfs_read_inode(inode); nfs_fill_inode(inode, fattr); @@ -824,7 +827,7 @@ printk("nfs_refresh_inode: invalidating %ld pages\n", inode->i_nrpages); if (!S_ISDIR(inode->i_mode)) { /* This sends off all dirty pages off to the server. * Note that this function must not sleep. */ - nfs_invalidate_pages(inode); + nfs_inval(inode); invalidate_inode_pages(inode); } else nfs_invalidate_dircache(inode); diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 6f1fdd7ff..3f9b16078 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -225,11 +225,24 @@ nfs_readpage(struct file *file, struct page *page) { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; - int error = -1; + int error; dprintk("NFS: nfs_readpage (%p %ld@%ld)\n", page, PAGE_SIZE, page->offset); set_bit(PG_locked, &page->flags); + + /* + * Try to flush any pending writes to the file.. + * + * NOTE! Because we own the page lock, there cannot + * be any new pending writes generated at this point + * for this page (other pages can be written to). + */ + error = nfs_wb_page(inode, page); + if (error) + return error; + + error = -1; atomic_inc(&page->count); if (!IS_SWAPFILE(inode) && !PageError(page) && NFS_SERVER(inode)->rsize >= PAGE_SIZE) diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index 3ae490c37..bf23ad8aa 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -19,7 +19,7 @@ #include <asm/uaccess.h> static int nfs_readlink(struct dentry *, char *, int); -static struct dentry *nfs_follow_link(struct dentry *, struct dentry *); +static struct dentry *nfs_follow_link(struct dentry *, struct dentry *, unsigned int); /* * symlinks can't do much... @@ -68,7 +68,7 @@ static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen) } static struct dentry * -nfs_follow_link(struct dentry * dentry, struct dentry *base) +nfs_follow_link(struct dentry * dentry, struct dentry *base, unsigned int follow) { int error; unsigned int len; @@ -94,7 +94,7 @@ nfs_follow_link(struct dentry * dentry, struct dentry *base) path[len] = 0; kfree(mem); - result = lookup_dentry(path, base, 1); + result = lookup_dentry(path, base, follow); kfree(path); out: return result; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index f8658f37f..33b4dd16a 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -46,7 +46,6 @@ * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de> */ -#include <linux/config.h> #include <linux/types.h> #include <linux/malloc.h> #include <linux/swap.h> @@ -59,7 +58,7 @@ #define NFS_PARANOIA 1 #define NFSDBG_FACILITY NFSDBG_PAGECACHE -static void nfs_wback_lock(struct rpc_task *task); +static void nfs_wback_begin(struct rpc_task *task); static void nfs_wback_result(struct rpc_task *task); static void nfs_cancel_request(struct nfs_wreq *req); @@ -73,9 +72,7 @@ static void nfs_cancel_request(struct nfs_wreq *req); * Limit number of delayed writes */ static int nr_write_requests = 0; -static int nr_failed_requests = 0; static struct rpc_wait_queue write_queue = RPC_INIT_WAITQ("write_chain"); -struct nfs_wreq * nfs_failed_requests = NULL; /* Hack for future NFS swap support */ #ifndef IS_SWAPFILE @@ -83,46 +80,6 @@ struct nfs_wreq * nfs_failed_requests = NULL; #endif /* - * Unlock a page after writing it - */ -static inline void -nfs_unlock_page(struct page *page) -{ - dprintk("NFS: unlock %ld\n", page->offset); - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); - -#ifdef CONFIG_NFS_SWAP - /* async swap-out support */ - if (test_and_clear_bit(PG_decr_after, &page->flags)) - atomic_dec(&page->count); - if (test_and_clear_bit(PG_swap_unlock_after, &page->flags)) { - /* - * We're doing a swap, so check that this page is - * swap-cached and do the necessary cleanup. - */ - swap_after_unlock_page(page->offset); - } -#endif -} - -/* - * Transfer a page lock to a write request waiting for it. - */ -static inline void -transfer_page_lock(struct nfs_wreq *req) -{ - dprintk("NFS: transfer_page_lock\n"); - - req->wb_flags &= ~NFS_WRITE_WANTLOCK; - req->wb_flags |= NFS_WRITE_LOCKED; - rpc_wake_up_task(&req->wb_task); - - dprintk("NFS: wake up task %d (flags %x)\n", - req->wb_task.tk_pid, req->wb_flags); -} - -/* * Write a page synchronously. * Offset is the data offset within the page. */ @@ -195,7 +152,6 @@ io_error: inode->i_ino, fattr.fileid); } - nfs_unlock_page(page); return written? written : result; } @@ -220,11 +176,13 @@ remove_write_request(struct nfs_wreq **q, struct nfs_wreq *wreq) } /* - * Find a write request for a given page + * Find a non-busy write request for a given page to + * try to combine with. */ static inline struct nfs_wreq * find_write_request(struct inode *inode, struct page *page) { + pid_t pid = current->pid; struct nfs_wreq *head, *req; dprintk("NFS: find_write_request(%x/%ld, %p)\n", @@ -232,76 +190,23 @@ find_write_request(struct inode *inode, struct page *page) if (!(req = head = NFS_WRITEBACK(inode))) return NULL; do { - if (req->wb_page == page) - return req; - } while ((req = WB_NEXT(req)) != head); - return NULL; -} - -/* - * Find any requests for the specified dentry. - */ -int -nfs_find_dentry_request(struct inode *inode, struct dentry *dentry) -{ - struct nfs_wreq *head, *req; - int found = 0; - - req = head = NFS_WRITEBACK(inode); - while (req != NULL) { - if (req->wb_dentry == dentry && !WB_CANCELLED(req)) { - found = 1; - break; - } - if ((req = WB_NEXT(req)) == head) - break; - } - return found; -} - -/* - * Find a failed write request by pid - */ -static struct nfs_wreq * -find_failed_request(struct inode *inode, pid_t pid) -{ - struct nfs_wreq *head, *req; + /* + * We can't combine with canceled requests or + * requests that have already been started.. + */ + if (req->wb_flags & (NFS_WRITE_CANCELLED | NFS_WRITE_INPROGRESS)) + continue; - req = head = nfs_failed_requests; - while (req != NULL) { - if (req->wb_inode == inode && (pid == 0 || req->wb_pid == pid)) + if (req->wb_page == page && req->wb_pid == pid) return req; - if ((req = WB_NEXT(req)) == head) - break; - } - return NULL; -} - -/* - * Add a request to the failed list. - */ -static void -append_failed_request(struct nfs_wreq * req) -{ - static int old_max = 16; - append_write_request(&nfs_failed_requests, req); - nr_failed_requests++; - if (nr_failed_requests >= old_max) { - printk("NFS: %d failed requests\n", nr_failed_requests); - old_max = old_max << 1; - } -} + /* + * Ehh, don't keep too many tasks queued.. + */ + rpc_wake_up_task(&req->wb_task); -/* - * Remove a request from the failed list and free it. - */ -static void -remove_failed_request(struct nfs_wreq * req) -{ - remove_write_request(&nfs_failed_requests, req); - kfree(req); - nr_failed_requests--; + } while ((req = WB_NEXT(req)) != head); + return NULL; } /* @@ -310,14 +215,8 @@ remove_failed_request(struct nfs_wreq * req) int nfs_check_failed_request(struct inode * inode) { - struct nfs_wreq * req; - int found = 0; - - while ((req = find_failed_request(inode, 0)) != NULL) { - remove_failed_request(req); - found++; - } - return found; + /* FIXME! */ + return 0; } /* @@ -334,6 +233,10 @@ update_write_request(struct nfs_wreq *req, unsigned int first, dprintk("nfs: trying to update write request %p\n", req); + /* not contiguous? */ + if (rqlast < first || last < rqfirst) + return 0; + /* Check the credentials associated with this write request. * If the buffer is owned by the same user, we can happily * add our data without risking server permission problems. @@ -349,12 +252,25 @@ update_write_request(struct nfs_wreq *req, unsigned int first, rqfirst = first; if (rqlast < last) rqlast = last; + req->wb_offset = rqfirst; req->wb_bytes = rqlast - rqfirst; + req->wb_count++; return 1; } +static inline void +free_write_request(struct nfs_wreq * req) +{ + if (!--req->wb_count) { + struct inode *inode = req->wb_inode; + remove_write_request(&NFS_WRITEBACK(inode), req); + kfree(req); + nr_write_requests--; + } +} + /* * Create and initialize a writeback request */ @@ -371,8 +287,7 @@ create_write_request(struct dentry *dentry, struct inode *inode, page->offset + offset, bytes); /* FIXME: Enforce hard limit on number of concurrent writes? */ - - wreq = (struct nfs_wreq *) kmalloc(sizeof(*wreq), GFP_USER); + wreq = (struct nfs_wreq *) kmalloc(sizeof(*wreq), GFP_KERNEL); if (!wreq) goto out_fail; memset(wreq, 0, sizeof(*wreq)); @@ -380,7 +295,7 @@ create_write_request(struct dentry *dentry, struct inode *inode, task = &wreq->wb_task; rpc_init_task(task, clnt, nfs_wback_result, RPC_TASK_NFSWRITE); task->tk_calldata = wreq; - task->tk_action = nfs_wback_lock; + task->tk_action = nfs_wback_begin; rpcauth_lookupcred(task); /* Obtain user creds */ if (task->tk_status < 0) @@ -393,8 +308,7 @@ create_write_request(struct dentry *dentry, struct inode *inode, wreq->wb_page = page; wreq->wb_offset = offset; wreq->wb_bytes = bytes; - - atomic_inc(&page->count); + wreq->wb_count = 2; /* One for the IO, one for us */ append_write_request(&NFS_WRITEBACK(inode), wreq); @@ -414,8 +328,7 @@ out_fail: * Schedule a writeback RPC call. * If the server is congested, don't add to our backlog of queued * requests but call it synchronously. - * The function returns false if the page has been unlocked as the - * consequence of a synchronous write call. + * The function returns whether we should wait for the thing or not. * * FIXME: Here we could walk the inode's lock list to see whether the * page we're currently writing to has been write-locked by the caller. @@ -438,7 +351,6 @@ schedule_write_request(struct nfs_wreq *req, int sync) dprintk("NFS: %4d schedule_write_request (sync)\n", task->tk_pid); /* Page is already locked */ - req->wb_flags |= NFS_WRITE_LOCKED; rpc_clnt_sigmask(clnt, &oldmask); rpc_execute(task); rpc_clnt_sigunmask(clnt, &oldmask); @@ -450,39 +362,38 @@ schedule_write_request(struct nfs_wreq *req, int sync) rpc_sleep_on(&write_queue, task, NULL, NULL); } - return sync == 0; + return sync; } /* - * Wait for request to complete - * This is almost a copy of __wait_on_page + * Wait for request to complete. */ -static inline int +static int wait_on_write_request(struct nfs_wreq *req) { + struct rpc_clnt *clnt = NFS_CLIENT(req->wb_inode); struct wait_queue wait = { current, NULL }; - struct page *page = req->wb_page; - int retval; sigset_t oldmask; - struct rpc_clnt *clnt = NFS_CLIENT(req->wb_inode); + int retval; + + /* Make sure it's started.. */ + if (!WB_INPROGRESS(req)) + rpc_wake_up_task(&req->wb_task); rpc_clnt_sigmask(clnt, &oldmask); - add_wait_queue(&page->wait, &wait); - atomic_inc(&page->count); + add_wait_queue(&req->wb_wait, &wait); for (;;) { current->state = TASK_INTERRUPTIBLE; retval = 0; - if (!PageLocked(page)) + if (req->wb_flags & NFS_WRITE_COMPLETE) break; retval = -ERESTARTSYS; if (signalled()) break; schedule(); } - remove_wait_queue(&page->wait, &wait); + remove_wait_queue(&req->wb_wait, &wait); current->state = TASK_RUNNING; - /* N.B. page may have been unused, so we must use free_page() */ - free_page(page_address(page)); rpc_clnt_sigunmask(clnt, &oldmask); return retval; } @@ -505,21 +416,18 @@ nfs_writepage(struct file * file, struct page *page) * things with a page scheduled for an RPC call (e.g. invalidate it). */ int -nfs_updatepage(struct file *file, struct page *page, const char *buffer, - unsigned long offset, unsigned int count, int sync) +nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsigned int count, int sync) { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; - u8 *page_addr = (u8 *) page_address(page); struct nfs_wreq *req; - int status = 0, page_locked = 1; + int synchronous = sync; + int retval; dprintk("NFS: nfs_updatepage(%s/%s %d@%ld, sync=%d)\n", dentry->d_parent->d_name.name, dentry->d_name.name, count, page->offset+offset, sync); - set_bit(PG_locked, &page->flags); - /* * Try to find a corresponding request on the writeback queue. * If there is one, we can be sure that this request is not @@ -530,151 +438,54 @@ nfs_updatepage(struct file *file, struct page *page, const char *buffer, * After returning, generic_file_write will wait on the * page and retry the update. */ - if ((req = find_write_request(inode, page)) != NULL) { - if (update_write_request(req, offset, count)) { - /* N.B. check for a fault here and cancel the req */ - /* - * SECURITY - copy_from_user must zero the - * rest of the data after a fault! - */ - copy_from_user(page_addr + offset, buffer, count); - goto updated; - } - dprintk("NFS: wake up conflicting write request.\n"); - transfer_page_lock(req); - return 0; - } - - /* Copy data to page buffer. */ - status = -EFAULT; - if (copy_from_user(page_addr + offset, buffer, count)) - goto done; + req = find_write_request(inode, page); + if (req && update_write_request(req, offset, count)) + goto updated; - /* If wsize is smaller than page size, update and write + /* + * If wsize is smaller than page size, update and write * page synchronously. */ if (NFS_SERVER(inode)->wsize < PAGE_SIZE) return nfs_writepage_sync(dentry, inode, page, offset, count); /* Create the write request. */ - status = -ENOBUFS; req = create_write_request(dentry, inode, page, offset, count); if (!req) - goto done; + return -ENOBUFS; - /* Schedule request */ - page_locked = schedule_write_request(req, sync); - -updated: /* - * If we wrote up to the end of the chunk, transmit request now. - * We should be a bit more intelligent about detecting whether a - * process accesses the file sequentially or not. + * Ok, there's another user of this page with the new request.. + * The IO completion will then free the page and the dentry. */ - if (page_locked && (offset + count >= PAGE_SIZE || sync)) - req->wb_flags |= NFS_WRITE_WANTLOCK; - - /* If the page was written synchronously, return any error that - * may have happened; otherwise return the write count. */ - if (page_locked || (status = nfs_write_error(inode)) >= 0) - status = count; - -done: - /* Unlock page and wake up anyone sleeping on it */ - if (page_locked) { - if (req && WB_WANTLOCK(req)) { - transfer_page_lock(req); - /* rpc_execute(&req->wb_task); */ - if (sync) { - /* if signalled, ensure request is cancelled */ - if ((count = wait_on_write_request(req)) != 0) { - nfs_cancel_request(req); - status = count; - } - if ((count = nfs_write_error(inode)) < 0) - status = count; - } - } else { - if (status < 0) { -printk("NFS: %s/%s write failed, clearing bit\n", -dentry->d_parent->d_name.name, dentry->d_name.name); - clear_bit(PG_uptodate, &page->flags); - } - nfs_unlock_page(page); - } - } - - dprintk("NFS: nfs_updatepage returns %d (isize %ld)\n", - status, inode->i_size); - return status; -} - -/* - * Flush out a dirty page. - */ -static void -nfs_flush_request(struct nfs_wreq *req) -{ - struct page *page = req->wb_page; - -#ifdef NFS_DEBUG_VERBOSE -if (req->wb_inode != page->inode) -printk("NFS: inode %ld no longer has page %p\n", req->wb_inode->i_ino, page); -#endif - dprintk("NFS: nfs_flush_request(%s/%s, @%ld)\n", - req->wb_dentry->d_parent->d_name.name, - req->wb_dentry->d_name.name, page->offset); - - req->wb_flags |= NFS_WRITE_WANTLOCK; - if (!test_and_set_bit(PG_locked, &page->flags)) { - transfer_page_lock(req); - } else { - printk(KERN_WARNING "NFS oops in %s: can't lock page!\n", - __FUNCTION__); - rpc_wake_up_task(&req->wb_task); - } -} - -/* - * Flush writeback requests. See nfs_flush_dirty_pages for details. - */ -static struct nfs_wreq * -nfs_flush_pages(struct inode *inode, pid_t pid, off_t offset, off_t len, - int invalidate) -{ - struct nfs_wreq *head, *req, *last = NULL; - off_t rqoffset, rqend, end; + atomic_inc(&page->count); + dget(dentry); - end = len? offset + len : 0x7fffffffUL; + /* Schedule request */ + synchronous = schedule_write_request(req, sync); - req = head = NFS_WRITEBACK(inode); - while (req != NULL) { - dprintk("NFS: %4d nfs_flush inspect %s/%s @%ld fl %x\n", - req->wb_task.tk_pid, - req->wb_dentry->d_parent->d_name.name, - req->wb_dentry->d_name.name, - req->wb_page->offset, req->wb_flags); +updated: + if (req->wb_bytes == PAGE_SIZE) + set_bit(PG_uptodate, &page->flags); - rqoffset = req->wb_page->offset + req->wb_offset; - rqend = rqoffset + req->wb_bytes; - if (rqoffset < end && offset < rqend && - (pid == 0 || req->wb_pid == pid)) { - if (!WB_INPROGRESS(req) && !WB_HAVELOCK(req)) { -#ifdef NFS_DEBUG_VERBOSE -printk("nfs_flush: flushing inode=%ld, %d @ %lu\n", -req->wb_inode->i_ino, req->wb_bytes, rqoffset); -#endif - nfs_flush_request(req); - } - last = req; + retval = count; + if (synchronous) { + int status = wait_on_write_request(req); + if (status) { + nfs_cancel_request(req); + retval = status; + } else { + status = req->wb_status; + if (status < 0) + retval = status; } - if (invalidate) - req->wb_flags |= NFS_WRITE_INVALIDATE; - if ((req = WB_NEXT(req)) == head) - break; + + if (retval < 0) + clear_bit(PG_uptodate, &page->flags); } - return last; + free_write_request(req); + return retval; } /* @@ -711,53 +522,71 @@ nfs_cancel_dirty(struct inode *inode, pid_t pid) } /* - * Flush out all dirty pages belonging to a certain user process and - * maybe wait for the RPC calls to complete. - * - * Another purpose of this function is sync()ing a file range before a - * write lock is released. This is what offset and length are for, even if - * this isn't used by the nlm module yet. + * If we're waiting on somebody else's request + * we need to increment the counter during the + * wait so that the request doesn't disappear + * from under us during the wait.. */ -int -nfs_flush_dirty_pages(struct inode *inode, pid_t pid, off_t offset, off_t len) +static int FASTCALL(wait_on_other_req(struct nfs_wreq *)); +static int wait_on_other_req(struct nfs_wreq *req) { - struct nfs_wreq *last = NULL; - int result = 0; + int retval; + req->wb_count++; + retval = wait_on_write_request(req); + free_write_request(req); + return retval; +} - dprintk("NFS: flush_dirty_pages(%x/%ld for pid %d %ld/%ld)\n", - inode->i_dev, inode->i_ino, current->pid, offset, len); +/* + * This writes back a set of requests according to the condition. + * + * If this ever gets much more convoluted, use a fn pointer for + * the condition.. + */ +#define NFS_WB(inode, cond) { int retval = 0 ; \ + do { \ + struct nfs_wreq *req = NFS_WRITEBACK(inode); \ + struct nfs_wreq *head = req; \ + if (!req) break; \ + for (;;) { \ + if (!(req->wb_flags & NFS_WRITE_COMPLETE)) \ + if (cond) break; \ + req = WB_NEXT(req); \ + if (req == head) goto out; \ + } \ + retval = wait_on_other_req(req); \ + } while (!retval); \ +out: return retval; \ +} - for (;;) { - /* Flush all pending writes for the pid and file region */ - last = nfs_flush_pages(inode, pid, offset, len, 0); - if (last == NULL) - break; - result = wait_on_write_request(last); - if (result) { - nfs_cancel_dirty(inode,pid); - break; - } - } +int +nfs_wb_all(struct inode *inode) +{ + NFS_WB(inode, 1); +} - return result; +/* + * Write back all requests on one page - we do this before reading it. + */ +int +nfs_wb_page(struct inode *inode, struct page *page) +{ + NFS_WB(inode, req->wb_page == page); } /* - * Flush out any pending write requests and flag that they be discarded - * after the write is complete. - * - * This function is called from nfs_refresh_inode just before it calls - * invalidate_inode_pages. After nfs_flush_pages returns, we can be sure - * that all dirty pages are locked, so that invalidate_inode_pages does - * not throw away any dirty pages. + * Write back all pending writes for one user.. */ -void -nfs_invalidate_pages(struct inode *inode) +int +nfs_wb_pid(struct inode *inode, pid_t pid) { - dprintk("NFS: nfs_invalidate_pages(%x/%ld)\n", - inode->i_dev, inode->i_ino); + NFS_WB(inode, req->wb_pid == pid); +} - nfs_flush_pages(inode, 0, 0, 0, 1); +void +nfs_inval(struct inode *inode) +{ + nfs_cancel_dirty(inode,0); } /* @@ -795,21 +624,8 @@ nfs_truncate_dirty_pages(struct inode *inode, unsigned long offset) int nfs_check_error(struct inode *inode) { - struct nfs_wreq *req; - int status = 0; - - dprintk("nfs: checking for write error inode %04x/%ld\n", - inode->i_dev, inode->i_ino); - - req = find_failed_request(inode, current->pid); - if (req) { - dprintk("nfs: write error %d inode %04x/%ld\n", - req->wb_task.tk_status, inode->i_dev, inode->i_ino); - - status = req->wb_task.tk_status; - remove_failed_request(req); - } - return status; + /* FIXME! */ + return 0; } /* @@ -819,23 +635,16 @@ nfs_check_error(struct inode *inode) * set up the RPC call info, and pass to the call FSM. */ static void -nfs_wback_lock(struct rpc_task *task) +nfs_wback_begin(struct rpc_task *task) { struct nfs_wreq *req = (struct nfs_wreq *) task->tk_calldata; struct page *page = req->wb_page; struct dentry *dentry = req->wb_dentry; - dprintk("NFS: %4d nfs_wback_lock (%s/%s, status=%d flags=%x)\n", + dprintk("NFS: %4d nfs_wback_begin (%s/%s, status=%d flags=%x)\n", task->tk_pid, dentry->d_parent->d_name.name, dentry->d_name.name, task->tk_status, req->wb_flags); - if (!WB_HAVELOCK(req)) - req->wb_flags |= NFS_WRITE_WANTLOCK; - - if (WB_WANTLOCK(req) && test_and_set_bit(PG_locked, &page->flags)) - goto out_locked; - req->wb_flags &= ~NFS_WRITE_WANTLOCK; - req->wb_flags |= NFS_WRITE_LOCKED; task->tk_status = 0; /* Setup the task struct for a writeback call */ @@ -848,12 +657,6 @@ nfs_wback_lock(struct rpc_task *task) req->wb_flags |= NFS_WRITE_INPROGRESS; return; - -out_locked: - printk("NFS: page already locked in writeback_lock!\n"); - task->tk_timeout = 2 * HZ; - rpc_sleep_on(&write_queue, task, NULL, NULL); - return; } /* @@ -873,15 +676,10 @@ nfs_wback_result(struct rpc_task *task) /* Set the WRITE_COMPLETE flag, but leave WRITE_INPROGRESS set */ req->wb_flags |= NFS_WRITE_COMPLETE; + req->wb_status = status; + if (status < 0) { - /* - * An error occurred. Report the error back to the - * application by adding the request to the failed - * requests list. - */ - if (find_failed_request(inode, req->wb_pid)) - status = 0; - clear_bit(PG_uptodate, &page->flags); + req->wb_flags |= NFS_WRITE_INVALIDATE; } else if (!WB_CANCELLED(req)) { struct nfs_fattr *fattr = &req->wb_fattr; /* Update attributes as result of writeback. @@ -911,31 +709,28 @@ nfs_wback_result(struct rpc_task *task) } } - /* - * This call might block, so we defer removing the request - * from the inode's writeback list. - */ rpc_release_task(task); if (WB_INVALIDATE(req)) clear_bit(PG_uptodate, &page->flags); - if (WB_HAVELOCK(req)) - nfs_unlock_page(page); + __free_page(page); + dput(req->wb_dentry); + + wake_up(&req->wb_wait); + /* - * Now it's safe to remove the request from the inode's - * writeback list and wake up any tasks sleeping on it. - * If the request failed, add it to the failed list. + * FIXME! + * + * We should not free the request here if it has pending + * error status on it. We should just leave it around, to + * let the error be collected later. However, the error + * collecting routines are too stupid for that right now, + * so we just drop the error on the floor at this point + * for any async writes. + * + * This should not be a major headache to fix, but I want + * to validate basic operations first. */ - remove_write_request(&NFS_WRITEBACK(inode), req); - - if (status >= 0) - kfree(req); - else { - dprintk("NFS: %4d saving write failure code\n", task->tk_pid); - append_failed_request(req); - } - - free_page(page_address(page)); - nr_write_requests--; + free_write_request(req); } |