From: Linus Torvalds Date: Tue, 10 Jun 2014 22:02:42 +0000 (-0700) Subject: Merge tag 'nfs-for-3.16-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs X-Git-Tag: v3.16-rc1~55 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d1e1cda862c16252087374ac75949b0e89a5717e;p=thirdparty%2Flinux.git Merge tag 'nfs-for-3.16-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs Pull NFS client updates from Trond Myklebust: "Highlights include: - massive cleanup of the NFS read/write code by Anna and Dros - support multiple NFS read/write requests per page in order to deal with non-page aligned pNFS striping. Also cleans up the r/wsize < page size code nicely. - stable fix for ensuring inode is declared uptodate only after all the attributes have been checked. - stable fix for a kernel Oops when remounting - NFS over RDMA client fixes - move the pNFS files layout driver into its own subdirectory" * tag 'nfs-for-3.16-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (79 commits) NFS: populate ->net in mount data when remounting pnfs: fix lockup caused by pnfs_generic_pg_test NFSv4.1: Fix typo in dprintk NFSv4.1: Comment is now wrong and redundant to code NFS: Use raw_write_seqcount_begin/end int nfs4_reclaim_open_state xprtrdma: Disconnect on registration failure xprtrdma: Remove BUG_ON() call sites xprtrdma: Avoid deadlock when credit window is reset SUNRPC: Move congestion window constants to header file xprtrdma: Reset connection timeout after successful reconnect xprtrdma: Use macros for reconnection timeout constants xprtrdma: Allocate missing pagelist xprtrdma: Remove Tavor MTU setting xprtrdma: Ensure ia->ri_id->qp is not NULL when reconnecting xprtrdma: Reduce the number of hardway buffer allocations xprtrdma: Limit work done by completion handler xprtrmda: Reduce calls to ib_poll_cq() in completion handlers xprtrmda: Reduce lock contention in completion handlers xprtrdma: Split the completion queue xprtrdma: Make rpcrdma_ep_destroy() return void ... --- d1e1cda862c16252087374ac75949b0e89a5717e diff --cc fs/nfs/pagelist.c index 03ed984ab4d80,a8759825ac764..b6ee3a6ee96dd --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@@ -133,6 -138,151 +138,151 @@@ nfs_iocounter_wait(struct nfs_io_counte return __nfs_iocounter_wait(c); } + static int nfs_wait_bit_uninterruptible(void *word) + { + io_schedule(); + return 0; + } + + /* + * nfs_page_group_lock - lock the head of the page group + * @req - request in group that is to be locked + * + * this lock must be held if modifying the page group list + */ + void + nfs_page_group_lock(struct nfs_page *req) + { + struct nfs_page *head = req->wb_head; + + WARN_ON_ONCE(head != head->wb_head); + + wait_on_bit_lock(&head->wb_flags, PG_HEADLOCK, + nfs_wait_bit_uninterruptible, + TASK_UNINTERRUPTIBLE); + } + + /* + * nfs_page_group_unlock - unlock the head of the page group + * @req - request in group that is to be unlocked + */ + void + nfs_page_group_unlock(struct nfs_page *req) + { + struct nfs_page *head = req->wb_head; + + WARN_ON_ONCE(head != head->wb_head); + - smp_mb__before_clear_bit(); ++ smp_mb__before_atomic(); + clear_bit(PG_HEADLOCK, &head->wb_flags); - smp_mb__after_clear_bit(); ++ smp_mb__after_atomic(); + wake_up_bit(&head->wb_flags, PG_HEADLOCK); + } + + /* + * nfs_page_group_sync_on_bit_locked + * + * must be called with page group lock held + */ + static bool + nfs_page_group_sync_on_bit_locked(struct nfs_page *req, unsigned int bit) + { + struct nfs_page *head = req->wb_head; + struct nfs_page *tmp; + + WARN_ON_ONCE(!test_bit(PG_HEADLOCK, &head->wb_flags)); + WARN_ON_ONCE(test_and_set_bit(bit, &req->wb_flags)); + + tmp = req->wb_this_page; + while (tmp != req) { + if (!test_bit(bit, &tmp->wb_flags)) + return false; + tmp = tmp->wb_this_page; + } + + /* true! reset all bits */ + tmp = req; + do { + clear_bit(bit, &tmp->wb_flags); + tmp = tmp->wb_this_page; + } while (tmp != req); + + return true; + } + + /* + * nfs_page_group_sync_on_bit - set bit on current request, but only + * return true if the bit is set for all requests in page group + * @req - request in page group + * @bit - PG_* bit that is used to sync page group + */ + bool nfs_page_group_sync_on_bit(struct nfs_page *req, unsigned int bit) + { + bool ret; + + nfs_page_group_lock(req); + ret = nfs_page_group_sync_on_bit_locked(req, bit); + nfs_page_group_unlock(req); + + return ret; + } + + /* + * nfs_page_group_init - Initialize the page group linkage for @req + * @req - a new nfs request + * @prev - the previous request in page group, or NULL if @req is the first + * or only request in the group (the head). + */ + static inline void + nfs_page_group_init(struct nfs_page *req, struct nfs_page *prev) + { + WARN_ON_ONCE(prev == req); + + if (!prev) { + req->wb_head = req; + req->wb_this_page = req; + } else { + WARN_ON_ONCE(prev->wb_this_page != prev->wb_head); + WARN_ON_ONCE(!test_bit(PG_HEADLOCK, &prev->wb_head->wb_flags)); + req->wb_head = prev->wb_head; + req->wb_this_page = prev->wb_this_page; + prev->wb_this_page = req; + + /* grab extra ref if head request has extra ref from + * the write/commit path to handle handoff between write + * and commit lists */ + if (test_bit(PG_INODE_REF, &prev->wb_head->wb_flags)) + kref_get(&req->wb_kref); + } + } + + /* + * nfs_page_group_destroy - sync the destruction of page groups + * @req - request that no longer needs the page group + * + * releases the page group reference from each member once all + * members have called this function. + */ + static void + nfs_page_group_destroy(struct kref *kref) + { + struct nfs_page *req = container_of(kref, struct nfs_page, wb_kref); + struct nfs_page *tmp, *next; + + if (!nfs_page_group_sync_on_bit(req, PG_TEARDOWN)) + return; + + tmp = req; + do { + next = tmp->wb_this_page; + /* unlink and free */ + tmp->wb_this_page = tmp; + tmp->wb_head = tmp; + nfs_free_request(tmp); + tmp = next; + } while (tmp != req); + } + /** * nfs_create_request - Create an NFS read/write request. * @ctx: open context to use