#include <linux/smp_lock.h>
#include <linux/mempool.h>
+#include "delegation.h"
+
#define NFSDBG_FACILITY NFSDBG_PAGECACHE
#define MIN_POOL_WRITE (32)
/*
* Local function declarations
*/
-static struct nfs_page * nfs_update_request(struct file*, struct inode *,
+static struct nfs_page * nfs_update_request(struct nfs_open_context*,
+ struct inode *,
struct page *,
unsigned int, unsigned int);
static void nfs_writeback_done_partial(struct nfs_write_data *, int);
* Write a page synchronously.
* Offset is the data offset within the page.
*/
-static int nfs_writepage_sync(struct file *file, struct inode *inode,
+static int nfs_writepage_sync(struct nfs_open_context *ctx, struct inode *inode,
struct page *page, unsigned int offset, unsigned int count,
int how)
{
memset(wdata, 0, sizeof(*wdata));
wdata->flags = how;
+ wdata->cred = ctx->cred;
wdata->inode = inode;
wdata->args.fh = NFS_FH(inode);
- wdata->args.lockowner = current->files;
+ wdata->args.context = ctx;
wdata->args.pages = &page;
wdata->args.stable = NFS_FILE_SYNC;
wdata->args.pgbase = offset;
wdata->args.count = count;
wdata->args.offset = page_offset(page) + wdata->args.pgbase;
- result = NFS_PROTO(inode)->write(wdata, file);
+ result = NFS_PROTO(inode)->write(wdata);
if (result < 0) {
/* Must mark the page invalid after I/O error */
io_error:
nfs_end_data_update_defer(inode);
- if (wdata->cred)
- put_rpccred(wdata->cred);
kfree(wdata);
return written ? written : result;
}
-static int nfs_writepage_async(struct file *file, struct inode *inode,
- struct page *page, unsigned int offset, unsigned int count)
+static int nfs_writepage_async(struct nfs_open_context *ctx,
+ struct inode *inode, struct page *page,
+ unsigned int offset, unsigned int count)
{
struct nfs_page *req;
int status;
- req = nfs_update_request(file, inode, page, offset, count);
+ req = nfs_update_request(ctx, inode, page, offset, count);
status = (IS_ERR(req)) ? PTR_ERR(req) : 0;
if (status < 0)
goto out;
*/
int nfs_writepage(struct page *page, struct writeback_control *wbc)
{
+ struct nfs_open_context *ctx;
struct inode *inode = page->mapping->host;
unsigned long end_index;
unsigned offset = PAGE_CACHE_SIZE;
if (page->index >= end_index+1 || !offset)
goto out;
do_it:
+ ctx = nfs_find_open_context(inode, FMODE_WRITE);
+ if (ctx == NULL) {
+ err = -EBADF;
+ goto out;
+ }
lock_kernel();
if (!IS_SYNC(inode) && inode_referenced) {
- err = nfs_writepage_async(NULL, inode, page, 0, offset);
+ err = nfs_writepage_async(ctx, inode, page, 0, offset);
if (err >= 0) {
err = 0;
if (wbc->for_reclaim)
nfs_flush_inode(inode, 0, 0, FLUSH_STABLE);
}
} else {
- err = nfs_writepage_sync(NULL, inode, page, 0,
+ err = nfs_writepage_sync(ctx, inode, page, 0,
offset, priority);
if (err >= 0) {
if (err != offset)
}
}
unlock_kernel();
+ put_nfs_open_context(ctx);
out:
unlock_page(page);
if (inode_referenced)
/*
* Insert a write request into an inode
*/
-static inline int
-nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
+static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
{
struct nfs_inode *nfsi = NFS_I(inode);
int error;
if (!nfsi->npages) {
igrab(inode);
nfs_begin_data_update(inode);
+ if (nfs_have_delegation(inode, FMODE_WRITE))
+ nfsi->change_attr++;
}
nfsi->npages++;
- req->wb_count++;
+ atomic_inc(&req->wb_count);
return 0;
}
/*
* Insert a write request into an inode
*/
-static void
-nfs_inode_remove_request(struct nfs_page *req)
+static void nfs_inode_remove_request(struct nfs_page *req)
{
- struct nfs_inode *nfsi;
- struct inode *inode;
+ struct inode *inode = req->wb_context->dentry->d_inode;
+ struct nfs_inode *nfsi = NFS_I(inode);
BUG_ON (!NFS_WBACK_BUSY(req));
- spin_lock(&nfs_wreq_lock);
- inode = req->wb_inode;
- nfsi = NFS_I(inode);
+
+ spin_lock(&nfsi->req_lock);
radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index);
nfsi->npages--;
if (!nfsi->npages) {
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
nfs_end_data_update_defer(inode);
iput(inode);
} else
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
nfs_clear_request(req);
nfs_release_request(req);
}
req = (struct nfs_page*)radix_tree_lookup(&nfsi->nfs_page_tree, index);
if (req)
- req->wb_count++;
+ atomic_inc(&req->wb_count);
return req;
}
nfs_find_request(struct inode *inode, unsigned long index)
{
struct nfs_page *req;
+ struct nfs_inode *nfsi = NFS_I(inode);
- spin_lock(&nfs_wreq_lock);
+ spin_lock(&nfsi->req_lock);
req = _nfs_find_request(inode, index);
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
return req;
}
static void
nfs_mark_request_dirty(struct nfs_page *req)
{
- struct inode *inode = req->wb_inode;
+ struct inode *inode = req->wb_context->dentry->d_inode;
struct nfs_inode *nfsi = NFS_I(inode);
- spin_lock(&nfs_wreq_lock);
+ spin_lock(&nfsi->req_lock);
nfs_list_add_request(req, &nfsi->dirty);
nfsi->ndirty++;
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
inc_page_state(nr_dirty);
mark_inode_dirty(inode);
}
static inline int
nfs_dirty_request(struct nfs_page *req)
{
- struct nfs_inode *nfsi = NFS_I(req->wb_inode);
+ struct nfs_inode *nfsi = NFS_I(req->wb_context->dentry->d_inode);
return !list_empty(&req->wb_list) && req->wb_list_head == &nfsi->dirty;
}
static void
nfs_mark_request_commit(struct nfs_page *req)
{
- struct inode *inode = req->wb_inode;
+ struct inode *inode = req->wb_context->dentry->d_inode;
struct nfs_inode *nfsi = NFS_I(inode);
- spin_lock(&nfs_wreq_lock);
+ spin_lock(&nfsi->req_lock);
nfs_list_add_request(req, &nfsi->commit);
nfsi->ncommit++;
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
inc_page_state(nr_unstable);
mark_inode_dirty(inode);
}
else
idx_end = idx_start + npages - 1;
- spin_lock(&nfs_wreq_lock);
+ spin_lock(&nfsi->req_lock);
next = idx_start;
while (radix_tree_gang_lookup(&nfsi->nfs_page_tree, (void **)&req, next, 1)) {
if (req->wb_index > idx_end)
if (!NFS_WBACK_BUSY(req))
continue;
- req->wb_count++;
- spin_unlock(&nfs_wreq_lock);
+ atomic_inc(&req->wb_count);
+ spin_unlock(&nfsi->req_lock);
error = nfs_wait_on_request(req);
nfs_release_request(req);
if (error < 0)
return error;
- spin_lock(&nfs_wreq_lock);
+ spin_lock(&nfsi->req_lock);
res++;
}
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
return res;
}
*
* Note: Should always be called with the Page Lock held!
*/
-static struct nfs_page *
-nfs_update_request(struct file* file, struct inode *inode, struct page *page,
- unsigned int offset, unsigned int bytes)
+static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
+ struct inode *inode, struct page *page,
+ unsigned int offset, unsigned int bytes)
{
struct nfs_server *server = NFS_SERVER(inode);
+ struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_page *req, *new = NULL;
unsigned long rqend, end;
/* Loop over all inode entries and see if we find
* A request for the page we wish to update
*/
- spin_lock(&nfs_wreq_lock);
+ spin_lock(&nfsi->req_lock);
req = _nfs_find_request(inode, page->index);
if (req) {
if (!nfs_lock_request_dontget(req)) {
int error;
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
error = nfs_wait_on_request(req);
nfs_release_request(req);
if (error < 0)
return ERR_PTR(error);
continue;
}
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
if (new)
nfs_release_request(new);
break;
nfs_lock_request_dontget(new);
error = nfs_inode_add_request(inode, new);
if (error) {
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
nfs_unlock_request(new);
return ERR_PTR(error);
}
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
nfs_mark_request_dirty(new);
return new;
}
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
- new = nfs_create_request(file, inode, page, offset, bytes);
+ new = nfs_create_request(ctx, inode, page, offset, bytes);
if (IS_ERR(new))
return new;
- if (file) {
- new->wb_file = file;
- get_file(file);
- }
}
/* We have a request for our page.
* request.
*/
rqend = req->wb_offset + req->wb_bytes;
- if (req->wb_file != file
+ if (req->wb_context != ctx
|| req->wb_page != page
|| !nfs_dirty_request(req)
|| offset > rqend || end < req->wb_offset) {
return req;
}
-int
-nfs_flush_incompatible(struct file *file, struct page *page)
+int nfs_flush_incompatible(struct file *file, struct page *page)
{
+ struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
struct inode *inode = page->mapping->host;
struct nfs_page *req;
int status = 0;
*/
req = nfs_find_request(inode, page->index);
if (req) {
- if (!NFS_PROTO(inode)->request_compatible(req, file, page))
+ if (req->wb_page != page || ctx != req->wb_context)
status = nfs_wb_page(inode, page);
nfs_release_request(req);
}
int nfs_updatepage(struct file *file, struct page *page,
unsigned int offset, unsigned int count)
{
+ struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
struct dentry *dentry = file->f_dentry;
struct inode *inode = page->mapping->host;
struct nfs_page *req;
count, (long long)(page_offset(page) +offset));
if (IS_SYNC(inode)) {
- status = nfs_writepage_sync(file, inode, page, offset, count, 0);
+ status = nfs_writepage_sync(ctx, inode, page, offset, count, 0);
if (status > 0) {
if (offset == 0 && status == PAGE_CACHE_SIZE)
SetPageUptodate(page);
* it out now.
*/
do {
- req = nfs_update_request(file, inode, page, offset, count);
+ req = nfs_update_request(ctx, inode, page, offset, count);
status = (IS_ERR(req)) ? PTR_ERR(req) : 0;
if (status != -EBUSY)
break;
* NB: take care not to mess about with data->commit et al. */
data->req = req;
- data->inode = inode = req->wb_inode;
- data->cred = req->wb_cred;
+ data->inode = inode = req->wb_context->dentry->d_inode;
+ data->cred = req->wb_context->cred;
data->args.fh = NFS_FH(inode);
data->args.offset = req_offset(req) + offset;
data->args.pgbase = req->wb_pgbase + offset;
data->args.pages = data->pagevec;
data->args.count = count;
- data->args.lockowner = req->wb_lockowner;
- data->args.state = req->wb_state;
+ data->args.context = req->wb_context;
data->res.fattr = &data->fattr;
data->res.count = count;
while (!list_empty(head)) {
pages += nfs_coalesce_requests(head, &one_request, wpages);
req = nfs_list_entry(one_request.next);
- error = nfs_flush_one(&one_request, req->wb_inode, how);
+ error = nfs_flush_one(&one_request, req->wb_context->dentry->d_inode, how);
if (error < 0)
break;
}
struct page *page = req->wb_page;
dprintk("NFS: write (%s/%Ld %d@%Ld)",
- req->wb_inode->i_sb->s_id,
- (long long)NFS_FILEID(req->wb_inode),
+ req->wb_context->dentry->d_inode->i_sb->s_id,
+ (long long)NFS_FILEID(req->wb_context->dentry->d_inode),
req->wb_bytes,
(long long)req_offset(req));
if (status < 0) {
ClearPageUptodate(page);
SetPageError(page);
- if (req->wb_file)
- req->wb_file->f_error = status;
+ req->wb_context->error = status;
dprintk(", error = %d\n", status);
} else {
#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
page = req->wb_page;
dprintk("NFS: write (%s/%Ld %d@%Ld)",
- req->wb_inode->i_sb->s_id,
- (long long)NFS_FILEID(req->wb_inode),
+ req->wb_context->dentry->d_inode->i_sb->s_id,
+ (long long)NFS_FILEID(req->wb_context->dentry->d_inode),
req->wb_bytes,
(long long)req_offset(req));
if (status < 0) {
ClearPageUptodate(page);
SetPageError(page);
- if (req->wb_file)
- req->wb_file->f_error = status;
+ req->wb_context->error = status;
end_page_writeback(page);
nfs_inode_remove_request(req);
dprintk(", error = %d\n", status);
list_splice_init(head, &data->pages);
first = nfs_list_entry(data->pages.next);
last = nfs_list_entry(data->pages.prev);
- inode = first->wb_inode;
+ inode = first->wb_context->dentry->d_inode;
/*
* Determine the offset range of requests in the COMMIT call.
len = 0;
data->inode = inode;
- data->cred = first->wb_cred;
+ data->cred = first->wb_context->cred;
data->args.fh = NFS_FH(data->inode);
data->args.offset = start;
nfs_list_remove_request(req);
dprintk("NFS: commit (%s/%Ld %d@%Ld)",
- req->wb_inode->i_sb->s_id,
- (long long)NFS_FILEID(req->wb_inode),
+ req->wb_context->dentry->d_inode->i_sb->s_id,
+ (long long)NFS_FILEID(req->wb_context->dentry->d_inode),
req->wb_bytes,
(long long)req_offset(req));
if (task->tk_status < 0) {
- if (req->wb_file)
- req->wb_file->f_error = task->tk_status;
+ req->wb_context->error = task->tk_status;
nfs_inode_remove_request(req);
dprintk(", error = %d\n", task->tk_status);
goto next;
int nfs_flush_inode(struct inode *inode, unsigned long idx_start,
unsigned int npages, int how)
{
+ struct nfs_inode *nfsi = NFS_I(inode);
LIST_HEAD(head);
int res,
error = 0;
- spin_lock(&nfs_wreq_lock);
+ spin_lock(&nfsi->req_lock);
res = nfs_scan_dirty(inode, &head, idx_start, npages);
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
if (res)
error = nfs_flush_list(&head, NFS_SERVER(inode)->wpages, how);
if (error < 0)
int nfs_commit_inode(struct inode *inode, unsigned long idx_start,
unsigned int npages, int how)
{
+ struct nfs_inode *nfsi = NFS_I(inode);
LIST_HEAD(head);
int res,
error = 0;
- spin_lock(&nfs_wreq_lock);
+ spin_lock(&nfsi->req_lock);
res = nfs_scan_commit(inode, &head, idx_start, npages);
if (res) {
res += nfs_scan_commit(inode, &head, 0, 0);
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
error = nfs_commit_list(&head, how);
} else
- spin_unlock(&nfs_wreq_lock);
+ spin_unlock(&nfsi->req_lock);
if (error < 0)
return error;
return res;