📄 write.c
字号:
#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)/* * Add a request to the inode's commit list. */static voidnfs_mark_request_commit(struct nfs_page *req){ struct inode *inode = req->wb_context->dentry->d_inode; struct nfs_inode *nfsi = NFS_I(inode); spin_lock(&nfsi->req_lock); nfs_list_add_request(req, &nfsi->commit); nfsi->ncommit++; spin_unlock(&nfsi->req_lock); inc_page_state(nr_unstable); mark_inode_dirty(inode);}#endif/* * Wait for a request to complete. * * Interruptible by signals only if mounted with intr flag. */static intnfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int npages){ struct nfs_inode *nfsi = NFS_I(inode); struct nfs_page *req; unsigned long idx_end, next; unsigned int res = 0; int error; if (npages == 0) idx_end = ~0; else idx_end = idx_start + npages - 1; 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) break; next = req->wb_index + 1; if (!NFS_WBACK_BUSY(req)) continue; 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(&nfsi->req_lock); res++; } spin_unlock(&nfsi->req_lock); return res;}/* * nfs_scan_dirty - Scan an inode for dirty requests * @inode: NFS inode to scan * @dst: destination list * @idx_start: lower bound of page->index to scan. * @npages: idx_start + npages sets the upper bound to scan. * * Moves requests from the inode's dirty page list. * The requests are *not* checked to ensure that they form a contiguous set. */static intnfs_scan_dirty(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages){ struct nfs_inode *nfsi = NFS_I(inode); int res; res = nfs_scan_list(&nfsi->dirty, dst, idx_start, npages); nfsi->ndirty -= res; sub_page_state(nr_dirty,res); if ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty)) printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n"); return res;}#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)/* * nfs_scan_commit - Scan an inode for commit requests * @inode: NFS inode to scan * @dst: destination list * @idx_start: lower bound of page->index to scan. * @npages: idx_start + npages sets the upper bound to scan. * * Moves requests from the inode's 'commit' request list. * The requests are *not* checked to ensure that they form a contiguous set. */static intnfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages){ struct nfs_inode *nfsi = NFS_I(inode); int res; res = nfs_scan_list(&nfsi->commit, dst, idx_start, npages); nfsi->ncommit -= res; if ((nfsi->ncommit == 0) != list_empty(&nfsi->commit)) printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n"); return res;}#endifstatic 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. * In order to match, the request's credentials must match those of * the calling process. * * Note: Should always be called with the Page Lock held! */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; end = offset + bytes; if (nfs_wait_on_write_congestion(page->mapping, server->flags & NFS_MOUNT_INTR)) return ERR_PTR(-ERESTARTSYS); for (;;) { /* Loop over all inode entries and see if we find * A request for the page we wish to update */ spin_lock(&nfsi->req_lock); req = _nfs_find_request(inode, page->index); if (req) { if (!nfs_lock_request_dontget(req)) { int error; 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(&nfsi->req_lock); if (new) nfs_release_request(new); break; } if (new) { int error; nfs_lock_request_dontget(new); error = nfs_inode_add_request(inode, new); if (error) { spin_unlock(&nfsi->req_lock); nfs_unlock_request(new); return ERR_PTR(error); } spin_unlock(&nfsi->req_lock); nfs_mark_request_dirty(new); return new; } spin_unlock(&nfsi->req_lock); new = nfs_create_request(ctx, inode, page, offset, bytes); if (IS_ERR(new)) return new; } /* We have a request for our page. * If the creds don't match, or the * page addresses don't match, * tell the caller to wait on the conflicting * request. */ rqend = req->wb_offset + req->wb_bytes; if (req->wb_context != ctx || req->wb_page != page || !nfs_dirty_request(req) || offset > rqend || end < req->wb_offset) { nfs_unlock_request(req); return ERR_PTR(-EBUSY); } /* Okay, the request matches. Update the region */ if (offset < req->wb_offset) { req->wb_offset = offset; req->wb_pgbase = offset; req->wb_bytes = rqend - req->wb_offset; } if (end > rqend) req->wb_bytes = end - req->wb_offset; return req;}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; /* * Look for a request corresponding to this page. If there * is one, and it belongs to another file, we flush it out * before we try to copy anything into the page. Do this * due to the lack of an ACCESS-type call in NFSv2. * Also do the same if we find a request from an existing * dropped page. */ req = nfs_find_request(inode, page->index); if (req) { if (req->wb_page != page || ctx != req->wb_context) status = nfs_wb_page(inode, page); nfs_release_request(req); } return (status < 0) ? status : 0;}/* * Update and possibly write a cached page of an NFS file. * * XXX: Keep an eye on generic_file_read to make sure it doesn't do bad * things with a page scheduled for an RPC call (e.g. invalidate it). */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; int status = 0; dprintk("NFS: nfs_updatepage(%s/%s %d@%Ld)\n", dentry->d_parent->d_name.name, dentry->d_name.name, count, (long long)(page_offset(page) +offset)); if (IS_SYNC(inode)) { status = nfs_writepage_sync(ctx, inode, page, offset, count, 0); if (status > 0) { if (offset == 0 && status == PAGE_CACHE_SIZE) SetPageUptodate(page); return 0; } return status; } /* If we're not using byte range locks, and we know the page * is entirely in cache, it may be more efficient to avoid * fragmenting write requests. */ if (PageUptodate(page) && inode->i_flock == NULL) { loff_t end_offs = i_size_read(inode) - 1; unsigned long end_index = end_offs >> PAGE_CACHE_SHIFT; count += offset; offset = 0; if (unlikely(end_offs < 0)) { /* Do nothing */ } else if (page->index == end_index) { unsigned int pglen; pglen = (unsigned int)(end_offs & (PAGE_CACHE_SIZE-1)) + 1; if (count < pglen) count = pglen; } else if (page->index < end_index) count = PAGE_CACHE_SIZE; } /* * Try to find an NFS request corresponding to this page * and update it. * If the existing request cannot be updated, we must flush * it out now. */ do { req = nfs_update_request(ctx, inode, page, offset, count); status = (IS_ERR(req)) ? PTR_ERR(req) : 0; if (status != -EBUSY) break; /* Request could not be updated. Flush it out and try again */ status = nfs_wb_page(inode, page); } while (status >= 0); if (status < 0) goto done; status = 0; /* Update file length */ nfs_grow_file(page, offset, count); /* Set the PG_uptodate flag? */ nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes); nfs_unlock_request(req);done: dprintk("NFS: nfs_updatepage returns %d (isize %Ld)\n", status, (long long)i_size_read(inode)); if (status < 0) ClearPageUptodate(page); return status;}static void nfs_writepage_release(struct nfs_page *req){ end_page_writeback(req->wb_page);#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) if (!PageError(req->wb_page)) { if (NFS_NEED_RESCHED(req)) { nfs_mark_request_dirty(req); goto out; } else if (NFS_NEED_COMMIT(req)) { nfs_mark_request_commit(req); goto out; } } nfs_inode_remove_request(req);out: nfs_clear_commit(req); nfs_clear_reschedule(req);#else nfs_inode_remove_request(req);#endif nfs_unlock_request(req);}static inline int flush_task_priority(int how){ switch (how & (FLUSH_HIGHPRI|FLUSH_LOWPRI)) { case FLUSH_HIGHPRI: return RPC_PRIORITY_HIGH; case FLUSH_LOWPRI: return RPC_PRIORITY_LOW; } return RPC_PRIORITY_NORMAL;}/* * Set up the argument/result storage required for the RPC call. */static void nfs_write_rpcsetup(struct nfs_page *req, struct nfs_write_data *data, unsigned int count, unsigned int offset, int how){ struct rpc_task *task = &data->task; struct inode *inode; /* Set up the RPC argument and reply structs * NB: take care not to mess about with data->commit et al. */ data->req = req; 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.context = req->wb_context; data->res.fattr = &data->fattr; data->res.count = count; data->res.verf = &data->verf; NFS_PROTO(inode)->write_setup(data, how); data->task.tk_priority = flush_task_priority(how); data->task.tk_cookie = (unsigned long)inode; data->task.tk_calldata = data; /* Release requests */ data->task.tk_release = nfs_writedata_release; dprintk("NFS: %4d initiated write call (req %s/%Ld, %u bytes @ offset %Lu)\n", task->tk_pid, inode->i_sb->s_id, (long long)NFS_FILEID(inode), count, (unsigned long long)data->args.offset);}static void nfs_execute_write(struct nfs_write_data *data){ struct rpc_clnt *clnt = NFS_CLIENT(data->inode); sigset_t oldset; rpc_clnt_sigmask(clnt, &oldset); lock_kernel(); rpc_execute(&data->task); unlock_kernel(); rpc_clnt_sigunmask(clnt, &oldset);}/* * Generate multiple small requests to write out a single * contiguous dirty area on one page. */static int nfs_flush_multi(struct list_head *head, struct inode *inode, int how){ 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; int requests = 0; LIST_HEAD(list); nfs_list_remove_request(req); nbytes = req->wb_bytes; for (;;) { data = nfs_writedata_alloc(); if (!data) goto out_bad; list_add(&data->pages, &list); requests++; if (nbytes <= wsize) break; nbytes -= wsize; } atomic_set(&req->wb_complete, requests); ClearPageError(page); SetPageWriteback(page); offset = 0; nbytes = req->wb_bytes; do { data = list_entry(list.next, struct nfs_write_data, pages); list_del_init(&data->pages); data->pagevec[0] = page; data->complete = nfs_writeback_done_partial; if (nbytes > wsize) { nfs_write_rpcsetup(req, data, wsize, offset, how); offset += wsize; nbytes -= wsize; } else { nfs_write_rpcsetup(req, data, nbytes, offset, how); nbytes = 0; } nfs_execute_write(data); } while (nbytes != 0); return 0;out_bad:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -