⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 direct.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	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(&current->mm->mmap_sem);		result = get_user_pages(current, current->mm, user_addr,					data->npages, 0, 0, data->pagevec, NULL);		up_read(&current->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 + -