Blame view

fs/nfs/write.c 57.2 KB
457c89965   Thomas Gleixner   treewide: Add SPD...
1
  // SPDX-License-Identifier: GPL-2.0-only
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
  /*
   * linux/fs/nfs/write.c
   *
7c85d9007   Trond Myklebust   NFS: Fixup some o...
5
   * Write file data over NFS.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
7
8
   *
   * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de>
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
10
11
12
13
  #include <linux/types.h>
  #include <linux/slab.h>
  #include <linux/mm.h>
  #include <linux/pagemap.h>
  #include <linux/file.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
  #include <linux/writeback.h>
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
15
  #include <linux/swap.h>
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
16
  #include <linux/migrate.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
21
  
  #include <linux/sunrpc/clnt.h>
  #include <linux/nfs_fs.h>
  #include <linux/nfs_mount.h>
  #include <linux/nfs_page.h>
3fcfab16c   Andrew Morton   [PATCH] separate ...
22
  #include <linux/backing-dev.h>
afeacc8c1   Paul Gortmaker   fs: add export.h ...
23
  #include <linux/export.h>
af7cf0579   Trond Myklebust   NFS: Allow multip...
24
25
  #include <linux/freezer.h>
  #include <linux/wait.h>
1eb5d98f1   Jeff Layton   nfs: convert to n...
26
  #include <linux/iversion.h>
3fcfab16c   Andrew Morton   [PATCH] separate ...
27

7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
28
  #include <linux/uaccess.h>
875bc3fbf   Trond Myklebust   NFS: Ensure NFS w...
29
  #include <linux/sched/mm.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
  
  #include "delegation.h"
49a70f278   Trond Myklebust   NFS: Cleanup: add...
32
  #include "internal.h"
91d5b4702   Chuck Lever   NFS: add I/O perf...
33
  #include "iostat.h"
def6ed7ef   Andy Adamson   nfs41 write seque...
34
  #include "nfs4_fs.h"
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
35
  #include "fscache.h"
94ad1c80e   Fred Isaman   NFSv4.1: coelesce...
36
  #include "pnfs.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37

f4ce1299b   Trond Myklebust   NFS: Add event tr...
38
  #include "nfstrace.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
41
42
  #define NFSDBG_FACILITY		NFSDBG_PAGECACHE
  
  #define MIN_POOL_WRITE		(32)
  #define MIN_POOL_COMMIT		(4)
919e3bd9a   Trond Myklebust   NFS: Ensure we co...
43
44
45
46
47
  struct nfs_io_completion {
  	void (*complete)(void *data);
  	void *data;
  	struct kref refcount;
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
50
  /*
   * Local function declarations
   */
f8512ad0d   Fred Isaman   nfs: don't ignore...
51
  static void nfs_redirty_request(struct nfs_page *req);
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
52
  static const struct rpc_call_ops nfs_commit_ops;
061ae2edb   Fred Isaman   NFS: create compl...
53
  static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops;
f453a54a0   Fred Isaman   NFS: create nfs_c...
54
  static const struct nfs_commit_completion_ops nfs_commit_completion_ops;
4a0de55c5   Anna Schumaker   NFS: Create a com...
55
  static const struct nfs_rw_ops nfs_rw_write_ops;
06c9fdf3b   Trond Myklebust   NFS: On fatal wri...
56
  static void nfs_inode_remove_request(struct nfs_page *req);
d45813835   Weston Andros Adamson   nfs: handle multi...
57
  static void nfs_clear_request_commit(struct nfs_page *req);
02d1426c7   Weston Andros Adamson   pnfs: find swappe...
58
59
  static void nfs_init_cinfo_from_inode(struct nfs_commit_info *cinfo,
  				      struct inode *inode);
