Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'nfs-for-3.16-3' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client fixes from Trond Myklebust:
"Apologies for the relative lateness of this pull request, however the
commits fix some issues with the NFS read/write code updates in
3.16-rc1 that can cause serious Oopsing when using small r/wsize. The
delay was mainly due to extra testing to make sure that the fixes
behave correctly.

Highlights include;
- Stable fix for an NFSv3 posix ACL regression
- Multiple fixes for regressions to the NFS generic read/write code:
- Fix page splitting bugs that come into play when a small
rsize/wsize read/write needs to be sent again (due to error
conditions or page redirty)
- Fix nfs_wb_page_cancel, which is called by the "invalidatepage"
method
- Fix 2 compile warnings about unused variables
- Fix a performance issue affecting unstable writes"

* tag 'nfs-for-3.16-3' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
NFS: Don't reset pg_moreio in __nfs_pageio_add_request
NFS: Remove 2 unused variables
nfs: handle multiple reqs in nfs_wb_page_cancel
nfs: handle multiple reqs in nfs_page_async_flush
nfs: change find_request to find_head_request
nfs: nfs_page should take a ref on the head req
nfs: mark nfs_page reqs with flag for extra ref
nfs: only show Posix ACLs in listxattr if actually present

+348 -67
-2
fs/nfs/direct.c
··· 756 756 spin_unlock(&dreq->lock); 757 757 758 758 while (!list_empty(&hdr->pages)) { 759 - bool do_destroy = true; 760 759 761 760 req = nfs_list_entry(hdr->pages.next); 762 761 nfs_list_remove_request(req); ··· 764 765 case NFS_IOHDR_NEED_COMMIT: 765 766 kref_get(&req->wb_kref); 766 767 nfs_mark_request_commit(req, hdr->lseg, &cinfo); 767 - do_destroy = false; 768 768 } 769 769 nfs_unlock_and_release_request(req); 770 770 }
+1
fs/nfs/internal.h
··· 244 244 int nfs_generic_pgio(struct nfs_pageio_descriptor *, struct nfs_pgio_header *); 245 245 int nfs_initiate_pgio(struct rpc_clnt *, struct nfs_pgio_data *, 246 246 const struct rpc_call_ops *, int, int); 247 + void nfs_free_request(struct nfs_page *req); 247 248 248 249 static inline void nfs_iocounter_init(struct nfs_io_counter *c) 249 250 {
+43
fs/nfs/nfs3acl.c
··· 247 247 &posix_acl_default_xattr_handler, 248 248 NULL, 249 249 }; 250 + 251 + static int 252 + nfs3_list_one_acl(struct inode *inode, int type, const char *name, void *data, 253 + size_t size, ssize_t *result) 254 + { 255 + struct posix_acl *acl; 256 + char *p = data + *result; 257 + 258 + acl = get_acl(inode, type); 259 + if (!acl) 260 + return 0; 261 + 262 + posix_acl_release(acl); 263 + 264 + *result += strlen(name); 265 + *result += 1; 266 + if (!size) 267 + return 0; 268 + if (*result > size) 269 + return -ERANGE; 270 + 271 + strcpy(p, name); 272 + return 0; 273 + } 274 + 275 + ssize_t 276 + nfs3_listxattr(struct dentry *dentry, char *data, size_t size) 277 + { 278 + struct inode *inode = dentry->d_inode; 279 + ssize_t result = 0; 280 + int error; 281 + 282 + error = nfs3_list_one_acl(inode, ACL_TYPE_ACCESS, 283 + POSIX_ACL_XATTR_ACCESS, data, size, &result); 284 + if (error) 285 + return error; 286 + 287 + error = nfs3_list_one_acl(inode, ACL_TYPE_DEFAULT, 288 + POSIX_ACL_XATTR_DEFAULT, data, size, &result); 289 + if (error) 290 + return error; 291 + return result; 292 + }
+2 -2
fs/nfs/nfs3proc.c
··· 885 885 .getattr = nfs_getattr, 886 886 .setattr = nfs_setattr, 887 887 #ifdef CONFIG_NFS_V3_ACL 888 - .listxattr = generic_listxattr, 888 + .listxattr = nfs3_listxattr, 889 889 .getxattr = generic_getxattr, 890 890 .setxattr = generic_setxattr, 891 891 .removexattr = generic_removexattr, ··· 899 899 .getattr = nfs_getattr, 900 900 .setattr = nfs_setattr, 901 901 #ifdef CONFIG_NFS_V3_ACL 902 - .listxattr = generic_listxattr, 902 + .listxattr = nfs3_listxattr, 903 903 .getxattr = generic_getxattr, 904 904 .setxattr = generic_setxattr, 905 905 .removexattr = generic_removexattr,
+15 -5
fs/nfs/pagelist.c
··· 29 29 static struct kmem_cache *nfs_page_cachep; 30 30 static const struct rpc_call_ops nfs_pgio_common_ops; 31 31 32 - static void nfs_free_request(struct nfs_page *); 33 - 34 32 static bool nfs_pgarray_set(struct nfs_page_array *p, unsigned int pagecount) 35 33 { 36 34 p->npages = pagecount; ··· 237 239 WARN_ON_ONCE(prev == req); 238 240 239 241 if (!prev) { 242 + /* a head request */ 240 243 req->wb_head = req; 241 244 req->wb_this_page = req; 242 245 } else { 246 + /* a subrequest */ 243 247 WARN_ON_ONCE(prev->wb_this_page != prev->wb_head); 244 248 WARN_ON_ONCE(!test_bit(PG_HEADLOCK, &prev->wb_head->wb_flags)); 245 249 req->wb_head = prev->wb_head; 246 250 req->wb_this_page = prev->wb_this_page; 247 251 prev->wb_this_page = req; 248 252 253 + /* All subrequests take a ref on the head request until 254 + * nfs_page_group_destroy is called */ 255 + kref_get(&req->wb_head->wb_kref); 256 + 249 257 /* grab extra ref if head request has extra ref from 250 258 * the write/commit path to handle handoff between write 251 259 * and commit lists */ 252 - if (test_bit(PG_INODE_REF, &prev->wb_head->wb_flags)) 260 + if (test_bit(PG_INODE_REF, &prev->wb_head->wb_flags)) { 261 + set_bit(PG_INODE_REF, &req->wb_flags); 253 262 kref_get(&req->wb_kref); 263 + } 254 264 } 255 265 } 256 266 ··· 274 268 { 275 269 struct nfs_page *req = container_of(kref, struct nfs_page, wb_kref); 276 270 struct nfs_page *tmp, *next; 271 + 272 + /* subrequests must release the ref on the head request */ 273 + if (req->wb_head != req) 274 + nfs_release_request(req->wb_head); 277 275 278 276 if (!nfs_page_group_sync_on_bit(req, PG_TEARDOWN)) 279 277 return; ··· 404 394 * 405 395 * Note: Should never be called with the spinlock held! 406 396 */ 407 - static void nfs_free_request(struct nfs_page *req) 397 + void nfs_free_request(struct nfs_page *req) 408 398 { 409 399 WARN_ON_ONCE(req->wb_this_page != req); 410 400 ··· 935 925 nfs_pageio_doio(desc); 936 926 if (desc->pg_error < 0) 937 927 return 0; 938 - desc->pg_moreio = 0; 939 928 if (desc->pg_recoalesce) 940 929 return 0; 941 930 /* retry add_request for this subreq */ ··· 981 972 desc->pg_count = 0; 982 973 desc->pg_base = 0; 983 974 desc->pg_recoalesce = 0; 975 + desc->pg_moreio = 0; 984 976 985 977 while (!list_empty(&head)) { 986 978 struct nfs_page *req;
+287 -58
fs/nfs/write.c
··· 46 46 static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops; 47 47 static const struct nfs_commit_completion_ops nfs_commit_completion_ops; 48 48 static const struct nfs_rw_ops nfs_rw_write_ops; 49 + static void nfs_clear_request_commit(struct nfs_page *req); 49 50 50 51 static struct kmem_cache *nfs_wdata_cachep; 51 52 static mempool_t *nfs_wdata_mempool; ··· 92 91 set_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); 93 92 } 94 93 94 + /* 95 + * nfs_page_find_head_request_locked - find head request associated with @page 96 + * 97 + * must be called while holding the inode lock. 98 + * 99 + * returns matching head request with reference held, or NULL if not found. 100 + */ 95 101 static struct nfs_page * 96 - nfs_page_find_request_locked(struct nfs_inode *nfsi, struct page *page) 102 + nfs_page_find_head_request_locked(struct nfs_inode *nfsi, struct page *page) 97 103 { 98 104 struct nfs_page *req = NULL; 99 105 ··· 112 104 /* Linearly search the commit list for the correct req */ 113 105 list_for_each_entry_safe(freq, t, &nfsi->commit_info.list, wb_list) { 114 106 if (freq->wb_page == page) { 115 - req = freq; 107 + req = freq->wb_head; 116 108 break; 117 109 } 118 110 } 119 111 } 120 112 121 - if (req) 113 + if (req) { 114 + WARN_ON_ONCE(req->wb_head != req); 115 + 122 116 kref_get(&req->wb_kref); 117 + } 123 118 124 119 return req; 125 120 } 126 121 127 - static struct nfs_page *nfs_page_find_request(struct page *page) 122 + /* 123 + * nfs_page_find_head_request - find head request associated with @page 124 + * 125 + * returns matching head request with reference held, or NULL if not found. 126 + */ 127 + static struct nfs_page *nfs_page_find_head_request(struct page *page) 128 128 { 129 129 struct inode *inode = page_file_mapping(page)->host; 130 130 struct nfs_page *req = NULL; 131 131 132 132 spin_lock(&inode->i_lock); 133 - req = nfs_page_find_request_locked(NFS_I(inode), page); 133 + req = nfs_page_find_head_request_locked(NFS_I(inode), page); 134 134 spin_unlock(&inode->i_lock); 135 135 return req; 136 136 } ··· 290 274 clear_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC); 291 275 } 292 276 293 - static struct nfs_page *nfs_find_and_lock_request(struct page *page, bool nonblock) 277 + 278 + /* nfs_page_group_clear_bits 279 + * @req - an nfs request 280 + * clears all page group related bits from @req 281 + */ 282 + static void 283 + nfs_page_group_clear_bits(struct nfs_page *req) 294 284 { 295 - struct inode *inode = page_file_mapping(page)->host; 296 - struct nfs_page *req; 285 + clear_bit(PG_TEARDOWN, &req->wb_flags); 286 + clear_bit(PG_UNLOCKPAGE, &req->wb_flags); 287 + clear_bit(PG_UPTODATE, &req->wb_flags); 288 + clear_bit(PG_WB_END, &req->wb_flags); 289 + clear_bit(PG_REMOVE, &req->wb_flags); 290 + } 291 + 292 + 293 + /* 294 + * nfs_unroll_locks_and_wait - unlock all newly locked reqs and wait on @req 295 + * 296 + * this is a helper function for nfs_lock_and_join_requests 297 + * 298 + * @inode - inode associated with request page group, must be holding inode lock 299 + * @head - head request of page group, must be holding head lock 300 + * @req - request that couldn't lock and needs to wait on the req bit lock 301 + * @nonblock - if true, don't actually wait 302 + * 303 + * NOTE: this must be called holding page_group bit lock and inode spin lock 304 + * and BOTH will be released before returning. 305 + * 306 + * returns 0 on success, < 0 on error. 307 + */ 308 + static int 309 + nfs_unroll_locks_and_wait(struct inode *inode, struct nfs_page *head, 310 + struct nfs_page *req, bool nonblock) 311 + __releases(&inode->i_lock) 312 + { 313 + struct nfs_page *tmp; 297 314 int ret; 298 315 299 - spin_lock(&inode->i_lock); 300 - for (;;) { 301 - req = nfs_page_find_request_locked(NFS_I(inode), page); 302 - if (req == NULL) 303 - break; 304 - if (nfs_lock_request(req)) 305 - break; 306 - /* Note: If we hold the page lock, as is the case in nfs_writepage, 307 - * then the call to nfs_lock_request() will always 308 - * succeed provided that someone hasn't already marked the 309 - * request as dirty (in which case we don't care). 310 - */ 311 - spin_unlock(&inode->i_lock); 312 - if (!nonblock) 313 - ret = nfs_wait_on_request(req); 314 - else 315 - ret = -EAGAIN; 316 - nfs_release_request(req); 317 - if (ret != 0) 318 - return ERR_PTR(ret); 319 - spin_lock(&inode->i_lock); 320 - } 316 + /* relinquish all the locks successfully grabbed this run */ 317 + for (tmp = head ; tmp != req; tmp = tmp->wb_this_page) 318 + nfs_unlock_request(tmp); 319 + 320 + WARN_ON_ONCE(test_bit(PG_TEARDOWN, &req->wb_flags)); 321 + 322 + /* grab a ref on the request that will be waited on */ 323 + kref_get(&req->wb_kref); 324 + 325 + nfs_page_group_unlock(head); 321 326 spin_unlock(&inode->i_lock); 322 - return req; 327 + 328 + /* release ref from nfs_page_find_head_request_locked */ 329 + nfs_release_request(head); 330 + 331 + if (!nonblock) 332 + ret = nfs_wait_on_request(req); 333 + else 334 + ret = -EAGAIN; 335 + nfs_release_request(req); 336 + 337 + return ret; 338 + } 339 + 340 + /* 341 + * nfs_destroy_unlinked_subrequests - destroy recently unlinked subrequests 342 + * 343 + * @destroy_list - request list (using wb_this_page) terminated by @old_head 344 + * @old_head - the old head of the list 345 + * 346 + * All subrequests must be locked and removed from all lists, so at this point 347 + * they are only "active" in this function, and possibly in nfs_wait_on_request 348 + * with a reference held by some other context. 349 + */ 350 + static void 351 + nfs_destroy_unlinked_subrequests(struct nfs_page *destroy_list, 352 + struct nfs_page *old_head) 353 + { 354 + while (destroy_list) { 355 + struct nfs_page *subreq = destroy_list; 356 + 357 + destroy_list = (subreq->wb_this_page == old_head) ? 358 + NULL : subreq->wb_this_page; 359 + 360 + WARN_ON_ONCE(old_head != subreq->wb_head); 361 + 362 + /* make sure old group is not used */ 363 + subreq->wb_head = subreq; 364 + subreq->wb_this_page = subreq; 365 + 366 + nfs_clear_request_commit(subreq); 367 + 368 + /* subreq is now totally disconnected from page group or any 369 + * write / commit lists. last chance to wake any waiters */ 370 + nfs_unlock_request(subreq); 371 + 372 + if (!test_bit(PG_TEARDOWN, &subreq->wb_flags)) { 373 + /* release ref on old head request */ 374 + nfs_release_request(old_head); 375 + 376 + nfs_page_group_clear_bits(subreq); 377 + 378 + /* release the PG_INODE_REF reference */ 379 + if (test_and_clear_bit(PG_INODE_REF, &subreq->wb_flags)) 380 + nfs_release_request(subreq); 381 + else 382 + WARN_ON_ONCE(1); 383 + } else { 384 + WARN_ON_ONCE(test_bit(PG_CLEAN, &subreq->wb_flags)); 385 + /* zombie requests have already released the last 386 + * reference and were waiting on the rest of the 387 + * group to complete. Since it's no longer part of a 388 + * group, simply free the request */ 389 + nfs_page_group_clear_bits(subreq); 390 + nfs_free_request(subreq); 391 + } 392 + } 393 + } 394 + 395 + /* 396 + * nfs_lock_and_join_requests - join all subreqs to the head req and return 397 + * a locked reference, cancelling any pending 398 + * operations for this page. 399 + * 400 + * @page - the page used to lookup the "page group" of nfs_page structures 401 + * @nonblock - if true, don't block waiting for request locks 402 + * 403 + * This function joins all sub requests to the head request by first 404 + * locking all requests in the group, cancelling any pending operations 405 + * and finally updating the head request to cover the whole range covered by 406 + * the (former) group. All subrequests are removed from any write or commit 407 + * lists, unlinked from the group and destroyed. 408 + * 409 + * Returns a locked, referenced pointer to the head request - which after 410 + * this call is guaranteed to be the only request associated with the page. 411 + * Returns NULL if no requests are found for @page, or a ERR_PTR if an 412 + * error was encountered. 413 + */ 414 + static struct nfs_page * 415 + nfs_lock_and_join_requests(struct page *page, bool nonblock) 416 + { 417 + struct inode *inode = page_file_mapping(page)->host; 418 + struct nfs_page *head, *subreq; 419 + struct nfs_page *destroy_list = NULL; 420 + unsigned int total_bytes; 421 + int ret; 422 + 423 + try_again: 424 + total_bytes = 0; 425 + 426 + WARN_ON_ONCE(destroy_list); 427 + 428 + spin_lock(&inode->i_lock); 429 + 430 + /* 431 + * A reference is taken only on the head request which acts as a 432 + * reference to the whole page group - the group will not be destroyed 433 + * until the head reference is released. 434 + */ 435 + head = nfs_page_find_head_request_locked(NFS_I(inode), page); 436 + 437 + if (!head) { 438 + spin_unlock(&inode->i_lock); 439 + return NULL; 440 + } 441 + 442 + /* lock each request in the page group */ 443 + nfs_page_group_lock(head); 444 + subreq = head; 445 + do { 446 + /* 447 + * Subrequests are always contiguous, non overlapping 448 + * and in order. If not, it's a programming error. 449 + */ 450 + WARN_ON_ONCE(subreq->wb_offset != 451 + (head->wb_offset + total_bytes)); 452 + 453 + /* keep track of how many bytes this group covers */ 454 + total_bytes += subreq->wb_bytes; 455 + 456 + if (!nfs_lock_request(subreq)) { 457 + /* releases page group bit lock and 458 + * inode spin lock and all references */ 459 + ret = nfs_unroll_locks_and_wait(inode, head, 460 + subreq, nonblock); 461 + 462 + if (ret == 0) 463 + goto try_again; 464 + 465 + return ERR_PTR(ret); 466 + } 467 + 468 + subreq = subreq->wb_this_page; 469 + } while (subreq != head); 470 + 471 + /* Now that all requests are locked, make sure they aren't on any list. 472 + * Commit list removal accounting is done after locks are dropped */ 473 + subreq = head; 474 + do { 475 + nfs_list_remove_request(subreq); 476 + subreq = subreq->wb_this_page; 477 + } while (subreq != head); 478 + 479 + /* unlink subrequests from head, destroy them later */ 480 + if (head->wb_this_page != head) { 481 + /* destroy list will be terminated by head */ 482 + destroy_list = head->wb_this_page; 483 + head->wb_this_page = head; 484 + 485 + /* change head request to cover whole range that 486 + * the former page group covered */ 487 + head->wb_bytes = total_bytes; 488 + } 489 + 490 + /* 491 + * prepare head request to be added to new pgio descriptor 492 + */ 493 + nfs_page_group_clear_bits(head); 494 + 495 + /* 496 + * some part of the group was still on the inode list - otherwise 497 + * the group wouldn't be involved in async write. 498 + * grab a reference for the head request, iff it needs one. 499 + */ 500 + if (!test_and_set_bit(PG_INODE_REF, &head->wb_flags)) 501 + kref_get(&head->wb_kref); 502 + 503 + nfs_page_group_unlock(head); 504 + 505 + /* drop lock to clear_request_commit the head req and clean up 506 + * requests on destroy list */ 507 + spin_unlock(&inode->i_lock); 508 + 509 + nfs_destroy_unlinked_subrequests(destroy_list, head); 510 + 511 + /* clean up commit list state */ 512 + nfs_clear_request_commit(head); 513 + 514 + /* still holds ref on head from nfs_page_find_head_request_locked 515 + * and still has lock on head from lock loop */ 516 + return head; 323 517 } 324 518 325 519 /* ··· 542 316 struct nfs_page *req; 543 317 int ret = 0; 544 318 545 - req = nfs_find_and_lock_request(page, nonblock); 319 + req = nfs_lock_and_join_requests(page, nonblock); 546 320 if (!req) 547 321 goto out; 548 322 ret = PTR_ERR(req); ··· 674 448 set_page_private(req->wb_page, (unsigned long)req); 675 449 } 676 450 nfsi->npages++; 677 - set_bit(PG_INODE_REF, &req->wb_flags); 451 + /* this a head request for a page group - mark it as having an 452 + * extra reference so sub groups can follow suit */ 453 + WARN_ON(test_and_set_bit(PG_INODE_REF, &req->wb_flags)); 678 454 kref_get(&req->wb_kref); 679 455 spin_unlock(&inode->i_lock); 680 456 } ··· 702 474 nfsi->npages--; 703 475 spin_unlock(&inode->i_lock); 704 476 } 705 - nfs_release_request(req); 477 + 478 + if (test_and_clear_bit(PG_INODE_REF, &req->wb_flags)) 479 + nfs_release_request(req); 706 480 } 707 481 708 482 static void ··· 868 638 { 869 639 struct nfs_commit_info cinfo; 870 640 unsigned long bytes = 0; 871 - bool do_destroy; 872 641 873 642 if (test_bit(NFS_IOHDR_REDO, &hdr->flags)) 874 643 goto out; ··· 897 668 next: 898 669 nfs_unlock_request(req); 899 670 nfs_end_page_writeback(req); 900 - do_destroy = !test_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags); 901 671 nfs_release_request(req); 902 672 } 903 673 out: ··· 997 769 spin_lock(&inode->i_lock); 998 770 999 771 for (;;) { 1000 - req = nfs_page_find_request_locked(NFS_I(inode), page); 772 + req = nfs_page_find_head_request_locked(NFS_I(inode), page); 1001 773 if (req == NULL) 1002 774 goto out_unlock; 1003 775 ··· 1105 877 * dropped page. 1106 878 */ 1107 879 do { 1108 - req = nfs_page_find_request(page); 880 + req = nfs_page_find_head_request(page); 1109 881 if (req == NULL) 1110 882 return 0; 1111 883 l_ctx = req->wb_lock_context; ··· 1797 1569 struct nfs_page *req; 1798 1570 int ret = 0; 1799 1571 1800 - for (;;) { 1801 - wait_on_page_writeback(page); 1802 - req = nfs_page_find_request(page); 1803 - if (req == NULL) 1804 - break; 1805 - if (nfs_lock_request(req)) { 1806 - nfs_clear_request_commit(req); 1807 - nfs_inode_remove_request(req); 1808 - /* 1809 - * In case nfs_inode_remove_request has marked the 1810 - * page as being dirty 1811 - */ 1812 - cancel_dirty_page(page, PAGE_CACHE_SIZE); 1813 - nfs_unlock_and_release_request(req); 1814 - break; 1815 - } 1816 - ret = nfs_wait_on_request(req); 1817 - nfs_release_request(req); 1818 - if (ret < 0) 1819 - break; 1572 + wait_on_page_writeback(page); 1573 + 1574 + /* blocking call to cancel all requests and join to a single (head) 1575 + * request */ 1576 + req = nfs_lock_and_join_requests(page, false); 1577 + 1578 + if (IS_ERR(req)) { 1579 + ret = PTR_ERR(req); 1580 + } else if (req) { 1581 + /* all requests from this page have been cancelled by 1582 + * nfs_lock_and_join_requests, so just remove the head 1583 + * request from the inode / page_private pointer and 1584 + * release it */ 1585 + nfs_inode_remove_request(req); 1586 + /* 1587 + * In case nfs_inode_remove_request has marked the 1588 + * page as being dirty 1589 + */ 1590 + cancel_dirty_page(page, PAGE_CACHE_SIZE); 1591 + nfs_unlock_and_release_request(req); 1820 1592 } 1593 + 1821 1594 return ret; 1822 1595 } 1823 1596