Commit bbc8ea9e authored by Trond Myklebust's avatar Trond Myklebust

NFSv2/v3/v4: When pdflush() is trying to free up memory by calling our

  writepages() method, throttle all writes to that mountpoint.
parent b1355604
...@@ -32,7 +32,7 @@ static inline struct nfs_page * ...@@ -32,7 +32,7 @@ static inline struct nfs_page *
nfs_page_alloc(void) nfs_page_alloc(void)
{ {
struct nfs_page *p; struct nfs_page *p;
p = kmem_cache_alloc(nfs_page_cachep, SLAB_NOFS); p = kmem_cache_alloc(nfs_page_cachep, SLAB_KERNEL);
if (p) { if (p) {
memset(p, 0, sizeof(*p)); memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->wb_list); INIT_LIST_HEAD(&p->wb_list);
......
...@@ -76,11 +76,15 @@ static struct nfs_page * nfs_update_request(struct file*, struct inode *, ...@@ -76,11 +76,15 @@ static struct nfs_page * nfs_update_request(struct file*, struct inode *,
unsigned int, unsigned int); unsigned int, unsigned int);
static void nfs_writeback_done_partial(struct nfs_write_data *, int); static void nfs_writeback_done_partial(struct nfs_write_data *, int);
static void nfs_writeback_done_full(struct nfs_write_data *, int); static void nfs_writeback_done_full(struct nfs_write_data *, 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 kmem_cache_t *nfs_wdata_cachep; static kmem_cache_t *nfs_wdata_cachep;
static mempool_t *nfs_wdata_mempool; static mempool_t *nfs_wdata_mempool;
static mempool_t *nfs_commit_mempool; static mempool_t *nfs_commit_mempool;
static DECLARE_WAIT_QUEUE_HEAD(nfs_write_congestion);
static __inline__ struct nfs_write_data *nfs_writedata_alloc(void) static __inline__ struct nfs_write_data *nfs_writedata_alloc(void)
{ {
struct nfs_write_data *p; struct nfs_write_data *p;
...@@ -259,8 +263,7 @@ static int nfs_writepage_async(struct file *file, struct inode *inode, ...@@ -259,8 +263,7 @@ static int nfs_writepage_async(struct file *file, struct inode *inode,
/* /*
* Write an mmapped page to the server. * Write an mmapped page to the server.
*/ */
int int nfs_writepage(struct page *page, struct writeback_control *wbc)
nfs_writepage(struct page *page, struct writeback_control *wbc)
{ {
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
unsigned long end_index; unsigned long end_index;
...@@ -298,8 +301,11 @@ nfs_writepage(struct page *page, struct writeback_control *wbc) ...@@ -298,8 +301,11 @@ nfs_writepage(struct page *page, struct writeback_control *wbc)
lock_kernel(); lock_kernel();
if (!IS_SYNC(inode) && inode_referenced) { if (!IS_SYNC(inode) && inode_referenced) {
err = nfs_writepage_async(NULL, inode, page, 0, offset); err = nfs_writepage_async(NULL, inode, page, 0, offset);
if (err >= 0) if (err >= 0) {
err = 0; err = 0;
if (wbc->for_reclaim)
err = WRITEPAGE_ACTIVATE;
}
} else { } else {
err = nfs_writepage_sync(NULL, inode, page, 0, offset); err = nfs_writepage_sync(NULL, inode, page, 0, offset);
if (err == offset) if (err == offset)
...@@ -307,32 +313,46 @@ nfs_writepage(struct page *page, struct writeback_control *wbc) ...@@ -307,32 +313,46 @@ nfs_writepage(struct page *page, struct writeback_control *wbc)
} }
unlock_kernel(); unlock_kernel();
out: out:
if (err != WRITEPAGE_ACTIVATE)
unlock_page(page); unlock_page(page);
if (inode_referenced) if (inode_referenced)
iput(inode); iput(inode);
return err; return err;
} }
int /*
nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) * Note: causes nfs_update_request() to block on the assumption
* that the writeback is generated due to memory pressure.
*/
int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
{ {
struct backing_dev_info *bdi = mapping->backing_dev_info;
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
int is_sync = !wbc->nonblocking;
int err; int err;
err = generic_writepages(mapping, wbc); err = generic_writepages(mapping, wbc);
if (err) if (err)
goto out; return err;
while (test_and_set_bit(BDI_write_congested, &bdi->state) != 0) {
if (wbc->nonblocking)
return 0;
nfs_wait_on_write_congestion(mapping, 0);
}
err = nfs_flush_inode(inode, 0, 0, 0); err = nfs_flush_inode(inode, 0, 0, 0);
if (err < 0) if (err < 0)
goto out; goto out;
if (wbc->sync_mode == WB_SYNC_HOLD) wbc->nr_to_write -= err;
if (!wbc->nonblocking && wbc->sync_mode == WB_SYNC_ALL) {
err = nfs_wait_on_requests(inode, 0, 0);
if (err < 0)
goto out; goto out;
if (is_sync && wbc->sync_mode == WB_SYNC_ALL) { }
err = nfs_wb_all(inode); err = nfs_commit_inode(inode, 0, 0, 0);
} else if (err > 0)
nfs_commit_inode(inode, 0, 0, 0); wbc->nr_to_write -= err;
out: out:
clear_bit(BDI_write_congested, &bdi->state);
wake_up_all(&nfs_write_congestion);
return err; return err;
} }
...@@ -544,6 +564,38 @@ nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_st ...@@ -544,6 +564,38 @@ nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_st
} }
#endif #endif
static int nfs_wait_on_write_congestion(struct address_space *mapping, int intr)
{
struct backing_dev_info *bdi = mapping->backing_dev_info;
DEFINE_WAIT(wait);
int ret = 0;
might_sleep();
if (!bdi_write_congested(bdi))
return 0;
if (intr) {
struct rpc_clnt *clnt = NFS_CLIENT(mapping->host);
sigset_t oldset;
rpc_clnt_sigmask(clnt, &oldset);
prepare_to_wait(&nfs_write_congestion, &wait, TASK_INTERRUPTIBLE);
if (bdi_write_congested(bdi)) {
if (signalled())
ret = -ERESTARTSYS;
else
schedule();
}
rpc_clnt_sigunmask(clnt, &oldset);
} else {
prepare_to_wait(&nfs_write_congestion, &wait, TASK_UNINTERRUPTIBLE);
if (bdi_write_congested(bdi))
schedule();
}
finish_wait(&nfs_write_congestion, &wait);
return ret;
}
/* /*
* Try to update any existing write request, or create one if there is none. * Try to update any existing write request, or create one if there is none.
...@@ -556,11 +608,14 @@ static struct nfs_page * ...@@ -556,11 +608,14 @@ static struct nfs_page *
nfs_update_request(struct file* file, struct inode *inode, struct page *page, nfs_update_request(struct file* file, struct inode *inode, struct page *page,
unsigned int offset, unsigned int bytes) unsigned int offset, unsigned int bytes)
{ {
struct nfs_server *server = NFS_SERVER(inode);
struct nfs_page *req, *new = NULL; struct nfs_page *req, *new = NULL;
unsigned long rqend, end; unsigned long rqend, end;
end = offset + bytes; end = offset + bytes;
if (nfs_wait_on_write_congestion(page->mapping, server->flags & NFS_MOUNT_INTR))
return ERR_PTR(-ERESTARTSYS);
for (;;) { for (;;) {
/* Loop over all inode entries and see if we find /* Loop over all inode entries and see if we find
* A request for the page we wish to update * A request for the page we wish to update
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment