X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fnfs%2Fwrite.c;h=847b77d4c7aedd099f8a2abe0f0971713cdcccab;hb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;hp=a01a2fa0a598dcc8f99efb27da75fd60a728d833;hpb=a2c21200f1c81b08cb55e417b68150bba439b646;p=linux-2.6.git diff --git a/fs/nfs/write.c b/fs/nfs/write.c index a01a2fa0a..847b77d4c 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -63,6 +63,8 @@ #include #include +#include "delegation.h" + #define NFSDBG_FACILITY NFSDBG_PAGECACHE #define MIN_POOL_WRITE (32) @@ -71,7 +73,8 @@ /* * 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); @@ -173,7 +176,7 @@ static void nfs_mark_uptodate(struct page *page, unsigned int base, unsigned 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) { @@ -187,9 +190,10 @@ static int nfs_writepage_sync(struct file *file, struct inode *inode, 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; @@ -208,7 +212,7 @@ static int nfs_writepage_sync(struct file *file, struct inode *inode, 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 */ @@ -234,20 +238,19 @@ static int nfs_writepage_sync(struct file *file, struct inode *inode, 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; @@ -274,6 +277,7 @@ static int wb_priority(struct writeback_control *wbc) */ 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; @@ -308,16 +312,21 @@ int nfs_writepage(struct page *page, struct writeback_control *wbc) 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) @@ -326,6 +335,7 @@ do_it: } } unlock_kernel(); + put_nfs_open_context(ctx); out: unlock_page(page); if (inode_referenced) @@ -374,8 +384,7 @@ out: /* * 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; @@ -387,33 +396,33 @@ nfs_inode_add_request(struct inode *inode, struct nfs_page *req) 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); } @@ -429,7 +438,7 @@ _nfs_find_request(struct inode *inode, unsigned long index) req = (struct nfs_page*)radix_tree_lookup(&nfsi->nfs_page_tree, index); if (req) - req->wb_count++; + atomic_inc(&req->wb_count); return req; } @@ -437,10 +446,11 @@ static struct nfs_page * 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; } @@ -450,13 +460,13 @@ nfs_find_request(struct inode *inode, unsigned long index) 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); } @@ -467,7 +477,7 @@ nfs_mark_request_dirty(struct nfs_page *req) 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; } @@ -478,13 +488,13 @@ nfs_dirty_request(struct nfs_page *req) 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); } @@ -509,7 +519,7 @@ nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int 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) @@ -519,16 +529,16 @@ nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int 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; } @@ -619,11 +629,12 @@ static int nfs_wait_on_write_congestion(struct address_space *mapping, int intr) * * 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; @@ -635,19 +646,19 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page, /* 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; @@ -658,23 +669,19 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page, 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. @@ -684,7 +691,7 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *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) { @@ -705,9 +712,9 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page, 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; @@ -721,7 +728,7 @@ nfs_flush_incompatible(struct file *file, struct page *page) */ 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); } @@ -737,6 +744,7 @@ nfs_flush_incompatible(struct file *file, struct page *page) 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; @@ -747,7 +755,7 @@ int nfs_updatepage(struct file *file, struct page *page, 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); @@ -784,7 +792,7 @@ int nfs_updatepage(struct file *file, struct page *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; @@ -860,16 +868,15 @@ static void nfs_write_rpcsetup(struct nfs_page *req, * 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; @@ -1029,7 +1036,7 @@ nfs_flush_list(struct list_head *head, int wpages, int how) 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; } @@ -1054,16 +1061,15 @@ static void nfs_writeback_done_partial(struct nfs_write_data *data, int status) 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) @@ -1104,16 +1110,15 @@ static void nfs_writeback_done_full(struct nfs_write_data *data, int status) 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); @@ -1232,7 +1237,7 @@ static void nfs_commit_rpcsetup(struct list_head *head, 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. @@ -1246,7 +1251,7 @@ static void nfs_commit_rpcsetup(struct list_head *head, 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; @@ -1313,13 +1318,12 @@ nfs_commit_done(struct rpc_task *task) 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; @@ -1347,13 +1351,14 @@ nfs_commit_done(struct rpc_task *task) 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) @@ -1365,18 +1370,19 @@ int nfs_flush_inode(struct inode *inode, unsigned long idx_start, 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;