summaryrefslogtreecommitdiffstats
path: root/fs/nfs/write.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs/write.c')
-rw-r--r--fs/nfs/write.c120
1 files changed, 67 insertions, 53 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index fd8a4f07bc0..a630ad65d64 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -375,21 +375,14 @@ out_err:
/*
* Insert a write request into an inode
*/
-static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
+static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
{
struct nfs_inode *nfsi = NFS_I(inode);
- int error;
-
- error = radix_tree_preload(GFP_NOFS);
- if (error != 0)
- goto out;
/* Lock the request! */
nfs_lock_request_dontget(req);
spin_lock(&inode->i_lock);
- error = radix_tree_insert(&nfsi->nfs_page_tree, req->wb_index, req);
- BUG_ON(error);
if (!nfsi->npages && nfs_have_delegation(inode, FMODE_WRITE))
inode->i_version++;
set_bit(PG_MAPPED, &req->wb_flags);
@@ -398,11 +391,10 @@ static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
nfsi->npages++;
kref_get(&req->wb_kref);
spin_unlock(&inode->i_lock);
- radix_tree_preload_end();
-out:
- return error;
}
+static struct pnfs_layout_segment *nfs_clear_request_commit(struct nfs_page *req);
+
/*
* Remove a write request from an inode
*/
@@ -410,16 +402,18 @@ static void nfs_inode_remove_request(struct nfs_page *req)
{
struct inode *inode = req->wb_context->dentry->d_inode;
struct nfs_inode *nfsi = NFS_I(inode);
+ struct pnfs_layout_segment *lseg;
BUG_ON (!NFS_WBACK_BUSY(req));
spin_lock(&inode->i_lock);
+ lseg = nfs_clear_request_commit(req);
set_page_private(req->wb_page, 0);
ClearPagePrivate(req->wb_page);
clear_bit(PG_MAPPED, &req->wb_flags);
- radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index);
nfsi->npages--;
spin_unlock(&inode->i_lock);
+ put_lseg(lseg);
nfs_release_request(req);
}
@@ -438,31 +432,38 @@ nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg)
{
struct inode *inode = req->wb_context->dentry->d_inode;
struct nfs_inode *nfsi = NFS_I(inode);
+ struct list_head *clist;
+ clist = pnfs_choose_commit_list(req, lseg);
spin_lock(&inode->i_lock);
set_bit(PG_CLEAN, &(req)->wb_flags);
- radix_tree_tag_set(&nfsi->nfs_page_tree,
- req->wb_index,
- NFS_PAGE_TAG_COMMIT);
+ nfs_list_add_request(req, clist);
nfsi->ncommit++;
spin_unlock(&inode->i_lock);
- pnfs_mark_request_commit(req, lseg);
inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
inc_bdi_stat(req->wb_page->mapping->backing_dev_info, BDI_RECLAIMABLE);
__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
}
-static int
+static void
+nfs_clear_page_commit(struct page *page)
+{
+ dec_zone_page_state(page, NR_UNSTABLE_NFS);
+ dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE);
+}
+
+static struct pnfs_layout_segment *
nfs_clear_request_commit(struct nfs_page *req)
{
- struct page *page = req->wb_page;
+ struct pnfs_layout_segment *lseg = NULL;
if (test_and_clear_bit(PG_CLEAN, &(req)->wb_flags)) {
- dec_zone_page_state(page, NR_UNSTABLE_NFS);
- dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE);
- return 1;
+ nfs_clear_page_commit(req->wb_page);
+ lseg = pnfs_clear_request_commit(req);
+ NFS_I(req->wb_context->dentry->d_inode)->ncommit--;
+ list_del(&req->wb_list);
}
- return 0;
+ return lseg;
}
static inline
@@ -494,10 +495,10 @@ nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg)
{
}
-static inline int
+static inline struct pnfs_layout_segment *
nfs_clear_request_commit(struct nfs_page *req)
{
- return 0;
+ return NULL;
}
static inline
@@ -518,46 +519,67 @@ int nfs_reschedule_unstable_write(struct nfs_page *req,
static int
nfs_need_commit(struct nfs_inode *nfsi)
{
- return radix_tree_tagged(&nfsi->nfs_page_tree, NFS_PAGE_TAG_COMMIT);
+ return nfsi->ncommit > 0;
}
+/* i_lock held by caller */
+int
+nfs_scan_commit_list(struct list_head *src, struct list_head *dst, int max)
+{
+ struct nfs_page *req, *tmp;
+ int ret = 0;
+
+ list_for_each_entry_safe(req, tmp, src, wb_list) {
+ if (nfs_lock_request_dontget(req)) {
+ kref_get(&req->wb_kref);
+ list_move_tail(&req->wb_list, dst);
+ clear_bit(PG_CLEAN, &(req)->wb_flags);
+ ret++;
+ if (ret == max)
+ break;
+ }
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nfs_scan_commit_list);
+
/*
* nfs_scan_commit - Scan an inode for commit 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 'commit' request list.
* The requests are *not* checked to ensure that they form a contiguous set.
*/
static int
-nfs_scan_commit(struct inode *inode, struct list_head *dst, pgoff_t idx_start, unsigned int npages)
+nfs_scan_commit(struct inode *inode, struct list_head *dst)
{
struct nfs_inode *nfsi = NFS_I(inode);
- int ret;
-
- if (!nfs_need_commit(nfsi))
- return 0;
+ int ret = 0;
spin_lock(&inode->i_lock);
- ret = nfs_scan_list(nfsi, dst, idx_start, npages, NFS_PAGE_TAG_COMMIT);
- if (ret > 0)
+ if (nfsi->ncommit > 0) {
+ int pnfs_ret;
+
+ ret = nfs_scan_commit_list(&nfsi->commit_list, dst, INT_MAX);
+ pnfs_ret = pnfs_scan_commit_lists(inode, INT_MAX - ret);
+ if (pnfs_ret) {
+ ret += pnfs_ret;
+ set_bit(NFS_INO_PNFS_COMMIT, &nfsi->flags);
+ }
nfsi->ncommit -= ret;
+ }
spin_unlock(&inode->i_lock);
-
- if (nfs_need_commit(NFS_I(inode)))
- __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
-
return ret;
}
+
#else
static inline int nfs_need_commit(struct nfs_inode *nfsi)
{
return 0;
}
-static inline int nfs_scan_commit(struct inode *inode, struct list_head *dst, pgoff_t idx_start, unsigned int npages)
+static inline int nfs_scan_commit(struct inode *inode, struct list_head *dst)
{
return 0;
}
@@ -579,6 +601,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
unsigned int rqend;
unsigned int end;
int error;
+ struct pnfs_layout_segment *lseg = NULL;
if (!PagePrivate(page))
return NULL;
@@ -614,12 +637,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
spin_lock(&inode->i_lock);
}
- if (nfs_clear_request_commit(req) &&
- radix_tree_tag_clear(&NFS_I(inode)->nfs_page_tree,
- req->wb_index, NFS_PAGE_TAG_COMMIT) != NULL) {
- NFS_I(inode)->ncommit--;
- pnfs_clear_request_commit(req);
- }
+ lseg = nfs_clear_request_commit(req);
/* Okay, the request matches. Update the region */
if (offset < req->wb_offset) {
@@ -632,6 +650,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
req->wb_bytes = rqend - req->wb_offset;
out_unlock:
spin_unlock(&inode->i_lock);
+ put_lseg(lseg);
return req;
out_flushme:
spin_unlock(&inode->i_lock);
@@ -653,7 +672,6 @@ static struct nfs_page * nfs_setup_write_request(struct nfs_open_context* ctx,
{
struct inode *inode = page->mapping->host;
struct nfs_page *req;
- int error;
req = nfs_try_to_update_request(inode, page, offset, bytes);
if (req != NULL)
@@ -661,11 +679,7 @@ static struct nfs_page * nfs_setup_write_request(struct nfs_open_context* ctx,
req = nfs_create_request(ctx, inode, page, offset, bytes);
if (IS_ERR(req))
goto out;
- error = nfs_inode_add_request(inode, req);
- if (error != 0) {
- nfs_release_request(req);
- req = ERR_PTR(error);
- }
+ nfs_inode_add_request(inode, req);
out:
return req;
}
@@ -1458,7 +1472,7 @@ void nfs_commit_release_pages(struct nfs_write_data *data)
while (!list_empty(&data->pages)) {
req = nfs_list_entry(data->pages.next);
nfs_list_remove_request(req);
- nfs_clear_request_commit(req);
+ nfs_clear_page_commit(req->wb_page);
dprintk("NFS: commit (%s/%lld %d@%lld)",
req->wb_context->dentry->d_sb->s_id,
@@ -1515,7 +1529,7 @@ int nfs_commit_inode(struct inode *inode, int how)
res = nfs_commit_set_lock(NFS_I(inode), may_wait);
if (res <= 0)
goto out_mark_dirty;
- res = nfs_scan_commit(inode, &head, 0, 0);
+ res = nfs_scan_commit(inode, &head);
if (res) {
int error;