/* * linux/fs/nfs/write.c * * Write file data over NFS. * * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de> */ #include <linux/types.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/pagemap.h>#include <linux/file.h>#include <linux/writeback.h>#include <linux/swap.h>#include <linux/migrate.h> #include <linux/sunrpc/clnt.h>#include <linux/nfs_fs.h>#include <linux/nfs_mount.h>#include <linux/nfs_page.h>#include <linux/backing-dev.h>#include <linux/export.h> #include <asm/uaccess.h> #include "delegation.h"#include "internal.h"#include "iostat.h"#include "nfs4_fs.h"#include "fscache.h"#include "pnfs.h" #define NFSDBG_FACILITY NFSDBG_PAGECACHE #define MIN_POOL_WRITE (32)#define MIN_POOL_COMMIT (4) /* * Local function declarations */static void nfs_redirty_request(struct nfs_page *req);static const struct rpc_call_ops nfs_write_common_ops;static const struct rpc_call_ops nfs_commit_ops;static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops;static const struct nfs_commit_completion_ops nfs_commit_completion_ops; static struct kmem_cache *nfs_wdata_cachep;static mempool_t *nfs_wdata_mempool;static struct kmem_cache *nfs_cdata_cachep;static mempool_t *nfs_commit_mempool; struct nfs_commit_data *nfs_commitdata_alloc(void){ struct nfs_commit_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOFS); if (p) { memset(p, 0, sizeof(*p)); INIT_LIST_HEAD(&p->pages); } return p;}EXPORT_SYMBOL_GPL(nfs_commitdata_alloc); void nfs_commit_free(struct nfs_commit_data *p){ mempool_free(p, nfs_commit_mempool);}EXPORT_SYMBOL_GPL(nfs_commit_free); struct nfs_write_header *nfs_writehdr_alloc(void){ struct nfs_write_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS); if (p) { struct nfs_pgio_header *hdr = &p->header; memset(p, 0, sizeof(*p)); INIT_LIST_HEAD(&hdr->pages); INIT_LIST_HEAD(&hdr->rpc_list); spin_lock_init(&hdr->lock); atomic_set(&hdr->refcnt, 0); } return p;} static struct nfs_write_data *nfs_writedata_alloc(struct nfs_pgio_header *hdr, unsigned int pagecount){ struct nfs_write_data *data, *prealloc; prealloc = &container_of(hdr, struct nfs_write_header, header)->rpc_data; if (prealloc->header == NULL) data = prealloc; else data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) goto out; if (nfs_pgarray_set(&data->pages, pagecount)) { data->header = hdr; atomic_inc(&hdr->refcnt); } else { if (data != prealloc) kfree(data); data = NULL; }out: return data;} void nfs_writehdr_free(struct nfs_pgio_header *hdr){ struct nfs_write_header *whdr = container_of(hdr, struct nfs_write_header, header); mempool_free(whdr, nfs_wdata_mempool);} void nfs_writedata_release(struct nfs_write_data *wdata){ struct nfs_pgio_header *hdr = wdata->header; struct nfs_write_header *write_header = container_of(hdr, struct nfs_write_header, header); put_nfs_open_context(wdata->args.context); if (wdata->pages.pagevec != wdata->pages.page_array) kfree(wdata->pages.pagevec); if (wdata != &write_header->rpc_data) kfree(wdata); else wdata->header = NULL; if (atomic_dec_and_test(&hdr->refcnt)) hdr->completion_ops->completion(hdr);} static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error){ ctx->error = error; smp_wmb(); set_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);} static struct nfs_page *nfs_page_find_request_locked(struct page *page){ struct nfs_page *req = NULL; if (PagePrivate(page)) { req = (struct nfs_page *)page_private(page); if (req != NULL) kref_get(&req->wb_kref); } return req;} static struct nfs_page *nfs_page_find_request(struct page *page){ struct inode *inode = page->mapping->host; struct nfs_page *req = NULL; spin_lock(&inode->i_lock); req = nfs_page_find_request_locked(page); spin_unlock(&inode->i_lock); return req;} /* Adjust the file length if we're writing beyond the end */static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count){ struct inode *inode = page->mapping->host; loff_t end, i_size; pgoff_t end_index; spin_lock(&inode->i_lock); i_size = i_size_read(inode); end_index = (i_size - 1) >> PAGE_CACHE_SHIFT; if (i_size > 0 && page->index < end_index) goto out; end = ((loff_t)page->index << PAGE_CACHE_SHIFT) + ((loff_t)offset+count); if (i_size >= end) goto out; i_size_write(inode, end); nfs_inc_stats(inode, NFSIOS_EXTENDWRITE);out: spin_unlock(&inode->i_lock);} /* A writeback failed: mark the page as bad, and invalidate the page cache */static void nfs_set_pageerror(struct page *page){ SetPageError(page); nfs_zap_mapping(page->mapping->host, page->mapping);} /* We can set the PG_uptodate flag if we see that a write request * covers the full page. */static void nfs_mark_uptodate(struct page *page, unsigned int base, unsigned int count){ if (PageUptodate(page)) return; if (base != 0) return; if (count != nfs_page_length(page)) return; SetPageUptodate(page);} static int wb_priority(struct writeback_control *wbc){ if (wbc->for_reclaim) return FLUSH_HIGHPRI | FLUSH_STABLE; if (wbc->for_kupdate || wbc->for_background) return FLUSH_LOWPRI | FLUSH_COND_STABLE; return FLUSH_COND_STABLE;} /* * NFS congestion control */ int nfs_congestion_kb; #define NFS_CONGESTION_ON_THRESH (nfs_congestion_kb >> (PAGE_SHIFT-10))#define NFS_CONGESTION_OFF_THRESH \ (NFS_CONGESTION_ON_THRESH - (NFS_CONGESTION_ON_THRESH >> 2)) static int nfs_set_page_writeback(struct page *page){ int ret = test_set_page_writeback(page); if (!ret) { struct inode *inode = page->mapping->host; struct nfs_server *nfss = NFS_SERVER(inode); if (atomic_long_inc_return(&nfss->writeback) > NFS_CONGESTION_ON_THRESH) { set_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC); } } return ret;} static void nfs_end_page_writeback(struct page *page){ struct inode *inode = page->mapping->host; struct nfs_server *nfss = NFS_SERVER(inode); end_page_writeback(page); if (atomic_long_dec_return(&nfss->writeback) < NFS_CONGESTION_OFF_THRESH) clear_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC);} static struct nfs_page *nfs_find_and_lock_request(struct page *page, bool nonblock){ struct inode *inode = page->mapping->host; struct nfs_page *req; int ret; spin_lock(&inode->i_lock); for (;;) { req = nfs_page_find_request_locked(page); if (req == NULL) break; if (nfs_lock_request(req)) break; /* Note: If we hold the page lock, as is the case in nfs_writepage, * then the call to nfs_lock_request() will always * succeed provided that someone hasn't already marked the * request as dirty (in which case we don't care). */ spin_unlock(&inode->i_lock); if (!nonblock) ret = nfs_wait_on_request(req); else ret = -EAGAIN; nfs_release_request(req); if (ret != 0) return ERR_PTR(ret); spin_lock(&inode->i_lock); } spin_unlock(&inode->i_lock); return req;} /* * Find an associated nfs write request, and prepare to flush it out * May return an error if the user signalled nfs_wait_on_request(). */static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio, struct page *page, bool nonblock){ struct nfs_page *req; int ret = 0; req = nfs_find_and_lock_request(page, nonblock); if (!req) goto out; ret = PTR_ERR(req); if (IS_ERR(req)) goto out; ret = nfs_set_page_writeback(page); BUG_ON(ret != 0); BUG_ON(test_bit(PG_CLEAN, &req->wb_flags)); if (!nfs_pageio_add_request(pgio, req)) { nfs_redirty_request(req); ret = pgio->pg_error; }out: return ret;} static int nfs_do_writepage(struct page *page, struct writeback_control *wbc, struct nfs_pageio_descriptor *pgio){ struct inode *inode = page->mapping->host; int ret; nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGE); nfs_add_stats(inode, NFSIOS_WRITEPAGES, 1); nfs_pageio_cond_complete(pgio, page->index); ret = nfs_page_async_flush(pgio, page, wbc->sync_mode == WB_SYNC_NONE); if (ret == -EAGAIN) { redirty_page_for_writepage(wbc, page); ret = 0; } return ret;} /* * Write an mmapped page to the server. */static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc){ struct nfs_pageio_descriptor pgio; int err; nfs_pageio_init_write(&pgio, page->mapping->host, wb_priority(wbc), &nfs_async_write_completion_ops); err = nfs_do_writepage(page, wbc, &pgio); nfs_pageio_complete(&pgio); if (err < 0) return err; if (pgio.pg_error < 0) return pgio.pg_error; return 0;} int nfs_writepage(struct page *page, struct writeback_control *wbc){ int ret; ret = nfs_writepage_locked(page, wbc); unlock_page(page); return ret;} static int nfs_writepages_callback(struct page *page, struct writeback_control *wbc, void *data){ int ret; ret = nfs_do_writepage(page, wbc, data); unlock_page(page); return ret;} int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc){ struct inode *inode = mapping->host; unsigned long *bitlock = &NFS_I(inode)->flags; struct nfs_pageio_descriptor pgio; int err; /* Stop dirtying of new pages while we sync */ err = wait_on_bit_lock(bitlock, NFS_INO_FLUSHING, nfs_wait_bit_killable, TASK_KILLABLE); if (err) goto out_err; nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES); nfs_pageio_init_write(&pgio, inode, wb_priority(wbc), &nfs_async_write_completion_ops); err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio); nfs_pageio_complete(&pgio); clear_bit_unlock(NFS_INO_FLUSHING, bitlock); smp_mb__after_clear_bit(); wake_up_bit(bitlock, NFS_INO_FLUSHING); if (err < 0) goto out_err; err = pgio.pg_error; if (err < 0) goto out_err; return 0;out_err: return err;} /* * Insert a write request into an inode */static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req){ struct nfs_inode *nfsi = NFS_I(inode); /* Lock the request! */ nfs_lock_request(req); spin_lock(&inode->i_lock); if (!nfsi->npages && nfs_have_delegation(inode, FMODE_WRITE)) inode->i_version++; set_bit(PG_MAPPED, &req->wb_flags); SetPagePrivate(req->wb_page); set_page_private(req->wb_page, (unsigned long)req); nfsi->npages++; kref_get(&req->wb_kref); spin_unlock(&inode->i_lock);} /* * Remove a write request from an inode */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); BUG_ON (!NFS_WBACK_BUSY(req)); spin_lock(&inode->i_lock); set_page_private(req->wb_page, 0); ClearPagePrivate(req->wb_page); clear_bit(PG_MAPPED, &req->wb_flags); nfsi->npages--; spin_unlock(&inode->i_lock); nfs_release_request(req);} static voidnfs_mark_request_dirty(struct nfs_page *req){ __set_page_dirty_nobuffers(req->wb_page);} #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)/** * nfs_request_add_commit_list - add request to a commit list * @req: pointer to a struct nfs_page * @dst: commit list head * @cinfo: holds list lock and accounting info * * This sets the PG_CLEAN bit, updates the cinfo count of * number of outstanding requests requiring a commit as well as * the MM page stats. * * The caller must _not_ hold the cinfo->lock, but must be * holding the nfs_page lock. */voidnfs_request_add_commit_list(struct nfs_page *req, struct list_head *dst, struct nfs_commit_info *cinfo){ set_bit(PG_CLEAN, &(req)->wb_flags); spin_lock(cinfo->lock); nfs_list_add_request(req, dst); cinfo->mds->ncommit++; spin_unlock(cinfo->lock); if (!cinfo->dreq) { 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(req->wb_context->dentry->d_inode, I_DIRTY_DATASYNC); }}EXPORT_SYMBOL_GPL(nfs_request_add_commit_list); /** * nfs_request_remove_commit_list - Remove request from a commit list * @req: pointer to a nfs_page * @cinfo: holds list lock and accounting info * * This clears the PG_CLEAN bit, and updates the cinfo's count of * number of outstanding requests requiring a commit * It does not update the MM page stats. * * The caller _must_ hold the cinfo->lock and the nfs_page lock. */voidnfs_request_remove_commit_list(struct nfs_page *req, struct nfs_commit_info *cinfo){ if (!test_and_clear_bit(PG_CLEAN, &(req)->wb_flags)) return; nfs_list_remove_request(req); cinfo->mds->ncommit--;}EXPORT_SYMBOL_GPL(nfs_request_remove_commit_list);Lines 1–500 of 1851. Load more
/* * linux/fs/nfs/write.c * * Write file data over NFS. * * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de> */ #include <linux/types.h> #include <linux/slab.h> #include <linux/mm.h> #include <linux/pagemap.h> #include <linux/file.h> #include <linux/writeback.h> #include <linux/swap.h> #include <linux/migrate.h> #include <linux/sunrpc/clnt.h> #include <linux/nfs_fs.h> #include <linux/nfs_mount.h> #include <linux/nfs_page.h> #include <linux/backing-dev.h> #include <linux/export.h> #include <asm/uaccess.h> #include "delegation.h" #include "internal.h" #include "iostat.h" #include "nfs4_fs.h" #include "fscache.h" #include "pnfs.h" #define NFSDBG_FACILITY NFSDBG_PAGECACHE #define MIN_POOL_WRITE (32) #define MIN_POOL_COMMIT (4) /* * Local function declarations */ static void nfs_redirty_request(struct nfs_page *req); static const struct rpc_call_ops nfs_write_common_ops; static const struct rpc_call_ops nfs_commit_ops; static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops; static const struct nfs_commit_completion_ops nfs_commit_completion_ops; static struct kmem_cache *nfs_wdata_cachep; static mempool_t *nfs_wdata_mempool; static struct kmem_cache *nfs_cdata_cachep; static mempool_t *nfs_commit_mempool; struct nfs_commit_data *nfs_commitdata_alloc(void) { struct nfs_commit_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOFS); if (p) { memset(p, 0, sizeof(*p)); INIT_LIST_HEAD(&p->pages); } return p; } EXPORT_SYMBOL_GPL(nfs_commitdata_alloc); void nfs_commit_free(struct nfs_commit_data *p) { mempool_free(p, nfs_commit_mempool); } EXPORT_SYMBOL_GPL(nfs_commit_free); struct nfs_write_header *nfs_writehdr_alloc(void) { struct nfs_write_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS); if (p) { struct nfs_pgio_header *hdr = &p->header; memset(p, 0, sizeof(*p)); INIT_LIST_HEAD(&hdr->pages); INIT_LIST_HEAD(&hdr->rpc_list); spin_lock_init(&hdr->lock); atomic_set(&hdr->refcnt, 0); } return p; } static struct nfs_write_data *nfs_writedata_alloc(struct nfs_pgio_header *hdr, unsigned int pagecount) { struct nfs_write_data *data, *prealloc; prealloc = &container_of(hdr, struct nfs_write_header, header)->rpc_data; if (prealloc->header == NULL) data = prealloc; else data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) goto out; if (nfs_pgarray_set(&data->pages, pagecount)) { data->header = hdr; atomic_inc(&hdr->refcnt); } else { if (data != prealloc) kfree(data); data = NULL; } out: return data; } void nfs_writehdr_free(struct nfs_pgio_header *hdr) { struct nfs_write_header *whdr = container_of(hdr, struct nfs_write_header, header); mempool_free(whdr, nfs_wdata_mempool); } void nfs_writedata_release(struct nfs_write_data *wdata) { struct nfs_pgio_header *hdr = wdata->header; struct nfs_write_header *write_header = container_of(hdr, struct nfs_write_header, header); put_nfs_open_context(wdata->args.context); if (wdata->pages.pagevec != wdata->pages.page_array) kfree(wdata->pages.pagevec); if (wdata != &write_header->rpc_data) kfree(wdata); else wdata->header = NULL; if (atomic_dec_and_test(&hdr->refcnt)) hdr->completion_ops->completion(hdr); } static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error) { ctx->error = error; smp_wmb(); set_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); } static struct nfs_page *nfs_page_find_request_locked(struct page *page) { struct nfs_page *req = NULL; if (PagePrivate(page)) { req = (struct nfs_page *)page_private(page); if (req != NULL) kref_get(&req->wb_kref); } return req; } static struct nfs_page *nfs_page_find_request(struct page *page) { struct inode *inode = page->mapping->host; struct nfs_page *req = NULL; spin_lock(&inode->i_lock); req = nfs_page_find_request_locked(page); spin_unlock(&inode->i_lock); return req; } /* Adjust the file length if we're writing beyond the end */ static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count) { struct inode *inode = page->mapping->host; loff_t end, i_size; pgoff_t end_index; spin_lock(&inode->i_lock); i_size = i_size_read(inode); end_index = (i_size - 1) >> PAGE_CACHE_SHIFT; if (i_size > 0 && page->index < end_index) goto out; end = ((loff_t)page->index << PAGE_CACHE_SHIFT) + ((loff_t)offset+count); if (i_size >= end) goto out; i_size_write(inode, end); nfs_inc_stats(inode, NFSIOS_EXTENDWRITE); out: spin_unlock(&inode->i_lock); } /* A writeback failed: mark the page as bad, and invalidate the page cache */ static void nfs_set_pageerror(struct page *page) { SetPageError(page); nfs_zap_mapping(page->mapping->host, page->mapping); } /* We can set the PG_uptodate flag if we see that a write request * covers the full page. */ static void nfs_mark_uptodate(struct page *page, unsigned int base, unsigned int count) { if (PageUptodate(page)) return; if (base != 0) return; if (count != nfs_page_length(page)) return; SetPageUptodate(page); } static int wb_priority(struct writeback_control *wbc) { if (wbc->for_reclaim) return FLUSH_HIGHPRI | FLUSH_STABLE; if (wbc->for_kupdate || wbc->for_background) return FLUSH_LOWPRI | FLUSH_COND_STABLE; return FLUSH_COND_STABLE; } /* * NFS congestion control */ int nfs_congestion_kb; #define NFS_CONGESTION_ON_THRESH (nfs_congestion_kb >> (PAGE_SHIFT-10)) #define NFS_CONGESTION_OFF_THRESH \ (NFS_CONGESTION_ON_THRESH - (NFS_CONGESTION_ON_THRESH >> 2)) static int nfs_set_page_writeback(struct page *page) { int ret = test_set_page_writeback(page); if (!ret) { struct inode *inode = page->mapping->host; struct nfs_server *nfss = NFS_SERVER(inode); if (atomic_long_inc_return(&nfss->writeback) > NFS_CONGESTION_ON_THRESH) { set_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC); } } return ret; } static void nfs_end_page_writeback(struct page *page) { struct inode *inode = page->mapping->host; struct nfs_server *nfss = NFS_SERVER(inode); end_page_writeback(page); if (atomic_long_dec_return(&nfss->writeback) < NFS_CONGESTION_OFF_THRESH) clear_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC); } static struct nfs_page *nfs_find_and_lock_request(struct page *page, bool nonblock) { struct inode *inode = page->mapping->host; struct nfs_page *req; int ret; spin_lock(&inode->i_lock); for (;;) { req = nfs_page_find_request_locked(page); if (req == NULL) break; if (nfs_lock_request(req)) break; /* Note: If we hold the page lock, as is the case in nfs_writepage, * then the call to nfs_lock_request() will always * succeed provided that someone hasn't already marked the * request as dirty (in which case we don't care). */ spin_unlock(&inode->i_lock); if (!nonblock) ret = nfs_wait_on_request(req); else ret = -EAGAIN; nfs_release_request(req); if (ret != 0) return ERR_PTR(ret); spin_lock(&inode->i_lock); } spin_unlock(&inode->i_lock); return req; } /* * Find an associated nfs write request, and prepare to flush it out * May return an error if the user signalled nfs_wait_on_request(). */ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio, struct page *page, bool nonblock) { struct nfs_page *req; int ret = 0; req = nfs_find_and_lock_request(page, nonblock); if (!req) goto out; ret = PTR_ERR(req); if (IS_ERR(req)) goto out; ret = nfs_set_page_writeback(page); BUG_ON(ret != 0); BUG_ON(test_bit(PG_CLEAN, &req->wb_flags)); if (!nfs_pageio_add_request(pgio, req)) { nfs_redirty_request(req); ret = pgio->pg_error; } out: return ret; } static int nfs_do_writepage(struct page *page, struct writeback_control *wbc, struct nfs_pageio_descriptor *pgio) { struct inode *inode = page->mapping->host; int ret; nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGE); nfs_add_stats(inode, NFSIOS_WRITEPAGES, 1); nfs_pageio_cond_complete(pgio, page->index); ret = nfs_page_async_flush(pgio, page, wbc->sync_mode == WB_SYNC_NONE); if (ret == -EAGAIN) { redirty_page_for_writepage(wbc, page); ret = 0; } return ret; } /* * Write an mmapped page to the server. */ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc) { struct nfs_pageio_descriptor pgio; int err; nfs_pageio_init_write(&pgio, page->mapping->host, wb_priority(wbc), &nfs_async_write_completion_ops); err = nfs_do_writepage(page, wbc, &pgio); nfs_pageio_complete(&pgio); if (err < 0) return err; if (pgio.pg_error < 0) return pgio.pg_error; return 0; } int nfs_writepage(struct page *page, struct writeback_control *wbc) { int ret; ret = nfs_writepage_locked(page, wbc); unlock_page(page); return ret; } static int nfs_writepages_callback(struct page *page, struct writeback_control *wbc, void *data) { int ret; ret = nfs_do_writepage(page, wbc, data); unlock_page(page); return ret; } int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) { struct inode *inode = mapping->host; unsigned long *bitlock = &NFS_I(inode)->flags; struct nfs_pageio_descriptor pgio; int err; /* Stop dirtying of new pages while we sync */ err = wait_on_bit_lock(bitlock, NFS_INO_FLUSHING, nfs_wait_bit_killable, TASK_KILLABLE); if (err) goto out_err; nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES); nfs_pageio_init_write(&pgio, inode, wb_priority(wbc), &nfs_async_write_completion_ops); err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio); nfs_pageio_complete(&pgio); clear_bit_unlock(NFS_INO_FLUSHING, bitlock); smp_mb__after_clear_bit(); wake_up_bit(bitlock, NFS_INO_FLUSHING); if (err < 0) goto out_err; err = pgio.pg_error; if (err < 0) goto out_err; return 0; out_err: return err; } /* * Insert a write request into an inode */ static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req) { struct nfs_inode *nfsi = NFS_I(inode); /* Lock the request! */ nfs_lock_request(req); spin_lock(&inode->i_lock); if (!nfsi->npages && nfs_have_delegation(inode, FMODE_WRITE)) inode->i_version++; set_bit(PG_MAPPED, &req->wb_flags); SetPagePrivate(req->wb_page); set_page_private(req->wb_page, (unsigned long)req); nfsi->npages++; kref_get(&req->wb_kref); spin_unlock(&inode->i_lock); } /* * Remove a write request from an inode */ 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); BUG_ON (!NFS_WBACK_BUSY(req)); spin_lock(&inode->i_lock); set_page_private(req->wb_page, 0); ClearPagePrivate(req->wb_page); clear_bit(PG_MAPPED, &req->wb_flags); nfsi->npages--; spin_unlock(&inode->i_lock); nfs_release_request(req); } static void nfs_mark_request_dirty(struct nfs_page *req) { __set_page_dirty_nobuffers(req->wb_page); } #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) /** * nfs_request_add_commit_list - add request to a commit list * @req: pointer to a struct nfs_page * @dst: commit list head * @cinfo: holds list lock and accounting info * * This sets the PG_CLEAN bit, updates the cinfo count of * number of outstanding requests requiring a commit as well as * the MM page stats. * * The caller must _not_ hold the cinfo->lock, but must be * holding the nfs_page lock. */ void nfs_request_add_commit_list(struct nfs_page *req, struct list_head *dst, struct nfs_commit_info *cinfo) { set_bit(PG_CLEAN, &(req)->wb_flags); spin_lock(cinfo->lock); nfs_list_add_request(req, dst); cinfo->mds->ncommit++; spin_unlock(cinfo->lock); if (!cinfo->dreq) { 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(req->wb_context->dentry->d_inode, I_DIRTY_DATASYNC); } } EXPORT_SYMBOL_GPL(nfs_request_add_commit_list); /** * nfs_request_remove_commit_list - Remove request from a commit list * @req: pointer to a nfs_page * @cinfo: holds list lock and accounting info * * This clears the PG_CLEAN bit, and updates the cinfo's count of * number of outstanding requests requiring a commit * It does not update the MM page stats. * * The caller _must_ hold the cinfo->lock and the nfs_page lock. */ void nfs_request_remove_commit_list(struct nfs_page *req, struct nfs_commit_info *cinfo) { if (!test_and_clear_bit(PG_CLEAN, &(req)->wb_flags)) return; nfs_list_remove_request(req); cinfo->mds->ncommit--; } EXPORT_SYMBOL_GPL(nfs_request_remove_commit_list);