Blame view

fs/nfs/read.c 15.4 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
  /*
   * linux/fs/nfs/read.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
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  #include <linux/time.h>
  #include <linux/kernel.h>
  #include <linux/errno.h>
  #include <linux/fcntl.h>
  #include <linux/stat.h>
  #include <linux/mm.h>
  #include <linux/slab.h>
  #include <linux/pagemap.h>
  #include <linux/sunrpc/clnt.h>
  #include <linux/nfs_fs.h>
  #include <linux/nfs_page.h>
  #include <linux/smp_lock.h>
  
  #include <asm/system.h>
49a70f278   Trond Myklebust   NFS: Cleanup: add...
23
  #include "internal.h"
91d5b4702   Chuck Lever   NFS: add I/O perf...
24
  #include "iostat.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
  #define NFSDBG_FACILITY		NFSDBG_PAGECACHE
8d5658c94   Trond Myklebust   NFS: Fix a buffer...
26
27
  static int nfs_pagein_multi(struct inode *, struct list_head *, unsigned int, size_t, int);
  static int nfs_pagein_one(struct inode *, struct list_head *, unsigned int, size_t, int);
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
28
29
  static const struct rpc_call_ops nfs_read_partial_ops;
  static const struct rpc_call_ops nfs_read_full_ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30

e18b890bb   Christoph Lameter   [PATCH] slab: rem...
31
  static struct kmem_cache *nfs_rdata_cachep;
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
32
  static mempool_t *nfs_rdata_mempool;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
  
  #define MIN_POOL_READ	(32)
8d5658c94   Trond Myklebust   NFS: Fix a buffer...
35
  struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount)
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
36
  {
e6b4f8da3   Christoph Lameter   [PATCH] slab: rem...
37
  	struct nfs_read_data *p = mempool_alloc(nfs_rdata_mempool, GFP_NOFS);
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
38
39
40
41
  
  	if (p) {
  		memset(p, 0, sizeof(*p));
  		INIT_LIST_HEAD(&p->pages);
e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
42
  		p->npages = pagecount;
0d0b5cb36   Chuck Lever   NFS: Optimize all...
43
44
  		if (pagecount <= ARRAY_SIZE(p->page_array))
  			p->pagevec = p->page_array;
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
45
  		else {
0d0b5cb36   Chuck Lever   NFS: Optimize all...
46
47
  			p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_NOFS);
  			if (!p->pagevec) {
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
48
49
50
51
52
53
54
  				mempool_free(p, nfs_rdata_mempool);
  				p = NULL;
  			}
  		}
  	}
  	return p;
  }
5e4424af9   Trond Myklebust   SUNRPC: Remove no...
55
  static void nfs_readdata_free(struct nfs_read_data *p)
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
56
57
58
59
60
  {
  	if (p && (p->pagevec != &p->page_array[0]))
  		kfree(p->pagevec);
  	mempool_free(p, nfs_rdata_mempool);
  }
963d8fe53   Trond Myklebust   RPC: Clean up RPC...
61
  void nfs_readdata_release(void *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
  {
383ba7193   Trond Myklebust   NFS: Fix a deadlo...
63
64
65
66
  	struct nfs_read_data *rdata = data;
  
  	put_nfs_open_context(rdata->args.context);
  	nfs_readdata_free(rdata);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
68
69
  }
  
  static
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
  int nfs_return_empty_page(struct page *page)
  {
eebd2aa35   Christoph Lameter   Pagecache zeroing...
72
  	zero_user(page, 0, PAGE_CACHE_SIZE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
75
76
  	SetPageUptodate(page);
  	unlock_page(page);
  	return 0;
  }
1de3fc12e   Trond Myklebust   NFS: Clean up and...
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
  static void nfs_readpage_truncate_uninitialised_page(struct nfs_read_data *data)
  {
  	unsigned int remainder = data->args.count - data->res.count;
  	unsigned int base = data->args.pgbase + data->res.count;
  	unsigned int pglen;
  	struct page **pages;
  
  	if (data->res.eof == 0 || remainder == 0)
  		return;
  	/*
  	 * Note: "remainder" can never be negative, since we check for
  	 * 	this in the XDR code.
  	 */
  	pages = &data->args.pages[base >> PAGE_CACHE_SHIFT];
  	base &= ~PAGE_CACHE_MASK;
  	pglen = PAGE_CACHE_SIZE - base;
