Loading fs/nfs/pagelist.c +51 −25 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include <linux/nfs_page.h> #include <linux/nfs_fs.h> #include <linux/nfs_mount.h> #include <linux/writeback.h> #define NFS_PARANOIA 1 Loading Loading @@ -268,11 +269,10 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst, #define NFS_SCAN_MAXENTRIES 16 /** * nfs_scan_lock_dirty - Scan the radix tree for dirty requests * @nfsi: NFS inode * nfs_scan_dirty - Scan the radix tree for dirty requests * @mapping: pointer to address space * @wbc: writeback_control structure * @dst: Destination list * @idx_start: lower bound of page->index to scan * @npages: idx_start + npages sets the upper bound to scan. * * Moves elements from one of the inode request lists. * If the number of requests is set to 0, the entire address_space Loading @@ -280,46 +280,72 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst, * The requests are *not* checked to ensure that they form a contiguous set. * You must be holding the inode's req_lock when calling this function */ int nfs_scan_lock_dirty(struct nfs_inode *nfsi, struct list_head *dst, unsigned long idx_start, unsigned int npages) long nfs_scan_dirty(struct address_space *mapping, struct writeback_control *wbc, struct list_head *dst) { struct nfs_inode *nfsi = NFS_I(mapping->host); struct nfs_page *pgvec[NFS_SCAN_MAXENTRIES]; struct nfs_page *req; unsigned long idx_end; pgoff_t idx_start, idx_end; long count = wbc->nr_to_write; long res = 0; int found, i; int res; res = 0; if (npages == 0) idx_end = ~0; else idx_end = idx_start + npages - 1; if (nfsi->ndirty == 0 || count <= 0) return 0; if (wbc->range_cyclic) { idx_start = 0; idx_end = ULONG_MAX; } else if (wbc->range_end == 0) { idx_start = wbc->range_start >> PAGE_CACHE_SHIFT; idx_end = ULONG_MAX; } else { idx_start = wbc->range_start >> PAGE_CACHE_SHIFT; idx_end = wbc->range_end >> PAGE_CACHE_SHIFT; } for (;;) { unsigned int toscan = NFS_SCAN_MAXENTRIES; if (toscan > count) toscan = count; found = radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree, (void **)&pgvec[0], idx_start, NFS_SCAN_MAXENTRIES, (void **)&pgvec[0], idx_start, toscan, NFS_PAGE_TAG_DIRTY); /* Did we make progress? */ if (found <= 0) break; for (i = 0; i < found; i++) { req = pgvec[i]; if (req->wb_index > idx_end) if (!wbc->range_cyclic && req->wb_index > idx_end) goto out; idx_start = req->wb_index + 1; if (nfs_set_page_writeback_locked(req)) { /* Try to lock request and mark it for writeback */ if (!nfs_set_page_writeback_locked(req)) goto next; radix_tree_tag_clear(&nfsi->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_DIRTY); nfsi->ndirty--; nfs_list_remove_request(req); nfs_list_add_request(req, dst); dec_zone_page_state(req->wb_page, NR_FILE_DIRTY); res++; } if (res == LONG_MAX) goto out; count--; if (count == 0) goto out; next: idx_start = req->wb_index + 1; } } out: wbc->nr_to_write = count; WARN_ON ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty)); return res; } Loading fs/nfs/write.c +16 −38 Original line number Diff line number Diff line Loading @@ -79,7 +79,7 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context*, unsigned int, unsigned int); static int nfs_wait_on_write_congestion(struct address_space *, int); static int nfs_wait_on_requests(struct inode *, unsigned long, unsigned int); static int nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how); static long nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how); static const struct rpc_call_ops nfs_write_partial_ops; static const struct rpc_call_ops nfs_write_full_ops; static const struct rpc_call_ops nfs_commit_ops; Loading Loading @@ -400,10 +400,8 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) goto out; } err = nfs_commit_inode(inode, wb_priority(wbc)); if (err > 0) { wbc->nr_to_write -= err; if (err > 0) err = 0; } out: clear_bit(BDI_write_congested, &bdi->state); wake_up_all(&nfs_write_congestion); Loading Loading @@ -607,31 +605,6 @@ static void nfs_cancel_commit_list(struct list_head *head) } } /* * nfs_scan_dirty - Scan an inode for dirty requests * @inode: NFS inode to scan * @dst: destination list * @idx_start: lower bound of page->index to scan. * @npages: idx_start + npages sets the upper bound to scan. * * Moves requests from the inode's dirty page list. * The requests are *not* checked to ensure that they form a contiguous set. */ static int nfs_scan_dirty(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages) { struct nfs_inode *nfsi = NFS_I(inode); int res = 0; if (nfsi->ndirty != 0) { res = nfs_scan_lock_dirty(nfsi, dst, idx_start, npages); nfsi->ndirty -= res; if ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty)) printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n"); } return res; } #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) /* * nfs_scan_commit - Scan an inode for commit requests Loading Loading @@ -1467,22 +1440,19 @@ static inline int nfs_commit_list(struct inode *inode, struct list_head *head, i } #endif static int nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how) static long nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how) { struct nfs_inode *nfsi = NFS_I(mapping->host); LIST_HEAD(head); pgoff_t index = wbc->range_start >> PAGE_CACHE_SHIFT; unsigned long npages = 1 + (wbc->range_end >> PAGE_CACHE_SHIFT) - index; int res; long res; spin_lock(&nfsi->req_lock); res = nfs_scan_dirty(mapping->host, &head, index, npages); res = nfs_scan_dirty(mapping, wbc, &head); spin_unlock(&nfsi->req_lock); if (res) { int error = nfs_flush_list(mapping->host, &head, res, how); if (error < 0) return error; wbc->nr_to_write -= res; } return res; } Loading @@ -1506,13 +1476,21 @@ int nfs_commit_inode(struct inode *inode, int how) } #endif int nfs_sync_inode_wait(struct inode *inode, unsigned long idx_start, long nfs_sync_inode_wait(struct inode *inode, unsigned long idx_start, unsigned int npages, int how) { struct nfs_inode *nfsi = NFS_I(inode); struct address_space *mapping = inode->i_mapping; struct writeback_control wbc = { .bdi = mapping->backing_dev_info, .sync_mode = WB_SYNC_ALL, .nr_to_write = LONG_MAX, .range_start = ((loff_t)idx_start) << PAGE_CACHE_SHIFT, .range_end = ((loff_t)(idx_start + npages - 1)) << PAGE_CACHE_SHIFT, }; LIST_HEAD(head); int nocommit = how & FLUSH_NOCOMMIT; int pages, ret; long pages, ret; how &= ~FLUSH_NOCOMMIT; spin_lock(&nfsi->req_lock); Loading @@ -1520,7 +1498,7 @@ int nfs_sync_inode_wait(struct inode *inode, unsigned long idx_start, ret = nfs_wait_on_requests_locked(inode, idx_start, npages); if (ret != 0) continue; pages = nfs_scan_dirty(inode, &head, idx_start, npages); pages = nfs_scan_dirty(mapping, &wbc, &head); if (pages != 0) { spin_unlock(&nfsi->req_lock); if (how & FLUSH_INVALIDATE) { Loading include/linux/nfs_fs.h +1 −1 Original line number Diff line number Diff line Loading @@ -432,7 +432,7 @@ extern void nfs_writedata_release(void *); * Try to write back everything synchronously (but check the * return value!) */ extern int nfs_sync_inode_wait(struct inode *, unsigned long, unsigned int, int); extern long nfs_sync_inode_wait(struct inode *, unsigned long, unsigned int, int); #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) extern int nfs_commit_inode(struct inode *, int); extern struct nfs_write_data *nfs_commit_alloc(void); Loading include/linux/nfs_page.h +3 −2 Original line number Diff line number Diff line Loading @@ -60,8 +60,9 @@ extern void nfs_clear_request(struct nfs_page *req); extern void nfs_release_request(struct nfs_page *req); extern int nfs_scan_lock_dirty(struct nfs_inode *nfsi, struct list_head *dst, unsigned long idx_start, unsigned int npages); extern long nfs_scan_dirty(struct address_space *mapping, struct writeback_control *wbc, struct list_head *dst); extern int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, struct list_head *dst, unsigned long idx_start, unsigned int npages); extern int nfs_coalesce_requests(struct list_head *, struct list_head *, Loading Loading
fs/nfs/pagelist.c +51 −25 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include <linux/nfs_page.h> #include <linux/nfs_fs.h> #include <linux/nfs_mount.h> #include <linux/writeback.h> #define NFS_PARANOIA 1 Loading Loading @@ -268,11 +269,10 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst, #define NFS_SCAN_MAXENTRIES 16 /** * nfs_scan_lock_dirty - Scan the radix tree for dirty requests * @nfsi: NFS inode * nfs_scan_dirty - Scan the radix tree for dirty requests * @mapping: pointer to address space * @wbc: writeback_control structure * @dst: Destination list * @idx_start: lower bound of page->index to scan * @npages: idx_start + npages sets the upper bound to scan. * * Moves elements from one of the inode request lists. * If the number of requests is set to 0, the entire address_space Loading @@ -280,46 +280,72 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst, * The requests are *not* checked to ensure that they form a contiguous set. * You must be holding the inode's req_lock when calling this function */ int nfs_scan_lock_dirty(struct nfs_inode *nfsi, struct list_head *dst, unsigned long idx_start, unsigned int npages) long nfs_scan_dirty(struct address_space *mapping, struct writeback_control *wbc, struct list_head *dst) { struct nfs_inode *nfsi = NFS_I(mapping->host); struct nfs_page *pgvec[NFS_SCAN_MAXENTRIES]; struct nfs_page *req; unsigned long idx_end; pgoff_t idx_start, idx_end; long count = wbc->nr_to_write; long res = 0; int found, i; int res; res = 0; if (npages == 0) idx_end = ~0; else idx_end = idx_start + npages - 1; if (nfsi->ndirty == 0 || count <= 0) return 0; if (wbc->range_cyclic) { idx_start = 0; idx_end = ULONG_MAX; } else if (wbc->range_end == 0) { idx_start = wbc->range_start >> PAGE_CACHE_SHIFT; idx_end = ULONG_MAX; } else { idx_start = wbc->range_start >> PAGE_CACHE_SHIFT; idx_end = wbc->range_end >> PAGE_CACHE_SHIFT; } for (;;) { unsigned int toscan = NFS_SCAN_MAXENTRIES; if (toscan > count) toscan = count; found = radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree, (void **)&pgvec[0], idx_start, NFS_SCAN_MAXENTRIES, (void **)&pgvec[0], idx_start, toscan, NFS_PAGE_TAG_DIRTY); /* Did we make progress? */ if (found <= 0) break; for (i = 0; i < found; i++) { req = pgvec[i]; if (req->wb_index > idx_end) if (!wbc->range_cyclic && req->wb_index > idx_end) goto out; idx_start = req->wb_index + 1; if (nfs_set_page_writeback_locked(req)) { /* Try to lock request and mark it for writeback */ if (!nfs_set_page_writeback_locked(req)) goto next; radix_tree_tag_clear(&nfsi->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_DIRTY); nfsi->ndirty--; nfs_list_remove_request(req); nfs_list_add_request(req, dst); dec_zone_page_state(req->wb_page, NR_FILE_DIRTY); res++; } if (res == LONG_MAX) goto out; count--; if (count == 0) goto out; next: idx_start = req->wb_index + 1; } } out: wbc->nr_to_write = count; WARN_ON ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty)); return res; } Loading
fs/nfs/write.c +16 −38 Original line number Diff line number Diff line Loading @@ -79,7 +79,7 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context*, unsigned int, unsigned int); static int nfs_wait_on_write_congestion(struct address_space *, int); static int nfs_wait_on_requests(struct inode *, unsigned long, unsigned int); static int nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how); static long nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how); static const struct rpc_call_ops nfs_write_partial_ops; static const struct rpc_call_ops nfs_write_full_ops; static const struct rpc_call_ops nfs_commit_ops; Loading Loading @@ -400,10 +400,8 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) goto out; } err = nfs_commit_inode(inode, wb_priority(wbc)); if (err > 0) { wbc->nr_to_write -= err; if (err > 0) err = 0; } out: clear_bit(BDI_write_congested, &bdi->state); wake_up_all(&nfs_write_congestion); Loading Loading @@ -607,31 +605,6 @@ static void nfs_cancel_commit_list(struct list_head *head) } } /* * nfs_scan_dirty - Scan an inode for dirty requests * @inode: NFS inode to scan * @dst: destination list * @idx_start: lower bound of page->index to scan. * @npages: idx_start + npages sets the upper bound to scan. * * Moves requests from the inode's dirty page list. * The requests are *not* checked to ensure that they form a contiguous set. */ static int nfs_scan_dirty(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages) { struct nfs_inode *nfsi = NFS_I(inode); int res = 0; if (nfsi->ndirty != 0) { res = nfs_scan_lock_dirty(nfsi, dst, idx_start, npages); nfsi->ndirty -= res; if ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty)) printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n"); } return res; } #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) /* * nfs_scan_commit - Scan an inode for commit requests Loading Loading @@ -1467,22 +1440,19 @@ static inline int nfs_commit_list(struct inode *inode, struct list_head *head, i } #endif static int nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how) static long nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how) { struct nfs_inode *nfsi = NFS_I(mapping->host); LIST_HEAD(head); pgoff_t index = wbc->range_start >> PAGE_CACHE_SHIFT; unsigned long npages = 1 + (wbc->range_end >> PAGE_CACHE_SHIFT) - index; int res; long res; spin_lock(&nfsi->req_lock); res = nfs_scan_dirty(mapping->host, &head, index, npages); res = nfs_scan_dirty(mapping, wbc, &head); spin_unlock(&nfsi->req_lock); if (res) { int error = nfs_flush_list(mapping->host, &head, res, how); if (error < 0) return error; wbc->nr_to_write -= res; } return res; } Loading @@ -1506,13 +1476,21 @@ int nfs_commit_inode(struct inode *inode, int how) } #endif int nfs_sync_inode_wait(struct inode *inode, unsigned long idx_start, long nfs_sync_inode_wait(struct inode *inode, unsigned long idx_start, unsigned int npages, int how) { struct nfs_inode *nfsi = NFS_I(inode); struct address_space *mapping = inode->i_mapping; struct writeback_control wbc = { .bdi = mapping->backing_dev_info, .sync_mode = WB_SYNC_ALL, .nr_to_write = LONG_MAX, .range_start = ((loff_t)idx_start) << PAGE_CACHE_SHIFT, .range_end = ((loff_t)(idx_start + npages - 1)) << PAGE_CACHE_SHIFT, }; LIST_HEAD(head); int nocommit = how & FLUSH_NOCOMMIT; int pages, ret; long pages, ret; how &= ~FLUSH_NOCOMMIT; spin_lock(&nfsi->req_lock); Loading @@ -1520,7 +1498,7 @@ int nfs_sync_inode_wait(struct inode *inode, unsigned long idx_start, ret = nfs_wait_on_requests_locked(inode, idx_start, npages); if (ret != 0) continue; pages = nfs_scan_dirty(inode, &head, idx_start, npages); pages = nfs_scan_dirty(mapping, &wbc, &head); if (pages != 0) { spin_unlock(&nfsi->req_lock); if (how & FLUSH_INVALIDATE) { Loading
include/linux/nfs_fs.h +1 −1 Original line number Diff line number Diff line Loading @@ -432,7 +432,7 @@ extern void nfs_writedata_release(void *); * Try to write back everything synchronously (but check the * return value!) */ extern int nfs_sync_inode_wait(struct inode *, unsigned long, unsigned int, int); extern long nfs_sync_inode_wait(struct inode *, unsigned long, unsigned int, int); #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) extern int nfs_commit_inode(struct inode *, int); extern struct nfs_write_data *nfs_commit_alloc(void); Loading
include/linux/nfs_page.h +3 −2 Original line number Diff line number Diff line Loading @@ -60,8 +60,9 @@ extern void nfs_clear_request(struct nfs_page *req); extern void nfs_release_request(struct nfs_page *req); extern int nfs_scan_lock_dirty(struct nfs_inode *nfsi, struct list_head *dst, unsigned long idx_start, unsigned int npages); extern long nfs_scan_dirty(struct address_space *mapping, struct writeback_control *wbc, struct list_head *dst); extern int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, struct list_head *dst, unsigned long idx_start, unsigned int npages); extern int nfs_coalesce_requests(struct list_head *, struct list_head *, Loading