patch-2.1.60 linux/fs/nfs/write.c
Next file: linux/fs/nfsd/vfs.c
Previous file: linux/fs/nfs/inode.c
Back to the patch index
Back to the overall index
- Lines: 240
- Date:
Sat Oct 25 02:06:24 1997
- Orig file:
v2.1.59/linux/fs/nfs/write.c
- Orig date:
Mon Oct 20 10:36:52 1997
diff -u --recursive --new-file v2.1.59/linux/fs/nfs/write.c linux/fs/nfs/write.c
@@ -68,6 +68,8 @@
#define NFSDBG_FACILITY NFSDBG_PAGECACHE
+int check_failed_request(struct inode *);
+
static void nfs_wback_lock(struct rpc_task *task);
static void nfs_wback_result(struct rpc_task *task);
@@ -120,6 +122,7 @@
* 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;
@@ -282,28 +285,72 @@
/*
* Find a failed write request by pid
*/
-static inline struct nfs_wreq *
-find_failed_request(struct inode *inode, pid_t pid)
+static struct nfs_wreq *
+find_failed_request(struct inode *inode, pid_t pid, int all)
{
struct nfs_wreq *head, *req;
if (!(req = head = nfs_failed_requests))
return NULL;
do {
- if (req->wb_inode == inode && req->wb_pid == pid)
+ if (req->wb_inode == inode && (all || req->wb_pid == pid))
return req;
} while ((req = WB_NEXT(req)) != head);
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;
+ }
+}
+
+/*
+ * 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--;
+}
+
+/*
+ * Find and release all failed requests for this inode.
+ */
+int
+check_failed_request(struct inode * inode)
+{
+ struct nfs_wreq * req;
+ int found = 0;
+
+ while ((req = find_failed_request(inode, 0, 1)) != NULL) {
+ remove_failed_request(req);
+ found++;
+ }
+ return found;
+}
+
+/*
* Try to merge adjacent write requests. This works only for requests
* issued by the same user.
*/
static inline int
-update_write_request(struct nfs_wreq *req, unsigned first, unsigned bytes)
+update_write_request(struct nfs_wreq *req, unsigned int first,
+ unsigned int bytes)
{
- unsigned rqfirst = req->wb_offset,
+ unsigned int rqfirst = req->wb_offset,
rqlast = rqfirst + req->wb_bytes,
last = first + bytes;
@@ -335,7 +382,7 @@
*/
static inline struct nfs_wreq *
create_write_request(struct inode *inode, struct page *page,
- unsigned offset, unsigned bytes)
+ unsigned int offset, unsigned int bytes)
{
struct nfs_wreq *wreq;
struct rpc_clnt *clnt = NFS_CLIENT(inode);
@@ -448,9 +495,9 @@
}
remove_wait_queue(&page->wait, &wait);
current->state = TASK_RUNNING;
-if (atomic_read(&page->count) == 1)
-printk("NFS: lost a page\n");
- atomic_dec(&page->count);
+ if (atomic_read(&page->count) == 1)
+ printk("NFS: page unused while waiting\n");
+ free_page(page_address(page));
return retval;
}
@@ -620,7 +667,7 @@
}
/*
- * Cancel all writeback requests, both pending and in process.
+ * Cancel all writeback requests, both pending and in progress.
*/
static void
nfs_cancel_dirty(struct inode *inode, pid_t pid)
@@ -650,17 +697,18 @@
nfs_flush_dirty_pages(struct inode *inode, off_t offset, off_t len)
{
struct nfs_wreq *last = NULL;
+ int result = 0;
dprintk("NFS: flush_dirty_pages(%x/%ld for pid %d %ld/%ld)\n",
inode->i_dev, inode->i_ino, current->pid,
offset, len);
- if (IS_SOFT && signalled())
- nfs_cancel_dirty(inode, current->pid);
-
for (;;) {
- if (IS_SOFT && signalled())
- return -ERESTARTSYS;
+ if (IS_SOFT && signalled()) {
+ nfs_cancel_dirty(inode, current->pid);
+ result = -ERESTARTSYS;
+ break;
+ }
/* Flush all pending writes for this pid and file region */
last = nfs_flush_pages(inode, current->pid, offset, len, 0);
@@ -669,7 +717,7 @@
wait_on_write_request(last);
}
- return 0;
+ return result;
}
/*
@@ -732,15 +780,14 @@
dprintk("nfs: checking for write error inode %04x/%ld\n",
inode->i_dev, inode->i_ino);
- if (!(req = find_failed_request(inode, current->pid)))
- return 0;
-
- dprintk("nfs: write error %d inode %04x/%ld\n",
+ req = find_failed_request(inode, current->pid, 0);
+ 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_write_request(&nfs_failed_requests, req);
- kfree(req);
+ status = req->wb_task.tk_status;
+ remove_failed_request(req);
+ }
return status;
}
@@ -816,23 +863,14 @@
page = req->wb_page;
status = task->tk_status;
- /* Remove request from writeback list and wake up tasks
- * sleeping on it. */
- remove_write_request(&NFS_WRITEBACK(inode), req);
-
if (status < 0) {
/*
* An error occurred. Report the error back to the
- * application by adding the failed request to the
- * inode's error list.
+ * application by adding the request to the failed
+ * requests list.
*/
- if (find_failed_request(inode, req->wb_pid)) {
+ if (find_failed_request(inode, req->wb_pid, 0))
status = 0;
- } else {
- dprintk("NFS: %4d saving write failure code\n",
- task->tk_pid);
- append_write_request(&nfs_failed_requests, req);
- }
clear_bit(PG_uptodate, &page->flags);
} else if (!WB_CANCELLED(req)) {
struct nfs_fattr *fattr = req->wb_fattr;
@@ -860,6 +898,10 @@
}
}
+ /*
+ * This call might block, so we defer removing the request
+ * from the inode's writeback list.
+ */
rpc_release_task(task);
if (WB_INVALIDATE(req))
@@ -871,8 +913,20 @@
kfree(req->wb_args);
req->wb_args = 0;
}
+
+ /*
+ * 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.
+ */
+ 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--;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov