NFS: patch for 2.1.131

Trond Myklebust (trond.myklebust@fys.uio.no)
09 Dec 1998 01:01:24 +0100


--Multipart_Wed_Dec__9_01:01:24_1998-1
Content-Type: text/plain; charset=US-ASCII

Hi,

The following patch fixes a race in which an NFS read ends up clobbering
the inode from beneath an asynchronous write that is in progress. The
patch causes NFS reads to check whether any write is in progress
before calling nfs_refresh_inode.

In addition the patch also improves the protection of dentries for
asynchronous NFS reads/writes.

Cheers,
Trond

--Multipart_Wed_Dec__9_01:01:24_1998-1
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="nfs-2.1.131.dif"
Content-Transfer-Encoding: 7bit

--- linux/fs/nfs/write.c-2.1.131 Wed Dec 2 18:45:25 1998
+++ linux/fs/nfs/write.c Wed Dec 9 00:24:45 1998
@@ -175,6 +175,38 @@
rpc_remove_list(q, wreq);
}

+struct nfs_wreq *
+nfs_pending_write_(struct inode *inode, struct nfs_wreq *req)
+{
+ struct nfs_wreq *head = NFS_WRITEBACK(inode);
+ if (req == NULL)
+ req = head;
+ else
+ if ((req = WB_NEXT(req)) == head)
+ return NULL;
+
+ while(req != NULL) {
+ if (!(WB_COMPLETE(req) || WB_CANCELLED(req)))
+ return req;
+ if ((req = WB_NEXT(req)) == head)
+ break;
+ }
+ return NULL;
+}
+
+struct nfs_wreq *
+nfs_write_inprogress_(struct inode *inode, struct nfs_wreq *req)
+{
+ for(;;) {
+ req = nfs_pending_write_(inode, req);
+ if (req == NULL)
+ return req;
+ if (WB_INPROGRESS(req))
+ return req;
+ }
+}
+
+
/*
* Find a non-busy write request for a given page to
* try to combine with.
@@ -266,6 +298,7 @@
if (!--req->wb_count) {
struct inode *inode = req->wb_inode;
remove_write_request(&NFS_WRITEBACK(inode), req);
+ dput(req->wb_dentry);
kfree(req);
nr_write_requests--;
}
@@ -302,7 +335,7 @@
goto out_req;

/* Put the task on inode's writeback request list. */
- wreq->wb_dentry = dentry;
+ wreq->wb_dentry = dget(dentry);
wreq->wb_inode = inode;
wreq->wb_pid = current->pid;
wreq->wb_page = page;
@@ -459,7 +492,6 @@
* The IO completion will then free the page and the dentry.
*/
atomic_inc(&page->count);
- dget(dentry);

/* Schedule request */
synchronous = schedule_write_request(req, sync);
@@ -497,7 +529,8 @@
static void
nfs_cancel_request(struct nfs_wreq *req)
{
- req->wb_flags |= NFS_WRITE_CANCELLED;
+ req->wb_flags |= NFS_WRITE_CANCELLED | NFS_WRITE_INVALIDATE;
+ clear_bit(PG_uptodate, &req->wb_page->flags);
if (!WB_INPROGRESS(req)) {
rpc_exit(&req->wb_task, 0);
rpc_wake_up_task(&req->wb_task);
@@ -510,14 +543,13 @@
static void
nfs_cancel_dirty(struct inode *inode, pid_t pid)
{
- struct nfs_wreq *head, *req;
+ struct nfs_wreq *req;

- req = head = NFS_WRITEBACK(inode);
+ req = nfs_pending_write(inode);
while (req != NULL) {
if (pid == 0 || req->wb_pid == pid)
nfs_cancel_request(req);
- if ((req = WB_NEXT(req)) == head)
- break;
+ req = nfs_pending_write_(inode,req);
}
}

@@ -715,7 +747,6 @@
clear_bit(PG_uptodate, &page->flags);

__free_page(page);
- dput(req->wb_dentry);

wake_up(&req->wb_wait);

--- linux/fs/nfs/read.c-2.1.131 Mon Nov 16 04:56:33 1998
+++ linux/fs/nfs/read.c Tue Dec 8 23:47:15 1998
@@ -33,6 +33,7 @@
#define NFSDBG_FACILITY NFSDBG_PAGECACHE

struct nfs_rreq {
+ struct dentry * ra_dentry; /* dentry from which to read */
struct inode * ra_inode; /* inode from which to read */
struct page * ra_page; /* page to be read */
struct nfs_readargs ra_args; /* XDR argument struct */
@@ -110,6 +111,8 @@
break;
} while (count);

+ if (nfs_write_inprogress(inode) != NULL) refresh = 0;
+
memset(buffer, 0, count);
set_bit(PG_uptodate, &page->flags);
result = 0;
@@ -145,7 +148,8 @@
if (result < PAGE_SIZE) {
memset((char *) address + result, 0, PAGE_SIZE - result);
}
- nfs_refresh_inode(req->ra_inode, &req->ra_fattr);
+ if (nfs_write_inprogress(req->ra_inode) == NULL)
+ nfs_refresh_inode(req->ra_inode, &req->ra_fattr);
set_bit(PG_uptodate, &page->flags);
succ++;
} else {
@@ -155,6 +159,7 @@
}
/* N.B. Use nfs_unlock_page here? */
clear_bit(PG_locked, &page->flags);
+ dput(req->ra_dentry);
wake_up(&page->wait);

free_page(address);
@@ -185,6 +190,7 @@
/* N.B. Will the dentry remain valid for life of request? */
nfs_readreq_setup(req, NFS_FH(dentry), page->offset,
(void *) address, PAGE_SIZE);
+ req->ra_dentry = dget(dentry);
req->ra_inode = inode;
req->ra_page = page; /* count has been incremented by caller */

--- linux/include/linux/nfs_fs.h-2.1.131 Wed Dec 2 18:45:42 1998
+++ linux/include/linux/nfs_fs.h Tue Dec 8 23:46:35 1998
@@ -254,6 +254,22 @@
return _nfs_revalidate_inode(server, dentry);
}

+extern struct nfs_wreq *nfs_pending_write_(struct inode *, struct nfs_wreq *);
+
+static inline struct nfs_wreq *
+nfs_pending_write(struct inode *inode)
+{
+ return nfs_pending_write_(inode, NULL);
+}
+
+extern struct nfs_wreq *nfs_write_inprogress_(struct inode *, struct nfs_wreq *);
+
+static inline struct nfs_wreq *
+nfs_write_inprogress(struct inode *inode)
+{
+ return nfs_write_inprogress_(inode, NULL);
+}
+
static inline int
nfs_write_error(struct inode *inode)
{

--Multipart_Wed_Dec__9_01:01:24_1998-1--

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/