📄 write.c
字号:
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, struct file *file, unsigned long idx_start, unsigned int npages){ struct list_head *p, *head; unsigned long idx_end; unsigned int res = 0; int error; if (npages == 0) idx_end = ~0; else idx_end = idx_start + npages - 1; head = &inode->u.nfs_i.writeback; restart: spin_lock(&nfs_wreq_lock); list_for_each_prev(p, head) { unsigned long pg_idx; struct nfs_page *req = nfs_inode_wb_entry(p); if (file && req->wb_file != file) continue; pg_idx = page_index(req->wb_page); if (pg_idx < idx_start) break; if (pg_idx > idx_end) continue; if (!NFS_WBACK_BUSY(req)) continue; req->wb_count++; spin_unlock(&nfs_wreq_lock); error = nfs_wait_on_request(req); nfs_release_request(req); if (error < 0) return error; res++; goto restart; } spin_unlock(&nfs_wreq_lock); return res;}/** * nfs_scan_lru_dirty_timeout - Scan LRU list for timed out dirty requests * @server: NFS superblock data * @dst: destination list * * Moves a maximum of 'wpages' requests from the NFS dirty page LRU list. * The elements are checked to ensure that they form a contiguous set * of pages, and that they originated from the same file. */intnfs_scan_lru_dirty_timeout(struct nfs_server *server, struct list_head *dst){ struct inode *inode; int npages; npages = nfs_scan_lru_timeout(&server->lru_dirty, dst, server->wpages); if (npages) { inode = nfs_list_entry(dst->next)->wb_inode; inode->u.nfs_i.ndirty -= npages; } return npages;}/** * nfs_scan_lru_dirty - Scan LRU list for dirty requests * @server: NFS superblock data * @dst: destination list * * Moves a maximum of 'wpages' requests from the NFS dirty page LRU list. * The elements are checked to ensure that they form a contiguous set * of pages, and that they originated from the same file. */intnfs_scan_lru_dirty(struct nfs_server *server, struct list_head *dst){ struct inode *inode; int npages; npages = nfs_scan_lru(&server->lru_dirty, dst, server->wpages); if (npages) { inode = nfs_list_entry(dst->next)->wb_inode; inode->u.nfs_i.ndirty -= npages; } return npages;}/* * nfs_scan_dirty - Scan an inode for dirty requests * @inode: NFS inode to scan * @dst: destination list * @file: if set, ensure we match requests from this file * @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, struct file *file, unsigned long idx_start, unsigned int npages){ int res; res = nfs_scan_list(&inode->u.nfs_i.dirty, dst, file, idx_start, npages); inode->u.nfs_i.ndirty -= res; if ((inode->u.nfs_i.ndirty == 0) != list_empty(&inode->u.nfs_i.dirty)) printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n"); return res;}#ifdef CONFIG_NFS_V3/** * nfs_scan_lru_commit_timeout - Scan LRU list for timed out commit requests * @server: NFS superblock data * @dst: destination list * * Finds the first a timed out request in the NFS commit LRU list and moves it * to the list dst. If such an element is found, we move all other commit * requests that apply to the same inode. * The assumption is that doing everything in a single commit-to-disk is * the cheaper alternative. */intnfs_scan_lru_commit_timeout(struct nfs_server *server, struct list_head *dst){ struct inode *inode; int npages; npages = nfs_scan_lru_timeout(&server->lru_commit, dst, 1); if (npages) { inode = nfs_list_entry(dst->next)->wb_inode; npages += nfs_scan_list(&inode->u.nfs_i.commit, dst, NULL, 0, 0); inode->u.nfs_i.ncommit -= npages; } return npages;}/** * nfs_scan_lru_commit_timeout - Scan LRU list for timed out commit requests * @server: NFS superblock data * @dst: destination list * * Finds the first request in the NFS commit LRU list and moves it * to the list dst. If such an element is found, we move all other commit * requests that apply to the same inode. * The assumption is that doing everything in a single commit-to-disk is * the cheaper alternative. */intnfs_scan_lru_commit(struct nfs_server *server, struct list_head *dst){ struct inode *inode; int npages; npages = nfs_scan_lru(&server->lru_commit, dst, 1); if (npages) { inode = nfs_list_entry(dst->next)->wb_inode; npages += nfs_scan_list(&inode->u.nfs_i.commit, dst, NULL, 0, 0); inode->u.nfs_i.ncommit -= npages; } return npages;}/* * nfs_scan_commit - Scan an inode for commit requests * @inode: NFS inode to scan * @dst: destination list * @file: if set, ensure we collect requests from this file only. * @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, struct file *file, unsigned long idx_start, unsigned int npages){ int res; res = nfs_scan_list(&inode->u.nfs_i.commit, dst, file, idx_start, npages); inode->u.nfs_i.ncommit -= res; if ((inode->u.nfs_i.ncommit == 0) != list_empty(&inode->u.nfs_i.commit)) printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n"); return res;}#endif/* * 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 file* file, struct inode *inode, struct page *page, unsigned int offset, unsigned int bytes){ struct nfs_page *req, *new = NULL; unsigned long rqend, end; end = offset + bytes; for (;;) { /* Loop over all inode entries and see if we find * A request for the page we wish to update */ spin_lock(&nfs_wreq_lock); req = _nfs_find_request(inode, page); if (req) { if (!nfs_lock_request_dontget(req)) { int error; spin_unlock(&nfs_wreq_lock); error = nfs_wait_on_request(req); nfs_release_request(req); if (error < 0) return ERR_PTR(error); continue; } spin_unlock(&nfs_wreq_lock); if (new) nfs_release_request(new); break; } if (new) { nfs_lock_request_dontget(new); nfs_inode_add_request(inode, new); spin_unlock(&nfs_wreq_lock); nfs_mark_request_dirty(new); return new; } spin_unlock(&nfs_wreq_lock); new = nfs_create_request(file, inode, page, offset, bytes); if (IS_ERR(new)) return new; /* If the region is locked, adjust the timeout */ if (region_locked(inode, new)) new->wb_timeout = jiffies + NFS_WRITEBACK_LOCKDELAY; else new->wb_timeout = jiffies + NFS_WRITEBACK_DELAY; } /* 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_file != file || 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_bytes = rqend - req->wb_offset; } if (end > rqend) req->wb_bytes = end - req->wb_offset; return req;}/* * This is the strategy routine for NFS. * It is called by nfs_updatepage whenever the user wrote up to the end * of a page. * * We always try to submit a set of requests in parallel so that the * server's write code can gather writes. This is mainly for the benefit * of NFSv2. * * We never submit more requests than we think the remote can handle. * For UDP sockets, we make sure we don't exceed the congestion window; * for TCP, we limit the number of requests to 8. * * NFS_STRATEGY_PAGES gives the minimum number of requests for NFSv2 that * should be sent out in one go. This is for the benefit of NFSv2 servers * that perform write gathering. * * FIXME: Different servers may have different sweet spots. * Record the average congestion window in server struct? */#define NFS_STRATEGY_PAGES 8static voidnfs_strategy(struct inode *inode){ unsigned int dirty, wpages; dirty = inode->u.nfs_i.ndirty; wpages = NFS_SERVER(inode)->wpages;#ifdef CONFIG_NFS_V3 if (NFS_PROTO(inode)->version == 2) { if (dirty >= NFS_STRATEGY_PAGES * wpages) nfs_flush_file(inode, NULL, 0, 0, 0); } else if (dirty >= wpages) nfs_flush_file(inode, NULL, 0, 0, 0);#else if (dirty >= NFS_STRATEGY_PAGES * wpages) nfs_flush_file(inode, NULL, 0, 0, 0);#endif}intnfs_flush_incompatible(struct file *file, struct page *page){ struct inode *inode = file->f_dentry->d_inode; 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); if (req) { if (req->wb_file != file || req->wb_page != page) 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). */intnfs_updatepage(struct file *file, struct page *page, unsigned int offset, unsigned int count){ struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; struct nfs_page *req; loff_t end; 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 wsize is smaller than page size, update and write * page synchronously. */ if (NFS_SERVER(inode)->wsize < PAGE_CACHE_SIZE || IS_SYNC(inode)) return nfs_writepage_sync(file, inode, page, offset, count); /* * 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(file, 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; end = ((loff_t)page->index<<PAGE_CACHE_SHIFT) + (loff_t)(offset + count); if (inode->i_size < end) inode->i_size = end; /* If we wrote past the end of the page. * Call the strategy routine so it can send out a bunch * of requests. */ if (req->wb_offset == 0 && req->wb_bytes == PAGE_CACHE_SIZE) { SetPageUptodate(page); nfs_unlock_request(req); nfs_strategy(inode); } else nfs_unlock_request(req);done: dprintk("NFS: nfs_updatepage returns %d (isize %Ld)\n", status, (long long)inode->i_size); if (status < 0) ClearPageUptodate(page); return status;}/* * Set up the argument/result storage required for the RPC call. */static voidnfs_write_rpcsetup(struct list_head *head, struct nfs_write_data *data){ struct nfs_page *req; struct iovec *iov; unsigned int count; /* Set up the RPC argument and reply structs * NB: take care not to mess about with data->commit et al. */ iov = data->args.iov; count = 0; while (!list_empty(head)) { struct nfs_page *req = nfs_list_entry(head->next); nfs_list_remove_request(req); nfs_list_add_request(req, &data->pages); iov->iov_base = kmap(req->wb_page) + req->wb_offset; iov->iov_len = req->wb_bytes; count += req->wb_bytes; iov++; data->args.nriov++; } req = nfs_list_entry(data->pages.next); data->inode = req->wb_inode; data->cred = req->wb_cred; data->args.fh = NFS_FH(req->wb_inode); data->args.offset = page_offset(req->wb_page) + req->wb_offset; data->args.count = count; data->res.fattr = &data->fattr;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -