📄 write.c
字号:
while (!list_empty(&list)) { data = list_entry(list.next, struct nfs_write_data, pages); list_del(&data->pages); nfs_writedata_free(data); } nfs_mark_request_dirty(req); nfs_unlock_request(req); return -ENOMEM;}/* * 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 int nfs_flush_one(struct list_head *head, struct inode *inode, int how){ struct nfs_page *req; struct page **pages; struct nfs_write_data *data; unsigned int count; if (NFS_SERVER(inode)->wsize < PAGE_CACHE_SIZE) return nfs_flush_multi(head, inode, how); data = nfs_writedata_alloc(); if (!data) goto out_bad; pages = data->pagevec; count = 0; while (!list_empty(head)) { req = nfs_list_entry(head->next); nfs_list_remove_request(req); nfs_list_add_request(req, &data->pages); ClearPageError(req->wb_page); SetPageWriteback(req->wb_page); *pages++ = req->wb_page; count += req->wb_bytes; } req = nfs_list_entry(data->pages.next); data->complete = nfs_writeback_done_full; /* Set up the argument struct */ nfs_write_rpcsetup(req, data, count, 0, how); nfs_execute_write(data); 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;}intnfs_flush_list(struct list_head *head, int wpages, int how){ LIST_HEAD(one_request); struct nfs_page *req; int error = 0; unsigned int pages = 0; 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_context->dentry->d_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;}/* * Handle a write reply that flushed part of a page. */static void nfs_writeback_done_partial(struct nfs_write_data *data, int status){ struct nfs_page *req = data->req; struct page *page = req->wb_page; dprintk("NFS: write (%s/%Ld %d@%Ld)", 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); req->wb_context->error = status; dprintk(", error = %d\n", status); } else {#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) if (data->verf.committed < NFS_FILE_SYNC) { if (!NFS_NEED_COMMIT(req)) { nfs_defer_commit(req); memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf)); dprintk(" defer commit\n"); } else if (memcmp(&req->wb_verf, &data->verf, sizeof(req->wb_verf))) { nfs_defer_reschedule(req); dprintk(" server reboot detected\n"); } } else#endif dprintk(" OK\n"); } if (atomic_dec_and_test(&req->wb_complete)) nfs_writepage_release(req);}/* * Handle a write reply that flushes a whole page. * * 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. */static void nfs_writeback_done_full(struct nfs_write_data *data, int status){ struct nfs_page *req; struct page *page; /* Update attributes as result of writeback. */ while (!list_empty(&data->pages)) { req = nfs_list_entry(data->pages.next); nfs_list_remove_request(req); page = req->wb_page; dprintk("NFS: write (%s/%Ld %d@%Ld)", 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); req->wb_context->error = status; end_page_writeback(page); nfs_inode_remove_request(req); dprintk(", error = %d\n", status); goto next; } end_page_writeback(page);#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) if (data->args.stable != NFS_UNSTABLE || data->verf.committed == NFS_FILE_SYNC) { nfs_inode_remove_request(req); dprintk(" OK\n"); goto next; } memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf)); nfs_mark_request_commit(req); dprintk(" marked for commit\n");#else nfs_inode_remove_request(req);#endif next: nfs_unlock_request(req); }}/* * This function is called when the WRITE call is complete. */void nfs_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; dprintk("NFS: %4d nfs_writeback_done (status %d)\n", task->tk_pid, task->tk_status);#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) 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 NFS server %s:" " (committed = %d) != (stable = %d)\n", NFS_SERVER(data->inode)->hostname, resp->verf->committed, argp->stable); complain = jiffies + 300 * HZ; } }#endif /* Is this a short write? */ if (task->tk_status >= 0 && resp->count < argp->count) { static unsigned long complain; /* Has the server at least made some progress? */ if (resp->count != 0) { /* Was this an NFSv2 write or an NFSv3 stable write? */ if (resp->verf->committed != NFS_UNSTABLE) { /* Resend from where the server left off */ argp->offset += resp->count; argp->pgbase += resp->count; argp->count -= resp->count; } else { /* Resend as a stable write in order to avoid * headaches in the case of a server crash. */ argp->stable = NFS_FILE_SYNC; } rpc_restart_call(task); return; } 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 except throw an error. */ task->tk_status = -EIO; } /* * Process the nfs_page list */ data->complete(data, task->tk_status);}#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)static void nfs_commit_release(struct rpc_task *task){ struct nfs_write_data *wdata = (struct nfs_write_data *)task->tk_calldata; nfs_commit_free(wdata);}/* * Set up the argument/result storage required for the RPC call. */static void nfs_commit_rpcsetup(struct list_head *head, struct nfs_write_data *data, int how){ struct rpc_task *task = &data->task; 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_init(head, &data->pages); first = nfs_list_entry(data->pages.next); last = nfs_list_entry(data->pages.prev); inode = first->wb_context->dentry->d_inode; /* * Determine the offset range of requests in the COMMIT call. * We rely on the fact that data->pages is an ordered list... */ start = req_offset(first); end = req_offset(last) + last->wb_bytes; len = end - start; /* If 'len' is not a 32-bit quantity, pass '0' in the COMMIT call */ if (end >= i_size_read(inode) || len < 0 || len > (~((u32)0) >> 1)) len = 0; data->inode = inode; data->cred = first->wb_context->cred; data->args.fh = NFS_FH(data->inode); data->args.offset = start; data->args.count = len; data->res.count = len; data->res.fattr = &data->fattr; data->res.verf = &data->verf; NFS_PROTO(inode)->commit_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_commit_release; dprintk("NFS: %4d initiated commit call\n", task->tk_pid);}/* * Commit dirty pages */intnfs_commit_list(struct list_head *head, int how){ struct nfs_write_data *data; struct nfs_page *req; data = nfs_commit_alloc(); if (!data) goto out_bad; /* Set up the argument struct */ nfs_commit_rpcsetup(head, data, how); nfs_execute_write(data); 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 */voidnfs_commit_done(struct rpc_task *task){ struct nfs_write_data *data = (struct nfs_write_data *)task->tk_calldata; struct nfs_page *req; int res = 0; dprintk("NFS: %4d nfs_commit_done (status %d)\n", task->tk_pid, task->tk_status); while (!list_empty(&data->pages)) { req = nfs_list_entry(data->pages.next); nfs_list_remove_request(req); dprintk("NFS: commit (%s/%Ld %d@%Ld)", 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) { req->wb_context->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); res++; } sub_page_state(nr_unstable,res);}#endifint 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(&nfsi->req_lock); res = nfs_scan_dirty(inode, &head, idx_start, npages); spin_unlock(&nfsi->req_lock); if (res) error = nfs_flush_list(&head, NFS_SERVER(inode)->wpages, how); if (error < 0) return error; return res;}#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)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(&nfsi->req_lock); res = nfs_scan_commit(inode, &head, idx_start, npages); if (res) { res += nfs_scan_commit(inode, &head, 0, 0); spin_unlock(&nfsi->req_lock); error = nfs_commit_list(&head, how); } else spin_unlock(&nfsi->req_lock); if (error < 0) return error; return res;}#endifint nfs_sync_inode(struct inode *inode, unsigned long idx_start, unsigned int npages, int how){ int error, wait; wait = how & FLUSH_WAIT; how &= ~FLUSH_WAIT; do { error = 0; if (wait) error = nfs_wait_on_requests(inode, idx_start, npages); if (error == 0) error = nfs_flush_inode(inode, idx_start, npages, how);#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) if (error == 0) error = nfs_commit_inode(inode, idx_start, npages, how);#endif } while (error > 0); return error;}int nfs_init_writepagecache(void){ 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; nfs_wdata_mempool = mempool_create(MIN_POOL_WRITE, mempool_alloc_slab, mempool_free_slab, nfs_wdata_cachep); if (nfs_wdata_mempool == NULL) return -ENOMEM; nfs_commit_mempool = mempool_create(MIN_POOL_COMMIT, mempool_alloc_slab, mempool_free_slab, nfs_wdata_cachep); if (nfs_commit_mempool == NULL) return -ENOMEM; return 0;}void nfs_destroy_writepagecache(void){ mempool_destroy(nfs_commit_mempool); mempool_destroy(nfs_wdata_mempool); 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 + -