Blame view

fs/nfs/direct.c 27.6 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
  /*
   * linux/fs/nfs/direct.c
   *
   * Copyright (C) 2003 by Chuck Lever <cel@netapp.com>
   *
   * High-performance uncached I/O for the Linux NFS client
   *
   * There are important applications whose performance or correctness
   * depends on uncached access to file data.  Database clusters
88467055f   Chuck Lever   NFS: clean up com...
10
   * (multiple copies of the same instance running on separate hosts)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
   * implement their own cache coherency protocol that subsumes file
88467055f   Chuck Lever   NFS: clean up com...
12
13
14
   * system cache protocols.  Applications that process datasets
   * considerably larger than the client's memory do not always benefit
   * from a local cache.  A streaming video server, for instance, has no
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
   * need to cache the contents of a file.
   *
   * When an application requests uncached I/O, all read and write requests
   * are made directly to the server; data stored or fetched via these
   * requests is not cached in the Linux page cache.  The client does not
   * correct unaligned requests from applications.  All requested bytes are
   * held on permanent storage before a direct write system call returns to
   * an application.
   *
   * Solaris implements an uncached I/O facility called directio() that
   * is used for backups and sequential I/O to very large files.  Solaris
   * also supports uncaching whole NFS partitions with "-o forcedirectio,"
   * an undocumented mount option.
   *
   * Designed by Jeff Kimmel, Chuck Lever, and Trond Myklebust, with
   * help from Andrew Morton.
   *
   * 18 Dec 2001	Initial implementation for 2.4  --cel
   * 08 Jul 2002	Version for 2.4.19, with bug fixes --trondmy
   * 08 Jun 2003	Port to 2.5 APIs  --cel
   * 31 Mar 2004	Handle direct I/O without VFS support  --cel
   * 15 Sep 2004	Parallel async reads  --cel
88467055f   Chuck Lever   NFS: clean up com...
37
   * 04 May 2005	support O_DIRECT with aio  --cel
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
41
42
  #include <linux/errno.h>
  #include <linux/sched.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
45
  #include <linux/file.h>
  #include <linux/pagemap.h>
  #include <linux/kref.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
46
  #include <linux/slab.h>
7ec10f26e   Konstantin Khlebnikov   NFS: account dire...
47
  #include <linux/task_io_accounting_ops.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
50
51
52
53
54
  
  #include <linux/nfs_fs.h>
  #include <linux/nfs_page.h>
  #include <linux/sunrpc/clnt.h>
  
  #include <asm/system.h>
  #include <asm/uaccess.h>
60063497a   Arun Sharma   atomic: use <linu...
55
  #include <linux/atomic.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56

8d5658c94   Trond Myklebust   NFS: Fix a buffer...
57
  #include "internal.h"
91d5b4702   Chuck Lever   NFS: add I/O perf...
58
  #include "iostat.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
  #define NFSDBG_FACILITY		NFSDBG_VFS
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60

e18b890bb   Christoph Lameter   [PATCH] slab: rem...
61
  static struct kmem_cache *nfs_direct_cachep;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
65
66
67
  
  /*
   * This represents a set of asynchronous requests that we're waiting on
   */
  struct nfs_direct_req {
  	struct kref		kref;		/* release manager */
15ce4a0c1   Chuck Lever   NFS: Replace atom...
68
69
  
  	/* I/O parameters */
a8881f5a5   Trond Myklebust   NFS: O_DIRECT asy...
70
  	struct nfs_open_context	*ctx;		/* file open context info */
f11ac8db5   Trond Myklebust   NFSv4: Ensure tha...
71
  	struct nfs_lock_context *l_ctx;		/* Lock context info */
99514f8fd   Chuck Lever   NFS: make iocb av...
72
  	struct kiocb *		iocb;		/* controlling i/o request */
88467055f   Chuck Lever   NFS: clean up com...
73
  	struct inode *		inode;		/* target file of i/o */
15ce4a0c1   Chuck Lever   NFS: Replace atom...
74
75
  
  	/* completion state */
607f31e80   Trond Myklebust   Revert "Merge bra...
76
  	atomic_t		io_count;	/* i/os we're waiting for */
15ce4a0c1   Chuck Lever   NFS: Replace atom...
77
  	spinlock_t		lock;		/* protect completion state */
15ce4a0c1   Chuck Lever   NFS: Replace atom...
78
  	ssize_t			count,		/* bytes actually processed */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
  				error;		/* any reported error */
d72b7a6b2   Trond Myklebust   NFS: O_DIRECT nee...
80
  	struct completion	completion;	/* wait for i/o completion */
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
81
82
  
  	/* commit state */
607f31e80   Trond Myklebust   Revert "Merge bra...
83
  	struct list_head	rewrite_list;	/* saved nfs_write_data structs */
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
84
85
86
87
88
  	struct nfs_write_data *	commit_data;	/* special write_data for commits */
  	int			flags;
  #define NFS_ODIRECT_DO_COMMIT		(1)	/* an unstable reply was received */
  #define NFS_ODIRECT_RESCHED_WRITES	(2)	/* write verification failed */
  	struct nfs_writeverf	verf;		/* unstable write verifier */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
  };
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
90
  static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode);
607f31e80   Trond Myklebust   Revert "Merge bra...
91
92
93
94
95
96
97
98
99
100
101
  static const struct rpc_call_ops nfs_write_direct_ops;
  
  static inline void get_dreq(struct nfs_direct_req *dreq)
  {
  	atomic_inc(&dreq->io_count);
  }
  
  static inline int put_dreq(struct nfs_direct_req *dreq)
  {
  	return atomic_dec_and_test(&dreq->io_count);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
  /**
b8a32e2b8   Chuck Lever   NFS: clean up NFS...
103
104
105
106
107
108
109
110
111
112
113
   * nfs_direct_IO - NFS address space operation for direct I/O
   * @rw: direction (read or write)
   * @iocb: target I/O control block
   * @iov: array of vectors that define I/O buffer
   * @pos: offset in file to begin the operation
   * @nr_segs: size of iovec array
   *
   * The presence of this routine in the address space ops vector means
   * the NFS client supports direct I/O.  However, we shunt off direct
   * read and write requests before the VFS gets them, so this method
   * should never be called.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
   */
b8a32e2b8   Chuck Lever   NFS: clean up NFS...
115
116
  ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t pos, unsigned long nr_segs)
  {
b8a32e2b8   Chuck Lever   NFS: clean up NFS...
117
118
  	dprintk("NFS: nfs_direct_IO (%s) off/no(%Ld/%lu) EINVAL
  ",
01cce933d   Josef "Jeff" Sipek   [PATCH] nfs: chan...
119
  			iocb->ki_filp->f_path.dentry->d_name.name,
e99170ff3   Trond Myklebust   NFS,SUNRPC: Fix c...
120
  			(long long) pos, nr_segs);
b8a32e2b8   Chuck Lever   NFS: clean up NFS...
121
122
123
  
  	return -EINVAL;
  }
d4a8f3677   Trond Myklebust   NFS: Fix nfs_dire...
124
  static void nfs_direct_dirty_pages(struct page **pages, unsigned int pgbase, size_t count)
6b45d858e   Trond Myklebust   NFS: Clean up nfs...
125
  {
d4a8f3677   Trond Myklebust   NFS: Fix nfs_dire...
126
  	unsigned int npages;
749e146e0   Chuck Lever   NFS: Fix handful ...
127
  	unsigned int i;
d4a8f3677   Trond Myklebust   NFS: Fix nfs_dire...
128
129
130
131
132
  
  	if (count == 0)
  		return;
  	pages += (pgbase >> PAGE_SHIFT);
  	npages = (count + (pgbase & ~PAGE_MASK) + PAGE_SIZE - 1) >> PAGE_SHIFT;
6b45d858e   Trond Myklebust   NFS: Clean up nfs...
133
134
  	for (i = 0; i < npages; i++) {
  		struct page *page = pages[i];
607f31e80   Trond Myklebust   Revert "Merge bra...
135
  		if (!PageCompound(page))
d4a8f3677   Trond Myklebust   NFS: Fix nfs_dire...
136
  			set_page_dirty(page);
6b45d858e   Trond Myklebust   NFS: Clean up nfs...
137
  	}
9c93ab7df   Chuck Lever   NFS: refactor nfs...
138
  }
749e146e0   Chuck Lever   NFS: Fix handful ...
139
  static void nfs_direct_release_pages(struct page **pages, unsigned int npages)
9c93ab7df   Chuck Lever   NFS: refactor nfs...
140
  {
749e146e0   Chuck Lever   NFS: Fix handful ...
141
  	unsigned int i;
607f31e80   Trond Myklebust   Revert "Merge bra...
142
143
  	for (i = 0; i < npages; i++)
  		page_cache_release(pages[i]);
6b45d858e   Trond Myklebust   NFS: Clean up nfs...
144
  }
93619e598   Chuck Lever   NFS: create commo...
145
  static inline struct nfs_direct_req *nfs_direct_req_alloc(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
  {
93619e598   Chuck Lever   NFS: create commo...
147
  	struct nfs_direct_req *dreq;
e94b17660   Christoph Lameter   [PATCH] slab: rem...
148
  	dreq = kmem_cache_alloc(nfs_direct_cachep, GFP_KERNEL);
93619e598   Chuck Lever   NFS: create commo...
149
150
151
152
  	if (!dreq)
  		return NULL;
  
  	kref_init(&dreq->kref);
607f31e80   Trond Myklebust   Revert "Merge bra...
153
  	kref_get(&dreq->kref);
d72b7a6b2   Trond Myklebust   NFS: O_DIRECT nee...
154
  	init_completion(&dreq->completion);
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
155
  	INIT_LIST_HEAD(&dreq->rewrite_list);
93619e598   Chuck Lever   NFS: create commo...
156
  	dreq->iocb = NULL;
a8881f5a5   Trond Myklebust   NFS: O_DIRECT asy...
157
  	dreq->ctx = NULL;
f11ac8db5   Trond Myklebust   NFSv4: Ensure tha...
158
  	dreq->l_ctx = NULL;
15ce4a0c1   Chuck Lever   NFS: Replace atom...
159
  	spin_lock_init(&dreq->lock);
607f31e80   Trond Myklebust   Revert "Merge bra...
160
  	atomic_set(&dreq->io_count, 0);
15ce4a0c1   Chuck Lever   NFS: Replace atom...
161
162
  	dreq->count = 0;
  	dreq->error = 0;
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
163
  	dreq->flags = 0;
93619e598   Chuck Lever   NFS: create commo...
164
165
  
  	return dreq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
  }
b4946ffb1   Trond Myklebust   NFS: Fix a refcou...
167
  static void nfs_direct_req_free(struct kref *kref)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
  {
  	struct nfs_direct_req *dreq = container_of(kref, struct nfs_direct_req, kref);
a8881f5a5   Trond Myklebust   NFS: O_DIRECT asy...
170

f11ac8db5   Trond Myklebust   NFSv4: Ensure tha...
171
172
  	if (dreq->l_ctx != NULL)
  		nfs_put_lock_context(dreq->l_ctx);
a8881f5a5   Trond Myklebust   NFS: O_DIRECT asy...
173
174
  	if (dreq->ctx != NULL)
  		put_nfs_open_context(dreq->ctx);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
  	kmem_cache_free(nfs_direct_cachep, dreq);
  }
b4946ffb1   Trond Myklebust   NFS: Fix a refcou...
177
178
179
180
  static void nfs_direct_req_release(struct nfs_direct_req *dreq)
  {
  	kref_put(&dreq->kref, nfs_direct_req_free);
  }
d4cc948ba   Chuck Lever   NFS: update comme...
181
  /*
bc0fb201b   Chuck Lever   NFS: create commo...
182
183
184
185
   * Collects and returns the final error value/byte-count.
   */
  static ssize_t nfs_direct_wait(struct nfs_direct_req *dreq)
  {
15ce4a0c1   Chuck Lever   NFS: Replace atom...
186
  	ssize_t result = -EIOCBQUEUED;
bc0fb201b   Chuck Lever   NFS: create commo...
187
188
189
190
  
  	/* Async requests don't wait here */
  	if (dreq->iocb)
  		goto out;
150030b78   Matthew Wilcox   NFS: Switch from ...
191
  	result = wait_for_completion_killable(&dreq->completion);
bc0fb201b   Chuck Lever   NFS: create commo...
192
193
  
  	if (!result)
15ce4a0c1   Chuck Lever   NFS: Replace atom...
194
  		result = dreq->error;
bc0fb201b   Chuck Lever   NFS: create commo...
195
  	if (!result)
15ce4a0c1   Chuck Lever   NFS: Replace atom...
196
  		result = dreq->count;
bc0fb201b   Chuck Lever   NFS: create commo...
197
198
  
  out:
bc0fb201b   Chuck Lever   NFS: create commo...
199
200
201
202
  	return (ssize_t) result;
  }
  
  /*
607f31e80   Trond Myklebust   Revert "Merge bra...
203
204
   * Synchronous I/O uses a stack-allocated iocb.  Thus we can't trust
   * the iocb is still valid here if this is a synchronous request.
63ab46abc   Chuck Lever   NFS: create commo...
205
206
207
   */
  static void nfs_direct_complete(struct nfs_direct_req *dreq)
  {
63ab46abc   Chuck Lever   NFS: create commo...
208
  	if (dreq->iocb) {
15ce4a0c1   Chuck Lever   NFS: Replace atom...
209
  		long res = (long) dreq->error;
63ab46abc   Chuck Lever   NFS: create commo...
210
  		if (!res)
15ce4a0c1   Chuck Lever   NFS: Replace atom...
211
  			res = (long) dreq->count;
63ab46abc   Chuck Lever   NFS: create commo...
212
  		aio_complete(dreq->iocb, res, 0);
d72b7a6b2   Trond Myklebust   NFS: O_DIRECT nee...
213
214
  	}
  	complete_all(&dreq->completion);
63ab46abc   Chuck Lever   NFS: create commo...
215

b4946ffb1   Trond Myklebust   NFS: Fix a refcou...
216
  	nfs_direct_req_release(dreq);
63ab46abc   Chuck Lever   NFS: create commo...
217
218
219
  }
  
  /*
607f31e80   Trond Myklebust   Revert "Merge bra...
220
221
222
   * We must hold a reference to all the pages in this direct read request
   * until the RPCs complete.  This could be long *after* we are woken up in
   * nfs_direct_wait (for instance, if someone hits ^C on a slow server).
06cf6f2ed   Chuck Lever   NFS: Eliminate nf...
223
   */
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
224
  static void nfs_direct_read_result(struct rpc_task *task, void *calldata)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
  {
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
226
  	struct nfs_read_data *data = calldata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227

fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
228
229
230
231
232
233
234
235
236
  	nfs_readpage_result(task, data);
  }
  
  static void nfs_direct_read_release(void *calldata)
  {
  
  	struct nfs_read_data *data = calldata;
  	struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
  	int status = data->task.tk_status;
15ce4a0c1   Chuck Lever   NFS: Replace atom...
237
238
  
  	spin_lock(&dreq->lock);
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
239
240
  	if (unlikely(status < 0)) {
  		dreq->error = status;
d4a8f3677   Trond Myklebust   NFS: Fix nfs_dire...
241
242
243
244
245
246
247
248
249
  		spin_unlock(&dreq->lock);
  	} else {
  		dreq->count += data->res.count;
  		spin_unlock(&dreq->lock);
  		nfs_direct_dirty_pages(data->pagevec,
  				data->args.pgbase,
  				data->res.count);
  	}
  	nfs_direct_release_pages(data->pagevec, data->npages);
607f31e80   Trond Myklebust   Revert "Merge bra...
250
251
252
  
  	if (put_dreq(dreq))
  		nfs_direct_complete(dreq);
1ae88b2e4   Trond Myklebust   NFS: Fix an O_DIR...
253
  	nfs_readdata_free(data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
  }
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
255
  static const struct rpc_call_ops nfs_read_direct_ops = {
f11c88af2   Andy Adamson   nfs41: read seque...
256
257
258
  #if defined(CONFIG_NFS_V4_1)
  	.rpc_call_prepare = nfs_read_prepare,
  #endif /* CONFIG_NFS_V4_1 */
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
259
  	.rpc_call_done = nfs_direct_read_result,
fdd1e74c8   Trond Myklebust   NFS: Ensure that ...
260
  	.rpc_release = nfs_direct_read_release,
ec06c096e   Trond Myklebust   NFS: Cleanup of N...
261
  };
d4cc948ba   Chuck Lever   NFS: update comme...
262
  /*
607f31e80   Trond Myklebust   Revert "Merge bra...
263
264
265
266
267
   * For each rsize'd chunk of the user's buffer, dispatch an NFS READ
   * operation.  If nfs_readdata_alloc() or get_user_pages() fails,
   * bail and stop sending more reads.  Read length accounting is
   * handled automatically by nfs_direct_read_result().  Otherwise, if
   * no requests have been sent, just return an error.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268
   */
02fe49461   Chuck Lever   NFS: Clean up new...
269
270
271
  static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
  						const struct iovec *iov,
  						loff_t pos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
  {
a8881f5a5   Trond Myklebust   NFS: O_DIRECT asy...
273
  	struct nfs_open_context *ctx = dreq->ctx;
3d4ff43d8   Al Viro   nfs_open_context ...
274
  	struct inode *inode = ctx->dentry->d_inode;
02fe49461   Chuck Lever   NFS: Clean up new...
275
276
  	unsigned long user_addr = (unsigned long)iov->iov_base;
  	size_t count = iov->iov_len;
5dd602f20   Chuck Lever   NFS: use size_t t...
277
  	size_t rsize = NFS_SERVER(inode)->rsize;
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
278
  	struct rpc_task *task;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
279
280
281
  	struct rpc_message msg = {
  		.rpc_cred = ctx->cred,
  	};
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
282
283
  	struct rpc_task_setup task_setup_data = {
  		.rpc_client = NFS_CLIENT(inode),
bdc7f021f   Trond Myklebust   NFS: Clean up the...
284
  		.rpc_message = &msg,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
285
  		.callback_ops = &nfs_read_direct_ops,
101070ca2   Trond Myklebust   NFS: Ensure that ...
286
  		.workqueue = nfsiod_workqueue,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
287
288
  		.flags = RPC_TASK_ASYNC,
  	};
607f31e80   Trond Myklebust   Revert "Merge bra...
289
290
291
  	unsigned int pgbase;
  	int result;
  	ssize_t started = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
  	do {
82b145c5a   Chuck Lever   NFS: alloc nfs_re...
293
  		struct nfs_read_data *data;
5dd602f20   Chuck Lever   NFS: use size_t t...
294
  		size_t bytes;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295

e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
296
297
  		pgbase = user_addr & ~PAGE_MASK;
  		bytes = min(rsize,count);
607f31e80   Trond Myklebust   Revert "Merge bra...
298
  		result = -ENOMEM;
8d5658c94   Trond Myklebust   NFS: Fix a buffer...
299
  		data = nfs_readdata_alloc(nfs_page_array_len(pgbase, bytes));
607f31e80   Trond Myklebust   Revert "Merge bra...
300
301
  		if (unlikely(!data))
  			break;
607f31e80   Trond Myklebust   Revert "Merge bra...
302
303
304
305
  		down_read(&current->mm->mmap_sem);
  		result = get_user_pages(current, current->mm, user_addr,
  					data->npages, 1, 0, data->pagevec, NULL);
  		up_read(&current->mm->mmap_sem);
749e146e0   Chuck Lever   NFS: Fix handful ...
306
  		if (result < 0) {
1ae88b2e4   Trond Myklebust   NFS: Fix an O_DIR...
307
  			nfs_readdata_free(data);
749e146e0   Chuck Lever   NFS: Fix handful ...
308
309
310
  			break;
  		}
  		if ((unsigned)result < data->npages) {
d9df8d6b3   Trond Myklebust   NFS: Don't fail a...
311
312
313
  			bytes = result * PAGE_SIZE;
  			if (bytes <= pgbase) {
  				nfs_direct_release_pages(data->pagevec, result);
1ae88b2e4   Trond Myklebust   NFS: Fix an O_DIR...
314
  				nfs_readdata_free(data);
d9df8d6b3   Trond Myklebust   NFS: Don't fail a...
315
316
317
318
  				break;
  			}
  			bytes -= pgbase;
  			data->npages = result;
607f31e80   Trond Myklebust   Revert "Merge bra...
319
320
321
  		}
  
  		get_dreq(dreq);
82b145c5a   Chuck Lever   NFS: alloc nfs_re...
322

607f31e80   Trond Myklebust   Revert "Merge bra...
323
  		data->req = (struct nfs_page *) dreq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324
  		data->inode = inode;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
325
  		data->cred = msg.rpc_cred;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326
  		data->args.fh = NFS_FH(inode);
1ae88b2e4   Trond Myklebust   NFS: Fix an O_DIR...
327
  		data->args.context = ctx;
f11ac8db5   Trond Myklebust   NFSv4: Ensure tha...
328
  		data->args.lock_context = dreq->l_ctx;
88467055f   Chuck Lever   NFS: clean up com...
329
  		data->args.offset = pos;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
  		data->args.pgbase = pgbase;
607f31e80   Trond Myklebust   Revert "Merge bra...
331
  		data->args.pages = data->pagevec;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
333
334
335
  		data->args.count = bytes;
  		data->res.fattr = &data->fattr;
  		data->res.eof = 0;
  		data->res.count = bytes;
65d269538   Chuck Lever   NFS: Too many GET...
336
  		nfs_fattr_init(&data->fattr);
bdc7f021f   Trond Myklebust   NFS: Clean up the...
337
338
  		msg.rpc_argp = &data->args;
  		msg.rpc_resp = &data->res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339

077376919   Trond Myklebust   NFS/SUNRPC: Conve...
340
  		task_setup_data.task = &data->task;
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
341
  		task_setup_data.callback_data = data;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
342
  		NFS_PROTO(inode)->read_setup(data, &msg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343

077376919   Trond Myklebust   NFS/SUNRPC: Conve...
344
  		task = rpc_run_task(&task_setup_data);
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
345
346
347
  		if (IS_ERR(task))
  			break;
  		rpc_put_task(task);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348

a3f565b1e   Chuck Lever   NFS: fix print fo...
349
350
351
  		dprintk("NFS: %5u initiated direct read call "
  			"(req %s/%Ld, %zu bytes @ offset %Lu)
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352
353
354
355
356
  				data->task.tk_pid,
  				inode->i_sb->s_id,
  				(long long)NFS_FILEID(inode),
  				bytes,
  				(unsigned long long)data->args.offset);
607f31e80   Trond Myklebust   Revert "Merge bra...
357
358
  		started += bytes;
  		user_addr += bytes;
88467055f   Chuck Lever   NFS: clean up com...
359
  		pos += bytes;
e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
360
  		/* FIXME: Remove this unnecessary math from final patch */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
  		pgbase += bytes;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
362
  		pgbase &= ~PAGE_MASK;
e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
363
  		BUG_ON(pgbase != (user_addr & ~PAGE_MASK));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
365
366
  
  		count -= bytes;
  	} while (count != 0);
607f31e80   Trond Myklebust   Revert "Merge bra...
367

607f31e80   Trond Myklebust   Revert "Merge bra...
368
  	if (started)
c216fd708   Chuck Lever   NFS: Support mult...
369
  		return started;
607f31e80   Trond Myklebust   Revert "Merge bra...
370
  	return result < 0 ? (ssize_t) result : -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371
  }
19f737879   Chuck Lever   NFS: Introduce io...
372
373
374
375
376
377
378
379
380
381
382
383
384
  static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
  					      const struct iovec *iov,
  					      unsigned long nr_segs,
  					      loff_t pos)
  {
  	ssize_t result = -EINVAL;
  	size_t requested_bytes = 0;
  	unsigned long seg;
  
  	get_dreq(dreq);
  
  	for (seg = 0; seg < nr_segs; seg++) {
  		const struct iovec *vec = &iov[seg];
02fe49461   Chuck Lever   NFS: Clean up new...
385
  		result = nfs_direct_read_schedule_segment(dreq, vec, pos);
19f737879   Chuck Lever   NFS: Introduce io...
386
387
388
389
390
391
392
  		if (result < 0)
  			break;
  		requested_bytes += result;
  		if ((size_t)result < vec->iov_len)
  			break;
  		pos += vec->iov_len;
  	}
839f7ad69   Chuck Lever   NFS: Fix "kernel ...
393
394
395
396
397
398
399
400
  	/*
  	 * If no bytes were started, return the error, and let the
  	 * generic layer handle the completion.
  	 */
  	if (requested_bytes == 0) {
  		nfs_direct_req_release(dreq);
  		return result < 0 ? result : -EIO;
  	}
19f737879   Chuck Lever   NFS: Introduce io...
401
402
  	if (put_dreq(dreq))
  		nfs_direct_complete(dreq);
839f7ad69   Chuck Lever   NFS: Fix "kernel ...
403
  	return 0;
19f737879   Chuck Lever   NFS: Introduce io...
404
  }
c216fd708   Chuck Lever   NFS: Support mult...
405
406
  static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov,
  			       unsigned long nr_segs, loff_t pos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407
  {
f11ac8db5   Trond Myklebust   NFSv4: Ensure tha...
408
  	ssize_t result = -ENOMEM;
99514f8fd   Chuck Lever   NFS: make iocb av...
409
  	struct inode *inode = iocb->ki_filp->f_mapping->host;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410
  	struct nfs_direct_req *dreq;
607f31e80   Trond Myklebust   Revert "Merge bra...
411
  	dreq = nfs_direct_req_alloc();
f11ac8db5   Trond Myklebust   NFSv4: Ensure tha...
412
413
  	if (dreq == NULL)
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414

91d5b4702   Chuck Lever   NFS: add I/O perf...
415
  	dreq->inode = inode;
cd3758e37   Trond Myklebust   NFS: Replace file...
416
  	dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp));
f11ac8db5   Trond Myklebust   NFSv4: Ensure tha...
417
418
419
  	dreq->l_ctx = nfs_get_lock_context(dreq->ctx);
  	if (dreq->l_ctx == NULL)
  		goto out_release;
487b83723   Chuck Lever   NFS: support EIOC...
420
421
  	if (!is_sync_kiocb(iocb))
  		dreq->iocb = iocb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422

c216fd708   Chuck Lever   NFS: Support mult...
423
  	result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos);
