Blame view

fs/nfs/write.c 55.1 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
  /*
   * linux/fs/nfs/write.c
   *
7c85d9007   Trond Myklebust   NFS: Fixup some o...
4
   * Write file data over NFS.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
7
   *
   * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de>
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
11
12
  #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
13
  #include <linux/writeback.h>
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
14
  #include <linux/swap.h>
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
15
  #include <linux/migrate.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
18
19
20
  
  #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 ...
21
  #include <linux/backing-dev.h>
afeacc8c1   Paul Gortmaker   fs: add export.h ...
22
  #include <linux/export.h>
af7cf0579   Trond Myklebust   NFS: Allow multip...
23
24
  #include <linux/freezer.h>
  #include <linux/wait.h>
3fcfab16c   Andrew Morton   [PATCH] separate ...
25

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

f4ce1299b   Trond Myklebust   NFS: Add event tr...
35
  #include "nfstrace.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
38
39
40
41
42
43
  #define NFSDBG_FACILITY		NFSDBG_PAGECACHE
  
  #define MIN_POOL_WRITE		(32)
  #define MIN_POOL_COMMIT		(4)
  
  /*
   * Local function declarations
   */
f8512ad0d   Fred Isaman   nfs: don't ignore...
44
  static void nfs_redirty_request(struct nfs_page *req);
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
45
  static const struct rpc_call_ops nfs_commit_ops;
061ae2edb   Fred Isaman   NFS: create compl...
46
  static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops;
f453a54a0   Fred Isaman   NFS: create nfs_c...
47
  static const struct nfs_commit_completion_ops nfs_commit_completion_ops;
4a0de55c5   Anna Schumaker   NFS: Create a com...
48
  static const struct nfs_rw_ops nfs_rw_write_ops;
d45813835   Weston Andros Adamson   nfs: handle multi...
49
  static void nfs_clear_request_commit(struct nfs_page *req);
02d1426c7   Weston Andros Adamson   pnfs: find swappe...
50
51
  static void nfs_init_cinfo_from_inode(struct nfs_commit_info *cinfo,
  				      struct inode *inode);
3a3908c8b   Trond Myklebust   NFS: Fix a compil...
52
53
54
  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
55

e18b890bb   Christoph Lameter   [PATCH] slab: rem...
56
  static struct kmem_cache *nfs_wdata_cachep;
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
57
  static mempool_t *nfs_wdata_mempool;
0b7c01533   Fred Isaman   NFS: add a struct...
58
  static struct kmem_cache *nfs_cdata_cachep;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
  static mempool_t *nfs_commit_mempool;
0b7c01533   Fred Isaman   NFS: add a struct...
60
  struct nfs_commit_data *nfs_commitdata_alloc(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
  {
192e501b0   Mel Gorman   nfs: prevent page...
62
  	struct nfs_commit_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOIO);
40859d7ee   Chuck Lever   NFS: support larg...
63

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
65
66
67
68
69
  	if (p) {
  		memset(p, 0, sizeof(*p));
  		INIT_LIST_HEAD(&p->pages);
  	}
  	return p;
  }
e0c2b3801   Fred Isaman   NFSv4.1: filelayo...
70
  EXPORT_SYMBOL_GPL(nfs_commitdata_alloc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71

0b7c01533   Fred Isaman   NFS: add a struct...
72
  void nfs_commit_free(struct nfs_commit_data *p)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
75
  {
  	mempool_free(p, nfs_commit_mempool);
  }
e0c2b3801   Fred Isaman   NFSv4.1: filelayo...
76
  EXPORT_SYMBOL_GPL(nfs_commit_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77

1e7f3a485   Weston Andros Adamson   nfs: move nfs_pgi...
78
  static struct nfs_pgio_header *nfs_writehdr_alloc(void)
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
79
  {
1e7f3a485   Weston Andros Adamson   nfs: move nfs_pgi...
80
  	struct nfs_pgio_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOIO);
cd841605f   Fred Isaman   NFS: create commo...
81

4a0de55c5   Anna Schumaker   NFS: Create a com...
82
  	if (p)
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
83
  		memset(p, 0, sizeof(*p));
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
84
85
  	return p;
  }
6c75dc0d4   Fred Isaman   NFS: merge _full ...
86

1e7f3a485   Weston Andros Adamson   nfs: move nfs_pgi...
87
  static void nfs_writehdr_free(struct nfs_pgio_header *hdr)
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
88
  {
1e7f3a485   Weston Andros Adamson   nfs: move nfs_pgi...
89
  	mempool_free(hdr, nfs_wdata_mempool);
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
90
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91

7b159fc18   Trond Myklebust   NFS: Fall back to...
92
93
94
95
96
97
  static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error)
  {
  	ctx->error = error;
  	smp_wmb();
  	set_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
  }
84d3a9a91   Weston Andros Adamson   nfs: change find_...
98
99
100
101
102
103
104
  /*
   * 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...
105
  static struct nfs_page *
84d3a9a91   Weston Andros Adamson   nfs: change find_...
106
  nfs_page_find_head_request_locked(struct nfs_inode *nfsi, struct page *page)
277459d2e   Trond Myklebust   NFS: Store pointe...
107
108
  {
  	struct nfs_page *req = NULL;
29418aa4b   Mel Gorman   nfs: disable data...
109
  	if (PagePrivate(page))
277459d2e   Trond Myklebust   NFS: Store pointe...
110
  		req = (struct nfs_page *)page_private(page);
02d1426c7   Weston Andros Adamson   pnfs: find swappe...
111
112
113
  	else if (unlikely(PageSwapCache(page)))
  		req = nfs_page_search_commits_for_head_request_locked(nfsi,
  			page);
29418aa4b   Mel Gorman   nfs: disable data...
114

84d3a9a91   Weston Andros Adamson   nfs: change find_...
115
116
  	if (req) {
  		WARN_ON_ONCE(req->wb_head != req);
29418aa4b   Mel Gorman   nfs: disable data...
117
  		kref_get(&req->wb_kref);
84d3a9a91   Weston Andros Adamson   nfs: change find_...
118
  	}
29418aa4b   Mel Gorman   nfs: disable data...
119

277459d2e   Trond Myklebust   NFS: Store pointe...
120
121
  	return req;
  }
84d3a9a91   Weston Andros Adamson   nfs: change find_...
122
123
124
125
126
127
  /*
   * 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...
128
  {
d56b4ddf7   Mel Gorman   nfs: teach the NF...
129
  	struct inode *inode = page_file_mapping(page)->host;
277459d2e   Trond Myklebust   NFS: Store pointe...
130
  	struct nfs_page *req = NULL;
277459d2e   Trond Myklebust   NFS: Store pointe...
131

587142f85   Trond Myklebust   NFS: Replace NFS_...
132
  	spin_lock(&inode->i_lock);
84d3a9a91   Weston Andros Adamson   nfs: change find_...
133
  	req = nfs_page_find_head_request_locked(NFS_I(inode), page);
587142f85   Trond Myklebust   NFS: Replace NFS_...
134
  	spin_unlock(&inode->i_lock);
277459d2e   Trond Myklebust   NFS: Store pointe...
135
136
  	return req;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
139
  /* 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...
140
  	struct inode *inode = page_file_mapping(page)->host;
a3d01454b   Trond Myklebust   NFS: Remove BKL r...
141
142
  	loff_t end, i_size;
  	pgoff_t end_index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143

a3d01454b   Trond Myklebust   NFS: Remove BKL r...
144
145
  	spin_lock(&inode->i_lock);
  	i_size = i_size_read(inode);
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
146
  	end_index = (i_size - 1) >> PAGE_SHIFT;
8cd797887   Huang Ying   mm: remove page_f...
147
  	if (i_size > 0 && page_index(page) < end_index)
a3d01454b   Trond Myklebust   NFS: Remove BKL r...
148
  		goto out;
d56b4ddf7   Mel Gorman   nfs: teach the NF...
149
  	end = page_file_offset(page) + ((loff_t)offset+count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
  	if (i_size >= end)
a3d01454b   Trond Myklebust   NFS: Remove BKL r...
151
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
  	i_size_write(inode, end);
a3d01454b   Trond Myklebust   NFS: Remove BKL r...
153
154
155
  	nfs_inc_stats(inode, NFSIOS_EXTENDWRITE);
  out:
  	spin_unlock(&inode->i_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
  }
a301b7777   Trond Myklebust   NFS: Don't use Cl...
157
158
159
  /* A writeback failed: mark the page as bad, and invalidate the page cache */
  static void nfs_set_pageerror(struct page *page)
  {
d56b4ddf7   Mel Gorman   nfs: teach the NF...
160
  	nfs_zap_mapping(page_file_mapping(page)->host, page_file_mapping(page));
a301b7777   Trond Myklebust   NFS: Don't use Cl...
161
  }
d72ddcbab   Weston Andros Adamson   nfs: page group s...
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
  /*
   * 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;
  
  	WARN_ON_ONCE(head != head->wb_head);
  	WARN_ON_ONCE(!test_bit(PG_HEADLOCK, &head->wb_head->wb_flags));
  
  	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);
fd2f3a06d   Weston Andros Adamson   nfs: change nfs_p...
207
  	nfs_page_group_lock(req, false);
d72ddcbab   Weston Andros Adamson   nfs: page group s...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
  
  	do {
  		tmp = nfs_page_group_search_locked(req->wb_head, pos);
  		if (tmp) {
  			/* no way this should happen */
  			WARN_ON_ONCE(tmp->wb_pgbase != pos);
  			pos += tmp->wb_bytes - (pos - tmp->wb_pgbase);
  		}
  	} while (tmp && pos < len);
  
  	nfs_page_group_unlock(req);
  	WARN_ON_ONCE(pos > len);
  	return pos == len;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
224
  /* 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...
225
  static void nfs_mark_uptodate(struct nfs_page *req)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
  {
d72ddcbab   Weston Andros Adamson   nfs: page group s...
227
  	if (PageUptodate(req->wb_page))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
  		return;
d72ddcbab   Weston Andros Adamson   nfs: page group s...
229
  	if (!nfs_page_group_covers_page(req))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
  		return;
d72ddcbab   Weston Andros Adamson   nfs: page group s...
231
  	SetPageUptodate(req->wb_page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
234
  static int wb_priority(struct writeback_control *wbc)
  {
e87b4c7a7   NeilBrown   NFS: don't use ST...
235
  	int ret = 0;
cca588d6c   Trond Myklebust   NFS: Reclaim writ...
236

e87b4c7a7   NeilBrown   NFS: don't use ST...
237
238
  	if (wbc->sync_mode == WB_SYNC_ALL)
  		ret = FLUSH_COND_STABLE;
e87b4c7a7   NeilBrown   NFS: don't use ST...
239
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
242
  }
  
  /*
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
243
244
245
246
247
248
249
250
   * 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...
251
  static void nfs_set_page_writeback(struct page *page)
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
252
  {
deed85e76   Trond Myklebust   NFS: Remove BUG_O...
253
  	struct nfs_server *nfss = NFS_SERVER(page_file_mapping(page)->host);
5a6d41b32   Trond Myklebust   NFS: Ensure PG_wr...
254
  	int ret = test_set_page_writeback(page);
deed85e76   Trond Myklebust   NFS: Remove BUG_O...
255
  	WARN_ON_ONCE(ret != 0);
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
256

deed85e76   Trond Myklebust   NFS: Remove BUG_O...
257
258
259
260
  	if (atomic_long_inc_return(&nfss->writeback) >
  			NFS_CONGESTION_ON_THRESH) {
  		set_bdi_congested(&nfss->backing_dev_info,
  					BLK_RW_ASYNC);
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
261
262
  	}
  }
20633f042   Weston Andros Adamson   nfs: page group s...
263
  static void nfs_end_page_writeback(struct nfs_page *req)
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
264
  {
20633f042   Weston Andros Adamson   nfs: page group s...
265
  	struct inode *inode = page_file_mapping(req->wb_page)->host;
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
266
  	struct nfs_server *nfss = NFS_SERVER(inode);
20633f042   Weston Andros Adamson   nfs: page group s...
267
268
269
270
  	if (!nfs_page_group_sync_on_bit(req, PG_WB_END))
  		return;
  
  	end_page_writeback(req->wb_page);
c4dc4beed   Peter Zijlstra   nfs: remove conge...
271
  	if (atomic_long_dec_return(&nfss->writeback) < NFS_CONGESTION_OFF_THRESH)
8aa7e847d   Jens Axboe   Fix congestion_wa...
272
  		clear_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC);
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
273
  }
d45813835   Weston Andros Adamson   nfs: handle multi...
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
  
  /* nfs_page_group_clear_bits
   *   @req - an nfs request
   * clears all page group related bits from @req
   */
  static void
  nfs_page_group_clear_bits(struct nfs_page *req)
  {
  	clear_bit(PG_TEARDOWN, &req->wb_flags);
  	clear_bit(PG_UNLOCKPAGE, &req->wb_flags);
  	clear_bit(PG_UPTODATE, &req->wb_flags);
  	clear_bit(PG_WB_END, &req->wb_flags);
  	clear_bit(PG_REMOVE, &req->wb_flags);
  }
  
  
  /*
   * nfs_unroll_locks_and_wait -  unlock all newly locked reqs and wait on @req
   *
   * this is a helper function for nfs_lock_and_join_requests
   *
   * @inode - inode associated with request page group, must be holding inode lock
   * @head  - head request of page group, must be holding head lock
   * @req   - request that couldn't lock and needs to wait on the req bit lock
   * @nonblock - if true, don't actually wait
   *
   * NOTE: this must be called holding page_group bit lock and inode spin lock
   *       and BOTH will be released before returning.
   *
   * returns 0 on success, < 0 on error.
   */
  static int
  nfs_unroll_locks_and_wait(struct inode *inode, struct nfs_page *head,
  			  struct nfs_page *req, bool nonblock)
  	__releases(&inode->i_lock)
  {
  	struct nfs_page *tmp;
  	int ret;
  
  	/* relinquish all the locks successfully grabbed this run */
  	for (tmp = head ; tmp != req; tmp = tmp->wb_this_page)
  		nfs_unlock_request(tmp);
  
  	WARN_ON_ONCE(test_bit(PG_TEARDOWN, &req->wb_flags));
  
  	/* grab a ref on the request that will be waited on */
  	kref_get(&req->wb_kref);
  
  	nfs_page_group_unlock(head);
  	spin_unlock(&inode->i_lock);
  
  	/* release ref from nfs_page_find_head_request_locked */
  	nfs_release_request(head);
  
  	if (!nonblock)
  		ret = nfs_wait_on_request(req);
  	else
  		ret = -EAGAIN;
  	nfs_release_request(req);
  
  	return ret;
  }
  
  /*
   * 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,
  				 struct nfs_page *old_head)
  {
  	while (destroy_list) {
  		struct nfs_page *subreq = destroy_list;
  
  		destroy_list = (subreq->wb_this_page == old_head) ?
  				   NULL : subreq->wb_this_page;
  
  		WARN_ON_ONCE(old_head != subreq->wb_head);
  
  		/* make sure old group is not used */
  		subreq->wb_head = subreq;
  		subreq->wb_this_page = subreq;
d45813835   Weston Andros Adamson   nfs: handle multi...
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
  		/* subreq is now totally disconnected from page group or any
  		 * write / commit lists. last chance to wake any waiters */
  		nfs_unlock_request(subreq);
  
  		if (!test_bit(PG_TEARDOWN, &subreq->wb_flags)) {
  			/* release ref on old head request */
  			nfs_release_request(old_head);
  
  			nfs_page_group_clear_bits(subreq);
  
  			/* release the PG_INODE_REF reference */
  			if (test_and_clear_bit(PG_INODE_REF, &subreq->wb_flags))
  				nfs_release_request(subreq);
  			else
  				WARN_ON_ONCE(1);
  		} else {
  			WARN_ON_ONCE(test_bit(PG_CLEAN, &subreq->wb_flags));
  			/* zombie requests have already released the last
  			 * reference and were waiting on the rest of the
  			 * group to complete. Since it's no longer part of a
  			 * group, simply free the request */
  			nfs_page_group_clear_bits(subreq);
  			nfs_free_request(subreq);
  		}
  	}
  }
  
  /*
   * nfs_lock_and_join_requests - join all subreqs to the head req and return
   *                              a locked reference, cancelling any pending
   *                              operations for this page.
   *
   * @page - the page used to lookup the "page group" of nfs_page structures
   * @nonblock - if true, don't block waiting for request locks
   *
   * 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, bool nonblock)
e261f51f2   Trond Myklebust   NFS: Make nfs_upd...
410
  {
d56b4ddf7   Mel Gorman   nfs: teach the NF...
411
  	struct inode *inode = page_file_mapping(page)->host;
d45813835   Weston Andros Adamson   nfs: handle multi...
412
413
414
  	struct nfs_page *head, *subreq;
  	struct nfs_page *destroy_list = NULL;
  	unsigned int total_bytes;
e261f51f2   Trond Myklebust   NFS: Make nfs_upd...
415
  	int ret;
d45813835   Weston Andros Adamson   nfs: handle multi...
416
417
418
419
  try_again:
  	total_bytes = 0;
  
  	WARN_ON_ONCE(destroy_list);
587142f85   Trond Myklebust   NFS: Replace NFS_...
420
  	spin_lock(&inode->i_lock);
d45813835   Weston Andros Adamson   nfs: handle multi...
421
422
423
424
425
426
427
428
429
  
  	/*
  	 * 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_page_find_head_request_locked(NFS_I(inode), page);
  
  	if (!head) {
587142f85   Trond Myklebust   NFS: Replace NFS_...
430
  		spin_unlock(&inode->i_lock);
d45813835   Weston Andros Adamson   nfs: handle multi...
431
432
  		return NULL;
  	}
7c3af9752   Weston Andros Adamson   nfs: don't sleep ...
433
434
  	/* holding inode lock, so always make a non-blocking call to try the
  	 * page group lock */
fd2f3a06d   Weston Andros Adamson   nfs: change nfs_p...
435
  	ret = nfs_page_group_lock(head, true);
94970014c   Weston Andros Adamson   nfs: fix error ha...
436
437
  	if (ret < 0) {
  		spin_unlock(&inode->i_lock);
7c3af9752   Weston Andros Adamson   nfs: don't sleep ...
438
439
440
441
442
443
  
  		if (!nonblock && ret == -EAGAIN) {
  			nfs_page_group_lock_wait(head);
  			nfs_release_request(head);
  			goto try_again;
  		}
94970014c   Weston Andros Adamson   nfs: fix error ha...
444
  		nfs_release_request(head);
e7029206f   Weston Andros Adamson   nfs: check wait_o...
445
  		return ERR_PTR(ret);
94970014c   Weston Andros Adamson   nfs: fix error ha...
446
  	}
7c3af9752   Weston Andros Adamson   nfs: don't sleep ...
447
448
  
  	/* lock each request in the page group */
d45813835   Weston Andros Adamson   nfs: handle multi...
449
450
451
452
  	subreq = head;
  	do {
  		/*
  		 * Subrequests are always contiguous, non overlapping
309a1d65b   Weston Andros Adamson   nfs: handle overl...
453
  		 * and in order - but may be repeated (mirrored writes).
d45813835   Weston Andros Adamson   nfs: handle multi...
454
  		 */
309a1d65b   Weston Andros Adamson   nfs: handle overl...
455
456
457
458
459
460
461
462
463
464
  		if (subreq->wb_offset == (head->wb_offset + total_bytes)) {
  			/* keep track of how many bytes this group covers */
  			total_bytes += subreq->wb_bytes;
  		} else if (WARN_ON_ONCE(subreq->wb_offset < head->wb_offset ||
  			    ((subreq->wb_offset + subreq->wb_bytes) >
  			     (head->wb_offset + total_bytes)))) {
  			nfs_page_group_unlock(head);
  			spin_unlock(&inode->i_lock);
  			return ERR_PTR(-EIO);
  		}
d45813835   Weston Andros Adamson   nfs: handle multi...
465
466
467
468
469
470
471
472
473
  
  		if (!nfs_lock_request(subreq)) {
  			/* releases page group bit lock and
  			 * inode spin lock and all references */
  			ret = nfs_unroll_locks_and_wait(inode, head,
  				subreq, nonblock);
  
  			if (ret == 0)
  				goto try_again;
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
474
  			return ERR_PTR(ret);
d45813835   Weston Andros Adamson   nfs: handle multi...
475
476
477
478
479
480
481
482
483
  		}
  
  		subreq = subreq->wb_this_page;
  	} while (subreq != head);
  
  	/* 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...
484
  		nfs_clear_request_commit(subreq);
d45813835   Weston Andros Adamson   nfs: handle multi...
485
486
487
488
489
490
491
492
493
494
495
496
  		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;
  
  		/* change head request to cover whole range that
  		 * the former page group covered */
  		head->wb_bytes = total_bytes;
e261f51f2   Trond Myklebust   NFS: Make nfs_upd...
497
  	}
d45813835   Weston Andros Adamson   nfs: handle multi...
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
  
  	/*
  	 * prepare head request to be added to new pgio descriptor
  	 */
  	nfs_page_group_clear_bits(head);
  
  	/*
  	 * some part of the group was still on the inode list - otherwise
  	 * the group wouldn't be involved in async write.
  	 * grab a reference for the head request, iff it needs one.
  	 */
  	if (!test_and_set_bit(PG_INODE_REF, &head->wb_flags))
  		kref_get(&head->wb_kref);
  
  	nfs_page_group_unlock(head);
411a99adf   Weston Andros Adamson   nfs: clear_reques...
513
  	/* drop lock to clean uprequests on destroy list */
587142f85   Trond Myklebust   NFS: Replace NFS_...
514
  	spin_unlock(&inode->i_lock);
d45813835   Weston Andros Adamson   nfs: handle multi...
515
516
  
  	nfs_destroy_unlinked_subrequests(destroy_list, head);
d45813835   Weston Andros Adamson   nfs: handle multi...
517
518
519
  	/* still holds ref on head from nfs_page_find_head_request_locked
  	 * and still has lock on head from lock loop */
  	return head;
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
520
  }
0bcbf039f   Peng Tao   nfs: handle reque...
521
522
523
524
525
526
527
528
  static void nfs_write_error_remove_page(struct nfs_page *req)
  {
  	nfs_unlock_request(req);
  	nfs_end_page_writeback(req);
  	nfs_release_request(req);
  	generic_error_remove_page(page_file_mapping(req->wb_page),
  				  req->wb_page);
  }
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
529
530
531
532
533
  /*
   * 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,
d6c843b96   Peng Tao   nfs: only remove ...
534
535
  				struct page *page, bool nonblock,
  				bool launder)
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
536
537
538
  {
  	struct nfs_page *req;
  	int ret = 0;
d45813835   Weston Andros Adamson   nfs: handle multi...
539
  	req = nfs_lock_and_join_requests(page, nonblock);
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
540
541
542
543
544
  	if (!req)
  		goto out;
  	ret = PTR_ERR(req);
  	if (IS_ERR(req))
  		goto out;
deed85e76   Trond Myklebust   NFS: Remove BUG_O...
545
546
  	nfs_set_page_writeback(page);
  	WARN_ON_ONCE(test_bit(PG_CLEAN, &req->wb_flags));
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
547

deed85e76   Trond Myklebust   NFS: Remove BUG_O...
548
  	ret = 0;
f8512ad0d   Fred Isaman   nfs: don't ignore...
549
  	if (!nfs_pageio_add_request(pgio, req)) {
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
550
  		ret = pgio->pg_error;
0bcbf039f   Peng Tao   nfs: handle reque...
551
  		/*
d6c843b96   Peng Tao   nfs: only remove ...
552
553
554
  		 * Remove the problematic req upon fatal errors
  		 * in launder case, while other dirty pages can
  		 * still be around until they get flushed.
0bcbf039f   Peng Tao   nfs: handle reque...
555
556
557
  		 */
  		if (nfs_error_is_fatal(ret)) {
  			nfs_context_set_write_error(req->wb_context, ret);
d6c843b96   Peng Tao   nfs: only remove ...
558
559
560
561
  			if (launder) {
  				nfs_write_error_remove_page(req);
  				goto out;
  			}
0bcbf039f   Peng Tao   nfs: handle reque...
562
  		}
d6c843b96   Peng Tao   nfs: only remove ...
563
564
  		nfs_redirty_request(req);
  		ret = -EAGAIN;
40f90271a   Trond Myklebust   NFS: Fix up page ...
565
566
567
  	} else
  		nfs_add_stats(page_file_mapping(page)->host,
  				NFSIOS_WRITEPAGES, 1);
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
568
569
  out:
  	return ret;
e261f51f2   Trond Myklebust   NFS: Make nfs_upd...
570
  }
d6c843b96   Peng Tao   nfs: only remove ...
571
572
  static int nfs_do_writepage(struct page *page, struct writeback_control *wbc,
  			    struct nfs_pageio_descriptor *pgio, bool launder)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573
  {
cfb506e1d   Trond Myklebust   NFS: Ensure that ...
574
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
575

8cd797887   Huang Ying   mm: remove page_f...
576
  	nfs_pageio_cond_complete(pgio, page_index(page));
d6c843b96   Peng Tao   nfs: only remove ...
577
578
  	ret = nfs_page_async_flush(pgio, page, wbc->sync_mode == WB_SYNC_NONE,
  				   launder);
cfb506e1d   Trond Myklebust   NFS: Ensure that ...
579
580
581
582
583
  	if (ret == -EAGAIN) {
  		redirty_page_for_writepage(wbc, page);
  		ret = 0;
  	}
  	return ret;
f758c8851   Trond Myklebust   NFS: Clean up nfs...
584
  }
7fe7f8487   Trond Myklebust   NFS: Avoid a dead...
585

f758c8851   Trond Myklebust   NFS: Clean up nfs...
586
587
588
  /*
   * Write an mmapped page to the server.
   */
d6c843b96   Peng Tao   nfs: only remove ...
589
590
591
  static int nfs_writepage_locked(struct page *page,
  				struct writeback_control *wbc,
  				bool launder)
f758c8851   Trond Myklebust   NFS: Clean up nfs...
592
593
  {
  	struct nfs_pageio_descriptor pgio;
40f90271a   Trond Myklebust   NFS: Fix up page ...
594
  	struct inode *inode = page_file_mapping(page)->host;
f758c8851   Trond Myklebust   NFS: Clean up nfs...
595
  	int err;
49a70f278   Trond Myklebust   NFS: Cleanup: add...
596

40f90271a   Trond Myklebust   NFS: Fix up page ...
597
  	nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGE);
811ed92ec   Trond Myklebust   NFS: writepage of...
598
  	nfs_pageio_init_write(&pgio, inode, 0,
a20c93e31   Christoph Hellwig   nfs: remove ->wri...
599
  				false, &nfs_async_write_completion_ops);
d6c843b96   Peng Tao   nfs: only remove ...
600
  	err = nfs_do_writepage(page, wbc, &pgio, launder);
f758c8851   Trond Myklebust   NFS: Clean up nfs...
601
602
603
604
605
606
  	nfs_pageio_complete(&pgio);
  	if (err < 0)
  		return err;
  	if (pgio.pg_error < 0)
  		return pgio.pg_error;
  	return 0;
4d770ccf4   Trond Myklebust   NFS: Ensure that ...
607
608
609
610
  }
  
  int nfs_writepage(struct page *page, struct writeback_control *wbc)
  {
f758c8851   Trond Myklebust   NFS: Clean up nfs...
611
  	int ret;
4d770ccf4   Trond Myklebust   NFS: Ensure that ...
612

d6c843b96   Peng Tao   nfs: only remove ...
613
  	ret = nfs_writepage_locked(page, wbc, false);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
614
  	unlock_page(page);
f758c8851   Trond Myklebust   NFS: Clean up nfs...
615
616
617
618
619
620
  	return ret;
  }
  
  static int nfs_writepages_callback(struct page *page, struct writeback_control *wbc, void *data)
  {
  	int ret;
d6c843b96   Peng Tao   nfs: only remove ...
621
  	ret = nfs_do_writepage(page, wbc, data, false);
f758c8851   Trond Myklebust   NFS: Clean up nfs...
622
623
  	unlock_page(page);
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
624
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
625
626
  int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
627
  	struct inode *inode = mapping->host;
c63c7b051   Trond Myklebust   NFS: Fix a race w...
628
  	struct nfs_pageio_descriptor pgio;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
629
  	int err;
91d5b4702   Chuck Lever   NFS: add I/O perf...
630
  	nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES);
a20c93e31   Christoph Hellwig   nfs: remove ->wri...
631
632
  	nfs_pageio_init_write(&pgio, inode, wb_priority(wbc), false,
  				&nfs_async_write_completion_ops);
f758c8851   Trond Myklebust   NFS: Clean up nfs...
633
  	err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio);
c63c7b051   Trond Myklebust   NFS: Fix a race w...
634
  	nfs_pageio_complete(&pgio);
72cb77f4a   Trond Myklebust   NFS: Throttle pag...
635

f758c8851   Trond Myklebust   NFS: Clean up nfs...
636
  	if (err < 0)
72cb77f4a   Trond Myklebust   NFS: Throttle pag...
637
638
639
640
  		goto out_err;
  	err = pgio.pg_error;
  	if (err < 0)
  		goto out_err;
c63c7b051   Trond Myklebust   NFS: Fix a race w...
641
  	return 0;
72cb77f4a   Trond Myklebust   NFS: Throttle pag...
642
643
  out_err:
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
644
645
646
647
648
  }
  
  /*
   * Insert a write request into an inode
   */
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
649
  static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
650
651
  {
  	struct nfs_inode *nfsi = NFS_I(inode);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
652

2bfc6e566   Weston Andros Adamson   nfs: add support ...
653
  	WARN_ON_ONCE(req->wb_this_page != req);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
654
  	/* Lock the request! */
7ad84aa94   Trond Myklebust   NFS: Clean up - s...
655
  	nfs_lock_request(req);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
656
657
  
  	spin_lock(&inode->i_lock);
cb1410c71   Weston Andros Adamson   NFS: fix subtle c...
658
659
  	if (!nfsi->nrequests &&
  	    NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE))
a9a4a87a5   Trond Myklebust   NFS: Use the inod...
660
  		inode->i_version++;
29418aa4b   Mel Gorman   nfs: disable data...
661
662
663
664
665
666
667
668
669
  	/*
  	 * Swap-space should not get truncated. Hence no need to plug the race
  	 * with invalidate/truncate.
  	 */
  	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);
  	}
cb1410c71   Weston Andros Adamson   NFS: fix subtle c...
670
  	nfsi->nrequests++;
17089a29a   Weston Andros Adamson   nfs: mark nfs_pag...
671
  	/* this a head request for a page group - mark it as having an
cb1410c71   Weston Andros Adamson   NFS: fix subtle c...
672
673
674
  	 * 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...
675
  	WARN_ON(test_and_set_bit(PG_INODE_REF, &req->wb_flags));
c03b40246   Trond Myklebust   NFS: Convert stru...
676
  	kref_get(&req->wb_kref);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
677
  	spin_unlock(&inode->i_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
678
679
680
  }
  
  /*
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
681
   * Remove a write request from an inode
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
682
683
684
   */
  static void nfs_inode_remove_request(struct nfs_page *req)
  {
2b0143b5c   David Howells   VFS: normal files...
685
  	struct inode *inode = d_inode(req->wb_context->dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686
  	struct nfs_inode *nfsi = NFS_I(inode);
20633f042   Weston Andros Adamson   nfs: page group s...
687
  	struct nfs_page *head;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688

20633f042   Weston Andros Adamson   nfs: page group s...
689
690
691
692
  	if (nfs_page_group_sync_on_bit(req, PG_REMOVE)) {
  		head = req->wb_head;
  
  		spin_lock(&inode->i_lock);
67911c8f1   Anna Schumaker   NFS: Add nfs_comm...
693
  		if (likely(head->wb_page && !PageSwapCache(head->wb_page))) {
20633f042   Weston Andros Adamson   nfs: page group s...
694
695
  			set_page_private(head->wb_page, 0);
  			ClearPagePrivate(head->wb_page);
959054469   NeilBrown   NFS: avoid deadlo...
696
697
  			smp_mb__after_atomic();
  			wake_up_page(head->wb_page, PG_private);
20633f042   Weston Andros Adamson   nfs: page group s...
698
699
  			clear_bit(PG_MAPPED, &head->wb_flags);
  		}
cb1410c71   Weston Andros Adamson   NFS: fix subtle c...
700
701
702
703
704
  		nfsi->nrequests--;
  		spin_unlock(&inode->i_lock);
  	} else {
  		spin_lock(&inode->i_lock);
  		nfsi->nrequests--;
20633f042   Weston Andros Adamson   nfs: page group s...
705
  		spin_unlock(&inode->i_lock);
29418aa4b   Mel Gorman   nfs: disable data...
706
  	}
17089a29a   Weston Andros Adamson   nfs: mark nfs_pag...
707
708
709
  
  	if (test_and_clear_bit(PG_INODE_REF, &req->wb_flags))
  		nfs_release_request(req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710
  }
61822ab5e   Trond Myklebust   NFS: Ensure we on...
711
  static void
6d884e8fc   Fred   nfs: nfs_redirty_...
712
  nfs_mark_request_dirty(struct nfs_page *req)
61822ab5e   Trond Myklebust   NFS: Ensure we on...
713
  {
67911c8f1   Anna Schumaker   NFS: Add nfs_comm...
714
715
  	if (req->wb_page)
  		__set_page_dirty_nobuffers(req->wb_page);
61822ab5e   Trond Myklebust   NFS: Ensure we on...
716
  }
3a3908c8b   Trond Myklebust   NFS: Fix a compil...
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
  /*
   * 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...
748
  /**
86d80f973   Trond Myklebust   NFSv4.1/pnfs: Fix...
749
750
751
752
753
754
755
756
757
   * 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.
   *
fe238e601   Dave Wysochanski   NFS: Save struct ...
758
   * The caller must hold cinfo->inode->i_lock, and the nfs_page lock.
86d80f973   Trond Myklebust   NFSv4.1/pnfs: Fix...
759
760
761
762
763
764
765
766
767
768
769
770
   */
  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);
  	cinfo->mds->ncommit++;
  }
  EXPORT_SYMBOL_GPL(nfs_request_add_commit_list_locked);
  
  /**
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
771
772
   * nfs_request_add_commit_list - add request to a commit list
   * @req: pointer to a struct nfs_page
ea2cf2282   Fred Isaman   NFS: create struc...
773
774
   * @dst: commit list head
   * @cinfo: holds list lock and accounting info
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
775
   *
ea2cf2282   Fred Isaman   NFS: create struc...
776
   * This sets the PG_CLEAN bit, updates the cinfo count of
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
777
778
779
   * number of outstanding requests requiring a commit as well as
   * the MM page stats.
   *
ea2cf2282   Fred Isaman   NFS: create struc...
780
   * The caller must _not_ hold the cinfo->lock, but must be
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
781
   * holding the nfs_page lock.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
782
   */
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
783
  void
6272dcc6b   Anna Schumaker   NFS: Simplify nfs...
784
  nfs_request_add_commit_list(struct nfs_page *req, struct nfs_commit_info *cinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
785
  {
fe238e601   Dave Wysochanski   NFS: Save struct ...
786
  	spin_lock(&cinfo->inode->i_lock);
6272dcc6b   Anna Schumaker   NFS: Simplify nfs...
787
  	nfs_request_add_commit_list_locked(req, &cinfo->mds->list, cinfo);
fe238e601   Dave Wysochanski   NFS: Save struct ...
788
  	spin_unlock(&cinfo->inode->i_lock);
67911c8f1   Anna Schumaker   NFS: Add nfs_comm...
789
790
  	if (req->wb_page)
  		nfs_mark_page_unstable(req->wb_page, cinfo);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791
  }
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
792
793
794
795
796
  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...
797
   * @cinfo: holds list lock and accounting info
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
798
   *
ea2cf2282   Fred Isaman   NFS: create struc...
799
   * This clears the PG_CLEAN bit, and updates the cinfo's count of
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
800
801
802
   * number of outstanding requests requiring a commit
   * It does not update the MM page stats.
   *
ea2cf2282   Fred Isaman   NFS: create struc...
803
   * The caller _must_ hold the cinfo->lock and the nfs_page lock.
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
804
805
   */
  void
ea2cf2282   Fred Isaman   NFS: create struc...
806
807
  nfs_request_remove_commit_list(struct nfs_page *req,
  			       struct nfs_commit_info *cinfo)
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
808
  {
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
809
810
811
  	if (!test_and_clear_bit(PG_CLEAN, &(req)->wb_flags))
  		return;
  	nfs_list_remove_request(req);
ea2cf2282   Fred Isaman   NFS: create struc...
812
  	cinfo->mds->ncommit--;
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
813
814
  }
  EXPORT_SYMBOL_GPL(nfs_request_remove_commit_list);
ea2cf2282   Fred Isaman   NFS: create struc...
815
816
817
  static void nfs_init_cinfo_from_inode(struct nfs_commit_info *cinfo,
  				      struct inode *inode)
  {
fe238e601   Dave Wysochanski   NFS: Save struct ...
818
  	cinfo->inode = inode;
ea2cf2282   Fred Isaman   NFS: create struc...
819
820
  	cinfo->mds = &NFS_I(inode)->commit_info;
  	cinfo->ds = pnfs_get_ds_info(inode);
b359f9d09   Fred Isaman   NFS: add dreq to ...
821
  	cinfo->dreq = NULL;
f453a54a0   Fred Isaman   NFS: create nfs_c...
822
  	cinfo->completion_ops = &nfs_commit_completion_ops;
ea2cf2282   Fred Isaman   NFS: create struc...
823
824
825
826
827
828
  }
  
  void nfs_init_cinfo(struct nfs_commit_info *cinfo,
  		    struct inode *inode,
  		    struct nfs_direct_req *dreq)
  {
1763da123   Fred Isaman   NFS: rewrite dire...
829
830
831
832
  	if (dreq)
  		nfs_init_cinfo_from_dreq(cinfo, dreq);
  	else
  		nfs_init_cinfo_from_inode(cinfo, inode);
ea2cf2282   Fred Isaman   NFS: create struc...
833
834
  }
  EXPORT_SYMBOL_GPL(nfs_init_cinfo);
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
835
836
837
838
  
  /*
   * Add a request to the inode's commit list.
   */
1763da123   Fred Isaman   NFS: rewrite dire...
839
  void
ea2cf2282   Fred Isaman   NFS: create struc...
840
  nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg,
b57ff1303   Weston Andros Adamson   pnfs: pass ds_com...
841
  			struct nfs_commit_info *cinfo, u32 ds_commit_idx)
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
842
  {
b57ff1303   Weston Andros Adamson   pnfs: pass ds_com...
843
  	if (pnfs_mark_request_commit(req, lseg, cinfo, ds_commit_idx))
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
844
  		return;
6272dcc6b   Anna Schumaker   NFS: Simplify nfs...
845
  	nfs_request_add_commit_list(req, cinfo);
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
846
  }
8e821cad1   Trond Myklebust   NFS: clean up the...
847

d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
848
849
850
  static void
  nfs_clear_page_commit(struct page *page)
  {
11fb99898   Mel Gorman   mm: move most fil...
851
  	dec_node_page_state(page, NR_UNSTABLE_NFS);
93f78d882   Tejun Heo   writeback: move b...
852
853
  	dec_wb_stat(&inode_to_bdi(page_file_mapping(page)->host)->wb,
  		    WB_RECLAIMABLE);
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
854
  }
411a99adf   Weston Andros Adamson   nfs: clear_reques...
855
  /* Called holding inode (/cinfo) lock */
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
856
  static void
e468bae97   Trond Myklebust   NFS: Allow redirt...
857
858
  nfs_clear_request_commit(struct nfs_page *req)
  {
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
859
  	if (test_bit(PG_CLEAN, &req->wb_flags)) {
2b0143b5c   David Howells   VFS: normal files...
860
  		struct inode *inode = d_inode(req->wb_context->dentry);
ea2cf2282   Fred Isaman   NFS: create struc...
861
  		struct nfs_commit_info cinfo;
e468bae97   Trond Myklebust   NFS: Allow redirt...
862

ea2cf2282   Fred Isaman   NFS: create struc...
863
864
  		nfs_init_cinfo_from_inode(&cinfo, inode);
  		if (!pnfs_clear_request_commit(req, &cinfo)) {
ea2cf2282   Fred Isaman   NFS: create struc...
865
  			nfs_request_remove_commit_list(req, &cinfo);
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
866
  		}
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
867
  		nfs_clear_page_commit(req->wb_page);
e468bae97   Trond Myklebust   NFS: Allow redirt...
868
  	}
e468bae97   Trond Myklebust   NFS: Allow redirt...
869
  }
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
870
  int nfs_write_need_commit(struct nfs_pgio_header *hdr)
8e821cad1   Trond Myklebust   NFS: clean up the...
871
  {
c65e6254c   Weston Andros Adamson   nfs: remove unuse...
872
  	if (hdr->verf.committed == NFS_DATA_SYNC)
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
873
  		return hdr->lseg == NULL;
c65e6254c   Weston Andros Adamson   nfs: remove unuse...
874
  	return hdr->verf.committed != NFS_FILE_SYNC;
8e821cad1   Trond Myklebust   NFS: clean up the...
875
  }
061ae2edb   Fred Isaman   NFS: create compl...
876
  static void nfs_write_completion(struct nfs_pgio_header *hdr)
8e821cad1   Trond Myklebust   NFS: clean up the...
877
  {
ea2cf2282   Fred Isaman   NFS: create struc...
878
  	struct nfs_commit_info cinfo;
6c75dc0d4   Fred Isaman   NFS: merge _full ...
879
880
881
882
  	unsigned long bytes = 0;
  
  	if (test_bit(NFS_IOHDR_REDO, &hdr->flags))
  		goto out;
ea2cf2282   Fred Isaman   NFS: create struc...
883
  	nfs_init_cinfo_from_inode(&cinfo, hdr->inode);
6c75dc0d4   Fred Isaman   NFS: merge _full ...
884
885
  	while (!list_empty(&hdr->pages)) {
  		struct nfs_page *req = nfs_list_entry(hdr->pages.next);
6c75dc0d4   Fred Isaman   NFS: merge _full ...
886
887
888
889
890
  
  		bytes += req->wb_bytes;
  		nfs_list_remove_request(req);
  		if (test_bit(NFS_IOHDR_ERROR, &hdr->flags) &&
  		    (hdr->good_bytes < bytes)) {
d1182b33e   Trond Myklebust   NFS: nfs_set_page...
891
  			nfs_set_pageerror(req->wb_page);
6c75dc0d4   Fred Isaman   NFS: merge _full ...
892
893
894
  			nfs_context_set_write_error(req->wb_context, hdr->error);
  			goto remove_req;
  		}
c65e6254c   Weston Andros Adamson   nfs: remove unuse...
895
  		if (nfs_write_need_commit(hdr)) {
f79d06f54   Anna Schumaker   NFS: Move the wri...
896
  			memcpy(&req->wb_verf, &hdr->verf.verifier, sizeof(req->wb_verf));
b57ff1303   Weston Andros Adamson   pnfs: pass ds_com...
897
  			nfs_mark_request_commit(req, hdr->lseg, &cinfo,
a7d42ddb3   Weston Andros Adamson   nfs: add mirrorin...
898
  				hdr->pgio_mirror_idx);
6c75dc0d4   Fred Isaman   NFS: merge _full ...
899
900
901
902
903
  			goto next;
  		}
  remove_req:
  		nfs_inode_remove_request(req);
  next:
1d1afcbc2   Trond Myklebust   NFS: Clean up - R...
904
  		nfs_unlock_request(req);
20633f042   Weston Andros Adamson   nfs: page group s...
905
  		nfs_end_page_writeback(req);
3aff4ebb9   Trond Myklebust   NFS: Prevent a de...
906
  		nfs_release_request(req);
6c75dc0d4   Fred Isaman   NFS: merge _full ...
907
908
909
  	}
  out:
  	hdr->release(hdr);
8e821cad1   Trond Myklebust   NFS: clean up the...
910
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
911

ce59515c1   Anna Schumaker   NFS: Create a com...
912
  unsigned long
ea2cf2282   Fred Isaman   NFS: create struc...
913
  nfs_reqs_to_commit(struct nfs_commit_info *cinfo)
fb8a1f11b   Trond Myklebust   NFS: cleanup - re...
914
  {
ea2cf2282   Fred Isaman   NFS: create struc...
915
  	return cinfo->mds->ncommit;
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
916
  }
fe238e601   Dave Wysochanski   NFS: Save struct ...
917
  /* cinfo->inode->i_lock held by caller */
1763da123   Fred Isaman   NFS: rewrite dire...
918
  int
ea2cf2282   Fred Isaman   NFS: create struc...
919
920
  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...
921
922
923
924
925
  {
  	struct nfs_page *req, *tmp;
  	int ret = 0;
  
  	list_for_each_entry_safe(req, tmp, src, wb_list) {
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
926
927
  		if (!nfs_lock_request(req))
  			continue;
7ad84aa94   Trond Myklebust   NFS: Clean up - s...
928
  		kref_get(&req->wb_kref);
fe238e601   Dave Wysochanski   NFS: Save struct ...
929
  		if (cond_resched_lock(&cinfo->inode->i_lock))
3b3be88d6   Trond Myklebust   NFS: Use cond_res...
930
  			list_safe_reset_next(req, tmp, wb_list);
ea2cf2282   Fred Isaman   NFS: create struc...
931
  		nfs_request_remove_commit_list(req, cinfo);
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
932
933
  		nfs_list_add_request(req, dst);
  		ret++;
1763da123   Fred Isaman   NFS: rewrite dire...
934
  		if ((ret == max) && !cinfo->dreq)
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
935
  			break;
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
936
937
  	}
  	return ret;
fb8a1f11b   Trond Myklebust   NFS: cleanup - re...
938
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
939
940
941
  /*
   * nfs_scan_commit - Scan an inode for commit requests
   * @inode: NFS inode to scan
ea2cf2282   Fred Isaman   NFS: create struc...
942
943
   * @dst: mds destination list
   * @cinfo: mds and ds lists of reqs ready to commit
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
944
945
946
947
   *
   * 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...
948
  int
ea2cf2282   Fred Isaman   NFS: create struc...
949
950
  nfs_scan_commit(struct inode *inode, struct list_head *dst,
  		struct nfs_commit_info *cinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
951
  {
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
952
  	int ret = 0;
fb8a1f11b   Trond Myklebust   NFS: cleanup - re...
953

fe238e601   Dave Wysochanski   NFS: Save struct ...
954
  	spin_lock(&cinfo->inode->i_lock);
ea2cf2282   Fred Isaman   NFS: create struc...
955
  	if (cinfo->mds->ncommit > 0) {
8dd377588   Trond Myklebust   NFSv4.1: Clean up...
956
  		const int max = INT_MAX;
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
957

ea2cf2282   Fred Isaman   NFS: create struc...
958
959
960
  		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...
961
  	}
fe238e601   Dave Wysochanski   NFS: Save struct ...
962
  	spin_unlock(&cinfo->inode->i_lock);
ff778d02b   Trond Myklebust   NFS: Add a count ...
963
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
964
  }
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
965

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
966
  /*
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
967
968
   * 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
969
   *
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
970
971
   * If the attempt fails, then the existing request is flushed out
   * to disk.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
972
   */
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
973
974
975
976
  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
977
  {
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
978
979
980
981
982
983
984
  	struct nfs_page *req;
  	unsigned int rqend;
  	unsigned int end;
  	int error;
  
  	if (!PagePrivate(page))
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
985
986
  
  	end = offset + bytes;
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
987
  	spin_lock(&inode->i_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
988

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
989
  	for (;;) {
84d3a9a91   Weston Andros Adamson   nfs: change find_...
990
  		req = nfs_page_find_head_request_locked(NFS_I(inode), page);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
991
992
  		if (req == NULL)
  			goto out_unlock;
2bfc6e566   Weston Andros Adamson   nfs: add support ...
993
994
995
  		/* should be handled by nfs_flush_incompatible */
  		WARN_ON_ONCE(req->wb_head != req);
  		WARN_ON_ONCE(req->wb_this_page != req);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
996
997
998
999
1000
1001
1002
  		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.
  		 */
e468bae97   Trond Myklebust   NFS: Allow redirt...
1003
  		if (offset > rqend
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1004
1005
  		    || end < req->wb_offset)
  			goto out_flushme;
7ad84aa94   Trond Myklebust   NFS: Clean up - s...
1006
  		if (nfs_lock_request(req))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1007
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1008

e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1009
  		/* The request is locked, so wait and then retry */
587142f85   Trond Myklebust   NFS: Replace NFS_...
1010
  		spin_unlock(&inode->i_lock);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1011
1012
1013
1014
1015
  		error = nfs_wait_on_request(req);
  		nfs_release_request(req);
  		if (error != 0)
  			goto out_err;
  		spin_lock(&inode->i_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1016
1017
1018
1019
1020
1021
  	}
  
  	/* 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
1022
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1023
1024
  	if (end > rqend)
  		req->wb_bytes = end - req->wb_offset;
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1025
1026
1027
  	else
  		req->wb_bytes = rqend - req->wb_offset;
  out_unlock:
ca138f368   Fred Isaman   NFS: check for re...
1028
1029
  	if (req)
  		nfs_clear_request_commit(req);
411a99adf   Weston Andros Adamson   nfs: clear_reques...
1030
  	spin_unlock(&inode->i_lock);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
  	return req;
  out_flushme:
  	spin_unlock(&inode->i_lock);
  	nfs_release_request(req);
  	error = nfs_wb_page(inode, page);
  out_err:
  	return ERR_PTR(error);
  }
  
  /*
   * 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...
1050
  	struct inode *inode = page_file_mapping(page)->host;
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1051
  	struct nfs_page	*req;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1052

e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1053
1054
1055
  	req = nfs_try_to_update_request(inode, page, offset, bytes);
  	if (req != NULL)
  		goto out;
2bfc6e566   Weston Andros Adamson   nfs: add support ...
1056
  	req = nfs_create_request(ctx, page, NULL, offset, bytes);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1057
1058
  	if (IS_ERR(req))
  		goto out;
d6d6dc7cd   Fred Isaman   NFS: remove nfs_i...
1059
  	nfs_inode_add_request(inode, req);
efc91ed01   Trond Myklebust   NFS: Optimise app...
1060
  out:
61e930a90   Trond Myklebust   NFS: Fix a writeb...
1061
  	return req;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1062
  }
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
  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...
1073
  	nfs_mark_uptodate(req);
a6305ddb0   Trond Myklebust   NFS: Fix a race w...
1074
  	nfs_mark_request_dirty(req);
1d1afcbc2   Trond Myklebust   NFS: Clean up - R...
1075
  	nfs_unlock_and_release_request(req);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
1076
1077
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1078
1079
  int nfs_flush_incompatible(struct file *file, struct page *page)
  {
cd3758e37   Trond Myklebust   NFS: Replace file...
1080
  	struct nfs_open_context *ctx = nfs_file_open_context(file);
2a369153c   Trond Myklebust   NFS: Clean up hel...
1081
  	struct nfs_lock_context *l_ctx;
bd61e0a9c   Jeff Layton   locks: convert po...
1082
  	struct file_lock_context *flctx = file_inode(file)->i_flctx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1083
  	struct nfs_page	*req;
1a54533ec   Trond Myklebust   NFS: Add nfs_set_...
1084
  	int do_flush, status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1085
1086
1087
1088
1089
1090
1091
1092
  	/*
  	 * 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_...
1093
  	do {
84d3a9a91   Weston Andros Adamson   nfs: change find_...
1094
  		req = nfs_page_find_head_request(page);
1a54533ec   Trond Myklebust   NFS: Add nfs_set_...
1095
1096
  		if (req == NULL)
  			return 0;
2a369153c   Trond Myklebust   NFS: Clean up hel...
1097
  		l_ctx = req->wb_lock_context;
138a2935d   Trond Myklebust   NFS: Relax requir...
1098
1099
  		do_flush = req->wb_page != page ||
  			!nfs_match_open_context(req->wb_context, ctx);
2bfc6e566   Weston Andros Adamson   nfs: add support ...
1100
1101
  		/* for now, flush if more than 1 request in page_group */
  		do_flush |= req->wb_this_page != req;
bd61e0a9c   Jeff Layton   locks: convert po...
1102
1103
1104
  		if (l_ctx && flctx &&
  		    !(list_empty_careful(&flctx->flc_posix) &&
  		      list_empty_careful(&flctx->flc_flock))) {
d51fdb87a   NeilBrown   NFS: discard nfs_...
1105
  			do_flush |= l_ctx->lockowner != current->files;
5263e31e4   Jeff Layton   locks: move flock...
1106
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1107
  		nfs_release_request(req);
1a54533ec   Trond Myklebust   NFS: Add nfs_set_...
1108
1109
  		if (!do_flush)
  			return 0;
d56b4ddf7   Mel Gorman   nfs: teach the NF...
1110
  		status = nfs_wb_page(page_file_mapping(page)->host, page);
1a54533ec   Trond Myklebust   NFS: Add nfs_set_...
1111
1112
  	} while (status == 0);
  	return status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1113
1114
1115
  }
  
  /*
dc24826bf   Andy Adamson   NFS avoid expired...
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
   * 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);
  	struct rpc_auth *auth = NFS_SERVER(inode)->client->cl_auth;
  
  	return rpcauth_key_timeout_notify(auth, ctx->cred);
  }
  
  /*
   * Test if the open context credential key is marked to expire soon.
   */
ce52914eb   Scott Mayhew   sunrpc: move NO_C...
1137
  bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx, struct inode *inode)
dc24826bf   Andy Adamson   NFS avoid expired...
1138
  {
ce52914eb   Scott Mayhew   sunrpc: move NO_C...
1139
1140
1141
  	struct rpc_auth *auth = NFS_SERVER(inode)->client->cl_auth;
  
  	return rpcauth_cred_key_to_expire(auth, ctx->cred);
dc24826bf   Andy Adamson   NFS avoid expired...
1142
1143
1144
  }
  
  /*
5d47a3560   Trond Myklebust   NFS: Fix a potent...
1145
1146
1147
1148
   * 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...
1149
  static bool nfs_write_pageuptodate(struct page *page, struct inode *inode)
5d47a3560   Trond Myklebust   NFS: Fix a potent...
1150
  {
d529ef83c   Jeff Layton   NFS: fix the hand...
1151
  	struct nfs_inode *nfsi = NFS_I(inode);
8d197a568   Trond Myklebust   NFS: Always trust...
1152
1153
  	if (nfs_have_delegated_attributes(inode))
  		goto out;
18dd78c42   Scott Mayhew   nfs: Fix cache_va...
1154
  	if (nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE)
d529ef83c   Jeff Layton   NFS: fix the hand...
1155
  		return false;
4db72b40f   Jeff Layton   nfs: add memory b...
1156
  	smp_rmb();
d529ef83c   Jeff Layton   NFS: fix the hand...
1157
  	if (test_bit(NFS_INO_INVALIDATING, &nfsi->flags))
8d197a568   Trond Myklebust   NFS: Always trust...
1158
1159
  		return false;
  out:
18dd78c42   Scott Mayhew   nfs: Fix cache_va...
1160
1161
  	if (nfsi->cache_validity & NFS_INO_INVALID_DATA)
  		return false;
8d197a568   Trond Myklebust   NFS: Always trust...
1162
  	return PageUptodate(page) != 0;
5d47a3560   Trond Myklebust   NFS: Fix a potent...
1163
  }
5263e31e4   Jeff Layton   locks: move flock...
1164
1165
1166
1167
1168
1169
  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...
1170
1171
1172
1173
1174
  /* 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 ...
1175
1176
   * 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...
1177
1178
1179
   */
  static int nfs_can_extend_write(struct file *file, struct page *page, struct inode *inode)
  {
5263e31e4   Jeff Layton   locks: move flock...
1180
1181
1182
  	int ret;
  	struct file_lock_context *flctx = inode->i_flctx;
  	struct file_lock *fl;
c7559663e   Scott Mayhew   NFS: Allow nfs_up...
1183
1184
  	if (file->f_flags & O_DSYNC)
  		return 0;
263b4509e   Scott Mayhew   nfs: always make ...
1185
1186
  	if (!nfs_write_pageuptodate(page, inode))
  		return 0;
c7559663e   Scott Mayhew   NFS: Allow nfs_up...
1187
1188
  	if (NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE))
  		return 1;
bd61e0a9c   Jeff Layton   locks: convert po...
1189
1190
  	if (!flctx || (list_empty_careful(&flctx->flc_flock) &&
  		       list_empty_careful(&flctx->flc_posix)))
8fa4592a1   Trond Myklebust   NFS: Fix a write ...
1191
  		return 1;
5263e31e4   Jeff Layton   locks: move flock...
1192
1193
  
  	/* Check to see if there are whole file write locks */
5263e31e4   Jeff Layton   locks: move flock...
1194
  	ret = 0;
6109c8503   Jeff Layton   locks: add a dedi...
1195
  	spin_lock(&flctx->flc_lock);
bd61e0a9c   Jeff Layton   locks: convert po...
1196
1197
1198
1199
1200
1201
  	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...
1202
1203
1204
1205
1206
  		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...
1207
  	spin_unlock(&flctx->flc_lock);
5263e31e4   Jeff Layton   locks: move flock...
1208
  	return ret;
c7559663e   Scott Mayhew   NFS: Allow nfs_up...
1209
  }
5d47a3560   Trond Myklebust   NFS: Fix a potent...
1210
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1211
1212
1213
1214
1215
1216
1217
1218
   * 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...
1219
  	struct nfs_open_context *ctx = nfs_file_open_context(file);
d56b4ddf7   Mel Gorman   nfs: teach the NF...
1220
  	struct inode	*inode = page_file_mapping(page)->host;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1221
  	int		status = 0;
91d5b4702   Chuck Lever   NFS: add I/O perf...
1222
  	nfs_inc_stats(inode, NFSIOS_VFSUPDATEPAGE);
6de1472f1   Al Viro   nfs: use %p[dD] i...
1223
1224
1225
  	dprintk("NFS:       nfs_updatepage(%pD2 %d@%lld)
  ",
  		file, count, (long long)(page_file_offset(page) + offset));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1226

149a4fddd   Benjamin Coddington   nfs: don't create...
1227
1228
  	if (!count)
  		goto out;
c7559663e   Scott Mayhew   NFS: Allow nfs_up...
1229
  	if (nfs_can_extend_write(file, page, inode)) {
49a70f278   Trond Myklebust   NFS: Cleanup: add...
1230
  		count = max(count + offset, nfs_page_length(page));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1231
  		offset = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1232
  	}
e21195a74   Trond Myklebust   NFS: More cleanup...
1233
  	status = nfs_writepage_setup(ctx, page, offset, count);
03fa9e84e   Trond Myklebust   NFS: nfs_updatepa...
1234
1235
  	if (status < 0)
  		nfs_set_pageerror(page);
59b7c05ff   Trond Myklebust   Revert "NFS: Ensu...
1236
1237
  	else
  		__set_page_dirty_nobuffers(page);
149a4fddd   Benjamin Coddington   nfs: don't create...
1238
  out:
48186c7d5   Chuck Lever   NFS: Fix trace de...
1239
1240
  	dprintk("NFS:       nfs_updatepage returns %d (isize %lld)
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1241
  			status, (long long)i_size_read(inode));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1242
1243
  	return status;
  }
3ff7576dd   Trond Myklebust   SUNRPC: Clean up ...
1244
  static int flush_task_priority(int how)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1245
1246
1247
1248
1249
1250
1251
1252
1253
  {
  	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...
1254
1255
  static void nfs_initiate_write(struct nfs_pgio_header *hdr,
  			       struct rpc_message *msg,
abde71f4d   Tom Haynes   pnfs: Add nfs_rpc...
1256
  			       const struct nfs_rpc_ops *rpc_ops,
1ed26f330   Anna Schumaker   NFS: Create a com...
1257
  			       struct rpc_task_setup *task_setup_data, int how)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1258
  {
3ff7576dd   Trond Myklebust   SUNRPC: Clean up ...
1259
  	int priority = flush_task_priority(how);
d138d5d17   Andy Adamson   NFSv4.1: rearrang...
1260

1ed26f330   Anna Schumaker   NFS: Create a com...
1261
  	task_setup_data->priority = priority;
abde71f4d   Tom Haynes   pnfs: Add nfs_rpc...
1262
  	rpc_ops->write_setup(hdr, msg);
d138d5d17   Andy Adamson   NFSv4.1: rearrang...
1263

abde71f4d   Tom Haynes   pnfs: Add nfs_rpc...
1264
  	nfs4_state_protect_write(NFS_SERVER(hdr->inode)->nfs_client,
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1265
  				 &task_setup_data->rpc_client, msg, hdr);
275acaafd   Trond Myklebust   NFS: Clean up: sp...
1266
  }
6d884e8fc   Fred   nfs: nfs_redirty_...
1267
1268
1269
1270
1271
1272
1273
  /* 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)
  {
  	nfs_mark_request_dirty(req);
c70701131   Trond Myklebust   NFS: Ensure we se...
1274
  	set_bit(NFS_CONTEXT_RESEND_WRITES, &req->wb_context->flags);
1d1afcbc2   Trond Myklebust   NFS: Clean up - R...
1275
  	nfs_unlock_request(req);
20633f042   Weston Andros Adamson   nfs: page group s...
1276
  	nfs_end_page_writeback(req);
3aff4ebb9   Trond Myklebust   NFS: Prevent a de...
1277
  	nfs_release_request(req);
6d884e8fc   Fred   nfs: nfs_redirty_...
1278
  }
061ae2edb   Fred Isaman   NFS: create compl...
1279
  static void nfs_async_write_error(struct list_head *head)
6c75dc0d4   Fred Isaman   NFS: merge _full ...
1280
1281
1282
1283
1284
1285
1286
1287
1288
  {
  	struct nfs_page	*req;
  
  	while (!list_empty(head)) {
  		req = nfs_list_entry(head->next);
  		nfs_list_remove_request(req);
  		nfs_redirty_request(req);
  	}
  }
dc602dd70   Trond Myklebust   NFS/pNFS: Fix up ...
1289
1290
1291
1292
  static void nfs_async_write_reschedule_io(struct nfs_pgio_header *hdr)
  {
  	nfs_async_write_error(&hdr->pages);
  }
061ae2edb   Fred Isaman   NFS: create compl...
1293
1294
1295
  static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops = {
  	.error_cleanup = nfs_async_write_error,
  	.completion = nfs_write_completion,
dc602dd70   Trond Myklebust   NFS/pNFS: Fix up ...
1296
  	.reschedule_io = nfs_async_write_reschedule_io,
061ae2edb   Fred Isaman   NFS: create compl...
1297
  };
57208fa7e   Bryan Schumaker   NFS: Create an wr...
1298
  void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
a20c93e31   Christoph Hellwig   nfs: remove ->wri...
1299
  			       struct inode *inode, int ioflags, bool force_mds,
061ae2edb   Fred Isaman   NFS: create compl...
1300
  			       const struct nfs_pgio_completion_ops *compl_ops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1301
  {
a20c93e31   Christoph Hellwig   nfs: remove ->wri...
1302
  	struct nfs_server *server = NFS_SERVER(inode);
41d8d5b7a   Anna Schumaker   NFS: Create a com...
1303
  	const struct nfs_pageio_ops *pg_ops = &nfs_pgio_rw_ops;
a20c93e31   Christoph Hellwig   nfs: remove ->wri...
1304
1305
1306
1307
1308
  
  #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...
1309
1310
  	nfs_pageio_init(pgio, inode, pg_ops, compl_ops, &nfs_rw_write_ops,
  			server->wsize, ioflags);
1751c3638   Trond Myklebust   NFS: Cleanup of t...
1311
  }
ddda8e0aa   Bryan Schumaker   NFS: Convert v2 i...
1312
  EXPORT_SYMBOL_GPL(nfs_pageio_init_write);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1313

dce81290e   Trond Myklebust   NFS: Move the pnf...
1314
1315
  void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio)
  {
a7d42ddb3   Weston Andros Adamson   nfs: add mirrorin...
1316
  	struct nfs_pgio_mirror *mirror;
6f29b9bba   Kinglong Mee   NFS: Do cleanup b...
1317
1318
  	if (pgio->pg_ops && pgio->pg_ops->pg_cleanup)
  		pgio->pg_ops->pg_cleanup(pgio);
41d8d5b7a   Anna Schumaker   NFS: Create a com...
1319
  	pgio->pg_ops = &nfs_pgio_rw_ops;
a7d42ddb3   Weston Andros Adamson   nfs: add mirrorin...
1320
1321
1322
1323
1324
  
  	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...
1325
  }
1f9453578   Trond Myklebust   NFS: Clean up - s...
1326
  EXPORT_SYMBOL_GPL(nfs_pageio_reset_write_mds);
dce81290e   Trond Myklebust   NFS: Move the pnf...
1327

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1328

0b7c01533   Fred Isaman   NFS: add a struct...
1329
1330
1331
1332
1333
1334
  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...
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
  /*
   * 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...
1346

1f2edbe3f   Trond Myklebust   NFS: Don't ignore...
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
  	/*
  	 * 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...
1359

a08a8cd37   Trond Myklebust   NFS: Add attribut...
1360
1361
1362
1363
1364
  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...
1365
  	u64 size = argp->offset + resp->count;
a08a8cd37   Trond Myklebust   NFS: Add attribut...
1366
1367
  
  	if (!(fattr->valid & NFS_ATTR_FATTR_SIZE))
2b83d3de4   Trond Myklebust   NFSv4/pnfs: Ensur...
1368
1369
1370
  		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...
1371
  		return;
2b83d3de4   Trond Myklebust   NFSv4/pnfs: Ensur...
1372
1373
  	}
  	if (size != fattr->size)
a08a8cd37   Trond Myklebust   NFS: Add attribut...
1374
1375
1376
  		return;
  	/* Set attribute barrier */
  	nfs_fattr_set_barrier(fattr);
2b83d3de4   Trond Myklebust   NFSv4/pnfs: Ensur...
1377
1378
  	/* ...and update size */
  	fattr->valid |= NFS_ATTR_FATTR_SIZE;
a08a8cd37   Trond Myklebust   NFS: Add attribut...
1379
1380
1381
1382
  }
  
  void nfs_writeback_update_inode(struct nfs_pgio_header *hdr)
  {
2b83d3de4   Trond Myklebust   NFSv4/pnfs: Ensur...
1383
  	struct nfs_fattr *fattr = &hdr->fattr;
a08a8cd37   Trond Myklebust   NFS: Add attribut...
1384
  	struct inode *inode = hdr->inode;
a08a8cd37   Trond Myklebust   NFS: Add attribut...
1385
1386
1387
1388
1389
1390
  	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
1391
1392
1393
  /*
   * This function is called when the WRITE call is complete.
   */
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1394
1395
  static int nfs_writeback_done(struct rpc_task *task,
  			      struct nfs_pgio_header *hdr,
0eecb2145   Anna Schumaker   NFS: Create a com...
1396
  			      struct inode *inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1397
  {
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1398
  	int status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1399

f551e44ff   Chuck Lever   NFS: add comments...
1400
1401
1402
1403
1404
1405
1406
  	/*
  	 * ->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...
1407
  	status = NFS_PROTO(inode)->write_done(task, hdr);
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1408
  	if (status != 0)
0eecb2145   Anna Schumaker   NFS: Create a com...
1409
  		return status;
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1410
  	nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, hdr->res.count);
91d5b4702   Chuck Lever   NFS: add I/O perf...
1411

d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1412
1413
  	if (hdr->res.verf->committed < hdr->args.stable &&
  	    task->tk_status >= 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1414
1415
1416
1417
1418
1419
1420
1421
1422
  		/* 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...
1423
  		/* Note this will print the MDS for a DS write */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1424
  		if (time_before(complain, jiffies)) {
48186c7d5   Chuck Lever   NFS: Fix trace de...
1425
  			dprintk("NFS:       faulty NFS server %s:"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1426
1427
  				" (committed = %d) != (stable = %d)
  ",
cd841605f   Fred Isaman   NFS: create commo...
1428
  				NFS_SERVER(inode)->nfs_client->cl_hostname,
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1429
  				hdr->res.verf->committed, hdr->args.stable);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1430
1431
1432
  			complain = jiffies + 300 * HZ;
  		}
  	}
1f2edbe3f   Trond Myklebust   NFS: Don't ignore...
1433
1434
1435
1436
  
  	/* Deal with the suid/sgid bit corner case */
  	if (nfs_should_remove_suid(inode))
  		nfs_mark_for_revalidate(inode);
0eecb2145   Anna Schumaker   NFS: Create a com...
1437
1438
1439
1440
1441
1442
  	return 0;
  }
  
  /*
   * This function is called when the WRITE call is complete.
   */
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1443
1444
  static void nfs_writeback_result(struct rpc_task *task,
  				 struct nfs_pgio_header *hdr)
0eecb2145   Anna Schumaker   NFS: Create a com...
1445
  {
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1446
1447
  	struct nfs_pgio_args	*argp = &hdr->args;
  	struct nfs_pgio_res	*resp = &hdr->res;
1f2edbe3f   Trond Myklebust   NFS: Don't ignore...
1448
1449
  
  	if (resp->count < argp->count) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1450
  		static unsigned long    complain;
6c75dc0d4   Fred Isaman   NFS: merge _full ...
1451
  		/* This a short write! */
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1452
  		nfs_inc_stats(hdr->inode, NFSIOS_SHORTWRITE);
91d5b4702   Chuck Lever   NFS: add I/O perf...
1453

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1454
  		/* Has the server at least made some progress? */
6c75dc0d4   Fred Isaman   NFS: merge _full ...
1455
1456
1457
1458
1459
1460
1461
  		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
1462
  			}
d45f60c67   Weston Andros Adamson   nfs: merge nfs_pg...
1463
  			nfs_set_pgio_error(hdr, -EIO, argp->offset);
6c75dc0d4   Fred Isaman   NFS: merge _full ...
1464
  			task->tk_status = -EIO;
136028967   Fred Isaman   NFS: change nfs_w...
1465
  			return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1466
  		}
f8417b481   Kinglong Mee   NFSv4.1/pnfs: Ret...
1467
1468
1469
1470
1471
1472
  
  		/* For non rpc-based layout drivers, retry-through-MDS */
  		if (!task->tk_ops) {
  			hdr->pnfs_error = -EAGAIN;
  			return;
  		}
6c75dc0d4   Fred Isaman   NFS: merge _full ...
1473
1474
1475
  		/* 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...
1476
  			hdr->mds_offset += resp->count;
6c75dc0d4   Fred Isaman   NFS: merge _full ...
1477
1478
1479
1480
1481
1482
1483
1484
  			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
1485
  		}
6c75dc0d4   Fred Isaman   NFS: merge _full ...
1486
  		rpc_restart_call_prepare(task);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1487
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1488
  }
af7cf0579   Trond Myklebust   NFS: Allow multip...
1489
  static int wait_on_commit(struct nfs_mds_commit_info *cinfo)
71d0a6112   Trond Myklebust   NFS: Fix an unsta...
1490
  {
af7cf0579   Trond Myklebust   NFS: Allow multip...
1491
1492
1493
  	return wait_on_atomic_t(&cinfo->rpcs_out,
  			nfs_wait_atomic_killable, TASK_KILLABLE);
  }
b8413f98f   Trond Myklebust   NFS: Fix a hang/i...
1494

af7cf0579   Trond Myklebust   NFS: Allow multip...
1495
1496
1497
  static void nfs_commit_begin(struct nfs_mds_commit_info *cinfo)
  {
  	atomic_inc(&cinfo->rpcs_out);
71d0a6112   Trond Myklebust   NFS: Fix an unsta...
1498
  }
af7cf0579   Trond Myklebust   NFS: Allow multip...
1499
  static void nfs_commit_end(struct nfs_mds_commit_info *cinfo)
71d0a6112   Trond Myklebust   NFS: Fix an unsta...
1500
  {
af7cf0579   Trond Myklebust   NFS: Allow multip...
1501
1502
  	if (atomic_dec_and_test(&cinfo->rpcs_out))
  		wake_up_atomic_t(&cinfo->rpcs_out);
71d0a6112   Trond Myklebust   NFS: Fix an unsta...
1503
  }
0b7c01533   Fred Isaman   NFS: add a struct...
1504
  void nfs_commitdata_release(struct nfs_commit_data *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1505
  {
0b7c01533   Fred Isaman   NFS: add a struct...
1506
1507
  	put_nfs_open_context(data->context);
  	nfs_commit_free(data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1508
  }
e0c2b3801   Fred Isaman   NFSv4.1: filelayo...
1509
  EXPORT_SYMBOL_GPL(nfs_commitdata_release);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1510

0b7c01533   Fred Isaman   NFS: add a struct...
1511
  int nfs_initiate_commit(struct rpc_clnt *clnt, struct nfs_commit_data *data,
c36aae9ad   Peng Tao   nfs: allow differ...
1512
  			const struct nfs_rpc_ops *nfs_ops,
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1513
  			const struct rpc_call_ops *call_ops,
9f0ec176b   Andy Adamson   NFSv4.1 set RPC_T...
1514
  			int how, int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1515
  {
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
1516
  	struct rpc_task *task;
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1517
  	int priority = flush_task_priority(how);
bdc7f021f   Trond Myklebust   NFS: Clean up the...
1518
1519
1520
  	struct rpc_message msg = {
  		.rpc_argp = &data->args,
  		.rpc_resp = &data->res,
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1521
  		.rpc_cred = data->cred,
bdc7f021f   Trond Myklebust   NFS: Clean up the...
1522
  	};
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
1523
  	struct rpc_task_setup task_setup_data = {
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
1524
  		.task = &data->task,
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1525
  		.rpc_client = clnt,
bdc7f021f   Trond Myklebust   NFS: Clean up the...
1526
  		.rpc_message = &msg,
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1527
  		.callback_ops = call_ops,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
1528
  		.callback_data = data,
101070ca2   Trond Myklebust   NFS: Ensure that ...
1529
  		.workqueue = nfsiod_workqueue,
9f0ec176b   Andy Adamson   NFSv4.1 set RPC_T...
1530
  		.flags = RPC_TASK_ASYNC | flags,
3ff7576dd   Trond Myklebust   SUNRPC: Clean up ...
1531
  		.priority = priority,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
1532
  	};
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1533
  	/* Set up the initial task struct.  */
c36aae9ad   Peng Tao   nfs: allow differ...
1534
  	nfs_ops->commit_setup(data, &msg);
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1535

b4839ebe2   Kinglong Mee   nfs: Remove inval...
1536
1537
  	dprintk("NFS: initiated commit call
  ");
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1538

8c21c62c4   Weston Andros Adamson   nfs4.1: Add SP4_M...
1539
1540
  	nfs4_state_protect(NFS_SERVER(data->inode)->nfs_client,
  		NFS_SP4_MACH_CRED_COMMIT, &task_setup_data.rpc_client, &msg);
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1541
1542
1543
1544
1545
1546
1547
1548
  	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...
1549
  EXPORT_SYMBOL_GPL(nfs_initiate_commit);
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1550

378520b83   Peng Tao   nfs41: add a help...
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
  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...
1562
1563
1564
  /*
   * Set up the argument/result storage required for the RPC call.
   */
0b7c01533   Fred Isaman   NFS: add a struct...
1565
  void nfs_init_commit(struct nfs_commit_data *data,
f453a54a0   Fred Isaman   NFS: create nfs_c...
1566
1567
1568
  		     struct list_head *head,
  		     struct pnfs_layout_segment *lseg,
  		     struct nfs_commit_info *cinfo)
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1569
1570
  {
  	struct nfs_page *first = nfs_list_entry(head->next);
2b0143b5c   David Howells   VFS: normal files...
1571
  	struct inode *inode = d_inode(first->wb_context->dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1572
1573
1574
1575
1576
  
  	/* Set up the RPC argument and reply structs
  	 * NB: take care not to mess about with data->commit et al. */
  
  	list_splice_init(head, &data->pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1577

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1578
  	data->inode	  = inode;
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1579
  	data->cred	  = first->wb_context->cred;
988b6dceb   Fred Isaman   NFSv4.1: remove G...
1580
  	data->lseg	  = lseg; /* reference transferred */
378520b83   Peng Tao   nfs41: add a help...
1581
1582
1583
  	/* only set lwb for pnfs commit */
  	if (lseg)
  		data->lwb = nfs_get_lwb(&data->pages);
9ace33cdc   Fred Isaman   NFSv4.1: rearrang...
1584
  	data->mds_ops     = &nfs_commit_ops;
f453a54a0   Fred Isaman   NFS: create nfs_c...
1585
  	data->completion_ops = cinfo->completion_ops;
b359f9d09   Fred Isaman   NFS: add dreq to ...
1586
  	data->dreq	  = cinfo->dreq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1587
1588
  
  	data->args.fh     = NFS_FH(data->inode);
3da28eb1c   Trond Myklebust   [PATCH] NFS: Repl...
1589
1590
1591
  	/* Note: we always request a commit of the entire inode */
  	data->args.offset = 0;
  	data->args.count  = 0;
0b7c01533   Fred Isaman   NFS: add a struct...
1592
  	data->context     = get_nfs_open_context(first->wb_context);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1593
1594
  	data->res.fattr   = &data->fattr;
  	data->res.verf    = &data->verf;
0e574af1b   Trond Myklebust   NFS: Cleanup init...
1595
  	nfs_fattr_init(&data->fattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1596
  }
e0c2b3801   Fred Isaman   NFSv4.1: filelayo...
1597
  EXPORT_SYMBOL_GPL(nfs_init_commit);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1598

e0c2b3801   Fred Isaman   NFSv4.1: filelayo...
1599
  void nfs_retry_commit(struct list_head *page_list,
ea2cf2282   Fred Isaman   NFS: create struc...
1600
  		      struct pnfs_layout_segment *lseg,
b57ff1303   Weston Andros Adamson   pnfs: pass ds_com...
1601
1602
  		      struct nfs_commit_info *cinfo,
  		      u32 ds_commit_idx)
64bfeb49b   Fred Isaman   NFSv4.1: pull err...
1603
1604
1605
1606
1607
1608
  {
  	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...
1609
  		nfs_mark_request_commit(req, lseg, cinfo, ds_commit_idx);
487b9b8af   Tom Haynes   nfs: Can call nfs...
1610
1611
  		if (!cinfo->dreq)
  			nfs_clear_page_commit(req->wb_page);
1d1afcbc2   Trond Myklebust   NFS: Clean up - R...
1612
  		nfs_unlock_and_release_request(req);
64bfeb49b   Fred Isaman   NFSv4.1: pull err...
1613
1614
  	}
  }
e0c2b3801   Fred Isaman   NFSv4.1: filelayo...
1615
  EXPORT_SYMBOL_GPL(nfs_retry_commit);
64bfeb49b   Fred Isaman   NFSv4.1: pull err...
1616

b20135d0b   Trond Myklebust   NFSv4.1/pNFS: Don...
1617
1618
1619
1620
1621
1622
  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
1623
1624
1625
1626
  /*
   * Commit dirty pages
   */
  static int
ea2cf2282   Fred Isaman   NFS: create struc...
1627
1628
  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
1629
  {
0b7c01533   Fred Isaman   NFS: add a struct...
1630
  	struct nfs_commit_data	*data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1631

ade8febde   Weston Andros Adamson   nfs: avoid race t...
1632
1633
1634
  	/* another commit raced with us */
  	if (list_empty(head))
  		return 0;
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1635
  	data = nfs_commitdata_alloc();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1636
1637
1638
1639
1640
  
  	if (!data)
  		goto out_bad;
  
  	/* Set up the argument struct */
f453a54a0   Fred Isaman   NFS: create nfs_c...
1641
1642
  	nfs_init_commit(data, head, NULL, cinfo);
  	atomic_inc(&cinfo->mds->rpcs_out);
c36aae9ad   Peng Tao   nfs: allow differ...
1643
1644
  	return nfs_initiate_commit(NFS_CLIENT(inode), data, NFS_PROTO(inode),
  				   data->mds_ops, how, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1645
   out_bad:
b57ff1303   Weston Andros Adamson   pnfs: pass ds_com...
1646
  	nfs_retry_commit(head, NULL, cinfo, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1647
1648
  	return -ENOMEM;
  }
67911c8f1   Anna Schumaker   NFS: Add nfs_comm...
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
  int nfs_commit_file(struct file *file, struct nfs_write_verifier *verf)
  {
  	struct inode *inode = file_inode(file);
  	struct nfs_open_context *open;
  	struct nfs_commit_info cinfo;
  	struct nfs_page *req;
  	int ret;
  
  	open = get_nfs_open_context(nfs_file_open_context(file));
  	req  = nfs_create_request(open, NULL, NULL, 0, i_size_read(inode));
2997bfd04   Dan Carpenter   NFS: checking for...
1659
1660
  	if (IS_ERR(req)) {
  		ret = PTR_ERR(req);
67911c8f1   Anna Schumaker   NFS: Add nfs_comm...
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
  		goto out_put;
  	}
  
  	nfs_init_cinfo_from_inode(&cinfo, inode);
  
  	memcpy(&req->wb_verf, verf, sizeof(struct nfs_write_verifier));
  	nfs_request_add_commit_list(req, &cinfo);
  	ret = nfs_commit_inode(inode, FLUSH_SYNC);
  	if (ret > 0)
  		ret = 0;
  
  	nfs_free_request(req);
  out_put:
  	put_nfs_open_context(open);
  	return ret;
  }
  EXPORT_SYMBOL_GPL(nfs_commit_file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1678
1679
1680
  /*
   * COMMIT call returned
   */
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1681
  static void nfs_commit_done(struct rpc_task *task, void *calldata)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1682
  {
0b7c01533   Fred Isaman   NFS: add a struct...
1683
  	struct nfs_commit_data	*data = calldata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1684

a3f565b1e   Chuck Lever   NFS: fix print fo...
1685
1686
          dprintk("NFS: %5u nfs_commit_done (status %d)
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1687
                                  task->tk_pid, task->tk_status);
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1688
  	/* Call the NFS version-specific code */
c0d0e96b8   Trond Myklebust   NFS: Get rid of p...
1689
  	NFS_PROTO(data->inode)->commit_done(task, data);
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1690
  }
f453a54a0   Fred Isaman   NFS: create nfs_c...
1691
  static void nfs_commit_release_pages(struct nfs_commit_data *data)
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1692
  {
5917ce844   Fred Isaman   NFSv4.1: pull out...
1693
  	struct nfs_page	*req;
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1694
  	int status = data->task.tk_status;
f453a54a0   Fred Isaman   NFS: create nfs_c...
1695
  	struct nfs_commit_info cinfo;
353db7966   NeilBrown   NFS: avoid waitin...
1696
  	struct nfs_server *nfss;
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1697

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1698
1699
1700
  	while (!list_empty(&data->pages)) {
  		req = nfs_list_entry(data->pages.next);
  		nfs_list_remove_request(req);
67911c8f1   Anna Schumaker   NFS: Add nfs_comm...
1701
1702
  		if (req->wb_page)
  			nfs_clear_page_commit(req->wb_page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1703

1e8968c5b   Niels de Vos   NFS: dprintk() sh...
1704
  		dprintk("NFS:       commit (%s/%llu %d@%lld)",
3d4ff43d8   Al Viro   nfs_open_context ...
1705
  			req->wb_context->dentry->d_sb->s_id,
2b0143b5c   David Howells   VFS: normal files...
1706
  			(unsigned long long)NFS_FILEID(d_inode(req->wb_context->dentry)),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1707
1708
  			req->wb_bytes,
  			(long long)req_offset(req));
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1709
1710
  		if (status < 0) {
  			nfs_context_set_write_error(req->wb_context, status);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1711
  			nfs_inode_remove_request(req);
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1712
1713
  			dprintk(", error = %d
  ", status);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1714
1715
1716
1717
1718
  			goto next;
  		}
  
  		/* Okay, COMMIT succeeded, apparently. Check the verifier
  		 * returned by the server against all stored verfs. */
8fc3c3862   Trond Myklebust   NFS: Fix O_DIRECT...
1719
  		if (!nfs_write_verifier_cmp(&req->wb_verf, &data->verf.verifier)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1720
1721
1722
1723
1724
1725
1726
1727
1728
  			/* We have a match */
  			nfs_inode_remove_request(req);
  			dprintk(" OK
  ");
  			goto next;
  		}
  		/* We have a mismatch. Write the page again */
  		dprintk(" mismatch
  ");
6d884e8fc   Fred   nfs: nfs_redirty_...
1729
  		nfs_mark_request_dirty(req);
05990d1bf   Trond Myklebust   NFS: Fix fdatasyn...
1730
  		set_bit(NFS_CONTEXT_RESEND_WRITES, &req->wb_context->flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1731
  	next:
1d1afcbc2   Trond Myklebust   NFS: Clean up - R...
1732
  		nfs_unlock_and_release_request(req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1733
  	}
353db7966   NeilBrown   NFS: avoid waitin...
1734
1735
1736
  	nfss = NFS_SERVER(data->inode);
  	if (atomic_long_read(&nfss->writeback) < NFS_CONGESTION_OFF_THRESH)
  		clear_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC);
f453a54a0   Fred Isaman   NFS: create nfs_c...
1737
  	nfs_init_cinfo(&cinfo, data->inode, data->dreq);
af7cf0579   Trond Myklebust   NFS: Allow multip...
1738
  	nfs_commit_end(cinfo.mds);
5917ce844   Fred Isaman   NFSv4.1: pull out...
1739
1740
1741
1742
  }
  
  static void nfs_commit_release(void *calldata)
  {
0b7c01533   Fred Isaman   NFS: add a struct...
1743
  	struct nfs_commit_data *data = calldata;
5917ce844   Fred Isaman   NFSv4.1: pull out...
1744

f453a54a0   Fred Isaman   NFS: create nfs_c...
1745
  	data->completion_ops->completion(data);
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1746
  	nfs_commitdata_release(calldata);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1747
  }
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1748
1749
  
  static const struct rpc_call_ops nfs_commit_ops = {
0b7c01533   Fred Isaman   NFS: add a struct...
1750
  	.rpc_call_prepare = nfs_commit_prepare,
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1751
1752
1753
  	.rpc_call_done = nfs_commit_done,
  	.rpc_release = nfs_commit_release,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1754

f453a54a0   Fred Isaman   NFS: create nfs_c...
1755
1756
  static const struct nfs_commit_completion_ops nfs_commit_completion_ops = {
  	.completion = nfs_commit_release_pages,
b20135d0b   Trond Myklebust   NFSv4.1/pNFS: Don...
1757
  	.resched_write = nfs_commit_resched_write,
f453a54a0   Fred Isaman   NFS: create nfs_c...
1758
  };
1763da123   Fred Isaman   NFS: rewrite dire...
1759
1760
  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...
1761
1762
  {
  	int status;
ea2cf2282   Fred Isaman   NFS: create struc...
1763
  	status = pnfs_commit_list(inode, head, how, cinfo);
84c53ab5c   Fred Isaman   NFS: create nfs_g...
1764
  	if (status == PNFS_NOT_ATTEMPTED)
ea2cf2282   Fred Isaman   NFS: create struc...
1765
  		status = nfs_commit_list(inode, head, how, cinfo);
84c53ab5c   Fred Isaman   NFS: create nfs_g...
1766
1767
  	return status;
  }
b608b283a   Trond Myklebust   NFS: kswapd must ...
1768
  int nfs_commit_inode(struct inode *inode, int how)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1769
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1770
  	LIST_HEAD(head);
ea2cf2282   Fred Isaman   NFS: create struc...
1771
  	struct nfs_commit_info cinfo;
71d0a6112   Trond Myklebust   NFS: Fix an unsta...
1772
  	int may_wait = how & FLUSH_SYNC;
af7cf0579   Trond Myklebust   NFS: Allow multip...
1773
  	int error = 0;
b8413f98f   Trond Myklebust   NFS: Fix a hang/i...
1774
  	int res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1775

ea2cf2282   Fred Isaman   NFS: create struc...
1776
  	nfs_init_cinfo_from_inode(&cinfo, inode);
af7cf0579   Trond Myklebust   NFS: Allow multip...
1777
  	nfs_commit_begin(cinfo.mds);
ea2cf2282   Fred Isaman   NFS: create struc...
1778
  	res = nfs_scan_commit(inode, &head, &cinfo);
af7cf0579   Trond Myklebust   NFS: Allow multip...
1779
  	if (res)
ea2cf2282   Fred Isaman   NFS: create struc...
1780
  		error = nfs_generic_commit_list(inode, &head, how, &cinfo);
af7cf0579   Trond Myklebust   NFS: Allow multip...
1781
1782
1783
1784
1785
1786
1787
1788
  	nfs_commit_end(cinfo.mds);
  	if (error < 0)
  		goto out_error;
  	if (!may_wait)
  		goto out_mark_dirty;
  	error = wait_on_commit(cinfo.mds);
  	if (error < 0)
  		return error;
c5efa5fc9   Trond Myklebust   NFS: Ensure that ...
1789
  	return res;
af7cf0579   Trond Myklebust   NFS: Allow multip...
1790
1791
  out_error:
  	res = error;
c5efa5fc9   Trond Myklebust   NFS: Ensure that ...
1792
1793
1794
1795
1796
1797
1798
  	/* Note: If we exit without ensuring that the commit is complete,
  	 * we must mark the inode as dirty. Otherwise, future calls to
  	 * sync_inode() with the WB_SYNC_ALL flag set will fail to ensure
  	 * that the data is on the disk.
  	 */
  out_mark_dirty:
  	__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1799
1800
  	return res;
  }
b20135d0b   Trond Myklebust   NFSv4.1/pNFS: Don...
1801
  EXPORT_SYMBOL_GPL(nfs_commit_inode);
8fc795f70   Trond Myklebust   NFS: Cleanup - mo...
1802

ae09c31f6   Anna Schumaker   NFS: Rename nfs_c...
1803
  int nfs_write_inode(struct inode *inode, struct writeback_control *wbc)
8fc795f70   Trond Myklebust   NFS: Cleanup - mo...
1804
  {
420e3646b   Trond Myklebust   NFS: Reduce the n...
1805
1806
1807
  	struct nfs_inode *nfsi = NFS_I(inode);
  	int flags = FLUSH_SYNC;
  	int ret = 0;
8fc795f70   Trond Myklebust   NFS: Cleanup - mo...
1808

3236c3e1a   Jeff Layton   nfs: don't redirt...
1809
  	/* no commits means nothing needs to be done */
ea2cf2282   Fred Isaman   NFS: create struc...
1810
  	if (!nfsi->commit_info.ncommit)
3236c3e1a   Jeff Layton   nfs: don't redirt...
1811
  		return ret;
a00dd6c03   Jeff Layton   NFS: don't use FL...
1812
1813
1814
1815
  	if (wbc->sync_mode == WB_SYNC_NONE) {
  		/* Don't commit yet if this is a non-blocking flush and there
  		 * are a lot of outstanding writes for this mapping.
  		 */
cb1410c71   Weston Andros Adamson   NFS: fix subtle c...
1816
  		if (nfsi->commit_info.ncommit <= (nfsi->nrequests >> 1))
a00dd6c03   Jeff Layton   NFS: don't use FL...
1817
  			goto out_mark_dirty;
420e3646b   Trond Myklebust   NFS: Reduce the n...
1818

a00dd6c03   Jeff Layton   NFS: don't use FL...
1819
  		/* don't wait for the COMMIT response */
420e3646b   Trond Myklebust   NFS: Reduce the n...
1820
  		flags = 0;
a00dd6c03   Jeff Layton   NFS: don't use FL...
1821
  	}
420e3646b   Trond Myklebust   NFS: Reduce the n...
1822
1823
1824
1825
1826
1827
1828
1829
  	ret = nfs_commit_inode(inode, flags);
  	if (ret >= 0) {
  		if (wbc->sync_mode == WB_SYNC_NONE) {
  			if (ret < wbc->nr_to_write)
  				wbc->nr_to_write -= ret;
  			else
  				wbc->nr_to_write = 0;
  		}
8fc795f70   Trond Myklebust   NFS: Cleanup - mo...
1830
  		return 0;
420e3646b   Trond Myklebust   NFS: Reduce the n...
1831
1832
  	}
  out_mark_dirty:
8fc795f70   Trond Myklebust   NFS: Cleanup - mo...
1833
1834
1835
  	__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
  	return ret;
  }
89d77c8fa   Bryan Schumaker   NFS: Convert v4 i...
1836
  EXPORT_SYMBOL_GPL(nfs_write_inode);
a8d8f02cf   Bryan Schumaker   NFS: Create custo...
1837

acdc53b21   Trond Myklebust   NFS: Replace __nf...
1838
  /*
837bb1d75   Trond Myklebust   NFSv4.2: Fix writ...
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
   * 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...
1857
1858
1859
   * flush the inode to disk.
   */
  int nfs_wb_all(struct inode *inode)
34901f70d   Trond Myklebust   NFS: Writeback op...
1860
  {
f4ce1299b   Trond Myklebust   NFS: Add event tr...
1861
1862
1863
  	int ret;
  
  	trace_nfs_writeback_inode_enter(inode);
5bb89b470   Trond Myklebust   NFSv4.1/pnfs: Sep...
1864
  	ret = filemap_write_and_wait(inode->i_mapping);
6b1968756   Chuck Lever   nfs: stat(2) fail...
1865
1866
1867
1868
1869
1870
1871
  	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...
1872

6b1968756   Chuck Lever   nfs: stat(2) fail...
1873
  out:
f4ce1299b   Trond Myklebust   NFS: Add event tr...
1874
1875
  	trace_nfs_writeback_inode_exit(inode, ret);
  	return ret;
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1876
  }
ddda8e0aa   Bryan Schumaker   NFS: Convert v2 i...
1877
  EXPORT_SYMBOL_GPL(nfs_wb_all);
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1878

1b3b4a1a2   Trond Myklebust   NFS: Fix a write ...
1879
1880
1881
  int nfs_wb_page_cancel(struct inode *inode, struct page *page)
  {
  	struct nfs_page *req;
1b3b4a1a2   Trond Myklebust   NFS: Fix a write ...
1882
  	int ret = 0;
3e2170451   Weston Andros Adamson   nfs: handle multi...
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
  	wait_on_page_writeback(page);
  
  	/* blocking call to cancel all requests and join to a single (head)
  	 * request */
  	req = nfs_lock_and_join_requests(page, false);
  
  	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...
1897
  		nfs_unlock_and_release_request(req);
1b3b4a1a2   Trond Myklebust   NFS: Fix a write ...
1898
  	}
3e2170451   Weston Andros Adamson   nfs: handle multi...
1899

1b3b4a1a2   Trond Myklebust   NFS: Fix a write ...
1900
1901
  	return ret;
  }
7f2f12d96   Trond Myklebust   NFS: Simplify nfs...
1902
1903
1904
  /*
   * Write back all requests on one page - we do this before reading it.
   */
d6c843b96   Peng Tao   nfs: only remove ...
1905
  int nfs_wb_single_page(struct inode *inode, struct page *page, bool launder)
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1906
  {
29418aa4b   Mel Gorman   nfs: disable data...
1907
  	loff_t range_start = page_file_offset(page);
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
1908
  	loff_t range_end = range_start + (loff_t)(PAGE_SIZE - 1);
4d770ccf4   Trond Myklebust   NFS: Ensure that ...
1909
  	struct writeback_control wbc = {
4d770ccf4   Trond Myklebust   NFS: Ensure that ...
1910
  		.sync_mode = WB_SYNC_ALL,
7f2f12d96   Trond Myklebust   NFS: Simplify nfs...
1911
  		.nr_to_write = 0,
4d770ccf4   Trond Myklebust   NFS: Ensure that ...
1912
1913
1914
1915
  		.range_start = range_start,
  		.range_end = range_end,
  	};
  	int ret;
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1916

f4ce1299b   Trond Myklebust   NFS: Add event tr...
1917
  	trace_nfs_writeback_page_enter(inode);
0522f6ade   Trond Myklebust   NFS: Fix another ...
1918
  	for (;;) {
ba8b06e67   Trond Myklebust   NFS: Ensure that ...
1919
  		wait_on_page_writeback(page);
73e3302f6   Trond Myklebust   NFS: Fix nfs_wb_p...
1920
  		if (clear_page_dirty_for_io(page)) {
d6c843b96   Peng Tao   nfs: only remove ...
1921
  			ret = nfs_writepage_locked(page, &wbc, launder);
73e3302f6   Trond Myklebust   NFS: Fix nfs_wb_p...
1922
1923
  			if (ret < 0)
  				goto out_error;
0522f6ade   Trond Myklebust   NFS: Fix another ...
1924
  			continue;
7f2f12d96   Trond Myklebust   NFS: Simplify nfs...
1925
  		}
f4ce1299b   Trond Myklebust   NFS: Add event tr...
1926
  		ret = 0;
0522f6ade   Trond Myklebust   NFS: Fix another ...
1927
1928
1929
  		if (!PagePrivate(page))
  			break;
  		ret = nfs_commit_inode(inode, FLUSH_SYNC);
ba8b06e67   Trond Myklebust   NFS: Ensure that ...
1930
  		if (ret < 0)
73e3302f6   Trond Myklebust   NFS: Fix nfs_wb_p...
1931
  			goto out_error;
7f2f12d96   Trond Myklebust   NFS: Simplify nfs...
1932
  	}
73e3302f6   Trond Myklebust   NFS: Fix nfs_wb_p...
1933
  out_error:
f4ce1299b   Trond Myklebust   NFS: Add event tr...
1934
  	trace_nfs_writeback_page_exit(inode, ret);
4d770ccf4   Trond Myklebust   NFS: Ensure that ...
1935
  	return ret;
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1936
  }
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
1937
1938
  #ifdef CONFIG_MIGRATION
  int nfs_migrate_page(struct address_space *mapping, struct page *newpage,
a6bc32b89   Mel Gorman   mm: compaction: i...
1939
  		struct page *page, enum migrate_mode mode)
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
1940
  {
2da956523   Jeff Layton   nfs: don't try to...
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
  	/*
  	 * 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...
1951

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

a6bc32b89   Mel Gorman   mm: compaction: i...
1955
  	return migrate_page(mapping, newpage, page, mode);
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
1956
1957
  }
  #endif
f7b422b17   David Howells   NFS: Split fs/nfs...
1958
  int __init nfs_init_writepagecache(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1959
1960
  {
  	nfs_wdata_cachep = kmem_cache_create("nfs_write_data",
1e7f3a485   Weston Andros Adamson   nfs: move nfs_pgi...
1961
  					     sizeof(struct nfs_pgio_header),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1962
  					     0, SLAB_HWCACHE_ALIGN,
20c2df83d   Paul Mundt   mm: Remove slab d...
1963
  					     NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1964
1965
  	if (nfs_wdata_cachep == NULL)
  		return -ENOMEM;
93d2341c7   Matthew Dobson   [PATCH] mempool: ...
1966
1967
  	nfs_wdata_mempool = mempool_create_slab_pool(MIN_POOL_WRITE,
  						     nfs_wdata_cachep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1968
  	if (nfs_wdata_mempool == NULL)
3dd4765fc   Jeff Layton   nfs: tear down ca...
1969
  		goto out_destroy_write_cache;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1970

0b7c01533   Fred Isaman   NFS: add a struct...
1971
1972
1973
1974
1975
  	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...
1976
  		goto out_destroy_write_mempool;
0b7c01533   Fred Isaman   NFS: add a struct...
1977

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

89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
  	/*
  	 * 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
  	 */
  	nfs_congestion_kb = (16*int_sqrt(totalram_pages)) << (PAGE_SHIFT-10);
  	if (nfs_congestion_kb > 256*1024)
  		nfs_congestion_kb = 256*1024;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2002
  	return 0;
3dd4765fc   Jeff Layton   nfs: tear down ca...
2003
2004
2005
2006
2007
2008
2009
2010
  
  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
2011
  }
266bee886   David Brownell   [PATCH] fix stati...
2012
  void nfs_destroy_writepagecache(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2013
2014
  {
  	mempool_destroy(nfs_commit_mempool);
3dd4765fc   Jeff Layton   nfs: tear down ca...
2015
  	kmem_cache_destroy(nfs_cdata_cachep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2016
  	mempool_destroy(nfs_wdata_mempool);
1a1d92c10   Alexey Dobriyan   [PATCH] Really ig...
2017
  	kmem_cache_destroy(nfs_wdata_cachep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2018
  }
4a0de55c5   Anna Schumaker   NFS: Create a com...
2019
  static const struct nfs_rw_ops nfs_rw_write_ops = {
a4cdda591   Anna Schumaker   NFS: Create a com...
2020
  	.rw_mode		= FMODE_WRITE,
4a0de55c5   Anna Schumaker   NFS: Create a com...
2021
2022
  	.rw_alloc_header	= nfs_writehdr_alloc,
  	.rw_free_header		= nfs_writehdr_free,
0eecb2145   Anna Schumaker   NFS: Create a com...
2023
2024
  	.rw_done		= nfs_writeback_done,
  	.rw_result		= nfs_writeback_result,
1ed26f330   Anna Schumaker   NFS: Create a com...
2025
  	.rw_initiate		= nfs_initiate_write,
4a0de55c5   Anna Schumaker   NFS: Create a com...
2026
  };