static DECLARE_WAIT_QUEUE_HEAD(nfs_write_congestion);
-struct nfs_write_data *nfs_commit_alloc(unsigned int pagecount)
+struct nfs_write_data *nfs_commit_alloc(void)
{
struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, SLAB_NOFS);
if (p) {
memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->pages);
- if (pagecount <= ARRAY_SIZE(p->page_array))
- p->pagevec = p->page_array;
- else {
- p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_NOFS);
- if (!p->pagevec) {
- mempool_free(p, nfs_commit_mempool);
- p = NULL;
- }
- }
}
return p;
}
mempool_free(p, nfs_commit_mempool);
}
-struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount)
+struct nfs_write_data *nfs_writedata_alloc(size_t len)
{
+ unsigned int pagecount = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, SLAB_NOFS);
if (p) {
memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->pages);
+ p->npages = pagecount;
if (pagecount <= ARRAY_SIZE(p->page_array))
p->pagevec = p->page_array;
else {
int result, written = 0;
struct nfs_write_data *wdata;
- wdata = nfs_writedata_alloc(1);
+ wdata = nfs_writedata_alloc(wsize);
if (!wdata)
return -ENOMEM;
out:
clear_bit(BDI_write_congested, &bdi->state);
wake_up_all(&nfs_write_congestion);
+ writeback_congestion_end();
return err;
}
req = nfs_list_entry(head->next);
nfs_list_remove_request(req);
nfs_inode_remove_request(req);
- nfs_clear_page_writeback(req);
dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
+ nfs_clear_page_writeback(req);
}
}
struct nfs_page *req = nfs_list_entry(head->next);
struct page *page = req->wb_page;
struct nfs_write_data *data;
- unsigned int wsize = NFS_SERVER(inode)->wsize;
- unsigned int nbytes, offset;
+ size_t wsize = NFS_SERVER(inode)->wsize, nbytes;
+ unsigned int offset;
int requests = 0;
LIST_HEAD(list);
nfs_list_remove_request(req);
nbytes = req->wb_bytes;
- for (;;) {
- data = nfs_writedata_alloc(1);
+ do {
+ size_t len = min(nbytes, wsize);
+
+ data = nfs_writedata_alloc(len);
if (!data)
goto out_bad;
list_add(&data->pages, &list);
requests++;
- if (nbytes <= wsize)
- break;
- nbytes -= wsize;
- }
+ nbytes -= len;
+ } while (nbytes != 0);
atomic_set(&req->wb_complete, requests);
ClearPageError(page);
struct nfs_write_data *data;
unsigned int count;
- data = nfs_writedata_alloc(NFS_SERVER(inode)->wpages);
+ data = nfs_writedata_alloc(NFS_SERVER(inode)->wsize);
if (!data)
goto out_bad;
dprintk("NFS: %4d nfs_writeback_done (status %d)\n",
task->tk_pid, task->tk_status);
- /* Call the NFS version-specific code */
+ /*
+ * ->write_done will attempt to use post-op attributes to detect
+ * conflicting writes by other clients. A strict interpretation
+ * of close-to-open would allow us to continue caching even if
+ * another writer had changed the file, but some applications
+ * depend on tighter cache coherency when writing.
+ */
status = NFS_PROTO(data->inode)->write_done(task, data);
if (status != 0)
return status;
if (time_before(complain, jiffies)) {
dprintk("NFS: faulty NFS server %s:"
" (committed = %d) != (stable = %d)\n",
- NFS_SERVER(data->inode)->hostname,
+ NFS_SERVER(data->inode)->nfs_client->cl_hostname,
resp->verf->committed, argp->stable);
complain = jiffies + 300 * HZ;
}
struct nfs_write_data *data;
struct nfs_page *req;
- data = nfs_commit_alloc(NFS_SERVER(inode)->wpages);
+ data = nfs_commit_alloc();
if (!data)
goto out_bad;
req = nfs_list_entry(head->next);
nfs_list_remove_request(req);
nfs_mark_request_commit(req);
- nfs_clear_page_writeback(req);
dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
+ nfs_clear_page_writeback(req);
}
return -ENOMEM;
}
{
mempool_destroy(nfs_commit_mempool);
mempool_destroy(nfs_wdata_mempool);
- if (kmem_cache_destroy(nfs_wdata_cachep))
- printk(KERN_INFO "nfs_write_data: not all structures were freed\n");
+ kmem_cache_destroy(nfs_wdata_cachep);
}