Blame view

fs/nfs/read.c 17.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
  #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>
64419a9b2   Andy Adamson   NFSv4.1: generic ...
20
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
  
  #include <asm/system.h>
bae724ef9   Fred Isaman   NFSv4.1: shift pn...
23
  #include "pnfs.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24

f11c88af2   Andy Adamson   nfs41: read seque...
25
  #include "nfs4_fs.h"
49a70f278   Trond Myklebust   NFS: Cleanup: add...
26
  #include "internal.h"
91d5b4702   Chuck Lever   NFS: add I/O perf...
27
  #include "iostat.h"
9a9fc1c03   David Howells   NFS: Read pages f...
28
  #include "fscache.h"
91d5b4702   Chuck Lever   NFS: add I/O perf...
29

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
  #define NFSDBG_FACILITY		NFSDBG_PAGECACHE
1751c3638   Trond Myklebust   NFS: Cleanup of t...
31
  static const struct nfs_pageio_ops nfs_pageio_read_ops;
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
32
33
  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
34

e18b890bb   Christoph Lameter   [PATCH] slab: rem...
35
  static struct kmem_cache *nfs_rdata_cachep;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36

8d5658c94   Trond Myklebust   NFS: Fix a buffer...
37
  struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount)
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
38
  {
b6ee8cd26   Trond Myklebust   NFS: Get rid of t...
39
  	struct nfs_read_data *p;
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
40

b6ee8cd26   Trond Myklebust   NFS: Get rid of t...
41
  	p = kmem_cache_zalloc(nfs_rdata_cachep, GFP_KERNEL);
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
42
  	if (p) {
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
43
  		INIT_LIST_HEAD(&p->pages);
e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
44
  		p->npages = pagecount;
0d0b5cb36   Chuck Lever   NFS: Optimize all...
45
46
  		if (pagecount <= ARRAY_SIZE(p->page_array))
  			p->pagevec = p->page_array;
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
47
  		else {
93870d76f   Trond Myklebust   NFS: Read request...
48
  			p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_KERNEL);
0d0b5cb36   Chuck Lever   NFS: Optimize all...
49
  			if (!p->pagevec) {
b6ee8cd26   Trond Myklebust   NFS: Get rid of t...
50
  				kmem_cache_free(nfs_rdata_cachep, p);
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
51
52
53
54
55
56
  				p = NULL;
  			}
  		}
  	}
  	return p;
  }
1ae88b2e4   Trond Myklebust   NFS: Fix an O_DIR...
57
  void nfs_readdata_free(struct nfs_read_data *p)
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
58
59
60
  {
  	if (p && (p->pagevec != &p->page_array[0]))
  		kfree(p->pagevec);
b6ee8cd26   Trond Myklebust   NFS: Get rid of t...
61
  	kmem_cache_free(nfs_rdata_cachep, p);
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
62
  }
493292ddc   Trond Myklebust   NFS: Move the pnf...
63
  void nfs_readdata_release(struct nfs_read_data *rdata)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
  {
bae724ef9   Fred Isaman   NFSv4.1: shift pn...
65
  	put_lseg(rdata->lseg);
383ba7193   Trond Myklebust   NFS: Fix a deadlo...
66
67
  	put_nfs_open_context(rdata->args.context);
  	nfs_readdata_free(rdata);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
70
  }
  
  static
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
  int nfs_return_empty_page(struct page *page)
  {
eebd2aa35   Christoph Lameter   Pagecache zeroing...
73
  	zero_user(page, 0, PAGE_CACHE_SIZE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
76
77
  	SetPageUptodate(page);
  	unlock_page(page);
  	return 0;
  }
1de3fc12e   Trond Myklebust   NFS: Clean up and...
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  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...
94
95
  	for (;;) {
  		if (remainder <= pglen) {
eebd2aa35   Christoph Lameter   Pagecache zeroing...
96
  			zero_user(*pages, base, remainder);
79558f361   Trond Myklebust   NFS: Fix issue wi...
97
98
  			break;
  		}
eebd2aa35   Christoph Lameter   Pagecache zeroing...
99
  		zero_user(*pages, base, pglen);
79558f361   Trond Myklebust   NFS: Fix issue wi...
100
101
102
103
104
  		pages++;
  		remainder -= pglen;
  		pglen = PAGE_CACHE_SIZE;
  		base = 0;
  	}
1de3fc12e   Trond Myklebust   NFS: Clean up and...
105
  }