3a3908c8b   Trond Myklebust   NFS: Fix a compil...
60
61
62
  static struct nfs_page *
  nfs_page_search_commits_for_head_request_locked(struct nfs_inode *nfsi,
  						struct page *page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63

e18b890bb   Christoph Lameter   [PATCH] slab: rem...
64
  static struct kmem_cache *nfs_wdata_cachep;
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
65
  static mempool_t *nfs_wdata_mempool;
0b7c01533   Fred Isaman   NFS: add a struct...
66
  static struct kmem_cache *nfs_cdata_cachep;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
  static mempool_t *nfs_commit_mempool;
518662e0f   NeilBrown   NFS: fix usage of...
68
  struct nfs_commit_data *nfs_commitdata_alloc(bool never_fail)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
  {
518662e0f   NeilBrown   NFS: fix usage of...
70
  	struct nfs_commit_data *p;
40859d7ee   Chuck Lever   NFS: support larg...
71

518662e0f   NeilBrown   NFS: fix usage of...
72
73
74
75
76
77
78
79
80
81
82
83
84
85
  	if (never_fail)
  		p = mempool_alloc(nfs_commit_mempool, GFP_NOIO);
  	else {
  		/* It is OK to do some reclaim, not no safe to wait
  		 * for anything to be returned to the pool.
  		 * mempool_alloc() cannot handle that particular combination,
  		 * so we need two separate attempts.
  		 */
  		p = mempool_alloc(nfs_commit_mempool, GFP_NOWAIT);
  		if (!p)
  			p = kmem_cache_alloc(nfs_cdata_cachep, GFP_NOIO |
  					     __GFP_NOWARN | __GFP_NORETRY);
  		if (!p)
  			return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
  	}
518662e0f   NeilBrown   NFS: fix usage of...
87
88
89
  
  	memset(p, 0, sizeof(*p));
  	INIT_LIST_HEAD(&p->pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
91
  	return p;
  }
e0c2b3801   Fred Isaman   NFSv4.1: filelayo...
92
  EXPORT_SYMBOL_GPL(nfs_commitdata_alloc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93

0b7c01533   Fred Isaman   NFS: add a struct...
94
  void nfs_commit_free(struct nfs_commit_data *p)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
96
97
  {
  	mempool_free(p, nfs_commit_mempool);
  }
e0c2b3801   Fred Isaman   NFSv4.1: filelayo...
98
  EXPORT_SYMBOL_GPL(nfs_commit_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99

1e7f3a485   Weston Andros Adamson   nfs: move nfs_pgi...
100
  static struct nfs_pgio_header *nfs_writehdr_alloc(void)
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
101
  {
2b17d725f   Trond Myklebust   NFS: Clean up wri...
102
  	struct nfs_pgio_header *p = mempool_alloc(nfs_wdata_mempool, GFP_KERNEL);
cd841605f   Fred Isaman   NFS: create commo...
103

237f8306c   NeilBrown   NFS: don't expect...
104
105
  	memset(p, 0, sizeof(*p));
  	p->rw_mode = FMODE_WRITE;
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
106
107
  	return p;
  }
6c75dc0d4   Fred Isaman   NFS: merge _full ...
108

1e7f3a485   Weston Andros Adamson   nfs: move nfs_pgi...
109
  static void nfs_writehdr_free(struct nfs_pgio_header *hdr)
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
110
  {
1e7f3a485   Weston Andros Adamson   nfs: move nfs_pgi...
111
  	mempool_free(hdr, nfs_wdata_mempool);
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
112
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113

919e3bd9a   Trond Myklebust   NFS: Ensure we co...
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
  static struct nfs_io_completion *nfs_io_completion_alloc(gfp_t gfp_flags)
  {
  	return kmalloc(sizeof(struct nfs_io_completion), gfp_flags);
  }
  
  static void nfs_io_completion_init(struct nfs_io_completion *ioc,
  		void (*complete)(void *), void *data)
  {
  	ioc->complete = complete;
  	ioc->data = data;
  	kref_init(&ioc->refcount);
  }
  
  static void nfs_io_completion_release(struct kref *kref)
  {
  	struct nfs_io_completion *ioc = container_of(kref,
  			struct nfs_io_completion, refcount);
  	ioc->complete(ioc->data);
  	kfree(ioc);
  }
  
  static void nfs_io_completion_get(struct nfs_io_completion *ioc)
  {
  	if (ioc != NULL)
  		kref_get(&ioc->refcount);
  }
  
  static void nfs_io_completion_put(struct nfs_io_completion *ioc)
  {
  	if (ioc != NULL)
  		kref_put(&ioc->refcount, nfs_io_completion_release);
  }
e00ed89d7   Trond Myklebust   NFS: Refactor nfs...
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
  static void
  nfs_page_set_inode_ref(struct nfs_page *req, struct inode *inode)
  {
  	if (!test_and_set_bit(PG_INODE_REF, &req->wb_flags)) {
  		kref_get(&req->wb_kref);
  		atomic_long_inc(&NFS_I(inode)->nrequests);
  	}
  }
  
  static int
  nfs_cancel_remove_inode(struct nfs_page *req, struct inode *inode)
  {
  	int ret;
  
  	if (!test_bit(PG_REMOVE, &req->wb_flags))
  		return 0;
  	ret = nfs_page_group_lock(req);
  	if (ret)
  		return ret;
  	if (test_and_clear_bit(PG_REMOVE, &req->wb_flags))
  		nfs_page_set_inode_ref(req, inode);
  	nfs_page_group_unlock(req);
  	return 0;
  }
bd37d6fce   Trond Myklebust   NFSv4: Convert nf...
170
171
172
173
174
175
176
  static struct nfs_page *
  nfs_page_private_request(struct page *page)
  {
  	if (!PagePrivate(page))
  		return NULL;
  	return (struct nfs_page *)page_private(page);
  }
84d3a9a91   Weston Andros Adamson   nfs: change find_...
177
178
179
180
181
182
183
  /*
   * nfs_page_find_head_request_locked - find head request associated with @page
   *
   * must be called while holding the inode lock.
   *
   * returns matching head request with reference held, or NULL if not found.
   */
29418aa4b   Mel Gorman   nfs: disable data...
184
  static struct nfs_page *
b30d2f04c   Trond Myklebust   NFS: Refactor nfs...
185
  nfs_page_find_private_request(struct page *page)
277459d2e   Trond Myklebust   NFS: Store pointe...
186
  {
4b9bb25b3   Trond Myklebust   NFS: Switch to us...
187
  	struct address_space *mapping = page_file_mapping(page);
bd37d6fce   Trond Myklebust   NFSv4: Convert nf...
188
  	struct nfs_page *req;
277459d2e   Trond Myklebust   NFS: Store pointe...
189

b30d2f04c   Trond Myklebust   NFS: Refactor nfs...
190
191
  	if (!PagePrivate(page))
  		return NULL;
4b9bb25b3   Trond Myklebust   NFS: Switch to us...
192
  	spin_lock(&mapping->private_lock);
bd37d6fce   Trond Myklebust   NFSv4: Convert nf...
193
  	req = nfs_page_private_request(page);
84d3a9a91   Weston Andros Adamson   nfs: change find_...
194
195
  	if (req) {
  		WARN_ON_ONCE(req->wb_head != req);
29418aa4b   Mel Gorman   nfs: disable data...
196
  		kref_get(&req->wb_kref);
84d3a9a91   Weston Andros Adamson   nfs: change find_...
197
  	}
4b9bb25b3   Trond Myklebust   NFS: Switch to us...
198
  	spin_unlock(&mapping->private_lock);
b30d2f04c   Trond Myklebust   NFS: Refactor nfs...
199
200
  	return req;
  }
29418aa4b   Mel Gorman   nfs: disable data...
201

b30d2f04c   Trond Myklebust   NFS: Refactor nfs...
202
203
204
205
206
207
208
209
  static struct nfs_page *
  nfs_page_find_swap_request(struct page *page)
  {
  	struct inode *inode = page_file_mapping(page)->host;
  	struct nfs_inode *nfsi = NFS_I(inode);
  	struct nfs_page *req = NULL;
  	if (!PageSwapCache(page))
  		return NULL;
e824f99ad   Trond Myklebust   NFSv4: Use a mute...
210
  	mutex_lock(&nfsi->commit_mutex);
b30d2f04c   Trond Myklebust   NFS: Refactor nfs...
211
212
213
214
215
216
217
218
  	if (PageSwapCache(page)) {
  		req = nfs_page_search_commits_for_head_request_locked(nfsi,
  			page);
  		if (req) {
  			WARN_ON_ONCE(req->wb_head != req);
  			kref_get(&req->wb_kref);
  		}
  	}
e824f99ad   Trond Myklebust   NFSv4: Use a mute...
219
  	mutex_unlock(&nfsi->commit_mutex);
277459d2e   Trond Myklebust   NFS: Store pointe...
220
221
  	return req;
  }
84d3a9a91   Weston Andros Adamson   nfs: change find_...
222
223
224
225
226
227
  /*
   * nfs_page_find_head_request - find head request associated with @page
   *
   * returns matching head request with reference held, or NULL if not found.
   */
  static struct nfs_page *nfs_page_find_head_request(struct page *page)
277459d2e   Trond Myklebust   NFS: Store pointe...
228
  {
b30d2f04c   Trond Myklebust   NFS: Refactor nfs...
229
  	struct nfs_page *req;
277459d2e   Trond Myklebust   NFS: Store pointe...
230

b30d2f04c   Trond Myklebust   NFS: Refactor nfs...
231
232
233
  	req = nfs_page_find_private_request(page);
  	if (!req)
  		req = nfs_page_find_swap_request(page);
277459d2e   Trond Myklebust   NFS: Store pointe...
234
235
  	return req;
  }
e00ed89d7   Trond Myklebust   NFS: Refactor nfs...
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
  static struct nfs_page *nfs_find_and_lock_page_request(struct page *page)
  {
  	struct inode *inode = page_file_mapping(page)->host;
  	struct nfs_page *req, *head;
  	int ret;
  
  	for (;;) {
  		req = nfs_page_find_head_request(page);
  		if (!req)
  			return req;
  		head = nfs_page_group_lock_head(req);
  		if (head != req)
  			nfs_release_request(req);
  		if (IS_ERR(head))
  			return head;
  		ret = nfs_cancel_remove_inode(head, inode);
  		if (ret < 0) {
  			nfs_unlock_and_release_request(head);
  			return ERR_PTR(ret);
  		}
  		/* Ensure that nobody removed the request before we locked it */
  		if (head == nfs_page_private_request(page))
  			break;
  		if (PageSwapCache(page))
  			break;
  		nfs_unlock_and_release_request(head);
  	}
  	return head;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
  /* Adjust the file length if we're writing beyond the end */
  static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count)
  {
d56b4ddf7   Mel Gorman   nfs: teach the NF...
268
  	struct inode *inode = page_file_mapping(page)->host;
a3d01454b   Trond Myklebust   NFS: Remove BKL r...
269
270
  	loff_t end, i_size;
  	pgoff_t end_index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271

a3d01454b   Trond Myklebust   NFS: Remove BKL r...
272
273
  	spin_lock(&inode->i_lock);
  	i_size = i_size_read(inode);
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
274
  	end_index = (i_size - 1) >> PAGE_SHIFT;
8cd797887   Huang Ying   mm: remove page_f...
275
  	if (i_size > 0 && page_index(page) < end_index)
a3d01454b   Trond Myklebust   NFS: Remove BKL r...
276
  		goto out;
d56b4ddf7   Mel Gorman   nfs: teach the NF...
277
  	end = page_file_offset(page) + ((loff_t)offset+count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
  	if (i_size >= end)
a3d01454b   Trond Myklebust   NFS: Remove BKL r...
279
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  	i_size_write(inode, end);
f6cdfa6dd   Trond Myklebust   NFSv4: Declare th...
281
  	NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_SIZE;
a3d01454b   Trond Myklebust   NFS: Remove BKL r...
282
283
284
  	nfs_inc_stats(inode, NFSIOS_EXTENDWRITE);
  out:
  	spin_unlock(&inode->i_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
  }
a301b7777   Trond Myklebust   NFS: Don't use Cl...
286
  /* A writeback failed: mark the page as bad, and invalidate the page cache */
d2ceb7e57   Benjamin Coddington   NFS: Don't use pa...
287
  static void nfs_set_pageerror(struct address_space *mapping)
a301b7777   Trond Myklebust   NFS: Don't use Cl...
288
  {
0df68ced5   Trond Myklebust   NFS: Revalidate t...
289
  	struct inode *inode = mapping->host;
d2ceb7e57   Benjamin Coddington   NFS: Don't use pa...
290
  	nfs_zap_mapping(mapping->host, mapping);
0df68ced5   Trond Myklebust   NFS: Revalidate t...
291
292
293
294
295
296
  	/* Force file size revalidation */
  	spin_lock(&inode->i_lock);
  	NFS_I(inode)->cache_validity |= NFS_INO_REVAL_FORCED |
  					NFS_INO_REVAL_PAGECACHE |
  					NFS_INO_INVALID_SIZE;
  	spin_unlock(&inode->i_lock);
a301b7777   Trond Myklebust   NFS: Don't use Cl...
297
  }
6fbda89b2   Trond Myklebust   NFS: Replace cust...
298
299
  static void nfs_mapping_set_error(struct page *page, int error)
  {
b8946d7bf   Trond Myklebust   NFS: Revalidate t...
300
  	struct address_space *mapping = page_file_mapping(page);
6fbda89b2   Trond Myklebust   NFS: Replace cust...
301
  	SetPageError(page);
b8946d7bf   Trond Myklebust   NFS: Revalidate t...
302
303
  	mapping_set_error(mapping, error);
  	nfs_set_pageerror(mapping);
6fbda89b2   Trond Myklebust   NFS: Replace cust...
304
  }
d72ddcbab   Weston Andros Adamson   nfs: page group s...
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
  /*
   * nfs_page_group_search_locked
   * @head - head request of page group
   * @page_offset - offset into page
   *
   * Search page group with head @head to find a request that contains the
   * page offset @page_offset.
   *
   * Returns a pointer to the first matching nfs request, or NULL if no
   * match is found.
   *
   * Must be called with the page group lock held
   */
  static struct nfs_page *
  nfs_page_group_search_locked(struct nfs_page *head, unsigned int page_offset)
  {
  	struct nfs_page *req;
d72ddcbab   Weston Andros Adamson   nfs: page group s...
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
  	req = head;
  	do {
  		if (page_offset >= req->wb_pgbase &&
  		    page_offset < (req->wb_pgbase + req->wb_bytes))
  			return req;
  
  		req = req->wb_this_page;
  	} while (req != head);
  
  	return NULL;
  }
  
  /*
   * nfs_page_group_covers_page
   * @head - head request of page group
   *
   * Return true if the page group with head @head covers the whole page,
   * returns false otherwise
   */
  static bool nfs_page_group_covers_page(struct nfs_page *req)
  {
  	struct nfs_page *tmp;
  	unsigned int pos = 0;
  	unsigned int len = nfs_page_length(req->wb_page);
1344b7ea1   Trond Myklebust   NFS: Remove unuse...
346
  	nfs_page_group_lock(req);
d72ddcbab   Weston Andros Adamson   nfs: page group s...
347

7e8a30f8b   Trond Myklebust   NFS: Fix up nfs_p...
348
  	for (;;) {
d72ddcbab   Weston Andros Adamson   nfs: page group s...
349
  		tmp = nfs_page_group_search_locked(req->wb_head, pos);
7e8a30f8b   Trond Myklebust   NFS: Fix up nfs_p...
350
351
352
353
  		if (!tmp)
  			break;
  		pos = tmp->wb_pgbase + tmp->wb_bytes;
  	}
d72ddcbab   Weston Andros Adamson   nfs: page group s...
354
355
  
  	nfs_page_group_unlock(req);
7e8a30f8b   Trond Myklebust   NFS: Fix up nfs_p...
356
  	return pos >= len;
d72ddcbab   Weston Andros Adamson   nfs: page group s...
357
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
359
360
  /* We can set the PG_uptodate flag if we see that a write request
   * covers the full page.
   */
d72ddcbab   Weston Andros Adamson   nfs: page group s...
361
  static void nfs_mark_uptodate(struct nfs_page *req)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
362
  {
d72ddcbab   Weston Andros Adamson   nfs: page group s...
363
  	if (PageUptodate(req->wb_page))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
  		return;
d72ddcbab   Weston Andros Adamson   nfs: page group s...
365
  	if (!nfs_page_group_covers_page(req))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
  		return;
d72ddcbab   Weston Andros Adamson   nfs: page group s...
367
  	SetPageUptodate(req->wb_page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
370
  static int wb_priority(struct writeback_control *wbc)
  {
e87b4c7a7   NeilBrown   NFS: don't use ST...
371
  	int ret = 0;
cca588d6c   Trond Myklebust   NFS: Reclaim writ...
372

e87b4c7a7   NeilBrown   NFS: don't use ST...
373
374
  	if (wbc->sync_mode == WB_SYNC_ALL)
  		ret = FLUSH_COND_STABLE;
e87b4c7a7   NeilBrown   NFS: don't use ST...
375
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
377
378
  }
  
  /*
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
379
380
381
382
383
384
385
386
   * NFS congestion control
   */
  
  int nfs_congestion_kb;
  
  #define NFS_CONGESTION_ON_THRESH 	(nfs_congestion_kb >> (PAGE_SHIFT-10))
  #define NFS_CONGESTION_OFF_THRESH	\
  	(NFS_CONGESTION_ON_THRESH - (NFS_CONGESTION_ON_THRESH >> 2))
deed85e76   Trond Myklebust   NFS: Remove BUG_O...
387
  static void nfs_set_page_writeback(struct page *page)
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
388
  {
0db10944a   Jan Kara   nfs: Convert to s...
389
390
  	struct inode *inode = page_file_mapping(page)->host;
  	struct nfs_server *nfss = NFS_SERVER(inode);
5a6d41b32   Trond Myklebust   NFS: Ensure PG_wr...
391
  	int ret = test_set_page_writeback(page);
deed85e76   Trond Myklebust   NFS: Remove BUG_O...
392
  	WARN_ON_ONCE(ret != 0);
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
393

deed85e76   Trond Myklebust   NFS: Remove BUG_O...
394
  	if (atomic_long_inc_return(&nfss->writeback) >
0db10944a   Jan Kara   nfs: Convert to s...
395
396
  			NFS_CONGESTION_ON_THRESH)
  		set_bdi_congested(inode_to_bdi(inode), BLK_RW_ASYNC);
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
397
  }
20633f042   Weston Andros Adamson   nfs: page group s...
398
  static void nfs_end_page_writeback(struct nfs_page *req)
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
399
  {
20633f042   Weston Andros Adamson   nfs: page group s...
400
  	struct inode *inode = page_file_mapping(req->wb_page)->host;
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
401
  	struct nfs_server *nfss = NFS_SERVER(inode);
31a01f093   Trond Myklebust   NFS: Don't unlock...
402
  	bool is_done;
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
403

31a01f093   Trond Myklebust   NFS: Don't unlock...
404
405
406
  	is_done = nfs_page_group_sync_on_bit(req, PG_WB_END);
  	nfs_unlock_request(req);
  	if (!is_done)
20633f042   Weston Andros Adamson   nfs: page group s...
407
408
409
  		return;
  
  	end_page_writeback(req->wb_page);
c4dc4beed   Peter Zijlstra   nfs: remove conge...
410
  	if (atomic_long_dec_return(&nfss->writeback) < NFS_CONGESTION_OFF_THRESH)
0db10944a   Jan Kara   nfs: Convert to s...
411
  		clear_bdi_congested(inode_to_bdi(inode), BLK_RW_ASYNC);
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
412
  }
d45813835   Weston Andros Adamson   nfs: handle multi...
413
  /*
d45813835   Weston Andros Adamson   nfs: handle multi...
414
415
416
417
418
419
420
421
422
423
424
   * nfs_destroy_unlinked_subrequests - destroy recently unlinked subrequests
   *
   * @destroy_list - request list (using wb_this_page) terminated by @old_head
   * @old_head - the old head of the list
   *
   * All subrequests must be locked and removed from all lists, so at this point
   * they are only "active" in this function, and possibly in nfs_wait_on_request
   * with a reference held by some other context.
   */
  static void
  nfs_destroy_unlinked_subrequests(struct nfs_page *destroy_list,
b66aaa8df   Trond Myklebust   NFS: Fix the inod...
425
426
  				 struct nfs_page *old_head,
  				 struct inode *inode)
d45813835   Weston Andros Adamson   nfs: handle multi...
427
428
429
430
431
432
  {
  	while (destroy_list) {
  		struct nfs_page *subreq = destroy_list;
  
  		destroy_list = (subreq->wb_this_page == old_head) ?
  				   NULL : subreq->wb_this_page;
08ca8b21f   Trond Myklebust   NFS: Fix races nf...
433
434
  		/* Note: lock subreq in order to change subreq->wb_head */
  		nfs_page_set_headlock(subreq);
d45813835   Weston Andros Adamson   nfs: handle multi...
435
436
437
  		WARN_ON_ONCE(old_head != subreq->wb_head);
  
  		/* make sure old group is not used */
d45813835   Weston Andros Adamson   nfs: handle multi...
438
  		subreq->wb_this_page = subreq;
08ca8b21f   Trond Myklebust   NFS: Fix races nf...
439
  		subreq->wb_head = subreq;
d45813835   Weston Andros Adamson   nfs: handle multi...
440

902a4c004   Trond Myklebust   NFS: Remove nfs_p...
441
  		clear_bit(PG_REMOVE, &subreq->wb_flags);
5b2b5187f   Trond Myklebust   NFS: Fix nfs_page...
442
443
  		/* Note: races with nfs_page_group_destroy() */
  		if (!kref_read(&subreq->wb_kref)) {
5b2b5187f   Trond Myklebust   NFS: Fix nfs_page...
444
  			/* Check if we raced with nfs_page_group_destroy() */
08ca8b21f   Trond Myklebust   NFS: Fix races nf...
445
446
  			if (test_and_clear_bit(PG_TEARDOWN, &subreq->wb_flags)) {
  				nfs_page_clear_headlock(subreq);
5b2b5187f   Trond Myklebust   NFS: Fix nfs_page...
447
  				nfs_free_request(subreq);
08ca8b21f   Trond Myklebust   NFS: Fix races nf...
448
449
  			} else
  				nfs_page_clear_headlock(subreq);
5b2b5187f   Trond Myklebust   NFS: Fix nfs_page...
450
451
  			continue;
  		}
08ca8b21f   Trond Myklebust   NFS: Fix races nf...
452
  		nfs_page_clear_headlock(subreq);
d45813835   Weston Andros Adamson   nfs: handle multi...
453

add42de31   Trond Myklebust   NFS: Fix a page l...
454
  		nfs_release_request(old_head);
5b2b5187f   Trond Myklebust   NFS: Fix nfs_page...
455
456
457
  
  		if (test_and_clear_bit(PG_INODE_REF, &subreq->wb_flags)) {
  			nfs_release_request(subreq);
a6b6d5b85   Trond Myklebust   NFS: Use an atomi...
458
  			atomic_long_dec(&NFS_I(inode)->nrequests);
d45813835   Weston Andros Adamson   nfs: handle multi...
459
  		}
5b2b5187f   Trond Myklebust   NFS: Fix nfs_page...
460

5b2b5187f   Trond Myklebust   NFS: Fix nfs_page...
461
462
463
  		/* subreq is now totally disconnected from page group or any
  		 * write / commit lists. last chance to wake any waiters */
  		nfs_unlock_and_release_request(subreq);
d45813835   Weston Andros Adamson   nfs: handle multi...
464
465
466
467
  	}
  }
  
  /*
e00ed89d7   Trond Myklebust   NFS: Refactor nfs...
468
469
470
   * nfs_join_page_group - destroy subrequests of the head req
   * @head: the page used to lookup the "page group" of nfs_page structures
   * @inode: Inode to which the request belongs.
d45813835   Weston Andros Adamson   nfs: handle multi...
471
472
473
474
475
476
   *
   * This function joins all sub requests to the head request by first
   * locking all requests in the group, cancelling any pending operations
   * and finally updating the head request to cover the whole range covered by
   * the (former) group.  All subrequests are removed from any write or commit
   * lists, unlinked from the group and destroyed.
d45813835   Weston Andros Adamson   nfs: handle multi...
477
   */
ed5d588fe   Trond Myklebust   NFS: Try to join ...
478
  void
e00ed89d7   Trond Myklebust   NFS: Refactor nfs...
479
  nfs_join_page_group(struct nfs_page *head, struct inode *inode)
e261f51f2   Trond Myklebust   NFS: Make nfs_upd...
480
  {
e00ed89d7   Trond Myklebust   NFS: Refactor nfs...
481
  	struct nfs_page *subreq;
d45813835   Weston Andros Adamson   nfs: handle multi...
482
  	struct nfs_page *destroy_list = NULL;
a62f8e3bd   Trond Myklebust   NFS: Clean up nfs...
483
  	unsigned int pgbase, off, bytes;
a62f8e3bd   Trond Myklebust   NFS: Clean up nfs...
484
485
486
487
  
  	pgbase = head->wb_pgbase;
  	bytes = head->wb_bytes;
  	off = head->wb_offset;
a0e265bc7   Trond Myklebust   NFS: Fix an ABBA ...
488
489
  	for (subreq = head->wb_this_page; subreq != head;
  			subreq = subreq->wb_this_page) {
a62f8e3bd   Trond Myklebust   NFS: Clean up nfs...
490
491
492
493
494
  		/* Subrequests should always form a contiguous range */
  		if (pgbase > subreq->wb_pgbase) {
  			off -= pgbase - subreq->wb_pgbase;
  			bytes += pgbase - subreq->wb_pgbase;
  			pgbase = subreq->wb_pgbase;
309a1d65b   Weston Andros Adamson   nfs: handle overl...
495
  		}
a62f8e3bd   Trond Myklebust   NFS: Clean up nfs...
496
497
  		bytes = max(subreq->wb_pgbase + subreq->wb_bytes
  				- pgbase, bytes);
a0e265bc7   Trond Myklebust   NFS: Fix an ABBA ...
498
  	}
d45813835   Weston Andros Adamson   nfs: handle multi...
499

a62f8e3bd   Trond Myklebust   NFS: Clean up nfs...
500
501
502
503
  	/* Set the head request's range to cover the former page group */
  	head->wb_pgbase = pgbase;
  	head->wb_bytes = bytes;
  	head->wb_offset = off;
d45813835   Weston Andros Adamson   nfs: handle multi...
504
505
506
507
  	/* Now that all requests are locked, make sure they aren't on any list.
  	 * Commit list removal accounting is done after locks are dropped */
  	subreq = head;
  	do {
411a99adf   Weston Andros Adamson   nfs: clear_reques...
508
  		nfs_clear_request_commit(subreq);
d45813835   Weston Andros Adamson   nfs: handle multi...
509
510
511
512
513
514
515
516
  		subreq = subreq->wb_this_page;
  	} while (subreq != head);
  
  	/* unlink subrequests from head, destroy them later */
  	if (head->wb_this_page != head) {
  		/* destroy list will be terminated by head */
  		destroy_list = head->wb_this_page;
  		head->wb_this_page = head;
e261f51f2   Trond Myklebust   NFS: Make nfs_upd...
517
  	}
d45813835   Weston Andros Adamson   nfs: handle multi...
518

e00ed89d7   Trond Myklebust   NFS: Refactor nfs...
519
520
  	nfs_destroy_unlinked_subrequests(destroy_list, head, inode);
  }
b66aaa8df   Trond Myklebust   NFS: Fix the inod...
521

e00ed89d7   Trond Myklebust   NFS: Refactor nfs...
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
  /*
   * nfs_lock_and_join_requests - join all subreqs to the head req
   * @page: the page used to lookup the "page group" of nfs_page structures
   *
   * This function joins all sub requests to the head request by first
   * locking all requests in the group, cancelling any pending operations
   * and finally updating the head request to cover the whole range covered by
   * the (former) group.  All subrequests are removed from any write or commit
   * lists, unlinked from the group and destroyed.
   *
   * Returns a locked, referenced pointer to the head request - which after
   * this call is guaranteed to be the only request associated with the page.
   * Returns NULL if no requests are found for @page, or a ERR_PTR if an
   * error was encountered.
   */
  static struct nfs_page *
  nfs_lock_and_join_requests(struct page *page)
  {
  	struct inode *inode = page_file_mapping(page)->host;
  	struct nfs_page *head;
  	int ret;
d45813835   Weston Andros Adamson   nfs: handle multi...
543

e00ed89d7   Trond Myklebust   NFS: Refactor nfs...
544
545
546
547
548
549
550
551
  	/*
  	 * A reference is taken only on the head request which acts as a
  	 * reference to the whole page group - the group will not be destroyed
  	 * until the head reference is released.
  	 */
  	head = nfs_find_and_lock_page_request(page);
  	if (IS_ERR_OR_NULL(head))
  		return head;
d45813835   Weston Andros Adamson   nfs: handle multi...
552

e00ed89d7   Trond Myklebust   NFS: Refactor nfs...
553
554
555
  	/* lock each request in the page group */
  	ret = nfs_page_group_lock_subrequests(head);
  	if (ret < 0) {
b5bab9bf9   Trond Myklebust   NFS: Reduce inode...
556
  		nfs_unlock_and_release_request(head);
e00ed89d7   Trond Myklebust   NFS: Refactor nfs...
557
  		return ERR_PTR(ret);
b5bab9bf9   Trond Myklebust   NFS: Reduce inode...
558
  	}
e00ed89d7   Trond Myklebust   NFS: Refactor nfs...
559
  	nfs_join_page_group(head, inode);
0671d8f10   Markus Elfring   nfs/write: Use co...
560

e00ed89d7   Trond Myklebust   NFS: Refactor nfs...
561
  	return head;
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
562
  }
6fbda89b2   Trond Myklebust   NFS: Replace cust...
563
  static void nfs_write_error(struct nfs_page *req, int error)
0bcbf039f   Peng Tao   nfs: handle reque...
564
  {
861e1671b   Chuck Lever   NFS: Introduce tr...
565
  	trace_nfs_write_error(req, error);
6fbda89b2   Trond Myklebust   NFS: Replace cust...
566
  	nfs_mapping_set_error(req->wb_page, error);
06c9fdf3b   Trond Myklebust   NFS: On fatal wri...
567
  	nfs_inode_remove_request(req);
0bcbf039f   Peng Tao   nfs: handle reque...
568
  	nfs_end_page_writeback(req);
1f84ccdf3   Fred Isaman   NFS: Fix use afte...
569
  	nfs_release_request(req);
0bcbf039f   Peng Tao   nfs: handle reque...
570
  }
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
571
572
573
574
575
  /*
   * Find an associated nfs write request, and prepare to flush it out
   * May return an error if the user signalled nfs_wait_on_request().
   */
  static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
6d17d653c   Trond Myklebust   NFS: Simplify pag...
576
  				struct page *page)
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
577
578
579
  {
  	struct nfs_page *req;
  	int ret = 0;
6d17d653c   Trond Myklebust   NFS: Simplify pag...
580
  	req = nfs_lock_and_join_requests(page);
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
581
582
583
584
585
  	if (!req)
  		goto out;
  	ret = PTR_ERR(req);
  	if (IS_ERR(req))
  		goto out;
deed85e76   Trond Myklebust   NFS: Remove BUG_O...
586
587
  	nfs_set_page_writeback(page);
  	WARN_ON_ONCE(test_bit(PG_CLEAN, &req->wb_flags));
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
588

a6598813a   Trond Myklebust   NFS: Don't write ...
589
  	/* If there is a fatal error that covers this write, just exit */
96c414559   Trond Myklebust   NFS: Fix writepag...
590
591
  	ret = pgio->pg_error;
  	if (nfs_error_is_fatal_on_server(ret))
a6598813a   Trond Myklebust   NFS: Don't write ...
592
  		goto out_launder;
96c414559   Trond Myklebust   NFS: Fix writepag...
593
  	ret = 0;
f8512ad0d   Fred Isaman   nfs: don't ignore...
594
  	if (!nfs_pageio_add_request(pgio, req)) {
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
595
  		ret = pgio->pg_error;
0bcbf039f   Peng Tao   nfs: handle reque...
596
  		/*
c373fff7b   Trond Myklebust   NFSv4: Don't spec...
597
  		 * Remove the problematic req upon fatal errors on the server
0bcbf039f   Peng Tao   nfs: handle reque...
598
599
  		 */
  		if (nfs_error_is_fatal(ret)) {
c373fff7b   Trond Myklebust   NFSv4: Don't spec...
600
  			if (nfs_error_is_fatal_on_server(ret))
a6598813a   Trond Myklebust   NFS: Don't write ...
601
  				goto out_launder;
8fc75bed9   Trond Myklebust   NFS: Fix up retur...
602
603
  		} else
  			ret = -EAGAIN;
d6c843b96   Peng Tao   nfs: only remove ...
604
  		nfs_redirty_request(req);
96c414559   Trond Myklebust   NFS: Fix writepag...
605
  		pgio->pg_error = 0;
40f90271a   Trond Myklebust   NFS: Fix up page ...
606
607
608
  	} else
  		nfs_add_stats(page_file_mapping(page)->host,
  				NFSIOS_WRITEPAGES, 1);
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
609
610
  out:
  	return ret;
a6598813a   Trond Myklebust   NFS: Don't write ...
611
  out_launder:
6fbda89b2   Trond Myklebust   NFS: Replace cust...
612
  	nfs_write_error(req, ret);
14bebe3c9   Trond Myklebust   NFS: Don't interr...
613
  	return 0;
e261f51f2   Trond Myklebust   NFS: Make nfs_upd...
614
  }
d6c843b96   Peng Tao   nfs: only remove ...
615
  static int nfs_do_writepage(struct page *page, struct writeback_control *wbc,
c373fff7b   Trond Myklebust   NFSv4: Don't spec...
616
  			    struct nfs_pageio_descriptor *pgio)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
  {
cfb506e1d   Trond Myklebust   NFS: Ensure that ...
618
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619

8cd797887   Huang Ying   mm: remove page_f...
620
  	nfs_pageio_cond_complete(pgio, page_index(page));
6d17d653c   Trond Myklebust   NFS: Simplify pag...
621
  	ret = nfs_page_async_flush(pgio, page);
cfb506e1d   Trond Myklebust   NFS: Ensure that ...
622
623
  	if (ret == -EAGAIN) {
  		redirty_page_for_writepage(wbc, page);
96c414559   Trond Myklebust   NFS: Fix writepag...
624
  		ret = AOP_WRITEPAGE_ACTIVATE;
cfb506e1d   Trond Myklebust   NFS: Ensure that ...
625
626
  	}
  	return ret;
f758c8851   Trond Myklebust   NFS: Clean up nfs...
627
  }
7fe7f8487   Trond Myklebust   NFS: Avoid a dead...
628

f758c8851   Trond Myklebust   NFS: Clean up nfs...
629
630
631
  /*
   * Write an mmapped page to the server.
   */
d6c843b96   Peng Tao   nfs: only remove ...
632
  static int nfs_writepage_locked(struct page *page,
c373fff7b   Trond Myklebust   NFSv4: Don't spec...
633
  				struct writeback_control *wbc)
f758c8851   Trond Myklebust   NFS: Clean up nfs...
634
635
  {
  	struct nfs_pageio_descriptor pgio;
40f90271a   Trond Myklebust   NFS: Fix up page ...
636
  	struct inode *inode = page_file_mapping(page)->host;
f758c8851   Trond Myklebust   NFS: Clean up nfs...
637
  	int err;
49a70f278   Trond Myklebust   NFS: Cleanup: add...
638

40f90271a   Trond Myklebust   NFS: Fix up page ...
639
  	nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGE);
811ed92ec   Trond Myklebust   NFS: writepage of...
640
  	nfs_pageio_init_write(&pgio, inode, 0,
a20c93e31   Christoph Hellwig   nfs: remove ->wri...
641
  				false, &nfs_async_write_completion_ops);
c373fff7b   Trond Myklebust   NFSv4: Don't spec...
642
  	err = nfs_do_writepage(page, wbc, &pgio);
96c414559   Trond Myklebust   NFS: Fix writepag...
643
  	pgio.pg_error = 0;
f758c8851   Trond Myklebust   NFS: Clean up nfs...
644
645
646
  	nfs_pageio_complete(&pgio);
  	if (err < 0)
  		return err;
96c414559   Trond Myklebust   NFS: Fix writepag...
647
  	if (nfs_error_is_fatal(pgio.pg_error))
f758c8851   Trond Myklebust   NFS: Clean up nfs...
648
649
  		return pgio.pg_error;
  	return 0;
4d770ccf4   Trond Myklebust   NFS: Ensure that ...
650
651
652
653
  }
  
  int nfs_writepage(struct page *page, struct writeback_control *wbc)
  {
f758c8851   Trond Myklebust   NFS: Clean up nfs...
654
  	int ret;
4d770ccf4   Trond Myklebust   NFS: Ensure that ...
655

c373fff7b   Trond Myklebust   NFSv4: Don't spec...
656
  	ret = nfs_writepage_locked(page, wbc);
96c414559   Trond Myklebust   NFS: Fix writepag...
657
658
  	if (ret != AOP_WRITEPAGE_ACTIVATE)
  		unlock_page(page);
f758c8851   Trond Myklebust   NFS: Clean up nfs...
659
660
661
662
663
664
  	return ret;
  }
  
  static int nfs_writepages_callback(struct page *page, struct writeback_control *wbc, void *data)
  {
  	int ret;
c373fff7b   Trond Myklebust   NFSv4: Don't spec...
665
  	ret = nfs_do_writepage(page, wbc, data);
96c414559   Trond Myklebust   NFS: Fix writepag...
666
667
  	if (ret != AOP_WRITEPAGE_ACTIVATE)
  		unlock_page(page);
f758c8851   Trond Myklebust   NFS: Clean up nfs...
668
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
669
  }
919e3bd9a   Trond Myklebust   NFS: Ensure we co...
670
671
672
673
  static void nfs_io_completion_commit(void *inode)
  {
  	nfs_commit_inode(inode, 0);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
674
675
  int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
676
  	struct inode *inode = mapping->host;
c63c7b051   Trond Myklebust   NFS: Fix a race w...
677
  	struct nfs_pageio_descriptor pgio;
875bc3fbf   Trond Myklebust   NFS: Ensure NFS w...
678
  	struct nfs_io_completion *ioc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
679
  	int err;
91d5b4702   Chuck Lever   NFS: add I/O perf...
680
  	nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES);
2b17d725f   Trond Myklebust   NFS: Clean up wri...
681
  	ioc = nfs_io_completion_alloc(GFP_KERNEL);
919e3bd9a   Trond Myklebust   NFS: Ensure we co...
682
683
  	if (ioc)
  		nfs_io_completion_init(ioc, nfs_io_completion_commit, inode);
a20c93e31   Christoph Hellwig   nfs: remove ->wri...
684
685
  	nfs_pageio_init_write(&pgio, inode, wb_priority(wbc), false,
  				&nfs_async_write_completion_ops);
919e3bd9a   Trond Myklebust   NFS: Ensure we co...
686
  	pgio.pg_io_completion = ioc;
f758c8851   Trond Myklebust   NFS: Clean up nfs...
687
  	err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio);
96c414559   Trond Myklebust   NFS: Fix writepag...
688
  	pgio.pg_error = 0;
c63c7b051   Trond Myklebust   NFS: Fix a race w...
689
  	nfs_pageio_complete(&pgio);
919e3bd9a   Trond Myklebust   NFS: Ensure we co...
690
  	nfs_io_completion_put(ioc);
72cb77f4a   Trond Myklebust   NFS: Throttle pag...
691

f758c8851   Trond Myklebust   NFS: Clean up nfs...
692
  	if (err < 0)
72cb77f4a   Trond Myklebust   NFS: Throttle pag...
693
694
  		goto out_err;
  	err = pgio.pg_error;
96c414559   Trond Myklebust   NFS: Fix writepag...
695
  	if (nfs_error_is_fatal(err))
72cb77f4a   Trond Myklebust   NFS: Throttle pag...
696
  		goto out_err;
c63c7b051   Trond Myklebust   NFS: Fix a race w...
697
  	return 0;
72cb77f4a   Trond Myklebust   NFS: Throttle pag...
698
699
  out_err:
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
700
701
702
703
704
  }
  
  /*
   * Insert a write request into an inode
   */
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
705
  static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
706
  {
4b9bb25b3   Trond Myklebust   NFS: Switch to us...
707
  	struct address_space *mapping = page_file_mapping(req->wb_page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
708
  	struct nfs_inode *nfsi = NFS_I(inode);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
709

2bfc6e566   Weston Andros Adamson   nfs: add support ...
710
  	WARN_ON_ONCE(req->wb_this_page != req);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
711
  	/* Lock the request! */
7ad84aa94   Trond Myklebust   NFS: Clean up - s...
712
  	nfs_lock_request(req);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
713

29418aa4b   Mel Gorman   nfs: disable data...
714
715
716
717
  	/*
  	 * Swap-space should not get truncated. Hence no need to plug the race
  	 * with invalidate/truncate.
  	 */
4b9bb25b3   Trond Myklebust   NFS: Switch to us...
718
719
  	spin_lock(&mapping->private_lock);
  	if (!nfs_have_writebacks(inode) &&
1eb5d98f1   Jeff Layton   nfs: convert to n...
720
721
  	    NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE))
  		inode_inc_iversion_raw(inode);
29418aa4b   Mel Gorman   nfs: disable data...
722
723
724
725
726
  	if (likely(!PageSwapCache(req->wb_page))) {
  		set_bit(PG_MAPPED, &req->wb_flags);
  		SetPagePrivate(req->wb_page);
  		set_page_private(req->wb_page, (unsigned long)req);
  	}
4b9bb25b3   Trond Myklebust   NFS: Switch to us...
727
  	spin_unlock(&mapping->private_lock);
a6b6d5b85   Trond Myklebust   NFS: Use an atomi...
728
  	atomic_long_inc(&nfsi->nrequests);
17089a29a   Weston Andros Adamson   nfs: mark nfs_pag...
729
  	/* this a head request for a page group - mark it as having an
cb1410c71   Weston Andros Adamson   NFS: fix subtle c...
730
731
732
  	 * extra reference so sub groups can follow suit.
  	 * This flag also informs pgio layer when to bump nrequests when
  	 * adding subrequests. */
17089a29a   Weston Andros Adamson   nfs: mark nfs_pag...
733
  	WARN_ON(test_and_set_bit(PG_INODE_REF, &req->wb_flags));
c03b40246   Trond Myklebust   NFS: Convert stru...
734
  	kref_get(&req->wb_kref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
735
736
737
  }
  
  /*
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
738
   * Remove a write request from an inode
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
739
740
741
   */
  static void nfs_inode_remove_request(struct nfs_page *req)
  {
4b9bb25b3   Trond Myklebust   NFS: Switch to us...
742
743
  	struct address_space *mapping = page_file_mapping(req->wb_page);
  	struct inode *inode = mapping->host;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
744
  	struct nfs_inode *nfsi = NFS_I(inode);
20633f042   Weston Andros Adamson   nfs: page group s...
745
  	struct nfs_page *head;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
746

20633f042   Weston Andros Adamson   nfs: page group s...
747
748
  	if (nfs_page_group_sync_on_bit(req, PG_REMOVE)) {
  		head = req->wb_head;
4b9bb25b3   Trond Myklebust   NFS: Switch to us...
749
  		spin_lock(&mapping->private_lock);
67911c8f1   Anna Schumaker   NFS: Add nfs_comm...
750
  		if (likely(head->wb_page && !PageSwapCache(head->wb_page))) {
20633f042   Weston Andros Adamson   nfs: page group s...
751
752
753
754
  			set_page_private(head->wb_page, 0);
  			ClearPagePrivate(head->wb_page);
  			clear_bit(PG_MAPPED, &head->wb_flags);
  		}
4b9bb25b3   Trond Myklebust   NFS: Switch to us...
755
  		spin_unlock(&mapping->private_lock);
29418aa4b   Mel Gorman   nfs: disable data...
756
  	}
17089a29a   Weston Andros Adamson   nfs: mark nfs_pag...
757

33ea5aaa8   ZhangXiaoxu   nfs: Fix nfsi->nr...
758
  	if (test_and_clear_bit(PG_INODE_REF, &req->wb_flags)) {
17089a29a   Weston Andros Adamson   nfs: mark nfs_pag...
759
  		nfs_release_request(req);
33ea5aaa8   ZhangXiaoxu   nfs: Fix nfsi->nr...
760
761
  		atomic_long_dec(&nfsi->nrequests);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
762
  }
61822ab5e   Trond Myklebust   NFS: Ensure we on...
763
  static void
6d884e8fc   Fred   nfs: nfs_redirty_...
764
  nfs_mark_request_dirty(struct nfs_page *req)
61822ab5e   Trond Myklebust   NFS: Ensure we on...
765
  {
67911c8f1   Anna Schumaker   NFS: Add nfs_comm...
766
767
  	if (req->wb_page)
  		__set_page_dirty_nobuffers(req->wb_page);
61822ab5e   Trond Myklebust   NFS: Ensure we on...
768
  }
3a3908c8b   Trond Myklebust   NFS: Fix a compil...
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
  /*
   * nfs_page_search_commits_for_head_request_locked
   *
   * Search through commit lists on @inode for the head request for @page.
   * Must be called while holding the inode (which is cinfo) lock.
   *
   * Returns the head request if found, or NULL if not found.
   */
  static struct nfs_page *
  nfs_page_search_commits_for_head_request_locked(struct nfs_inode *nfsi,
  						struct page *page)
  {
  	struct nfs_page *freq, *t;
  	struct nfs_commit_info cinfo;
  	struct inode *inode = &nfsi->vfs_inode;
  
  	nfs_init_cinfo_from_inode(&cinfo, inode);
  
  	/* search through pnfs commit lists */
  	freq = pnfs_search_commit_reqs(inode, &cinfo, page);
  	if (freq)
  		return freq->wb_head;
  
  	/* Linearly search the commit list for the correct request */
  	list_for_each_entry_safe(freq, t, &cinfo.mds->list, wb_list) {
  		if (freq->wb_page == page)
  			return freq->wb_head;
  	}
  
  	return NULL;
  }
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
800
  /**
86d80f973   Trond Myklebust   NFSv4.1/pnfs: Fix...
801
802
803
804
805
806
807
808
809
   * nfs_request_add_commit_list_locked - add request to a commit list
   * @req: pointer to a struct nfs_page
   * @dst: commit list head
   * @cinfo: holds list lock and accounting info
   *
   * This sets the PG_CLEAN bit, updates the cinfo count of
   * number of outstanding requests requiring a commit as well as
   * the MM page stats.
   *
e824f99ad   Trond Myklebust   NFSv4: Use a mute...
810
811
   * The caller must hold NFS_I(cinfo->inode)->commit_mutex, and the
   * nfs_page lock.
86d80f973   Trond Myklebust   NFSv4.1/pnfs: Fix...
812
813
814
815
816
817
818
   */
  void
  nfs_request_add_commit_list_locked(struct nfs_page *req, struct list_head *dst,
  			    struct nfs_commit_info *cinfo)
  {
  	set_bit(PG_CLEAN, &req->wb_flags);
  	nfs_list_add_request(req, dst);
5cb953d4b   Trond Myklebust   NFS: Use an atomi...
819
  	atomic_long_inc(&cinfo->mds->ncommit);
86d80f973   Trond Myklebust   NFSv4.1/pnfs: Fix...
820
821
822
823
  }
  EXPORT_SYMBOL_GPL(nfs_request_add_commit_list_locked);
  
  /**
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
824
825
   * nfs_request_add_commit_list - add request to a commit list
   * @req: pointer to a struct nfs_page
ea2cf2282   Fred Isaman   NFS: create struc...
826
   * @cinfo: holds list lock and accounting info
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
827
   *
ea2cf2282   Fred Isaman   NFS: create struc...
828
   * This sets the PG_CLEAN bit, updates the cinfo count of
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
829
830
831
   * number of outstanding requests requiring a commit as well as
   * the MM page stats.
   *
ea2cf2282   Fred Isaman   NFS: create struc...
832
   * The caller must _not_ hold the cinfo->lock, but must be
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
833
   * holding the nfs_page lock.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
834
   */
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
835
  void
6272dcc6b   Anna Schumaker   NFS: Simplify nfs...
836
  nfs_request_add_commit_list(struct nfs_page *req, struct nfs_commit_info *cinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
837
  {
e824f99ad   Trond Myklebust   NFSv4: Use a mute...
838
  	mutex_lock(&NFS_I(cinfo->inode)->commit_mutex);
6272dcc6b   Anna Schumaker   NFS: Simplify nfs...
839
  	nfs_request_add_commit_list_locked(req, &cinfo->mds->list, cinfo);
e824f99ad   Trond Myklebust   NFSv4: Use a mute...
840
  	mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
67911c8f1   Anna Schumaker   NFS: Add nfs_comm...
841
842
  	if (req->wb_page)
  		nfs_mark_page_unstable(req->wb_page, cinfo);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
843
  }
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
844
845
846
847
848
  EXPORT_SYMBOL_GPL(nfs_request_add_commit_list);
  
  /**
   * nfs_request_remove_commit_list - Remove request from a commit list
   * @req: pointer to a nfs_page
ea2cf2282   Fred Isaman   NFS: create struc...
849
   * @cinfo: holds list lock and accounting info
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
850
   *
ea2cf2282   Fred Isaman   NFS: create struc...
851
   * This clears the PG_CLEAN bit, and updates the cinfo's count of
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
852
853
854
   * number of outstanding requests requiring a commit
   * It does not update the MM page stats.
   *
ea2cf2282   Fred Isaman   NFS: create struc...
855
   * The caller _must_ hold the cinfo->lock and the nfs_page lock.
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
856
857
   */
  void
ea2cf2282   Fred Isaman   NFS: create struc...
858
859
  nfs_request_remove_commit_list(struct nfs_page *req,
  			       struct nfs_commit_info *cinfo)
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
860
  {
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
861
862
863
  	if (!test_and_clear_bit(PG_CLEAN, &(req)->wb_flags))
  		return;
  	nfs_list_remove_request(req);
5cb953d4b   Trond Myklebust   NFS: Use an atomi...
864
  	atomic_long_dec(&cinfo->mds->ncommit);
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
865
866
  }
  EXPORT_SYMBOL_GPL(nfs_request_remove_commit_list);
ea2cf2282   Fred Isaman   NFS: create struc...
867
868
869
  static void nfs_init_cinfo_from_inode(struct nfs_commit_info *cinfo,
  				      struct inode *inode)
  {
fe238e601   Dave Wysochanski   NFS: Save struct ...
870
  	cinfo->inode = inode;
ea2cf2282   Fred Isaman   NFS: create struc...
871
872
  	cinfo->mds = &NFS_I(inode)->commit_info;
  	cinfo->ds = pnfs_get_ds_info(inode);
b359f9d09   Fred Isaman   NFS: add dreq to ...
873
  	cinfo->dreq = NULL;
f453a54a0   Fred Isaman   NFS: create nfs_c...
874
  	cinfo->completion_ops = &nfs_commit_completion_ops;
ea2cf2282   Fred Isaman   NFS: create struc...
875
876
877
878
879
880
  }
  
  void nfs_init_cinfo(struct nfs_commit_info *cinfo,
  		    struct inode *inode,
  		    struct nfs_direct_req *dreq)
  {
1763da123   Fred Isaman   NFS: rewrite dire...
881
882
883
884
  	if (dreq)
  		nfs_init_cinfo_from_dreq(cinfo, dreq);
  	else
  		nfs_init_cinfo_from_inode(cinfo, inode);
ea2cf2282   Fred Isaman   NFS: create struc...
885
886
  }
  EXPORT_SYMBOL_GPL(nfs_init_cinfo);
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
887
888
889
890
  
  /*
   * Add a request to the inode's commit list.
   */
1763da123   Fred Isaman   NFS: rewrite dire...
891
  void
ea2cf2282   Fred Isaman   NFS: create struc...
892
  nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg,
b57ff1303   Weston Andros Adamson   pnfs: pass ds_com...
893
  			struct nfs_commit_info *cinfo, u32 ds_commit_idx)
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
894
  {
b57ff1303   Weston Andros Adamson   pnfs: pass ds_com...
895
  	if (pnfs_mark_request_commit(req, lseg, cinfo, ds_commit_idx))
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
896
  		return;
6272dcc6b   Anna Schumaker   NFS: Simplify nfs...
897
  	nfs_request_add_commit_list(req, cinfo);
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
898
  }
8e821cad1   Trond Myklebust   NFS: clean up the...
899

d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
900
901
902
  static void
  nfs_clear_page_commit(struct page *page)
  {
8d92890bd   NeilBrown   mm/writeback: dis...
903
  	dec_node_page_state(page, NR_WRITEBACK);
93f78d882   Tejun Heo   writeback: move b...
904
  	dec_wb_stat(&inode_to_bdi(page_file_mapping(page)->host)->wb,
8d92890bd   NeilBrown   mm/writeback: dis...
905
  		    WB_WRITEBACK);
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
906
  }
b5bab9bf9   Trond Myklebust   NFS: Reduce inode...
907
  /* Called holding the request lock on @req */
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
908
  static void
e468bae97   Trond Myklebust   NFS: Allow redirt...
909
910
  nfs_clear_request_commit(struct nfs_page *req)
  {
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
911
  	if (test_bit(PG_CLEAN, &req->wb_flags)) {
9fcd5960e   Trond Myklebust   NFS: Add a helper...
912
913
  		struct nfs_open_context *ctx = nfs_req_openctx(req);
  		struct inode *inode = d_inode(ctx->dentry);
ea2cf2282   Fred Isaman   NFS: create struc...
914
  		struct nfs_commit_info cinfo;
e468bae97   Trond Myklebust   NFS: Allow redirt...
915

ea2cf2282   Fred Isaman   NFS: create struc...
916
  		nfs_init_cinfo_from_inode(&cinfo, inode);
e824f99ad   Trond Myklebust   NFSv4: Use a mute...
917
  		mutex_lock(&NFS_I(inode)->commit_mutex);
ea2cf2282   Fred Isaman   NFS: create struc...
918
  		if (!pnfs_clear_request_commit(req, &cinfo)) {
ea2cf2282   Fred Isaman   NFS: create struc...
919
  			nfs_request_remove_commit_list(req, &cinfo);
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
920
  		}
e824f99ad   Trond Myklebust   NFSv4: Use a mute...
921
  		mutex_unlock(&NFS_I(inode)->commit_mutex);
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
922
  		nfs_clear_page_commit(req->wb_page);
e468bae97   Trond Myklebust   NFS: Allow redirt...
923
  	}
e468bae97   Trond Myklebust   NFS: Allow redirt...
924
  }
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
925
  int nfs_write_need_commit(struct nfs_pgio_header *hdr)
8e821cad1   Trond Myklebust   NFS: clean up the...
926
  {
c65e6254c   Weston Andros Adamson   nfs: remove unuse...
927
  	if (hdr->verf.committed == NFS_DATA_SYNC)
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
928
  		return hdr->lseg == NULL;
c65e6254c   Weston Andros Adamson   nfs: remove unuse...
929
  	return hdr->verf.committed != NFS_FILE_SYNC;
8e821cad1   Trond Myklebust   NFS: clean up the...
930
  }
919e3bd9a   Trond Myklebust   NFS: Ensure we co...
931
932
933
934
  static void nfs_async_write_init(struct nfs_pgio_header *hdr)
  {
  	nfs_io_completion_get(hdr->io_completion);
  }
061ae2edb   Fred Isaman   NFS: create compl...
935
  static void nfs_write_completion(struct nfs_pgio_header *hdr)
8e821cad1   Trond Myklebust   NFS: clean up the...
936
  {
ea2cf2282   Fred Isaman   NFS: create struc...
937
  	struct nfs_commit_info cinfo;
6c75dc0d4   Fred Isaman   NFS: merge _full ...
938
939
940
941
  	unsigned long bytes = 0;
  
  	if (test_bit(NFS_IOHDR_REDO, &hdr->flags))
  		goto out;
ea2cf2282   Fred Isaman   NFS: create struc...
942
  	nfs_init_cinfo_from_inode(&cinfo, hdr->inode);
6c75dc0d4   Fred Isaman   NFS: merge _full ...
943
944
  	while (!list_empty(&hdr->pages)) {
  		struct nfs_page *req = nfs_list_entry(hdr->pages.next);
6c75dc0d4   Fred Isaman   NFS: merge _full ...
945
946
947
948
949
  
  		bytes += req->wb_bytes;
  		nfs_list_remove_request(req);
  		if (test_bit(NFS_IOHDR_ERROR, &hdr->flags) &&
  		    (hdr->good_bytes < bytes)) {
861e1671b   Chuck Lever   NFS: Introduce tr...
950
  			trace_nfs_comp_error(req, hdr->error);
6fbda89b2   Trond Myklebust   NFS: Replace cust...
951
  			nfs_mapping_set_error(req->wb_page, hdr->error);
6c75dc0d4   Fred Isaman   NFS: merge _full ...
952
953
  			goto remove_req;
  		}
c65e6254c   Weston Andros Adamson   nfs: remove unuse...
954
  		if (nfs_write_need_commit(hdr)) {
33344e0f7   Trond Myklebust   pNFS: Add trackin...
955
956
  			/* Reset wb_nio, since the write was successful. */
  			req->wb_nio = 0;
f79d06f54   Anna Schumaker   NFS: Move the wri...
957
  			memcpy(&req->wb_verf, &hdr->verf.verifier, sizeof(req->wb_verf));
b57ff1303   Weston Andros Adamson   pnfs: pass ds_com...
958
  			nfs_mark_request_commit(req, hdr->lseg, &cinfo,
a7d42ddb3   Weston Andros Adamson   nfs: add mirrorin...
959
  				hdr->pgio_mirror_idx);
6c75dc0d4   Fred Isaman   NFS: merge _full ...
960
961
962
963
964
  			goto next;
  		}
  remove_req:
  		nfs_inode_remove_request(req);
  next:
20633f042   Weston Andros Adamson   nfs: page group s...
965
  		nfs_end_page_writeback(req);
3aff4ebb9   Trond Myklebust   NFS: Prevent a de...
966
  		nfs_release_request(req);
6c75dc0d4   Fred Isaman   NFS: merge _full ...
967
968
  	}
  out:
919e3bd9a   Trond Myklebust   NFS: Ensure we co...
969
  	nfs_io_completion_put(hdr->io_completion);
6c75dc0d4   Fred Isaman   NFS: merge _full ...
970
  	hdr->release(hdr);
8e821cad1   Trond Myklebust   NFS: clean up the...
971
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
972

ce59515c1   Anna Schumaker   NFS: Create a com...
973
  unsigned long
ea2cf2282   Fred Isaman   NFS: create struc...
974
  nfs_reqs_to_commit(struct nfs_commit_info *cinfo)
fb8a1f11b   Trond Myklebust   NFS: cleanup - re...
975
  {
5cb953d4b   Trond Myklebust   NFS: Use an atomi...
976
  	return atomic_long_read(&cinfo->mds->ncommit);
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
977
  }
e824f99ad   Trond Myklebust   NFSv4: Use a mute...
978
  /* NFS_I(cinfo->inode)->commit_mutex held by caller */
1763da123   Fred Isaman   NFS: rewrite dire...
979
  int
ea2cf2282   Fred Isaman   NFS: create struc...
980
981
  nfs_scan_commit_list(struct list_head *src, struct list_head *dst,
  		     struct nfs_commit_info *cinfo, int max)
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
982
  {
137da553d   Trond Myklebust   NFS: nfs_lock_and...
983
  	struct nfs_page *req, *tmp;
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
984
  	int ret = 0;
137da553d   Trond Myklebust   NFS: nfs_lock_and...
985
986
  restart:
  	list_for_each_entry_safe(req, tmp, src, wb_list) {
7ad84aa94   Trond Myklebust   NFS: Clean up - s...
987
  		kref_get(&req->wb_kref);
2ce209c42   Trond Myklebust   NFS: Wait for req...
988
989
  		if (!nfs_lock_request(req)) {
  			int status;
137da553d   Trond Myklebust   NFS: nfs_lock_and...
990
991
992
993
994
995
996
  
  			/* Prevent deadlock with nfs_lock_and_join_requests */
  			if (!list_empty(dst)) {
  				nfs_release_request(req);
  				continue;
  			}
  			/* Ensure we make progress to prevent livelock */
2ce209c42   Trond Myklebust   NFS: Wait for req...
997
998
999
1000
1001
1002
  			mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
  			status = nfs_wait_on_request(req);
  			nfs_release_request(req);
  			mutex_lock(&NFS_I(cinfo->inode)->commit_mutex);
  			if (status < 0)
  				break;
137da553d   Trond Myklebust   NFS: nfs_lock_and...
1003
  			goto restart;
2ce209c42   Trond Myklebust   NFS: Wait for req...
1004
  		}
ea2cf2282   Fred Isaman   NFS: create struc...
1005
  		nfs_request_remove_commit_list(req, cinfo);
5d2a9d9da   Trond Myklebust   NFS: Remove pnfs_...
1006
  		clear_bit(PG_COMMIT_TO_DS, &req->wb_flags);
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
1007
1008
  		nfs_list_add_request(req, dst);
  		ret++;
1763da123   Fred Isaman   NFS: rewrite dire...
1009
  		if ((ret == max) && !cinfo->dreq)
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
1010
  			break;
e824f99ad   Trond Myklebust   NFSv4: Use a mute...
1011
  		cond_resched();
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
1012
1013
  	}
  	return ret;
fb8a1f11b   Trond Myklebust   NFS: cleanup - re...
1014
  }
5d2a9d9da   Trond Myklebust   NFS: Remove pnfs_...
1015
  EXPORT_SYMBOL_GPL(nfs_scan_commit_list);
fb8a1f11b   Trond Myklebust   NFS: cleanup - re...
1016

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1017
1018
1019
  /*
   * nfs_scan_commit - Scan an inode for commit requests
   * @inode: NFS inode to scan
ea2cf2282   Fred Isaman   NFS: create struc...
1020
1021
   * @dst: mds destination list
   * @cinfo: mds and ds lists of reqs ready to commit
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1022
1023
1024
1025
   *
   * Moves requests from the inode's 'commit' request list.
   * The requests are *not* checked to ensure that they form a contiguous set.
   */
1763da123   Fred Isaman   NFS: rewrite dire...
1026
  int
ea2cf2282   Fred Isaman   NFS: create struc...
1027
1028
  nfs_scan_commit(struct inode *inode, struct list_head *dst,
  		struct nfs_commit_info *cinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1029
  {
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
1030
  	int ret = 0;
fb8a1f11b   Trond Myklebust   NFS: cleanup - re...
1031

5cb953d4b   Trond Myklebust   NFS: Use an atomi...
1032
1033
  	if (!atomic_long_read(&cinfo->mds->ncommit))
  		return 0;
e824f99ad   Trond Myklebust   NFSv4: Use a mute...
1034
  	mutex_lock(&NFS_I(cinfo->inode)->commit_mutex);
5cb953d4b   Trond Myklebust   NFS: Use an atomi...
1035
  	if (atomic_long_read(&cinfo->mds->ncommit) > 0) {
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
1036
  		const int max = INT_MAX;
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
1037

ea2cf2282   Fred Isaman   NFS: create struc...
1038
1039
1040
  		ret = nfs_scan_commit_list(&cinfo->mds->list, dst,
  					   cinfo, max);
  		ret += pnfs_scan_commit_lists(inode, cinfo, max - ret);
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
1041
  	}
e824f99ad   Trond Myklebust   NFSv4: Use a mute...
1042
  	mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
ff778d02b   Trond Myklebust   NFS: Add a count ...
1043
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1044
  }
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
1045

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1046
  /*
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1047
1048
   * Search for an existing write request, and attempt to update
   * it to reflect a new dirty region on a given page.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1049
   *
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1050
1051
   * If the attempt fails, then the existing request is flushed out
   * to disk.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1052
   */
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1053
1054
1055
1056
  static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
  		struct page *page,
  		unsigned int offset,
  		unsigned int bytes)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1057
  {
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1058
1059
1060
1061
  	struct nfs_page *req;
  	unsigned int rqend;
  	unsigned int end;
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1062
  	end = offset + bytes;
f6032f216   Trond Myklebust   NFS: Teach nfs_tr...
1063
1064
1065
  	req = nfs_lock_and_join_requests(page);
  	if (IS_ERR_OR_NULL(req))
  		return req;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1066

f6032f216   Trond Myklebust   NFS: Teach nfs_tr...
1067
1068
1069
1070
1071
1072
1073
1074
1075
  	rqend = req->wb_offset + req->wb_bytes;
  	/*
  	 * Tell the caller to flush out the request if
  	 * the offsets are non-contiguous.
  	 * Note: nfs_flush_incompatible() will already
  	 * have flushed out requests having wrong owners.
  	 */
  	if (offset > rqend || end < req->wb_offset)
  		goto out_flushme;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1076
1077
1078
1079
1080
  
  	/* Okay, the request matches. Update the region */
  	if (offset < req->wb_offset) {
  		req->wb_offset = offset;
  		req->wb_pgbase = offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1081
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1082
1083
  	if (end > rqend)
  		req->wb_bytes = end - req->wb_offset;
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1084
1085
  	else
  		req->wb_bytes = rqend - req->wb_offset;
33344e0f7   Trond Myklebust   pNFS: Add trackin...
1086
  	req->wb_nio = 0;
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1087
1088
  	return req;
  out_flushme:
f6032f216   Trond Myklebust   NFS: Teach nfs_tr...
1089
1090
1091
1092
1093
1094
1095
  	/*
  	 * Note: we mark the request dirty here because
  	 * nfs_lock_and_join_requests() cannot preserve
  	 * commit flags, so we have to replay the write.
  	 */
  	nfs_mark_request_dirty(req);
  	nfs_unlock_and_release_request(req);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1096
  	error = nfs_wb_page(inode, page);
f6032f216   Trond Myklebust   NFS: Teach nfs_tr...
1097
  	return (error < 0) ? ERR_PTR(error) : NULL;
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
  }
  
  /*
   * Try to update an existing write request, or create one if there is none.
   *
   * Note: Should always be called with the Page Lock held to prevent races
   * if we have to add a new request. Also assumes that the caller has
   * already called nfs_flush_incompatible() if necessary.
   */
  static struct nfs_page * nfs_setup_write_request(struct nfs_open_context* ctx,
  		struct page *page, unsigned int offset, unsigned int bytes)
  {
d56b4ddf7   Mel Gorman   nfs: teach the NF...
1110
  	struct inode *inode = page_file_mapping(page)->host;
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1111
  	struct nfs_page	*req;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1112

e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1113
1114
1115
  	req = nfs_try_to_update_request(inode, page, offset, bytes);
  	if (req != NULL)
  		goto out;
28b1d3f5a   Trond Myklebust   NFS: Remove unuse...
1116
  	req = nfs_create_request(ctx, page, offset, bytes);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1117
1118
  	if (IS_ERR(req))
  		goto out;
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
1119
  	nfs_inode_add_request(inode, req);
efc91ed01   Trond Myklebust   NFS: Optimise app...
1120
  out:
61e930a90   Trond Myklebust   NFS: Fix a writeb...
1121
  	return req;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1122
  }
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
  static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page,
  		unsigned int offset, unsigned int count)
  {
  	struct nfs_page	*req;
  
  	req = nfs_setup_write_request(ctx, page, offset, count);
  	if (IS_ERR(req))
  		return PTR_ERR(req);
  	/* Update file length */
  	nfs_grow_file(page, offset, count);
d72ddcbab   Weston Andros Adamson   nfs: page group s...
1133
  	nfs_mark_uptodate(req);
a6305ddb0   Trond Myklebust   NFS: Fix a race w...
1134
  	nfs_mark_request_dirty(req);
1d1afcbc2   Trond Myklebust   NFS: Clean up - R...
1135
  	nfs_unlock_and_release_request(req);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1136
1137
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1138
1139
  int nfs_flush_incompatible(struct file *file, struct page *page)
  {
cd3758e37   Trond Myklebust   NFS: Replace file...
1140
  	struct nfs_open_context *ctx = nfs_file_open_context(file);
2a369153c   Trond Myklebust   NFS: Clean up hel...
1141
  	struct nfs_lock_context *l_ctx;
bd61e0a9c   Jeff Layton   locks: convert po...
1142
  	struct file_lock_context *flctx = file_inode(file)->i_flctx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1143
  	struct nfs_page	*req;
1a54533ec   Trond Myklebust   NFS: Add nfs_set_...
1144
  	int do_flush, status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1145
1146
1147
1148
1149
1150
1151
1152
  	/*
  	 * Look for a request corresponding to this page. If there
  	 * is one, and it belongs to another file, we flush it out
  	 * before we try to copy anything into the page. Do this
  	 * due to the lack of an ACCESS-type call in NFSv2.
  	 * Also do the same if we find a request from an existing
  	 * dropped page.
  	 */
1a54533ec   Trond Myklebust   NFS: Add nfs_set_...
1153
  	do {
84d3a9a91   Weston Andros Adamson   nfs: change find_...
1154
  		req = nfs_page_find_head_request(page);
1a54533ec   Trond Myklebust   NFS: Add nfs_set_...
1155
1156
  		if (req == NULL)
  			return 0;
2a369153c   Trond Myklebust   NFS: Clean up hel...
1157
  		l_ctx = req->wb_lock_context;
138a2935d   Trond Myklebust   NFS: Relax requir...
1158
  		do_flush = req->wb_page != page ||
9fcd5960e   Trond Myklebust   NFS: Add a helper...
1159
  			!nfs_match_open_context(nfs_req_openctx(req), ctx);
bd61e0a9c   Jeff Layton   locks: convert po...
1160
1161
1162
  		if (l_ctx && flctx &&
  		    !(list_empty_careful(&flctx->flc_posix) &&
  		      list_empty_careful(&flctx->flc_flock))) {
d51fdb87a   NeilBrown   NFS: discard nfs_...
1163
  			do_flush |= l_ctx->lockowner != current->files;
5263e31e4   Jeff Layton   locks: move flock...
1164
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1165
  		nfs_release_request(req);
1a54533ec   Trond Myklebust   NFS: Add nfs_set_...
1166
1167
  		if (!do_flush)
  			return 0;
d56b4ddf7   Mel Gorman   nfs: teach the NF...
1168
  		status = nfs_wb_page(page_file_mapping(page)->host, page);
1a54533ec   Trond Myklebust   NFS: Add nfs_set_...
1169
1170
  	} while (status == 0);
  	return status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1171
1172
1173
  }
  
  /*
dc24826bf   Andy Adamson   NFS avoid expired...
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
   * Avoid buffered writes when a open context credential's key would
   * expire soon.
   *
   * Returns -EACCES if the key will expire within RPC_KEY_EXPIRE_FAIL.
   *
   * Return 0 and set a credential flag which triggers the inode to flush
   * and performs  NFS_FILE_SYNC writes if the key will expired within
   * RPC_KEY_EXPIRE_TIMEO.
   */
  int
  nfs_key_timeout_notify(struct file *filp, struct inode *inode)
  {
  	struct nfs_open_context *ctx = nfs_file_open_context(filp);
dc24826bf   Andy Adamson   NFS avoid expired...
1187

ddf529eee   NeilBrown   NFS: move credent...
1188
1189
1190
1191
1192
  	if (nfs_ctx_key_to_expire(ctx, inode) &&
  	    !ctx->ll_cred)
  		/* Already expired! */
  		return -EACCES;
  	return 0;
dc24826bf   Andy Adamson   NFS avoid expired...
1193
1194
1195
1196
1197
  }
  
  /*
   * Test if the open context credential key is marked to expire soon.
   */
ce52914eb   Scott Mayhew   sunrpc: move NO_C...
1198
  bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx, struct inode *inode)
dc24826bf   Andy Adamson   NFS avoid expired...
1199
  {
ce52914eb   Scott Mayhew   sunrpc: move NO_C...
1200
  	struct rpc_auth *auth = NFS_SERVER(inode)->client->cl_auth;
ddf529eee   NeilBrown   NFS: move credent...
1201
1202
  	struct rpc_cred *cred = ctx->ll_cred;
  	struct auth_cred acred = {
a52458b48   NeilBrown   NFS/NFSD/SUNRPC: ...
1203
  		.cred = ctx->cred,
ddf529eee   NeilBrown   NFS: move credent...
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
  	};
  
  	if (cred && !cred->cr_ops->crmatch(&acred, cred, 0)) {
  		put_rpccred(cred);
  		ctx->ll_cred = NULL;
  		cred = NULL;
  	}
  	if (!cred)
  		cred = auth->au_ops->lookup_cred(auth, &acred, 0);
  	if (!cred || IS_ERR(cred))
  		return true;
  	ctx->ll_cred = cred;
  	return !!(cred->cr_ops->crkey_timeout &&
  		  cred->cr_ops->crkey_timeout(cred));
dc24826bf   Andy Adamson   NFS avoid expired...
1218
1219
1220
  }
  
  /*
5d47a3560   Trond Myklebust   NFS: Fix a potent...
1221
1222
1223
1224
   * If the page cache is marked as unsafe or invalid, then we can't rely on
   * the PageUptodate() flag. In this case, we will need to turn off
   * write optimisations that depend on the page contents being correct.
   */
8d197a568   Trond Myklebust   NFS: Always trust...
1225
  static bool nfs_write_pageuptodate(struct page *page, struct inode *inode)
5d47a3560   Trond Myklebust   NFS: Fix a potent...
1226
  {
d529ef83c   Jeff Layton   NFS: fix the hand...
1227
  	struct nfs_inode *nfsi = NFS_I(inode);
8d197a568   Trond Myklebust   NFS: Always trust...
1228
1229
  	if (nfs_have_delegated_attributes(inode))
  		goto out;
18dd78c42   Scott Mayhew   nfs: Fix cache_va...
1230
  	if (nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE)
d529ef83c   Jeff Layton   NFS: fix the hand...
1231
  		return false;
4db72b40f   Jeff Layton   nfs: add memory b...
1232
  	smp_rmb();
d529ef83c   Jeff Layton   NFS: fix the hand...
1233
  	if (test_bit(NFS_INO_INVALIDATING, &nfsi->flags))
8d197a568   Trond Myklebust   NFS: Always trust...
1234
1235
  		return false;
  out:
18dd78c42   Scott Mayhew   nfs: Fix cache_va...
1236
1237
  	if (nfsi->cache_validity & NFS_INO_INVALID_DATA)
  		return false;
8d197a568   Trond Myklebust   NFS: Always trust...
1238
  	return PageUptodate(page) != 0;
5d47a3560   Trond Myklebust   NFS: Fix a potent...
1239
  }
5263e31e4   Jeff Layton   locks: move flock...
1240
1241
1242
1243
1244
1245
  static bool
  is_whole_file_wrlock(struct file_lock *fl)
  {
  	return fl->fl_start == 0 && fl->fl_end == OFFSET_MAX &&
  			fl->fl_type == F_WRLCK;
  }
c7559663e   Scott Mayhew   NFS: Allow nfs_up...
1246
1247
1248
1249
1250
  /* If we know the page is up to date, and we're not using byte range locks (or
   * if we have the whole file locked for writing), it may be more efficient to
   * extend the write to cover the entire page in order to avoid fragmentation
   * inefficiencies.
   *
263b4509e   Scott Mayhew   nfs: always make ...
1251
1252
   * If the file is opened for synchronous writes then we can just skip the rest
   * of the checks.
c7559663e   Scott Mayhew   NFS: Allow nfs_up...
1253
1254
1255
   */
  static int nfs_can_extend_write(struct file *file, struct page *page, struct inode *inode)
  {
5263e31e4   Jeff Layton   locks: move flock...
1256
1257
1258
  	int ret;
  	struct file_lock_context *flctx = inode->i_flctx;
  	struct file_lock *fl;
c7559663e   Scott Mayhew   NFS: Allow nfs_up...
1259
1260
  	if (file->f_flags & O_DSYNC)
  		return 0;
263b4509e   Scott Mayhew   nfs: always make ...
1261
1262
  	if (!nfs_write_pageuptodate(page, inode))
  		return 0;
c7559663e   Scott Mayhew   NFS: Allow nfs_up...
1263
1264
  	if (NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE))
  		return 1;
bd61e0a9c   Jeff Layton   locks: convert po...
1265
1266
  	if (!flctx || (list_empty_careful(&flctx->flc_flock) &&
  		       list_empty_careful(&flctx->flc_posix)))
8fa4592a1   Trond Myklebust   NFS: Fix a write ...
1267
  		return 1;
5263e31e4   Jeff Layton   locks: move flock...
1268
1269
  
  	/* Check to see if there are whole file write locks */
5263e31e4   Jeff Layton   locks: move flock...
1270
  	ret = 0;
6109c8503   Jeff Layton   locks: add a dedi...
1271
  	spin_lock(&flctx->flc_lock);
bd61e0a9c   Jeff Layton   locks: convert po...
1272
1273
1274
1275
1276
1277
  	if (!list_empty(&flctx->flc_posix)) {
  		fl = list_first_entry(&flctx->flc_posix, struct file_lock,
  					fl_list);
  		if (is_whole_file_wrlock(fl))
  			ret = 1;
  	} else if (!list_empty(&flctx->flc_flock)) {
5263e31e4   Jeff Layton   locks: move flock...
1278
1279
1280
1281
1282
  		fl = list_first_entry(&flctx->flc_flock, struct file_lock,
  					fl_list);
  		if (fl->fl_type == F_WRLCK)
  			ret = 1;
  	}
6109c8503   Jeff Layton   locks: add a dedi...
1283
  	spin_unlock(&flctx->flc_lock);
5263e31e4   Jeff Layton   locks: move flock...
1284
  	return ret;
c7559663e   Scott Mayhew   NFS: Allow nfs_up...
1285
  }
5d47a3560   Trond Myklebust   NFS: Fix a potent...
1286
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1287
1288
1289
1290
1291
1292
1293
1294
   * Update and possibly write a cached page of an NFS file.
   *
   * XXX: Keep an eye on generic_file_read to make sure it doesn't do bad
   * things with a page scheduled for an RPC call (e.g. invalidate it).
   */
  int nfs_updatepage(struct file *file, struct page *page,
  		unsigned int offset, unsigned int count)
  {
cd3758e37   Trond Myklebust   NFS: Replace file...
1295
  	struct nfs_open_context *ctx = nfs_file_open_context(file);
d2ceb7e57   Benjamin Coddington   NFS: Don't use pa...
1296
1297
  	struct address_space *mapping = page_file_mapping(page);
  	struct inode	*inode = mapping->host;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1298
  	int		status = 0;
91d5b4702   Chuck Lever   NFS: add I/O perf...
1299
  	nfs_inc_stats(inode, NFSIOS_VFSUPDATEPAGE);
6de1472f1   Al Viro   nfs: use %p[dD] i...
1300
1301
1302
  	dprintk("NFS:       nfs_updatepage(%pD2 %d@%lld)
  ",
  		file, count, (long long)(page_file_offset(page) + offset));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1303

149a4fddd   Benjamin Coddington   nfs: don't create...
1304
1305
  	if (!count)
  		goto out;
c7559663e   Scott Mayhew   NFS: Allow nfs_up...
1306
  	if (nfs_can_extend_write(file, page, inode)) {
49a70f278   Trond Myklebust   NFS: Cleanup: add...
1307
  		count = max(count + offset, nfs_page_length(page));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1308
  		offset = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1309
  	}
e21195a74   Trond Myklebust   NFS: More cleanup...
1310
  	status = nfs_writepage_setup(ctx, page, offset, count);
03fa9e84e   Trond Myklebust   NFS: nfs_updatepa...
1311
  	if (status < 0)
d2ceb7e57   Benjamin Coddington   NFS: Don't use pa...
1312
  		nfs_set_pageerror(mapping);
59b7c05ff   Trond Myklebust   Revert "NFS: Ensu...
1313
1314
  	else
  		__set_page_dirty_nobuffers(page);
149a4fddd   Benjamin Coddington   nfs: don't create...
1315
  out:
48186c7d5   Chuck Lever   NFS: Fix trace de...
1316
1317
  	dprintk("NFS:       nfs_updatepage returns %d (isize %lld)
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1318
  			status, (long long)i_size_read(inode));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1319
1320
  	return status;
  }
3ff7576dd   Trond Myklebust   SUNRPC: Clean up ...
1321
  static int flush_task_priority(int how)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1322
1323
1324
1325
1326
1327
1328
1329
1330
  {
  	switch (how & (FLUSH_HIGHPRI|FLUSH_LOWPRI)) {
  		case FLUSH_HIGHPRI:
  			return RPC_PRIORITY_HIGH;
  		case FLUSH_LOWPRI:
  			return RPC_PRIORITY_LOW;
  	}
  	return RPC_PRIORITY_NORMAL;
  }
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1331
1332
  static void nfs_initiate_write(struct nfs_pgio_header *hdr,
  			       struct rpc_message *msg,
abde71f4d   Tom Haynes   pnfs: Add nfs_rpc...
1333
  			       const struct nfs_rpc_ops *rpc_ops,
1ed26f330   Anna Schumaker   NFS: Create a com...
1334
  			       struct rpc_task_setup *task_setup_data, int how)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1335
  {
3ff7576dd   Trond Myklebust   SUNRPC: Clean up ...
1336
  	int priority = flush_task_priority(how);
d138d5d17   Andy Adamson   NFSv4.1: rearrang...
1337

1ed26f330   Anna Schumaker   NFS: Create a com...
1338
  	task_setup_data->priority = priority;
fb91fb0ee   Anna Schumaker   NFS: Move call to...
1339
  	rpc_ops->write_setup(hdr, msg, &task_setup_data->rpc_client);
5bb2a7cb9   Trond Myklebust   NFS: Clean up gen...
1340
  	trace_nfs_initiate_write(hdr);
275acaafd   Trond Myklebust   NFS: Clean up: sp...
1341
  }
6d884e8fc   Fred   nfs: nfs_redirty_...
1342
1343
1344
1345
1346
1347
  /* If a nfs_flush_* function fails, it should remove reqs from @head and
   * call this on each, which will prepare them to be retried on next
   * writeback using standard nfs.
   */
  static void nfs_redirty_request(struct nfs_page *req)
  {
33344e0f7   Trond Myklebust   pNFS: Add trackin...
1348
1349
  	/* Bump the transmission count */
  	req->wb_nio++;
6d884e8fc   Fred   nfs: nfs_redirty_...
1350
  	nfs_mark_request_dirty(req);
9fcd5960e   Trond Myklebust   NFS: Add a helper...
1351
  	set_bit(NFS_CONTEXT_RESEND_WRITES, &nfs_req_openctx(req)->flags);
20633f042   Weston Andros Adamson   nfs: page group s...
1352
  	nfs_end_page_writeback(req);
3aff4ebb9   Trond Myklebust   NFS: Prevent a de...
1353
  	nfs_release_request(req);
6d884e8fc   Fred   nfs: nfs_redirty_...
1354
  }
df3accb84   Trond Myklebust   NFS: Pass error i...
1355
  static void nfs_async_write_error(struct list_head *head, int error)
6c75dc0d4   Fred Isaman   NFS: merge _full ...
1356
1357
1358
1359
1360
1361
  {
  	struct nfs_page	*req;
  
  	while (!list_empty(head)) {
  		req = nfs_list_entry(head->next);
  		nfs_list_remove_request(req);
6fbda89b2   Trond Myklebust   NFS: Replace cust...
1362
1363
1364
1365
  		if (nfs_error_is_fatal(error))
  			nfs_write_error(req, error);
  		else
  			nfs_redirty_request(req);
6c75dc0d4   Fred Isaman   NFS: merge _full ...
1366
1367
  	}
  }
dc602dd70   Trond Myklebust   NFS/pNFS: Fix up ...
1368
1369
  static void nfs_async_write_reschedule_io(struct nfs_pgio_header *hdr)
  {
df3accb84   Trond Myklebust   NFS: Pass error i...
1370
  	nfs_async_write_error(&hdr->pages, 0);
7be7b3ca1   Trond Myklebust   NFS: Ensure we im...
1371
1372
  	filemap_fdatawrite_range(hdr->inode->i_mapping, hdr->args.offset,
  			hdr->args.offset + hdr->args.count - 1);
dc602dd70   Trond Myklebust   NFS/pNFS: Fix up ...
1373
  }
061ae2edb   Fred Isaman   NFS: create compl...
1374
  static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops = {
919e3bd9a   Trond Myklebust   NFS: Ensure we co...
1375
  	.init_hdr = nfs_async_write_init,
061ae2edb   Fred Isaman   NFS: create compl...
1376
1377
  	.error_cleanup = nfs_async_write_error,
  	.completion = nfs_write_completion,
dc602dd70   Trond Myklebust   NFS/pNFS: Fix up ...
1378
  	.reschedule_io = nfs_async_write_reschedule_io,
061ae2edb   Fred Isaman   NFS: create compl...
1379
  };
57208fa7e   Bryan Schumaker   NFS: Create an wr...
1380
  void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
a20c93e31   Christoph Hellwig   nfs: remove ->wri...
1381
  			       struct inode *inode, int ioflags, bool force_mds,
061ae2edb   Fred Isaman   NFS: create compl...
1382
  			       const struct nfs_pgio_completion_ops *compl_ops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1383
  {
a20c93e31   Christoph Hellwig   nfs: remove ->wri...
1384
  	struct nfs_server *server = NFS_SERVER(inode);
41d8d5b7a   Anna Schumaker   NFS: Create a com...
1385
  	const struct nfs_pageio_ops *pg_ops = &nfs_pgio_rw_ops;
a20c93e31   Christoph Hellwig   nfs: remove ->wri...
1386
1387
1388
1389
1390
  
  #ifdef CONFIG_NFS_V4_1
  	if (server->pnfs_curr_ld && !force_mds)
  		pg_ops = server->pnfs_curr_ld->pg_write_ops;
  #endif
4a0de55c5   Anna Schumaker   NFS: Create a com...
1391
  	nfs_pageio_init(pgio, inode, pg_ops, compl_ops, &nfs_rw_write_ops,
3bde7afda   Trond Myklebust   NFS: Remove unuse...
1392
  			server->wsize, ioflags);
1751c3638   Trond Myklebust   NFS: Cleanup of t...
1393
  }
ddda8e0aa   Bryan Schumaker   NFS: Convert v2 i...
1394
  EXPORT_SYMBOL_GPL(nfs_pageio_init_write);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1395

dce81290e   Trond Myklebust   NFS: Move the pnf...
1396
1397
  void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio)
  {
a7d42ddb3   Weston Andros Adamson   nfs: add mirrorin...
1398
  	struct nfs_pgio_mirror *mirror;
6f29b9bba   Kinglong Mee   NFS: Do cleanup b...
1399
1400
  	if (pgio->pg_ops && pgio->pg_ops->pg_cleanup)
  		pgio->pg_ops->pg_cleanup(pgio);
41d8d5b7a   Anna Schumaker   NFS: Create a com...
1401
  	pgio->pg_ops = &nfs_pgio_rw_ops;
a7d42ddb3   Weston Andros Adamson   nfs: add mirrorin...
1402
1403
1404
1405
1406
  
  	nfs_pageio_stop_mirroring(pgio);
  
  	mirror = &pgio->pg_mirrors[0];
  	mirror->pg_bsize = NFS_SERVER(pgio->pg_inode)->wsize;
dce81290e   Trond Myklebust   NFS: Move the pnf...
1407
  }
1f9453578   Trond Myklebust   NFS: Clean up - s...
1408
  EXPORT_SYMBOL_GPL(nfs_pageio_reset_write_mds);
dce81290e   Trond Myklebust   NFS: Move the pnf...
1409

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1410

0b7c01533   Fred Isaman   NFS: add a struct...
1411
1412
1413
1414
1415
1416
  void nfs_commit_prepare(struct rpc_task *task, void *calldata)
  {
  	struct nfs_commit_data *data = calldata;
  
  	NFS_PROTO(data->inode)->commit_rpc_prepare(task, data);
  }
1f2edbe3f   Trond Myklebust   NFS: Don't ignore...
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
  /*
   * Special version of should_remove_suid() that ignores capabilities.
   */
  static int nfs_should_remove_suid(const struct inode *inode)
  {
  	umode_t mode = inode->i_mode;
  	int kill = 0;
  
  	/* suid always must be killed */
  	if (unlikely(mode & S_ISUID))
  		kill = ATTR_KILL_SUID;
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1428

1f2edbe3f   Trond Myklebust   NFS: Don't ignore...
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
  	/*
  	 * sgid without any exec bits is just a mandatory locking mark; leave
  	 * it alone.  If some exec bits are set, it's a real sgid; kill it.
  	 */
  	if (unlikely((mode & S_ISGID) && (mode & S_IXGRP)))
  		kill |= ATTR_KILL_SGID;
  
  	if (unlikely(kill && S_ISREG(mode)))
  		return kill;
  
  	return 0;
  }
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1441

a08a8cd37   Trond Myklebust   NFS: Add attribut...
1442
1443
1444
1445
1446
  static void nfs_writeback_check_extend(struct nfs_pgio_header *hdr,
  		struct nfs_fattr *fattr)
  {
  	struct nfs_pgio_args *argp = &hdr->args;
  	struct nfs_pgio_res *resp = &hdr->res;
2b83d3de4   Trond Myklebust   NFSv4/pnfs: Ensur...
1447
  	u64 size = argp->offset + resp->count;
a08a8cd37   Trond Myklebust   NFS: Add attribut...
1448
1449
  
  	if (!(fattr->valid & NFS_ATTR_FATTR_SIZE))
2b83d3de4   Trond Myklebust   NFSv4/pnfs: Ensur...
1450
1451
1452
  		fattr->size = size;
  	if (nfs_size_to_loff_t(fattr->size) < i_size_read(hdr->inode)) {
  		fattr->valid &= ~NFS_ATTR_FATTR_SIZE;
a08a8cd37   Trond Myklebust   NFS: Add attribut...
1453
  		return;
2b83d3de4   Trond Myklebust   NFSv4/pnfs: Ensur...
1454
1455
  	}
  	if (size != fattr->size)
a08a8cd37   Trond Myklebust   NFS: Add attribut...
1456
1457
1458
  		return;
  	/* Set attribute barrier */
  	nfs_fattr_set_barrier(fattr);
2b83d3de4   Trond Myklebust   NFSv4/pnfs: Ensur...
1459
1460
  	/* ...and update size */
  	fattr->valid |= NFS_ATTR_FATTR_SIZE;
a08a8cd37   Trond Myklebust   NFS: Add attribut...
1461
1462
1463
1464
  }
  
  void nfs_writeback_update_inode(struct nfs_pgio_header *hdr)
  {
2b83d3de4   Trond Myklebust   NFSv4/pnfs: Ensur...
1465
  	struct nfs_fattr *fattr = &hdr->fattr;
a08a8cd37   Trond Myklebust   NFS: Add attribut...
1466
  	struct inode *inode = hdr->inode;
a08a8cd37   Trond Myklebust   NFS: Add attribut...
1467
1468
1469
1470
1471
1472
  	spin_lock(&inode->i_lock);
  	nfs_writeback_check_extend(hdr, fattr);
  	nfs_post_op_update_inode_force_wcc_locked(inode, fattr);
  	spin_unlock(&inode->i_lock);
  }
  EXPORT_SYMBOL_GPL(nfs_writeback_update_inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1473
1474
1475
  /*
   * This function is called when the WRITE call is complete.
   */
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1476
1477
  static int nfs_writeback_done(struct rpc_task *task,
  			      struct nfs_pgio_header *hdr,
0eecb2145   Anna Schumaker   NFS: Create a com...
1478
  			      struct inode *inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1479
  {
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1480
  	int status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1481

f551e44ff   Chuck Lever   NFS: add comments...
1482
1483
1484
1485
1486
1487
1488
  	/*
  	 * ->write_done will attempt to use post-op attributes to detect
  	 * conflicting writes by other clients.  A strict interpretation
  	 * of close-to-open would allow us to continue caching even if
  	 * another writer had changed the file, but some applications
  	 * depend on tighter cache coherency when writing.
  	 */
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1489
  	status = NFS_PROTO(inode)->write_done(task, hdr);
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1490
  	if (status != 0)
0eecb2145   Anna Schumaker   NFS: Create a com...
1491
  		return status;
8224b2734   Chuck Lever   NFS: Add static N...
1492

d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1493
  	nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, hdr->res.count);
5bb2a7cb9   Trond Myklebust   NFS: Clean up gen...
1494
  	trace_nfs_writeback_done(task, hdr);
91d5b4702   Chuck Lever   NFS: add I/O perf...
1495

d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1496
1497
  	if (hdr->res.verf->committed < hdr->args.stable &&
  	    task->tk_status >= 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1498
1499
1500
1501
1502
1503
1504
1505
1506
  		/* We tried a write call, but the server did not
  		 * commit data to stable storage even though we
  		 * requested it.
  		 * Note: There is a known bug in Tru64 < 5.0 in which
  		 *	 the server reports NFS_DATA_SYNC, but performs
  		 *	 NFS_FILE_SYNC. We therefore implement this checking
  		 *	 as a dprintk() in order to avoid filling syslog.
  		 */
  		static unsigned long    complain;
a69aef149   Fred Isaman   NFSv4.1: pnfs fil...
1507
  		/* Note this will print the MDS for a DS write */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1508
  		if (time_before(complain, jiffies)) {
48186c7d5   Chuck Lever   NFS: Fix trace de...
1509
  			dprintk("NFS:       faulty NFS server %s:"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1510
1511
  				" (committed = %d) != (stable = %d)
  ",
cd841605f   Fred Isaman   NFS: create commo...
1512
  				NFS_SERVER(inode)->nfs_client->cl_hostname,
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1513
  				hdr->res.verf->committed, hdr->args.stable);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1514
1515
1516
  			complain = jiffies + 300 * HZ;
  		}
  	}
1f2edbe3f   Trond Myklebust   NFS: Don't ignore...
1517
1518
  
  	/* Deal with the suid/sgid bit corner case */
16e143751   Trond Myklebust   NFS: More fine gr...
1519
1520
1521
1522
1523
  	if (nfs_should_remove_suid(inode)) {
  		spin_lock(&inode->i_lock);
  		NFS_I(inode)->cache_validity |= NFS_INO_INVALID_OTHER;
  		spin_unlock(&inode->i_lock);
  	}
0eecb2145   Anna Schumaker   NFS: Create a com...
1524
1525
1526
1527
1528
1529
  	return 0;
  }
  
  /*
   * This function is called when the WRITE call is complete.
   */
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1530
1531
  static void nfs_writeback_result(struct rpc_task *task,
  				 struct nfs_pgio_header *hdr)
0eecb2145   Anna Schumaker   NFS: Create a com...
1532
  {
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1533
1534
  	struct nfs_pgio_args	*argp = &hdr->args;
  	struct nfs_pgio_res	*resp = &hdr->res;
1f2edbe3f   Trond Myklebust   NFS: Don't ignore...
1535
1536
  
  	if (resp->count < argp->count) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1537
  		static unsigned long    complain;
6c75dc0d4   Fred Isaman   NFS: merge _full ...
1538
  		/* This a short write! */
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1539
  		nfs_inc_stats(hdr->inode, NFSIOS_SHORTWRITE);
91d5b4702   Chuck Lever   NFS: add I/O perf...
1540

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1541
  		/* Has the server at least made some progress? */
6c75dc0d4   Fred Isaman   NFS: merge _full ...
1542
1543
1544
1545
1546
1547
1548
  		if (resp->count == 0) {
  			if (time_before(complain, jiffies)) {
  				printk(KERN_WARNING
  				       "NFS: Server wrote zero bytes, expected %u.
  ",
  				       argp->count);
  				complain = jiffies + 300 * HZ;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1549
  			}
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1550
  			nfs_set_pgio_error(hdr, -EIO, argp->offset);
6c75dc0d4   Fred Isaman   NFS: merge _full ...
1551
  			task->tk_status = -EIO;
136028967   Fred Isaman   NFS: change nfs_w...
1552
  			return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1553
  		}
f8417b481   Kinglong Mee   NFSv4.1/pnfs: Ret...
1554
1555
1556
1557
1558
1559
  
  		/* For non rpc-based layout drivers, retry-through-MDS */
  		if (!task->tk_ops) {
  			hdr->pnfs_error = -EAGAIN;
  			return;
  		}
6c75dc0d4   Fred Isaman   NFS: merge _full ...
1560
1561
1562
  		/* Was this an NFSv2 write or an NFSv3 stable write? */
  		if (resp->verf->committed != NFS_UNSTABLE) {
  			/* Resend from where the server left off */
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1563
  			hdr->mds_offset += resp->count;
6c75dc0d4   Fred Isaman   NFS: merge _full ...
1564
1565
1566
1567
1568
1569
1570
1571
  			argp->offset += resp->count;
  			argp->pgbase += resp->count;
  			argp->count -= resp->count;
  		} else {
  			/* Resend as a stable write in order to avoid
  			 * headaches in the case of a server crash.
  			 */
  			argp->stable = NFS_FILE_SYNC;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1572
  		}
8c9cb7149   Trond Myklebust   NFS: When resendi...
1573
1574
  		resp->count = 0;
  		resp->verf->committed = 0;
6c75dc0d4   Fred Isaman   NFS: merge _full ...
1575
  		rpc_restart_call_prepare(task);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1576
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1577
  }
af7cf0579   Trond Myklebust   NFS: Allow multip...
1578
  static int wait_on_commit(struct nfs_mds_commit_info *cinfo)
71d0a6112   Trond Myklebust   NFS: Fix an unsta...
1579
  {
723c921e7   Peter Zijlstra   sched/wait, fs/nf...
1580
1581
  	return wait_var_event_killable(&cinfo->rpcs_out,
  				       !atomic_read(&cinfo->rpcs_out));
af7cf0579   Trond Myklebust   NFS: Allow multip...
1582
  }
b8413f98f   Trond Myklebust   NFS: Fix a hang/i...
1583

af7cf0579   Trond Myklebust   NFS: Allow multip...
1584
1585
1586
  static void nfs_commit_begin(struct nfs_mds_commit_info *cinfo)
  {
  	atomic_inc(&cinfo->rpcs_out);
71d0a6112   Trond Myklebust   NFS: Fix an unsta...
1587
  }
af7cf0579   Trond Myklebust   NFS: Allow multip...
1588
  static void nfs_commit_end(struct nfs_mds_commit_info *cinfo)
71d0a6112   Trond Myklebust   NFS: Fix an unsta...
1589
  {
af7cf0579   Trond Myklebust   NFS: Allow multip...
1590
  	if (atomic_dec_and_test(&cinfo->rpcs_out))
723c921e7   Peter Zijlstra   sched/wait, fs/nf...
1591
  		wake_up_var(&cinfo->rpcs_out);
71d0a6112   Trond Myklebust   NFS: Fix an unsta...
1592
  }
0b7c01533   Fred Isaman   NFS: add a struct...
1593
  void nfs_commitdata_release(struct nfs_commit_data *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1594
  {
0b7c01533   Fred Isaman   NFS: add a struct...
1595
1596
  	put_nfs_open_context(data->context);
  	nfs_commit_free(data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1597
  }
e0c2b3801   Fred Isaman   NFSv4.1: filelayo...
1598
  EXPORT_SYMBOL_GPL(nfs_commitdata_release);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1599

0b7c01533   Fred Isaman   NFS: add a struct...
1600
  int nfs_initiate_commit(struct rpc_clnt *clnt, struct nfs_commit_data *data,
c36aae9ad   Peng Tao   nfs: allow differ...
1601
  			const struct nfs_rpc_ops *nfs_ops,
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1602
  			const struct rpc_call_ops *call_ops,
9f0ec176b   Andy Adamson   NFSv4.1 set RPC_T...
1603
  			int how, int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1604
  {
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
1605
  	struct rpc_task *task;
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1606
  	int priority = flush_task_priority(how);
bdc7f021f   Trond Myklebust   NFS: Clean up the...
1607
1608
1609
  	struct rpc_message msg = {
  		.rpc_argp = &data->args,
  		.rpc_resp = &data->res,
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1610
  		.rpc_cred = data->cred,
bdc7f021f   Trond Myklebust   NFS: Clean up the...
1611
  	};
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
1612
  	struct rpc_task_setup task_setup_data = {
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
1613
  		.task = &data->task,
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1614
  		.rpc_client = clnt,
bdc7f021f   Trond Myklebust   NFS: Clean up the...
1615
  		.rpc_message = &msg,
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1616
  		.callback_ops = call_ops,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
1617
  		.callback_data = data,
101070ca2   Trond Myklebust   NFS: Ensure that ...
1618
  		.workqueue = nfsiod_workqueue,
4fa7ef69e   Trond Myklebust   NFS/pnfs: Don't u...
1619
  		.flags = RPC_TASK_ASYNC | flags,
3ff7576dd   Trond Myklebust   SUNRPC: Clean up ...
1620
  		.priority = priority,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
1621
  	};
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1622
  	/* Set up the initial task struct.  */
e9ae1ee2b   Anna Schumaker   NFS: Move call to...
1623
  	nfs_ops->commit_setup(data, &msg, &task_setup_data.rpc_client);
8224b2734   Chuck Lever   NFS: Add static N...
1624
  	trace_nfs_initiate_commit(data);
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1625

b4839ebe2   Kinglong Mee   nfs: Remove inval...
1626
1627
  	dprintk("NFS: initiated commit call
  ");
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1628
1629
1630
1631
1632
1633
1634
1635
1636
  
  	task = rpc_run_task(&task_setup_data);
  	if (IS_ERR(task))
  		return PTR_ERR(task);
  	if (how & FLUSH_SYNC)
  		rpc_wait_for_completion_task(task);
  	rpc_put_task(task);
  	return 0;
  }
e0c2b3801   Fred Isaman   NFSv4.1: filelayo...
1637
  EXPORT_SYMBOL_GPL(nfs_initiate_commit);
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1638

378520b83   Peng Tao   nfs41: add a help...
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
  static loff_t nfs_get_lwb(struct list_head *head)
  {
  	loff_t lwb = 0;
  	struct nfs_page *req;
  
  	list_for_each_entry(req, head, wb_list)
  		if (lwb < (req_offset(req) + req->wb_bytes))
  			lwb = req_offset(req) + req->wb_bytes;
  
  	return lwb;
  }
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1650
1651
1652
  /*
   * Set up the argument/result storage required for the RPC call.
   */
0b7c01533   Fred Isaman   NFS: add a struct...
1653
  void nfs_init_commit(struct nfs_commit_data *data,
f453a54a0   Fred Isaman   NFS: create nfs_c...
1654
1655
1656
  		     struct list_head *head,
  		     struct pnfs_layout_segment *lseg,
  		     struct nfs_commit_info *cinfo)
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1657
  {
19573c939   Trond Myklebust   NFS/pNFS: Refacto...
1658
1659
1660
  	struct nfs_page *first;
  	struct nfs_open_context *ctx;
  	struct inode *inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1661
1662
1663
  
  	/* Set up the RPC argument and reply structs
  	 * NB: take care not to mess about with data->commit et al. */
19573c939   Trond Myklebust   NFS/pNFS: Refacto...
1664
1665
1666
1667
1668
1669
  	if (head)
  		list_splice_init(head, &data->pages);
  
  	first = nfs_list_entry(data->pages.next);
  	ctx = nfs_req_openctx(first);
  	inode = d_inode(ctx->dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1670

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1671
  	data->inode	  = inode;
9fcd5960e   Trond Myklebust   NFS: Add a helper...
1672
  	data->cred	  = ctx->cred;
988b6dceb   Fred Isaman   NFSv4.1: remove G...
1673
  	data->lseg	  = lseg; /* reference transferred */
378520b83   Peng Tao   nfs41: add a help...
1674
1675
1676
  	/* only set lwb for pnfs commit */
  	if (lseg)
  		data->lwb = nfs_get_lwb(&data->pages);
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1677
  	data->mds_ops     = &nfs_commit_ops;
f453a54a0   Fred Isaman   NFS: create nfs_c...
1678
  	data->completion_ops = cinfo->completion_ops;
b359f9d09   Fred Isaman   NFS: add dreq to ...
1679
  	data->dreq	  = cinfo->dreq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1680
1681
  
  	data->args.fh     = NFS_FH(data->inode);
3da28eb1c   Trond Myklebust   [PATCH] NFS: Repl...
1682
1683
1684
  	/* Note: we always request a commit of the entire inode */
  	data->args.offset = 0;
  	data->args.count  = 0;
9fcd5960e   Trond Myklebust   NFS: Add a helper...
1685
  	data->context     = get_nfs_open_context(ctx);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1686
1687
  	data->res.fattr   = &data->fattr;
  	data->res.verf    = &data->verf;
0e574af1b   Trond Myklebust   NFS: Cleanup init...
1688
  	nfs_fattr_init(&data->fattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1689
  }
e0c2b3801   Fred Isaman   NFSv4.1: filelayo...
1690
  EXPORT_SYMBOL_GPL(nfs_init_commit);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1691

e0c2b3801   Fred Isaman   NFSv4.1: filelayo...
1692
  void nfs_retry_commit(struct list_head *page_list,
ea2cf2282   Fred Isaman   NFS: create struc...
1693
  		      struct pnfs_layout_segment *lseg,
b57ff1303   Weston Andros Adamson   pnfs: pass ds_com...
1694
1695
  		      struct nfs_commit_info *cinfo,
  		      u32 ds_commit_idx)
64bfeb49b   Fred Isaman   NFSv4.1: pull err...
1696
1697
1698
1699
1700
1701
  {
  	struct nfs_page *req;
  
  	while (!list_empty(page_list)) {
  		req = nfs_list_entry(page_list->next);
  		nfs_list_remove_request(req);
b57ff1303   Weston Andros Adamson   pnfs: pass ds_com...
1702
  		nfs_mark_request_commit(req, lseg, cinfo, ds_commit_idx);
487b9b8af   Tom Haynes   nfs: Can call nfs...
1703
1704
  		if (!cinfo->dreq)
  			nfs_clear_page_commit(req->wb_page);
1d1afcbc2   Trond Myklebust   NFS: Clean up - R...
1705
  		nfs_unlock_and_release_request(req);
64bfeb49b   Fred Isaman   NFSv4.1: pull err...
1706
1707
  	}
  }
e0c2b3801   Fred Isaman   NFSv4.1: filelayo...
1708
  EXPORT_SYMBOL_GPL(nfs_retry_commit);
64bfeb49b   Fred Isaman   NFSv4.1: pull err...
1709

b20135d0b   Trond Myklebust   NFSv4.1/pNFS: Don...
1710
1711
1712
1713
1714
1715
  static void
  nfs_commit_resched_write(struct nfs_commit_info *cinfo,
  		struct nfs_page *req)
  {
  	__set_page_dirty_nobuffers(req->wb_page);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1716
1717
1718
1719
  /*
   * Commit dirty pages
   */
  static int
ea2cf2282   Fred Isaman   NFS: create struc...
1720
1721
  nfs_commit_list(struct inode *inode, struct list_head *head, int how,
  		struct nfs_commit_info *cinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1722
  {
0b7c01533   Fred Isaman   NFS: add a struct...
1723
  	struct nfs_commit_data	*data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1724

ade8febde   Weston Andros Adamson   nfs: avoid race t...
1725
1726
1727
  	/* another commit raced with us */
  	if (list_empty(head))
  		return 0;
518662e0f   NeilBrown   NFS: fix usage of...
1728
  	data = nfs_commitdata_alloc(true);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1729
1730
  
  	/* Set up the argument struct */
f453a54a0   Fred Isaman   NFS: create nfs_c...
1731
1732
  	nfs_init_commit(data, head, NULL, cinfo);
  	atomic_inc(&cinfo->mds->rpcs_out);
c36aae9ad   Peng Tao   nfs: allow differ...
1733
  	return nfs_initiate_commit(NFS_CLIENT(inode), data, NFS_PROTO(inode),
4fa7ef69e   Trond Myklebust   NFS/pnfs: Don't u...
1734
  				   data->mds_ops, how, RPC_TASK_CRED_NOREF);
67911c8f1   Anna Schumaker   NFS: Add nfs_comm...
1735
  }
67911c8f1   Anna Schumaker   NFS: Add nfs_comm...
1736

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1737
1738
1739
  /*
   * COMMIT call returned
   */
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1740
  static void nfs_commit_done(struct rpc_task *task, void *calldata)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1741
  {
0b7c01533   Fred Isaman   NFS: add a struct...
1742
  	struct nfs_commit_data	*data = calldata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1743

a3f565b1e   Chuck Lever   NFS: fix print fo...
1744
1745
          dprintk("NFS: %5u nfs_commit_done (status %d)
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1746
                                  task->tk_pid, task->tk_status);
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1747
  	/* Call the NFS version-specific code */
c0d0e96b8   Trond Myklebust   NFS: Get rid of p...
1748
  	NFS_PROTO(data->inode)->commit_done(task, data);
7bdd297ea   Trond Myklebust   NFS: Clean up gen...
1749
  	trace_nfs_commit_done(task, data);
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1750
  }
f453a54a0   Fred Isaman   NFS: create nfs_c...
1751
  static void nfs_commit_release_pages(struct nfs_commit_data *data)
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1752
  {
221203ce6   Trond Myklebust   NFS/pnfs: Fix pnf...
1753
  	const struct nfs_writeverf *verf = data->res.verf;
5917ce844   Fred Isaman   NFSv4.1: pull out...
1754
  	struct nfs_page	*req;
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1755
  	int status = data->task.tk_status;
f453a54a0   Fred Isaman   NFS: create nfs_c...
1756
  	struct nfs_commit_info cinfo;
353db7966   NeilBrown   NFS: avoid waitin...
1757
  	struct nfs_server *nfss;
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1758

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1759
1760
1761
  	while (!list_empty(&data->pages)) {
  		req = nfs_list_entry(data->pages.next);
  		nfs_list_remove_request(req);
67911c8f1   Anna Schumaker   NFS: Add nfs_comm...
1762
1763
  		if (req->wb_page)
  			nfs_clear_page_commit(req->wb_page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1764

1e8968c5b   Niels de Vos   NFS: dprintk() sh...
1765
  		dprintk("NFS:       commit (%s/%llu %d@%lld)",
9fcd5960e   Trond Myklebust   NFS: Add a helper...
1766
1767
  			nfs_req_openctx(req)->dentry->d_sb->s_id,
  			(unsigned long long)NFS_FILEID(d_inode(nfs_req_openctx(req)->dentry)),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1768
1769
  			req->wb_bytes,
  			(long long)req_offset(req));
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1770
  		if (status < 0) {
6fbda89b2   Trond Myklebust   NFS: Replace cust...
1771
  			if (req->wb_page) {
861e1671b   Chuck Lever   NFS: Introduce tr...
1772
  				trace_nfs_commit_error(req, status);
6fbda89b2   Trond Myklebust   NFS: Replace cust...
1773
  				nfs_mapping_set_error(req->wb_page, status);
38a33101d   Kinglong Mee   NFS: fix the faul...
1774
  				nfs_inode_remove_request(req);
6fbda89b2   Trond Myklebust   NFS: Replace cust...
1775
  			}
ddeaa6379   Joe Perches   sunrpc & nfs: Add...
1776
1777
  			dprintk_cont(", error = %d
  ", status);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1778
1779
1780
1781
1782
  			goto next;
  		}
  
  		/* Okay, COMMIT succeeded, apparently. Check the verifier
  		 * returned by the server against all stored verfs. */
1f28476dc   Trond Myklebust   NFS: Fix O_DIRECT...
1783
  		if (nfs_write_match_verf(verf, req)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1784
  			/* We have a match */
38a33101d   Kinglong Mee   NFS: fix the faul...
1785
1786
  			if (req->wb_page)
  				nfs_inode_remove_request(req);
ddeaa6379   Joe Perches   sunrpc & nfs: Add...
1787
1788
  			dprintk_cont(" OK
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1789
1790
1791
  			goto next;
  		}
  		/* We have a mismatch. Write the page again */
ddeaa6379   Joe Perches   sunrpc & nfs: Add...
1792
1793
  		dprintk_cont(" mismatch
  ");
6d884e8fc   Fred   nfs: nfs_redirty_...
1794
  		nfs_mark_request_dirty(req);
9fcd5960e   Trond Myklebust   NFS: Add a helper...
1795
  		set_bit(NFS_CONTEXT_RESEND_WRITES, &nfs_req_openctx(req)->flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1796
  	next:
1d1afcbc2   Trond Myklebust   NFS: Clean up - R...
1797
  		nfs_unlock_and_release_request(req);
7f1bda447   Trond Myklebust   NFS: Add a cond_r...
1798
1799
  		/* Latency breaker */
  		cond_resched();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1800
  	}
353db7966   NeilBrown   NFS: avoid waitin...
1801
1802
  	nfss = NFS_SERVER(data->inode);
  	if (atomic_long_read(&nfss->writeback) < NFS_CONGESTION_OFF_THRESH)
0db10944a   Jan Kara   nfs: Convert to s...
1803
  		clear_bdi_congested(inode_to_bdi(data->inode), BLK_RW_ASYNC);
353db7966   NeilBrown   NFS: avoid waitin...
1804

f453a54a0   Fred Isaman   NFS: create nfs_c...
1805
  	nfs_init_cinfo(&cinfo, data->inode, data->dreq);
af7cf0579   Trond Myklebust   NFS: Allow multip...
1806
  	nfs_commit_end(cinfo.mds);
5917ce844   Fred Isaman   NFSv4.1: pull out...
1807
1808
1809
1810
  }
  
  static void nfs_commit_release(void *calldata)
  {
0b7c01533   Fred Isaman   NFS: add a struct...
1811
  	struct nfs_commit_data *data = calldata;
5917ce844   Fred Isaman   NFSv4.1: pull out...
1812

f453a54a0   Fred Isaman   NFS: create nfs_c...
1813
  	data->completion_ops->completion(data);
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1814
  	nfs_commitdata_release(calldata);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1815
  }
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1816
1817
  
  static const struct rpc_call_ops nfs_commit_ops = {
0b7c01533   Fred Isaman   NFS: add a struct...
1818
  	.rpc_call_prepare = nfs_commit_prepare,
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1819
1820
1821
  	.rpc_call_done = nfs_commit_done,
  	.rpc_release = nfs_commit_release,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1822

f453a54a0   Fred Isaman   NFS: create nfs_c...
1823
1824
  static const struct nfs_commit_completion_ops nfs_commit_completion_ops = {
  	.completion = nfs_commit_release_pages,
b20135d0b   Trond Myklebust   NFSv4.1/pNFS: Don...
1825
  	.resched_write = nfs_commit_resched_write,
f453a54a0   Fred Isaman   NFS: create nfs_c...
1826
  };
1763da123   Fred Isaman   NFS: rewrite dire...
1827
1828
  int nfs_generic_commit_list(struct inode *inode, struct list_head *head,
  			    int how, struct nfs_commit_info *cinfo)
84c53ab5c   Fred Isaman   NFS: create nfs_g...
1829
1830
  {
  	int status;
ea2cf2282   Fred Isaman   NFS: create struc...
1831
  	status = pnfs_commit_list(inode, head, how, cinfo);
84c53ab5c   Fred Isaman   NFS: create nfs_g...
1832
  	if (status == PNFS_NOT_ATTEMPTED)
ea2cf2282   Fred Isaman   NFS: create struc...
1833
  		status = nfs_commit_list(inode, head, how, cinfo);
84c53ab5c   Fred Isaman   NFS: create nfs_g...
1834
1835
  	return status;
  }
c4f24df94   Trond Myklebust   NFS: Fix unstable...
1836
1837
  static int __nfs_commit_inode(struct inode *inode, int how,
  		struct writeback_control *wbc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1838
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1839
  	LIST_HEAD(head);
ea2cf2282   Fred Isaman   NFS: create struc...
1840
  	struct nfs_commit_info cinfo;
71d0a6112   Trond Myklebust   NFS: Fix an unsta...
1841
  	int may_wait = how & FLUSH_SYNC;
c4f24df94   Trond Myklebust   NFS: Fix unstable...
1842
  	int ret, nscan;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1843

ea2cf2282   Fred Isaman   NFS: create struc...
1844
  	nfs_init_cinfo_from_inode(&cinfo, inode);
af7cf0579   Trond Myklebust   NFS: Allow multip...
1845
  	nfs_commit_begin(cinfo.mds);
c4f24df94   Trond Myklebust   NFS: Fix unstable...
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
  	for (;;) {
  		ret = nscan = nfs_scan_commit(inode, &head, &cinfo);
  		if (ret <= 0)
  			break;
  		ret = nfs_generic_commit_list(inode, &head, how, &cinfo);
  		if (ret < 0)
  			break;
  		ret = 0;
  		if (wbc && wbc->sync_mode == WB_SYNC_NONE) {
  			if (nscan < wbc->nr_to_write)
  				wbc->nr_to_write -= nscan;
  			else
  				wbc->nr_to_write = 0;
  		}
  		if (nscan < INT_MAX)
  			break;
  		cond_resched();
  	}
af7cf0579   Trond Myklebust   NFS: Allow multip...
1864
  	nfs_commit_end(cinfo.mds);
c4f24df94   Trond Myklebust   NFS: Fix unstable...
1865
1866
1867
1868
1869
1870
1871
1872
  	if (ret || !may_wait)
  		return ret;
  	return wait_on_commit(cinfo.mds);
  }
  
  int nfs_commit_inode(struct inode *inode, int how)
  {
  	return __nfs_commit_inode(inode, how, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1873
  }
b20135d0b   Trond Myklebust   NFSv4.1/pNFS: Don...
1874
  EXPORT_SYMBOL_GPL(nfs_commit_inode);
8fc795f70   Trond Myklebust   NFS: Cleanup - mo...
1875

ae09c31f6   Anna Schumaker   NFS: Rename nfs_c...
1876
  int nfs_write_inode(struct inode *inode, struct writeback_control *wbc)
8fc795f70   Trond Myklebust   NFS: Cleanup - mo...
1877
  {
420e3646b   Trond Myklebust   NFS: Reduce the n...
1878
1879
1880
  	struct nfs_inode *nfsi = NFS_I(inode);
  	int flags = FLUSH_SYNC;
  	int ret = 0;
8fc795f70   Trond Myklebust   NFS: Cleanup - mo...
1881

a00dd6c03   Jeff Layton   NFS: don't use FL...
1882
  	if (wbc->sync_mode == WB_SYNC_NONE) {
c4f24df94   Trond Myklebust   NFS: Fix unstable...
1883
1884
1885
  		/* no commits means nothing needs to be done */
  		if (!atomic_long_read(&nfsi->commit_info.ncommit))
  			goto check_requests_outstanding;
a00dd6c03   Jeff Layton   NFS: don't use FL...
1886
1887
1888
  		/* Don't commit yet if this is a non-blocking flush and there
  		 * are a lot of outstanding writes for this mapping.
  		 */
1a4edf0f4   Trond Myklebust   NFS: Fix commit p...
1889
  		if (mapping_tagged(inode->i_mapping, PAGECACHE_TAG_WRITEBACK))
a00dd6c03   Jeff Layton   NFS: don't use FL...
1890
  			goto out_mark_dirty;
420e3646b   Trond Myklebust   NFS: Reduce the n...
1891

a00dd6c03   Jeff Layton   NFS: don't use FL...
1892
  		/* don't wait for the COMMIT response */
420e3646b   Trond Myklebust   NFS: Reduce the n...
1893
  		flags = 0;
a00dd6c03   Jeff Layton   NFS: don't use FL...
1894
  	}
c4f24df94   Trond Myklebust   NFS: Fix unstable...
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
  	ret = __nfs_commit_inode(inode, flags, wbc);
  	if (!ret) {
  		if (flags & FLUSH_SYNC)
  			return 0;
  	} else if (atomic_long_read(&nfsi->commit_info.ncommit))
  		goto out_mark_dirty;
  
  check_requests_outstanding:
  	if (!atomic_read(&nfsi->commit_info.rpcs_out))
  		return ret;
420e3646b   Trond Myklebust   NFS: Reduce the n...
1905
  out_mark_dirty:
8fc795f70   Trond Myklebust   NFS: Cleanup - mo...
1906
1907
1908
  	__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
  	return ret;
  }
89d77c8fa   Bryan Schumaker   NFS: Convert v4 i...
1909
  EXPORT_SYMBOL_GPL(nfs_write_inode);
a8d8f02cf   Bryan Schumaker   NFS: Create custo...
1910

acdc53b21   Trond Myklebust   NFS: Replace __nf...
1911
  /*
837bb1d75   Trond Myklebust   NFSv4.2: Fix writ...
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
   * Wrapper for filemap_write_and_wait_range()
   *
   * Needed for pNFS in order to ensure data becomes visible to the
   * client.
   */
  int nfs_filemap_write_and_wait_range(struct address_space *mapping,
  		loff_t lstart, loff_t lend)
  {
  	int ret;
  
  	ret = filemap_write_and_wait_range(mapping, lstart, lend);
  	if (ret == 0)
  		ret = pnfs_sync_inode(mapping->host, true);
  	return ret;
  }
  EXPORT_SYMBOL_GPL(nfs_filemap_write_and_wait_range);
  
  /*
acdc53b21   Trond Myklebust   NFS: Replace __nf...
1930
1931
1932
   * flush the inode to disk.
   */
  int nfs_wb_all(struct inode *inode)
34901f70d   Trond Myklebust   NFS: Writeback op...
1933
  {
f4ce1299b   Trond Myklebust   NFS: Add event tr...
1934
1935
1936
  	int ret;
  
  	trace_nfs_writeback_inode_enter(inode);
5bb89b470   Trond Myklebust   NFSv4.1/pnfs: Sep...
1937
  	ret = filemap_write_and_wait(inode->i_mapping);
6b1968756   Chuck Lever   nfs: stat(2) fail...
1938
1939
1940
1941
1942
1943
1944
  	if (ret)
  		goto out;
  	ret = nfs_commit_inode(inode, FLUSH_SYNC);
  	if (ret < 0)
  		goto out;
  	pnfs_sync_inode(inode, true);
  	ret = 0;
34901f70d   Trond Myklebust   NFS: Writeback op...
1945

6b1968756   Chuck Lever   nfs: stat(2) fail...
1946
  out:
f4ce1299b   Trond Myklebust   NFS: Add event tr...
1947
1948
  	trace_nfs_writeback_inode_exit(inode, ret);
  	return ret;
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1949
  }
ddda8e0aa   Bryan Schumaker   NFS: Convert v2 i...
1950
  EXPORT_SYMBOL_GPL(nfs_wb_all);
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1951

1b3b4a1a2   Trond Myklebust   NFS: Fix a write ...
1952
1953
1954
  int nfs_wb_page_cancel(struct inode *inode, struct page *page)
  {
  	struct nfs_page *req;
1b3b4a1a2   Trond Myklebust   NFS: Fix a write ...
1955
  	int ret = 0;
3e2170451   Weston Andros Adamson   nfs: handle multi...
1956
1957
1958
1959
  	wait_on_page_writeback(page);
  
  	/* blocking call to cancel all requests and join to a single (head)
  	 * request */
6d17d653c   Trond Myklebust   NFS: Simplify pag...
1960
  	req = nfs_lock_and_join_requests(page);
3e2170451   Weston Andros Adamson   nfs: handle multi...
1961
1962
1963
1964
1965
1966
1967
1968
1969
  
  	if (IS_ERR(req)) {
  		ret = PTR_ERR(req);
  	} else if (req) {
  		/* all requests from this page have been cancelled by
  		 * nfs_lock_and_join_requests, so just remove the head
  		 * request from the inode / page_private pointer and
  		 * release it */
  		nfs_inode_remove_request(req);
3e2170451   Weston Andros Adamson   nfs: handle multi...
1970
  		nfs_unlock_and_release_request(req);
1b3b4a1a2   Trond Myklebust   NFS: Fix a write ...
1971
  	}
3e2170451   Weston Andros Adamson   nfs: handle multi...
1972

1b3b4a1a2   Trond Myklebust   NFS: Fix a write ...
1973
1974
  	return ret;
  }
7f2f12d96   Trond Myklebust   NFS: Simplify nfs...
1975
1976
1977
  /*
   * Write back all requests on one page - we do this before reading it.
   */
c373fff7b   Trond Myklebust   NFSv4: Don't spec...
1978
  int nfs_wb_page(struct inode *inode, struct page *page)
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1979
  {
29418aa4b   Mel Gorman   nfs: disable data...
1980
  	loff_t range_start = page_file_offset(page);
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
1981
  	loff_t range_end = range_start + (loff_t)(PAGE_SIZE - 1);
4d770ccf4   Trond Myklebust   NFS: Ensure that ...
1982
  	struct writeback_control wbc = {
4d770ccf4   Trond Myklebust   NFS: Ensure that ...
1983
  		.sync_mode = WB_SYNC_ALL,
7f2f12d96   Trond Myklebust   NFS: Simplify nfs...
1984
  		.nr_to_write = 0,
4d770ccf4   Trond Myklebust   NFS: Ensure that ...
1985
1986
1987
1988
  		.range_start = range_start,
  		.range_end = range_end,
  	};
  	int ret;
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1989

f4ce1299b   Trond Myklebust   NFS: Add event tr...
1990
  	trace_nfs_writeback_page_enter(inode);
0522f6ade   Trond Myklebust   NFS: Fix another ...
1991
  	for (;;) {
ba8b06e67   Trond Myklebust   NFS: Ensure that ...
1992
  		wait_on_page_writeback(page);
73e3302f6   Trond Myklebust   NFS: Fix nfs_wb_p...
1993
  		if (clear_page_dirty_for_io(page)) {
c373fff7b   Trond Myklebust   NFSv4: Don't spec...
1994
  			ret = nfs_writepage_locked(page, &wbc);
73e3302f6   Trond Myklebust   NFS: Fix nfs_wb_p...
1995
1996
  			if (ret < 0)
  				goto out_error;
0522f6ade   Trond Myklebust   NFS: Fix another ...
1997
  			continue;
7f2f12d96   Trond Myklebust   NFS: Simplify nfs...
1998
  		}
f4ce1299b   Trond Myklebust   NFS: Add event tr...
1999
  		ret = 0;
0522f6ade   Trond Myklebust   NFS: Fix another ...
2000
2001
2002
  		if (!PagePrivate(page))
  			break;
  		ret = nfs_commit_inode(inode, FLUSH_SYNC);
ba8b06e67   Trond Myklebust   NFS: Ensure that ...
2003
  		if (ret < 0)
73e3302f6   Trond Myklebust   NFS: Fix nfs_wb_p...
2004
  			goto out_error;
7f2f12d96   Trond Myklebust   NFS: Simplify nfs...
2005
  	}
73e3302f6   Trond Myklebust   NFS: Fix nfs_wb_p...
2006
  out_error:
f4ce1299b   Trond Myklebust   NFS: Add event tr...
2007
  	trace_nfs_writeback_page_exit(inode, ret);
4d770ccf4   Trond Myklebust   NFS: Ensure that ...
2008
  	return ret;
1c75950b9   Trond Myklebust   NFS: cleanup of n...
2009
  }
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
2010
2011
  #ifdef CONFIG_MIGRATION
  int nfs_migrate_page(struct address_space *mapping, struct page *newpage,
a6bc32b89   Mel Gorman   mm: compaction: i...
2012
  		struct page *page, enum migrate_mode mode)
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
2013
  {
2da956523   Jeff Layton   nfs: don't try to...
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
  	/*
  	 * If PagePrivate is set, then the page is currently associated with
  	 * an in-progress read or write request. Don't try to migrate it.
  	 *
  	 * FIXME: we could do this in principle, but we'll need a way to ensure
  	 *        that we can safely release the inode reference while holding
  	 *        the page lock.
  	 */
  	if (PagePrivate(page))
  		return -EBUSY;
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
2024

8c209ce72   David Howells   NFS: nfs_migrate_...
2025
2026
  	if (!nfs_fscache_release_page(page, GFP_KERNEL))
  		return -EBUSY;
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
2027

a6bc32b89   Mel Gorman   mm: compaction: i...
2028
  	return migrate_page(mapping, newpage, page, mode);
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
2029
2030
  }
  #endif
f7b422b17   David Howells   NFS: Split fs/nfs...
2031
  int __init nfs_init_writepagecache(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2032
2033
  {
  	nfs_wdata_cachep = kmem_cache_create("nfs_write_data",
1e7f3a485   Weston Andros Adamson   nfs: move nfs_pgi...
2034
  					     sizeof(struct nfs_pgio_header),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2035
  					     0, SLAB_HWCACHE_ALIGN,
20c2df83d   Paul Mundt   mm: Remove slab d...
2036
  					     NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2037
2038
  	if (nfs_wdata_cachep == NULL)
  		return -ENOMEM;
93d2341c7   Matthew Dobson   [PATCH] mempool: ...
2039
2040
  	nfs_wdata_mempool = mempool_create_slab_pool(MIN_POOL_WRITE,
  						     nfs_wdata_cachep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2041
  	if (nfs_wdata_mempool == NULL)
3dd4765fc   Jeff Layton   nfs: tear down ca...
2042
  		goto out_destroy_write_cache;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2043

0b7c01533   Fred Isaman   NFS: add a struct...
2044
2045
2046
2047
2048
  	nfs_cdata_cachep = kmem_cache_create("nfs_commit_data",
  					     sizeof(struct nfs_commit_data),
  					     0, SLAB_HWCACHE_ALIGN,
  					     NULL);
  	if (nfs_cdata_cachep == NULL)
3dd4765fc   Jeff Layton   nfs: tear down ca...
2049
  		goto out_destroy_write_mempool;
0b7c01533   Fred Isaman   NFS: add a struct...
2050

93d2341c7   Matthew Dobson   [PATCH] mempool: ...
2051
  	nfs_commit_mempool = mempool_create_slab_pool(MIN_POOL_COMMIT,
4c1002100   Yanchuan Nian   nfs: Fix wrong sl...
2052
  						      nfs_cdata_cachep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2053
  	if (nfs_commit_mempool == NULL)
3dd4765fc   Jeff Layton   nfs: tear down ca...
2054
  		goto out_destroy_commit_cache;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2055

89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
  	/*
  	 * NFS congestion size, scale with available memory.
  	 *
  	 *  64MB:    8192k
  	 * 128MB:   11585k
  	 * 256MB:   16384k
  	 * 512MB:   23170k
  	 *   1GB:   32768k
  	 *   2GB:   46340k
  	 *   4GB:   65536k
  	 *   8GB:   92681k
  	 *  16GB:  131072k
  	 *
  	 * This allows larger machines to have larger/more transfers.
  	 * Limit the default to 256M
  	 */
ca79b0c21   Arun KS   mm: convert total...
2072
  	nfs_congestion_kb = (16*int_sqrt(totalram_pages())) << (PAGE_SHIFT-10);
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
2073
2074
  	if (nfs_congestion_kb > 256*1024)
  		nfs_congestion_kb = 256*1024;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2075
  	return 0;
3dd4765fc   Jeff Layton   nfs: tear down ca...
2076
2077
2078
2079
2080
2081
2082
2083
  
  out_destroy_commit_cache:
  	kmem_cache_destroy(nfs_cdata_cachep);
  out_destroy_write_mempool:
  	mempool_destroy(nfs_wdata_mempool);
  out_destroy_write_cache:
  	kmem_cache_destroy(nfs_wdata_cachep);
  	return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2084
  }
266bee886   David Brownell   [PATCH] fix stati...
2085
  void nfs_destroy_writepagecache(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2086
2087
  {
  	mempool_destroy(nfs_commit_mempool);
3dd4765fc   Jeff Layton   nfs: tear down ca...
2088
  	kmem_cache_destroy(nfs_cdata_cachep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2089
  	mempool_destroy(nfs_wdata_mempool);
1a1d92c10   Alexey Dobriyan   [PATCH] Really ig...
2090
  	kmem_cache_destroy(nfs_wdata_cachep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2091
  }
4a0de55c5   Anna Schumaker   NFS: Create a com...
2092
2093
2094
  static const struct nfs_rw_ops nfs_rw_write_ops = {
  	.rw_alloc_header	= nfs_writehdr_alloc,
  	.rw_free_header		= nfs_writehdr_free,
0eecb2145   Anna Schumaker   NFS: Create a com...
2095
2096
  	.rw_done		= nfs_writeback_done,
  	.rw_result		= nfs_writeback_result,
1ed26f330   Anna Schumaker   NFS: Create a com...
2097
  	.rw_initiate		= nfs_initiate_write,
4a0de55c5   Anna Schumaker   NFS: Create a com...
2098
  };