diff options
| author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2005-06-22 13:16:31 -0400 |
|---|---|---|
| committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2005-06-22 16:07:39 -0400 |
| commit | 3da28eb1c6545fe73263a24eba0996217490e1eb (patch) | |
| tree | 944ccf9418c75a5c0b121f2c554c92dc93de1efa | |
| parent | c6a556b88adfacd2af90be84357c8165d716c27d (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.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 |
