Blame view

fs/nfs/write.c 41.6 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>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
  #include <asm/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
  
  #include "delegation.h"
49a70f278   Trond Myklebust   NFS: Cleanup: add...
25
  #include "internal.h"
91d5b4702   Chuck Lever   NFS: add I/O perf...
26
  #include "iostat.h"
def6ed7ef   Andy Adamson   nfs41 write seque...
27
  #include "nfs4_fs.h"
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
28
  #include "fscache.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
31
32
33
34
35
36
37
  
  #define NFSDBG_FACILITY		NFSDBG_PAGECACHE
  
  #define MIN_POOL_WRITE		(32)
  #define MIN_POOL_COMMIT		(4)
  
  /*
   * Local function declarations
   */
c63c7b051   Trond Myklebust   NFS: Fix a race w...
38
39
  static void nfs_pageio_init_write(struct nfs_pageio_descriptor *desc,
  				  struct inode *inode, int ioflags);
f8512ad0d   Fred Isaman   nfs: don't ignore...
40
  static void nfs_redirty_request(struct nfs_page *req);
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
41
42
43
  static const struct rpc_call_ops nfs_write_partial_ops;
  static const struct rpc_call_ops nfs_write_full_ops;
  static const struct rpc_call_ops nfs_commit_ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44

e18b890bb   Christoph Lameter   [PATCH] slab: rem...
45
  static struct kmem_cache *nfs_wdata_cachep;
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
46
  static mempool_t *nfs_wdata_mempool;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
  static mempool_t *nfs_commit_mempool;
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
48
  struct nfs_write_data *nfs_commitdata_alloc(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
  {
e6b4f8da3   Christoph Lameter   [PATCH] slab: rem...
50
  	struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOFS);
40859d7ee   Chuck Lever   NFS: support larg...
51

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
54
  	if (p) {
  		memset(p, 0, sizeof(*p));
  		INIT_LIST_HEAD(&p->pages);
5f7dbd5c7   Andy Adamson   nfs41: set up seq...
55
  		p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
  	}
  	return p;
  }
5e4424af9   Trond Myklebust   SUNRPC: Remove no...
59
  void nfs_commit_free(struct nfs_write_data *p)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
  {
40859d7ee   Chuck Lever   NFS: support larg...
61
62
  	if (p && (p->pagevec != &p->page_array[0]))
  		kfree(p->pagevec);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
  	mempool_free(p, nfs_commit_mempool);
  }
8d5658c94   Trond Myklebust   NFS: Fix a buffer...
65
  struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount)
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
66
  {
e6b4f8da3   Christoph Lameter   [PATCH] slab: rem...
67
  	struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS);
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
68
69
70
71
  
  	if (p) {
  		memset(p, 0, sizeof(*p));
  		INIT_LIST_HEAD(&p->pages);
e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
72
  		p->npages = pagecount;
5f7dbd5c7   Andy Adamson   nfs41: set up seq...
73
  		p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
0d0b5cb36   Chuck Lever   NFS: Optimize all...
74
75
  		if (pagecount <= ARRAY_SIZE(p->page_array))
  			p->pagevec = p->page_array;
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
76
  		else {
0d0b5cb36   Chuck Lever   NFS: Optimize all...
77
78
  			p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_NOFS);
  			if (!p->pagevec) {
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
79
80
81
82
83
84
85
  				mempool_free(p, nfs_wdata_mempool);
  				p = NULL;
  			}
  		}
  	}
  	return p;
  }
1ae88b2e4   Trond Myklebust   NFS: Fix an O_DIR...
86
  void nfs_writedata_free(struct nfs_write_data *p)
3feb2d493   Trond Myklebust   NFS: Uninline nfs...
87
88
89
90
91
  {
  	if (p && (p->pagevec != &p->page_array[0]))
  		kfree(p->pagevec);
  	mempool_free(p, nfs_wdata_mempool);
  }
1ae88b2e4   Trond Myklebust   NFS: Fix an O_DIR...
92
  static void nfs_writedata_release(struct nfs_write_data *wdata)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
  {
383ba7193   Trond Myklebust   NFS: Fix a deadlo...
94
  	put_nfs_open_context(wdata->args.context);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
96
  	nfs_writedata_free(wdata);
  }
7b159fc18   Trond Myklebust   NFS: Fall back to...
97
98
99
100
101
102
  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);
  }