607f31e80   Trond Myklebust   Revert "Merge bra...
424
425
  	if (!result)
  		result = nfs_direct_wait(dreq);
f11ac8db5   Trond Myklebust   NFSv4: Ensure tha...
426
  out_release:
b4946ffb1   Trond Myklebust   NFS: Fix a refcou...
427
  	nfs_direct_req_release(dreq);
f11ac8db5   Trond Myklebust   NFSv4: Ensure tha...
428
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429
430
  	return result;
  }
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
431
  static void nfs_direct_free_writedata(struct nfs_direct_req *dreq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
  {
607f31e80   Trond Myklebust   Revert "Merge bra...
433
434
  	while (!list_empty(&dreq->rewrite_list)) {
  		struct nfs_write_data *data = list_entry(dreq->rewrite_list.next, struct nfs_write_data, pages);
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
435
  		list_del(&data->pages);
607f31e80   Trond Myklebust   Revert "Merge bra...
436
  		nfs_direct_release_pages(data->pagevec, data->npages);
1ae88b2e4   Trond Myklebust   NFS: Fix an O_DIR...
437
  		nfs_writedata_free(data);
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
438
439
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
440

fad614904   Trond Myklebust   nfs: Use UNSTABLE...
441
442
443
  #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
  static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
  {
607f31e80   Trond Myklebust   Revert "Merge bra...
444
445
446
  	struct inode *inode = dreq->inode;
  	struct list_head *p;
  	struct nfs_write_data *data;
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
447
  	struct rpc_task *task;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
448
449
450
  	struct rpc_message msg = {
  		.rpc_cred = dreq->ctx->cred,
  	};
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
451
452
  	struct rpc_task_setup task_setup_data = {
  		.rpc_client = NFS_CLIENT(inode),
a8b40bc7e   Terry Loftin   nfs: Panic when c...
453
  		.rpc_message = &msg,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
454
  		.callback_ops = &nfs_write_direct_ops,
101070ca2   Trond Myklebust   NFS: Ensure that ...
455
  		.workqueue = nfsiod_workqueue,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
456
457
  		.flags = RPC_TASK_ASYNC,
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458

fad614904   Trond Myklebust   nfs: Use UNSTABLE...
459
  	dreq->count = 0;
607f31e80   Trond Myklebust   Revert "Merge bra...
460
461
462
463
464
465
  	get_dreq(dreq);
  
  	list_for_each(p, &dreq->rewrite_list) {
  		data = list_entry(p, struct nfs_write_data, pages);
  
  		get_dreq(dreq);
bdc7f021f   Trond Myklebust   NFS: Clean up the...
466
467
  		/* Use stable writes */
  		data->args.stable = NFS_FILE_SYNC;
607f31e80   Trond Myklebust   Revert "Merge bra...
468
469
470
471
472
473
474
475
476
477
478
  		/*
  		 * Reset data->res.
  		 */
  		nfs_fattr_init(&data->fattr);
  		data->res.count = data->args.count;
  		memset(&data->verf, 0, sizeof(data->verf));
  
  		/*
  		 * Reuse data->task; data->args should not have changed
  		 * since the original request was sent.
  		 */
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
479
  		task_setup_data.task = &data->task;
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
480
  		task_setup_data.callback_data = data;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
481
482
483
  		msg.rpc_argp = &data->args;
  		msg.rpc_resp = &data->res;
  		NFS_PROTO(inode)->write_setup(data, &msg);
607f31e80   Trond Myklebust   Revert "Merge bra...
484

607f31e80   Trond Myklebust   Revert "Merge bra...
485
486
487
  		/*
  		 * We're called via an RPC callback, so BKL is already held.
  		 */
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
488
489
490
  		task = rpc_run_task(&task_setup_data);
  		if (!IS_ERR(task))
  			rpc_put_task(task);
607f31e80   Trond Myklebust   Revert "Merge bra...
491
492
493
494
495
496
497
498
499
  
  		dprintk("NFS: %5u rescheduled direct write call (req %s/%Ld, %u bytes @ offset %Lu)
  ",
  				data->task.tk_pid,
  				inode->i_sb->s_id,
  				(long long)NFS_FILEID(inode),
  				data->args.count,
  				(unsigned long long)data->args.offset);
  	}
fedb595c6   Chuck Lever   NFS: "open code" ...
500

607f31e80   Trond Myklebust   Revert "Merge bra...
501
502
  	if (put_dreq(dreq))
  		nfs_direct_write_complete(dreq, inode);
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
503
504
505
506
507
  }
  
  static void nfs_direct_commit_result(struct rpc_task *task, void *calldata)
  {
  	struct nfs_write_data *data = calldata;
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
508
509
  
  	/* Call the NFS version-specific code */
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
510
511
512
513
514
515
516
517
518
519
  	NFS_PROTO(data->inode)->commit_done(task, data);
  }
  
  static void nfs_direct_commit_release(void *calldata)
  {
  	struct nfs_write_data *data = calldata;
  	struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
  	int status = data->task.tk_status;
  
  	if (status < 0) {
60fa3f769   Trond Myklebust   NFS: Fix two bugs...
520
521
  		dprintk("NFS: %5u commit failed with error %d.
  ",
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
522
  				data->task.tk_pid, status);
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
523
  		dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
60fa3f769   Trond Myklebust   NFS: Fix two bugs...
524
  	} else if (memcmp(&dreq->verf, &data->verf, sizeof(data->verf))) {
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
525
526
  		dprintk("NFS: %5u commit verify failed
  ", data->task.tk_pid);
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
527
  		dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
528
  	}
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
529
530
  	dprintk("NFS: %5u commit returned %d
  ", data->task.tk_pid, status);
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
531
  	nfs_direct_write_complete(dreq, data->inode);
1ae88b2e4   Trond Myklebust   NFS: Fix an O_DIR...
532
  	nfs_commit_free(data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
  }
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
534
  static const struct rpc_call_ops nfs_commit_direct_ops = {
21d9a851a   Andy Adamson   nfs41 commit sequ...
535
536
537
  #if defined(CONFIG_NFS_V4_1)
  	.rpc_call_prepare = nfs_write_prepare,
  #endif /* CONFIG_NFS_V4_1 */
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
538
  	.rpc_call_done = nfs_direct_commit_result,
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
539
  	.rpc_release = nfs_direct_commit_release,
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
540
541
542
  };
  
  static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
543
  {
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
544
  	struct nfs_write_data *data = dreq->commit_data;
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
545
  	struct rpc_task *task;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
546
547
548
549
550
  	struct rpc_message msg = {
  		.rpc_argp = &data->args,
  		.rpc_resp = &data->res,
  		.rpc_cred = dreq->ctx->cred,
  	};
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
551
  	struct rpc_task_setup task_setup_data = {
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
552
  		.task = &data->task,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
553
  		.rpc_client = NFS_CLIENT(dreq->inode),
bdc7f021f   Trond Myklebust   NFS: Clean up the...
554
  		.rpc_message = &msg,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
555
556
  		.callback_ops = &nfs_commit_direct_ops,
  		.callback_data = data,
101070ca2   Trond Myklebust   NFS: Ensure that ...
557
  		.workqueue = nfsiod_workqueue,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
558
559
  		.flags = RPC_TASK_ASYNC,
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
560

fad614904   Trond Myklebust   nfs: Use UNSTABLE...
561
  	data->inode = dreq->inode;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
562
  	data->cred = msg.rpc_cred;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563

fad614904   Trond Myklebust   nfs: Use UNSTABLE...
564
  	data->args.fh = NFS_FH(data->inode);
607f31e80   Trond Myklebust   Revert "Merge bra...
565
566
  	data->args.offset = 0;
  	data->args.count = 0;
1ae88b2e4   Trond Myklebust   NFS: Fix an O_DIR...
567
  	data->args.context = dreq->ctx;
f11ac8db5   Trond Myklebust   NFSv4: Ensure tha...
568
  	data->args.lock_context = dreq->l_ctx;
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
569
570
571
  	data->res.count = 0;
  	data->res.fattr = &data->fattr;
  	data->res.verf = &data->verf;
65d269538   Chuck Lever   NFS: Too many GET...
572
  	nfs_fattr_init(&data->fattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573

bdc7f021f   Trond Myklebust   NFS: Clean up the...
574
  	NFS_PROTO(data->inode)->commit_setup(data, &msg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
575

fad614904   Trond Myklebust   nfs: Use UNSTABLE...
576
577
  	/* Note: task.tk_ops->rpc_release will free dreq->commit_data */
  	dreq->commit_data = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
578

e99170ff3   Trond Myklebust   NFS,SUNRPC: Fix c...
579
580
  	dprintk("NFS: %5u initiated commit call
  ", data->task.tk_pid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581

077376919   Trond Myklebust   NFS/SUNRPC: Conve...
582
583
584
  	task = rpc_run_task(&task_setup_data);
  	if (!IS_ERR(task))
  		rpc_put_task(task);
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
585
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586

fad614904   Trond Myklebust   nfs: Use UNSTABLE...
587
588
589
  static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode)
  {
  	int flags = dreq->flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590

fad614904   Trond Myklebust   nfs: Use UNSTABLE...
591
592
593
594
  	dreq->flags = 0;
  	switch (flags) {
  		case NFS_ODIRECT_DO_COMMIT:
  			nfs_direct_commit_schedule(dreq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595
  			break;
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
596
597
598
599
  		case NFS_ODIRECT_RESCHED_WRITES:
  			nfs_direct_write_reschedule(dreq);
  			break;
  		default:
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
600
601
602
  			if (dreq->commit_data != NULL)
  				nfs_commit_free(dreq->commit_data);
  			nfs_direct_free_writedata(dreq);
cd9ae2b6a   Trond Myklebust   [PATCH] NFS: Deal...
603
  			nfs_zap_mapping(inode, inode->i_mapping);
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
604
605
606
  			nfs_direct_complete(dreq);
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
607

fad614904   Trond Myklebust   nfs: Use UNSTABLE...
608
609
  static void nfs_alloc_commit_data(struct nfs_direct_req *dreq)
  {
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
610
  	dreq->commit_data = nfs_commitdata_alloc();
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
611
612
613
614
615
616
617
618
  	if (dreq->commit_data != NULL)
  		dreq->commit_data->req = (struct nfs_page *) dreq;
  }
  #else
  static inline void nfs_alloc_commit_data(struct nfs_direct_req *dreq)
  {
  	dreq->commit_data = NULL;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619

fad614904   Trond Myklebust   nfs: Use UNSTABLE...
620
621
  static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode)
  {
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
622
  	nfs_direct_free_writedata(dreq);
cd9ae2b6a   Trond Myklebust   [PATCH] NFS: Deal...
623
  	nfs_zap_mapping(inode, inode->i_mapping);
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
624
625
626
  	nfs_direct_complete(dreq);
  }
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
627

462d5b329   Chuck Lever   NFS: make direct ...
628
  static void nfs_direct_write_result(struct rpc_task *task, void *calldata)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
629
  {
462d5b329   Chuck Lever   NFS: make direct ...
630
  	struct nfs_write_data *data = calldata;
462d5b329   Chuck Lever   NFS: make direct ...
631

83762c56c   Fred Isaman   NFS: remove point...
632
  	nfs_writeback_done(task, data);
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
633
634
635
636
637
638
639
640
641
642
643
  }
  
  /*
   * NB: Return the value of the first error return code.  Subsequent
   *     errors after the first one are ignored.
   */
  static void nfs_direct_write_release(void *calldata)
  {
  	struct nfs_write_data *data = calldata;
  	struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
  	int status = data->task.tk_status;
462d5b329   Chuck Lever   NFS: make direct ...
644

15ce4a0c1   Chuck Lever   NFS: Replace atom...
645
  	spin_lock(&dreq->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
646

eda3cef8d   Trond Myklebust   [PATCH] NFS: Fix ...
647
  	if (unlikely(status < 0)) {
432409eeb   Neil Brown   NFS: Fix for bug ...
648
  		/* An error has occurred, so we should not commit */
60fa3f769   Trond Myklebust   NFS: Fix two bugs...
649
  		dreq->flags = 0;
eda3cef8d   Trond Myklebust   [PATCH] NFS: Fix ...
650
  		dreq->error = status;
eda3cef8d   Trond Myklebust   [PATCH] NFS: Fix ...
651
  	}
432409eeb   Neil Brown   NFS: Fix for bug ...
652
653
  	if (unlikely(dreq->error != 0))
  		goto out_unlock;
eda3cef8d   Trond Myklebust   [PATCH] NFS: Fix ...
654
655
  
  	dreq->count += data->res.count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656

fad614904   Trond Myklebust   nfs: Use UNSTABLE...
657
658
659
660
661
  	if (data->res.verf->committed != NFS_FILE_SYNC) {
  		switch (dreq->flags) {
  			case 0:
  				memcpy(&dreq->verf, &data->verf, sizeof(dreq->verf));
  				dreq->flags = NFS_ODIRECT_DO_COMMIT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
662
  				break;
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
663
664
  			case NFS_ODIRECT_DO_COMMIT:
  				if (memcmp(&dreq->verf, &data->verf, sizeof(dreq->verf))) {
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
665
666
  					dprintk("NFS: %5u write verify failed
  ", data->task.tk_pid);
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
667
668
  					dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
  				}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
669
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
670
  	}
eda3cef8d   Trond Myklebust   [PATCH] NFS: Fix ...
671
  out_unlock:
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
672
  	spin_unlock(&dreq->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
673

607f31e80   Trond Myklebust   Revert "Merge bra...
674
675
  	if (put_dreq(dreq))
  		nfs_direct_write_complete(dreq, data->inode);
462d5b329   Chuck Lever   NFS: make direct ...
676
677
678
  }
  
  static const struct rpc_call_ops nfs_write_direct_ops = {
def6ed7ef   Andy Adamson   nfs41 write seque...
679
680
681
  #if defined(CONFIG_NFS_V4_1)
  	.rpc_call_prepare = nfs_write_prepare,
  #endif /* CONFIG_NFS_V4_1 */
462d5b329   Chuck Lever   NFS: make direct ...
682
  	.rpc_call_done = nfs_direct_write_result,
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
683
  	.rpc_release = nfs_direct_write_release,
462d5b329   Chuck Lever   NFS: make direct ...
684
685
686
  };
  
  /*
607f31e80   Trond Myklebust   Revert "Merge bra...
687
688
689
690
691
   * For each wsize'd chunk of the user's buffer, dispatch an NFS WRITE
   * operation.  If nfs_writedata_alloc() or get_user_pages() fails,
   * bail and stop sending more writes.  Write length accounting is
   * handled automatically by nfs_direct_write_result().  Otherwise, if
   * no requests have been sent, just return an error.
462d5b329   Chuck Lever   NFS: make direct ...
692
   */
02fe49461   Chuck Lever   NFS: Clean up new...
693
694
695
  static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
  						 const struct iovec *iov,
  						 loff_t pos, int sync)
462d5b329   Chuck Lever   NFS: make direct ...
696
  {
a8881f5a5   Trond Myklebust   NFS: O_DIRECT asy...
697
  	struct nfs_open_context *ctx = dreq->ctx;
3d4ff43d8   Al Viro   nfs_open_context ...
698
  	struct inode *inode = ctx->dentry->d_inode;
02fe49461   Chuck Lever   NFS: Clean up new...
699
700
  	unsigned long user_addr = (unsigned long)iov->iov_base;
  	size_t count = iov->iov_len;
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
701
  	struct rpc_task *task;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
702
703
704
  	struct rpc_message msg = {
  		.rpc_cred = ctx->cred,
  	};
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
705
706
  	struct rpc_task_setup task_setup_data = {
  		.rpc_client = NFS_CLIENT(inode),
bdc7f021f   Trond Myklebust   NFS: Clean up the...
707
  		.rpc_message = &msg,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
708
  		.callback_ops = &nfs_write_direct_ops,
101070ca2   Trond Myklebust   NFS: Ensure that ...
709
  		.workqueue = nfsiod_workqueue,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
710
711
  		.flags = RPC_TASK_ASYNC,
  	};
462d5b329   Chuck Lever   NFS: make direct ...
712
  	size_t wsize = NFS_SERVER(inode)->wsize;
607f31e80   Trond Myklebust   Revert "Merge bra...
713
714
715
  	unsigned int pgbase;
  	int result;
  	ssize_t started = 0;
82b145c5a   Chuck Lever   NFS: alloc nfs_re...
716

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
  	do {
82b145c5a   Chuck Lever   NFS: alloc nfs_re...
718
  		struct nfs_write_data *data;
462d5b329   Chuck Lever   NFS: make direct ...
719
  		size_t bytes;
e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
720
721
  		pgbase = user_addr & ~PAGE_MASK;
  		bytes = min(wsize,count);
607f31e80   Trond Myklebust   Revert "Merge bra...
722
  		result = -ENOMEM;
8d5658c94   Trond Myklebust   NFS: Fix a buffer...
723
  		data = nfs_writedata_alloc(nfs_page_array_len(pgbase, bytes));
607f31e80   Trond Myklebust   Revert "Merge bra...
724
725
  		if (unlikely(!data))
  			break;
607f31e80   Trond Myklebust   Revert "Merge bra...
726
727
728
729
  		down_read(&current->mm->mmap_sem);
  		result = get_user_pages(current, current->mm, user_addr,
  					data->npages, 0, 0, data->pagevec, NULL);
  		up_read(&current->mm->mmap_sem);
749e146e0   Chuck Lever   NFS: Fix handful ...
730
  		if (result < 0) {
1ae88b2e4   Trond Myklebust   NFS: Fix an O_DIR...
731
  			nfs_writedata_free(data);
749e146e0   Chuck Lever   NFS: Fix handful ...
732
733
734
  			break;
  		}
  		if ((unsigned)result < data->npages) {
d9df8d6b3   Trond Myklebust   NFS: Don't fail a...
735
736
737
  			bytes = result * PAGE_SIZE;
  			if (bytes <= pgbase) {
  				nfs_direct_release_pages(data->pagevec, result);
1ae88b2e4   Trond Myklebust   NFS: Fix an O_DIR...
738
  				nfs_writedata_free(data);
d9df8d6b3   Trond Myklebust   NFS: Don't fail a...
739
740
741
742
  				break;
  			}
  			bytes -= pgbase;
  			data->npages = result;
607f31e80   Trond Myklebust   Revert "Merge bra...
743
744
745
  		}
  
  		get_dreq(dreq);
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
746
  		list_move_tail(&data->pages, &dreq->rewrite_list);
462d5b329   Chuck Lever   NFS: make direct ...
747

607f31e80   Trond Myklebust   Revert "Merge bra...
748
  		data->req = (struct nfs_page *) dreq;
462d5b329   Chuck Lever   NFS: make direct ...
749
  		data->inode = inode;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
750
  		data->cred = msg.rpc_cred;
462d5b329   Chuck Lever   NFS: make direct ...
751
  		data->args.fh = NFS_FH(inode);
1ae88b2e4   Trond Myklebust   NFS: Fix an O_DIR...
752
  		data->args.context = ctx;
f11ac8db5   Trond Myklebust   NFSv4: Ensure tha...
753
  		data->args.lock_context = dreq->l_ctx;
88467055f   Chuck Lever   NFS: clean up com...
754
  		data->args.offset = pos;
462d5b329   Chuck Lever   NFS: make direct ...
755
  		data->args.pgbase = pgbase;
607f31e80   Trond Myklebust   Revert "Merge bra...
756
  		data->args.pages = data->pagevec;
462d5b329   Chuck Lever   NFS: make direct ...
757
  		data->args.count = bytes;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
758
  		data->args.stable = sync;
462d5b329   Chuck Lever   NFS: make direct ...
759
760
  		data->res.fattr = &data->fattr;
  		data->res.count = bytes;
47989d745   Chuck Lever   NFS: remove suppo...
761
  		data->res.verf = &data->verf;
65d269538   Chuck Lever   NFS: Too many GET...
762
  		nfs_fattr_init(&data->fattr);
462d5b329   Chuck Lever   NFS: make direct ...
763

077376919   Trond Myklebust   NFS/SUNRPC: Conve...
764
  		task_setup_data.task = &data->task;
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
765
  		task_setup_data.callback_data = data;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
766
767
768
  		msg.rpc_argp = &data->args;
  		msg.rpc_resp = &data->res;
  		NFS_PROTO(inode)->write_setup(data, &msg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
769

077376919   Trond Myklebust   NFS/SUNRPC: Conve...
770
  		task = rpc_run_task(&task_setup_data);
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
771
772
773
  		if (IS_ERR(task))
  			break;
  		rpc_put_task(task);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
774

a3f565b1e   Chuck Lever   NFS: fix print fo...
775
776
777
  		dprintk("NFS: %5u initiated direct write call "
  			"(req %s/%Ld, %zu bytes @ offset %Lu)
  ",
462d5b329   Chuck Lever   NFS: make direct ...
778
779
780
781
782
  				data->task.tk_pid,
  				inode->i_sb->s_id,
  				(long long)NFS_FILEID(inode),
  				bytes,
  				(unsigned long long)data->args.offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
783

607f31e80   Trond Myklebust   Revert "Merge bra...
784
785
  		started += bytes;
  		user_addr += bytes;
88467055f   Chuck Lever   NFS: clean up com...
786
  		pos += bytes;
e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
787
788
  
  		/* FIXME: Remove this useless math from the final patch */
462d5b329   Chuck Lever   NFS: make direct ...
789
  		pgbase += bytes;
462d5b329   Chuck Lever   NFS: make direct ...
790
  		pgbase &= ~PAGE_MASK;
e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
791
  		BUG_ON(pgbase != (user_addr & ~PAGE_MASK));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
792

462d5b329   Chuck Lever   NFS: make direct ...
793
794
  		count -= bytes;
  	} while (count != 0);
607f31e80   Trond Myklebust   Revert "Merge bra...
795

607f31e80   Trond Myklebust   Revert "Merge bra...
796
  	if (started)
c216fd708   Chuck Lever   NFS: Support mult...
797
  		return started;
607f31e80   Trond Myklebust   Revert "Merge bra...
798
  	return result < 0 ? (ssize_t) result : -EFAULT;
462d5b329   Chuck Lever   NFS: make direct ...
799
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
800

19f737879   Chuck Lever   NFS: Introduce io...
801
802
803
804
805
806
807
808
809
810
811
812
813
  static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
  					       const struct iovec *iov,
  					       unsigned long nr_segs,
  					       loff_t pos, int sync)
  {
  	ssize_t result = 0;
  	size_t requested_bytes = 0;
  	unsigned long seg;
  
  	get_dreq(dreq);
  
  	for (seg = 0; seg < nr_segs; seg++) {
  		const struct iovec *vec = &iov[seg];
02fe49461   Chuck Lever   NFS: Clean up new...
814
815
  		result = nfs_direct_write_schedule_segment(dreq, vec,
  							   pos, sync);
19f737879   Chuck Lever   NFS: Introduce io...
816
817
818
819
820
821
822
  		if (result < 0)
  			break;
  		requested_bytes += result;
  		if ((size_t)result < vec->iov_len)
  			break;
  		pos += vec->iov_len;
  	}
839f7ad69   Chuck Lever   NFS: Fix "kernel ...
823
824
825
826
827
828
829
830
  	/*
  	 * If no bytes were started, return the error, and let the
  	 * generic layer handle the completion.
  	 */
  	if (requested_bytes == 0) {
  		nfs_direct_req_release(dreq);
  		return result < 0 ? result : -EIO;
  	}
19f737879   Chuck Lever   NFS: Introduce io...
831
832
  	if (put_dreq(dreq))
  		nfs_direct_write_complete(dreq, dreq->inode);
839f7ad69   Chuck Lever   NFS: Fix "kernel ...
833
  	return 0;
19f737879   Chuck Lever   NFS: Introduce io...
834
  }
c216fd708   Chuck Lever   NFS: Support mult...
835
836
837
  static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
  				unsigned long nr_segs, loff_t pos,
  				size_t count)
462d5b329   Chuck Lever   NFS: make direct ...
838
  {
f11ac8db5   Trond Myklebust   NFSv4: Ensure tha...
839
  	ssize_t result = -ENOMEM;
c89f2ee5f   Chuck Lever   NFS: make iocb av...
840
  	struct inode *inode = iocb->ki_filp->f_mapping->host;
462d5b329   Chuck Lever   NFS: make direct ...
841
  	struct nfs_direct_req *dreq;
fad614904   Trond Myklebust   nfs: Use UNSTABLE...
842
  	size_t wsize = NFS_SERVER(inode)->wsize;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
843
  	int sync = NFS_UNSTABLE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
844

607f31e80   Trond Myklebust   Revert "Merge bra...
845
  	dreq = nfs_direct_req_alloc();
462d5b329   Chuck Lever   NFS: make direct ...
846
  	if (!dreq)
f11ac8db5   Trond Myklebust   NFSv4: Ensure tha...
847
  		goto out;
607f31e80   Trond Myklebust   Revert "Merge bra...
848
  	nfs_alloc_commit_data(dreq);
b47d19de2   Arun Bharadwaj   Pure nfs client p...
849
  	if (dreq->commit_data == NULL || count <= wsize)
bdc7f021f   Trond Myklebust   NFS: Clean up the...
850
  		sync = NFS_FILE_SYNC;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
851

c89f2ee5f   Chuck Lever   NFS: make iocb av...
852
  	dreq->inode = inode;
cd3758e37   Trond Myklebust   NFS: Replace file...
853
  	dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp));
f11ac8db5   Trond Myklebust   NFSv4: Ensure tha...
854
  	dreq->l_ctx = nfs_get_lock_context(dreq->ctx);
568a810d7   Steve Dickson   Fixed Regression ...
855
  	if (dreq->l_ctx == NULL)
f11ac8db5   Trond Myklebust   NFSv4: Ensure tha...
856
  		goto out_release;
c89f2ee5f   Chuck Lever   NFS: make iocb av...
857
858
  	if (!is_sync_kiocb(iocb))
  		dreq->iocb = iocb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
859

c216fd708   Chuck Lever   NFS: Support mult...
860
  	result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, sync);
607f31e80   Trond Myklebust   Revert "Merge bra...
861
862
  	if (!result)
  		result = nfs_direct_wait(dreq);
f11ac8db5   Trond Myklebust   NFSv4: Ensure tha...
863
  out_release:
b4946ffb1   Trond Myklebust   NFS: Fix a refcou...
864
  	nfs_direct_req_release(dreq);
f11ac8db5   Trond Myklebust   NFSv4: Ensure tha...
865
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
866
867
868
869
870
871
  	return result;
  }
  
  /**
   * nfs_file_direct_read - file direct read operation for NFS files
   * @iocb: target I/O control block
027445c37   Badari Pulavarty   [PATCH] Vectorize...
872
873
   * @iov: vector of user buffers into which to read data
   * @nr_segs: size of iov vector
88467055f   Chuck Lever   NFS: clean up com...
874
   * @pos: byte offset in file where reading starts
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
875
876
877
878
879
880
   *
   * We use this function for direct reads instead of calling
   * generic_file_aio_read() in order to avoid gfar's check to see if
   * the request starts before the end of the file.  For that check
   * to work, we must generate a GETATTR before each direct read, and
   * even then there is a window between the GETATTR and the subsequent
88467055f   Chuck Lever   NFS: clean up com...
881
   * READ where the file size could change.  Our preference is simply
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
882
883
   * to do all reads the application wants, and the server will take
   * care of managing the end of file boundary.
88467055f   Chuck Lever   NFS: clean up com...
884
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
885
886
887
888
889
   * This function also eliminates unnecessarily updating the file's
   * atime locally, as the NFS server sets the file's atime, and this
   * client must read the updated atime from the server back into its
   * cache.
   */
027445c37   Badari Pulavarty   [PATCH] Vectorize...
890
891
  ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
  				unsigned long nr_segs, loff_t pos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
892
893
  {
  	ssize_t retval = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
894
  	struct file *file = iocb->ki_filp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
895
  	struct address_space *mapping = file->f_mapping;
c216fd708   Chuck Lever   NFS: Support mult...
896
897
898
899
  	size_t count;
  
  	count = iov_length(iov, nr_segs);
  	nfs_add_stats(mapping->host, NFSIOS_DIRECTREADBYTES, count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
900

6da24bc9c   Chuck Lever   NFS: Use NFSDBG_F...
901
902
  	dfprintk(FILE, "NFS: direct read(%s/%s, %zd@%Ld)
  ",
01cce933d   Josef "Jeff" Sipek   [PATCH] nfs: chan...
903
904
  		file->f_path.dentry->d_parent->d_name.name,
  		file->f_path.dentry->d_name.name,
c216fd708   Chuck Lever   NFS: Support mult...
905
  		count, (long long) pos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
906

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
907
908
909
  	retval = 0;
  	if (!count)
  		goto out;
29884df0d   Trond Myklebust   NFS: Fix another ...
910
911
912
  	retval = nfs_sync_mapping(mapping);
  	if (retval)
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
913

7ec10f26e   Konstantin Khlebnikov   NFS: account dire...
914
  	task_io_account_read(count);
c216fd708   Chuck Lever   NFS: Support mult...
915
  	retval = nfs_direct_read(iocb, iov, nr_segs, pos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
916
  	if (retval > 0)
0cdd80d07   Chuck Lever   NFS: remove suppo...
917
  		iocb->ki_pos = pos + retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
918
919
920
921
922
923
924
925
  
  out:
  	return retval;
  }
  
  /**
   * nfs_file_direct_write - file direct write operation for NFS files
   * @iocb: target I/O control block
027445c37   Badari Pulavarty   [PATCH] Vectorize...
926
927
   * @iov: vector of user buffers from which to write data
   * @nr_segs: size of iov vector
88467055f   Chuck Lever   NFS: clean up com...
928
   * @pos: byte offset in file where writing starts
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
929
930
931
932
933
934
935
936
   *
   * We use this function for direct writes instead of calling
   * generic_file_aio_write() in order to avoid taking the inode
   * semaphore and updating the i_size.  The NFS server will set
   * the new i_size and this client must read the updated size
   * back into its cache.  We let the server do generic write
   * parameter checking and report problems.
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
937
938
939
940
941
942
943
944
   * We eliminate local atime updates, see direct read above.
   *
   * We avoid unnecessary page cache invalidations for normal cached
   * readers of this file.
   *
   * Note that O_APPEND is not supported for NFS direct writes, as there
   * is no atomic O_APPEND write facility in the NFS protocol.
   */
027445c37   Badari Pulavarty   [PATCH] Vectorize...
945
946
  ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
  				unsigned long nr_segs, loff_t pos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
947
  {
070ea6021   Chuck Lever   NFS: Clean ups in...
948
  	ssize_t retval = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
949
  	struct file *file = iocb->ki_filp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
950
  	struct address_space *mapping = file->f_mapping;
c216fd708   Chuck Lever   NFS: Support mult...
951
  	size_t count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
952

c216fd708   Chuck Lever   NFS: Support mult...
953
954
  	count = iov_length(iov, nr_segs);
  	nfs_add_stats(mapping->host, NFSIOS_DIRECTWRITTENBYTES, count);
6da24bc9c   Chuck Lever   NFS: Use NFSDBG_F...
955
956
  	dfprintk(FILE, "NFS: direct write(%s/%s, %zd@%Ld)
  ",
01cce933d   Josef "Jeff" Sipek   [PATCH] nfs: chan...
957
958
  		file->f_path.dentry->d_parent->d_name.name,
  		file->f_path.dentry->d_name.name,
c216fd708   Chuck Lever   NFS: Support mult...
959
  		count, (long long) pos);
027445c37   Badari Pulavarty   [PATCH] Vectorize...
960

ce1a8e679   Chuck Lever   NFS: use generic_...
961
962
  	retval = generic_write_checks(file, &pos, &count, 0);
  	if (retval)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
963
  		goto out;
ce1a8e679   Chuck Lever   NFS: use generic_...
964
965
966
  
  	retval = -EINVAL;
  	if ((ssize_t) count < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
967
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
968
969
970
  	retval = 0;
  	if (!count)
  		goto out;
ce1a8e679   Chuck Lever   NFS: use generic_...
971

29884df0d   Trond Myklebust   NFS: Fix another ...
972
973
974
  	retval = nfs_sync_mapping(mapping);
  	if (retval)
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
975

7ec10f26e   Konstantin Khlebnikov   NFS: account dire...
976
  	task_io_account_write(count);
c216fd708   Chuck Lever   NFS: Support mult...
977
  	retval = nfs_direct_write(iocb, iov, nr_segs, pos, count);
9eafa8cc5   Chuck Lever   NFS: support EIOC...
978

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
979
  	if (retval > 0)
ce1a8e679   Chuck Lever   NFS: use generic_...
980
  		iocb->ki_pos = pos + retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
981
982
983
984
  
  out:
  	return retval;
  }
88467055f   Chuck Lever   NFS: clean up com...
985
986
987
988
  /**
   * nfs_init_directcache - create a slab cache for nfs_direct_req structures
   *
   */
f7b422b17   David Howells   NFS: Split fs/nfs...
989
  int __init nfs_init_directcache(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
990
991
992
  {
  	nfs_direct_cachep = kmem_cache_create("nfs_direct_cache",
  						sizeof(struct nfs_direct_req),
fffb60f93   Paul Jackson   [PATCH] cpuset me...
993
994
  						0, (SLAB_RECLAIM_ACCOUNT|
  							SLAB_MEM_SPREAD),
20c2df83d   Paul Mundt   mm: Remove slab d...
995
  						NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
996
997
998
999
1000
  	if (nfs_direct_cachep == NULL)
  		return -ENOMEM;
  
  	return 0;
  }
88467055f   Chuck Lever   NFS: clean up com...
1001
  /**
f7b422b17   David Howells   NFS: Split fs/nfs...
1002
   * nfs_destroy_directcache - destroy the slab cache for nfs_direct_req structures
88467055f   Chuck Lever   NFS: clean up com...
1003
1004
   *
   */
266bee886   David Brownell   [PATCH] fix stati...
1005
  void nfs_destroy_directcache(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1006
  {
1a1d92c10   Alexey Dobriyan   [PATCH] Really ig...
1007
  	kmem_cache_destroy(nfs_direct_cachep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1008
  }