patch-1.3.82 linux/fs/nfs/bio.c

Next file: linux/fs/nfs/cache.c
Previous file: linux/fs/nfs/README
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.81/linux/fs/nfs/bio.c linux/fs/nfs/bio.c
@@ -0,0 +1,164 @@
+/*
+ * linux/fs/nfs/bio.c
+ *
+ * Block I/O for NFS
+ *
+ * Partial copy of Linus' read cache modifications to fs/nfs/file.c
+ * modified for async RPC by okir@monad.swb.de
+ *
+ * We do an ugly hack here in order to return proper error codes to the
+ * user program when a read request failed. This is a huge problem because
+ * generic_file_read only checks the return value of inode->i_op->readpage()
+ * which is usually 0 for async RPC. To overcome this obstacle, we set
+ * the error bit of the page to 1 when an error occurs, and make nfs_readpage
+ * transmit requests synchronously when encountering this.
+ *
+ * Another possible solution to this problem may be to have a cache of recent
+ * RPC call results indexed by page pointer, or even a result code field
+ * in struct page.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfsiod.h>
+#include <linux/malloc.h>
+#include <linux/pagemap.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#undef DEBUG_BIO
+#ifdef DEBUG_BIO
+#define dprintk(args...)	printk(## args)
+#else
+#define dprintk(args...)	/* nothing */
+#endif
+
+static inline int
+do_read_nfs_sync(struct inode * inode, struct page * page)
+{
+	struct nfs_fattr fattr;
+	int		result, refresh = 0;
+	int		count = PAGE_SIZE;
+	int		rsize = NFS_SERVER(inode)->rsize;
+	char		*buf = (char *) page_address(page);
+	unsigned long	pos = page->offset;
+
+	dprintk("NFS: do_read_nfs_sync(%p)\n", page);
+
+	page->locked = 1;
+	page->error = 0;
+
+	do {
+		if (count < rsize)
+			rsize = count;
+		result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode), 
+			pos, rsize, buf, &fattr);
+		dprintk("nfs_proc_read(%s, (%x,%lx), %ld, %d, %p) = %d\n",
+				NFS_SERVER(inode)->hostname,
+				inode->i_dev, inode->i_ino,
+				pos, rsize, buf, result);
+		if (result < 0)
+			break;
+		refresh = 1;
+		count -= result;
+		pos += result;
+		buf += result;
+		if (result < rsize)
+			break;
+	} while (count);
+
+	memset(buf, 0, count);
+	if (refresh) {
+		nfs_refresh_inode(inode, &fattr);
+		result = 0;
+		page->uptodate = 1;
+	}
+	page->locked = 0;
+	wake_up(&page->wait);
+	return result;
+}
+
+/*
+ * This is the callback from nfsiod telling us whether a reply was
+ * received or some error occurred (timeout or socket shutdown).
+ */
+static void
+nfs_read_cb(int result, struct nfsiod_req *req)
+{
+	struct page	*page = (struct page *) req->rq_cdata;
+	static int	succ = 0, fail = 0;
+
+	dprintk("BIO: received callback for page %p, result %d\n",
+			page, result);
+
+	if (result >= 0
+	 && (result = nfs_proc_read_reply(&req->rq_rpcreq)) >= 0) {
+		succ++;
+		page->uptodate = 1;
+	} else {
+		fail++;
+		printk("BIO: %d successful reads, %d failures\n", succ, fail);
+		page->error = 1;
+	}
+	page->locked = 0;
+	wake_up(&page->wait);
+	free_page(page_address(page));
+}
+
+static inline int
+do_read_nfs_async(struct inode *inode, struct page *page)
+{
+	struct nfsiod_req *req;
+	int		result = -1;	/* totally arbitrary */
+
+	dprintk("NFS: do_read_nfs_async(%p)\n", page);
+
+	page->locked = 1;
+	page->error = 0;
+
+	if (!(req = nfsiod_reserve(NFS_SERVER(inode), nfs_read_cb)))
+		goto done;
+	result = nfs_proc_read_request(&req->rq_rpcreq,
+			NFS_SERVER(inode), NFS_FH(inode),
+			page->offset, PAGE_SIZE, 
+			(__u32 *) page_address(page));
+	if (result >= 0) {
+		req->rq_cdata = page;
+		page->count++;
+		result = nfsiod_enqueue(req);
+		if (result >= 0)
+			dprintk("NFS: enqueued async READ request.\n");
+	}
+	if (result < 0) {
+		dprintk("NFS: deferring async READ request.\n");
+		nfsiod_release(req);
+		page->locked = 0;
+		wake_up(&page->wait);
+	}
+
+done:
+	return result < 0? result : 0;
+}
+
+int
+nfs_readpage(struct inode *inode, struct page *page)
+{
+	unsigned long	address;
+	int		error = -1;
+
+	dprintk("NFS: nfs_readpage %08lx\n", page_address(page));
+	address = page_address(page);
+	page->count++;
+	if (!page->error && NFS_SERVER(inode)->rsize >= PAGE_SIZE)
+		error = do_read_nfs_async(inode, page);
+	if (error < 0)		/* couldn't enqueue */
+		error = do_read_nfs_sync(inode, page);
+	free_page(address);
+	return error;
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this