📄 write.c
字号:
status = 0; if (synchronous) { int error; error = nfs_sync_file(inode, file, page_index(page), 1, FLUSH_SYNC|FLUSH_STABLE); if (error < 0 || (error = file->f_error) < 0) status = error; file->f_error = 0; } else { /* 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) nfs_strategy(inode); } nfs_release_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; data->res.count = count; data->res.verf = &data->verf;}/* * Create an RPC task for the given write request and kick it. * The page must have been locked by the caller. * * It may happen that the page we're passed is not marked dirty. * This is the case if nfs_updatepage detects a conflicting request * that has been written but not committed. */static intnfs_flush_one(struct list_head *head, struct inode *inode, int how){ struct rpc_clnt *clnt = NFS_CLIENT(inode); struct nfs_write_data *data; struct rpc_task *task; struct rpc_message msg; int flags, async = !(how & FLUSH_SYNC), stable = (how & FLUSH_STABLE); sigset_t oldset; data = nfs_writedata_alloc(); if (!data) goto out_bad; task = &data->task; /* Set the initial flags for the task. */ flags = (async) ? RPC_TASK_ASYNC : 0; /* Set up the argument struct */ nfs_write_rpcsetup(head, data); if (stable) { if (!inode->u.nfs_i.ncommit) data->args.stable = NFS_FILE_SYNC; else data->args.stable = NFS_DATA_SYNC; } else data->args.stable = NFS_UNSTABLE; /* Finalize the task. */ rpc_init_task(task, clnt, nfs_writeback_done, flags); task->tk_calldata = data; /* Release requests */ task->tk_release = nfs_writedata_release;#ifdef CONFIG_NFS_V3 msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ? NFS3PROC_WRITE : NFSPROC_WRITE;#else msg.rpc_proc = NFSPROC_WRITE;#endif msg.rpc_argp = &data->args; msg.rpc_resp = &data->res; msg.rpc_cred = data->cred; dprintk("NFS: %4d initiated write call (req %x/%Ld count %d nriov %d)\n", task->tk_pid, inode->i_dev, (long long)NFS_FILEID(inode), data->args.count, data->args.nriov); rpc_clnt_sigmask(clnt, &oldset); rpc_call_setup(task, &msg, 0); rpc_execute(task); rpc_clnt_sigunmask(clnt, &oldset); return 0; out_bad: while (!list_empty(head)) { struct nfs_page *req = nfs_list_entry(head->next); nfs_list_remove_request(req); nfs_mark_request_dirty(req); nfs_unlock_request(req); } return -ENOMEM;}static intnfs_flush_list(struct inode *inode, struct list_head *head, int how){ LIST_HEAD(one_request); struct nfs_page *req; int error = 0; unsigned int pages = 0, wpages = NFS_SERVER(inode)->wpages; 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); if (error < 0) break; } if (error >= 0) return pages; while (!list_empty(head)) { req = nfs_list_entry(head->next); nfs_list_remove_request(req); nfs_mark_request_dirty(req); nfs_unlock_request(req); } return error;}/* * This function is called when the WRITE call is complete. */static voidnfs_writeback_done(struct rpc_task *task){ struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; struct nfs_writeargs *argp = &data->args; struct nfs_writeres *resp = &data->res; struct inode *inode = data->inode; struct nfs_page *req; struct page *page; dprintk("NFS: %4d nfs_writeback_done (status %d)\n", task->tk_pid, task->tk_status); /* We can't handle that yet but we check for it nevertheless */ if (resp->count < argp->count && task->tk_status >= 0) { static unsigned long complain; if (time_before(complain, jiffies)) { printk(KERN_WARNING "NFS: Server wrote less than requested.\n"); complain = jiffies + 300 * HZ; } /* Can't do anything about it right now except throw * an error. */ task->tk_status = -EIO; }#ifdef CONFIG_NFS_V3 if (resp->verf->committed < argp->stable && task->tk_status >= 0) { /* We tried a write call, but the server did not * commit data to stable storage even though we * requested it. * Note: There is a known bug in Tru64 < 5.0 in which * the server reports NFS_DATA_SYNC, but performs * NFS_FILE_SYNC. We therefore implement this checking * as a dprintk() in order to avoid filling syslog. */ static unsigned long complain; if (time_before(complain, jiffies)) { dprintk("NFS: faulty NFSv3 server %s:" " (committed = %d) != (stable = %d)\n", NFS_SERVER(inode)->hostname, resp->verf->committed, argp->stable); complain = jiffies + 300 * HZ; } }#endif /* * Update attributes as result of writeback. * FIXME: There is an inherent race with invalidate_inode_pages and * writebacks since the page->count is kept > 1 for as long * as the page has a write request pending. */ nfs_write_attributes(inode, resp->fattr); while (!list_empty(&data->pages)) { req = nfs_list_entry(data->pages.next); nfs_list_remove_request(req); page = req->wb_page; kunmap(page); dprintk("NFS: write (%x/%Ld %d@%Ld)", req->wb_inode->i_dev, (long long)NFS_FILEID(req->wb_inode), req->wb_bytes, (long long)(page_offset(page) + req->wb_offset)); if (task->tk_status < 0) { ClearPageUptodate(page); SetPageError(page); if (req->wb_file) req->wb_file->f_error = task->tk_status; nfs_inode_remove_request(req); dprintk(", error = %d\n", task->tk_status); goto next; }#ifdef CONFIG_NFS_V3 if (resp->verf->committed != NFS_UNSTABLE) { nfs_inode_remove_request(req); dprintk(" OK\n"); goto next; } memcpy(&req->wb_verf, resp->verf, sizeof(req->wb_verf)); req->wb_timeout = jiffies + NFS_COMMIT_DELAY; nfs_mark_request_commit(req); dprintk(" marked for commit\n");#else nfs_inode_remove_request(req);#endif next: nfs_unlock_request(req); }}#ifdef CONFIG_NFS_V3/* * Set up the argument/result storage required for the RPC call. */static voidnfs_commit_rpcsetup(struct list_head *head, struct nfs_write_data *data){ struct nfs_page *first, *last; struct inode *inode; loff_t start, end, len; /* Set up the RPC argument and reply structs * NB: take care not to mess about with data->commit et al. */ list_splice(head, &data->pages); INIT_LIST_HEAD(head); first = nfs_list_entry(data->pages.next); last = nfs_list_entry(data->pages.prev); inode = first->wb_inode; /* * Determine the offset range of requests in the COMMIT call. * We rely on the fact that data->pages is an ordered list... */ start = page_offset(first->wb_page) + first->wb_offset; end = page_offset(last->wb_page) + (last->wb_offset + last->wb_bytes); len = end - start; /* If 'len' is not a 32-bit quantity, pass '0' in the COMMIT call */ if (end >= inode->i_size || len < 0 || len > (~((u32)0) >> 1)) len = 0; data->inode = inode; data->cred = first->wb_cred; data->args.fh = NFS_FH(inode); data->args.offset = start; data->res.count = data->args.count = (u32)len; data->res.fattr = &data->fattr; data->res.verf = &data->verf;}/* * Commit dirty pages */static intnfs_commit_list(struct list_head *head, int how){ struct rpc_message msg; struct rpc_clnt *clnt; struct nfs_write_data *data; struct rpc_task *task; struct nfs_page *req; int flags, async = !(how & FLUSH_SYNC); sigset_t oldset; data = nfs_writedata_alloc(); if (!data) goto out_bad; task = &data->task; flags = (async) ? RPC_TASK_ASYNC : 0; /* Set up the argument struct */ nfs_commit_rpcsetup(head, data); req = nfs_list_entry(data->pages.next); clnt = NFS_CLIENT(req->wb_inode); rpc_init_task(task, clnt, nfs_commit_done, flags); task->tk_calldata = data; /* Release requests */ task->tk_release = nfs_writedata_release; msg.rpc_proc = NFS3PROC_COMMIT; msg.rpc_argp = &data->args; msg.rpc_resp = &data->res; msg.rpc_cred = data->cred; dprintk("NFS: %4d initiated commit call\n", task->tk_pid); rpc_clnt_sigmask(clnt, &oldset); rpc_call_setup(task, &msg, 0); rpc_execute(task); rpc_clnt_sigunmask(clnt, &oldset); return 0; out_bad: while (!list_empty(head)) { req = nfs_list_entry(head->next); nfs_list_remove_request(req); nfs_mark_request_commit(req); nfs_unlock_request(req); } return -ENOMEM;}/* * COMMIT call returned */static voidnfs_commit_done(struct rpc_task *task){ struct nfs_write_data *data = (struct nfs_write_data *)task->tk_calldata; struct nfs_writeres *resp = &data->res; struct nfs_page *req; struct inode *inode = data->inode; dprintk("NFS: %4d nfs_commit_done (status %d)\n", task->tk_pid, task->tk_status); nfs_write_attributes(inode, resp->fattr); while (!list_empty(&data->pages)) { req = nfs_list_entry(data->pages.next); nfs_list_remove_request(req); dprintk("NFS: commit (%x/%Ld %d@%Ld)", req->wb_inode->i_dev, (long long)NFS_FILEID(req->wb_inode), req->wb_bytes, (long long)(page_offset(req->wb_page) + req->wb_offset)); if (task->tk_status < 0) { if (req->wb_file) req->wb_file->f_error = task->tk_status; nfs_inode_remove_request(req); dprintk(", error = %d\n", task->tk_status); goto next; } /* Okay, COMMIT succeeded, apparently. Check the verifier * returned by the server against all stored verfs. */ if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) { /* We have a match */ nfs_inode_remove_request(req); dprintk(" OK\n"); goto next; } /* We have a mismatch. Write the page again */ dprintk(" mismatch\n"); nfs_mark_request_dirty(req); next: nfs_unlock_request(req); }}#endifint nfs_flush_file(struct inode *inode, struct file *file, unsigned long idx_start, unsigned int npages, int how){ LIST_HEAD(head); int res, error = 0; res = nfs_scan_dirty(inode, &head, file, idx_start, npages); if (res) error = nfs_flush_list(inode, &head, how); if (error < 0) return error; return res;}int nfs_flush_timeout(struct inode *inode, int how){ LIST_HEAD(head); int pages, error = 0; pages = nfs_scan_dirty_timeout(inode, &head); if (pages) error = nfs_flush_list(inode, &head, how); if (error < 0) return error; return pages;}#ifdef CONFIG_NFS_V3int nfs_commit_file(struct inode *inode, struct file *file, unsigned long idx_start, unsigned int npages, int how){ LIST_HEAD(head); int res, error = 0; res = nfs_scan_commit(inode, &head, file, idx_start, npages); if (res) error = nfs_commit_list(&head, how); if (error < 0) return error; return res;}int nfs_commit_timeout(struct inode *inode, int how){ LIST_HEAD(head); int pages, error = 0; pages = nfs_scan_commit_timeout(inode, &head); if (pages) { pages += nfs_scan_commit(inode, &head, NULL, 0, 0); error = nfs_commit_list(&head, how); } if (error < 0) return error; return pages;}#endifint nfs_sync_file(struct inode *inode, struct file *file, unsigned long idx_start, unsigned int npages, int how){ int error, wait; wait = how & FLUSH_WAIT; how &= ~FLUSH_WAIT; if (!inode && file) inode = file->f_dentry->d_inode; do { error = 0; if (wait) error = nfs_wait_on_requests(inode, file, idx_start, npages); if (error == 0) error = nfs_flush_file(inode, file, idx_start, npages, how);#ifdef CONFIG_NFS_V3 if (error == 0) error = nfs_commit_file(inode, file, idx_start, npages, how);#endif } while (error > 0); return error;}int nfs_init_nfspagecache(void){ nfs_page_cachep = kmem_cache_create("nfs_page", sizeof(struct nfs_page), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); if (nfs_page_cachep == NULL) return -ENOMEM; nfs_wdata_cachep = kmem_cache_create("nfs_write_data", sizeof(struct nfs_write_data), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); if (nfs_wdata_cachep == NULL) return -ENOMEM; return 0;}void nfs_destroy_nfspagecache(void){ if (kmem_cache_destroy(nfs_page_cachep)) printk(KERN_INFO "nfs_page: not all structures were freed\n"); if (kmem_cache_destroy(nfs_wdata_cachep)) printk(KERN_INFO "nfs_write_data: not all structures were freed\n");}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -