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 + -
显示快捷键?