62e4a7698   Trond Myklebust   NFS: Revert pnfs ...
106
  void nfs_pageio_init_read_mds(struct nfs_pageio_descriptor *pgio,
1751c3638   Trond Myklebust   NFS: Cleanup of t...
107
108
109
110
111
  		struct inode *inode)
  {
  	nfs_pageio_init(pgio, inode, &nfs_pageio_read_ops,
  			NFS_SERVER(inode)->rsize, 0);
  }
493292ddc   Trond Myklebust   NFS: Move the pnf...
112
113
114
115
116
  void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio)
  {
  	pgio->pg_ops = &nfs_pageio_read_ops;
  	pgio->pg_bsize = NFS_SERVER(pgio->pg_inode)->rsize;
  }
1f9453578   Trond Myklebust   NFS: Clean up - s...
117
  EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds);
493292ddc   Trond Myklebust   NFS: Move the pnf...
118

1751c3638   Trond Myklebust   NFS: Cleanup of t...
119
120
121
122
123
124
  static void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio,
  		struct inode *inode)
  {
  	if (!pnfs_pageio_init_read(pgio, inode))
  		nfs_pageio_init_read_mds(pgio, inode);
  }
f42b293d6   David Howells   NFS: nfs_readpage...
125
126
  int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
  		       struct page *page)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
  	struct nfs_page	*new;
  	unsigned int len;
c76069bda   Fred Isaman   NFSv4.1: rearrang...
130
  	struct nfs_pageio_descriptor pgio;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131

49a70f278   Trond Myklebust   NFS: Cleanup: add...
132
  	len = nfs_page_length(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
135
136
137
138
139
140
  	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...
141
  		zero_user_segment(page, len, PAGE_CACHE_SIZE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142

1751c3638   Trond Myklebust   NFS: Cleanup of t...
143
  	nfs_pageio_init_read(&pgio, inode);
d8007d4dd   Trond Myklebust   NFSv4.1: Add an i...
144
  	nfs_pageio_add_request(&pgio, new);
1751c3638   Trond Myklebust   NFS: Cleanup of t...
145
  	nfs_pageio_complete(&pgio);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
147
148
149
150
  	return 0;
  }
  
  static void nfs_readpage_release(struct nfs_page *req)
  {
3d4ff43d8   Al Viro   nfs_open_context ...
151
  	struct inode *d_inode = req->wb_context->dentry->d_inode;
7f8e05f60   David Howells   NFS: Store pages ...
152
153
154
  
  	if (PageUptodate(req->wb_page))
  		nfs_readpage_to_fscache(d_inode, req->wb_page, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
  	unlock_page(req->wb_page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
  	dprintk("NFS: read done (%s/%Ld %d@%Ld)
  ",
3d4ff43d8   Al Viro   nfs_open_context ...
158
159
  			req->wb_context->dentry->d_inode->i_sb->s_id,
  			(long long)NFS_FILEID(req->wb_context->dentry->d_inode),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
  			req->wb_bytes,
  			(long long)req_offset(req));
10d2c46f9   Nick Wilson   [PATCH] NFS: fix ...
162
  	nfs_release_request(req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
  }
dc70d7b31   Andy Adamson   NFSv4.1: filelayo...
164
  int nfs_initiate_read(struct nfs_read_data *data, struct rpc_clnt *clnt,
64419a9b2   Andy Adamson   NFSv4.1: generic ...
165
  		      const struct rpc_call_ops *call_ops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
  {
64419a9b2   Andy Adamson   NFSv4.1: generic ...
167
  	struct inode *inode = data->inode;
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
168
  	int swap_flags = IS_SWAPFILE(inode) ? NFS_RPC_SWAPFLAGS : 0;
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
169
  	struct rpc_task *task;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
170
171
172
  	struct rpc_message msg = {
  		.rpc_argp = &data->args,
  		.rpc_resp = &data->res,
64419a9b2   Andy Adamson   NFSv4.1: generic ...
173
  		.rpc_cred = data->cred,
bdc7f021f   Trond Myklebust   NFS: Clean up the...
174
  	};
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
175
  	struct rpc_task_setup task_setup_data = {
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
176
  		.task = &data->task,
64419a9b2   Andy Adamson   NFSv4.1: generic ...
177
  		.rpc_client = clnt,
bdc7f021f   Trond Myklebust   NFS: Clean up the...
178
  		.rpc_message = &msg,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
179
180
  		.callback_ops = call_ops,
  		.callback_data = data,
101070ca2   Trond Myklebust   NFS: Ensure that ...
181
  		.workqueue = nfsiod_workqueue,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
182
183
  		.flags = RPC_TASK_ASYNC | swap_flags,
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184

64419a9b2   Andy Adamson   NFSv4.1: generic ...
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
  	/* Set up the initial task struct. */
  	NFS_PROTO(inode)->read_setup(data, &msg);
  
  	dprintk("NFS: %5u initiated read call (req %s/%lld, %u bytes @ "
  			"offset %llu)
  ",
  			data->task.tk_pid,
  			inode->i_sb->s_id,
  			(long long)NFS_FILEID(inode),
  			data->args.count,
  			(unsigned long long)data->args.offset);
  
  	task = rpc_run_task(&task_setup_data);
  	if (IS_ERR(task))
  		return PTR_ERR(task);
  	rpc_put_task(task);
  	return 0;
  }
dc70d7b31   Andy Adamson   NFSv4.1: filelayo...
203
  EXPORT_SYMBOL_GPL(nfs_initiate_read);
64419a9b2   Andy Adamson   NFSv4.1: generic ...
204
205
206
207
  
  /*
   * Set up the NFS read request struct
   */
6e4efd568   Trond Myklebust   NFS: Clean up nfs...
208
209
  static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
  		unsigned int count, unsigned int offset)
64419a9b2   Andy Adamson   NFSv4.1: generic ...
210
  {
3d4ff43d8   Al Viro   nfs_open_context ...
211
  	struct inode *inode = req->wb_context->dentry->d_inode;
64419a9b2   Andy Adamson   NFSv4.1: generic ...
212

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
  	data->req	  = req;
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
214
  	data->inode	  = inode;
64419a9b2   Andy Adamson   NFSv4.1: generic ...
215
  	data->cred	  = req->wb_context->cred;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
217
218
219
220
221
  
  	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...
222
  	data->args.context = get_nfs_open_context(req->wb_context);
f11ac8db5   Trond Myklebust   NFSv4: Ensure tha...
223
  	data->args.lock_context = req->wb_lock_context;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
225
226
227
  
  	data->res.fattr   = &data->fattr;
  	data->res.count   = count;
  	data->res.eof     = 0;
0e574af1b   Trond Myklebust   NFS: Cleanup init...
228
  	nfs_fattr_init(&data->fattr);
6e4efd568   Trond Myklebust   NFS: Clean up nfs...
229
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230

6e4efd568   Trond Myklebust   NFS: Clean up nfs...
231
  static int nfs_do_read(struct nfs_read_data *data,
493292ddc   Trond Myklebust   NFS: Move the pnf...
232
  		const struct rpc_call_ops *call_ops)
6e4efd568   Trond Myklebust   NFS: Clean up nfs...
233
  {
5f00bcb38   Stephen Rothwell   Merge branch 'mas...
234
  	struct inode *inode = data->args.context->dentry->d_inode;
6e4efd568   Trond Myklebust   NFS: Clean up nfs...
235

64419a9b2   Andy Adamson   NFSv4.1: generic ...
236
  	return nfs_initiate_read(data, NFS_CLIENT(inode), call_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
  }
275acaafd   Trond Myklebust   NFS: Clean up: sp...
238
239
  static int
  nfs_do_multiple_reads(struct list_head *head,
493292ddc   Trond Myklebust   NFS: Move the pnf...
240
  		const struct rpc_call_ops *call_ops)
275acaafd   Trond Myklebust   NFS: Clean up: sp...
241
242
243
244
245
246
247
248
249
  {
  	struct nfs_read_data *data;
  	int ret = 0;
  
  	while (!list_empty(head)) {
  		int ret2;
  
  		data = list_entry(head->next, struct nfs_read_data, list);
  		list_del_init(&data->list);
493292ddc   Trond Myklebust   NFS: Move the pnf...
250
  		ret2 = nfs_do_read(data, call_ops);
275acaafd   Trond Myklebust   NFS: Clean up: sp...
251
252
253
254
255
  		if (ret == 0)
  			ret = ret2;
  	}
  	return ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
257
258
259
260
261
262
263
  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);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
266
267
268
  		nfs_readpage_release(req);
  	}
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
271
272
273
274
275
276
277
278
279
280
   * 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.
   */
275acaafd   Trond Myklebust   NFS: Clean up: sp...
281
  static int nfs_pagein_multi(struct nfs_pageio_descriptor *desc, struct list_head *res)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
  {
c76069bda   Fred Isaman   NFSv4.1: rearrang...
283
  	struct nfs_page *req = nfs_list_entry(desc->pg_list.next);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
285
  	struct page *page = req->wb_page;
  	struct nfs_read_data *data;
d097971d8   Trond Myklebust   NFS: Use the nfs_...
286
  	size_t rsize = desc->pg_bsize, nbytes;
e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
287
  	unsigned int offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
  	int requests = 0;
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
289
  	int ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
291
  
  	nfs_list_remove_request(req);
275acaafd   Trond Myklebust   NFS: Clean up: sp...
292
  	offset = 0;
c76069bda   Fred Isaman   NFSv4.1: rearrang...
293
  	nbytes = desc->pg_count;
e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
294
295
  	do {
  		size_t len = min(nbytes,rsize);
8d5658c94   Trond Myklebust   NFS: Fix a buffer...
296
  		data = nfs_readdata_alloc(1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
298
  		if (!data)
  			goto out_bad;
275acaafd   Trond Myklebust   NFS: Clean up: sp...
299
300
301
  		data->pagevec[0] = page;
  		nfs_read_rpcsetup(req, data, len, offset);
  		list_add(&data->list, res);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
  		requests++;
e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
303
  		nbytes -= len;
275acaafd   Trond Myklebust   NFS: Clean up: sp...
304
  		offset += len;
e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
305
  	} while(nbytes != 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
  	atomic_set(&req->wb_complete, requests);
50828d7e6   Trond Myklebust   NFS: Cache rpc_op...
307
  	desc->pg_rpc_callops = &nfs_read_partial_ops;
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
308
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
  out_bad:
275acaafd   Trond Myklebust   NFS: Clean up: sp...
310
311
  	while (!list_empty(res)) {
  		data = list_entry(res->next, struct nfs_read_data, list);
6e4efd568   Trond Myklebust   NFS: Clean up nfs...
312
  		list_del(&data->list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
314
  		nfs_readdata_free(data);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
316
317
  	nfs_readpage_release(req);
  	return -ENOMEM;
  }
275acaafd   Trond Myklebust   NFS: Clean up: sp...
318
  static int nfs_pagein_one(struct nfs_pageio_descriptor *desc, struct list_head *res)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
320
321
322
  {
  	struct nfs_page		*req;
  	struct page		**pages;
  	struct nfs_read_data	*data;
c76069bda   Fred Isaman   NFSv4.1: rearrang...
323
  	struct list_head *head = &desc->pg_list;
3b6091846   Peng Tao   NFS: fix return v...
324
  	int ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325

c76069bda   Fred Isaman   NFSv4.1: rearrang...
326
327
  	data = nfs_readdata_alloc(nfs_page_array_len(desc->pg_base,
  						     desc->pg_count));
bae724ef9   Fred Isaman   NFSv4.1: shift pn...
328
329
  	if (!data) {
  		nfs_async_read_error(head);
3b6091846   Peng Tao   NFS: fix return v...
330
  		ret = -ENOMEM;
bae724ef9   Fred Isaman   NFSv4.1: shift pn...
331
332
  		goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
  	pages = data->pagevec;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
338
  	while (!list_empty(head)) {
  		req = nfs_list_entry(head->next);
  		nfs_list_remove_request(req);
  		nfs_list_add_request(req, &data->pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
  		*pages++ = req->wb_page;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
341
  	}
  	req = nfs_list_entry(data->pages.next);
6e4efd568   Trond Myklebust   NFS: Clean up nfs...
342
  	nfs_read_rpcsetup(req, data, desc->pg_count, 0);
275acaafd   Trond Myklebust   NFS: Clean up: sp...
343
  	list_add(&data->list, res);
50828d7e6   Trond Myklebust   NFS: Cache rpc_op...
344
  	desc->pg_rpc_callops = &nfs_read_full_ops;
bae724ef9   Fred Isaman   NFSv4.1: shift pn...
345
  out:
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
346
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
  }
493292ddc   Trond Myklebust   NFS: Move the pnf...
348
349
350
351
352
353
354
355
  int nfs_generic_pagein(struct nfs_pageio_descriptor *desc, struct list_head *head)
  {
  	if (desc->pg_bsize < PAGE_CACHE_SIZE)
  		return nfs_pagein_multi(desc, head);
  	return nfs_pagein_one(desc, head);
  }
  
  static int nfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc)
1751c3638   Trond Myklebust   NFS: Cleanup of t...
356
  {
275acaafd   Trond Myklebust   NFS: Clean up: sp...
357
358
  	LIST_HEAD(head);
  	int ret;
493292ddc   Trond Myklebust   NFS: Move the pnf...
359
  	ret = nfs_generic_pagein(desc, &head);
50828d7e6   Trond Myklebust   NFS: Cache rpc_op...
360
  	if (ret == 0)
493292ddc   Trond Myklebust   NFS: Move the pnf...
361
  		ret = nfs_do_multiple_reads(&head, desc->pg_rpc_callops);
275acaafd   Trond Myklebust   NFS: Clean up: sp...
362
  	return ret;
1751c3638   Trond Myklebust   NFS: Cleanup of t...
363
  }
1751c3638   Trond Myklebust   NFS: Cleanup of t...
364
365
366
367
368
  
  static const struct nfs_pageio_ops nfs_pageio_read_ops = {
  	.pg_test = nfs_generic_pg_test,
  	.pg_doio = nfs_generic_pg_readpages,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
  /*
0b6713014   Trond Myklebust   NFS: Fix asynchro...
370
371
372
373
374
375
   * 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...
376
377
  	dprintk("NFS: %s: %5u, (status %d)
  ", __func__, task->tk_pid,
0b6713014   Trond Myklebust   NFS: Fix asynchro...
378
379
380
381
382
383
384
385
386
  			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...
387
  		set_bit(NFS_INO_STALE, &NFS_I(data->inode)->flags);
0b6713014   Trond Myklebust   NFS: Fix asynchro...
388
389
  		nfs_mark_for_revalidate(data->inode);
  	}
0b6713014   Trond Myklebust   NFS: Fix asynchro...
390
391
  	return 0;
  }
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
392
  static void nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data)
0b6713014   Trond Myklebust   NFS: Fix asynchro...
393
394
395
396
397
  {
  	struct nfs_readargs *argp = &data->args;
  	struct nfs_readres *resp = &data->res;
  
  	if (resp->eof || resp->count == argp->count)
d61e612a7   Trond Myklebust   NFSv41: Clean up ...
398
  		return;
0b6713014   Trond Myklebust   NFS: Fix asynchro...
399
400
401
402
403
  
  	/* This is a short read! */
  	nfs_inc_stats(data->inode, NFSIOS_SHORTREAD);
  	/* Has the server at least made some progress? */
  	if (resp->count == 0)
d61e612a7   Trond Myklebust   NFSv41: Clean up ...
404
  		return;
0b6713014   Trond Myklebust   NFS: Fix asynchro...
405
406
  
  	/* Yes, so retry the read at the end of the data */
cbdabc7f8   Andy Adamson   NFSv4.1: filelayo...
407
  	data->mds_offset += resp->count;
0b6713014   Trond Myklebust   NFS: Fix asynchro...
408
409
410
  	argp->offset += resp->count;
  	argp->pgbase += resp->count;
  	argp->count -= resp->count;
d00c5d438   Trond Myklebust   NFS: Get rid of n...
411
  	rpc_restart_call_prepare(task);
0b6713014   Trond Myklebust   NFS: Fix asynchro...
412
413
414
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
415
416
   * Handle a read reply that fills part of a page.
   */
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
417
  static void nfs_readpage_result_partial(struct rpc_task *task, void *calldata)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
  {
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
419
  	struct nfs_read_data *data = calldata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
   
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
421
422
  	if (nfs_readpage_result(task, data) != 0)
  		return;
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
423
424
  	if (task->tk_status < 0)
  		return;
0b6713014   Trond Myklebust   NFS: Fix asynchro...
425

fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
426
427
428
429
430
431
432
433
434
435
436
437
  	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)
fba730050   Trond Myklebust   NFS: Don't rely o...
438
  		set_bit(PG_PARTIAL_READ_FAILED, &req->wb_flags);
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
439

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
440
  	if (atomic_dec_and_test(&req->wb_complete)) {
fba730050   Trond Myklebust   NFS: Don't rely o...
441
  		if (!test_bit(PG_PARTIAL_READ_FAILED, &req->wb_flags))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
443
444
  			SetPageUptodate(page);
  		nfs_readpage_release(req);
  	}
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
445
  	nfs_readdata_release(calldata);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
  }
f11c88af2   Andy Adamson   nfs41: read seque...
447
448
449
450
  #if defined(CONFIG_NFS_V4_1)
  void nfs_read_prepare(struct rpc_task *task, void *calldata)
  {
  	struct nfs_read_data *data = calldata;
035168ab3   Trond Myklebust   NFSv4.1: Make nfs...
451
  	if (nfs4_setup_sequence(NFS_SERVER(data->inode),
f11c88af2   Andy Adamson   nfs41: read seque...
452
453
454
455
456
457
  				&data->args.seq_args, &data->res.seq_res,
  				0, task))
  		return;
  	rpc_call_start(task);
  }
  #endif /* CONFIG_NFS_V4_1 */
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
458
  static const struct rpc_call_ops nfs_read_partial_ops = {
f11c88af2   Andy Adamson   nfs41: read seque...
459
460
461
  #if defined(CONFIG_NFS_V4_1)
  	.rpc_call_prepare = nfs_read_prepare,
  #endif /* CONFIG_NFS_V4_1 */
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
462
  	.rpc_call_done = nfs_readpage_result_partial,
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
463
  	.rpc_release = nfs_readpage_release_partial,
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
464
  };
1de3fc12e   Trond Myklebust   NFS: Clean up and...
465
466
467
468
469
  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...
470
471
  	if (data->res.eof)
  		count = data->args.count;
1de3fc12e   Trond Myklebust   NFS: Clean up and...
472
473
474
475
476
477
478
  	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...
479
480
481
482
  	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...
483
484
  		SetPageUptodate(*pages);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
486
487
488
  /*
   * 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...
489
  static void nfs_readpage_result_full(struct rpc_task *task, void *calldata)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
490
  {
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
491
  	struct nfs_read_data *data = calldata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
492

0b6713014   Trond Myklebust   NFS: Fix asynchro...
493
494
  	if (nfs_readpage_result(task, data) != 0)
  		return;
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
495
496
  	if (task->tk_status < 0)
  		return;
1de3fc12e   Trond Myklebust   NFS: Clean up and...
497
  	/*
0b6713014   Trond Myklebust   NFS: Fix asynchro...
498
  	 * Note: nfs_readpage_retry may change the values of
1de3fc12e   Trond Myklebust   NFS: Clean up and...
499
  	 * data->args. In the multi-page case, we therefore need
0b6713014   Trond Myklebust   NFS: Fix asynchro...
500
501
  	 * to ensure that we call nfs_readpage_set_pages_uptodate()
  	 * first.
1de3fc12e   Trond Myklebust   NFS: Clean up and...
502
  	 */
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
503
504
505
506
507
508
509
510
  	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
511
512
  	while (!list_empty(&data->pages)) {
  		struct nfs_page *req = nfs_list_entry(data->pages.next);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513

1de3fc12e   Trond Myklebust   NFS: Clean up and...
514
  		nfs_list_remove_request(req);
62e4a7698   Trond Myklebust   NFS: Revert pnfs ...
515
  		nfs_readpage_release(req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
516
  	}
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
517
  	nfs_readdata_release(calldata);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
  }
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
519
  static const struct rpc_call_ops nfs_read_full_ops = {
f11c88af2   Andy Adamson   nfs41: read seque...
520
521
522
  #if defined(CONFIG_NFS_V4_1)
  	.rpc_call_prepare = nfs_read_prepare,
  #endif /* CONFIG_NFS_V4_1 */
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
523
  	.rpc_call_done = nfs_readpage_result_full,
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
524
  	.rpc_release = nfs_readpage_release_full,
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
525
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527
528
529
530
531
532
533
534
535
536
537
538
539
540
   * 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...
541
542
  	nfs_inc_stats(inode, NFSIOS_VFSREADPAGE);
  	nfs_add_stats(inode, NFSIOS_READPAGES, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
543
544
545
546
547
548
549
550
551
  	/*
  	 * 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...
552
553
554
  		goto out_unlock;
  	if (PageUptodate(page))
  		goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555

5f004cf2a   Trond Myklebust   NFS: Make read() ...
556
557
  	error = -ESTALE;
  	if (NFS_STALE(inode))
de05a0cc2   Trond Myklebust   NFS: Minor read o...
558
  		goto out_unlock;
5f004cf2a   Trond Myklebust   NFS: Make read() ...
559

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
560
  	if (file == NULL) {
cf1308ff7   Trond Myklebust   NFS: Fix missing ...
561
  		error = -EBADF;
d530838bf   Trond Myklebust   NFSv4: Fix proble...
562
  		ctx = nfs_find_open_context(inode, NULL, FMODE_READ);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563
  		if (ctx == NULL)
de05a0cc2   Trond Myklebust   NFS: Minor read o...
564
  			goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
565
  	} else
cd3758e37   Trond Myklebust   NFS: Replace file...
566
  		ctx = get_nfs_open_context(nfs_file_open_context(file));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567

9a9fc1c03   David Howells   NFS: Read pages f...
568
569
570
571
572
  	if (!IS_SYNC(inode)) {
  		error = nfs_readpage_from_fscache(ctx, inode, page);
  		if (error == 0)
  			goto out;
  	}
8e0969f04   Trond Myklebust   NFS: Remove nfs_r...
573
  	error = nfs_readpage_async(ctx, inode, page);
9a9fc1c03   David Howells   NFS: Read pages f...
574
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
575
576
  	put_nfs_open_context(ctx);
  	return error;
de05a0cc2   Trond Myklebust   NFS: Minor read o...
577
  out_unlock:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
578
579
580
581
582
  	unlock_page(page);
  	return error;
  }
  
  struct nfs_readdesc {
8b09bee30   Trond Myklebust   NFS: Cleanup for ...
583
  	struct nfs_pageio_descriptor *pgio;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
584
585
586
587
588
589
590
591
592
593
  	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...
594
  	int error;
49a70f278   Trond Myklebust   NFS: Cleanup: add...
595
  	len = nfs_page_length(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596
597
  	if (len == 0)
  		return nfs_return_empty_page(page);
de05a0cc2   Trond Myklebust   NFS: Minor read o...
598

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
599
  	new = nfs_create_request(desc->ctx, inode, page, 0, len);
de05a0cc2   Trond Myklebust   NFS: Minor read o...
600
601
  	if (IS_ERR(new))
  		goto out_error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
602
  	if (len < PAGE_CACHE_SIZE)
eebd2aa35   Christoph Lameter   Pagecache zeroing...
603
  		zero_user_segment(page, len, PAGE_CACHE_SIZE);
f8512ad0d   Fred Isaman   nfs: don't ignore...
604
605
606
607
  	if (!nfs_pageio_add_request(desc->pgio, new)) {
  		error = desc->pgio->pg_error;
  		goto out_unlock;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608
  	return 0;
de05a0cc2   Trond Myklebust   NFS: Minor read o...
609
610
  out_error:
  	error = PTR_ERR(new);
de05a0cc2   Trond Myklebust   NFS: Minor read o...
611
612
613
  out_unlock:
  	unlock_page(page);
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
614
615
616
617
618
  }
  
  int nfs_readpages(struct file *filp, struct address_space *mapping,
  		struct list_head *pages, unsigned nr_pages)
  {
8b09bee30   Trond Myklebust   NFS: Cleanup for ...
619
  	struct nfs_pageio_descriptor pgio;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
620
  	struct nfs_readdesc desc = {
8b09bee30   Trond Myklebust   NFS: Cleanup for ...
621
  		.pgio = &pgio,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622
623
  	};
  	struct inode *inode = mapping->host;
8b09bee30   Trond Myklebust   NFS: Cleanup for ...
624
  	unsigned long npages;
5f004cf2a   Trond Myklebust   NFS: Make read() ...
625
  	int ret = -ESTALE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
626
627
628
629
630
631
  
  	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...
632
  	nfs_inc_stats(inode, NFSIOS_VFSREADPAGES);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
633

5f004cf2a   Trond Myklebust   NFS: Make read() ...
634
635
  	if (NFS_STALE(inode))
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
  	if (filp == NULL) {
d530838bf   Trond Myklebust   NFSv4: Fix proble...
637
  		desc.ctx = nfs_find_open_context(inode, NULL, FMODE_READ);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
639
640
  		if (desc.ctx == NULL)
  			return -EBADF;
  	} else
cd3758e37   Trond Myklebust   NFS: Replace file...
641
  		desc.ctx = get_nfs_open_context(nfs_file_open_context(filp));
9a9fc1c03   David Howells   NFS: Read pages f...
642
643
644
645
646
647
648
649
  
  	/* attempt to read as many of the pages as possible from the cache
  	 * - this returns -ENOBUFS immediately if the cookie is negative
  	 */
  	ret = nfs_readpages_from_fscache(desc.ctx, inode, mapping,
  					 pages, &nr_pages);
  	if (ret == 0)
  		goto read_complete; /* all pages were read */
1751c3638   Trond Myklebust   NFS: Cleanup of t...
650
  	nfs_pageio_init_read(&pgio, inode);
8b09bee30   Trond Myklebust   NFS: Cleanup for ...
651

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
652
  	ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc);
8b09bee30   Trond Myklebust   NFS: Cleanup for ...
653
654
655
656
  
  	nfs_pageio_complete(&pgio);
  	npages = (pgio.pg_bytes_written + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
  	nfs_add_stats(inode, NFSIOS_READPAGES, npages);
9a9fc1c03   David Howells   NFS: Read pages f...
657
  read_complete:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658
  	put_nfs_open_context(desc.ctx);
5f004cf2a   Trond Myklebust   NFS: Make read() ...
659
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660
661
  	return ret;
  }
f7b422b17   David Howells   NFS: Split fs/nfs...
662
  int __init nfs_init_readpagecache(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
663
664
665
666
  {
  	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...
667
  					     NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
668
669
  	if (nfs_rdata_cachep == NULL)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
670
671
  	return 0;
  }
266bee886   David Brownell   [PATCH] fix stati...
672
  void nfs_destroy_readpagecache(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
673
  {
1a1d92c10   Alexey Dobriyan   [PATCH] Really ig...
674
  	kmem_cache_destroy(nfs_rdata_cachep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
675
  }