summaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/dir.c129
-rw-r--r--fs/nfs/file.c58
-rw-r--r--fs/nfs/inode.c29
-rw-r--r--fs/nfs/read.c15
-rw-r--r--fs/nfs/symlink.c6
-rw-r--r--fs/nfs/write.c531
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);
}