📄 direct.c
字号:
if (put_dreq(dreq)) nfs_direct_write_complete(dreq, inode);}static void nfs_direct_commit_result(struct rpc_task *task, void *calldata){ struct nfs_write_data *data = calldata; struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req; /* Call the NFS version-specific code */ if (NFS_PROTO(data->inode)->commit_done(task, data) != 0) return; if (unlikely(task->tk_status < 0)) { dprintk("NFS: %5u commit failed with error %d.\n", task->tk_pid, task->tk_status); dreq->flags = NFS_ODIRECT_RESCHED_WRITES; } else if (memcmp(&dreq->verf, &data->verf, sizeof(data->verf))) { dprintk("NFS: %5u commit verify failed\n", task->tk_pid); dreq->flags = NFS_ODIRECT_RESCHED_WRITES; } dprintk("NFS: %5u commit returned %d\n", task->tk_pid, task->tk_status); nfs_direct_write_complete(dreq, data->inode);}static const struct rpc_call_ops nfs_commit_direct_ops = { .rpc_call_done = nfs_direct_commit_result, .rpc_release = nfs_commit_release,};static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq){ struct nfs_write_data *data = dreq->commit_data; data->inode = dreq->inode; data->cred = dreq->ctx->cred; data->args.fh = NFS_FH(data->inode); data->args.offset = 0; data->args.count = 0; data->res.count = 0; data->res.fattr = &data->fattr; data->res.verf = &data->verf; rpc_init_task(&data->task, NFS_CLIENT(dreq->inode), RPC_TASK_ASYNC, &nfs_commit_direct_ops, data); NFS_PROTO(data->inode)->commit_setup(data, 0); data->task.tk_priority = RPC_PRIORITY_NORMAL; data->task.tk_cookie = (unsigned long)data->inode; /* Note: task.tk_ops->rpc_release will free dreq->commit_data */ dreq->commit_data = NULL; dprintk("NFS: %5u initiated commit call\n", data->task.tk_pid); rpc_execute(&data->task);}static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode){ int flags = dreq->flags; dreq->flags = 0; switch (flags) { case NFS_ODIRECT_DO_COMMIT: nfs_direct_commit_schedule(dreq); break; case NFS_ODIRECT_RESCHED_WRITES: nfs_direct_write_reschedule(dreq); break; default: if (dreq->commit_data != NULL) nfs_commit_free(dreq->commit_data); nfs_direct_free_writedata(dreq); nfs_zap_mapping(inode, inode->i_mapping); nfs_direct_complete(dreq); }}static void nfs_alloc_commit_data(struct nfs_direct_req *dreq){ dreq->commit_data = nfs_commit_alloc(); if (dreq->commit_data != NULL) dreq->commit_data->req = (struct nfs_page *) dreq;}#elsestatic inline void nfs_alloc_commit_data(struct nfs_direct_req *dreq){ dreq->commit_data = NULL;}static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode){ nfs_direct_free_writedata(dreq); nfs_zap_mapping(inode, inode->i_mapping); nfs_direct_complete(dreq);}#endifstatic void nfs_direct_write_result(struct rpc_task *task, void *calldata){ struct nfs_write_data *data = calldata; struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req; int status = task->tk_status; if (nfs_writeback_done(task, data) != 0) return; spin_lock(&dreq->lock); if (unlikely(status < 0)) { /* An error has occurred, so we should not commit */ dreq->flags = 0; dreq->error = status; } if (unlikely(dreq->error != 0)) goto out_unlock; dreq->count += data->res.count; if (data->res.verf->committed != NFS_FILE_SYNC) { switch (dreq->flags) { case 0: memcpy(&dreq->verf, &data->verf, sizeof(dreq->verf)); dreq->flags = NFS_ODIRECT_DO_COMMIT; break; case NFS_ODIRECT_DO_COMMIT: if (memcmp(&dreq->verf, &data->verf, sizeof(dreq->verf))) { dprintk("NFS: %5u write verify failed\n", task->tk_pid); dreq->flags = NFS_ODIRECT_RESCHED_WRITES; } } }out_unlock: spin_unlock(&dreq->lock);}/* * NB: Return the value of the first error return code. Subsequent * errors after the first one are ignored. */static void nfs_direct_write_release(void *calldata){ struct nfs_write_data *data = calldata; struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req; if (put_dreq(dreq)) nfs_direct_write_complete(dreq, data->inode);}static const struct rpc_call_ops nfs_write_direct_ops = { .rpc_call_done = nfs_direct_write_result, .rpc_release = nfs_direct_write_release,};/* * For each wsize'd chunk of the user's buffer, dispatch an NFS WRITE * operation. If nfs_writedata_alloc() or get_user_pages() fails, * bail and stop sending more writes. Write length accounting is * handled automatically by nfs_direct_write_result(). Otherwise, if * no requests have been sent, just return an error. */static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq, const struct iovec *iov, loff_t pos, int sync){ struct nfs_open_context *ctx = dreq->ctx; struct inode *inode = ctx->path.dentry->d_inode; unsigned long user_addr = (unsigned long)iov->iov_base; size_t count = iov->iov_len; size_t wsize = NFS_SERVER(inode)->wsize; unsigned int pgbase; int result; ssize_t started = 0; do { struct nfs_write_data *data; size_t bytes; pgbase = user_addr & ~PAGE_MASK; bytes = min(wsize,count); result = -ENOMEM; data = nfs_writedata_alloc(nfs_page_array_len(pgbase, bytes)); if (unlikely(!data)) break; down_read(¤t->mm->mmap_sem); result = get_user_pages(current, current->mm, user_addr, data->npages, 0, 0, data->pagevec, NULL); up_read(¤t->mm->mmap_sem); if (result < 0) { nfs_writedata_release(data); break; } if ((unsigned)result < data->npages) { bytes = result * PAGE_SIZE; if (bytes <= pgbase) { nfs_direct_release_pages(data->pagevec, result); nfs_writedata_release(data); break; } bytes -= pgbase; data->npages = result; } get_dreq(dreq); list_move_tail(&data->pages, &dreq->rewrite_list); data->req = (struct nfs_page *) dreq; data->inode = inode; data->cred = ctx->cred; data->args.fh = NFS_FH(inode); data->args.context = ctx; data->args.offset = pos; data->args.pgbase = pgbase; data->args.pages = data->pagevec; data->args.count = bytes; data->res.fattr = &data->fattr; data->res.count = bytes; data->res.verf = &data->verf; rpc_init_task(&data->task, NFS_CLIENT(inode), RPC_TASK_ASYNC, &nfs_write_direct_ops, data); NFS_PROTO(inode)->write_setup(data, sync); data->task.tk_priority = RPC_PRIORITY_NORMAL; data->task.tk_cookie = (unsigned long) inode; rpc_execute(&data->task); dprintk("NFS: %5u initiated direct write call " "(req %s/%Ld, %zu bytes @ offset %Lu)\n", data->task.tk_pid, inode->i_sb->s_id, (long long)NFS_FILEID(inode), bytes, (unsigned long long)data->args.offset); started += bytes; user_addr += bytes; pos += bytes; /* FIXME: Remove this useless math from the final patch */ pgbase += bytes; pgbase &= ~PAGE_MASK; BUG_ON(pgbase != (user_addr & ~PAGE_MASK)); count -= bytes; } while (count != 0); if (started) return started; return result < 0 ? (ssize_t) result : -EFAULT;}static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, const struct iovec *iov, unsigned long nr_segs, loff_t pos, int sync){ ssize_t result = 0; size_t requested_bytes = 0; unsigned long seg; get_dreq(dreq); for (seg = 0; seg < nr_segs; seg++) { const struct iovec *vec = &iov[seg]; result = nfs_direct_write_schedule_segment(dreq, vec, pos, sync); if (result < 0) break; requested_bytes += result; if ((size_t)result < vec->iov_len) break; pos += vec->iov_len; } if (put_dreq(dreq)) nfs_direct_write_complete(dreq, dreq->inode); if (requested_bytes != 0) return 0; if (result < 0) return result; return -EIO;}static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos, size_t count){ ssize_t result = 0; sigset_t oldset; struct inode *inode = iocb->ki_filp->f_mapping->host; struct rpc_clnt *clnt = NFS_CLIENT(inode); struct nfs_direct_req *dreq; size_t wsize = NFS_SERVER(inode)->wsize; int sync = 0; dreq = nfs_direct_req_alloc(); if (!dreq) return -ENOMEM; nfs_alloc_commit_data(dreq); if (dreq->commit_data == NULL || count < wsize) sync = FLUSH_STABLE; dreq->inode = inode; dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp)); if (!is_sync_kiocb(iocb)) dreq->iocb = iocb; rpc_clnt_sigmask(clnt, &oldset); result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, sync); if (!result) result = nfs_direct_wait(dreq); rpc_clnt_sigunmask(clnt, &oldset); nfs_direct_req_release(dreq); return result;}/** * nfs_file_direct_read - file direct read operation for NFS files * @iocb: target I/O control block * @iov: vector of user buffers into which to read data * @nr_segs: size of iov vector * @pos: byte offset in file where reading starts * * We use this function for direct reads instead of calling * generic_file_aio_read() in order to avoid gfar's check to see if * the request starts before the end of the file. For that check * to work, we must generate a GETATTR before each direct read, and * even then there is a window between the GETATTR and the subsequent * READ where the file size could change. Our preference is simply * to do all reads the application wants, and the server will take * care of managing the end of file boundary. * * This function also eliminates unnecessarily updating the file's * atime locally, as the NFS server sets the file's atime, and this * client must read the updated atime from the server back into its * cache. */ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos){ ssize_t retval = -EINVAL; struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; size_t count; count = iov_length(iov, nr_segs); nfs_add_stats(mapping->host, NFSIOS_DIRECTREADBYTES, count); dprintk("nfs: direct read(%s/%s, %zd@%Ld)\n", file->f_path.dentry->d_parent->d_name.name, file->f_path.dentry->d_name.name, count, (long long) pos); retval = 0; if (!count) goto out; retval = nfs_sync_mapping(mapping); if (retval) goto out; retval = nfs_direct_read(iocb, iov, nr_segs, pos); if (retval > 0) iocb->ki_pos = pos + retval;out: return retval;}/** * nfs_file_direct_write - file direct write operation for NFS files * @iocb: target I/O control block * @iov: vector of user buffers from which to write data * @nr_segs: size of iov vector * @pos: byte offset in file where writing starts * * We use this function for direct writes instead of calling * generic_file_aio_write() in order to avoid taking the inode * semaphore and updating the i_size. The NFS server will set * the new i_size and this client must read the updated size * back into its cache. We let the server do generic write * parameter checking and report problems. * * We also avoid an unnecessary invocation of generic_osync_inode(), * as it is fairly meaningless to sync the metadata of an NFS file. * * We eliminate local atime updates, see direct read above. * * We avoid unnecessary page cache invalidations for normal cached * readers of this file. * * Note that O_APPEND is not supported for NFS direct writes, as there * is no atomic O_APPEND write facility in the NFS protocol. */ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos){ ssize_t retval = -EINVAL; struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; size_t count; count = iov_length(iov, nr_segs); nfs_add_stats(mapping->host, NFSIOS_DIRECTWRITTENBYTES, count); dfprintk(VFS, "nfs: direct write(%s/%s, %zd@%Ld)\n", file->f_path.dentry->d_parent->d_name.name, file->f_path.dentry->d_name.name, count, (long long) pos); retval = generic_write_checks(file, &pos, &count, 0); if (retval) goto out; retval = -EINVAL; if ((ssize_t) count < 0) goto out; retval = 0; if (!count) goto out; retval = nfs_sync_mapping(mapping); if (retval) goto out; retval = nfs_direct_write(iocb, iov, nr_segs, pos, count); if (retval > 0) iocb->ki_pos = pos + retval;out: return retval;}/** * nfs_init_directcache - create a slab cache for nfs_direct_req structures * */int __init nfs_init_directcache(void){ nfs_direct_cachep = kmem_cache_create("nfs_direct_cache", sizeof(struct nfs_direct_req), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), NULL); if (nfs_direct_cachep == NULL) return -ENOMEM; return 0;}/** * nfs_destroy_directcache - destroy the slab cache for nfs_direct_req structures * */void nfs_destroy_directcache(void){ kmem_cache_destroy(nfs_direct_cachep);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -