loop.c

来自「linux 内核源代码」· C语言 代码 · 共 1,565 行 · 第 1/3 页

C
1,565
字号
/* *  linux/drivers/block/loop.c * *  Written by Theodore Ts'o, 3/29/93 * * Copyright 1993 by Theodore Ts'o.  Redistribution of this file is * permitted under the GNU General Public License. * * DES encryption plus some minor changes by Werner Almesberger, 30-MAY-1993 * more DES encryption plus IDEA encryption by Nicholas J. Leon, June 20, 1996 * * Modularized and updated for 1.1.16 kernel - Mitch Dsouza 28th May 1994 * Adapted for 1.3.59 kernel - Andries Brouwer, 1 Feb 1996 * * Fixed do_loop_request() re-entrancy - Vincent.Renardias@waw.com Mar 20, 1997 * * Added devfs support - Richard Gooch <rgooch@atnf.csiro.au> 16-Jan-1998 * * Handle sparse backing files correctly - Kenn Humborg, Jun 28, 1998 * * Loadable modules and other fixes by AK, 1998 * * Make real block number available to downstream transfer functions, enables * CBC (and relatives) mode encryption requiring unique IVs per data block. * Reed H. Petty, rhp@draper.net * * Maximum number of loop devices now dynamic via max_loop module parameter. * Russell Kroll <rkroll@exploits.org> 19990701 * * Maximum number of loop devices when compiled-in now selectable by passing * max_loop=<1-255> to the kernel on boot. * Erik I. Bolsø, <eriki@himolde.no>, Oct 31, 1999 * * Completely rewrite request handling to be make_request_fn style and * non blocking, pushing work to a helper thread. Lots of fixes from * Al Viro too. * Jens Axboe <axboe@suse.de>, Nov 2000 * * Support up to 256 loop devices * Heinz Mauelshagen <mge@sistina.com>, Feb 2002 * * Support for falling back on the write file operation when the address space * operations prepare_write and/or commit_write are not available on the * backing filesystem. * Anton Altaparmakov, 16 Feb 2005 * * Still To Fix: * - Advisory locking is ignored here. * - Should use an own CAP_* category instead of CAP_SYS_ADMIN * */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/sched.h>#include <linux/fs.h>#include <linux/file.h>#include <linux/stat.h>#include <linux/errno.h>#include <linux/major.h>#include <linux/wait.h>#include <linux/blkdev.h>#include <linux/blkpg.h>#include <linux/init.h>#include <linux/smp_lock.h>#include <linux/swap.h>#include <linux/slab.h>#include <linux/loop.h>#include <linux/compat.h>#include <linux/suspend.h>#include <linux/freezer.h>#include <linux/writeback.h>#include <linux/buffer_head.h>		/* for invalidate_bdev() */#include <linux/completion.h>#include <linux/highmem.h>#include <linux/gfp.h>#include <linux/kthread.h>#include <linux/splice.h>#include <asm/uaccess.h>static LIST_HEAD(loop_devices);static DEFINE_MUTEX(loop_devices_mutex);/* * Transfer functions */static int transfer_none(struct loop_device *lo, int cmd,			 struct page *raw_page, unsigned raw_off,			 struct page *loop_page, unsigned loop_off,			 int size, sector_t real_block){	char *raw_buf = kmap_atomic(raw_page, KM_USER0) + raw_off;	char *loop_buf = kmap_atomic(loop_page, KM_USER1) + loop_off;	if (cmd == READ)		memcpy(loop_buf, raw_buf, size);	else		memcpy(raw_buf, loop_buf, size);	kunmap_atomic(raw_buf, KM_USER0);	kunmap_atomic(loop_buf, KM_USER1);	cond_resched();	return 0;}static int transfer_xor(struct loop_device *lo, int cmd,			struct page *raw_page, unsigned raw_off,			struct page *loop_page, unsigned loop_off,			int size, sector_t real_block){	char *raw_buf = kmap_atomic(raw_page, KM_USER0) + raw_off;	char *loop_buf = kmap_atomic(loop_page, KM_USER1) + loop_off;	char *in, *out, *key;	int i, keysize;	if (cmd == READ) {		in = raw_buf;		out = loop_buf;	} else {		in = loop_buf;		out = raw_buf;	}	key = lo->lo_encrypt_key;	keysize = lo->lo_encrypt_key_size;	for (i = 0; i < size; i++)		*out++ = *in++ ^ key[(i & 511) % keysize];	kunmap_atomic(raw_buf, KM_USER0);	kunmap_atomic(loop_buf, KM_USER1);	cond_resched();	return 0;}static int xor_init(struct loop_device *lo, const struct loop_info64 *info){	if (unlikely(info->lo_encrypt_key_size <= 0))		return -EINVAL;	return 0;}static struct loop_func_table none_funcs = {	.number = LO_CRYPT_NONE,	.transfer = transfer_none,}; 	static struct loop_func_table xor_funcs = {	.number = LO_CRYPT_XOR,	.transfer = transfer_xor,	.init = xor_init}; 	/* xfer_funcs[0] is special - its release function is never called */static struct loop_func_table *xfer_funcs[MAX_LO_CRYPT] = {	&none_funcs,	&xor_funcs};static loff_t get_loop_size(struct loop_device *lo, struct file *file){	loff_t size, offset, loopsize;	/* Compute loopsize in bytes */	size = i_size_read(file->f_mapping->host);	offset = lo->lo_offset;	loopsize = size - offset;	if (lo->lo_sizelimit > 0 && lo->lo_sizelimit < loopsize)		loopsize = lo->lo_sizelimit;	/*	 * Unfortunately, if we want to do I/O on the device,	 * the number of 512-byte sectors has to fit into a sector_t.	 */	return loopsize >> 9;}static intfigure_loop_size(struct loop_device *lo){	loff_t size = get_loop_size(lo, lo->lo_backing_file);	sector_t x = (sector_t)size;	if (unlikely((loff_t)x != size))		return -EFBIG;	set_capacity(lo->lo_disk, x);	return 0;					}static inline intlo_do_transfer(struct loop_device *lo, int cmd,	       struct page *rpage, unsigned roffs,	       struct page *lpage, unsigned loffs,	       int size, sector_t rblock){	if (unlikely(!lo->transfer))		return 0;	return lo->transfer(lo, cmd, rpage, roffs, lpage, loffs, size, rblock);}/** * do_lo_send_aops - helper for writing data to a loop device * * This is the fast version for backing filesystems which implement the address * space operations write_begin and write_end. */static int do_lo_send_aops(struct loop_device *lo, struct bio_vec *bvec,		int bsize, loff_t pos, struct page *unused){	struct file *file = lo->lo_backing_file; /* kudos to NFsckingS */	struct address_space *mapping = file->f_mapping;	pgoff_t index;	unsigned offset, bv_offs;	int len, ret;	mutex_lock(&mapping->host->i_mutex);	index = pos >> PAGE_CACHE_SHIFT;	offset = pos & ((pgoff_t)PAGE_CACHE_SIZE - 1);	bv_offs = bvec->bv_offset;	len = bvec->bv_len;	while (len > 0) {		sector_t IV;		unsigned size, copied;		int transfer_result;		struct page *page;		void *fsdata;		IV = ((sector_t)index << (PAGE_CACHE_SHIFT - 9))+(offset >> 9);		size = PAGE_CACHE_SIZE - offset;		if (size > len)			size = len;		ret = pagecache_write_begin(file, mapping, pos, size, 0,							&page, &fsdata);		if (ret)			goto fail;		transfer_result = lo_do_transfer(lo, WRITE, page, offset,				bvec->bv_page, bv_offs, size, IV);		copied = size;		if (unlikely(transfer_result))			copied = 0;		ret = pagecache_write_end(file, mapping, pos, size, copied,							page, fsdata);		if (ret < 0 || ret != copied)			goto fail;		if (unlikely(transfer_result))			goto fail;		bv_offs += copied;		len -= copied;		offset = 0;		index++;		pos += copied;	}	ret = 0;out:	mutex_unlock(&mapping->host->i_mutex);	return ret;fail:	ret = -1;	goto out;}/** * __do_lo_send_write - helper for writing data to a loop device * * This helper just factors out common code between do_lo_send_direct_write() * and do_lo_send_write(). */static int __do_lo_send_write(struct file *file,		u8 *buf, const int len, loff_t pos){	ssize_t bw;	mm_segment_t old_fs = get_fs();	set_fs(get_ds());	bw = file->f_op->write(file, buf, len, &pos);	set_fs(old_fs);	if (likely(bw == len))		return 0;	printk(KERN_ERR "loop: Write error at byte offset %llu, length %i.\n",			(unsigned long long)pos, len);	if (bw >= 0)		bw = -EIO;	return bw;}/** * do_lo_send_direct_write - helper for writing data to a loop device * * This is the fast, non-transforming version for backing filesystems which do * not implement the address space operations write_begin and write_end. * It uses the write file operation which should be present on all writeable * filesystems. */static int do_lo_send_direct_write(struct loop_device *lo,		struct bio_vec *bvec, int bsize, loff_t pos, struct page *page){	ssize_t bw = __do_lo_send_write(lo->lo_backing_file,			kmap(bvec->bv_page) + bvec->bv_offset,			bvec->bv_len, pos);	kunmap(bvec->bv_page);	cond_resched();	return bw;}/** * do_lo_send_write - helper for writing data to a loop device * * This is the slow, transforming version for filesystems which do not * implement the address space operations write_begin and write_end.  It * uses the write file operation which should be present on all writeable * filesystems. * * Using fops->write is slower than using aops->{prepare,commit}_write in the * transforming case because we need to double buffer the data as we cannot do * the transformations in place as we do not have direct access to the * destination pages of the backing file. */static int do_lo_send_write(struct loop_device *lo, struct bio_vec *bvec,		int bsize, loff_t pos, struct page *page){	int ret = lo_do_transfer(lo, WRITE, page, 0, bvec->bv_page,			bvec->bv_offset, bvec->bv_len, pos >> 9);	if (likely(!ret))		return __do_lo_send_write(lo->lo_backing_file,				page_address(page), bvec->bv_len,				pos);	printk(KERN_ERR "loop: Transfer error at byte offset %llu, "			"length %i.\n", (unsigned long long)pos, bvec->bv_len);	if (ret > 0)		ret = -EIO;	return ret;}static int lo_send(struct loop_device *lo, struct bio *bio, int bsize,		loff_t pos){	int (*do_lo_send)(struct loop_device *, struct bio_vec *, int, loff_t,			struct page *page);	struct bio_vec *bvec;	struct page *page = NULL;	int i, ret = 0;	do_lo_send = do_lo_send_aops;	if (!(lo->lo_flags & LO_FLAGS_USE_AOPS)) {		do_lo_send = do_lo_send_direct_write;		if (lo->transfer != transfer_none) {			page = alloc_page(GFP_NOIO | __GFP_HIGHMEM);			if (unlikely(!page))				goto fail;			kmap(page);			do_lo_send = do_lo_send_write;		}	}	bio_for_each_segment(bvec, bio, i) {		ret = do_lo_send(lo, bvec, bsize, pos, page);		if (ret < 0)			break;		pos += bvec->bv_len;	}	if (page) {		kunmap(page);		__free_page(page);	}out:	return ret;fail:	printk(KERN_ERR "loop: Failed to allocate temporary page for write.\n");	ret = -ENOMEM;	goto out;}struct lo_read_data {	struct loop_device *lo;	struct page *page;	unsigned offset;	int bsize;};static intlo_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf,		struct splice_desc *sd){	struct lo_read_data *p = sd->u.data;	struct loop_device *lo = p->lo;	struct page *page = buf->page;	sector_t IV;	size_t size;	int ret;	ret = buf->ops->confirm(pipe, buf);	if (unlikely(ret))		return ret;	IV = ((sector_t) page->index << (PAGE_CACHE_SHIFT - 9)) +							(buf->offset >> 9);	size = sd->len;	if (size > p->bsize)		size = p->bsize;	if (lo_do_transfer(lo, READ, page, buf->offset, p->page, p->offset, size, IV)) {		printk(KERN_ERR "loop: transfer error block %ld\n",		       page->index);		size = -EINVAL;	}	flush_dcache_page(p->page);	if (size > 0)		p->offset += size;	return size;}static intlo_direct_splice_actor(struct pipe_inode_info *pipe, struct splice_desc *sd){	return __splice_from_pipe(pipe, sd, lo_splice_actor);}static intdo_lo_receive(struct loop_device *lo,	      struct bio_vec *bvec, int bsize, loff_t pos){	struct lo_read_data cookie;	struct splice_desc sd;	struct file *file;	long retval;	cookie.lo = lo;	cookie.page = bvec->bv_page;	cookie.offset = bvec->bv_offset;	cookie.bsize = bsize;	sd.len = 0;	sd.total_len = bvec->bv_len;	sd.flags = 0;	sd.pos = pos;	sd.u.data = &cookie;	file = lo->lo_backing_file;	retval = splice_direct_to_actor(file, &sd, lo_direct_splice_actor);	if (retval < 0)		return retval;	return 0;}static intlo_receive(struct loop_device *lo, struct bio *bio, int bsize, loff_t pos){	struct bio_vec *bvec;	int i, ret = 0;	bio_for_each_segment(bvec, bio, i) {		ret = do_lo_receive(lo, bvec, bsize, pos);		if (ret < 0)			break;		pos += bvec->bv_len;	}	return ret;}static int do_bio_filebacked(struct loop_device *lo, struct bio *bio){	loff_t pos;	int ret;	pos = ((loff_t) bio->bi_sector << 9) + lo->lo_offset;	if (bio_rw(bio) == WRITE)		ret = lo_send(lo, bio, lo->lo_blocksize, pos);	else		ret = lo_receive(lo, bio, lo->lo_blocksize, pos);	return ret;}/* * Add bio to back of pending list */static void loop_add_bio(struct loop_device *lo, struct bio *bio){	if (lo->lo_biotail) {		lo->lo_biotail->bi_next = bio;		lo->lo_biotail = bio;	} else		lo->lo_bio = lo->lo_biotail = bio;}/* * Grab first pending buffer */static struct bio *loop_get_bio(struct loop_device *lo){	struct bio *bio;	if ((bio = lo->lo_bio)) {		if (bio == lo->lo_biotail)			lo->lo_biotail = NULL;		lo->lo_bio = bio->bi_next;		bio->bi_next = NULL;	}	return bio;}static int loop_make_request(struct request_queue *q, struct bio *old_bio){	struct loop_device *lo = q->queuedata;	int rw = bio_rw(old_bio);	if (rw == READA)		rw = READ;	BUG_ON(!lo || (rw != READ && rw != WRITE));

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?