277459d2e   Trond Myklebust   NFS: Store pointe...
103
104
105
106
107
108
109
  static struct nfs_page *nfs_page_find_request_locked(struct page *page)
  {
  	struct nfs_page *req = NULL;
  
  	if (PagePrivate(page)) {
  		req = (struct nfs_page *)page_private(page);
  		if (req != NULL)
c03b40246   Trond Myklebust   NFS: Convert stru...
110
  			kref_get(&req->wb_kref);
277459d2e   Trond Myklebust   NFS: Store pointe...
111
112
113
114
115
116
  	}
  	return req;
  }
  
  static struct nfs_page *nfs_page_find_request(struct page *page)
  {
587142f85   Trond Myklebust   NFS: Replace NFS_...
117
  	struct inode *inode = page->mapping->host;
277459d2e   Trond Myklebust   NFS: Store pointe...
118
  	struct nfs_page *req = NULL;
277459d2e   Trond Myklebust   NFS: Store pointe...
119

587142f85   Trond Myklebust   NFS: Replace NFS_...
120
  	spin_lock(&inode->i_lock);
277459d2e   Trond Myklebust   NFS: Store pointe...
121
  	req = nfs_page_find_request_locked(page);
587142f85   Trond Myklebust   NFS: Replace NFS_...
122
  	spin_unlock(&inode->i_lock);
277459d2e   Trond Myklebust   NFS: Store pointe...
123
124
  	return req;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
128
  /* 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)
  {
  	struct inode *inode = page->mapping->host;
a3d01454b   Trond Myklebust   NFS: Remove BKL r...
129
130
  	loff_t end, i_size;
  	pgoff_t end_index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131

a3d01454b   Trond Myklebust   NFS: Remove BKL r...
132
133
134
  	spin_lock(&inode->i_lock);
  	i_size = i_size_read(inode);
  	end_index = (i_size - 1) >> PAGE_CACHE_SHIFT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
  	if (i_size > 0 && page->index < end_index)
a3d01454b   Trond Myklebust   NFS: Remove BKL r...
136
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
  	end = ((loff_t)page->index << PAGE_CACHE_SHIFT) + ((loff_t)offset+count);
  	if (i_size >= end)
a3d01454b   Trond Myklebust   NFS: Remove BKL r...
139
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
  	i_size_write(inode, end);
a3d01454b   Trond Myklebust   NFS: Remove BKL r...
141
142
143
  	nfs_inc_stats(inode, NFSIOS_EXTENDWRITE);
  out:
  	spin_unlock(&inode->i_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
  }
a301b7777   Trond Myklebust   NFS: Don't use Cl...
145
146
147
148
149
150
  /* A writeback failed: mark the page as bad, and invalidate the page cache */
  static void nfs_set_pageerror(struct page *page)
  {
  	SetPageError(page);
  	nfs_zap_mapping(page->mapping->host, page->mapping);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
152
153
154
155
  /* We can set the PG_uptodate flag if we see that a write request
   * covers the full page.
   */
  static void nfs_mark_uptodate(struct page *page, unsigned int base, unsigned int count)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
158
159
  	if (PageUptodate(page))
  		return;
  	if (base != 0)
  		return;
49a70f278   Trond Myklebust   NFS: Cleanup: add...
160
  	if (count != nfs_page_length(page))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
  		return;
49a70f278   Trond Myklebust   NFS: Cleanup: add...
162
  	SetPageUptodate(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
165
166
  static int wb_priority(struct writeback_control *wbc)
  {
  	if (wbc->for_reclaim)
c63c7b051   Trond Myklebust   NFS: Fix a race w...
167
  		return FLUSH_HIGHPRI | FLUSH_STABLE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
170
171
172
173
  	if (wbc->for_kupdate)
  		return FLUSH_LOWPRI;
  	return 0;
  }
  
  /*
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
174
175
176
177
178
179
180
181
   * 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))
5a6d41b32   Trond Myklebust   NFS: Ensure PG_wr...
182
  static int nfs_set_page_writeback(struct page *page)
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
183
  {
5a6d41b32   Trond Myklebust   NFS: Ensure PG_wr...
184
185
186
  	int ret = test_set_page_writeback(page);
  
  	if (!ret) {
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
187
188
  		struct inode *inode = page->mapping->host;
  		struct nfs_server *nfss = NFS_SERVER(inode);
277866a0e   Peter Zijlstra   nfs: fix congesti...
189
  		if (atomic_long_inc_return(&nfss->writeback) >
8aa7e847d   Jens Axboe   Fix congestion_wa...
190
191
192
193
  				NFS_CONGESTION_ON_THRESH) {
  			set_bdi_congested(&nfss->backing_dev_info,
  						BLK_RW_ASYNC);
  		}
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
194
  	}
5a6d41b32   Trond Myklebust   NFS: Ensure PG_wr...
195
  	return ret;
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
196
197
198
199
200
201
202
203
  }
  
  static void nfs_end_page_writeback(struct page *page)
  {
  	struct inode *inode = page->mapping->host;
  	struct nfs_server *nfss = NFS_SERVER(inode);
  
  	end_page_writeback(page);
c4dc4beed   Peter Zijlstra   nfs: remove conge...
204
  	if (atomic_long_dec_return(&nfss->writeback) < NFS_CONGESTION_OFF_THRESH)
8aa7e847d   Jens Axboe   Fix congestion_wa...
205
  		clear_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC);
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
206
  }
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
207
  static struct nfs_page *nfs_find_and_lock_request(struct page *page)
e261f51f2   Trond Myklebust   NFS: Make nfs_upd...
208
  {
587142f85   Trond Myklebust   NFS: Replace NFS_...
209
  	struct inode *inode = page->mapping->host;
e261f51f2   Trond Myklebust   NFS: Make nfs_upd...
210
  	struct nfs_page *req;
e261f51f2   Trond Myklebust   NFS: Make nfs_upd...
211
  	int ret;
587142f85   Trond Myklebust   NFS: Replace NFS_...
212
  	spin_lock(&inode->i_lock);
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
213
  	for (;;) {
e261f51f2   Trond Myklebust   NFS: Make nfs_upd...
214
  		req = nfs_page_find_request_locked(page);
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
215
216
  		if (req == NULL)
  			break;
acee478af   Trond Myklebust   NFS: Clean up the...
217
  		if (nfs_set_page_tag_locked(req))
e261f51f2   Trond Myklebust   NFS: Make nfs_upd...
218
219
  			break;
  		/* Note: If we hold the page lock, as is the case in nfs_writepage,
acee478af   Trond Myklebust   NFS: Clean up the...
220
  		 *	 then the call to nfs_set_page_tag_locked() will always
e261f51f2   Trond Myklebust   NFS: Make nfs_upd...
221
222
223
  		 *	 succeed provided that someone hasn't already marked the
  		 *	 request as dirty (in which case we don't care).
  		 */
587142f85   Trond Myklebust   NFS: Replace NFS_...
224
  		spin_unlock(&inode->i_lock);
e261f51f2   Trond Myklebust   NFS: Make nfs_upd...
225
226
227
  		ret = nfs_wait_on_request(req);
  		nfs_release_request(req);
  		if (ret != 0)
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
228
  			return ERR_PTR(ret);
587142f85   Trond Myklebust   NFS: Replace NFS_...
229
  		spin_lock(&inode->i_lock);
e261f51f2   Trond Myklebust   NFS: Make nfs_upd...
230
  	}
587142f85   Trond Myklebust   NFS: Replace NFS_...
231
  	spin_unlock(&inode->i_lock);
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
  	return req;
  }
  
  /*
   * 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,
  				struct page *page)
  {
  	struct nfs_page *req;
  	int ret = 0;
  
  	req = nfs_find_and_lock_request(page);
  	if (!req)
  		goto out;
  	ret = PTR_ERR(req);
  	if (IS_ERR(req))
  		goto out;
  
  	ret = nfs_set_page_writeback(page);
  	BUG_ON(ret != 0);
  	BUG_ON(test_bit(PG_CLEAN, &req->wb_flags));
f8512ad0d   Fred Isaman   nfs: don't ignore...
255
256
  	if (!nfs_pageio_add_request(pgio, req)) {
  		nfs_redirty_request(req);
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
257
  		ret = pgio->pg_error;
f8512ad0d   Fred Isaman   nfs: don't ignore...
258
  	}
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
259
260
  out:
  	return ret;
e261f51f2   Trond Myklebust   NFS: Make nfs_upd...
261
  }
f758c8851   Trond Myklebust   NFS: Clean up nfs...
262
  static int nfs_do_writepage(struct page *page, struct writeback_control *wbc, struct nfs_pageio_descriptor *pgio)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
  	struct inode *inode = page->mapping->host;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265

91d5b4702   Chuck Lever   NFS: add I/O perf...
266
267
  	nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGE);
  	nfs_add_stats(inode, NFSIOS_WRITEPAGES, 1);
7fe7f8487   Trond Myklebust   NFS: Avoid a dead...
268
  	nfs_pageio_cond_complete(pgio, page->index);
f758c8851   Trond Myklebust   NFS: Clean up nfs...
269
270
  	return nfs_page_async_flush(pgio, page);
  }
7fe7f8487   Trond Myklebust   NFS: Avoid a dead...
271

f758c8851   Trond Myklebust   NFS: Clean up nfs...
272
273
274
275
276
277
278
  /*
   * Write an mmapped page to the server.
   */
  static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc)
  {
  	struct nfs_pageio_descriptor pgio;
  	int err;
49a70f278   Trond Myklebust   NFS: Cleanup: add...
279

f758c8851   Trond Myklebust   NFS: Clean up nfs...
280
281
282
283
284
285
286
287
  	nfs_pageio_init_write(&pgio, page->mapping->host, wb_priority(wbc));
  	err = nfs_do_writepage(page, wbc, &pgio);
  	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 ...
288
289
290
291
  }
  
  int nfs_writepage(struct page *page, struct writeback_control *wbc)
  {
f758c8851   Trond Myklebust   NFS: Clean up nfs...
292
  	int ret;
4d770ccf4   Trond Myklebust   NFS: Ensure that ...
293

f758c8851   Trond Myklebust   NFS: Clean up nfs...
294
  	ret = nfs_writepage_locked(page, wbc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
  	unlock_page(page);
f758c8851   Trond Myklebust   NFS: Clean up nfs...
296
297
298
299
300
301
302
303
304
305
  	return ret;
  }
  
  static int nfs_writepages_callback(struct page *page, struct writeback_control *wbc, void *data)
  {
  	int ret;
  
  	ret = nfs_do_writepage(page, wbc, data);
  	unlock_page(page);
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
308
  int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
  	struct inode *inode = mapping->host;
72cb77f4a   Trond Myklebust   NFS: Throttle pag...
310
  	unsigned long *bitlock = &NFS_I(inode)->flags;
c63c7b051   Trond Myklebust   NFS: Fix a race w...
311
  	struct nfs_pageio_descriptor pgio;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
  	int err;
72cb77f4a   Trond Myklebust   NFS: Throttle pag...
313
314
315
316
317
  	/* Stop dirtying of new pages while we sync */
  	err = wait_on_bit_lock(bitlock, NFS_INO_FLUSHING,
  			nfs_wait_bit_killable, TASK_KILLABLE);
  	if (err)
  		goto out_err;
91d5b4702   Chuck Lever   NFS: add I/O perf...
318
  	nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES);
c63c7b051   Trond Myklebust   NFS: Fix a race w...
319
  	nfs_pageio_init_write(&pgio, inode, wb_priority(wbc));
f758c8851   Trond Myklebust   NFS: Clean up nfs...
320
  	err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio);
c63c7b051   Trond Myklebust   NFS: Fix a race w...
321
  	nfs_pageio_complete(&pgio);
72cb77f4a   Trond Myklebust   NFS: Throttle pag...
322
323
324
325
  
  	clear_bit_unlock(NFS_INO_FLUSHING, bitlock);
  	smp_mb__after_clear_bit();
  	wake_up_bit(bitlock, NFS_INO_FLUSHING);
f758c8851   Trond Myklebust   NFS: Clean up nfs...
326
  	if (err < 0)
72cb77f4a   Trond Myklebust   NFS: Throttle pag...
327
328
329
330
  		goto out_err;
  	err = pgio.pg_error;
  	if (err < 0)
  		goto out_err;
c63c7b051   Trond Myklebust   NFS: Fix a race w...
331
  	return 0;
72cb77f4a   Trond Myklebust   NFS: Throttle pag...
332
333
  out_err:
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
336
337
338
  }
  
  /*
   * Insert a write request into an inode
   */
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
339
  static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
341
342
  {
  	struct nfs_inode *nfsi = NFS_I(inode);
  	int error;
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
343
344
345
346
347
348
349
350
  	error = radix_tree_preload(GFP_NOFS);
  	if (error != 0)
  		goto out;
  
  	/* Lock the request! */
  	nfs_lock_request_dontget(req);
  
  	spin_lock(&inode->i_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
  	error = radix_tree_insert(&nfsi->nfs_page_tree, req->wb_index, req);
278525963   Nick Piggin   nfs: use GFP_NOFS...
352
  	BUG_ON(error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
354
  	if (!nfsi->npages) {
  		igrab(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355
356
357
  		if (nfs_have_delegation(inode, FMODE_WRITE))
  			nfsi->change_attr++;
  	}
deb7d6382   Trond Myklebust   NFS: Fix a race w...
358
  	SetPagePrivate(req->wb_page);
277459d2e   Trond Myklebust   NFS: Store pointe...
359
  	set_page_private(req->wb_page, (unsigned long)req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
  	nfsi->npages++;
c03b40246   Trond Myklebust   NFS: Convert stru...
361
  	kref_get(&req->wb_kref);
278525963   Nick Piggin   nfs: use GFP_NOFS...
362
363
  	radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index,
  				NFS_PAGE_TAG_LOCKED);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
364
365
366
367
  	spin_unlock(&inode->i_lock);
  	radix_tree_preload_end();
  out:
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
369
370
  }
  
  /*
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
371
   * Remove a write request from an inode
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
373
374
   */
  static void nfs_inode_remove_request(struct nfs_page *req)
  {
88be9f990   Trond Myklebust   NFS: Replace vfsm...
375
  	struct inode *inode = req->wb_context->path.dentry->d_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
377
378
  	struct nfs_inode *nfsi = NFS_I(inode);
  
  	BUG_ON (!NFS_WBACK_BUSY(req));
587142f85   Trond Myklebust   NFS: Replace NFS_...
379
  	spin_lock(&inode->i_lock);
277459d2e   Trond Myklebust   NFS: Store pointe...
380
  	set_page_private(req->wb_page, 0);
deb7d6382   Trond Myklebust   NFS: Fix a race w...
381
  	ClearPagePrivate(req->wb_page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
383
384
  	radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index);
  	nfsi->npages--;
  	if (!nfsi->npages) {
587142f85   Trond Myklebust   NFS: Replace NFS_...
385
  		spin_unlock(&inode->i_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
387
  		iput(inode);
  	} else
587142f85   Trond Myklebust   NFS: Replace NFS_...
388
  		spin_unlock(&inode->i_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
390
391
  	nfs_clear_request(req);
  	nfs_release_request(req);
  }
61822ab5e   Trond Myklebust   NFS: Ensure we on...
392
  static void
6d884e8fc   Fred   nfs: nfs_redirty_...
393
  nfs_mark_request_dirty(struct nfs_page *req)
61822ab5e   Trond Myklebust   NFS: Ensure we on...
394
  {
61822ab5e   Trond Myklebust   NFS: Ensure we on...
395
396
  	__set_page_dirty_nobuffers(req->wb_page);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397
398
399
400
401
402
403
  #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
  /*
   * Add a request to the inode's commit list.
   */
  static void
  nfs_mark_request_commit(struct nfs_page *req)
  {
88be9f990   Trond Myklebust   NFS: Replace vfsm...
404
  	struct inode *inode = req->wb_context->path.dentry->d_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
  	struct nfs_inode *nfsi = NFS_I(inode);
587142f85   Trond Myklebust   NFS: Replace NFS_...
406
  	spin_lock(&inode->i_lock);
e468bae97   Trond Myklebust   NFS: Allow redirt...
407
  	set_bit(PG_CLEAN, &(req)->wb_flags);
5c3696834   Trond Myklebust   NFS cleanup: spee...
408
409
410
  	radix_tree_tag_set(&nfsi->nfs_page_tree,
  			req->wb_index,
  			NFS_PAGE_TAG_COMMIT);
587142f85   Trond Myklebust   NFS: Replace NFS_...
411
  	spin_unlock(&inode->i_lock);
fd39fc856   Christoph Lameter   [PATCH] zoned vm ...
412
  	inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
c9e51e418   Peter Zijlstra   mm: count reclaim...
413
  	inc_bdi_stat(req->wb_page->mapping->backing_dev_info, BDI_RECLAIMABLE);
a18030445   Trond Myklebust   NFS: Clean up cal...
414
  	__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
415
  }
8e821cad1   Trond Myklebust   NFS: clean up the...
416

e468bae97   Trond Myklebust   NFS: Allow redirt...
417
418
419
420
421
422
423
424
425
426
427
428
  static int
  nfs_clear_request_commit(struct nfs_page *req)
  {
  	struct page *page = req->wb_page;
  
  	if (test_and_clear_bit(PG_CLEAN, &(req)->wb_flags)) {
  		dec_zone_page_state(page, NR_UNSTABLE_NFS);
  		dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE);
  		return 1;
  	}
  	return 0;
  }
8e821cad1   Trond Myklebust   NFS: clean up the...
429
430
431
432
433
434
435
436
437
  static inline
  int nfs_write_need_commit(struct nfs_write_data *data)
  {
  	return data->verf.committed != NFS_FILE_SYNC;
  }
  
  static inline
  int nfs_reschedule_unstable_write(struct nfs_page *req)
  {
e468bae97   Trond Myklebust   NFS: Allow redirt...
438
  	if (test_and_clear_bit(PG_NEED_COMMIT, &req->wb_flags)) {
8e821cad1   Trond Myklebust   NFS: clean up the...
439
440
441
442
  		nfs_mark_request_commit(req);
  		return 1;
  	}
  	if (test_and_clear_bit(PG_NEED_RESCHED, &req->wb_flags)) {
6d884e8fc   Fred   nfs: nfs_redirty_...
443
  		nfs_mark_request_dirty(req);
8e821cad1   Trond Myklebust   NFS: clean up the...
444
445
446
447
448
449
450
451
452
  		return 1;
  	}
  	return 0;
  }
  #else
  static inline void
  nfs_mark_request_commit(struct nfs_page *req)
  {
  }
e468bae97   Trond Myklebust   NFS: Allow redirt...
453
454
455
456
457
  static inline int
  nfs_clear_request_commit(struct nfs_page *req)
  {
  	return 0;
  }
8e821cad1   Trond Myklebust   NFS: clean up the...
458
459
460
461
462
463
464
465
466
467
468
  static inline
  int nfs_write_need_commit(struct nfs_write_data *data)
  {
  	return 0;
  }
  
  static inline
  int nfs_reschedule_unstable_write(struct nfs_page *req)
  {
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
469
470
471
472
473
  #endif
  
  /*
   * Wait for a request to complete.
   *
150030b78   Matthew Wilcox   NFS: Switch from ...
474
   * Interruptible by fatal signals only.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
   */
ca52fec15   Trond Myklebust   NFS: Use pgoff_t ...
476
  static int nfs_wait_on_requests_locked(struct inode *inode, pgoff_t idx_start, unsigned int npages)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477
478
479
  {
  	struct nfs_inode *nfsi = NFS_I(inode);
  	struct nfs_page *req;
ca52fec15   Trond Myklebust   NFS: Use pgoff_t ...
480
  	pgoff_t idx_end, next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481
482
483
484
485
486
487
  	unsigned int		res = 0;
  	int			error;
  
  	if (npages == 0)
  		idx_end = ~0;
  	else
  		idx_end = idx_start + npages - 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
488
  	next = idx_start;
9fd367f0f   Trond Myklebust   NFS cleanup: Rena...
489
  	while (radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree, (void **)&req, next, 1, NFS_PAGE_TAG_LOCKED)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
490
491
492
493
  		if (req->wb_index > idx_end)
  			break;
  
  		next = req->wb_index + 1;
c6a556b88   Trond Myklebust   [PATCH] NFS: Make...
494
  		BUG_ON(!NFS_WBACK_BUSY(req));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495

c03b40246   Trond Myklebust   NFS: Convert stru...
496
  		kref_get(&req->wb_kref);
587142f85   Trond Myklebust   NFS: Replace NFS_...
497
  		spin_unlock(&inode->i_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
498
499
  		error = nfs_wait_on_request(req);
  		nfs_release_request(req);
587142f85   Trond Myklebust   NFS: Replace NFS_...
500
  		spin_lock(&inode->i_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
502
  		if (error < 0)
  			return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
503
504
  		res++;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
506
  	return res;
  }
83715ad54   Trond Myklebust   NFS: Fix NFS page...
507
508
509
510
511
512
513
  static void nfs_cancel_commit_list(struct list_head *head)
  {
  	struct nfs_page *req;
  
  	while(!list_empty(head)) {
  		req = nfs_list_entry(head->next);
  		nfs_list_remove_request(req);
e468bae97   Trond Myklebust   NFS: Allow redirt...
514
  		nfs_clear_request_commit(req);
83715ad54   Trond Myklebust   NFS: Fix NFS page...
515
  		nfs_inode_remove_request(req);
b6dff26a0   Trond Myklebust   [PATCH] NFS: Fix ...
516
  		nfs_unlock_request(req);
83715ad54   Trond Myklebust   NFS: Fix NFS page...
517
518
  	}
  }
47c625642   Trond Myklebust   NFS: Fix up a mis...
519
  #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
fb8a1f11b   Trond Myklebust   NFS: cleanup - re...
520
521
522
523
524
  static int
  nfs_need_commit(struct nfs_inode *nfsi)
  {
  	return radix_tree_tagged(&nfsi->nfs_page_tree, NFS_PAGE_TAG_COMMIT);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
525
526
527
528
529
530
531
532
533
534
535
  /*
   * nfs_scan_commit - Scan an inode for commit requests
   * @inode: NFS inode to scan
   * @dst: destination list
   * @idx_start: lower bound of page->index to scan.
   * @npages: idx_start + npages sets the upper bound to scan.
   *
   * Moves requests from the inode's 'commit' request list.
   * The requests are *not* checked to ensure that they form a contiguous set.
   */
  static int
ca52fec15   Trond Myklebust   NFS: Use pgoff_t ...
536
  nfs_scan_commit(struct inode *inode, struct list_head *dst, pgoff_t idx_start, unsigned int npages)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537
538
  {
  	struct nfs_inode *nfsi = NFS_I(inode);
3da28eb1c   Trond Myklebust   [PATCH] NFS: Repl...
539

fb8a1f11b   Trond Myklebust   NFS: cleanup - re...
540
541
542
543
  	if (!nfs_need_commit(nfsi))
  		return 0;
  
  	return nfs_scan_list(nfsi, dst, idx_start, npages, NFS_PAGE_TAG_COMMIT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
544
  }
c42de9dd6   Trond Myklebust   NFS: Fix a race i...
545
  #else
fb8a1f11b   Trond Myklebust   NFS: cleanup - re...
546
547
548
549
  static inline int nfs_need_commit(struct nfs_inode *nfsi)
  {
  	return 0;
  }
ca52fec15   Trond Myklebust   NFS: Use pgoff_t ...
550
  static inline int nfs_scan_commit(struct inode *inode, struct list_head *dst, pgoff_t idx_start, unsigned int npages)
c42de9dd6   Trond Myklebust   NFS: Fix a race i...
551
552
553
  {
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
554
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
  /*
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
556
557
   * 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
558
   *
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
559
560
   * If the attempt fails, then the existing request is flushed out
   * to disk.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
   */
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
562
563
564
565
  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
566
  {
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
567
568
569
570
571
572
573
  	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
574
575
  
  	end = offset + bytes;
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
576
  	spin_lock(&inode->i_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
577

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
578
  	for (;;) {
277459d2e   Trond Myklebust   NFS: Store pointe...
579
  		req = nfs_page_find_request_locked(page);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
580
581
582
583
584
585
586
587
588
589
  		if (req == NULL)
  			goto out_unlock;
  
  		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...
590
  		if (offset > rqend
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
591
592
593
594
  		    || end < req->wb_offset)
  			goto out_flushme;
  
  		if (nfs_set_page_tag_locked(req))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596

e7d39069e   Trond Myklebust   NFS: Clean up nfs...
597
  		/* The request is locked, so wait and then retry */
587142f85   Trond Myklebust   NFS: Replace NFS_...
598
  		spin_unlock(&inode->i_lock);
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
599
600
601
602
603
  		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
604
  	}
e468bae97   Trond Myklebust   NFS: Allow redirt...
605
606
607
  	if (nfs_clear_request_commit(req))
  		radix_tree_tag_clear(&NFS_I(inode)->nfs_page_tree,
  				req->wb_index, NFS_PAGE_TAG_COMMIT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608
609
610
611
  	/* 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
612
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
613
614
  	if (end > rqend)
  		req->wb_bytes = end - req->wb_offset;
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
  	else
  		req->wb_bytes = rqend - req->wb_offset;
  out_unlock:
  	spin_unlock(&inode->i_lock);
  	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)
  {
  	struct inode *inode = page->mapping->host;
  	struct nfs_page	*req;
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641

e7d39069e   Trond Myklebust   NFS: Clean up nfs...
642
643
644
645
646
647
648
649
650
651
652
  	req = nfs_try_to_update_request(inode, page, offset, bytes);
  	if (req != NULL)
  		goto out;
  	req = nfs_create_request(ctx, inode, page, offset, bytes);
  	if (IS_ERR(req))
  		goto out;
  	error = nfs_inode_add_request(inode, req);
  	if (error != 0) {
  		nfs_release_request(req);
  		req = ERR_PTR(error);
  	}
efc91ed01   Trond Myklebust   NFS: Optimise app...
653
  out:
61e930a90   Trond Myklebust   NFS: Fix a writeb...
654
  	return req;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
  }
e7d39069e   Trond Myklebust   NFS: Clean up nfs...
656
657
658
659
660
661
662
663
664
665
666
667
668
669
  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);
  	nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes);
  	nfs_clear_page_tag_locked(req);
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
670
671
  int nfs_flush_incompatible(struct file *file, struct page *page)
  {
cd3758e37   Trond Myklebust   NFS: Replace file...
672
  	struct nfs_open_context *ctx = nfs_file_open_context(file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
673
  	struct nfs_page	*req;
1a54533ec   Trond Myklebust   NFS: Add nfs_set_...
674
  	int do_flush, status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
675
676
677
678
679
680
681
682
  	/*
  	 * 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_...
683
684
685
686
  	do {
  		req = nfs_page_find_request(page);
  		if (req == NULL)
  			return 0;
e468bae97   Trond Myklebust   NFS: Allow redirt...
687
  		do_flush = req->wb_page != page || req->wb_context != ctx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688
  		nfs_release_request(req);
1a54533ec   Trond Myklebust   NFS: Add nfs_set_...
689
690
691
692
693
  		if (!do_flush)
  			return 0;
  		status = nfs_wb_page(page->mapping->host, page);
  	} while (status == 0);
  	return status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
694
695
696
  }
  
  /*
5d47a3560   Trond Myklebust   NFS: Fix a potent...
697
698
699
700
701
702
703
704
705
706
707
   * 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.
   */
  static int nfs_write_pageuptodate(struct page *page, struct inode *inode)
  {
  	return PageUptodate(page) &&
  		!(NFS_I(inode)->cache_validity & (NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA));
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
708
709
710
711
712
713
714
715
   * 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...
716
  	struct nfs_open_context *ctx = nfs_file_open_context(file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
  	struct inode	*inode = page->mapping->host;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
718
  	int		status = 0;
91d5b4702   Chuck Lever   NFS: add I/O perf...
719
  	nfs_inc_stats(inode, NFSIOS_VFSUPDATEPAGE);
48186c7d5   Chuck Lever   NFS: Fix trace de...
720
721
  	dprintk("NFS:       nfs_updatepage(%s/%s %d@%lld)
  ",
01cce933d   Josef "Jeff" Sipek   [PATCH] nfs: chan...
722
723
  		file->f_path.dentry->d_parent->d_name.name,
  		file->f_path.dentry->d_name.name, count,
48186c7d5   Chuck Lever   NFS: Fix trace de...
724
  		(long long)(page_offset(page) + offset));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
725

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
726
  	/* If we're not using byte range locks, and we know the page
5d47a3560   Trond Myklebust   NFS: Fix a potent...
727
728
729
  	 * is up to date, it may be more efficient to extend the write
  	 * to cover the entire page in order to avoid fragmentation
  	 * inefficiencies.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
  	 */
5d47a3560   Trond Myklebust   NFS: Fix a potent...
731
732
  	if (nfs_write_pageuptodate(page, inode) &&
  			inode->i_flock == NULL &&
4b5621f6b   Trond Myklebust   NFS: Fix an f_mod...
733
  			!(file->f_flags & O_SYNC)) {
49a70f278   Trond Myklebust   NFS: Cleanup: add...
734
  		count = max(count + offset, nfs_page_length(page));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
735
  		offset = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
  	}
e21195a74   Trond Myklebust   NFS: More cleanup...
737
  	status = nfs_writepage_setup(ctx, page, offset, count);
03fa9e84e   Trond Myklebust   NFS: nfs_updatepa...
738
739
740
741
  	if (status < 0)
  		nfs_set_pageerror(page);
  	else
  		__set_page_dirty_nobuffers(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
742

48186c7d5   Chuck Lever   NFS: Fix trace de...
743
744
  	dprintk("NFS:       nfs_updatepage returns %d (isize %lld)
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
745
  			status, (long long)i_size_read(inode));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
746
747
748
749
750
  	return status;
  }
  
  static void nfs_writepage_release(struct nfs_page *req)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
751

7e5f61466   Trond Myklebust   NFS: Revert commi...
752
  	if (PageError(req->wb_page) || !nfs_reschedule_unstable_write(req)) {
8e821cad1   Trond Myklebust   NFS: clean up the...
753
754
755
756
  		nfs_end_page_writeback(req->wb_page);
  		nfs_inode_remove_request(req);
  	} else
  		nfs_end_page_writeback(req->wb_page);
9fd367f0f   Trond Myklebust   NFS cleanup: Rena...
757
  	nfs_clear_page_tag_locked(req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
758
  }
3ff7576dd   Trond Myklebust   SUNRPC: Clean up ...
759
  static int flush_task_priority(int how)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
760
761
762
763
764
765
766
767
768
769
770
771
772
  {
  	switch (how & (FLUSH_HIGHPRI|FLUSH_LOWPRI)) {
  		case FLUSH_HIGHPRI:
  			return RPC_PRIORITY_HIGH;
  		case FLUSH_LOWPRI:
  			return RPC_PRIORITY_LOW;
  	}
  	return RPC_PRIORITY_NORMAL;
  }
  
  /*
   * Set up the argument/result storage required for the RPC call.
   */
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
773
  static int nfs_write_rpcsetup(struct nfs_page *req,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
774
  		struct nfs_write_data *data,
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
775
  		const struct rpc_call_ops *call_ops,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
776
777
778
  		unsigned int count, unsigned int offset,
  		int how)
  {
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
779
780
  	struct inode *inode = req->wb_context->path.dentry->d_inode;
  	int flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
3ff7576dd   Trond Myklebust   SUNRPC: Clean up ...
781
  	int priority = flush_task_priority(how);
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
782
  	struct rpc_task *task;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
783
784
785
786
787
  	struct rpc_message msg = {
  		.rpc_argp = &data->args,
  		.rpc_resp = &data->res,
  		.rpc_cred = req->wb_context->cred,
  	};
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
788
789
  	struct rpc_task_setup task_setup_data = {
  		.rpc_client = NFS_CLIENT(inode),
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
790
  		.task = &data->task,
bdc7f021f   Trond Myklebust   NFS: Clean up the...
791
  		.rpc_message = &msg,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
792
793
  		.callback_ops = call_ops,
  		.callback_data = data,
101070ca2   Trond Myklebust   NFS: Ensure that ...
794
  		.workqueue = nfsiod_workqueue,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
795
  		.flags = flags,
3ff7576dd   Trond Myklebust   SUNRPC: Clean up ...
796
  		.priority = priority,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
797
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
798
799
800
801
802
  
  	/* Set up the RPC argument and reply structs
  	 * NB: take care not to mess about with data->commit et al. */
  
  	data->req = req;
88be9f990   Trond Myklebust   NFS: Replace vfsm...
803
  	data->inode = inode = req->wb_context->path.dentry->d_inode;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
804
  	data->cred = msg.rpc_cred;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
805
806
807
808
809
810
  
  	data->args.fh     = NFS_FH(inode);
  	data->args.offset = req_offset(req) + offset;
  	data->args.pgbase = req->wb_pgbase + offset;
  	data->args.pages  = data->pagevec;
  	data->args.count  = count;
383ba7193   Trond Myklebust   NFS: Fix a deadlo...
811
  	data->args.context = get_nfs_open_context(req->wb_context);
bdc7f021f   Trond Myklebust   NFS: Clean up the...
812
813
814
  	data->args.stable  = NFS_UNSTABLE;
  	if (how & FLUSH_STABLE) {
  		data->args.stable = NFS_DATA_SYNC;
fb8a1f11b   Trond Myklebust   NFS: cleanup - re...
815
  		if (!nfs_need_commit(NFS_I(inode)))
bdc7f021f   Trond Myklebust   NFS: Clean up the...
816
817
  			data->args.stable = NFS_FILE_SYNC;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
818
819
820
821
  
  	data->res.fattr   = &data->fattr;
  	data->res.count   = count;
  	data->res.verf    = &data->verf;
0e574af1b   Trond Myklebust   NFS: Cleanup init...
822
  	nfs_fattr_init(&data->fattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
823

788e7a89a   Trond Myklebust   NFS: Cleanup of N...
824
  	/* Set up the initial task struct.  */
bdc7f021f   Trond Myklebust   NFS: Clean up the...
825
  	NFS_PROTO(inode)->write_setup(data, &msg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
826

a3f565b1e   Chuck Lever   NFS: fix print fo...
827
  	dprintk("NFS: %5u initiated write call "
48186c7d5   Chuck Lever   NFS: Fix trace de...
828
829
  		"(req %s/%lld, %u bytes @ offset %llu)
  ",
0bbacc402   Chuck Lever   NFS,SUNRPC,NLM: f...
830
  		data->task.tk_pid,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
831
832
833
834
  		inode->i_sb->s_id,
  		(long long)NFS_FILEID(inode),
  		count,
  		(unsigned long long)data->args.offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
835

077376919   Trond Myklebust   NFS/SUNRPC: Conve...
836
  	task = rpc_run_task(&task_setup_data);
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
837
838
839
840
  	if (IS_ERR(task))
  		return PTR_ERR(task);
  	rpc_put_task(task);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
841
  }
6d884e8fc   Fred   nfs: nfs_redirty_...
842
843
844
845
846
847
848
849
850
851
  /* 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);
  	nfs_end_page_writeback(req->wb_page);
  	nfs_clear_page_tag_locked(req);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
852
853
854
855
  /*
   * Generate multiple small requests to write out a single
   * contiguous dirty area on one page.
   */
8d5658c94   Trond Myklebust   NFS: Fix a buffer...
856
  static int nfs_flush_multi(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int how)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
857
858
859
860
  {
  	struct nfs_page *req = nfs_list_entry(head->next);
  	struct page *page = req->wb_page;
  	struct nfs_write_data *data;
e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
861
862
  	size_t wsize = NFS_SERVER(inode)->wsize, nbytes;
  	unsigned int offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
863
  	int requests = 0;
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
864
  	int ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
865
866
867
  	LIST_HEAD(list);
  
  	nfs_list_remove_request(req);
bcb71bba7   Trond Myklebust   NFS: Another clea...
868
  	nbytes = count;
e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
869
870
  	do {
  		size_t len = min(nbytes, wsize);
8d5658c94   Trond Myklebust   NFS: Fix a buffer...
871
  		data = nfs_writedata_alloc(1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
872
873
874
875
  		if (!data)
  			goto out_bad;
  		list_add(&data->pages, &list);
  		requests++;
e9f7bee1d   Trond Myklebust   [PATCH] NFS: larg...
876
877
  		nbytes -= len;
  	} while (nbytes != 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
878
879
880
  	atomic_set(&req->wb_complete, requests);
  
  	ClearPageError(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
881
  	offset = 0;
bcb71bba7   Trond Myklebust   NFS: Another clea...
882
  	nbytes = count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
883
  	do {
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
884
  		int ret2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
885
886
887
888
  		data = list_entry(list.next, struct nfs_write_data, pages);
  		list_del_init(&data->pages);
  
  		data->pagevec[0] = page;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
889

bcb71bba7   Trond Myklebust   NFS: Another clea...
890
891
  		if (nbytes < wsize)
  			wsize = nbytes;
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
892
  		ret2 = nfs_write_rpcsetup(req, data, &nfs_write_partial_ops,
bcb71bba7   Trond Myklebust   NFS: Another clea...
893
  				   wsize, offset, how);
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
894
895
  		if (ret == 0)
  			ret = ret2;
bcb71bba7   Trond Myklebust   NFS: Another clea...
896
897
  		offset += wsize;
  		nbytes -= wsize;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
898
  	} while (nbytes != 0);
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
899
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
900
901
902
903
904
  
  out_bad:
  	while (!list_empty(&list)) {
  		data = list_entry(list.next, struct nfs_write_data, pages);
  		list_del(&data->pages);
8aca67f0a   Trond Myklebust   SUNRPC: Fix a pot...
905
  		nfs_writedata_release(data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
906
  	}
61822ab5e   Trond Myklebust   NFS: Ensure we on...
907
  	nfs_redirty_request(req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
908
909
910
911
912
913
914
915
916
917
918
  	return -ENOMEM;
  }
  
  /*
   * Create an RPC task for the given write request and kick it.
   * The page must have been locked by the caller.
   *
   * It may happen that the page we're passed is not marked dirty.
   * This is the case if nfs_updatepage detects a conflicting request
   * that has been written but not committed.
   */
8d5658c94   Trond Myklebust   NFS: Fix a buffer...
919
  static int nfs_flush_one(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int how)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
920
921
922
923
  {
  	struct nfs_page		*req;
  	struct page		**pages;
  	struct nfs_write_data	*data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
924

8d5658c94   Trond Myklebust   NFS: Fix a buffer...
925
  	data = nfs_writedata_alloc(npages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
926
927
928
929
  	if (!data)
  		goto out_bad;
  
  	pages = data->pagevec;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
930
931
932
933
934
  	while (!list_empty(head)) {
  		req = nfs_list_entry(head->next);
  		nfs_list_remove_request(req);
  		nfs_list_add_request(req, &data->pages);
  		ClearPageError(req->wb_page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
935
  		*pages++ = req->wb_page;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
936
937
  	}
  	req = nfs_list_entry(data->pages.next);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
938
  	/* Set up the argument struct */
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
939
  	return nfs_write_rpcsetup(req, data, &nfs_write_full_ops, count, 0, how);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
940
941
   out_bad:
  	while (!list_empty(head)) {
10afec908   Trond Myklebust   NFS: Fix some 'sp...
942
  		req = nfs_list_entry(head->next);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
943
  		nfs_list_remove_request(req);
61822ab5e   Trond Myklebust   NFS: Ensure we on...
944
  		nfs_redirty_request(req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
945
946
947
  	}
  	return -ENOMEM;
  }
c63c7b051   Trond Myklebust   NFS: Fix a race w...
948
949
  static void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
  				  struct inode *inode, int ioflags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
950
  {
bf4285e75   Chuck Lever   NFS: Fix minor mi...
951
  	size_t wsize = NFS_SERVER(inode)->wsize;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
952

bcb71bba7   Trond Myklebust   NFS: Another clea...
953
  	if (wsize < PAGE_CACHE_SIZE)
c63c7b051   Trond Myklebust   NFS: Fix a race w...
954
  		nfs_pageio_init(pgio, inode, nfs_flush_multi, wsize, ioflags);
bcb71bba7   Trond Myklebust   NFS: Another clea...
955
  	else
c63c7b051   Trond Myklebust   NFS: Fix a race w...
956
  		nfs_pageio_init(pgio, inode, nfs_flush_one, wsize, ioflags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
957
958
959
960
961
  }
  
  /*
   * Handle a write reply that flushed part of a page.
   */
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
962
  static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
963
  {
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
964
  	struct nfs_write_data	*data = calldata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
965

48186c7d5   Chuck Lever   NFS: Fix trace de...
966
967
968
969
970
971
  	dprintk("NFS: %5u write(%s/%lld %d@%lld)",
  		task->tk_pid,
  		data->req->wb_context->path.dentry->d_inode->i_sb->s_id,
  		(long long)
  		  NFS_FILEID(data->req->wb_context->path.dentry->d_inode),
  		data->req->wb_bytes, (long long)req_offset(data->req));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
972

c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
973
974
  	nfs_writeback_done(task, data);
  }
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
975

c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
976
977
978
979
980
981
982
983
  static void nfs_writeback_release_partial(void *calldata)
  {
  	struct nfs_write_data	*data = calldata;
  	struct nfs_page		*req = data->req;
  	struct page		*page = req->wb_page;
  	int status = data->task.tk_status;
  
  	if (status < 0) {
a301b7777   Trond Myklebust   NFS: Don't use Cl...
984
  		nfs_set_pageerror(page);
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
985
986
987
  		nfs_context_set_write_error(req->wb_context, status);
  		dprintk(", error = %d
  ", status);
8e821cad1   Trond Myklebust   NFS: clean up the...
988
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
989
  	}
8e821cad1   Trond Myklebust   NFS: clean up the...
990
  	if (nfs_write_need_commit(data)) {
587142f85   Trond Myklebust   NFS: Replace NFS_...
991
  		struct inode *inode = page->mapping->host;
8e821cad1   Trond Myklebust   NFS: clean up the...
992

587142f85   Trond Myklebust   NFS: Replace NFS_...
993
  		spin_lock(&inode->i_lock);
8e821cad1   Trond Myklebust   NFS: clean up the...
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
  		if (test_bit(PG_NEED_RESCHED, &req->wb_flags)) {
  			/* Do nothing we need to resend the writes */
  		} else if (!test_and_set_bit(PG_NEED_COMMIT, &req->wb_flags)) {
  			memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf));
  			dprintk(" defer commit
  ");
  		} else if (memcmp(&req->wb_verf, &data->verf, sizeof(req->wb_verf))) {
  			set_bit(PG_NEED_RESCHED, &req->wb_flags);
  			clear_bit(PG_NEED_COMMIT, &req->wb_flags);
  			dprintk(" server reboot detected
  ");
  		}
587142f85   Trond Myklebust   NFS: Replace NFS_...
1006
  		spin_unlock(&inode->i_lock);
8e821cad1   Trond Myklebust   NFS: clean up the...
1007
1008
1009
1010
1011
  	} else
  		dprintk(" OK
  ");
  
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1012
1013
  	if (atomic_dec_and_test(&req->wb_complete))
  		nfs_writepage_release(req);
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1014
  	nfs_writedata_release(calldata);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1015
  }
def6ed7ef   Andy Adamson   nfs41 write seque...
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
  #if defined(CONFIG_NFS_V4_1)
  void nfs_write_prepare(struct rpc_task *task, void *calldata)
  {
  	struct nfs_write_data *data = calldata;
  	struct nfs_client *clp = (NFS_SERVER(data->inode))->nfs_client;
  
  	if (nfs4_setup_sequence(clp, &data->args.seq_args,
  				&data->res.seq_res, 1, task))
  		return;
  	rpc_call_start(task);
  }
  #endif /* CONFIG_NFS_V4_1 */
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1028
  static const struct rpc_call_ops nfs_write_partial_ops = {
def6ed7ef   Andy Adamson   nfs41 write seque...
1029
1030
1031
  #if defined(CONFIG_NFS_V4_1)
  	.rpc_call_prepare = nfs_write_prepare,
  #endif /* CONFIG_NFS_V4_1 */
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1032
  	.rpc_call_done = nfs_writeback_done_partial,
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1033
  	.rpc_release = nfs_writeback_release_partial,
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1034
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1035
1036
1037
1038
1039
1040
1041
  /*
   * Handle a write reply that flushes a whole page.
   *
   * FIXME: There is an inherent race with invalidate_inode_pages and
   *	  writebacks since the page->count is kept > 1 for as long
   *	  as the page has a write request pending.
   */
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1042
  static void nfs_writeback_done_full(struct rpc_task *task, void *calldata)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1043
  {
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1044
  	struct nfs_write_data	*data = calldata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1045

c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1046
1047
1048
1049
1050
1051
1052
  	nfs_writeback_done(task, data);
  }
  
  static void nfs_writeback_release_full(void *calldata)
  {
  	struct nfs_write_data	*data = calldata;
  	int status = data->task.tk_status;
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1053

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1054
1055
  	/* Update attributes as result of writeback. */
  	while (!list_empty(&data->pages)) {
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1056
1057
  		struct nfs_page *req = nfs_list_entry(data->pages.next);
  		struct page *page = req->wb_page;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1058
  		nfs_list_remove_request(req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1059

48186c7d5   Chuck Lever   NFS: Fix trace de...
1060
1061
  		dprintk("NFS: %5u write (%s/%lld %d@%lld)",
  			data->task.tk_pid,
88be9f990   Trond Myklebust   NFS: Replace vfsm...
1062
1063
  			req->wb_context->path.dentry->d_inode->i_sb->s_id,
  			(long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1064
1065
  			req->wb_bytes,
  			(long long)req_offset(req));
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1066
  		if (status < 0) {
a301b7777   Trond Myklebust   NFS: Don't use Cl...
1067
  			nfs_set_pageerror(page);
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1068
1069
1070
  			nfs_context_set_write_error(req->wb_context, status);
  			dprintk(", error = %d
  ", status);
8e821cad1   Trond Myklebust   NFS: clean up the...
1071
  			goto remove_request;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1072
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1073

8e821cad1   Trond Myklebust   NFS: clean up the...
1074
1075
1076
1077
1078
1079
  		if (nfs_write_need_commit(data)) {
  			memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf));
  			nfs_mark_request_commit(req);
  			nfs_end_page_writeback(page);
  			dprintk(" marked for commit
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1080
1081
  			goto next;
  		}
8e821cad1   Trond Myklebust   NFS: clean up the...
1082
1083
1084
1085
  		dprintk(" OK
  ");
  remove_request:
  		nfs_end_page_writeback(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1086
  		nfs_inode_remove_request(req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1087
  	next:
9fd367f0f   Trond Myklebust   NFS cleanup: Rena...
1088
  		nfs_clear_page_tag_locked(req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1089
  	}
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1090
  	nfs_writedata_release(calldata);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1091
  }
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1092
  static const struct rpc_call_ops nfs_write_full_ops = {
def6ed7ef   Andy Adamson   nfs41 write seque...
1093
1094
1095
  #if defined(CONFIG_NFS_V4_1)
  	.rpc_call_prepare = nfs_write_prepare,
  #endif /* CONFIG_NFS_V4_1 */
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1096
  	.rpc_call_done = nfs_writeback_done_full,
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1097
  	.rpc_release = nfs_writeback_release_full,
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1098
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1099
1100
1101
  /*
   * This function is called when the WRITE call is complete.
   */
462d5b329   Chuck Lever   NFS: make direct ...
1102
  int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1103
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1104
1105
  	struct nfs_writeargs	*argp = &data->args;
  	struct nfs_writeres	*resp = &data->res;
eedc020e7   Andy Adamson   nfs41: use rpc pr...
1106
  	struct nfs_server	*server = NFS_SERVER(data->inode);
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1107
  	int status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1108

a3f565b1e   Chuck Lever   NFS: fix print fo...
1109
1110
  	dprintk("NFS: %5u nfs_writeback_done (status %d)
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1111
  		task->tk_pid, task->tk_status);
f551e44ff   Chuck Lever   NFS: add comments...
1112
1113
1114
1115
1116
1117
1118
  	/*
  	 * ->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.
  	 */
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1119
1120
1121
  	status = NFS_PROTO(data->inode)->write_done(task, data);
  	if (status != 0)
  		return status;
91d5b4702   Chuck Lever   NFS: add I/O perf...
1122
  	nfs_add_stats(data->inode, NFSIOS_SERVERWRITTENBYTES, resp->count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
  #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
  	if (resp->verf->committed < argp->stable && task->tk_status >= 0) {
  		/* 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;
  
  		if (time_before(complain, jiffies)) {
48186c7d5   Chuck Lever   NFS: Fix trace de...
1136
  			dprintk("NFS:       faulty NFS server %s:"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1137
1138
  				" (committed = %d) != (stable = %d)
  ",
eedc020e7   Andy Adamson   nfs41: use rpc pr...
1139
  				server->nfs_client->cl_hostname,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1140
1141
1142
1143
1144
1145
1146
1147
  				resp->verf->committed, argp->stable);
  			complain = jiffies + 300 * HZ;
  		}
  	}
  #endif
  	/* Is this a short write? */
  	if (task->tk_status >= 0 && resp->count < argp->count) {
  		static unsigned long    complain;
91d5b4702   Chuck Lever   NFS: add I/O perf...
1148
  		nfs_inc_stats(data->inode, NFSIOS_SHORTWRITE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
  		/* Has the server at least made some progress? */
  		if (resp->count != 0) {
  			/* Was this an NFSv2 write or an NFSv3 stable write? */
  			if (resp->verf->committed != NFS_UNSTABLE) {
  				/* Resend from where the server left off */
  				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;
  			}
eedc020e7   Andy Adamson   nfs41: use rpc pr...
1163
  			nfs4_restart_rpc(task, server->nfs_client);
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1164
  			return -EAGAIN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
  		}
  		if (time_before(complain, jiffies)) {
  			printk(KERN_WARNING
  			       "NFS: Server wrote zero bytes, expected %u.
  ",
  					argp->count);
  			complain = jiffies + 300 * HZ;
  		}
  		/* Can't do anything about it except throw an error. */
  		task->tk_status = -EIO;
  	}
eedc020e7   Andy Adamson   nfs41: use rpc pr...
1176
  	nfs4_sequence_free_slot(server->nfs_client, &data->res.seq_res);
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1177
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1178
1179
1180
1181
  }
  
  
  #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1182
  void nfs_commitdata_release(void *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1183
  {
383ba7193   Trond Myklebust   NFS: Fix a deadlo...
1184
1185
1186
  	struct nfs_write_data *wdata = data;
  
  	put_nfs_open_context(wdata->args.context);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1187
1188
1189
1190
1191
1192
  	nfs_commit_free(wdata);
  }
  
  /*
   * Set up the argument/result storage required for the RPC call.
   */
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
1193
  static int nfs_commit_rpcsetup(struct list_head *head,
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1194
1195
  		struct nfs_write_data *data,
  		int how)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1196
  {
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
1197
1198
1199
  	struct nfs_page *first = nfs_list_entry(head->next);
  	struct inode *inode = first->wb_context->path.dentry->d_inode;
  	int flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
3ff7576dd   Trond Myklebust   SUNRPC: Clean up ...
1200
  	int priority = flush_task_priority(how);
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
1201
  	struct rpc_task *task;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
1202
1203
1204
1205
1206
  	struct rpc_message msg = {
  		.rpc_argp = &data->args,
  		.rpc_resp = &data->res,
  		.rpc_cred = first->wb_context->cred,
  	};
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
1207
  	struct rpc_task_setup task_setup_data = {
077376919   Trond Myklebust   NFS/SUNRPC: Conve...
1208
  		.task = &data->task,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
1209
  		.rpc_client = NFS_CLIENT(inode),
bdc7f021f   Trond Myklebust   NFS: Clean up the...
1210
  		.rpc_message = &msg,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
1211
1212
  		.callback_ops = &nfs_commit_ops,
  		.callback_data = data,
101070ca2   Trond Myklebust   NFS: Ensure that ...
1213
  		.workqueue = nfsiod_workqueue,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
1214
  		.flags = flags,
3ff7576dd   Trond Myklebust   SUNRPC: Clean up ...
1215
  		.priority = priority,
84115e1cd   Trond Myklebust   SUNRPC: Cleanup o...
1216
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1217
1218
1219
1220
1221
  
  	/* 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
1222

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1223
  	data->inode	  = inode;
bdc7f021f   Trond Myklebust   NFS: Clean up the...
1224
  	data->cred	  = msg.rpc_cred;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1225
1226
  
  	data->args.fh     = NFS_FH(data->inode);
3da28eb1c   Trond Myklebust   [PATCH] NFS: Repl...
1227
1228
1229
  	/* Note: we always request a commit of the entire inode */
  	data->args.offset = 0;
  	data->args.count  = 0;
383ba7193   Trond Myklebust   NFS: Fix a deadlo...
1230
  	data->args.context = get_nfs_open_context(first->wb_context);
3da28eb1c   Trond Myklebust   [PATCH] NFS: Repl...
1231
  	data->res.count   = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1232
1233
  	data->res.fattr   = &data->fattr;
  	data->res.verf    = &data->verf;
0e574af1b   Trond Myklebust   NFS: Cleanup init...
1234
  	nfs_fattr_init(&data->fattr);
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1235
1236
  
  	/* Set up the initial task struct.  */
bdc7f021f   Trond Myklebust   NFS: Clean up the...
1237
  	NFS_PROTO(inode)->commit_setup(data, &msg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1238

a3f565b1e   Chuck Lever   NFS: fix print fo...
1239
1240
  	dprintk("NFS: %5u initiated commit call
  ", data->task.tk_pid);
bdc7f021f   Trond Myklebust   NFS: Clean up the...
1241

077376919   Trond Myklebust   NFS/SUNRPC: Conve...
1242
  	task = rpc_run_task(&task_setup_data);
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
1243
1244
1245
1246
  	if (IS_ERR(task))
  		return PTR_ERR(task);
  	rpc_put_task(task);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1247
1248
1249
1250
1251
1252
  }
  
  /*
   * Commit dirty pages
   */
  static int
40859d7ee   Chuck Lever   NFS: support larg...
1253
  nfs_commit_list(struct inode *inode, struct list_head *head, int how)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1254
1255
1256
  {
  	struct nfs_write_data	*data;
  	struct nfs_page         *req;
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1257
  	data = nfs_commitdata_alloc();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1258
1259
1260
1261
1262
  
  	if (!data)
  		goto out_bad;
  
  	/* Set up the argument struct */
dbae4c73f   Trond Myklebust   NFS: Ensure that ...
1263
  	return nfs_commit_rpcsetup(head, data, how);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1264
1265
1266
1267
1268
   out_bad:
  	while (!list_empty(head)) {
  		req = nfs_list_entry(head->next);
  		nfs_list_remove_request(req);
  		nfs_mark_request_commit(req);
83715ad54   Trond Myklebust   NFS: Fix NFS page...
1269
  		dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
c9e51e418   Peter Zijlstra   mm: count reclaim...
1270
1271
  		dec_bdi_stat(req->wb_page->mapping->backing_dev_info,
  				BDI_RECLAIMABLE);
9fd367f0f   Trond Myklebust   NFS cleanup: Rena...
1272
  		nfs_clear_page_tag_locked(req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1273
1274
1275
1276
1277
1278
1279
  	}
  	return -ENOMEM;
  }
  
  /*
   * COMMIT call returned
   */
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1280
  static void nfs_commit_done(struct rpc_task *task, void *calldata)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1281
  {
963d8fe53   Trond Myklebust   RPC: Clean up RPC...
1282
  	struct nfs_write_data	*data = calldata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1283

a3f565b1e   Chuck Lever   NFS: fix print fo...
1284
1285
          dprintk("NFS: %5u nfs_commit_done (status %d)
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1286
                                  task->tk_pid, task->tk_status);
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1287
1288
1289
  	/* Call the NFS version-specific code */
  	if (NFS_PROTO(data->inode)->commit_done(task, data) != 0)
  		return;
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1290
1291
1292
1293
1294
1295
1296
  }
  
  static void nfs_commit_release(void *calldata)
  {
  	struct nfs_write_data	*data = calldata;
  	struct nfs_page		*req;
  	int status = data->task.tk_status;
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1297

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1298
1299
1300
  	while (!list_empty(&data->pages)) {
  		req = nfs_list_entry(data->pages.next);
  		nfs_list_remove_request(req);
e468bae97   Trond Myklebust   NFS: Allow redirt...
1301
  		nfs_clear_request_commit(req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1302

48186c7d5   Chuck Lever   NFS: Fix trace de...
1303
  		dprintk("NFS:       commit (%s/%lld %d@%lld)",
88be9f990   Trond Myklebust   NFS: Replace vfsm...
1304
1305
  			req->wb_context->path.dentry->d_inode->i_sb->s_id,
  			(long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1306
1307
  			req->wb_bytes,
  			(long long)req_offset(req));
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1308
1309
  		if (status < 0) {
  			nfs_context_set_write_error(req->wb_context, status);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1310
  			nfs_inode_remove_request(req);
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1311
1312
  			dprintk(", error = %d
  ", status);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
  			goto next;
  		}
  
  		/* Okay, COMMIT succeeded, apparently. Check the verifier
  		 * returned by the server against all stored verfs. */
  		if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) {
  			/* 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_...
1328
  		nfs_mark_request_dirty(req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1329
  	next:
9fd367f0f   Trond Myklebust   NFS cleanup: Rena...
1330
  		nfs_clear_page_tag_locked(req);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1331
  	}
c9d8f89d9   Trond Myklebust   NFS: Ensure that ...
1332
  	nfs_commitdata_release(calldata);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1333
  }
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1334
1335
  
  static const struct rpc_call_ops nfs_commit_ops = {
21d9a851a   Andy Adamson   nfs41 commit sequ...
1336
1337
1338
  #if defined(CONFIG_NFS_V4_1)
  	.rpc_call_prepare = nfs_write_prepare,
  #endif /* CONFIG_NFS_V4_1 */
788e7a89a   Trond Myklebust   NFS: Cleanup of N...
1339
1340
1341
  	.rpc_call_done = nfs_commit_done,
  	.rpc_release = nfs_commit_release,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1342

3da28eb1c   Trond Myklebust   [PATCH] NFS: Repl...
1343
  int nfs_commit_inode(struct inode *inode, int how)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1344
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1345
  	LIST_HEAD(head);
7d46a49f5   Trond Myklebust   NFS: Clean up nfs...
1346
  	int res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1347

587142f85   Trond Myklebust   NFS: Replace NFS_...
1348
  	spin_lock(&inode->i_lock);
3da28eb1c   Trond Myklebust   [PATCH] NFS: Repl...
1349
  	res = nfs_scan_commit(inode, &head, 0, 0);
587142f85   Trond Myklebust   NFS: Replace NFS_...
1350
  	spin_unlock(&inode->i_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1351
  	if (res) {
7d46a49f5   Trond Myklebust   NFS: Clean up nfs...
1352
  		int error = nfs_commit_list(inode, &head, how);
3da28eb1c   Trond Myklebust   [PATCH] NFS: Repl...
1353
1354
1355
  		if (error < 0)
  			return error;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1356
1357
  	return res;
  }
c63c7b051   Trond Myklebust   NFS: Fix a race w...
1358
1359
1360
1361
1362
  #else
  static inline int nfs_commit_list(struct inode *inode, struct list_head *head, int how)
  {
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1363
  #endif
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1364
  long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_control *wbc, int how)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1365
  {
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1366
  	struct inode *inode = mapping->host;
ca52fec15   Trond Myklebust   NFS: Use pgoff_t ...
1367
  	pgoff_t idx_start, idx_end;
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1368
  	unsigned int npages = 0;
c42de9dd6   Trond Myklebust   NFS: Fix a race i...
1369
  	LIST_HEAD(head);
70b9ecbdb   Trond Myklebust   NFS: Make stat() ...
1370
  	int nocommit = how & FLUSH_NOCOMMIT;
3f442547b   Trond Myklebust   NFS: Clean up nfs...
1371
  	long pages, ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1372

1c75950b9   Trond Myklebust   NFS: cleanup of n...
1373
1374
1375
1376
1377
1378
1379
  	/* FIXME */
  	if (wbc->range_cyclic)
  		idx_start = 0;
  	else {
  		idx_start = wbc->range_start >> PAGE_CACHE_SHIFT;
  		idx_end = wbc->range_end >> PAGE_CACHE_SHIFT;
  		if (idx_end > idx_start) {
ca52fec15   Trond Myklebust   NFS: Use pgoff_t ...
1380
  			pgoff_t l_npages = 1 + idx_end - idx_start;
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1381
1382
  			npages = l_npages;
  			if (sizeof(npages) != sizeof(l_npages) &&
ca52fec15   Trond Myklebust   NFS: Use pgoff_t ...
1383
  					(pgoff_t)npages != l_npages)
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1384
1385
1386
  				npages = 0;
  		}
  	}
c42de9dd6   Trond Myklebust   NFS: Fix a race i...
1387
  	how &= ~FLUSH_NOCOMMIT;
587142f85   Trond Myklebust   NFS: Replace NFS_...
1388
  	spin_lock(&inode->i_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1389
  	do {
c42de9dd6   Trond Myklebust   NFS: Fix a race i...
1390
1391
  		ret = nfs_wait_on_requests_locked(inode, idx_start, npages);
  		if (ret != 0)
70b9ecbdb   Trond Myklebust   NFS: Make stat() ...
1392
  			continue;
c42de9dd6   Trond Myklebust   NFS: Fix a race i...
1393
1394
  		if (nocommit)
  			break;
d2ccddf04   Trond Myklebust   NFS: Flesh out nf...
1395
  		pages = nfs_scan_commit(inode, &head, idx_start, npages);
724c439c2   Trond Myklebust   NFS: Clean up nfs...
1396
  		if (pages == 0)
c42de9dd6   Trond Myklebust   NFS: Fix a race i...
1397
  			break;
d2ccddf04   Trond Myklebust   NFS: Flesh out nf...
1398
  		if (how & FLUSH_INVALIDATE) {
587142f85   Trond Myklebust   NFS: Replace NFS_...
1399
  			spin_unlock(&inode->i_lock);
83715ad54   Trond Myklebust   NFS: Fix NFS page...
1400
  			nfs_cancel_commit_list(&head);
e8e058e83   Trond Myklebust   NFS: Fix nfs_sync...
1401
  			ret = pages;
587142f85   Trond Myklebust   NFS: Replace NFS_...
1402
  			spin_lock(&inode->i_lock);
d2ccddf04   Trond Myklebust   NFS: Flesh out nf...
1403
1404
1405
  			continue;
  		}
  		pages += nfs_scan_commit(inode, &head, 0, 0);
587142f85   Trond Myklebust   NFS: Replace NFS_...
1406
  		spin_unlock(&inode->i_lock);
c42de9dd6   Trond Myklebust   NFS: Fix a race i...
1407
  		ret = nfs_commit_list(inode, &head, how);
587142f85   Trond Myklebust   NFS: Replace NFS_...
1408
  		spin_lock(&inode->i_lock);
c42de9dd6   Trond Myklebust   NFS: Fix a race i...
1409
  	} while (ret >= 0);
587142f85   Trond Myklebust   NFS: Replace NFS_...
1410
  	spin_unlock(&inode->i_lock);
c42de9dd6   Trond Myklebust   NFS: Fix a race i...
1411
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1412
  }
34901f70d   Trond Myklebust   NFS: Writeback op...
1413
  static int __nfs_write_mapping(struct address_space *mapping, struct writeback_control *wbc, int how)
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1414
  {
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1415
  	int ret;
34901f70d   Trond Myklebust   NFS: Writeback op...
1416
  	ret = nfs_writepages(mapping, wbc);
61822ab5e   Trond Myklebust   NFS: Ensure we on...
1417
1418
  	if (ret < 0)
  		goto out;
34901f70d   Trond Myklebust   NFS: Writeback op...
1419
  	ret = nfs_sync_mapping_wait(mapping, wbc, how);
ed90ef51a   Trond Myklebust   NFS: Clean up NFS...
1420
1421
1422
  	if (ret < 0)
  		goto out;
  	return 0;
61822ab5e   Trond Myklebust   NFS: Ensure we on...
1423
  out:
e507d9ebb   Trond Myklebust   NFS: Ensure the i...
1424
  	__mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1425
1426
  	return ret;
  }
34901f70d   Trond Myklebust   NFS: Writeback op...
1427
1428
1429
1430
1431
  /* Two pass sync: first using WB_SYNC_NONE, then WB_SYNC_ALL */
  static int nfs_write_mapping(struct address_space *mapping, int how)
  {
  	struct writeback_control wbc = {
  		.bdi = mapping->backing_dev_info,
72cb77f4a   Trond Myklebust   NFS: Throttle pag...
1432
  		.sync_mode = WB_SYNC_ALL,
34901f70d   Trond Myklebust   NFS: Writeback op...
1433
  		.nr_to_write = LONG_MAX,
d7fb12077   Trond Myklebust   NFS: Don't use ra...
1434
1435
  		.range_start = 0,
  		.range_end = LLONG_MAX,
34901f70d   Trond Myklebust   NFS: Writeback op...
1436
  	};
34901f70d   Trond Myklebust   NFS: Writeback op...
1437

34901f70d   Trond Myklebust   NFS: Writeback op...
1438
1439
  	return __nfs_write_mapping(mapping, &wbc, how);
  }
ed90ef51a   Trond Myklebust   NFS: Clean up NFS...
1440
1441
1442
1443
  /*
   * flush the inode to disk.
   */
  int nfs_wb_all(struct inode *inode)
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1444
  {
ed90ef51a   Trond Myklebust   NFS: Clean up NFS...
1445
1446
  	return nfs_write_mapping(inode->i_mapping, 0);
  }
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1447

ed90ef51a   Trond Myklebust   NFS: Clean up NFS...
1448
1449
1450
  int nfs_wb_nocommit(struct inode *inode)
  {
  	return nfs_write_mapping(inode->i_mapping, FLUSH_NOCOMMIT);
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1451
  }
1b3b4a1a2   Trond Myklebust   NFS: Fix a write ...
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
  int nfs_wb_page_cancel(struct inode *inode, struct page *page)
  {
  	struct nfs_page *req;
  	loff_t range_start = page_offset(page);
  	loff_t range_end = range_start + (loff_t)(PAGE_CACHE_SIZE - 1);
  	struct writeback_control wbc = {
  		.bdi = page->mapping->backing_dev_info,
  		.sync_mode = WB_SYNC_ALL,
  		.nr_to_write = LONG_MAX,
  		.range_start = range_start,
  		.range_end = range_end,
  	};
  	int ret = 0;
  
  	BUG_ON(!PageLocked(page));
  	for (;;) {
  		req = nfs_page_find_request(page);
  		if (req == NULL)
  			goto out;
e468bae97   Trond Myklebust   NFS: Allow redirt...
1471
  		if (test_bit(PG_CLEAN, &req->wb_flags)) {
1b3b4a1a2   Trond Myklebust   NFS: Fix a write ...
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
  			nfs_release_request(req);
  			break;
  		}
  		if (nfs_lock_request_dontget(req)) {
  			nfs_inode_remove_request(req);
  			/*
  			 * In case nfs_inode_remove_request has marked the
  			 * page as being dirty
  			 */
  			cancel_dirty_page(page, PAGE_CACHE_SIZE);
  			nfs_unlock_request(req);
  			break;
  		}
  		ret = nfs_wait_on_request(req);
  		if (ret < 0)
  			goto out;
  	}
  	if (!PagePrivate(page))
  		return 0;
  	ret = nfs_sync_mapping_wait(page->mapping, &wbc, FLUSH_INVALIDATE);
  out:
  	return ret;
  }
5334eb13d   Adrian Bunk   NFS: make nfs_wb_...
1495
1496
  static int nfs_wb_page_priority(struct inode *inode, struct page *page,
  				int how)
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1497
1498
1499
  {
  	loff_t range_start = page_offset(page);
  	loff_t range_end = range_start + (loff_t)(PAGE_CACHE_SIZE - 1);
4d770ccf4   Trond Myklebust   NFS: Ensure that ...
1500
1501
1502
1503
1504
1505
1506
1507
  	struct writeback_control wbc = {
  		.bdi = page->mapping->backing_dev_info,
  		.sync_mode = WB_SYNC_ALL,
  		.nr_to_write = LONG_MAX,
  		.range_start = range_start,
  		.range_end = range_end,
  	};
  	int ret;
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1508

73e3302f6   Trond Myklebust   NFS: Fix nfs_wb_p...
1509
1510
1511
1512
1513
1514
1515
1516
  	do {
  		if (clear_page_dirty_for_io(page)) {
  			ret = nfs_writepage_locked(page, &wbc);
  			if (ret < 0)
  				goto out_error;
  		} else if (!PagePrivate(page))
  			break;
  		ret = nfs_sync_mapping_wait(page->mapping, &wbc, how);
4d770ccf4   Trond Myklebust   NFS: Ensure that ...
1517
  		if (ret < 0)
73e3302f6   Trond Myklebust   NFS: Fix nfs_wb_p...
1518
1519
1520
1521
  			goto out_error;
  	} while (PagePrivate(page));
  	return 0;
  out_error:
e507d9ebb   Trond Myklebust   NFS: Ensure the i...
1522
  	__mark_inode_dirty(inode, I_DIRTY_PAGES);
4d770ccf4   Trond Myklebust   NFS: Ensure that ...
1523
  	return ret;
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1524
1525
1526
1527
1528
1529
1530
  }
  
  /*
   * Write back all requests on one page - we do this before reading it.
   */
  int nfs_wb_page(struct inode *inode, struct page* page)
  {
4d770ccf4   Trond Myklebust   NFS: Ensure that ...
1531
  	return nfs_wb_page_priority(inode, page, FLUSH_STABLE);
1c75950b9   Trond Myklebust   NFS: cleanup of n...
1532
  }
074cc1dee   Trond Myklebust   NFS: Add a ->migr...
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
  #ifdef CONFIG_MIGRATION
  int nfs_migrate_page(struct address_space *mapping, struct page *newpage,
  		struct page *page)
  {
  	struct nfs_page *req;
  	int ret;
  
  	if (PageFsCache(page))
  		nfs_fscache_release_page(page, GFP_KERNEL);
  
  	req = nfs_find_and_lock_request(page);
  	ret = PTR_ERR(req);
  	if (IS_ERR(req))
  		goto out;
  
  	ret = migrate_page(mapping, newpage, page);
  	if (!req)
  		goto out;
  	if (ret)
  		goto out_unlock;
  	page_cache_get(newpage);
  	req->wb_page = newpage;
  	SetPagePrivate(newpage);
  	set_page_private(newpage, page_private(page));
  	ClearPagePrivate(page);
  	set_page_private(page, 0);
  	page_cache_release(page);
  out_unlock:
  	nfs_clear_page_tag_locked(req);
  	nfs_release_request(req);
  out:
  	return ret;
  }
  #endif
f7b422b17   David Howells   NFS: Split fs/nfs...
1567
  int __init nfs_init_writepagecache(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1568
1569
1570
1571
  {
  	nfs_wdata_cachep = kmem_cache_create("nfs_write_data",
  					     sizeof(struct nfs_write_data),
  					     0, SLAB_HWCACHE_ALIGN,
20c2df83d   Paul Mundt   mm: Remove slab d...
1572
  					     NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1573
1574
  	if (nfs_wdata_cachep == NULL)
  		return -ENOMEM;
93d2341c7   Matthew Dobson   [PATCH] mempool: ...
1575
1576
  	nfs_wdata_mempool = mempool_create_slab_pool(MIN_POOL_WRITE,
  						     nfs_wdata_cachep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1577
1578
  	if (nfs_wdata_mempool == NULL)
  		return -ENOMEM;
93d2341c7   Matthew Dobson   [PATCH] mempool: ...
1579
1580
  	nfs_commit_mempool = mempool_create_slab_pool(MIN_POOL_COMMIT,
  						      nfs_wdata_cachep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1581
1582
  	if (nfs_commit_mempool == NULL)
  		return -ENOMEM;
89a09141d   Peter Zijlstra   [PATCH] nfs: fix ...
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
  	/*
  	 * 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
1602
1603
  	return 0;
  }
266bee886   David Brownell   [PATCH] fix stati...
1604
  void nfs_destroy_writepagecache(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1605
1606
1607
  {
  	mempool_destroy(nfs_commit_mempool);
  	mempool_destroy(nfs_wdata_mempool);
1a1d92c10   Alexey Dobriyan   [PATCH] Really ig...
1608
  	kmem_cache_destroy(nfs_wdata_cachep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1609
  }