Commit 238c6d54 authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/agk/linux-2.6-dm

* git://git.kernel.org/pub/scm/linux/kernel/git/agk/linux-2.6-dm:
  dm snapshot: extend exception store functions
  dm snapshot: split out exception store implementations
  dm snapshot: rename struct exception_store
  dm snapshot: separate out exception store interface
  dm mpath: move trigger_event to system workqueue
  dm: add name and uuid to sysfs
  dm table: rework reference counting
  dm: support barriers on simple devices
  dm request: extend target interface
  dm request: add caches
  dm ioctl: allow dm_copy_name_and_uuid to return only one field
  dm log: ensure log bitmap fits on log device
  dm log: move region_size validation
  dm log: avoid reinitialising io_req on every operation
  dm: consolidate target deregistration error handling
  dm raid1: fix error count
  dm log: fix dm_io_client leak on error paths
  dm snapshot: change yield to msleep
  dm table: drop reference at unbind
parents 8e128ce3 a159c1ac
......@@ -3,9 +3,10 @@
#
dm-mod-objs := dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \
dm-ioctl.o dm-io.o dm-kcopyd.o
dm-ioctl.o dm-io.o dm-kcopyd.o dm-sysfs.o
dm-multipath-objs := dm-path-selector.o dm-mpath.o
dm-snapshot-objs := dm-snap.o dm-exception-store.o
dm-snapshot-objs := dm-snap.o dm-exception-store.o dm-snap-transient.o \
dm-snap-persistent.o
dm-mirror-objs := dm-raid1.o
md-mod-objs := md.o bitmap.o
raid456-objs := raid5.o raid6algos.o raid6recov.o raid6tables.o \
......
......@@ -1322,11 +1322,7 @@ static int __init dm_crypt_init(void)
static void __exit dm_crypt_exit(void)
{
int r = dm_unregister_target(&crypt_target);
if (r < 0)
DMERR("unregister failed %d", r);
dm_unregister_target(&crypt_target);
kmem_cache_destroy(_crypt_io_pool);
}
......
......@@ -364,11 +364,7 @@ static int __init dm_delay_init(void)
static void __exit dm_delay_exit(void)
{
int r = dm_unregister_target(&delay_target);
if (r < 0)
DMERR("unregister failed %d", r);
dm_unregister_target(&delay_target);
kmem_cache_destroy(delayed_cache);
destroy_workqueue(kdelayd_wq);
}
......
This diff is collapsed.
/*
* Copyright (C) 2001-2002 Sistina Software (UK) Limited.
* Copyright (C) 2008 Red Hat, Inc. All rights reserved.
*
* Device-mapper snapshot exception store.
*
* This file is released under the GPL.
*/
#ifndef _LINUX_DM_EXCEPTION_STORE
#define _LINUX_DM_EXCEPTION_STORE
#include <linux/blkdev.h>
#include <linux/device-mapper.h>
/*
* The snapshot code deals with largish chunks of the disk at a
* time. Typically 32k - 512k.
*/
typedef sector_t chunk_t;
/*
* An exception is used where an old chunk of data has been
* replaced by a new one.
* If chunk_t is 64 bits in size, the top 8 bits of new_chunk hold the number
* of chunks that follow contiguously. Remaining bits hold the number of the
* chunk within the device.
*/
struct dm_snap_exception {
struct list_head hash_list;
chunk_t old_chunk;
chunk_t new_chunk;
};
/*
* Abstraction to handle the meta/layout of exception stores (the
* COW device).
*/
struct dm_exception_store {
/*
* Destroys this object when you've finished with it.
*/
void (*destroy) (struct dm_exception_store *store);
/*
* The target shouldn't read the COW device until this is
* called. As exceptions are read from the COW, they are
* reported back via the callback.
*/
int (*read_metadata) (struct dm_exception_store *store,
int (*callback)(void *callback_context,
chunk_t old, chunk_t new),
void *callback_context);
/*
* Find somewhere to store the next exception.
*/
int (*prepare_exception) (struct dm_exception_store *store,
struct dm_snap_exception *e);
/*
* Update the metadata with this exception.
*/
void (*commit_exception) (struct dm_exception_store *store,
struct dm_snap_exception *e,
void (*callback) (void *, int success),
void *callback_context);
/*
* The snapshot is invalid, note this in the metadata.
*/
void (*drop_snapshot) (struct dm_exception_store *store);
int (*status) (struct dm_exception_store *store, status_type_t status,
char *result, unsigned int maxlen);
/*
* Return how full the snapshot is.
*/
void (*fraction_full) (struct dm_exception_store *store,
sector_t *numerator,
sector_t *denominator);
struct dm_snapshot *snap;
void *context;
};
/*
* Funtions to manipulate consecutive chunks
*/
# if defined(CONFIG_LBD) || (BITS_PER_LONG == 64)
# define DM_CHUNK_CONSECUTIVE_BITS 8
# define DM_CHUNK_NUMBER_BITS 56
static inline chunk_t dm_chunk_number(chunk_t chunk)
{
return chunk & (chunk_t)((1ULL << DM_CHUNK_NUMBER_BITS) - 1ULL);
}
static inline unsigned dm_consecutive_chunk_count(struct dm_snap_exception *e)
{
return e->new_chunk >> DM_CHUNK_NUMBER_BITS;
}
static inline void dm_consecutive_chunk_count_inc(struct dm_snap_exception *e)
{
e->new_chunk += (1ULL << DM_CHUNK_NUMBER_BITS);
BUG_ON(!dm_consecutive_chunk_count(e));
}
# else
# define DM_CHUNK_CONSECUTIVE_BITS 0
static inline chunk_t dm_chunk_number(chunk_t chunk)
{
return chunk;
}
static inline unsigned dm_consecutive_chunk_count(struct dm_snap_exception *e)
{
return 0;
}
static inline void dm_consecutive_chunk_count_inc(struct dm_snap_exception *e)
{
}
# endif
int dm_exception_store_init(void);
void dm_exception_store_exit(void);
/*
* Two exception store implementations.
*/
int dm_persistent_snapshot_init(void);
void dm_persistent_snapshot_exit(void);
int dm_transient_snapshot_init(void);
void dm_transient_snapshot_exit(void);
int dm_create_persistent(struct dm_exception_store *store);
int dm_create_transient(struct dm_exception_store *store);
#endif /* _LINUX_DM_EXCEPTION_STORE */
......@@ -233,7 +233,7 @@ static void __hash_remove(struct hash_cell *hc)
}
if (hc->new_map)
dm_table_put(hc->new_map);
dm_table_destroy(hc->new_map);
dm_put(hc->md);
free_cell(hc);
}
......@@ -827,8 +827,8 @@ static int do_resume(struct dm_ioctl *param)
r = dm_swap_table(md, new_map);
if (r) {
dm_table_destroy(new_map);
dm_put(md);
dm_table_put(new_map);
return r;
}
......@@ -836,8 +836,6 @@ static int do_resume(struct dm_ioctl *param)
set_disk_ro(dm_disk(md), 0);
else
set_disk_ro(dm_disk(md), 1);
dm_table_put(new_map);
}
if (dm_suspended(md))
......@@ -1080,7 +1078,7 @@ static int table_load(struct dm_ioctl *param, size_t param_size)
}
if (hc->new_map)
dm_table_put(hc->new_map);
dm_table_destroy(hc->new_map);
hc->new_map = t;
up_write(&_hash_lock);
......@@ -1109,7 +1107,7 @@ static int table_clear(struct dm_ioctl *param, size_t param_size)
}
if (hc->new_map) {
dm_table_put(hc->new_map);
dm_table_destroy(hc->new_map);
hc->new_map = NULL;
}
......@@ -1550,8 +1548,10 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid)
goto out;
}
strcpy(name, hc->name);
strcpy(uuid, hc->uuid ? : "");
if (name)
strcpy(name, hc->name);
if (uuid)
strcpy(uuid, hc->uuid ? : "");
out:
up_read(&_hash_lock);
......
......@@ -142,6 +142,7 @@ static struct target_type linear_target = {
.status = linear_status,
.ioctl = linear_ioctl,
.merge = linear_merge,
.features = DM_TARGET_SUPPORTS_BARRIERS,
};
int __init dm_linear_init(void)
......@@ -156,8 +157,5 @@ int __init dm_linear_init(void)
void dm_linear_exit(void)
{
int r = dm_unregister_target(&linear_target);
if (r < 0)
DMERR("unregister failed %d", r);
dm_unregister_target(&linear_target);
}
......@@ -326,8 +326,6 @@ static void header_from_disk(struct log_header *core, struct log_header *disk)
static int rw_header(struct log_c *lc, int rw)
{
lc->io_req.bi_rw = rw;
lc->io_req.mem.ptr.vma = lc->disk_header;
lc->io_req.notify.fn = NULL;
return dm_io(&lc->io_req, 1, &lc->header_location, NULL);
}
......@@ -362,10 +360,15 @@ static int read_header(struct log_c *log)
return 0;
}
static inline int write_header(struct log_c *log)
static int _check_region_size(struct dm_target *ti, uint32_t region_size)
{
header_to_disk(&log->header, log->disk_header);
return rw_header(log, WRITE);
if (region_size < 2 || region_size > ti->len)
return 0;
if (!is_power_of_2(region_size))
return 0;
return 1;
}
/*----------------------------------------------------------------
......@@ -403,8 +406,9 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
}
}
if (sscanf(argv[0], "%u", &region_size) != 1) {
DMWARN("invalid region size string");
if (sscanf(argv[0], "%u", &region_size) != 1 ||
!_check_region_size(ti, region_size)) {
DMWARN("invalid region size %s", argv[0]);
return -EINVAL;
}
......@@ -453,8 +457,18 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
*/
buf_size = dm_round_up((LOG_OFFSET << SECTOR_SHIFT) +
bitset_size, ti->limits.hardsect_size);
if (buf_size > dev->bdev->bd_inode->i_size) {
DMWARN("log device %s too small: need %llu bytes",
dev->name, (unsigned long long)buf_size);
kfree(lc);
return -EINVAL;
}
lc->header_location.count = buf_size >> SECTOR_SHIFT;
lc->io_req.mem.type = DM_IO_VMA;
lc->io_req.notify.fn = NULL;
lc->io_req.client = dm_io_client_create(dm_div_up(buf_size,
PAGE_SIZE));
if (IS_ERR(lc->io_req.client)) {
......@@ -467,10 +481,12 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
lc->disk_header = vmalloc(buf_size);
if (!lc->disk_header) {
DMWARN("couldn't allocate disk log buffer");
dm_io_client_destroy(lc->io_req.client);
kfree(lc);
return -ENOMEM;
}
lc->io_req.mem.ptr.vma = lc->disk_header;
lc->clean_bits = (void *)lc->disk_header +
(LOG_OFFSET << SECTOR_SHIFT);
}
......@@ -482,6 +498,8 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
DMWARN("couldn't allocate sync bitset");
if (!dev)
vfree(lc->clean_bits);
else
dm_io_client_destroy(lc->io_req.client);
vfree(lc->disk_header);
kfree(lc);
return -ENOMEM;
......@@ -495,6 +513,8 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
vfree(lc->sync_bits);
if (!dev)
vfree(lc->clean_bits);
else
dm_io_client_destroy(lc->io_req.client);
vfree(lc->disk_header);
kfree(lc);
return -ENOMEM;
......@@ -631,8 +651,10 @@ static int disk_resume(struct dm_dirty_log *log)
/* set the correct number of regions in the header */
lc->header.nr_regions = lc->region_count;
header_to_disk(&lc->header, lc->disk_header);
/* write the new header */
r = write_header(lc);
r = rw_header(lc, WRITE);
if (r) {
DMWARN("%s: Failed to write header on dirty region log device",
lc->log_dev->name);
......@@ -682,7 +704,7 @@ static int disk_flush(struct dm_dirty_log *log)
if (!lc->touched)
return 0;
r = write_header(lc);
r = rw_header(lc, WRITE);
if (r)
fail_log_device(lc);
else
......
......@@ -889,7 +889,7 @@ static int fail_path(struct pgpath *pgpath)
dm_path_uevent(DM_UEVENT_PATH_FAILED, m->ti,
pgpath->path.dev->name, m->nr_valid_paths);
queue_work(kmultipathd, &m->trigger_event);
schedule_work(&m->trigger_event);
queue_work(kmultipathd, &pgpath->deactivate_path);
out:
......@@ -932,7 +932,7 @@ static int reinstate_path(struct pgpath *pgpath)
dm_path_uevent(DM_UEVENT_PATH_REINSTATED, m->ti,
pgpath->path.dev->name, m->nr_valid_paths);
queue_work(kmultipathd, &m->trigger_event);
schedule_work(&m->trigger_event);
out:
spin_unlock_irqrestore(&m->lock, flags);
......@@ -976,7 +976,7 @@ static void bypass_pg(struct multipath *m, struct priority_group *pg,
spin_unlock_irqrestore(&m->lock, flags);
queue_work(kmultipathd, &m->trigger_event);
schedule_work(&m->trigger_event);
}
/*
......@@ -1006,7 +1006,7 @@ static int switch_pg_num(struct multipath *m, const char *pgstr)
}
spin_unlock_irqrestore(&m->lock, flags);
queue_work(kmultipathd, &m->trigger_event);
schedule_work(&m->trigger_event);
return 0;
}
......@@ -1495,14 +1495,10 @@ static int __init dm_multipath_init(void)
static void __exit dm_multipath_exit(void)
{
int r;
destroy_workqueue(kmpath_handlerd);
destroy_workqueue(kmultipathd);
r = dm_unregister_target(&multipath_target);
if (r < 0)
DMERR("target unregister failed %d", r);
dm_unregister_target(&multipath_target);
kmem_cache_destroy(_mpio_cache);
}
......
......@@ -197,9 +197,6 @@ static void fail_mirror(struct mirror *m, enum dm_raid1_error error_type)
struct mirror_set *ms = m->ms;
struct mirror *new;
if (!errors_handled(ms))
return;
/*
* error_count is used for nothing more than a
* simple way to tell if a device has encountered
......@@ -210,6 +207,9 @@ static void fail_mirror(struct mirror *m, enum dm_raid1_error error_type)
if (test_and_set_bit(error_type, &m->error_type))
return;
if (!errors_handled(ms))
return;
if (m != get_default_mirror(ms))
goto out;
......@@ -808,12 +808,6 @@ static void free_context(struct mirror_set *ms, struct dm_target *ti,
kfree(ms);
}
static inline int _check_region_size(struct dm_target *ti, uint32_t size)
{
return !(size % (PAGE_SIZE >> 9) || !is_power_of_2(size) ||
size > ti->len);
}
static int get_mirror(struct mirror_set *ms, struct dm_target *ti,
unsigned int mirror, char **argv)
{
......@@ -872,12 +866,6 @@ static struct dm_dirty_log *create_dirty_log(struct dm_target *ti,
return NULL;
}
if (!_check_region_size(ti, dl->type->get_region_size(dl))) {
ti->error = "Invalid region size";
dm_dirty_log_destroy(dl);
return NULL;
}
return dl;
}
......@@ -1300,11 +1288,7 @@ static int __init dm_mirror_init(void)
static void __exit dm_mirror_exit(void)
{
int r;
r = dm_unregister_target(&mirror_target);
if (r < 0)
DMERR("unregister failed %d", r);
dm_unregister_target(&mirror_target);
}
/* Module hooks */
......
This diff is collapsed.
/*
* Copyright (C) 2001-2002 Sistina Software (UK) Limited.
* Copyright (C) 2006-2008 Red Hat GmbH
*
* This file is released under the GPL.
*/
#include "dm-exception-store.h"
#include "dm-snap.h"
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/dm-io.h>
#define DM_MSG_PREFIX "transient snapshot"
/*-----------------------------------------------------------------
* Implementation of the store for non-persistent snapshots.
*---------------------------------------------------------------*/
struct transient_c {
sector_t next_free;
};
static void transient_destroy(struct dm_exception_store *store)
{
kfree(store->context);
}
static int transient_read_metadata(struct dm_exception_store *store,
int (*callback)(void *callback_context,
chunk_t old, chunk_t new),
void *callback_context)
{
return 0;
}
static int transient_prepare_exception(struct dm_exception_store *store,
struct dm_snap_exception *e)
{
struct transient_c *tc = (struct transient_c *) store->context;
sector_t size = get_dev_size(store->snap->cow->bdev);
if (size < (tc->next_free + store->snap->chunk_size))
return -1;
e->new_chunk = sector_to_chunk(store->snap, tc->next_free);
tc->next_free += store->snap->chunk_size;
return 0;
}
static void transient_commit_exception(struct dm_exception_store *store,
struct dm_snap_exception *e,
void (*callback) (void *, int success),
void *callback_context)
{
/* Just succeed */
callback(callback_context, 1);
}
static void transient_fraction_full(struct dm_exception_store *store,
sector_t *numerator, sector_t *denominator)
{
*numerator = ((struct transient_c *) store->context)->next_free;
*denominator = get_dev_size(store->snap->cow->bdev);
}
int dm_create_transient(struct dm_exception_store *store)
{
struct transient_c *tc;
store->destroy = transient_destroy;
store->read_metadata = transient_read_metadata;
store->prepare_exception = transient_prepare_exception;
store->commit_exception = transient_commit_exception;
store->drop_snapshot = NULL;
store->fraction_full = transient_fraction_full;
tc = kmalloc(sizeof(struct transient_c), GFP_KERNEL);
if (!tc)
return -ENOMEM;
tc->next_free = 0;
store->context = tc;
return 0;
}
int dm_transient_snapshot_init(void)
{
return 0;
}
void dm_transient_snapshot_exit(void)
{
}
......@@ -9,6 +9,7 @@
#include <linux/blkdev.h>
#include <linux/ctype.h>
#include <linux/device-mapper.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kdev_t.h>
......@@ -20,6 +21,7 @@
#include <linux/log2.h>
#include <linux/dm-kcopyd.h>
#include "dm-exception-store.h"
#include "dm-snap.h"
#include "dm-bio-list.h"
......@@ -428,8 +430,13 @@ static void insert_completed_exception(struct dm_snapshot *s,
list_add(&new_e->hash_list, e ? &e->hash_list : l);
}
int dm_add_exception(struct dm_snapshot *s, chunk_t old, chunk_t new)
/*
* Callback used by the exception stores to load exceptions when
* initialising.
*/
static int dm_add_exception(void *context, chunk_t old, chunk_t new)
{
struct dm_snapshot *s = context;
struct dm_snap_exception *e;
e = alloc_exception();
......@@ -658,7 +665,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
spin_lock_init(&s->tracked_chunk_lock);
/* Metadata must only be loaded into one table at once */
r = s->store.read_metadata(&s->store);
r = s->store.read_metadata(&s->store, dm_add_exception, (void *)s);
if (r < 0) {
ti->error = "Failed to read snapshot metadata";
goto bad_load_and_register;
......@@ -735,7 +742,7 @@ static void snapshot_dtr(struct dm_target *ti)
unregister_snapshot(s);
while (atomic_read(&s->pending_exceptions_count))
yield();
msleep(1);
/*
* Ensure instructions in mempool_destroy aren't reordered
* before atomic_read.
......@@ -888,10 +895,10 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success)
/*
* Check for conflicting reads. This is extremely improbable,
* so yield() is sufficient and there is no need for a wait queue.
* so msleep(1) is sufficient and there is no need for a wait queue.
*/
while (__chunk_is_tracked(s, pe->e.old_chunk))
yield();
msleep(1);
/*
* Add a proper exception, and remove the
......@@ -1404,6 +1411,12 @@ static int __init dm_snapshot_init(void)
{
int r;
r = dm_exception_store_init();
if (r) {
DMERR("Failed to initialize exception stores");
return r;
}
r = dm_register_target(&snapshot_target);
if (r) {
DMERR("snapshot target register failed %d", r);
......@@ -1452,39 +1465,34 @@ static int __init dm_snapshot_init(void)
return 0;
bad_pending_pool:
bad_pending_pool:
kmem_cache_destroy(tracked_chunk_cache);
bad5:
bad5:
kmem_cache_destroy(pending_cache);
bad4:
bad4:
kmem_cache_destroy(exception_cache);
bad3:
bad3:
exit_origin_hash();
bad2:
bad2:
dm_unregister_target(&origin_target);
bad1:
bad1:
dm_unregister_target(&snapshot_target);
return r;
}
static void __exit dm_snapshot_exit(void)
{
int r;
destroy_workqueue(ksnapd);
r = dm_unregister_target(&snapshot_target);
if (r)
DMERR("snapshot unregister failed %d", r);
r = dm_unregister_target(&origin_target);
if (r)
DMERR("origin unregister failed %d", r);
dm_unregister_target(&snapshot_target);
dm_unregister_target(&origin_target);
exit_origin_hash();
kmem_cache_destroy(pending_cache);
kmem_cache_destroy(exception_cache);
kmem_cache_destroy(tracked_chunk_cache);
dm_exception_store_exit();
}
/* Module hooks */
......
/*
* dm-snapshot.c
*
* Copyright (C) 2001-2002 Sistina Software (UK) Limited.
*
* This file is released under the GPL.
......@@ -10,6 +8,7 @@
#define DM_SNAPSHOT_H
#include <linux/device-mapper.h>
#include "dm-exception-store.h"
#include "dm-bio-list.h"
#include <linux/blkdev.h>
#include <linux/workqueue.h>
......@@ -20,116 +19,6 @@ struct exception_table {
struct list_head *table;
};
/*
* The snapshot code deals with largish chunks of the disk at a
* time. Typically 32k - 512k.
*/
typedef sector_t chunk_t;
/*
* An exception is used where an old chunk of data has been
* replaced by a new one.
* If chunk_t is 64 bits in size, the top 8 bits of new_chunk hold the number
* of chunks that follow contiguously. Remaining bits hold the number of the
* chunk within the device.
*/
struct dm_snap_exception {
struct list_head hash_list;
chunk_t old_chunk;
chunk_t new_chunk;
};
/*
* Funtions to manipulate consecutive chunks
*/
# if defined(CONFIG_LBD) || (BITS_PER_LONG == 64)
# define DM_CHUNK_CONSECUTIVE_BITS 8
# define DM_CHUNK_NUMBER_BITS 56
static inline chunk_t dm_chunk_number(chunk_t chunk)
{
return chunk & (chunk_t)((1ULL << DM_CHUNK_NUMBER_BITS) - 1ULL);
}
static inline unsigned dm_consecutive_chunk_count(struct dm_snap_exception *e)
{
return e->new_chunk >> DM_CHUNK_NUMBER_BITS;
}
static inline void dm_consecutive_chunk_count_inc(struct dm_snap_exception *e)
{
e->new_chunk += (1ULL << DM_CHUNK_NUMBER_BITS);
BUG_ON(!dm_consecutive_chunk_count(e));
}
# else
# define DM_CHUNK_CONSECUTIVE_BITS 0
static inline chunk_t dm_chunk_number(chunk_t chunk)
{
return chunk;
}
static inline unsigned dm_consecutive_chunk_count(struct dm_snap_exception *e)
{
return 0;
}
static inline void dm_consecutive_chunk_count_inc(struct dm_snap_exception *e)
{
}
# endif
/*
* Abstraction to handle the meta/layout of exception stores (the
* COW device).
*/
struct exception_store {
/*
* Destroys this object when you've finished with it.
*/
void (*destroy) (struct exception_store *store);
/*
* The target shouldn't read the COW device until this is
* called.
*/
int (*read_metadata) (struct exception_store *store);
/*
* Find somewhere to store the next exception.
*/
int (*prepare_exception) (struct exception_store *store,
struct dm_snap_exception *e);
/*
* Update the metadata with this exception.
*/
void (*commit_exception) (struct exception_store *store,
struct dm_snap_exception *e,
void (*callback) (void *, int success),
void *callback_context);
/*
* The snapshot is invalid, note this in the metadata.
*/
void (*drop_snapshot) (struct exception_store *store);
/*
* Return how full the snapshot is.
*/
void (*fraction_full) (struct exception_store *store,
sector_t *numerator,
sector_t *denominator);
struct dm_snapshot *snap;
void *context;
};
#define DM_TRACKED_CHUNK_HASH_SIZE 16
#define DM_TRACKED_CHUNK_HASH(x) ((unsigned long)(x) & \
(DM_TRACKED_CHUNK_HASH_SIZE - 1))
......@@ -172,7 +61,7 @@ struct dm_snapshot {
spinlock_t pe_lock;
/* The on disk metadata handler */
struct exception_store store;
struct dm_exception_store store;
struct dm_kcopyd_client *kcopyd_client;
......@@ -186,20 +75,6 @@ struct dm_snapshot {
struct hlist_head tracked_chunk_hash[DM_TRACKED_CHUNK_HASH_SIZE];
};
/*
* Used by the exception stores to load exceptions hen
* initialising.
*/
int dm_add_exception(struct dm_snapshot *s, chunk_t old, chunk_t new);
/*
* Constructor and destructor for the default persistent
* store.
*/
int dm_create_persistent(struct exception_store *store);
int dm_create_transient(struct exception_store *store);
/*
* Return the number of sectors in the device.
*/
......
......@@ -337,9 +337,7 @@ int __init dm_stripe_init(void)
void dm_stripe_exit(void)
{
if (dm_unregister_target(&stripe_target))
DMWARN("target unregistration failed");
dm_unregister_target(&stripe_target);
destroy_workqueue(kstriped);
return;
......
/*
* Copyright (C) 2008 Red Hat, Inc. All rights reserved.
*
* This file is released under the GPL.
*/
#include <linux/sysfs.h>
#include <linux/dm-ioctl.h>
#include "dm.h"
struct dm_sysfs_attr {
struct attribute attr;
ssize_t (*show)(struct mapped_device *, char *);
ssize_t (*store)(struct mapped_device *, char *);
};
#define DM_ATTR_RO(_name) \
struct dm_sysfs_attr dm_attr_##_name = \
__ATTR(_name, S_IRUGO, dm_attr_##_name##_show, NULL)
static ssize_t dm_attr_show(struct kobject *kobj, struct attribute *attr,
char *page)
{
struct dm_sysfs_attr *dm_attr;
struct mapped_device *md;
ssize_t ret;
dm_attr = container_of(attr, struct dm_sysfs_attr, attr);
if (!dm_attr->show)
return -EIO;
md = dm_get_from_kobject(kobj);
if (!md)
return -EINVAL;
ret = dm_attr->show(md, page);
dm_put(md);
return ret;
}
static ssize_t dm_attr_name_show(struct mapped_device *md, char *buf)
{
if (dm_copy_name_and_uuid(md, buf, NULL))
return -EIO;
strcat(buf, "\n");
return strlen(buf);
}
static ssize_t dm_attr_uuid_show(struct mapped_device *md, char *buf)
{
if (dm_copy_name_and_uuid(md, NULL, buf))
return -EIO;
strcat(buf, "\n");
return strlen(buf);
}
static DM_ATTR_RO(name);
static DM_ATTR_RO(uuid);
static struct attribute *dm_attrs[] = {
&dm_attr_name.attr,
&dm_attr_uuid.attr,
NULL,
};
static struct sysfs_ops dm_sysfs_ops = {
.show = dm_attr_show,
};
/*
* dm kobject is embedded in mapped_device structure
* no need to define release function here
*/
static struct kobj_type dm_ktype = {
.sysfs_ops = &dm_sysfs_ops,
.default_attrs = dm_attrs,
};
/*
* Initialize kobj
* because nobody using md yet, no need to call explicit dm_get/put
*/
int dm_sysfs_init(struct mapped_device *md)
{
return kobject_init_and_add(dm_kobject(md), &dm_ktype,
&disk_to_dev(dm_disk(md))->kobj,
"%s", "dm");
}
/*
* Remove kobj, called after all references removed
*/
void dm_sysfs_exit(struct mapped_device *md)
{
kobject_put(dm_kobject(md));
}
/*
* Copyright (C) 2001 Sistina Software (UK) Limited.
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
* Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
*
* This file is released under the GPL.
*/
......@@ -15,6 +15,7 @@
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <asm/atomic.h>
#define DM_MSG_PREFIX "table"
......@@ -24,6 +25,19 @@
#define KEYS_PER_NODE (NODE_SIZE / sizeof(sector_t))
#define CHILDREN_PER_NODE (KEYS_PER_NODE + 1)
/*
* The table has always exactly one reference from either mapped_device->map
* or hash_cell->new_map. This reference is not counted in table->holders.
* A pair of dm_create_table/dm_destroy_table functions is used for table
* creation/destruction.
*
* Temporary references from the other code increase table->holders. A pair
* of dm_table_get/dm_table_put functions is used to manipulate it.
*
* When the table is about to be destroyed, we wait for table->holders to
* drop to zero.
*/
struct dm_table {
struct mapped_device *md;
atomic_t holders;
......@@ -38,6 +52,8 @@ struct dm_table {
sector_t *highs;
struct dm_target *targets;
unsigned barriers_supported:1;
/*
* Indicates the rw permissions for the new logical
* device. This should be a combination of FMODE_READ
......@@ -226,7 +242,8 @@ int dm_table_create(struct dm_table **result, fmode_t mode,
return -ENOMEM;
INIT_LIST_HEAD(&t->devices);
atomic_set(&t->holders, 1);
atomic_set(&t->holders, 0);
t->barriers_supported = 1;
if (!num_targets)
num_targets = KEYS_PER_NODE;
......@@ -256,10 +273,14 @@ static void free_devices(struct list_head *devices)
}
}
static void table_destroy(struct dm_table *t)
void dm_table_destroy(struct dm_table *t)
{
unsigned int i;
while (atomic_read(&t->holders))
msleep(1);
smp_mb();
/* free the indexes (see dm_table_complete) */
if (t->depth >= 2)
vfree(t->index[t->depth - 2]);
......@@ -297,8 +318,8 @@ void dm_table_put(struct dm_table *t)
if (!t)
return;
if (atomic_dec_and_test(&t->holders))
table_destroy(t);
smp_mb__before_atomic_dec();
atomic_dec(&t->holders);
}
/*
......@@ -728,6 +749,10 @@ int dm_table_add_target(struct dm_table *t, const char *type,
/* FIXME: the plan is to combine high here and then have
* the merge fn apply the target level restrictions. */
combine_restrictions_low(&t->limits, &tgt->limits);
if (!(tgt->type->features & DM_TARGET_SUPPORTS_BARRIERS))
t->barriers_supported = 0;
return 0;
bad:
......@@ -772,6 +797,12 @@ int dm_table_complete(struct dm_table *t)
check_for_valid_limits(&t->limits);
/*
* We only support barriers if there is exactly one underlying device.
*/
if (!list_is_singular(&t->devices))
t->barriers_supported = 0;
/* how many indexes will the btree have ? */
leaf_nodes = dm_div_up(t->num_targets, KEYS_PER_NODE);
t->depth = 1 + int_log(leaf_nodes, CHILDREN_PER_NODE);
......@@ -986,6 +1017,12 @@ struct mapped_device *dm_table_get_md(struct dm_table *t)
return t->md;
}
int dm_table_barrier_ok(struct dm_table *t)
{
return t->barriers_supported;
}
EXPORT_SYMBOL(dm_table_barrier_ok);
EXPORT_SYMBOL(dm_vcalloc);
EXPORT_SYMBOL(dm_get_device);
EXPORT_SYMBOL(dm_put_device);
......
......@@ -130,26 +130,26 @@ int dm_register_target(struct target_type *t)
return rv;
}
int dm_unregister_target(struct target_type *t)
void dm_unregister_target(struct target_type *t)
{
struct tt_internal *ti;
down_write(&_lock);
if (!(ti = __find_target_type(t->name))) {
up_write(&_lock);
return -EINVAL;
DMCRIT("Unregistering unrecognised target: %s", t->name);
BUG();
}
if (ti->use) {
up_write(&_lock);
return -ETXTBSY;
DMCRIT("Attempt to unregister target still in use: %s",
t->name);
BUG();
}
list_del(&ti->list);
kfree(ti);
up_write(&_lock);
return 0;
}
/*
......@@ -187,8 +187,7 @@ int __init dm_target_init(void)
void dm_target_exit(void)
{
if (dm_unregister_target(&error_target))
DMWARN("error target unregistration failed");
dm_unregister_target(&error_target);
}
EXPORT_SYMBOL(dm_register_target);
......
......@@ -69,10 +69,7 @@ static int __init dm_zero_init(void)
static void __exit dm_zero_exit(void)
{
int r = dm_unregister_target(&zero_target);
if (r < 0)
DMERR("unregister failed %d", r);
dm_unregister_target(&zero_target);
}
module_init(dm_zero_init)
......
/*
* Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
* Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
* Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
*
* This file is released under the GPL.
*/
......@@ -32,6 +32,7 @@ static unsigned int _major = 0;
static DEFINE_SPINLOCK(_minor_lock);
/*
* For bio-based dm.
* One of these is allocated per bio.
*/
struct dm_io {
......@@ -43,6 +44,7 @@ struct dm_io {
};
/*
* For bio-based dm.
* One of these is allocated per target within a bio. Hopefully
* this will be simplified out one day.
*/
......@@ -54,6 +56,27 @@ struct dm_target_io {
DEFINE_TRACE(block_bio_complete);
/*
* For request-based dm.
* One of these is allocated per request.
*/
struct dm_rq_target_io {
struct mapped_device *md;
struct dm_target *ti;
struct request *orig, clone;
int error;
union map_info info;
};
/*
* For request-based dm.
* One of these is allocated per bio.
*/
struct dm_rq_clone_bio_info {
struct bio *orig;
struct request *rq;
};
union map_info *dm_get_mapinfo(struct bio *bio)
{
if (bio && bio->bi_private)
......@@ -144,11 +167,16 @@ struct mapped_device {
/* forced geometry settings */
struct hd_geometry geometry;
/* sysfs handle */
struct kobject kobj;
};
#define MIN_IOS 256
static struct kmem_cache *_io_cache;
static struct kmem_cache *_tio_cache;
static struct kmem_cache *_rq_tio_cache;
static struct kmem_cache *_rq_bio_info_cache;
static int __init local_init(void)
{
......@@ -164,9 +192,17 @@ static int __init local_init(void)
if (!_tio_cache)
goto out_free_io_cache;
_rq_tio_cache = KMEM_CACHE(dm_rq_target_io, 0);
if (!_rq_tio_cache)
goto out_free_tio_cache;
_rq_bio_info_cache = KMEM_CACHE(dm_rq_clone_bio_info, 0);
if (!_rq_bio_info_cache)
goto out_free_rq_tio_cache;
r = dm_uevent_init();
if (r)
goto out_free_tio_cache;
goto out_free_rq_bio_info_cache;
_major = major;
r = register_blkdev(_major, _name);
......@@ -180,6 +216,10 @@ static int __init local_init(void)
out_uevent_exit:
dm_uevent_exit();
out_free_rq_bio_info_cache:
kmem_cache_destroy(_rq_bio_info_cache);
out_free_rq_tio_cache:
kmem_cache_destroy(_rq_tio_cache);
out_free_tio_cache:
kmem_cache_destroy(_tio_cache);
out_free_io_cache:
......@@ -190,6 +230,8 @@ static int __init local_init(void)
static void local_exit(void)
{
kmem_cache_destroy(_rq_bio_info_cache);
kmem_cache_destroy(_rq_tio_cache);
kmem_cache_destroy(_tio_cache);
kmem_cache_destroy(_io_cache);
unregister_blkdev(_major, _name);
......@@ -796,7 +838,11 @@ static int __split_bio(struct mapped_device *md, struct bio *bio)
ci.map = dm_get_table(md);
if (unlikely(!ci.map))
return -EIO;
if (unlikely(bio_barrier(bio) && !dm_table_barrier_ok(ci.map))) {
dm_table_put(ci.map);
bio_endio(bio, -EOPNOTSUPP);
return 0;
}
ci.md = md;
ci.bio = bio;
ci.io = alloc_io(md);
......@@ -880,15 +926,6 @@ static int dm_request(struct request_queue *q, struct bio *bio)
struct mapped_device *md = q->queuedata;
int cpu;
/*
* There is no use in forwarding any barrier request since we can't
* guarantee it is (or can be) handled by the targets correctly.
*/
if (unlikely(bio_barrier(bio))) {
bio_endio(bio, -EOPNOTSUPP);
return 0;
}
down_read(&md->io_lock);
cpu = part_stat_lock();
......@@ -943,8 +980,6 @@ static int dm_any_congested(void *congested_data, int bdi_bits)
struct mapped_device *md = congested_data;
struct dm_table *map;
atomic_inc(&md->pending);
if (!test_bit(DMF_BLOCK_IO, &md->flags)) {
map = dm_get_table(md);
if (map) {
......@@ -953,10 +988,6 @@ static int dm_any_congested(void *congested_data, int bdi_bits)
}
}
if (!atomic_dec_return(&md->pending))
/* nudge anyone waiting on suspend queue */
wake_up(&md->wait);
return r;
}
......@@ -1216,10 +1247,12 @@ static int __bind(struct mapped_device *md, struct dm_table *t)
if (md->suspended_bdev)
__set_size(md, size);
if (size == 0)
if (!size) {
dm_table_destroy(t);
return 0;
}
dm_table_get(t);
dm_table_event_callback(t, event_callback, md);
write_lock(&md->map_lock);
......@@ -1241,7 +1274,7 @@ static void __unbind(struct mapped_device *md)
write_lock(&md->map_lock);
md->map = NULL;
write_unlock(&md->map_lock);
dm_table_put(map);
dm_table_destroy(map);
}
/*
......@@ -1255,6 +1288,8 @@ int dm_create(int minor, struct mapped_device **result)
if (!md)
return -ENXIO;
dm_sysfs_init(md);
*result = md;
return 0;
}
......@@ -1330,8 +1365,9 @@ void dm_put(struct mapped_device *md)
dm_table_presuspend_targets(map);
dm_table_postsuspend_targets(map);
}
__unbind(md);
dm_sysfs_exit(md);
dm_table_put(map);
__unbind(md);
free_dev(md);
}
}
......@@ -1669,6 +1705,27 @@ struct gendisk *dm_disk(struct mapped_device *md)
return md->disk;
}
struct kobject *dm_kobject(struct mapped_device *md)
{
return &md->kobj;
}
/*
* struct mapped_device should not be exported outside of dm.c
* so use this check to verify that kobj is part of md structure
*/
struct mapped_device *dm_get_from_kobject(struct kobject *kobj)
{
struct mapped_device *md;
md = container_of(kobj, struct mapped_device, kobj);
if (&md->kobj != kobj)
return NULL;
dm_get(md);
return md;
}
int dm_suspended(struct mapped_device *md)
{
return test_bit(DMF_SUSPENDED, &md->flags);
......
......@@ -36,6 +36,7 @@ struct dm_table;
/*-----------------------------------------------------------------
* Internal table functions.
*---------------------------------------------------------------*/
void dm_table_destroy(struct dm_table *t);
void dm_table_event_callback(struct dm_table *t,
void (*fn)(void *), void *context);
struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index);
......@@ -51,6 +52,7 @@ int dm_table_any_congested(struct dm_table *t, int bdi_bits);
* To check the return value from dm_table_find_target().
*/
#define dm_target_is_valid(t) ((t)->table)
int dm_table_barrier_ok(struct dm_table *t);
/*-----------------------------------------------------------------
* A registry of target types.
......@@ -71,6 +73,14 @@ int dm_split_args(int *argc, char ***argvp, char *input);
int dm_interface_init(void);
void dm_interface_exit(void);
/*
* sysfs interface
*/
int dm_sysfs_init(struct mapped_device *md);
void dm_sysfs_exit(struct mapped_device *md);
struct kobject *dm_kobject(struct mapped_device *md);
struct mapped_device *dm_get_from_kobject(struct kobject *kobj);
/*
* Targets for linear and striped mappings
*/
......
......@@ -45,6 +45,8 @@ typedef void (*dm_dtr_fn) (struct dm_target *ti);
*/
typedef int (*dm_map_fn) (struct dm_target *ti, struct bio *bio,
union map_info *map_context);
typedef int (*dm_map_request_fn) (struct dm_target *ti, struct request *clone,
union map_info *map_context);
/*
* Returns:
......@@ -57,6 +59,9 @@ typedef int (*dm_map_fn) (struct dm_target *ti, struct bio *bio,
typedef int (*dm_endio_fn) (struct dm_target *ti,
struct bio *bio, int error,
union map_info *map_context);
typedef int (*dm_request_endio_fn) (struct dm_target *ti,
struct request *clone, int error,
union map_info *map_context);
typedef void (*dm_flush_fn) (struct dm_target *ti);
typedef void (*dm_presuspend_fn) (struct dm_target *ti);
......@@ -75,6 +80,13 @@ typedef int (*dm_ioctl_fn) (struct dm_target *ti, unsigned int cmd,
typedef int (*dm_merge_fn) (struct dm_target *ti, struct bvec_merge_data *bvm,
struct bio_vec *biovec, int max_size);
/*
* Returns:
* 0: The target can handle the next I/O immediately.
* 1: The target can't handle the next I/O immediately.
*/
typedef int (*dm_busy_fn) (struct dm_target *ti);
void dm_error(const char *message);
/*
......@@ -100,14 +112,23 @@ void dm_put_device(struct dm_target *ti, struct dm_dev *d);
/*
* Information about a target type
*/
/*
* Target features
*/
#define DM_TARGET_SUPPORTS_BARRIERS 0x00000001
struct target_type {
uint64_t features;
const char *name;
struct module *module;
unsigned version[3];
dm_ctr_fn ctr;
dm_dtr_fn dtr;
dm_map_fn map;
dm_map_request_fn map_rq;
dm_endio_fn end_io;
dm_request_endio_fn rq_end_io;
dm_flush_fn flush;
dm_presuspend_fn presuspend;
dm_postsuspend_fn postsuspend;
......@@ -117,6 +138,7 @@ struct target_type {
dm_message_fn message;
dm_ioctl_fn ioctl;
dm_merge_fn merge;
dm_busy_fn busy;
};
struct io_restrictions {
......@@ -157,8 +179,7 @@ struct dm_target {
};
int dm_register_target(struct target_type *t);
int dm_unregister_target(struct target_type *t);
void dm_unregister_target(struct target_type *t);
/*-----------------------------------------------------------------
* Functions for creating and manipulating mapped devices.
......@@ -276,6 +297,9 @@ void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size);
*---------------------------------------------------------------*/
#define DM_NAME "device-mapper"
#define DMCRIT(f, arg...) \
printk(KERN_CRIT DM_NAME ": " DM_MSG_PREFIX ": " f "\n", ## arg)
#define DMERR(f, arg...) \
printk(KERN_ERR DM_NAME ": " DM_MSG_PREFIX ": " f "\n", ## arg)
#define DMERR_LIMIT(f, arg...) \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment