Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | /* * 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. * * June 96: Added retries of RPCs that seem to have failed for a transient * reason. */ #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); set_bit(PG_locked, &page->flags); clear_bit(PG_error, &page->flags); 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); /* * Even if we had a partial success we can't mark the page * cache valid. */ if (result < 0) goto io_error; refresh = 1; count -= result; pos += result; buf += result; if (result < rsize) break; } while (count); memset(buf, 0, count); set_bit(PG_uptodate, &page->flags); result = 0; io_error: if (refresh) nfs_refresh_inode(inode, &fattr); clear_bit(PG_locked, &page->flags); wake_up(&page->wait); return result; } /* * This is the function to (re-) transmit an NFS readahead request */ static int nfsiod_read_setup(struct nfsiod_req *req) { struct inode *inode = req->rq_inode; struct page *page = req->rq_page; return nfs_proc_read_request(&req->rq_rpcreq, NFS_SERVER(inode), NFS_FH(inode), page->offset, PAGE_SIZE, (__u32 *) page_address(page)); } /* * This is the callback from nfsiod telling us whether a reply was * received or some error occurred (timeout or socket shutdown). */ static int nfsiod_read_result(int result, struct nfsiod_req *req) { struct nfs_server *server = NFS_SERVER(req->rq_inode); struct page *page = req->rq_page; static int succ = 0, fail = 0; int i; dprintk("BIO: received callback for page %p, result %d\n", page, result); if (result >= 0) { struct nfs_fattr fattr; result = nfs_proc_read_reply(&req->rq_rpcreq, &fattr); if (result >= 0) { nfs_refresh_inode(req->rq_inode, &fattr); if (result < PAGE_SIZE) memset((u8 *) page_address(page)+result, 0, PAGE_SIZE-result); } } else if (result == -ETIMEDOUT && !(server->flags & NFS_MOUNT_SOFT)) { /* XXX: Theoretically, we'd have to increment the initial * timeo here; but I'm not going to bother with this now * because this old nfsiod stuff will soon die anyway. */ result = -EAGAIN; } if (result == -EAGAIN && req->rq_retries--) { dprintk("BIO: retransmitting request.\n"); memset(&req->rq_rpcreq, 0, sizeof(struct rpc_ioreq)); while (rpc_reserve(server->rsock, &req->rq_rpcreq, 1) < 0) schedule(); current->fsuid = req->rq_fsuid; current->fsgid = req->rq_fsgid; for (i = 0; i < NGROUPS; i++) current->groups[i] = req->rq_groups[i]; nfsiod_read_setup(req); return 0; } if (result >= 0) { set_bit(PG_uptodate, &page->flags); succ++; } else { dprintk("BIO: %d successful reads, %d failures\n", succ, fail); set_bit(PG_error, &page->flags); fail++; } clear_bit(PG_locked, &page->flags); wake_up(&page->wait); free_page(page_address(page)); return 1; } static inline int do_read_nfs_async(struct inode *inode, struct page *page) { struct nfsiod_req *req; int result, i; dprintk("NFS: do_read_nfs_async(%p)\n", page); set_bit(PG_locked, &page->flags); clear_bit(PG_error, &page->flags); if (!(req = nfsiod_reserve(NFS_SERVER(inode)))) return -EAGAIN; req->rq_retries = 5; req->rq_callback = nfsiod_read_result; req->rq_inode = inode; req->rq_page = page; req->rq_fsuid = current->fsuid; req->rq_fsgid = current->fsgid; for (i = 0; i < NGROUPS; i++) req->rq_groups[i] = current->groups[i]; if ((result = nfsiod_read_setup(req)) >= 0) { page->count++; nfsiod_enqueue(req); } else { dprintk("NFS: deferring async READ request.\n"); nfsiod_release(req); clear_bit(PG_locked, &page->flags); wake_up(&page->wait); } return result < 0? result : 0; } int nfs_readpage(struct inode *inode, struct page *page) { unsigned long address; int error = -1; /* In case we're called from a page fault we want to */ /* make sure we're runnable before we schedule.. */ current->state = TASK_RUNNING; dprintk("NFS: nfs_readpage %08lx\n", page_address(page)); address = page_address(page); page->count++; if (!PageError(page) && 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; } |