aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2005-06-22 13:16:31 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2005-06-22 16:07:39 -0400
commit3da28eb1c6545fe73263a24eba0996217490e1eb (patch)
tree944ccf9418c75a5c0b121f2c554c92dc93de1efa
parentc6a556b88adfacd2af90be84357c8165d716c27d (diff)
[PATCH] NFS: Replace nfs_page insertion sort with a radix sort
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r--fs/nfs/inode.c2
-rw-r--r--fs/nfs/pagelist.c86
-rw-r--r--fs/nfs/write.c71
-rw-r--r--include/linux/nfs_fs.h4
-rw-r--r--include/linux/nfs_page.h18
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 */
190void
191nfs_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
210static int nfs_wait_bit_interruptible(void *word) 180static 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 */
278int
279nfs_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 }
316out:
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
537nfs_scan_dirty(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages) 539nfs_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
561nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages) 566nfs_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)
1360int nfs_commit_inode(struct inode *inode, unsigned long idx_start, 1356int 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 */
396extern int nfs_sync_inode(struct inode *, unsigned long, unsigned int, int); 396extern 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)
398extern int nfs_commit_inode(struct inode *, unsigned long, unsigned int, int); 398extern int nfs_commit_inode(struct inode *, int);
399#else 399#else
400static inline int 400static inline int
401nfs_commit_inode(struct inode *inode, unsigned long idx_start, unsigned int npages, int how) 401nfs_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
35struct nfs_inode;
34struct nfs_page { 36struct 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);
59extern void nfs_release_request(struct nfs_page *req); 61extern void nfs_release_request(struct nfs_page *req);
60 62
61 63
62extern void nfs_list_add_request(struct nfs_page *, struct list_head *); 64extern int nfs_scan_lock_dirty(struct nfs_inode *nfsi, struct list_head *dst,
63 65 unsigned long idx_start, unsigned int npages);
64extern int nfs_scan_list(struct list_head *, struct list_head *, 66extern int nfs_scan_list(struct list_head *, struct list_head *,
65 unsigned long, unsigned int); 67 unsigned long, unsigned int);
66extern int nfs_coalesce_requests(struct list_head *, struct list_head *, 68extern 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 */
104static inline void
105nfs_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