79558f361   Trond Myklebust   NFS: Fix issue wi...
93
94
  	for (;;) {
  		if (remainder <= pglen) {
eebd2aa35   Christoph Lameter   Pagecache zeroing...
95
  			zero_user(*pages, base, remainder);
79558f361   Trond Myklebust   NFS: Fix issue wi...
96
97
  			break;
  		}
eebd2aa35   Christoph Lameter   Pagecache zeroing...
98
  		zero_user(*pages, base, pglen);
79558f361   Trond Myklebust   NFS: Fix issue wi...
99
100
101
102
103
  		pages++;
  		remainder -= pglen;
  		pglen = PAGE_CACHE_SIZE;
  		base = 0;
  	}
1de3fc12e   Trond Myklebust   NFS: Clean up and...
104
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
106
107
108
109
110
  static int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
  		struct page *page)
  {
  	LIST_HEAD(one_request);
  	struct nfs_page	*new;
  	unsigned int len;
49a70f278   Trond Myklebust   NFS: Cleanup: add...
111
  	len = nfs_page_length(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
113
114
115
116
117
118
119
  	if (len == 0)
  		return nfs_return_empty_page(page);
  	new = nfs_create_request(ctx, inode, page, 0, len);
  	if (IS_ERR(new)) {
  		unlock_page(page);
  		return PTR_ERR(new);
  	}
  	if (len < PAGE_CACHE_SIZE)
eebd2aa35   Christoph Lameter   Pagecache zeroing...
120
  		zero_user_segment(page, len, PAGE_CACHE_SIZE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122
  	nfs_list_add_request(new, &one_request);
bcb71bba7   Trond Myklebust   NFS: Another clea...
123
  	if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE)
8d5658c94   Trond Myklebust   NFS: Fix a buffer...
124
  		nfs_pagein_multi(inode, &one_request, 1, len, 0);
bcb71bba7   Trond Myklebust   NFS: Another clea...
125
  	else
8d5658c94   Trond Myklebust   NFS: Fix a buffer...
126
  		nfs_pagein_one(inode, &one_request, 1, len, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
128
129
130
131
132
  	return 0;
  }
  
  static void nfs_readpage_release(struct nfs_page *req)
  {
  	unlock_page(req->wb_page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
  	dprintk("NFS: read done (%s/%Ld %d@%Ld)
  ",
88be9f990   Trond Myklebust   NFS: Replace vfsm...
135
136
  			req->wb_context->path.dentry->d_inode->i_sb->s_id,
  			(long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
  			req->wb_bytes,
  			(long long)req_offset(req));
10d2c46f9   Nick Wilson   [PATCH] NFS: fix ...
139
140
  	nfs_clear_request(req);
  	nfs_release_request(req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
143
144
145
  }
  
  /*
   * Set up the NFS read request struct
   */
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
146
  static int nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
147
  		const struct rpc_call_ops *call_ops,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
148
149
  		unsigned int count, unsigned int offset)
  {
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
150
151
  	struct inode *inode = req->wb_context->path.dentry->d_inode;
  	int swap_flags = IS_SWAPFILE(inode) ? NFS_RPC_SWAPFLAGS : 0;
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
152
  	struct rpc_task *task;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
153
154
155
156
157
  	struct rpc_message msg = {
  		.rpc_argp = &data->args,
  		.rpc_resp = &data->res,
  		.rpc_cred = req->wb_context->cred,
  	};
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
158
  	struct rpc_task_setup task_setup_data = {
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
159
  		.task = &data->task,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
160
  		.rpc_client = NFS_CLIENT(inode),
bdc7f021f   Trond Myklebust   NFS: Clean up the...
161
  		.rpc_message = &msg,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
162
163
  		.callback_ops = call_ops,
  		.callback_data = data,
101070ca2   Trond Myklebust   NFS: Ensure that ...
164
  		.workqueue = nfsiod_workqueue,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
165
166
  		.flags = RPC_TASK_ASYNC | swap_flags,
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
168
  
  	data->req	  = req;
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
169
  	data->inode	  = inode;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
170
  	data->cred	  = msg.rpc_cred;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
173
174
175
176
  
  	data->args.fh     = NFS_FH(inode);
  	data->args.offset = req_offset(req) + offset;
  	data->args.pgbase = req->wb_pgbase + offset;
  	data->args.pages  = data->pagevec;
  	data->args.count  = count;
383ba7193   Trond Myklebust   NFS: Fix a deadlo...
177
  	data->args.context = get_nfs_open_context(req->wb_context);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
179
180
181
  
  	data->res.fattr   = &data->fattr;
  	data->res.count   = count;
  	data->res.eof     = 0;
0e574af1b   Trond Myklebust   NFS: Cleanup init...
182
  	nfs_fattr_init(&data->fattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183

ec06c096e   Trond Myklebust   NFS: Cleanup of N...
184
  	/* Set up the initial task struct. */
bdc7f021f   Trond Myklebust   NFS: Clean up the...
185
  	NFS_PROTO(inode)->read_setup(data, &msg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186

a3f565b1e   Chuck Lever   NFS: fix print fo...
187
188
  	dprintk("NFS: %5u initiated read call (req %s/%Ld, %u bytes @ offset %Lu)
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
190
191
192
193
  			data->task.tk_pid,
  			inode->i_sb->s_id,
  			(long long)NFS_FILEID(inode),
  			count,
  			(unsigned long long)data->args.offset);
bdc7f021f   Trond Myklebust   NFS: Clean up the...
194

077376919   Trond Myklebust   NFS/SUNRPC: Conve...
195
  	task = rpc_run_task(&task_setup_data);
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
196
197
198
199
  	if (IS_ERR(task))
  		return PTR_ERR(task);
  	rpc_put_task(task);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
  }
  
  static void
  nfs_async_read_error(struct list_head *head)
  {
  	struct nfs_page	*req;
  
  	while (!list_empty(head)) {
  		req = nfs_list_entry(head->next);
  		nfs_list_remove_request(req);
  		SetPageError(req->wb_page);
  		nfs_readpage_release(req);
  	}
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
217
218
219
220
221
222
223
224
225
226
227
   * Generate multiple requests to fill a single page.
   *
   * We optimize to reduce the number of read operations on the wire.  If we
   * detect that we're reading a page, or an area of a page, that is past the
   * end of file, we do not generate NFS read operations but just clear the
   * parts of the page that would have come back zero from the server anyway.
   *
   * We rely on the cached value of i_size to make this determination; another
   * client can fill pages on the server past our cached end-of-file, but we
   * won't see the new data until our attribute cache is updated.  This is more
   * or less conventional NFS client behavior.
   */
8d5658c94   Trond Myklebust   NFS: Fix a buffer...
228
  static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
231
232
  {
  	struct nfs_page *req = nfs_list_entry(head->next);
  	struct page *page = req->wb_page;
  	struct nfs_read_data *data;
e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
233
234
  	size_t rsize = NFS_SERVER(inode)->rsize, nbytes;
  	unsigned int offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
  	int requests = 0;
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
236
  	int ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
238
239
  	LIST_HEAD(list);
  
  	nfs_list_remove_request(req);
bcb71bba7   Trond Myklebust   NFS: Another clea...
240
  	nbytes = count;
e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
241
242
  	do {
  		size_t len = min(nbytes,rsize);
8d5658c94   Trond Myklebust   NFS: Fix a buffer...
243
  		data = nfs_readdata_alloc(1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
  		if (!data)
  			goto out_bad;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
247
  		list_add(&data->pages, &list);
  		requests++;
e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
248
249
  		nbytes -= len;
  	} while(nbytes != 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
251
252
253
  	atomic_set(&req->wb_complete, requests);
  
  	ClearPageError(page);
  	offset = 0;
bcb71bba7   Trond Myklebust   NFS: Another clea...
254
  	nbytes = count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
  	do {
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
256
  		int ret2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
258
259
260
  		data = list_entry(list.next, struct nfs_read_data, pages);
  		list_del_init(&data->pages);
  
  		data->pagevec[0] = page;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261

bcb71bba7   Trond Myklebust   NFS: Another clea...
262
263
  		if (nbytes < rsize)
  			rsize = nbytes;
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
264
  		ret2 = nfs_read_rpcsetup(req, data, &nfs_read_partial_ops,
bcb71bba7   Trond Myklebust   NFS: Another clea...
265
  				  rsize, offset);
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
266
267
  		if (ret == 0)
  			ret = ret2;
bcb71bba7   Trond Myklebust   NFS: Another clea...
268
269
  		offset += rsize;
  		nbytes -= rsize;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
  	} while (nbytes != 0);
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
271
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
273
274
275
276
277
278
279
280
281
282
  
  out_bad:
  	while (!list_empty(&list)) {
  		data = list_entry(list.next, struct nfs_read_data, pages);
  		list_del(&data->pages);
  		nfs_readdata_free(data);
  	}
  	SetPageError(page);
  	nfs_readpage_release(req);
  	return -ENOMEM;
  }
8d5658c94   Trond Myklebust   NFS: Fix a buffer...
283
  static int nfs_pagein_one(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
285
286
287
  {
  	struct nfs_page		*req;
  	struct page		**pages;
  	struct nfs_read_data	*data;
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
288
  	int ret = -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289

8d5658c94   Trond Myklebust   NFS: Fix a buffer...
290
  	data = nfs_readdata_alloc(npages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
292
  	if (!data)
  		goto out_bad;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
  	pages = data->pagevec;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
295
296
297
298
299
  	while (!list_empty(head)) {
  		req = nfs_list_entry(head->next);
  		nfs_list_remove_request(req);
  		nfs_list_add_request(req, &data->pages);
  		ClearPageError(req->wb_page);
  		*pages++ = req->wb_page;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
  	}
  	req = nfs_list_entry(data->pages.next);
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
302
  	return nfs_read_rpcsetup(req, data, &nfs_read_full_ops, count, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
  out_bad:
  	nfs_async_read_error(head);
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
305
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
  /*
0b6713014   Trond Myklebust   NFS: Fix asynchro...
308
309
310
311
312
313
   * This is the callback from RPC telling us whether a reply was
   * received or some error occurred (timeout or socket shutdown).
   */
  int nfs_readpage_result(struct rpc_task *task, struct nfs_read_data *data)
  {
  	int status;
3110ff804   Harvey Harrison   nfs: replace rema...
314
315
  	dprintk("NFS: %s: %5u, (status %d)
  ", __func__, task->tk_pid,
0b6713014   Trond Myklebust   NFS: Fix asynchro...
316
317
318
319
320
321
322
323
324
  			task->tk_status);
  
  	status = NFS_PROTO(data->inode)->read_done(task, data);
  	if (status != 0)
  		return status;
  
  	nfs_add_stats(data->inode, NFSIOS_SERVERREADBYTES, data->res.count);
  
  	if (task->tk_status == -ESTALE) {
3a10c30ac   Benny Halevy   nfs: obliterate N...
325
  		set_bit(NFS_INO_STALE, &NFS_I(data->inode)->flags);
0b6713014   Trond Myklebust   NFS: Fix asynchro...
326
327
  		nfs_mark_for_revalidate(data->inode);
  	}
0b6713014   Trond Myklebust   NFS: Fix asynchro...
328
329
  	return 0;
  }
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
330
  static void nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data)
0b6713014   Trond Myklebust   NFS: Fix asynchro...
331
332
333
334
335
  {
  	struct nfs_readargs *argp = &data->args;
  	struct nfs_readres *resp = &data->res;
  
  	if (resp->eof || resp->count == argp->count)
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
336
  		return;
0b6713014   Trond Myklebust   NFS: Fix asynchro...
337
338
339
340
341
  
  	/* This is a short read! */
  	nfs_inc_stats(data->inode, NFSIOS_SHORTREAD);
  	/* Has the server at least made some progress? */
  	if (resp->count == 0)
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
342
  		return;
0b6713014   Trond Myklebust   NFS: Fix asynchro...
343
344
345
346
347
348
  
  	/* Yes, so retry the read at the end of the data */
  	argp->offset += resp->count;
  	argp->pgbase += resp->count;
  	argp->count -= resp->count;
  	rpc_restart_call(task);
0b6713014   Trond Myklebust   NFS: Fix asynchro...
349
350
351
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352
353
   * Handle a read reply that fills part of a page.
   */
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
354
  static void nfs_readpage_result_partial(struct rpc_task *task, void *calldata)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355
  {
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
356
  	struct nfs_read_data *data = calldata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
   
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
358
359
  	if (nfs_readpage_result(task, data) != 0)
  		return;
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
360
361
  	if (task->tk_status < 0)
  		return;
0b6713014   Trond Myklebust   NFS: Fix asynchro...
362

fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
363
364
365
366
367
368
369
370
371
372
373
374
  	nfs_readpage_truncate_uninitialised_page(data);
  	nfs_readpage_retry(task, data);
  }
  
  static void nfs_readpage_release_partial(void *calldata)
  {
  	struct nfs_read_data *data = calldata;
  	struct nfs_page *req = data->req;
  	struct page *page = req->wb_page;
  	int status = data->task.tk_status;
  
  	if (status < 0)
0b6713014   Trond Myklebust   NFS: Fix asynchro...
375
  		SetPageError(page);
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
376

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
379
380
381
  	if (atomic_dec_and_test(&req->wb_complete)) {
  		if (!PageError(page))
  			SetPageUptodate(page);
  		nfs_readpage_release(req);
  	}
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
382
  	nfs_readdata_release(calldata);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
  }
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
384
385
  static const struct rpc_call_ops nfs_read_partial_ops = {
  	.rpc_call_done = nfs_readpage_result_partial,
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
386
  	.rpc_release = nfs_readpage_release_partial,
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
387
  };
1de3fc12e   Trond Myklebust   NFS: Clean up and...
388
389
390
391
392
  static void nfs_readpage_set_pages_uptodate(struct nfs_read_data *data)
  {
  	unsigned int count = data->res.count;
  	unsigned int base = data->args.pgbase;
  	struct page **pages;
79558f361   Trond Myklebust   NFS: Fix issue wi...
393
394
  	if (data->res.eof)
  		count = data->args.count;
1de3fc12e   Trond Myklebust   NFS: Clean up and...
395
396
397
398
399
400
401
  	if (unlikely(count == 0))
  		return;
  	pages = &data->args.pages[base >> PAGE_CACHE_SHIFT];
  	base &= ~PAGE_CACHE_MASK;
  	count += base;
  	for (;count >= PAGE_CACHE_SIZE; count -= PAGE_CACHE_SIZE, pages++)
  		SetPageUptodate(*pages);
0b6713014   Trond Myklebust   NFS: Fix asynchro...
402
403
404
405
  	if (count == 0)
  		return;
  	/* Was this a short read? */
  	if (data->res.eof || data->res.count == data->args.count)
1de3fc12e   Trond Myklebust   NFS: Clean up and...
406
407
  		SetPageUptodate(*pages);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
409
410
411
  /*
   * This is the callback from RPC telling us whether a reply was
   * received or some error occurred (timeout or socket shutdown).
   */
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
412
  static void nfs_readpage_result_full(struct rpc_task *task, void *calldata)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
  {
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
414
  	struct nfs_read_data *data = calldata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
415

0b6713014   Trond Myklebust   NFS: Fix asynchro...
416
417
  	if (nfs_readpage_result(task, data) != 0)
  		return;
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
418
419
  	if (task->tk_status < 0)
  		return;
1de3fc12e   Trond Myklebust   NFS: Clean up and...
420
  	/*
0b6713014   Trond Myklebust   NFS: Fix asynchro...
421
  	 * Note: nfs_readpage_retry may change the values of
1de3fc12e   Trond Myklebust   NFS: Clean up and...
422
  	 * data->args. In the multi-page case, we therefore need
0b6713014   Trond Myklebust   NFS: Fix asynchro...
423
424
  	 * to ensure that we call nfs_readpage_set_pages_uptodate()
  	 * first.
1de3fc12e   Trond Myklebust   NFS: Clean up and...
425
  	 */
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
426
427
428
429
430
431
432
433
  	nfs_readpage_truncate_uninitialised_page(data);
  	nfs_readpage_set_pages_uptodate(data);
  	nfs_readpage_retry(task, data);
  }
  
  static void nfs_readpage_release_full(void *calldata)
  {
  	struct nfs_read_data *data = calldata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434
435
  	while (!list_empty(&data->pages)) {
  		struct nfs_page *req = nfs_list_entry(data->pages.next);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
436

1de3fc12e   Trond Myklebust   NFS: Clean up and...
437
  		nfs_list_remove_request(req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438
439
  		nfs_readpage_release(req);
  	}
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
440
  	nfs_readdata_release(calldata);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
441
  }
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
442
443
  static const struct rpc_call_ops nfs_read_full_ops = {
  	.rpc_call_done = nfs_readpage_result_full,
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
444
  	.rpc_release = nfs_readpage_release_full,
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
445
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
448
449
450
451
452
453
454
455
456
457
458
459
460
   * Read a page over NFS.
   * We read the page synchronously in the following case:
   *  -	The error flag is set for this page. This happens only when a
   *	previous async read operation failed.
   */
  int nfs_readpage(struct file *file, struct page *page)
  {
  	struct nfs_open_context *ctx;
  	struct inode *inode = page->mapping->host;
  	int		error;
  
  	dprintk("NFS: nfs_readpage (%p %ld@%lu)
  ",
  		page, PAGE_CACHE_SIZE, page->index);
91d5b4702   Chuck Lever   NFS: add I/O perf...
461
462
  	nfs_inc_stats(inode, NFSIOS_VFSREADPAGE);
  	nfs_add_stats(inode, NFSIOS_READPAGES, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
464
465
466
467
468
469
470
471
  	/*
  	 * 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)
de05a0cc2   Trond Myklebust   NFS: Minor read o...
472
473
474
  		goto out_unlock;
  	if (PageUptodate(page))
  		goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475

5f004cf2a   Trond Myklebust   NFS: Make read() ...
476
477
  	error = -ESTALE;
  	if (NFS_STALE(inode))
de05a0cc2   Trond Myklebust   NFS: Minor read o...
478
  		goto out_unlock;
5f004cf2a   Trond Myklebust   NFS: Make read() ...
479

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480
  	if (file == NULL) {
cf1308ff7   Trond Myklebust   NFS: Fix missing ...
481
  		error = -EBADF;
d530838bf   Trond Myklebust   NFSv4: Fix proble...
482
  		ctx = nfs_find_open_context(inode, NULL, FMODE_READ);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
  		if (ctx == NULL)
de05a0cc2   Trond Myklebust   NFS: Minor read o...
484
  			goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
  	} else
cd3758e37   Trond Myklebust   NFS: Replace file...
486
  		ctx = get_nfs_open_context(nfs_file_open_context(file));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
487

8e0969f04   Trond Myklebust   NFS: Remove nfs_r...
488
  	error = nfs_readpage_async(ctx, inode, page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
490
  	put_nfs_open_context(ctx);
  	return error;
de05a0cc2   Trond Myklebust   NFS: Minor read o...
491
  out_unlock:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
492
493
494
495
496
  	unlock_page(page);
  	return error;
  }
  
  struct nfs_readdesc {
8b09bee30   Trond Myklebust   NFS: Cleanup for ...
497
  	struct nfs_pageio_descriptor *pgio;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
498
499
500
501
502
503
504
505
506
507
  	struct nfs_open_context *ctx;
  };
  
  static int
  readpage_async_filler(void *data, struct page *page)
  {
  	struct nfs_readdesc *desc = (struct nfs_readdesc *)data;
  	struct inode *inode = page->mapping->host;
  	struct nfs_page *new;
  	unsigned int len;
de05a0cc2   Trond Myklebust   NFS: Minor read o...
508
509
510
511
512
513
514
  	int error;
  
  	error = nfs_wb_page(inode, page);
  	if (error)
  		goto out_unlock;
  	if (PageUptodate(page))
  		goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515

49a70f278   Trond Myklebust   NFS: Cleanup: add...
516
  	len = nfs_page_length(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
517
518
  	if (len == 0)
  		return nfs_return_empty_page(page);
de05a0cc2   Trond Myklebust   NFS: Minor read o...
519

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520
  	new = nfs_create_request(desc->ctx, inode, page, 0, len);
de05a0cc2   Trond Myklebust   NFS: Minor read o...
521
522
  	if (IS_ERR(new))
  		goto out_error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
  	if (len < PAGE_CACHE_SIZE)
eebd2aa35   Christoph Lameter   Pagecache zeroing...
524
  		zero_user_segment(page, len, PAGE_CACHE_SIZE);
f8512ad0d   Fred Isaman   nfs: don't ignore...
525
526
527
528
  	if (!nfs_pageio_add_request(desc->pgio, new)) {
  		error = desc->pgio->pg_error;
  		goto out_unlock;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529
  	return 0;
de05a0cc2   Trond Myklebust   NFS: Minor read o...
530
531
532
533
534
535
  out_error:
  	error = PTR_ERR(new);
  	SetPageError(page);
  out_unlock:
  	unlock_page(page);
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
536
537
538
539
540
  }
  
  int nfs_readpages(struct file *filp, struct address_space *mapping,
  		struct list_head *pages, unsigned nr_pages)
  {
8b09bee30   Trond Myklebust   NFS: Cleanup for ...
541
  	struct nfs_pageio_descriptor pgio;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
542
  	struct nfs_readdesc desc = {
8b09bee30   Trond Myklebust   NFS: Cleanup for ...
543
  		.pgio = &pgio,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
544
545
546
  	};
  	struct inode *inode = mapping->host;
  	struct nfs_server *server = NFS_SERVER(inode);
8b09bee30   Trond Myklebust   NFS: Cleanup for ...
547
548
  	size_t rsize = server->rsize;
  	unsigned long npages;
5f004cf2a   Trond Myklebust   NFS: Make read() ...
549
  	int ret = -ESTALE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
551
552
553
554
555
  
  	dprintk("NFS: nfs_readpages (%s/%Ld %d)
  ",
  			inode->i_sb->s_id,
  			(long long)NFS_FILEID(inode),
  			nr_pages);
91d5b4702   Chuck Lever   NFS: add I/O perf...
556
  	nfs_inc_stats(inode, NFSIOS_VFSREADPAGES);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
557

5f004cf2a   Trond Myklebust   NFS: Make read() ...
558
559
  	if (NFS_STALE(inode))
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
560
  	if (filp == NULL) {
d530838bf   Trond Myklebust   NFSv4: Fix proble...
561
  		desc.ctx = nfs_find_open_context(inode, NULL, FMODE_READ);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562
563
564
  		if (desc.ctx == NULL)
  			return -EBADF;
  	} else
cd3758e37   Trond Myklebust   NFS: Replace file...
565
  		desc.ctx = get_nfs_open_context(nfs_file_open_context(filp));
8b09bee30   Trond Myklebust   NFS: Cleanup for ...
566
567
568
569
  	if (rsize < PAGE_CACHE_SIZE)
  		nfs_pageio_init(&pgio, inode, nfs_pagein_multi, rsize, 0);
  	else
  		nfs_pageio_init(&pgio, inode, nfs_pagein_one, rsize, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
  	ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc);
8b09bee30   Trond Myklebust   NFS: Cleanup for ...
571
572
573
574
  
  	nfs_pageio_complete(&pgio);
  	npages = (pgio.pg_bytes_written + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
  	nfs_add_stats(inode, NFSIOS_READPAGES, npages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
575
  	put_nfs_open_context(desc.ctx);
5f004cf2a   Trond Myklebust   NFS: Make read() ...
576
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
577
578
  	return ret;
  }
f7b422b17   David Howells   NFS: Split fs/nfs...
579
  int __init nfs_init_readpagecache(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
580
581
582
583
  {
  	nfs_rdata_cachep = kmem_cache_create("nfs_read_data",
  					     sizeof(struct nfs_read_data),
  					     0, SLAB_HWCACHE_ALIGN,
20c2df83d   Paul Mundt   mm: Remove slab d...
584
  					     NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
585
586
  	if (nfs_rdata_cachep == NULL)
  		return -ENOMEM;
93d2341c7   Matthew Dobson   [PATCH] mempool: ...
587
588
  	nfs_rdata_mempool = mempool_create_slab_pool(MIN_POOL_READ,
  						     nfs_rdata_cachep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
589
590
591
592
593
  	if (nfs_rdata_mempool == NULL)
  		return -ENOMEM;
  
  	return 0;
  }
266bee886   David Brownell   [PATCH] fix stati...
594
  void nfs_destroy_readpagecache(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595
596
  {
  	mempool_destroy(nfs_rdata_mempool);
1a1d92c10   Alexey Dobriyan   [PATCH] Really ig...
597
  	kmem_cache_destroy(nfs_rdata_cachep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
  }