<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">From: Bryn Reeves &lt;breeves@redhat.com&gt;

This implements a loopback target for device mapper allowing a regular
file to be treated as a block device.

Signed-off-by: Bryn Reeves &lt;breeves@redhat.com&gt;
Signed-off-by: Alasdair G Kergon &lt;agk@redhat.com&gt;

---
 drivers/md/Kconfig   |    9 
 drivers/md/Makefile  |    1 
 drivers/md/dm-loop.c | 1033 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1043 insertions(+)

Index: linux-2.6.28-rc2/drivers/md/Kconfig
===================================================================
--- linux-2.6.28-rc2.orig/drivers/md/Kconfig	2008-11-04 12:10:26.000000000 +0000
+++ linux-2.6.28-rc2/drivers/md/Kconfig	2008-11-04 12:47:38.000000000 +0000
@@ -243,6 +243,15 @@ config DM_CRYPT
 
 	  If unsure, say N.
 
+config DM_LOOP
+	tristate "Loop target (EXPERIMENTAL)"
+	depends on BLK_DEV_DM &amp;&amp; EXPERIMENTAL
+	---help---
+	  This device-mapper target allows you to treat a regular file as
+	  a block device.
+
+	  If unsure, say N.
+
 config DM_SNAPSHOT
        tristate "Snapshot target"
        depends on BLK_DEV_DM
Index: linux-2.6.28-rc2/drivers/md/Makefile
===================================================================
--- linux-2.6.28-rc2.orig/drivers/md/Makefile	2008-11-04 12:10:26.000000000 +0000
+++ linux-2.6.28-rc2/drivers/md/Makefile	2008-11-04 12:47:38.000000000 +0000
@@ -32,6 +32,7 @@ obj-$(CONFIG_BLK_DEV_MD)	+= md-mod.o
 obj-$(CONFIG_BLK_DEV_DM)	+= dm-mod.o
 obj-$(CONFIG_DM_CRYPT)		+= dm-crypt.o
 obj-$(CONFIG_DM_DELAY)		+= dm-delay.o
+obj-$(CONFIG_DM_LOOP)		+= dm-loop.o
 obj-$(CONFIG_DM_MULTIPATH)	+= dm-multipath.o dm-round-robin.o
 obj-$(CONFIG_DM_SNAPSHOT)	+= dm-snapshot.o
 obj-$(CONFIG_DM_MIRROR)		+= dm-mirror.o dm-log.o dm-region-hash.o
Index: linux-2.6.28-rc2/drivers/md/dm-loop.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.28-rc2/drivers/md/dm-loop.c	2008-11-04 12:47:58.000000000 +0000
@@ -0,0 +1,1033 @@
+/*
+ * Copyright (C) 2006-2008 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of device-mapper.
+ *
+ * Extent mapping implementation heavily influenced by mm/swapfile.c
+ * Bryn Reeves &lt;breeves@redhat.com&gt;
+ *
+ * File mapping and block lookup algorithms support by
+ * Heinz Mauelshagen &lt;hjm@redhat.com&gt;.
+ *
+ * This file is released under the GPL.
+ */
+
+#include &lt;linux/kernel.h&gt;
+#include &lt;linux/slab.h&gt;
+#include &lt;linux/fs.h&gt;
+#include &lt;linux/module.h&gt;
+#include &lt;linux/vmalloc.h&gt;
+#include &lt;linux/syscalls.h&gt;
+#include &lt;linux/workqueue.h&gt;
+#include &lt;linux/file.h&gt;
+#include &lt;linux/bio.h&gt;
+#include &lt;linux/device-mapper.h&gt;
+
+#include "dm-bio-list.h"
+
+#define DM_LOOP_DAEMON "kloopd"
+#define DM_MSG_PREFIX "loop"
+
+enum flags { DM_LOOP_BMAP, DM_LOOP_FSIO };
+
+/*--------------------------------------------------------------------
+ * Loop context
+ *--------------------------------------------------------------------*/
+
+struct loop_c {
+	unsigned long flags;
+
+	/* Backing store */
+
+	struct file *filp;
+	char *path;
+	loff_t offset;
+	struct block_device *bdev;
+	unsigned blkbits;		/* file system block size shift bits */
+
+	loff_t size;			/* size of entire file in bytes */
+	loff_t blocks;			/* blocks allocated to loop file */
+	sector_t mapped_sectors;	/* size of mapped area in sectors */
+
+	int (*map_fn)(struct dm_target *, struct bio *);
+	void *map_data;
+};
+
+/*
+ * Block map extent
+ */
+struct dm_loop_extent {
+	sector_t start; 		/* start sector in mapped device */
+	sector_t to;			/* start sector on target device */
+	sector_t len;			/* length in sectors */
+};
+
+/*
+ * Temporary extent list
+ */
+struct extent_list {
+	struct dm_loop_extent *extent;
+	struct list_head list;
+};
+
+static struct kmem_cache *dm_loop_extent_cache;
+
+/*
+ * Block map private context
+ */
+struct block_map_c {
+	int nr_extents;			/* number of extents in map */
+	struct dm_loop_extent **map;	/* linear map of extent pointers */
+	struct dm_loop_extent **mru;	/* pointer to mru entry */
+	spinlock_t mru_lock;		/* protects mru */
+};
+
+/*
+ * File map private context
+ */
+struct file_map_c {
+	spinlock_t lock;		/* protects in */
+	struct bio_list in;		/* new bios for processing */
+	struct bio_list work;		/* bios queued for processing */
+	struct workqueue_struct *wq;	/* workqueue */
+	struct work_struct ws;		/* loop work */
+	struct loop_c *loop;		/* for filp &amp; offset */
+};
+
+/*--------------------------------------------------------------------
+ * Generic helpers
+ *--------------------------------------------------------------------*/
+
+static sector_t blk2sect(struct loop_c *lc, blkcnt_t block)
+{
+	return block &lt;&lt; (lc-&gt;blkbits - SECTOR_SHIFT);
+}
+
+static blkcnt_t sec2blk(struct loop_c *lc, sector_t sector)
+{
+	return sector &gt;&gt; (lc-&gt;blkbits - SECTOR_SHIFT);
+}
+
+/*--------------------------------------------------------------------
+ * File I/O helpers
+ *--------------------------------------------------------------------*/
+
+/*
+ * transfer data to/from file using the read/write file_operations.
+ */
+static int fs_io(int rw, struct file *filp, loff_t *pos, struct bio_vec *bv)
+{
+	ssize_t r;
+	void __user *ptr = (void __user __force *) kmap(bv-&gt;bv_page) +
+			   bv-&gt;bv_offset;
+	mm_segment_t old_fs = get_fs();
+
+	set_fs(get_ds());
+	r = (rw == READ) ? filp-&gt;f_op-&gt;read(filp, ptr, bv-&gt;bv_len, pos) :
+			   filp-&gt;f_op-&gt;write(filp, ptr, bv-&gt;bv_len, pos);
+	set_fs(old_fs);
+	kunmap(bv-&gt;bv_page);
+
+	return (r == bv-&gt;bv_len) ? 0 : -EIO;
+}
+
+/*
+ * Handle I/O for one bio
+ */
+static void do_one_bio(struct file_map_c *fc, struct bio *bio)
+{
+	int r = 0, rw = bio_data_dir(bio);
+	loff_t start = (bio-&gt;bi_sector &lt;&lt; 9) + fc-&gt;loop-&gt;offset, pos = start;
+	struct bio_vec *bv, *bv_end = bio-&gt;bi_io_vec + bio-&gt;bi_vcnt;
+
+	for (bv = bio-&gt;bi_io_vec; bv &lt; bv_end; bv++) {
+		r = fs_io(rw, fc-&gt;loop-&gt;filp, &amp;pos, bv);
+		if (r) {
+			DMERR("%s error %d", rw ? "write" : "read", r);
+			break;
+		}
+	}
+
+	bio_endio(bio, r);
+}
+
+/*
+ * Worker thread for a 'file' type loop device
+ */
+static void do_loop_work(struct work_struct *ws)
+{
+	struct file_map_c *fc = container_of(ws, struct file_map_c, ws);
+	struct bio *bio;
+
+	/* quickly grab all new bios queued and add them to the work list */
+	spin_lock_irq(&amp;fc-&gt;lock);
+	bio_list_merge(&amp;fc-&gt;work, &amp;fc-&gt;in);
+	bio_list_init(&amp;fc-&gt;in);
+	spin_unlock_irq(&amp;fc-&gt;lock);
+
+	/* work the list and do file I/O on all bios */
+	while ((bio = bio_list_pop(&amp;fc-&gt;work)))
+		do_one_bio(fc, bio);
+}
+
+/*
+ * Create work queue and initialize work
+ */
+static int loop_work_init(struct loop_c *lc)
+{
+	struct file_map_c *fc = lc-&gt;map_data;
+
+	fc-&gt;wq = create_singlethread_workqueue(DM_LOOP_DAEMON);
+	if (!fc-&gt;wq)
+		return -ENOMEM;
+
+	return 0;
+}
+
+/*
+ * Destroy work queue
+ */
+static void loop_work_exit(struct loop_c *lc)
+{
+	struct file_map_c *fc = lc-&gt;map_data;
+
+	if (fc-&gt;wq)
+		destroy_workqueue(fc-&gt;wq);
+}
+
+/*
+ * DM_LOOP_FSIO map_fn. Mapping just queues bios to the file map
+ * context and lets the daemon deal with them.
+ */
+static int loop_file_map(struct dm_target *ti, struct bio *bio)
+{
+	int wake;
+	struct loop_c *lc = ti-&gt;private;
+	struct file_map_c *fc = lc-&gt;map_data;
+
+	spin_lock_irq(&amp;fc-&gt;lock);
+	wake = bio_list_empty(&amp;fc-&gt;in);
+	bio_list_add(&amp;fc-&gt;in, bio);
+	spin_unlock_irq(&amp;fc-&gt;lock);
+
+	/*
+	 * Only call queue_work() if necessary to avoid
+	 * superfluous preempt_{disable/enable}() overhead.
+	 */
+	if (wake)
+		queue_work(fc-&gt;wq, &amp;fc-&gt;ws);
+
+	/* Handling bio - will submit later. */
+	return 0;
+}
+
+/*
+ * Shutdown the workqueue and free a file mapping
+ */
+static void destroy_file_map(struct loop_c *lc)
+{
+	loop_work_exit(lc);
+	kfree(lc-&gt;map_data);
+}
+
+/*
+ * Set up a file map context and workqueue
+ */
+static int setup_file_map(struct loop_c *lc)
+{
+	struct file_map_c *fc = kzalloc(sizeof(*fc), GFP_KERNEL);
+
+	if (!fc)
+		return -ENOMEM;
+
+	spin_lock_init(&amp;fc-&gt;lock);
+	bio_list_init(&amp;fc-&gt;in);
+	bio_list_init(&amp;fc-&gt;work);
+	INIT_WORK(&amp;fc-&gt;ws, do_loop_work);
+	fc-&gt;loop = lc;
+
+	lc-&gt;map_data = fc;
+	lc-&gt;map_fn = loop_file_map;
+
+	return loop_work_init(lc);
+}
+
+/*--------------------------------------------------------------------
+ * Block I/O helpers
+ *--------------------------------------------------------------------*/
+
+static int contains_sector(struct dm_loop_extent *e, sector_t s)
+{
+	if (likely(e))
+		return s &lt; (e-&gt;start + (e-&gt;len)) &amp;&amp; s &gt;= e-&gt;start;
+
+	return 0;
+}
+
+/*
+ * Walk over a linked list of extent_list structures, freeing them as
+ * we go. Does not free el-&gt;extent.
+ */
+static void destroy_extent_list(struct list_head *head)
+{
+	struct list_head *curr, *n;
+	struct extent_list *el;
+
+	if (list_empty(head))
+		return;
+
+	list_for_each_safe(curr, n, head) {
+		el = list_entry(curr, struct extent_list, list);
+		list_del(curr);
+		kfree(el);
+	}
+}
+
+/*
+ * Add a new extent to the tail of the list at *head with
+ * start/to/len parameters. Allocates from the extent cache.
+ */
+static int list_add_extent(struct list_head *head, sector_t start,
+			   sector_t to, sector_t len)
+{
+	struct dm_loop_extent *extent;
+	struct extent_list *list;
+
+	extent = kmem_cache_alloc(dm_loop_extent_cache, GFP_KERNEL);
+	if (!extent)
+		goto out;
+
+	list = kmalloc(sizeof(*list), GFP_KERNEL);
+	if (!list)
+		goto out;
+
+	extent-&gt;start = start;
+	extent-&gt;to = to;
+	extent-&gt;len = len;
+
+	list-&gt;extent = extent;
+	list_add_tail(&amp;list-&gt;list, head);
+
+	return 0;
+out:
+	if (extent)
+		kmem_cache_free(dm_loop_extent_cache, extent);
+	return -ENOMEM;
+}
+
+/*
+ * Return an extent range (i.e. beginning and ending physical block numbers).
+ */
+static int extent_range(struct inode *inode,
+			blkcnt_t logical_blk, blkcnt_t last_blk,
+			blkcnt_t *begin_blk, blkcnt_t *end_blk)
+{
+	sector_t dist = 0, phys_blk, probe_blk = logical_blk;
+
+	/* Find beginning physical block of extent starting at logical_blk. */
+	*begin_blk = phys_blk = bmap(inode, probe_blk);
+	if (!phys_blk)
+		return -ENXIO;
+
+	for (; phys_blk == *begin_blk + dist; dist++) {
+		*end_blk = phys_blk;
+		if (++probe_blk &gt; last_blk)
+			break;
+
+		phys_blk = bmap(inode, probe_blk);
+		if (unlikely(!phys_blk))
+			return -ENXIO;
+	}
+
+	return 0;
+}
+
+/*
+ * Create a sequential list of extents from an inode and return
+ * it in *head. On success return the number of extents found or
+ * -ERRNO on failure.
+ */
+static int loop_extents(struct loop_c *lc, struct inode *inode,
+			struct list_head *head)
+{
+	sector_t start = 0;
+	int r, nr_extents = 0;
+	blkcnt_t nr_blks = 0, begin_blk = 0, end_blk = 0;
+	blkcnt_t after_last_blk = sec2blk(lc,
+			(lc-&gt;mapped_sectors + (lc-&gt;offset &gt;&gt; 9)));
+	blkcnt_t logical_blk = sec2blk(lc, (lc-&gt;offset &gt;&gt; 9));
+
+	/* for each block in the mapped region */
+	while (logical_blk &lt; after_last_blk) {
+		r = extent_range(inode, logical_blk, after_last_blk - 1,
+				 &amp;begin_blk, &amp;end_blk);
+
+		/* sparse file fallback */
+		if (unlikely(r)) {
+			DMWARN("%s has a hole; sparse file detected - "
+			       "switching to filesystem I/O", lc-&gt;path);
+			clear_bit(DM_LOOP_BMAP, &amp;lc-&gt;flags);
+			set_bit(DM_LOOP_FSIO, &amp;lc-&gt;flags);
+			return r;
+		}
+
+		nr_blks = 1 + end_blk - begin_blk;
+
+		if (unlikely(!nr_blks))
+			continue;
+
+		r = list_add_extent(head, start, blk2sect(lc, begin_blk),
+				    blk2sect(lc, nr_blks));
+		if (unlikely(r))
+			return r;
+
+		/* advance to next extent */
+		nr_extents++;
+		start += blk2sect(lc, nr_blks);
+		logical_blk += nr_blks;
+	}
+
+	return nr_extents;
+}
+
+/*
+ * Walk over the extents in a block_map_c, returning them to the cache and
+ * freeing bc-&gt;map and bc.
+ */
+static void destroy_block_map(struct block_map_c *bc)
+{
+	unsigned i;
+
+	if (!bc)
+		return;
+
+	for (i = 0; i &lt; bc-&gt;nr_extents; i++)
+		kmem_cache_free(dm_loop_extent_cache, bc-&gt;map[i]);
+
+	DMDEBUG("destroying block map of %d entries", i);
+
+	vfree(bc-&gt;map);
+	kfree(bc);
+}
+
+/*
+ * Find an extent in *bc using binary search. Returns a pointer into the
+ * extent map. Calculate index as (extent - bc-&gt;map).
+ */
+static struct dm_loop_extent **extent_binary_lookup(struct block_map_c *bc,
+	    struct dm_loop_extent **extent_mru, sector_t sector)
+{
+	unsigned nr_extents = bc-&gt;nr_extents;
+	unsigned delta, dist, prev_dist = 0;
+	struct dm_loop_extent **eptr;
+
+	/* Optimize lookup range based on MRU extent. */
+	dist = extent_mru - bc-&gt;map;
+	if ((*extent_mru)-&gt;start &gt;= sector)
+		delta = dist = dist / 2;
+	else {
+		delta = (nr_extents - dist) / 2;
+		dist += delta;
+	}
+
+	eptr = bc-&gt;map + dist;
+	while (*eptr &amp;&amp; !contains_sector(*eptr, sector)) {
+		if (sector &gt;= (*eptr)-&gt;start + (*eptr)-&gt;len) {
+			prev_dist = dist;
+			if (delta &gt; 1)
+				delta /= 2;
+			dist += delta;
+		} else {
+			delta = (dist - prev_dist) / 2;
+			if (!delta)
+				delta = 1;
+			dist -= delta;
+		}
+		eptr = bc-&gt;map + dist;
+	}
+
+	return eptr;
+}
+
+/*
+ * Lookup an extent for a sector using the mru cache and binary search.
+ */
+static struct dm_loop_extent *extent_lookup(struct block_map_c *bc,
+					    sector_t sector)
+{
+	struct dm_loop_extent **eptr;
+
+	spin_lock_irq(&amp;bc-&gt;mru_lock);
+	eptr = bc-&gt;mru;
+	spin_unlock_irq(&amp;bc-&gt;mru_lock);
+
+	if (contains_sector(*eptr, sector))
+		return *eptr;
+
+	eptr = extent_binary_lookup(bc, eptr, sector);
+	if (!eptr)
+		return NULL;
+
+	spin_lock_irq(&amp;bc-&gt;mru_lock);
+	bc-&gt;mru = eptr;
+	spin_unlock_irq(&amp;bc-&gt;mru_lock);
+
+	return *eptr;
+}
+
+/*
+ * DM_LOOP_BMAP map_fn. Looks up the sector in the extent map and
+ * rewrites the bio device and bi_sector fields.
+ */
+static int loop_block_map(struct dm_target *ti, struct bio *bio)
+{
+	struct loop_c *lc = ti-&gt;private;
+	struct dm_loop_extent *extent = extent_lookup(lc-&gt;map_data,
+						      bio-&gt;bi_sector);
+
+	if (likely(extent)) {
+		bio-&gt;bi_bdev = lc-&gt;bdev;
+		bio-&gt;bi_sector = extent-&gt;to + (bio-&gt;bi_sector - extent-&gt;start);
+		return 1;	/* Done with bio -&gt; submit */
+	}
+
+	DMERR("no matching extent in map for sector %llu",
+	      (unsigned long long) bio-&gt;bi_sector + ti-&gt;begin);
+	BUG();
+
+	return -EIO;
+}
+
+/*
+ * Turn an extent_list into a linear pointer map of nr_extents + 1 entries
+ * and set the final entry to NULL.
+ */
+static struct dm_loop_extent **build_extent_map(struct list_head *head,
+						int nr_extents,
+						unsigned long *flags)
+{
+	unsigned map_size, cache_size;
+	struct dm_loop_extent **map, **curr;
+	struct list_head *pos;
+	struct extent_list *el;
+
+	map_size = 1 + (sizeof(*map) * nr_extents);
+	cache_size = kmem_cache_size(dm_loop_extent_cache) * nr_extents;
+
+	map = vmalloc(map_size);
+	curr = map;
+
+	DMDEBUG("allocated extent map of %u %s for %d extents (%u %s)",
+		(map_size &lt; 8192) ? map_size : map_size &gt;&gt; 10,
+		(map_size &lt; 8192) ? "bytes" : "kilobytes", nr_extents,
+		(cache_size &lt; 8192) ? cache_size : cache_size &gt;&gt; 10,
+		(cache_size &lt; 8192) ? "bytes" : "kilobytes");
+
+	list_for_each(pos, head) {
+		el = list_entry(pos, struct extent_list, list);
+		*(curr++) = el-&gt;extent;
+	}
+	*curr = NULL;
+
+	return map;
+}
+
+/*
+ * Set up a block map context and extent map
+ */
+static int setup_block_map(struct loop_c *lc, struct inode *inode)
+{
+	int r, nr_extents;
+	struct block_map_c *bc;
+	LIST_HEAD(head);
+
+	if (!inode || !inode-&gt;i_sb || !inode-&gt;i_sb-&gt;s_bdev)
+		return -ENXIO;
+
+	/* build a linked list of extents in linear order */
+	r = nr_extents = loop_extents(lc, inode, &amp;head);
+	if (nr_extents &lt; 1)
+		goto out;
+
+	r = -ENOMEM;
+	bc = kzalloc(sizeof(*bc), GFP_KERNEL);
+	if (!bc)
+		goto out;
+
+	/* create a linear map of pointers into the extent cache */
+	bc-&gt;map = build_extent_map(&amp;head, nr_extents, &amp;lc-&gt;flags);
+	destroy_extent_list(&amp;head);
+
+	if (IS_ERR(bc-&gt;map)) {
+		r = PTR_ERR(bc-&gt;map);
+		goto out;
+	}
+
+	spin_lock_init(&amp;bc-&gt;mru_lock);
+	bc-&gt;mru = bc-&gt;map;
+	bc-&gt;nr_extents = nr_extents;
+	lc-&gt;bdev = inode-&gt;i_sb-&gt;s_bdev;
+	lc-&gt;map_data = bc;
+	lc-&gt;map_fn = loop_block_map;
+
+	return 0;
+
+out:
+	return r;
+}
+
+/*--------------------------------------------------------------------
+ * Generic helpers
+ *--------------------------------------------------------------------*/
+
+/*
+ * Invalidate all unlocked loop file pages
+ */
+static int loop_invalidate_file(struct file *filp)
+{
+	int r;
+
+	/* Same as generic_file_direct_IO() */
+	unmap_mapping_range(filp-&gt;f_mapping, 0, ~0UL, 0);
+
+	r = filemap_write_and_wait(filp-&gt;f_mapping);
+	if (r)
+		return r;
+
+	/*
+	 * This will remove all pages except dirty ones.
+	 * If there are dirty pages at this point, it means that the user
+	 * is writing to the file and the coherency is lost anyway.
+	 * If the user was writing to the file simultaneously, this
+	 * returns non-zero, but we ignore that.
+	 */
+	invalidate_inode_pages2_range(filp-&gt;f_mapping, 0, ~0UL);
+
+	return 0;
+}
+
+/*
+ * Acquire or release a "no-truncate" lock on *filp.
+ * We overload the S_SWAPFILE flag for loop targets because
+ * it provides the same no-truncate semantics we require, and
+ * holding onto i_sem is no longer an option.
+ */
+static void file_truncate_lock(struct file *filp)
+{
+	struct inode *inode = filp-&gt;f_mapping-&gt;host;
+
+	mutex_lock(&amp;inode-&gt;i_mutex);
+	inode-&gt;i_flags |= S_SWAPFILE;
+	mutex_unlock(&amp;inode-&gt;i_mutex);
+}
+
+static void file_truncate_unlock(struct file *filp)
+{
+	struct inode *inode = filp-&gt;f_mapping-&gt;host;
+
+	mutex_lock(&amp;inode-&gt;i_mutex);
+	inode-&gt;i_flags &amp;= ~S_SWAPFILE;
+	mutex_unlock(&amp;inode-&gt;i_mutex);
+}
+
+/*
+ * Fill out split_io for taget backing store
+ */
+static void set_split_io(struct dm_target *ti)
+{
+	struct loop_c *lc = ti-&gt;private;
+
+	if (test_bit(DM_LOOP_BMAP, &amp;lc-&gt;flags))
+		/* Split I/O at block boundaries */
+		ti-&gt;split_io = 1 &lt;&lt; (lc-&gt;blkbits - SECTOR_SHIFT);
+	else
+		ti-&gt;split_io = 64;
+
+	DMDEBUG("splitting io at %llu sector boundaries",
+		(unsigned long long) ti-&gt;split_io);
+}
+
+/*
+ * Check that the loop file is regular and available.
+ */
+static int loop_check_file(struct dm_target *ti)
+{
+	struct loop_c *lc = ti-&gt;private;
+	struct file *filp = lc-&gt;filp;
+	struct inode *inode = filp-&gt;f_mapping-&gt;host;
+
+	if (!inode)
+		return -ENXIO;
+
+	ti-&gt;error = "backing file must be a regular file";
+	if (!S_ISREG(inode-&gt;i_mode))
+		return -EINVAL;
+
+	ti-&gt;error = "backing file is mapped into userspace for writing";
+	if (mapping_writably_mapped(filp-&gt;f_mapping))
+		return -EBUSY;
+
+	if (mapping_mapped(filp-&gt;f_mapping))
+		DMWARN("%s is mapped into userspace", lc-&gt;path);
+
+	if (!inode-&gt;i_sb || !inode-&gt;i_sb-&gt;s_bdev) {
+		DMWARN("%s has no blockdevice - switching to filesystem I/O",
+		       lc-&gt;path);
+		clear_bit(DM_LOOP_BMAP, &amp;lc-&gt;flags);
+		set_bit(DM_LOOP_FSIO, &amp;lc-&gt;flags);
+	}
+
+	ti-&gt;error = "backing file already in use";
+	if (IS_SWAPFILE(inode))
+		return -EBUSY;
+
+	return 0;
+}
+
+/*
+ * Check loop file size and store it in the loop context
+ */
+static int loop_setup_size(struct dm_target *ti)
+{
+	struct loop_c *lc = ti-&gt;private;
+	struct inode *inode = lc-&gt;filp-&gt;f_mapping-&gt;host;
+	int r = -EINVAL;
+
+	lc-&gt;size = i_size_read(inode);
+	lc-&gt;blkbits = inode-&gt;i_blkbits;
+
+	ti-&gt;error = "backing file is empty";
+	if (!lc-&gt;size)
+		goto out;
+
+	DMDEBUG("set backing file size to %llu", (unsigned long long) lc-&gt;size);
+
+	ti-&gt;error = "backing file cannot be less than one block in size";
+	if (lc-&gt;size &lt; (blk2sect(lc, 1) &lt;&lt; 9))
+		goto out;
+
+	ti-&gt;error = "loop file offset must be a multiple of fs blocksize";
+	if (lc-&gt;offset &amp; ((1 &lt;&lt; lc-&gt;blkbits) - 1))
+		goto out;
+
+	ti-&gt;error = "loop file offset too large";
+	if (lc-&gt;offset &gt; (lc-&gt;size - (1 &lt;&lt; 9)))
+		goto out;
+
+	lc-&gt;mapped_sectors = (lc-&gt;size - lc-&gt;offset) &gt;&gt; 9;
+	DMDEBUG("set mapped sectors to %llu (%llu bytes)",
+		(unsigned long long) lc-&gt;mapped_sectors,
+		(lc-&gt;size - lc-&gt;offset));
+
+	if ((lc-&gt;offset + (lc-&gt;mapped_sectors &lt;&lt; 9)) &lt; lc-&gt;size)
+		DMWARN("not using %llu bytes in incomplete block at EOF",
+		       lc-&gt;size - (lc-&gt;offset + (lc-&gt;mapped_sectors &lt;&lt; 9)));
+
+	ti-&gt;error = "mapped region cannot be smaller than target size";
+	if (lc-&gt;size - lc-&gt;offset &lt; (ti-&gt;len &lt;&lt; 9))
+		goto out;
+
+	r = 0;
+
+out:
+	return r;
+}
+
+/*
+ * release a loop file
+ */
+static void loop_put_file(struct file *filp)
+{
+	if (!filp)
+		return;
+
+	file_truncate_unlock(filp);
+	filp_close(filp, NULL);
+}
+
+/*
+ * Open loop file and perform type, availability and size checks.
+ */
+static int loop_get_file(struct dm_target *ti)
+{
+	int flags = ((dm_table_get_mode(ti-&gt;table) &amp; FMODE_WRITE) ?
+		     O_RDWR : O_RDONLY) | O_LARGEFILE;
+	struct loop_c *lc = ti-&gt;private;
+	struct file *filp;
+	int r = 0;
+
+	ti-&gt;error = "could not open backing file";
+	filp = filp_open(lc-&gt;path, flags, 0);
+	if (IS_ERR(filp))
+		return PTR_ERR(filp);
+	lc-&gt;filp = filp;
+	r = loop_check_file(ti);
+	if (r)
+		goto err;
+
+	r = loop_setup_size(ti);
+	if (r)
+		goto err;
+
+	file_truncate_lock(filp);
+	return 0;
+
+err:
+	fput(filp);
+	return r;
+}
+
+/*
+ * invalidate mapped pages belonging to the loop file
+ */
+static void loop_flush(struct dm_target *ti)
+{
+	struct loop_c *lc = ti-&gt;private;
+
+	loop_invalidate_file(lc-&gt;filp);
+}
+
+/*--------------------------------------------------------------------
+ * Device-mapper target methods
+ *--------------------------------------------------------------------*/
+
+/*
+ * Generic loop map function. Re-base I/O to target begin and submit
+ */
+static int loop_map(struct dm_target *ti, struct bio *bio,
+		    union map_info *context)
+{
+	struct loop_c *lc = ti-&gt;private;
+
+	if (unlikely(bio_barrier(bio)))
+		return -EOPNOTSUPP;
+
+	bio-&gt;bi_sector -= ti-&gt;begin;
+
+	if (lc-&gt;map_fn)
+		return lc-&gt;map_fn(ti, bio);
+
+	return -EIO;
+}
+
+/*
+ * Block status helper
+ */
+static ssize_t loop_file_status(struct loop_c *lc, char *result,
+				unsigned maxlen)
+{
+	ssize_t sz = 0;
+	struct file_map_c *fc = lc-&gt;map_data;
+	int qlen;
+
+	spin_lock_irq(&amp;fc-&gt;lock);
+	qlen = bio_list_size(&amp;fc-&gt;work);
+	qlen += bio_list_size(&amp;fc-&gt;in);
+	spin_unlock_irq(&amp;fc-&gt;lock);
+
+	DMEMIT("file %d", qlen);
+
+	return sz;
+}
+
+/*
+ * File status helper
+ */
+static ssize_t loop_block_status(struct loop_c *lc, char *result,
+				 unsigned maxlen)
+{
+	ssize_t sz = 0;
+	struct block_map_c *bc = lc-&gt;map_data;
+	int mru;
+
+	spin_lock_irq(&amp;bc-&gt;mru_lock);
+	mru = bc-&gt;mru - bc-&gt;map;
+	spin_unlock_irq(&amp;bc-&gt;mru_lock);
+
+	DMEMIT("block %d %d", bc-&gt;nr_extents, mru);
+
+	return sz;
+}
+
+/*
+ * This needs some thought on handling unlinked backing files. some parts of
+ * the kernel return a cached name (now invalid), while others return a dcache
+ * "/path/to/foo (deleted)" name (never was/is valid). Which is "better" is
+ * debatable.
+ *
+ * On the one hand, using a cached name gives table output which is directly
+ * usable assuming the user re-creates the unlinked image file, on the other
+ * it is more consistent with e.g. swap to use the dcache name.
+ *
+*/
+static int loop_status(struct dm_target *ti, status_type_t type, char *result,
+		       unsigned maxlen)
+{
+	struct loop_c *lc = ti-&gt;private;
+	ssize_t sz = 0;
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		if (test_bit(DM_LOOP_BMAP, &amp;lc-&gt;flags))
+			sz += loop_block_status(lc, result, maxlen - sz);
+		else if (test_bit(DM_LOOP_FSIO, &amp;lc-&gt;flags))
+			sz += loop_file_status(lc, result, maxlen - sz);
+		break;
+
+	case STATUSTYPE_TABLE:
+		DMEMIT("%s %llu", lc-&gt;path, lc-&gt;offset);
+		break;
+	}
+	return 0;
+}
+
+/*
+ * Destroy a loopback mapping
+ */
+static void loop_dtr(struct dm_target *ti)
+{
+	struct loop_c *lc = ti-&gt;private;
+
+	if ((dm_table_get_mode(ti-&gt;table) &amp; FMODE_WRITE))
+		loop_invalidate_file(lc-&gt;filp);
+
+	if (test_bit(DM_LOOP_BMAP, &amp;lc-&gt;flags) &amp;&amp; lc-&gt;map_data)
+		destroy_block_map((struct block_map_c *)lc-&gt;map_data);
+	if (test_bit(DM_LOOP_FSIO, &amp;lc-&gt;flags) &amp;&amp; lc-&gt;map_data)
+		destroy_file_map(lc);
+
+	loop_put_file(lc-&gt;filp);
+	DMINFO("released file %s", lc-&gt;path);
+
+	kfree(lc);
+}
+
+/*
+ * Construct a loopback mapping: &lt;path&gt; &lt;offset&gt;
+ */
+static int loop_ctr(struct dm_target *ti, unsigned argc, char **argv)
+{
+	struct loop_c *lc = NULL;
+	int r = -EINVAL;
+
+	ti-&gt;error = "invalid argument count";
+	if (argc != 2)
+		goto err;
+
+	r = -ENOMEM;
+	ti-&gt;error = "cannot allocate loop context";
+	lc = kzalloc(sizeof(*lc), GFP_KERNEL);
+	if (!lc)
+		goto err;
+
+	/* default */
+	set_bit(DM_LOOP_BMAP, &amp;lc-&gt;flags);
+	ti-&gt;error = "cannot allocate loop path";
+	lc-&gt;path = kstrdup(argv[0], GFP_KERNEL);
+	if (!lc-&gt;path)
+		goto err;
+
+	ti-&gt;private = lc;
+
+	r = -EINVAL;
+	ti-&gt;error = "invalid file offset";
+	if (sscanf(argv[1], "%lld", &amp;lc-&gt;offset) != 1)
+		goto err;
+
+	if (lc-&gt;offset)
+		DMDEBUG("setting file offset to %lld", lc-&gt;offset);
+
+	/* open &amp; check file and set size parameters */
+	r = loop_get_file(ti);
+
+	/* ti-&gt;error has been set by loop_get_file */
+	if (r)
+		goto err;
+
+	ti-&gt;error = "could not create loop mapping";
+	if (test_bit(DM_LOOP_BMAP, &amp;lc-&gt;flags))
+		r = setup_block_map(lc, lc-&gt;filp-&gt;f_mapping-&gt;host);
+	if (test_bit(DM_LOOP_FSIO, &amp;lc-&gt;flags))
+		r = setup_file_map(lc);
+
+	if (r)
+		goto err_putf;
+
+	loop_invalidate_file(lc-&gt;filp);
+
+	set_split_io(ti);
+	if (lc-&gt;bdev)
+		dm_set_device_limits(ti, lc-&gt;bdev);
+
+	DMDEBUG("constructed loop target on %s "
+		"(%lldk, %llu sectors)", lc-&gt;path,
+		(lc-&gt;size &gt;&gt; 10), (unsigned long long)lc-&gt;mapped_sectors);
+	ti-&gt;error = NULL;
+
+	return 0;
+
+err_putf:
+	loop_put_file(lc-&gt;filp);
+err:
+	kfree(lc);
+	return r;
+}
+
+static struct target_type loop_target = {
+	.name = "loop",
+	.version = {0, 0, 2},
+	.module = THIS_MODULE,
+	.ctr = loop_ctr,
+	.dtr = loop_dtr,
+	.map = loop_map,
+	.presuspend = loop_flush,
+	.flush = loop_flush,
+	.status = loop_status,
+};
+
+/*--------------------------------------------------------------------
+ * Module bits
+ *--------------------------------------------------------------------*/
+static int __init dm_loop_init(void)
+{
+	int r;
+
+	r = dm_register_target(&amp;loop_target);
+	if (r &lt; 0) {
+		DMERR("register failed %d", r);
+		goto err_target;
+	}
+
+	r = -ENOMEM;
+	dm_loop_extent_cache = KMEM_CACHE(dm_loop_extent, SLAB_HWCACHE_ALIGN);
+	if (!dm_loop_extent_cache)
+		goto err_extent_cache;
+
+	DMINFO("version %u.%u.%u loaded",
+	       loop_target.version[0], loop_target.version[1],
+	       loop_target.version[2]);
+
+	return 0;
+
+err_extent_cache:
+	dm_unregister_target(&amp;loop_target);
+err_target:
+	return r;
+}
+
+static void __exit dm_loop_exit(void)
+{
+	dm_unregister_target(&amp;loop_target);
+	kmem_cache_destroy(dm_loop_extent_cache);
+
+	DMINFO("version %u.%u.%u unloaded",
+	       loop_target.version[0], loop_target.version[1],
+	       loop_target.version[2]);
+}
+
+module_init(dm_loop_init);
+module_exit(dm_loop_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Bryn Reeves &lt;breeves@redhat.com&gt;");
+MODULE_DESCRIPTION("device-mapper loop target");
</pre></body></html>