diff options
-rw-r--r-- | fs/nfs/inode.c | 2 | ||||
-rw-r--r-- | fs/nfs/pagelist.c | 86 | ||||
-rw-r--r-- | fs/nfs/write.c | 71 | ||||
-rw-r--r-- | include/linux/nfs_fs.h | 4 | ||||
-rw-r--r-- | include/linux/nfs_page.h | 18 |
5 files changed, 107 insertions, 74 deletions
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 4f545f382ba6..4845911f1c63 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -135,7 +135,7 @@ nfs_write_inode(struct inode *inode, int sync) | |||
135 | int flags = sync ? FLUSH_WAIT : 0; | 135 | int flags = sync ? FLUSH_WAIT : 0; |
136 | int ret; | 136 | int ret; |
137 | 137 | ||
138 | ret = nfs_commit_inode(inode, 0, 0, flags); | 138 | ret = nfs_commit_inode(inode, flags); |
139 | if (ret < 0) | 139 | if (ret < 0) |
140 | return ret; | 140 | return ret; |
141 | return 0; | 141 | return 0; |
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index 356a33bb38a6..d53857b148e2 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c | |||
@@ -177,36 +177,6 @@ nfs_release_request(struct nfs_page *req) | |||
177 | nfs_page_free(req); | 177 | nfs_page_free(req); |
178 | } | 178 | } |
179 | 179 | ||
180 | /** | ||
181 | * nfs_list_add_request - Insert a request into a sorted list | ||
182 | * @req: request | ||
183 | * @head: head of list into which to insert the request. | ||
184 | * | ||
185 | * Note that the wb_list is sorted by page index in order to facilitate | ||
186 | * coalescing of requests. | ||
187 | * We use an insertion sort that is optimized for the case of appended | ||
188 | * writes. | ||
189 | */ | ||
190 | void | ||
191 | nfs_list_add_request(struct nfs_page *req, struct list_head *head) | ||
192 | { | ||
193 | struct list_head *pos; | ||
194 | |||
195 | #ifdef NFS_PARANOIA | ||
196 | if (!list_empty(&req->wb_list)) { | ||
197 | printk(KERN_ERR "NFS: Add to list failed!\n"); | ||
198 | BUG(); | ||
199 | } | ||
200 | #endif | ||
201 | list_for_each_prev(pos, head) { | ||
202 | struct nfs_page *p = nfs_list_entry(pos); | ||
203 | if (p->wb_index < req->wb_index) | ||
204 | break; | ||
205 | } | ||
206 | list_add(&req->wb_list, pos); | ||
207 | req->wb_list_head = head; | ||
208 | } | ||
209 | |||
210 | static int nfs_wait_bit_interruptible(void *word) | 180 | static int nfs_wait_bit_interruptible(void *word) |
211 | { | 181 | { |
212 | int ret = 0; | 182 | int ret = 0; |
@@ -291,6 +261,62 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst, | |||
291 | return npages; | 261 | return npages; |
292 | } | 262 | } |
293 | 263 | ||
264 | #define NFS_SCAN_MAXENTRIES 16 | ||
265 | /** | ||
266 | * nfs_scan_lock_dirty - Scan the radix tree for dirty requests | ||
267 | * @nfsi: NFS inode | ||
268 | * @dst: Destination list | ||
269 | * @idx_start: lower bound of page->index to scan | ||
270 | * @npages: idx_start + npages sets the upper bound to scan. | ||
271 | * | ||
272 | * Moves elements from one of the inode request lists. | ||
273 | * If the number of requests is set to 0, the entire address_space | ||
274 | * starting at index idx_start, is scanned. | ||
275 | * The requests are *not* checked to ensure that they form a contiguous set. | ||
276 | * You must be holding the inode's req_lock when calling this function | ||
277 | */ | ||
278 | int | ||
279 | nfs_scan_lock_dirty(struct nfs_inode *nfsi, struct list_head *dst, | ||
280 | unsigned long idx_start, unsigned int npages) | ||
281 | { | ||
282 | struct nfs_page *pgvec[NFS_SCAN_MAXENTRIES]; | ||
283 | struct nfs_page *req; | ||
284 | unsigned long idx_end; | ||
285 | int found, i; | ||
286 | int res; | ||
287 | |||
288 | res = 0; | ||
289 | if (npages == 0) | ||
290 | idx_end = ~0; | ||
291 | else | ||
292 | idx_end = idx_start + npages - 1; | ||
293 | |||
294 | for (;;) { | ||
295 | found = radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree, | ||
296 | (void **)&pgvec[0], idx_start, NFS_SCAN_MAXENTRIES, | ||
297 | NFS_PAGE_TAG_DIRTY); | ||
298 | if (found <= 0) | ||
299 | break; | ||
300 | for (i = 0; i < found; i++) { | ||
301 | req = pgvec[i]; | ||
302 | if (req->wb_index > idx_end) | ||
303 | goto out; | ||
304 | |||
305 | idx_start = req->wb_index + 1; | ||
306 | |||
307 | if (nfs_set_page_writeback_locked(req)) { | ||
308 | radix_tree_tag_clear(&nfsi->nfs_page_tree, | ||
309 | req->wb_index, NFS_PAGE_TAG_DIRTY); | ||
310 | nfs_list_remove_request(req); | ||
311 | nfs_list_add_request(req, dst); | ||
312 | res++; | ||
313 | } | ||
314 | } | ||
315 | } | ||
316 | out: | ||
317 | return res; | ||
318 | } | ||
319 | |||
294 | /** | 320 | /** |
295 | * nfs_scan_list - Scan a list for matching requests | 321 | * nfs_scan_list - Scan a list for matching requests |
296 | * @head: One of the NFS inode request lists | 322 | * @head: One of the NFS inode request lists |
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 58a39b0486a7..5130eda231d7 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -352,7 +352,7 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) | |||
352 | if (err < 0) | 352 | if (err < 0) |
353 | goto out; | 353 | goto out; |
354 | } | 354 | } |
355 | err = nfs_commit_inode(inode, 0, 0, wb_priority(wbc)); | 355 | err = nfs_commit_inode(inode, wb_priority(wbc)); |
356 | if (err > 0) { | 356 | if (err > 0) { |
357 | wbc->nr_to_write -= err; | 357 | wbc->nr_to_write -= err; |
358 | err = 0; | 358 | err = 0; |
@@ -446,6 +446,8 @@ nfs_mark_request_dirty(struct nfs_page *req) | |||
446 | struct nfs_inode *nfsi = NFS_I(inode); | 446 | struct nfs_inode *nfsi = NFS_I(inode); |
447 | 447 | ||
448 | spin_lock(&nfsi->req_lock); | 448 | spin_lock(&nfsi->req_lock); |
449 | radix_tree_tag_set(&nfsi->nfs_page_tree, | ||
450 | req->wb_index, NFS_PAGE_TAG_DIRTY); | ||
449 | nfs_list_add_request(req, &nfsi->dirty); | 451 | nfs_list_add_request(req, &nfsi->dirty); |
450 | nfsi->ndirty++; | 452 | nfsi->ndirty++; |
451 | spin_unlock(&nfsi->req_lock); | 453 | spin_unlock(&nfsi->req_lock); |
@@ -537,12 +539,15 @@ static int | |||
537 | nfs_scan_dirty(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages) | 539 | nfs_scan_dirty(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages) |
538 | { | 540 | { |
539 | struct nfs_inode *nfsi = NFS_I(inode); | 541 | struct nfs_inode *nfsi = NFS_I(inode); |
540 | int res; | 542 | int res = 0; |
541 | res = nfs_scan_list(&nfsi->dirty, dst, idx_start, npages); | 543 | |
542 | nfsi->ndirty -= res; | 544 | if (nfsi->ndirty != 0) { |
543 | sub_page_state(nr_dirty,res); | 545 | res = nfs_scan_lock_dirty(nfsi, dst, idx_start, npages); |
544 | if ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty)) | 546 | nfsi->ndirty -= res; |
545 | printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n"); | 547 | sub_page_state(nr_dirty,res); |
548 | if ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty)) | ||
549 | printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n"); | ||
550 | } | ||
546 | return res; | 551 | return res; |
547 | } | 552 | } |
548 | 553 | ||
@@ -561,11 +566,14 @@ static int | |||
561 | nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages) | 566 | nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages) |
562 | { | 567 | { |
563 | struct nfs_inode *nfsi = NFS_I(inode); | 568 | struct nfs_inode *nfsi = NFS_I(inode); |
564 | int res; | 569 | int res = 0; |
565 | res = nfs_scan_list(&nfsi->commit, dst, idx_start, npages); | 570 | |
566 | nfsi->ncommit -= res; | 571 | if (nfsi->ncommit != 0) { |
567 | if ((nfsi->ncommit == 0) != list_empty(&nfsi->commit)) | 572 | res = nfs_scan_list(&nfsi->commit, dst, idx_start, npages); |
568 | printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n"); | 573 | nfsi->ncommit -= res; |
574 | if ((nfsi->ncommit == 0) != list_empty(&nfsi->commit)) | ||
575 | printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n"); | ||
576 | } | ||
569 | return res; | 577 | return res; |
570 | } | 578 | } |
571 | #endif | 579 | #endif |
@@ -1209,36 +1217,24 @@ static void nfs_commit_rpcsetup(struct list_head *head, | |||
1209 | struct nfs_write_data *data, int how) | 1217 | struct nfs_write_data *data, int how) |
1210 | { | 1218 | { |
1211 | struct rpc_task *task = &data->task; | 1219 | struct rpc_task *task = &data->task; |
1212 | struct nfs_page *first, *last; | 1220 | struct nfs_page *first; |
1213 | struct inode *inode; | 1221 | struct inode *inode; |
1214 | loff_t start, end, len; | ||
1215 | 1222 | ||
1216 | /* Set up the RPC argument and reply structs | 1223 | /* Set up the RPC argument and reply structs |
1217 | * NB: take care not to mess about with data->commit et al. */ | 1224 | * NB: take care not to mess about with data->commit et al. */ |
1218 | 1225 | ||
1219 | list_splice_init(head, &data->pages); | 1226 | list_splice_init(head, &data->pages); |
1220 | first = nfs_list_entry(data->pages.next); | 1227 | first = nfs_list_entry(data->pages.next); |
1221 | last = nfs_list_entry(data->pages.prev); | ||
1222 | inode = first->wb_context->dentry->d_inode; | 1228 | inode = first->wb_context->dentry->d_inode; |
1223 | 1229 | ||
1224 | /* | ||
1225 | * Determine the offset range of requests in the COMMIT call. | ||
1226 | * We rely on the fact that data->pages is an ordered list... | ||
1227 | */ | ||
1228 | start = req_offset(first); | ||
1229 | end = req_offset(last) + last->wb_bytes; | ||
1230 | len = end - start; | ||
1231 | /* If 'len' is not a 32-bit quantity, pass '0' in the COMMIT call */ | ||
1232 | if (end >= i_size_read(inode) || len < 0 || len > (~((u32)0) >> 1)) | ||
1233 | len = 0; | ||
1234 | |||
1235 | data->inode = inode; | 1230 | data->inode = inode; |
1236 | data->cred = first->wb_context->cred; | 1231 | data->cred = first->wb_context->cred; |
1237 | 1232 | ||
1238 | data->args.fh = NFS_FH(data->inode); | 1233 | data->args.fh = NFS_FH(data->inode); |
1239 | data->args.offset = start; | 1234 | /* Note: we always request a commit of the entire inode */ |
1240 | data->args.count = len; | 1235 | data->args.offset = 0; |
1241 | data->res.count = len; | 1236 | data->args.count = 0; |
1237 | data->res.count = 0; | ||
1242 | data->res.fattr = &data->fattr; | 1238 | data->res.fattr = &data->fattr; |
1243 | data->res.verf = &data->verf; | 1239 | data->res.verf = &data->verf; |
1244 | 1240 | ||
@@ -1357,8 +1353,7 @@ static int nfs_flush_inode(struct inode *inode, unsigned long idx_start, | |||
1357 | } | 1353 | } |
1358 | 1354 | ||
1359 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) | 1355 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) |
1360 | int nfs_commit_inode(struct inode *inode, unsigned long idx_start, | 1356 | int nfs_commit_inode(struct inode *inode, int how) |
1361 | unsigned int npages, int how) | ||
1362 | { | 1357 | { |
1363 | struct nfs_inode *nfsi = NFS_I(inode); | 1358 | struct nfs_inode *nfsi = NFS_I(inode); |
1364 | LIST_HEAD(head); | 1359 | LIST_HEAD(head); |
@@ -1366,15 +1361,13 @@ int nfs_commit_inode(struct inode *inode, unsigned long idx_start, | |||
1366 | error = 0; | 1361 | error = 0; |
1367 | 1362 | ||
1368 | spin_lock(&nfsi->req_lock); | 1363 | spin_lock(&nfsi->req_lock); |
1369 | res = nfs_scan_commit(inode, &head, idx_start, npages); | 1364 | res = nfs_scan_commit(inode, &head, 0, 0); |
1365 | spin_unlock(&nfsi->req_lock); | ||
1370 | if (res) { | 1366 | if (res) { |
1371 | res += nfs_scan_commit(inode, &head, 0, 0); | ||
1372 | spin_unlock(&nfsi->req_lock); | ||
1373 | error = nfs_commit_list(&head, how); | 1367 | error = nfs_commit_list(&head, how); |
1374 | } else | 1368 | if (error < 0) |
1375 | spin_unlock(&nfsi->req_lock); | 1369 | return error; |
1376 | if (error < 0) | 1370 | } |
1377 | return error; | ||
1378 | return res; | 1371 | return res; |
1379 | } | 1372 | } |
1380 | #endif | 1373 | #endif |
@@ -1396,7 +1389,7 @@ int nfs_sync_inode(struct inode *inode, unsigned long idx_start, | |||
1396 | error = nfs_flush_inode(inode, idx_start, npages, how); | 1389 | error = nfs_flush_inode(inode, idx_start, npages, how); |
1397 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) | 1390 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) |
1398 | if (error == 0) | 1391 | if (error == 0) |
1399 | error = nfs_commit_inode(inode, idx_start, npages, how); | 1392 | error = nfs_commit_inode(inode, how); |
1400 | #endif | 1393 | #endif |
1401 | } while (error > 0); | 1394 | } while (error > 0); |
1402 | return error; | 1395 | return error; |
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 2954e44ed498..8ea249110fb0 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h | |||
@@ -395,10 +395,10 @@ extern void nfs_commit_done(struct rpc_task *); | |||
395 | */ | 395 | */ |
396 | extern int nfs_sync_inode(struct inode *, unsigned long, unsigned int, int); | 396 | extern int nfs_sync_inode(struct inode *, unsigned long, unsigned int, int); |
397 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) | 397 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) |
398 | extern int nfs_commit_inode(struct inode *, unsigned long, unsigned int, int); | 398 | extern int nfs_commit_inode(struct inode *, int); |
399 | #else | 399 | #else |
400 | static inline int | 400 | static inline int |
401 | nfs_commit_inode(struct inode *inode, unsigned long idx_start, unsigned int npages, int how) | 401 | nfs_commit_inode(struct inode *inode, int how) |
402 | { | 402 | { |
403 | return 0; | 403 | return 0; |
404 | } | 404 | } |
diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index db40e4590ba2..da2e077b65e2 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h | |||
@@ -22,6 +22,7 @@ | |||
22 | /* | 22 | /* |
23 | * Valid flags for the radix tree | 23 | * Valid flags for the radix tree |
24 | */ | 24 | */ |
25 | #define NFS_PAGE_TAG_DIRTY 0 | ||
25 | #define NFS_PAGE_TAG_WRITEBACK 1 | 26 | #define NFS_PAGE_TAG_WRITEBACK 1 |
26 | 27 | ||
27 | /* | 28 | /* |
@@ -31,6 +32,7 @@ | |||
31 | #define PG_NEED_COMMIT 1 | 32 | #define PG_NEED_COMMIT 1 |
32 | #define PG_NEED_RESCHED 2 | 33 | #define PG_NEED_RESCHED 2 |
33 | 34 | ||
35 | struct nfs_inode; | ||
34 | struct nfs_page { | 36 | struct nfs_page { |
35 | struct list_head wb_list, /* Defines state of page: */ | 37 | struct list_head wb_list, /* Defines state of page: */ |
36 | *wb_list_head; /* read/write/commit */ | 38 | *wb_list_head; /* read/write/commit */ |
@@ -59,8 +61,8 @@ extern void nfs_clear_request(struct nfs_page *req); | |||
59 | extern void nfs_release_request(struct nfs_page *req); | 61 | extern void nfs_release_request(struct nfs_page *req); |
60 | 62 | ||
61 | 63 | ||
62 | extern void nfs_list_add_request(struct nfs_page *, struct list_head *); | 64 | extern int nfs_scan_lock_dirty(struct nfs_inode *nfsi, struct list_head *dst, |
63 | 65 | unsigned long idx_start, unsigned int npages); | |
64 | extern int nfs_scan_list(struct list_head *, struct list_head *, | 66 | extern int nfs_scan_list(struct list_head *, struct list_head *, |
65 | unsigned long, unsigned int); | 67 | unsigned long, unsigned int); |
66 | extern int nfs_coalesce_requests(struct list_head *, struct list_head *, | 68 | extern int nfs_coalesce_requests(struct list_head *, struct list_head *, |
@@ -94,6 +96,18 @@ nfs_lock_request(struct nfs_page *req) | |||
94 | return 1; | 96 | return 1; |
95 | } | 97 | } |
96 | 98 | ||
99 | /** | ||
100 | * nfs_list_add_request - Insert a request into a list | ||
101 | * @req: request | ||
102 | * @head: head of list into which to insert the request. | ||
103 | */ | ||
104 | static inline void | ||
105 | nfs_list_add_request(struct nfs_page *req, struct list_head *head) | ||
106 | { | ||
107 | list_add_tail(&req->wb_list, head); | ||
108 | req->wb_list_head = head; | ||
109 | } | ||
110 | |||
97 | 111 | ||
98 | /** | 112 | /** |
99 | * nfs_list_remove_request - Remove a request from its wb_list | 113 | * nfs_list_remove_request - Remove a request from its wb_list |