/*
* Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
*
* This file is released under the GPL.
*/
#include "dm.h"
#include "dm-bio-list.h"
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/moduleparam.h>
#include <linux/blkpg.h>
#include <linux/bio.h>
#include <linux/buffer_head.h>
#include <linux/mempool.h>
#include <linux/slab.h>
#include <linux/idr.h>
#include <linux/hdreg.h>
#include <linux/blktrace_api.h>
static const char *_name = DM_NAME;
static unsigned int major = 0;
static unsigned int _major = 0;
/*
* One of these is allocated per bio.
*/
struct dm_io {
struct mapped_device *md;
int error;
struct bio *bio;
atomic_t io_count;
unsigned long start_time;
};
/*
* One of these is allocated per target within a bio. Hopefully
* this will be simplified out one day.
*/
struct target_io {
struct dm_io *io;
struct dm_target *ti;
union map_info info;
};
union map_info *dm_get_mapinfo(struct bio *bio)
{
if (bio && bio->bi_private)
return &((struct target_io *)bio->bi_private)->info;
return NULL;
}
/*
* Bits for the md->flags field.
*/
#define DMF_BLOCK_IO 0
#define DMF_SUSPENDED 1
#define DMF_FROZEN 2
struct mapped_device {
struct rw_semaphore io_lock;
struct semaphore suspend_lock;
rwlock_t map_lock;
atomic_t holders;
unsigned long flags;
request_queue_t *queue;
struct gendisk *disk;
char name[16];
void *interface_ptr;
/*
* A list of ios that arrived while we were suspended.
*/
atomic_t pending;
wait_queue_head_t wait;
struct bio_list deferred;
/*
* The current mapping.
*/
struct dm_table *map;
/*
* io objects are allocated from here.
*/
mempool_t *io_pool;
mempool_t *tio_pool;
/*
* Event handling.
*/
atomic_t event_nr;
wait_queue_head_t eventq;
/*
* freeze/thaw support require holding onto a super block
*/
struct super_block *frozen_sb;
struct block_device *suspended_bdev;
/* forced geometry settings */
struct hd_geometry geometry;
};
#define MIN_IOS 256
static kmem_cache_t *_io_cache;
static kmem_cache_t *_tio_cache;
static struct bio_set *dm_set;
static int __init local_init(void)
{
int r;
dm_set = bioset_create(16, 16, 4);
if (!dm_set)
return -ENOMEM;
/* allocate a slab for the dm_ios */
_io_cache = kmem_cache_create("dm_io",
sizeof(struct dm_io), 0, 0, NULL, NULL);
if (!_io_cache)
return -ENOMEM;
/* allocate a slab for the target ios */
_tio_cache = kmem_cache_create("dm_tio", sizeof(struct target_io),
0, 0, NULL, NULL);
if (!_tio_cache) {
kmem_cache_destroy(_io_cache);
return -ENOMEM;
}
_major = major;
r = register_blkdev(_major, _name);
if (r < 0) {
kmem_cache_destroy(_tio_cache);
kmem_cache_destroy(_io_cache);
return r;
}
if (!_major)
_major = r;
return 0;
}
static void local_exit(void)
{
kmem_cache_destroy(_tio_cache);
kmem_cache_destroy(_io_cache);
bioset_free(dm_set);
if (unregister_blkdev(_major, _name) < 0)
DMERR("devfs_unregister_blkdev failed");
_major = 0;
DMINFO("cleaned up");
}
int (*_inits[])(void) __initdata = {
local_init,
dm_target_init,
dm_linear_init,
dm_stripe_init,
dm_interface_init,
};
void (*_exits[])(void) = {
local_exit,
dm_target_exit,
dm_linear_exit,
dm_stripe_exit,
dm_interface_exit,
};
static int __init dm_init(void)
{
const int count = ARRAY_SIZE(_inits);
int r, i;
for (i = 0; i < count; i++) {
r = _inits[i]();
if (r)
goto bad;
}
return 0;
bad:
while (i--)
_exits[i]();
return r;
}
static void __exit dm_exit(void)
{
int i = ARRAY_SIZE(_exits);
while (i--)
_exits[i]();
}
/*
* Block device functions
*/
static int dm_blk_open(struct inode *inode, struct file *file)
{
struct mapped_device *md;
md = inode->i_bdev->bd_disk->private_data;
dm_get(md);
return 0;
}
static int dm_blk_close(struct inode
|