You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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
This commit is contained in:
+3
-2
@@ -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 @@ bad_queue:
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
+17
-728
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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,7 +1548,9 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (name)
|
||||
strcpy(name, hc->name);
|
||||
if (uuid)
|
||||
strcpy(uuid, hc->uuid ? : "");
|
||||
|
||||
out:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+31
-9
@@ -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", ®ion_size) != 1) {
|
||||
DMWARN("invalid region size string");
|
||||
if (sscanf(argv[0], "%u", ®ion_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);
|
||||
}
|
||||
|
||||
|
||||
+4
-20
@@ -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 */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
}
|
||||
+22
-14
@@ -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 @@ out:
|
||||
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);
|
||||
@@ -1469,22 +1482,17 @@ static int __init dm_snapshot_init(void)
|
||||
|
||||
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 */
|
||||
|
||||
+2
-127
@@ -1,6 +1,4 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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));
|
||||
}
|
||||
+42
-5
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* 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)
|
||||
|
||||
+80
-23
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* 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 @@ out_free_io_cache:
|
||||
|
||||
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)
|
||||
return 0;
|
||||
|
||||
dm_table_get(t);
|
||||
if (!size) {
|
||||
dm_table_destroy(t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user