* [PATCH 1/2] dm thin: rename dm_thin_id to more generic dm_dev_id @ 2012-03-02 23:59 Mike Snitzer 2012-03-02 23:59 ` [PATCH 2/2] dm thin: elevate the bio_prison and deferred_set code to allow for re-use Mike Snitzer 2012-03-05 10:29 ` [PATCH 1/2] dm thin: rename dm_thin_id to more generic dm_dev_id Joe Thornber 0 siblings, 2 replies; 6+ messages in thread From: Mike Snitzer @ 2012-03-02 23:59 UTC (permalink / raw) To: dm-devel; +Cc: ejt, snitzer dm_thin_id is too specific to thin devices. Rename to allow for reuse by other targets. Signed-off-by: Mike Snitzer <snitzer@redhat.com> --- drivers/md/dm-thin-metadata.c | 24 ++++++++++++------------ drivers/md/dm-thin-metadata.h | 14 +++++++------- drivers/md/dm-thin.c | 16 ++++++++-------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index 1783741..62e7024 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -184,7 +184,7 @@ struct dm_pool_metadata { struct dm_thin_device { struct list_head list; struct dm_pool_metadata *pmd; - dm_thin_id id; + dm_dev_id id; int open_count; int changed; @@ -799,7 +799,7 @@ int dm_pool_metadata_close(struct dm_pool_metadata *pmd) * on failure, @td is not initialized. */ static int __open_device(struct dm_pool_metadata *pmd, - dm_thin_id dev, int create, + dm_dev_id dev, int create, struct dm_thin_device **td) { int r, changed = 0; @@ -867,7 +867,7 @@ static void __close_device(struct dm_thin_device *td) } static int __create_thin(struct dm_pool_metadata *pmd, - dm_thin_id dev) + dm_dev_id dev) { int r; dm_block_t dev_root; @@ -910,7 +910,7 @@ static int __create_thin(struct dm_pool_metadata *pmd, return r; } -int dm_pool_create_thin(struct dm_pool_metadata *pmd, dm_thin_id dev) +int dm_pool_create_thin(struct dm_pool_metadata *pmd, dm_dev_id dev) { int r; @@ -923,7 +923,7 @@ int dm_pool_create_thin(struct dm_pool_metadata *pmd, dm_thin_id dev) static int __set_snapshot_details(struct dm_pool_metadata *pmd, struct dm_thin_device *snap, - dm_thin_id origin, uint32_t time) + dm_dev_id origin, uint32_t time) { int r; struct dm_thin_device *td; @@ -943,7 +943,7 @@ static int __set_snapshot_details(struct dm_pool_metadata *pmd, } static int __create_snap(struct dm_pool_metadata *pmd, - dm_thin_id dev, dm_thin_id origin) + dm_dev_id dev, dm_dev_id origin) { int r; dm_block_t origin_root; @@ -1000,8 +1000,8 @@ bad: } int dm_pool_create_snap(struct dm_pool_metadata *pmd, - dm_thin_id dev, - dm_thin_id origin) + dm_dev_id dev, + dm_dev_id origin) { int r; @@ -1012,7 +1012,7 @@ int dm_pool_create_snap(struct dm_pool_metadata *pmd, return r; } -static int __delete_device(struct dm_pool_metadata *pmd, dm_thin_id dev) +static int __delete_device(struct dm_pool_metadata *pmd, dm_dev_id dev) { int r; uint64_t key = dev; @@ -1045,7 +1045,7 @@ static int __delete_device(struct dm_pool_metadata *pmd, dm_thin_id dev) } int dm_pool_delete_thin_device(struct dm_pool_metadata *pmd, - dm_thin_id dev) + dm_dev_id dev) { int r; @@ -1188,7 +1188,7 @@ int dm_pool_get_held_metadata_root(struct dm_pool_metadata *pmd, return r; } -int dm_pool_open_thin_device(struct dm_pool_metadata *pmd, dm_thin_id dev, +int dm_pool_open_thin_device(struct dm_pool_metadata *pmd, dm_dev_id dev, struct dm_thin_device **td) { int r; @@ -1209,7 +1209,7 @@ int dm_pool_close_thin_device(struct dm_thin_device *td) return 0; } -dm_thin_id dm_thin_dev_id(struct dm_thin_device *td) +dm_dev_id dm_thin_dev_id(struct dm_thin_device *td) { return td->id; } diff --git a/drivers/md/dm-thin-metadata.h b/drivers/md/dm-thin-metadata.h index 4460857..511a223 100644 --- a/drivers/md/dm-thin-metadata.h +++ b/drivers/md/dm-thin-metadata.h @@ -34,7 +34,7 @@ struct dm_thin_device; /* * Device identifier */ -typedef uint64_t dm_thin_id; +typedef uint64_t dm_dev_id; /* * Reopens or creates a new, empty metadata volume. @@ -55,7 +55,7 @@ int dm_pool_metadata_close(struct dm_pool_metadata *pmd); /* * Device creation/deletion. */ -int dm_pool_create_thin(struct dm_pool_metadata *pmd, dm_thin_id dev); +int dm_pool_create_thin(struct dm_pool_metadata *pmd, dm_dev_id dev); /* * An internal snapshot. @@ -63,8 +63,8 @@ int dm_pool_create_thin(struct dm_pool_metadata *pmd, dm_thin_id dev); * You can only snapshot a quiesced origin i.e. one that is either * suspended or not instanced at all. */ -int dm_pool_create_snap(struct dm_pool_metadata *pmd, dm_thin_id dev, - dm_thin_id origin); +int dm_pool_create_snap(struct dm_pool_metadata *pmd, dm_dev_id dev, + dm_dev_id origin); /* * Deletes a virtual device from the metadata. It _is_ safe to call this @@ -72,7 +72,7 @@ int dm_pool_create_snap(struct dm_pool_metadata *pmd, dm_thin_id dev, * failing. You still need to call close() on the device. */ int dm_pool_delete_thin_device(struct dm_pool_metadata *pmd, - dm_thin_id dev); + dm_dev_id dev); /* * Commits _all_ metadata changes: device creation, deletion, mapping @@ -106,12 +106,12 @@ int dm_pool_get_held_metadata_root(struct dm_pool_metadata *pmd, /* * Opening the same device more than once will fail with -EBUSY. */ -int dm_pool_open_thin_device(struct dm_pool_metadata *pmd, dm_thin_id dev, +int dm_pool_open_thin_device(struct dm_pool_metadata *pmd, dm_dev_id dev, struct dm_thin_device **td); int dm_pool_close_thin_device(struct dm_thin_device *td); -dm_thin_id dm_thin_dev_id(struct dm_thin_device *td); +dm_dev_id dm_thin_dev_id(struct dm_thin_device *td); struct dm_thin_lookup_result { dm_block_t block; diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index d9ea643..536835f 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -107,7 +107,7 @@ struct bio_prison; struct cell_key { int virtual; - dm_thin_id dev; + dm_dev_id dev; dm_block_t block; }; @@ -560,7 +560,7 @@ struct pool_c { struct thin_c { struct dm_dev *pool_dev; struct dm_dev *origin_dev; - dm_thin_id dev_id; + dm_dev_id dev_id; struct pool *pool; struct dm_thin_device *td; @@ -2116,7 +2116,7 @@ static int check_arg_count(unsigned argc, unsigned args_required) return 0; } -static int read_dev_id(char *arg, dm_thin_id *dev_id, int warning) +static int read_dev_id(char *arg, dm_dev_id *dev_id, int warning) { if (!kstrtoull(arg, 10, (unsigned long long *)dev_id) && *dev_id <= MAX_DEV_ID) @@ -2130,7 +2130,7 @@ static int read_dev_id(char *arg, dm_thin_id *dev_id, int warning) static int process_create_thin_mesg(unsigned argc, char **argv, struct pool *pool) { - dm_thin_id dev_id; + dm_dev_id dev_id; int r; r = check_arg_count(argc, 2); @@ -2153,8 +2153,8 @@ static int process_create_thin_mesg(unsigned argc, char **argv, struct pool *poo static int process_create_snap_mesg(unsigned argc, char **argv, struct pool *pool) { - dm_thin_id dev_id; - dm_thin_id origin_dev_id; + dm_dev_id dev_id; + dm_dev_id origin_dev_id; int r; r = check_arg_count(argc, 3); @@ -2181,7 +2181,7 @@ static int process_create_snap_mesg(unsigned argc, char **argv, struct pool *poo static int process_delete_mesg(unsigned argc, char **argv, struct pool *pool) { - dm_thin_id dev_id; + dm_dev_id dev_id; int r; r = check_arg_count(argc, 2); @@ -2201,7 +2201,7 @@ static int process_delete_mesg(unsigned argc, char **argv, struct pool *pool) static int process_set_transaction_id_mesg(unsigned argc, char **argv, struct pool *pool) { - dm_thin_id old_id, new_id; + dm_dev_id old_id, new_id; int r; r = check_arg_count(argc, 3); -- 1.7.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/2] dm thin: elevate the bio_prison and deferred_set code to allow for re-use 2012-03-02 23:59 [PATCH 1/2] dm thin: rename dm_thin_id to more generic dm_dev_id Mike Snitzer @ 2012-03-02 23:59 ` Mike Snitzer 2012-03-03 0:26 ` Alasdair G Kergon 2012-03-03 3:13 ` [PATCH 2/2 v2] " Mike Snitzer 2012-03-05 10:29 ` [PATCH 1/2] dm thin: rename dm_thin_id to more generic dm_dev_id Joe Thornber 1 sibling, 2 replies; 6+ messages in thread From: Mike Snitzer @ 2012-03-02 23:59 UTC (permalink / raw) To: dm-devel; +Cc: ejt, snitzer Allow other DM targets to make use of the bio_prison and deferred_set infrastructure. Code elevated to drivers/md/dm-bio-prison.[hc] Signed-off-by: Mike Snitzer <snitzer@redhat.com> --- drivers/md/Makefile | 2 +- drivers/md/dm-bio-prison.c | 336 +++++++++++++++++++++++++++++++++++++ drivers/md/dm-bio-prison.h | 110 ++++++++++++ drivers/md/dm-thin-metadata.h | 6 +- drivers/md/dm-thin.c | 368 +---------------------------------------- 5 files changed, 449 insertions(+), 373 deletions(-) create mode 100644 drivers/md/dm-bio-prison.c create mode 100644 drivers/md/dm-bio-prison.h diff --git a/drivers/md/Makefile b/drivers/md/Makefile index 046860c..c1706ed 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -3,7 +3,7 @@ # dm-mod-y += dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \ - dm-ioctl.o dm-io.o dm-kcopyd.o dm-sysfs.o + dm-ioctl.o dm-io.o dm-kcopyd.o dm-sysfs.o dm-bio-prison.o dm-multipath-y += dm-path-selector.o dm-mpath.o dm-snapshot-y += dm-snap.o dm-exception-store.o dm-snap-transient.o \ dm-snap-persistent.o diff --git a/drivers/md/dm-bio-prison.c b/drivers/md/dm-bio-prison.c new file mode 100644 index 0000000..ddc4afc --- /dev/null +++ b/drivers/md/dm-bio-prison.c @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2012 Red Hat. + * + * This file is released under the GPL. + */ + +#include "dm-bio-prison.h" + +#include <linux/module.h> + +/*----------------------------------------------------------------*/ + +static uint32_t calc_nr_buckets(unsigned nr_cells) +{ + uint32_t n = 128; + + nr_cells /= 4; + nr_cells = min(nr_cells, 8192u); + + while (n < nr_cells) + n <<= 1; + + return n; +} + +/* + * @nr_cells should be the number of cells you want in use _concurrently_. + * Don't confuse it with the number of distinct keys. + */ +struct bio_prison *prison_create(unsigned nr_cells) +{ + unsigned i; + uint32_t nr_buckets = calc_nr_buckets(nr_cells); + size_t len = sizeof(struct bio_prison) + + (sizeof(struct hlist_head) * nr_buckets); + struct bio_prison *prison = kmalloc(len, GFP_KERNEL); + + if (!prison) + return NULL; + + spin_lock_init(&prison->lock); + prison->cell_pool = mempool_create_kmalloc_pool(nr_cells, + sizeof(struct cell)); + if (!prison->cell_pool) { + kfree(prison); + return NULL; + } + + prison->nr_buckets = nr_buckets; + prison->hash_mask = nr_buckets - 1; + prison->cells = (struct hlist_head *) (prison + 1); + for (i = 0; i < nr_buckets; i++) + INIT_HLIST_HEAD(prison->cells + i); + + return prison; +} +EXPORT_SYMBOL(prison_create); + +void prison_destroy(struct bio_prison *prison) +{ + mempool_destroy(prison->cell_pool); + kfree(prison); +} +EXPORT_SYMBOL(prison_destroy); + +static uint32_t hash_key(struct bio_prison *prison, struct cell_key *key) +{ + const unsigned long BIG_PRIME = 4294967291UL; + uint64_t hash = key->block * BIG_PRIME; + + return (uint32_t) (hash & prison->hash_mask); +} + +static int keys_equal(struct cell_key *lhs, struct cell_key *rhs) +{ + return (lhs->virtual == rhs->virtual) && + (lhs->dev == rhs->dev) && + (lhs->block == rhs->block); +} + +static struct cell *__search_bucket(struct hlist_head *bucket, + struct cell_key *key) +{ + struct cell *cell; + struct hlist_node *tmp; + + hlist_for_each_entry(cell, tmp, bucket, list) + if (keys_equal(&cell->key, key)) + return cell; + + return NULL; +} + +/* + * This may block if a new cell needs allocating. You must ensure that + * cells will be unlocked even if the calling thread is blocked. + * + * Returns 1 if the cell was already held, 0 if @inmate is the new holder. + */ +int bio_detain(struct bio_prison *prison, struct cell_key *key, + struct bio *inmate, struct cell **ref) +{ + int r; + unsigned long flags; + uint32_t hash = hash_key(prison, key); + struct cell *uninitialized_var(cell), *cell2 = NULL; + + BUG_ON(hash > prison->nr_buckets); + + spin_lock_irqsave(&prison->lock, flags); + cell = __search_bucket(prison->cells + hash, key); + + if (!cell) { + /* + * Allocate a new cell + */ + spin_unlock_irqrestore(&prison->lock, flags); + cell2 = mempool_alloc(prison->cell_pool, GFP_NOIO); + spin_lock_irqsave(&prison->lock, flags); + + /* + * We've been unlocked, so we have to double check that + * nobody else has inserted this cell in the meantime. + */ + cell = __search_bucket(prison->cells + hash, key); + + if (!cell) { + cell = cell2; + cell2 = NULL; + + cell->prison = prison; + memcpy(&cell->key, key, sizeof(cell->key)); + cell->holder = inmate; + bio_list_init(&cell->bios); + hlist_add_head(&cell->list, prison->cells + hash); + r = 0; + + } else { + mempool_free(cell2, prison->cell_pool); + cell2 = NULL; + r = 1; + bio_list_add(&cell->bios, inmate); + } + + } else { + r = 1; + bio_list_add(&cell->bios, inmate); + } + spin_unlock_irqrestore(&prison->lock, flags); + + *ref = cell; + return r; +} +EXPORT_SYMBOL(bio_detain); + +/* + * @inmates must have been initialised prior to this call + */ +static void __cell_release(struct cell *cell, struct bio_list *inmates) +{ + struct bio_prison *prison = cell->prison; + + hlist_del(&cell->list); + + if (inmates) { + bio_list_add(inmates, cell->holder); + bio_list_merge(inmates, &cell->bios); + } + + mempool_free(cell, prison->cell_pool); +} + +void cell_release(struct cell *cell, struct bio_list *bios) +{ + unsigned long flags; + struct bio_prison *prison = cell->prison; + + spin_lock_irqsave(&prison->lock, flags); + __cell_release(cell, bios); + spin_unlock_irqrestore(&prison->lock, flags); +} +EXPORT_SYMBOL(cell_release); + +/* + * There are a couple of places where we put a bio into a cell briefly + * before taking it out again. In these situations we know that no other + * bio may be in the cell. This function releases the cell, and also does + * a sanity check. + */ +static void __cell_release_singleton(struct cell *cell, struct bio *bio) +{ + hlist_del(&cell->list); + BUG_ON(cell->holder != bio); + BUG_ON(!bio_list_empty(&cell->bios)); +} + +void cell_release_singleton(struct cell *cell, struct bio *bio) +{ + unsigned long flags; + struct bio_prison *prison = cell->prison; + + spin_lock_irqsave(&prison->lock, flags); + __cell_release_singleton(cell, bio); + spin_unlock_irqrestore(&prison->lock, flags); +} +EXPORT_SYMBOL(cell_release_singleton); + +/* + * Sometimes we don't want the holder, just the additional bios. + */ +static void __cell_release_no_holder(struct cell *cell, struct bio_list *inmates) +{ + struct bio_prison *prison = cell->prison; + + hlist_del(&cell->list); + if (inmates) + bio_list_merge(inmates, &cell->bios); + + mempool_free(cell, prison->cell_pool); +} + +void cell_release_no_holder(struct cell *cell, struct bio_list *inmates) +{ + unsigned long flags; + struct bio_prison *prison = cell->prison; + + spin_lock_irqsave(&prison->lock, flags); + __cell_release_no_holder(cell, inmates); + spin_unlock_irqrestore(&prison->lock, flags); +} +EXPORT_SYMBOL(cell_release_no_holder); + +void cell_error(struct cell *cell) +{ + struct bio_prison *prison = cell->prison; + struct bio_list bios; + struct bio *bio; + unsigned long flags; + + bio_list_init(&bios); + + spin_lock_irqsave(&prison->lock, flags); + __cell_release(cell, &bios); + spin_unlock_irqrestore(&prison->lock, flags); + + while ((bio = bio_list_pop(&bios))) + bio_io_error(bio); +} +EXPORT_SYMBOL(cell_error); + +/*----------------------------------------------------------------*/ + +void ds_init(struct deferred_set *ds) +{ + int i; + + spin_lock_init(&ds->lock); + ds->current_entry = 0; + ds->sweeper = 0; + for (i = 0; i < DEFERRED_SET_SIZE; i++) { + ds->entries[i].ds = ds; + ds->entries[i].count = 0; + INIT_LIST_HEAD(&ds->entries[i].work_items); + } +} +EXPORT_SYMBOL(ds_init); + +struct deferred_entry *ds_inc(struct deferred_set *ds) +{ + unsigned long flags; + struct deferred_entry *entry; + + spin_lock_irqsave(&ds->lock, flags); + entry = ds->entries + ds->current_entry; + entry->count++; + spin_unlock_irqrestore(&ds->lock, flags); + + return entry; +} +EXPORT_SYMBOL(ds_inc); + +unsigned ds_next(unsigned index) +{ + return (index + 1) % DEFERRED_SET_SIZE; +} + +static void __sweep(struct deferred_set *ds, struct list_head *head) +{ + while ((ds->sweeper != ds->current_entry) && + !ds->entries[ds->sweeper].count) { + list_splice_init(&ds->entries[ds->sweeper].work_items, head); + ds->sweeper = ds_next(ds->sweeper); + } + + if ((ds->sweeper == ds->current_entry) && !ds->entries[ds->sweeper].count) + list_splice_init(&ds->entries[ds->sweeper].work_items, head); +} + +void ds_dec(struct deferred_entry *entry, struct list_head *head) +{ + unsigned long flags; + + spin_lock_irqsave(&entry->ds->lock, flags); + BUG_ON(!entry->count); + --entry->count; + __sweep(entry->ds, head); + spin_unlock_irqrestore(&entry->ds->lock, flags); +} +EXPORT_SYMBOL(ds_dec); + +/* + * Returns 1 if deferred or 0 if no pending items to delay job. + */ +int ds_add_work(struct deferred_set *ds, struct list_head *work) +{ + int r = 1; + unsigned long flags; + unsigned next_entry; + + spin_lock_irqsave(&ds->lock, flags); + if ((ds->sweeper == ds->current_entry) && + !ds->entries[ds->current_entry].count) + r = 0; + else { + list_add(work, &ds->entries[ds->current_entry].work_items); + next_entry = ds_next(ds->current_entry); + if (!ds->entries[next_entry].count) + ds->current_entry = next_entry; + } + spin_unlock_irqrestore(&ds->lock, flags); + + return r; +} +EXPORT_SYMBOL(ds_add_work); + +/*----------------------------------------------------------------*/ diff --git a/drivers/md/dm-bio-prison.h b/drivers/md/dm-bio-prison.h new file mode 100644 index 0000000..ba428d0 --- /dev/null +++ b/drivers/md/dm-bio-prison.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2012 Red Hat. + * + * This file is released under the GPL. + */ + +#ifndef DM_BIO_PRISON_H +#define DM_BIO_PRISON_H + +#include "persistent-data/dm-block-manager.h" + +#include <linux/list.h> + +/*----------------------------------------------------------------*/ + +/* + * Device identifier + */ +typedef uint64_t dm_dev_id; + +/*----------------------------------------------------------------*/ + +/* + * Sometimes we can't deal with a bio straight away. We put them in prison + * where they can't cause any mischief. Bios are put in a cell identified + * by a key, multiple bios can be in the same cell. When the cell is + * subsequently unlocked the bios become available. + */ +struct bio_prison; + +struct cell_key { + int virtual; + dm_dev_id dev; + dm_block_t block; +}; + +struct cell { + struct hlist_node list; + struct bio_prison *prison; + struct cell_key key; + struct bio *holder; + struct bio_list bios; +}; + +struct bio_prison { + spinlock_t lock; + mempool_t *cell_pool; + + unsigned nr_buckets; + unsigned hash_mask; + struct hlist_head *cells; +}; + +/* + * @nr_cells should be the number of cells you want in use _concurrently_. + * Don't confuse it with the number of distinct keys. + */ +struct bio_prison *prison_create(unsigned nr_cells); +void prison_destroy(struct bio_prison *prison); + +/* + * This may block if a new cell needs allocating. You must ensure that + * cells will be unlocked even if the calling thread is blocked. + * + * Returns 1 if the cell was already held, 0 if @inmate is the new holder. + */ +int bio_detain(struct bio_prison *prison, struct cell_key *key, + struct bio *inmate, struct cell **ref); +void cell_release(struct cell *cell, struct bio_list *bios); +void cell_release_singleton(struct cell *cell, struct bio *bio); +void cell_release_no_holder(struct cell *cell, struct bio_list *inmates); +void cell_error(struct cell *cell); + +/*----------------------------------------------------------------*/ + +/* + * We use the deferred set to keep track of pending reads to shared blocks. + * We do this to ensure the new mapping caused by a write isn't performed + * until these prior reads have completed. Otherwise the insertion of the + * new mapping could free the old block that the read bios are mapped to. + */ +#define DEFERRED_SET_SIZE 64 + +struct deferred_set; +struct deferred_entry { + struct deferred_set *ds; + unsigned count; + struct list_head work_items; +}; + +struct deferred_set { + spinlock_t lock; + unsigned current_entry; + unsigned sweeper; + struct deferred_entry entries[DEFERRED_SET_SIZE]; +}; + +void ds_init(struct deferred_set *ds); +struct deferred_entry *ds_inc(struct deferred_set *ds); +unsigned ds_next(unsigned index); +void ds_dec(struct deferred_entry *entry, struct list_head *head); + +/* + * Returns 1 if deferred or 0 if no pending items to delay job. + */ +int ds_add_work(struct deferred_set *ds, struct list_head *work); + +/*----------------------------------------------------------------*/ + +#endif diff --git a/drivers/md/dm-thin-metadata.h b/drivers/md/dm-thin-metadata.h index 511a223..d718189 100644 --- a/drivers/md/dm-thin-metadata.h +++ b/drivers/md/dm-thin-metadata.h @@ -7,6 +7,7 @@ #ifndef DM_THIN_METADATA_H #define DM_THIN_METADATA_H +#include "dm-bio-prison.h" #include "persistent-data/dm-block-manager.h" #define THIN_METADATA_BLOCK_SIZE 4096 @@ -32,11 +33,6 @@ struct dm_pool_metadata; struct dm_thin_device; /* - * Device identifier - */ -typedef uint64_t dm_dev_id; - -/* * Reopens or creates a new, empty metadata volume. */ struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev, diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 536835f..d0be4b0 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -4,6 +4,7 @@ * This file is released under the GPL. */ +#include "dm-bio-prison.h" #include "dm-thin-metadata.h" #include <linux/device-mapper.h> @@ -20,7 +21,6 @@ * Tunable constants */ #define ENDIO_HOOK_POOL_SIZE 10240 -#define DEFERRED_SET_SIZE 64 #define MAPPING_POOL_SIZE 1024 #define PRISON_CELLS 1024 #define COMMIT_PERIOD HZ @@ -98,372 +98,6 @@ /*----------------------------------------------------------------*/ /* - * Sometimes we can't deal with a bio straight away. We put them in prison - * where they can't cause any mischief. Bios are put in a cell identified - * by a key, multiple bios can be in the same cell. When the cell is - * subsequently unlocked the bios become available. - */ -struct bio_prison; - -struct cell_key { - int virtual; - dm_dev_id dev; - dm_block_t block; -}; - -struct cell { - struct hlist_node list; - struct bio_prison *prison; - struct cell_key key; - struct bio *holder; - struct bio_list bios; -}; - -struct bio_prison { - spinlock_t lock; - mempool_t *cell_pool; - - unsigned nr_buckets; - unsigned hash_mask; - struct hlist_head *cells; -}; - -static uint32_t calc_nr_buckets(unsigned nr_cells) -{ - uint32_t n = 128; - - nr_cells /= 4; - nr_cells = min(nr_cells, 8192u); - - while (n < nr_cells) - n <<= 1; - - return n; -} - -/* - * @nr_cells should be the number of cells you want in use _concurrently_. - * Don't confuse it with the number of distinct keys. - */ -static struct bio_prison *prison_create(unsigned nr_cells) -{ - unsigned i; - uint32_t nr_buckets = calc_nr_buckets(nr_cells); - size_t len = sizeof(struct bio_prison) + - (sizeof(struct hlist_head) * nr_buckets); - struct bio_prison *prison = kmalloc(len, GFP_KERNEL); - - if (!prison) - return NULL; - - spin_lock_init(&prison->lock); - prison->cell_pool = mempool_create_kmalloc_pool(nr_cells, - sizeof(struct cell)); - if (!prison->cell_pool) { - kfree(prison); - return NULL; - } - - prison->nr_buckets = nr_buckets; - prison->hash_mask = nr_buckets - 1; - prison->cells = (struct hlist_head *) (prison + 1); - for (i = 0; i < nr_buckets; i++) - INIT_HLIST_HEAD(prison->cells + i); - - return prison; -} - -static void prison_destroy(struct bio_prison *prison) -{ - mempool_destroy(prison->cell_pool); - kfree(prison); -} - -static uint32_t hash_key(struct bio_prison *prison, struct cell_key *key) -{ - const unsigned long BIG_PRIME = 4294967291UL; - uint64_t hash = key->block * BIG_PRIME; - - return (uint32_t) (hash & prison->hash_mask); -} - -static int keys_equal(struct cell_key *lhs, struct cell_key *rhs) -{ - return (lhs->virtual == rhs->virtual) && - (lhs->dev == rhs->dev) && - (lhs->block == rhs->block); -} - -static struct cell *__search_bucket(struct hlist_head *bucket, - struct cell_key *key) -{ - struct cell *cell; - struct hlist_node *tmp; - - hlist_for_each_entry(cell, tmp, bucket, list) - if (keys_equal(&cell->key, key)) - return cell; - - return NULL; -} - -/* - * This may block if a new cell needs allocating. You must ensure that - * cells will be unlocked even if the calling thread is blocked. - * - * Returns 1 if the cell was already held, 0 if @inmate is the new holder. - */ -static int bio_detain(struct bio_prison *prison, struct cell_key *key, - struct bio *inmate, struct cell **ref) -{ - int r; - unsigned long flags; - uint32_t hash = hash_key(prison, key); - struct cell *uninitialized_var(cell), *cell2 = NULL; - - BUG_ON(hash > prison->nr_buckets); - - spin_lock_irqsave(&prison->lock, flags); - cell = __search_bucket(prison->cells + hash, key); - - if (!cell) { - /* - * Allocate a new cell - */ - spin_unlock_irqrestore(&prison->lock, flags); - cell2 = mempool_alloc(prison->cell_pool, GFP_NOIO); - spin_lock_irqsave(&prison->lock, flags); - - /* - * We've been unlocked, so we have to double check that - * nobody else has inserted this cell in the meantime. - */ - cell = __search_bucket(prison->cells + hash, key); - - if (!cell) { - cell = cell2; - cell2 = NULL; - - cell->prison = prison; - memcpy(&cell->key, key, sizeof(cell->key)); - cell->holder = inmate; - bio_list_init(&cell->bios); - hlist_add_head(&cell->list, prison->cells + hash); - r = 0; - - } else { - mempool_free(cell2, prison->cell_pool); - cell2 = NULL; - r = 1; - bio_list_add(&cell->bios, inmate); - } - - } else { - r = 1; - bio_list_add(&cell->bios, inmate); - } - spin_unlock_irqrestore(&prison->lock, flags); - - *ref = cell; - return r; -} - -/* - * @inmates must have been initialised prior to this call - */ -static void __cell_release(struct cell *cell, struct bio_list *inmates) -{ - struct bio_prison *prison = cell->prison; - - hlist_del(&cell->list); - - if (inmates) { - bio_list_add(inmates, cell->holder); - bio_list_merge(inmates, &cell->bios); - } - - mempool_free(cell, prison->cell_pool); -} - -static void cell_release(struct cell *cell, struct bio_list *bios) -{ - unsigned long flags; - struct bio_prison *prison = cell->prison; - - spin_lock_irqsave(&prison->lock, flags); - __cell_release(cell, bios); - spin_unlock_irqrestore(&prison->lock, flags); -} - -/* - * There are a couple of places where we put a bio into a cell briefly - * before taking it out again. In these situations we know that no other - * bio may be in the cell. This function releases the cell, and also does - * a sanity check. - */ -static void __cell_release_singleton(struct cell *cell, struct bio *bio) -{ - hlist_del(&cell->list); - BUG_ON(cell->holder != bio); - BUG_ON(!bio_list_empty(&cell->bios)); -} - -static void cell_release_singleton(struct cell *cell, struct bio *bio) -{ - unsigned long flags; - struct bio_prison *prison = cell->prison; - - spin_lock_irqsave(&prison->lock, flags); - __cell_release_singleton(cell, bio); - spin_unlock_irqrestore(&prison->lock, flags); -} - -/* - * Sometimes we don't want the holder, just the additional bios. - */ -static void __cell_release_no_holder(struct cell *cell, struct bio_list *inmates) -{ - struct bio_prison *prison = cell->prison; - - hlist_del(&cell->list); - if (inmates) - bio_list_merge(inmates, &cell->bios); - - mempool_free(cell, prison->cell_pool); -} - -static void cell_release_no_holder(struct cell *cell, struct bio_list *inmates) -{ - unsigned long flags; - struct bio_prison *prison = cell->prison; - - spin_lock_irqsave(&prison->lock, flags); - __cell_release_no_holder(cell, inmates); - spin_unlock_irqrestore(&prison->lock, flags); -} - -static void cell_error(struct cell *cell) -{ - struct bio_prison *prison = cell->prison; - struct bio_list bios; - struct bio *bio; - unsigned long flags; - - bio_list_init(&bios); - - spin_lock_irqsave(&prison->lock, flags); - __cell_release(cell, &bios); - spin_unlock_irqrestore(&prison->lock, flags); - - while ((bio = bio_list_pop(&bios))) - bio_io_error(bio); -} - -/*----------------------------------------------------------------*/ - -/* - * We use the deferred set to keep track of pending reads to shared blocks. - * We do this to ensure the new mapping caused by a write isn't performed - * until these prior reads have completed. Otherwise the insertion of the - * new mapping could free the old block that the read bios are mapped to. - */ - -struct deferred_set; -struct deferred_entry { - struct deferred_set *ds; - unsigned count; - struct list_head work_items; -}; - -struct deferred_set { - spinlock_t lock; - unsigned current_entry; - unsigned sweeper; - struct deferred_entry entries[DEFERRED_SET_SIZE]; -}; - -static void ds_init(struct deferred_set *ds) -{ - int i; - - spin_lock_init(&ds->lock); - ds->current_entry = 0; - ds->sweeper = 0; - for (i = 0; i < DEFERRED_SET_SIZE; i++) { - ds->entries[i].ds = ds; - ds->entries[i].count = 0; - INIT_LIST_HEAD(&ds->entries[i].work_items); - } -} - -static struct deferred_entry *ds_inc(struct deferred_set *ds) -{ - unsigned long flags; - struct deferred_entry *entry; - - spin_lock_irqsave(&ds->lock, flags); - entry = ds->entries + ds->current_entry; - entry->count++; - spin_unlock_irqrestore(&ds->lock, flags); - - return entry; -} - -static unsigned ds_next(unsigned index) -{ - return (index + 1) % DEFERRED_SET_SIZE; -} - -static void __sweep(struct deferred_set *ds, struct list_head *head) -{ - while ((ds->sweeper != ds->current_entry) && - !ds->entries[ds->sweeper].count) { - list_splice_init(&ds->entries[ds->sweeper].work_items, head); - ds->sweeper = ds_next(ds->sweeper); - } - - if ((ds->sweeper == ds->current_entry) && !ds->entries[ds->sweeper].count) - list_splice_init(&ds->entries[ds->sweeper].work_items, head); -} - -static void ds_dec(struct deferred_entry *entry, struct list_head *head) -{ - unsigned long flags; - - spin_lock_irqsave(&entry->ds->lock, flags); - BUG_ON(!entry->count); - --entry->count; - __sweep(entry->ds, head); - spin_unlock_irqrestore(&entry->ds->lock, flags); -} - -/* - * Returns 1 if deferred or 0 if no pending items to delay job. - */ -static int ds_add_work(struct deferred_set *ds, struct list_head *work) -{ - int r = 1; - unsigned long flags; - unsigned next_entry; - - spin_lock_irqsave(&ds->lock, flags); - if ((ds->sweeper == ds->current_entry) && - !ds->entries[ds->current_entry].count) - r = 0; - else { - list_add(work, &ds->entries[ds->current_entry].work_items); - next_entry = ds_next(ds->current_entry); - if (!ds->entries[next_entry].count) - ds->current_entry = next_entry; - } - spin_unlock_irqrestore(&ds->lock, flags); - - return r; -} - -/*----------------------------------------------------------------*/ - -/* * Key building. */ static void build_data_key(struct dm_thin_device *td, -- 1.7.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 2/2] dm thin: elevate the bio_prison and deferred_set code to allow for re-use 2012-03-02 23:59 ` [PATCH 2/2] dm thin: elevate the bio_prison and deferred_set code to allow for re-use Mike Snitzer @ 2012-03-03 0:26 ` Alasdair G Kergon 2012-03-03 3:13 ` [PATCH 2/2 v2] " Mike Snitzer 1 sibling, 0 replies; 6+ messages in thread From: Alasdair G Kergon @ 2012-03-03 0:26 UTC (permalink / raw) To: Mike Snitzer; +Cc: dm-devel, ejt On Fri, Mar 02, 2012 at 06:59:30PM -0500, Mike Snitzer wrote: > +EXPORT_SYMBOL(prison_create); _GPL please and place a dm_something prefix on everything exported. Thanks, Alasdair ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/2 v2] dm thin: elevate the bio_prison and deferred_set code to allow for re-use 2012-03-02 23:59 ` [PATCH 2/2] dm thin: elevate the bio_prison and deferred_set code to allow for re-use Mike Snitzer 2012-03-03 0:26 ` Alasdair G Kergon @ 2012-03-03 3:13 ` Mike Snitzer 1 sibling, 0 replies; 6+ messages in thread From: Mike Snitzer @ 2012-03-03 3:13 UTC (permalink / raw) To: dm-devel; +Cc: ejt Allow other DM targets to make use of the bio_prison and deferred_set infrastructure. Code elevated to drivers/md/dm-bio-prison.[hc] Signed-off-by: Mike Snitzer <snitzer@redhat.com> --- drivers/md/Makefile | 2 drivers/md/dm-bio-prison.c | 336 ++++++++++++++++++++++++++++++++ drivers/md/dm-bio-prison.h | 109 ++++++++++ drivers/md/dm-thin-metadata.h | 6 drivers/md/dm-thin.c | 438 +++--------------------------------------- 5 files changed, 483 insertions(+), 408 deletions(-) create mode 100644 drivers/md/dm-bio-prison.c create mode 100644 drivers/md/dm-bio-prison.h Index: linux-2.6/drivers/md/Makefile =================================================================== --- linux-2.6.orig/drivers/md/Makefile +++ linux-2.6/drivers/md/Makefile @@ -3,7 +3,7 @@ # dm-mod-y += dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \ - dm-ioctl.o dm-io.o dm-kcopyd.o dm-sysfs.o + dm-ioctl.o dm-io.o dm-kcopyd.o dm-sysfs.o dm-bio-prison.o dm-multipath-y += dm-path-selector.o dm-mpath.o dm-snapshot-y += dm-snap.o dm-exception-store.o dm-snap-transient.o \ dm-snap-persistent.o Index: linux-2.6/drivers/md/dm-bio-prison.c =================================================================== --- /dev/null +++ linux-2.6/drivers/md/dm-bio-prison.c @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2012 Red Hat. + * + * This file is released under the GPL. + */ + +#include "dm-bio-prison.h" + +#include <linux/module.h> + +/*----------------------------------------------------------------*/ + +static uint32_t calc_nr_buckets(unsigned nr_cells) +{ + uint32_t n = 128; + + nr_cells /= 4; + nr_cells = min(nr_cells, 8192u); + + while (n < nr_cells) + n <<= 1; + + return n; +} + +/* + * @nr_cells should be the number of cells you want in use _concurrently_. + * Don't confuse it with the number of distinct keys. + */ +struct bio_prison *dm_prison_create(unsigned nr_cells) +{ + unsigned i; + uint32_t nr_buckets = calc_nr_buckets(nr_cells); + size_t len = sizeof(struct bio_prison) + + (sizeof(struct hlist_head) * nr_buckets); + struct bio_prison *prison = kmalloc(len, GFP_KERNEL); + + if (!prison) + return NULL; + + spin_lock_init(&prison->lock); + prison->cell_pool = mempool_create_kmalloc_pool(nr_cells, + sizeof(struct cell)); + if (!prison->cell_pool) { + kfree(prison); + return NULL; + } + + prison->nr_buckets = nr_buckets; + prison->hash_mask = nr_buckets - 1; + prison->cells = (struct hlist_head *) (prison + 1); + for (i = 0; i < nr_buckets; i++) + INIT_HLIST_HEAD(prison->cells + i); + + return prison; +} +EXPORT_SYMBOL_GPL(dm_prison_create); + +void dm_prison_destroy(struct bio_prison *prison) +{ + mempool_destroy(prison->cell_pool); + kfree(prison); +} +EXPORT_SYMBOL_GPL(dm_prison_destroy); + +static uint32_t hash_key(struct bio_prison *prison, struct cell_key *key) +{ + const unsigned long BIG_PRIME = 4294967291UL; + uint64_t hash = key->block * BIG_PRIME; + + return (uint32_t) (hash & prison->hash_mask); +} + +static int keys_equal(struct cell_key *lhs, struct cell_key *rhs) +{ + return (lhs->virtual == rhs->virtual) && + (lhs->dev == rhs->dev) && + (lhs->block == rhs->block); +} + +static struct cell *__search_bucket(struct hlist_head *bucket, + struct cell_key *key) +{ + struct cell *cell; + struct hlist_node *tmp; + + hlist_for_each_entry(cell, tmp, bucket, list) + if (keys_equal(&cell->key, key)) + return cell; + + return NULL; +} + +/* + * This may block if a new cell needs allocating. You must ensure that + * cells will be unlocked even if the calling thread is blocked. + * + * Returns 1 if the cell was already held, 0 if @inmate is the new holder. + */ +int dm_bio_detain(struct bio_prison *prison, struct cell_key *key, + struct bio *inmate, struct cell **ref) +{ + int r; + unsigned long flags; + uint32_t hash = hash_key(prison, key); + struct cell *uninitialized_var(cell), *cell2 = NULL; + + BUG_ON(hash > prison->nr_buckets); + + spin_lock_irqsave(&prison->lock, flags); + cell = __search_bucket(prison->cells + hash, key); + + if (!cell) { + /* + * Allocate a new cell + */ + spin_unlock_irqrestore(&prison->lock, flags); + cell2 = mempool_alloc(prison->cell_pool, GFP_NOIO); + spin_lock_irqsave(&prison->lock, flags); + + /* + * We've been unlocked, so we have to double check that + * nobody else has inserted this cell in the meantime. + */ + cell = __search_bucket(prison->cells + hash, key); + + if (!cell) { + cell = cell2; + cell2 = NULL; + + cell->prison = prison; + memcpy(&cell->key, key, sizeof(cell->key)); + cell->holder = inmate; + bio_list_init(&cell->bios); + hlist_add_head(&cell->list, prison->cells + hash); + r = 0; + + } else { + mempool_free(cell2, prison->cell_pool); + cell2 = NULL; + r = 1; + bio_list_add(&cell->bios, inmate); + } + + } else { + r = 1; + bio_list_add(&cell->bios, inmate); + } + spin_unlock_irqrestore(&prison->lock, flags); + + *ref = cell; + return r; +} +EXPORT_SYMBOL_GPL(dm_bio_detain); + +/* + * @inmates must have been initialised prior to this call + */ +static void __cell_release(struct cell *cell, struct bio_list *inmates) +{ + struct bio_prison *prison = cell->prison; + + hlist_del(&cell->list); + + if (inmates) { + bio_list_add(inmates, cell->holder); + bio_list_merge(inmates, &cell->bios); + } + + mempool_free(cell, prison->cell_pool); +} + +void dm_cell_release(struct cell *cell, struct bio_list *bios) +{ + unsigned long flags; + struct bio_prison *prison = cell->prison; + + spin_lock_irqsave(&prison->lock, flags); + __cell_release(cell, bios); + spin_unlock_irqrestore(&prison->lock, flags); +} +EXPORT_SYMBOL_GPL(dm_cell_release); + +/* + * There are a couple of places where we put a bio into a cell briefly + * before taking it out again. In these situations we know that no other + * bio may be in the cell. This function releases the cell, and also does + * a sanity check. + */ +static void __cell_release_singleton(struct cell *cell, struct bio *bio) +{ + hlist_del(&cell->list); + BUG_ON(cell->holder != bio); + BUG_ON(!bio_list_empty(&cell->bios)); +} + +void dm_cell_release_singleton(struct cell *cell, struct bio *bio) +{ + unsigned long flags; + struct bio_prison *prison = cell->prison; + + spin_lock_irqsave(&prison->lock, flags); + __cell_release_singleton(cell, bio); + spin_unlock_irqrestore(&prison->lock, flags); +} +EXPORT_SYMBOL_GPL(dm_cell_release_singleton); + +/* + * Sometimes we don't want the holder, just the additional bios. + */ +static void __cell_release_no_holder(struct cell *cell, struct bio_list *inmates) +{ + struct bio_prison *prison = cell->prison; + + hlist_del(&cell->list); + if (inmates) + bio_list_merge(inmates, &cell->bios); + + mempool_free(cell, prison->cell_pool); +} + +void dm_cell_release_no_holder(struct cell *cell, struct bio_list *inmates) +{ + unsigned long flags; + struct bio_prison *prison = cell->prison; + + spin_lock_irqsave(&prison->lock, flags); + __cell_release_no_holder(cell, inmates); + spin_unlock_irqrestore(&prison->lock, flags); +} +EXPORT_SYMBOL_GPL(dm_cell_release_no_holder); + +void dm_cell_error(struct cell *cell) +{ + struct bio_prison *prison = cell->prison; + struct bio_list bios; + struct bio *bio; + unsigned long flags; + + bio_list_init(&bios); + + spin_lock_irqsave(&prison->lock, flags); + __cell_release(cell, &bios); + spin_unlock_irqrestore(&prison->lock, flags); + + while ((bio = bio_list_pop(&bios))) + bio_io_error(bio); +} +EXPORT_SYMBOL_GPL(dm_cell_error); + +/*----------------------------------------------------------------*/ + +void dm_ds_init(struct deferred_set *ds) +{ + int i; + + spin_lock_init(&ds->lock); + ds->current_entry = 0; + ds->sweeper = 0; + for (i = 0; i < DEFERRED_SET_SIZE; i++) { + ds->entries[i].ds = ds; + ds->entries[i].count = 0; + INIT_LIST_HEAD(&ds->entries[i].work_items); + } +} +EXPORT_SYMBOL_GPL(dm_ds_init); + +struct deferred_entry *dm_ds_inc(struct deferred_set *ds) +{ + unsigned long flags; + struct deferred_entry *entry; + + spin_lock_irqsave(&ds->lock, flags); + entry = ds->entries + ds->current_entry; + entry->count++; + spin_unlock_irqrestore(&ds->lock, flags); + + return entry; +} +EXPORT_SYMBOL_GPL(dm_ds_inc); + +static unsigned ds_next(unsigned index) +{ + return (index + 1) % DEFERRED_SET_SIZE; +} + +static void __sweep(struct deferred_set *ds, struct list_head *head) +{ + while ((ds->sweeper != ds->current_entry) && + !ds->entries[ds->sweeper].count) { + list_splice_init(&ds->entries[ds->sweeper].work_items, head); + ds->sweeper = ds_next(ds->sweeper); + } + + if ((ds->sweeper == ds->current_entry) && !ds->entries[ds->sweeper].count) + list_splice_init(&ds->entries[ds->sweeper].work_items, head); +} + +void dm_ds_dec(struct deferred_entry *entry, struct list_head *head) +{ + unsigned long flags; + + spin_lock_irqsave(&entry->ds->lock, flags); + BUG_ON(!entry->count); + --entry->count; + __sweep(entry->ds, head); + spin_unlock_irqrestore(&entry->ds->lock, flags); +} +EXPORT_SYMBOL_GPL(dm_ds_dec); + +/* + * Returns 1 if deferred or 0 if no pending items to delay job. + */ +int dm_ds_add_work(struct deferred_set *ds, struct list_head *work) +{ + int r = 1; + unsigned long flags; + unsigned next_entry; + + spin_lock_irqsave(&ds->lock, flags); + if ((ds->sweeper == ds->current_entry) && + !ds->entries[ds->current_entry].count) + r = 0; + else { + list_add(work, &ds->entries[ds->current_entry].work_items); + next_entry = ds_next(ds->current_entry); + if (!ds->entries[next_entry].count) + ds->current_entry = next_entry; + } + spin_unlock_irqrestore(&ds->lock, flags); + + return r; +} +EXPORT_SYMBOL_GPL(dm_ds_add_work); + +/*----------------------------------------------------------------*/ Index: linux-2.6/drivers/md/dm-bio-prison.h =================================================================== --- /dev/null +++ linux-2.6/drivers/md/dm-bio-prison.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012 Red Hat. + * + * This file is released under the GPL. + */ + +#ifndef DM_BIO_PRISON_H +#define DM_BIO_PRISON_H + +#include "persistent-data/dm-block-manager.h" + +#include <linux/list.h> + +/*----------------------------------------------------------------*/ + +/* + * Device identifier + */ +typedef uint64_t dm_dev_id; + +/*----------------------------------------------------------------*/ + +/* + * Sometimes we can't deal with a bio straight away. We put them in prison + * where they can't cause any mischief. Bios are put in a cell identified + * by a key, multiple bios can be in the same cell. When the cell is + * subsequently unlocked the bios become available. + */ +struct bio_prison; + +struct cell_key { + int virtual; + dm_dev_id dev; + dm_block_t block; +}; + +struct cell { + struct hlist_node list; + struct bio_prison *prison; + struct cell_key key; + struct bio *holder; + struct bio_list bios; +}; + +struct bio_prison { + spinlock_t lock; + mempool_t *cell_pool; + + unsigned nr_buckets; + unsigned hash_mask; + struct hlist_head *cells; +}; + +/* + * @nr_cells should be the number of cells you want in use _concurrently_. + * Don't confuse it with the number of distinct keys. + */ +struct bio_prison *dm_prison_create(unsigned nr_cells); +void dm_prison_destroy(struct bio_prison *prison); + +/* + * This may block if a new cell needs allocating. You must ensure that + * cells will be unlocked even if the calling thread is blocked. + * + * Returns 1 if the cell was already held, 0 if @inmate is the new holder. + */ +int dm_bio_detain(struct bio_prison *prison, struct cell_key *key, + struct bio *inmate, struct cell **ref); +void dm_cell_release(struct cell *cell, struct bio_list *bios); +void dm_cell_release_singleton(struct cell *cell, struct bio *bio); +void dm_cell_release_no_holder(struct cell *cell, struct bio_list *inmates); +void dm_cell_error(struct cell *cell); + +/*----------------------------------------------------------------*/ + +/* + * We use the deferred set to keep track of pending reads to shared blocks. + * We do this to ensure the new mapping caused by a write isn't performed + * until these prior reads have completed. Otherwise the insertion of the + * new mapping could free the old block that the read bios are mapped to. + */ +#define DEFERRED_SET_SIZE 64 + +struct deferred_set; +struct deferred_entry { + struct deferred_set *ds; + unsigned count; + struct list_head work_items; +}; + +struct deferred_set { + spinlock_t lock; + unsigned current_entry; + unsigned sweeper; + struct deferred_entry entries[DEFERRED_SET_SIZE]; +}; + +void dm_ds_init(struct deferred_set *ds); +struct deferred_entry *dm_ds_inc(struct deferred_set *ds); +void dm_ds_dec(struct deferred_entry *entry, struct list_head *head); + +/* + * Returns 1 if deferred or 0 if no pending items to delay job. + */ +int dm_ds_add_work(struct deferred_set *ds, struct list_head *work); + +/*----------------------------------------------------------------*/ + +#endif Index: linux-2.6/drivers/md/dm-thin-metadata.h =================================================================== --- linux-2.6.orig/drivers/md/dm-thin-metadata.h +++ linux-2.6/drivers/md/dm-thin-metadata.h @@ -7,6 +7,7 @@ #ifndef DM_THIN_METADATA_H #define DM_THIN_METADATA_H +#include "dm-bio-prison.h" #include "persistent-data/dm-block-manager.h" #define THIN_METADATA_BLOCK_SIZE 4096 @@ -32,11 +33,6 @@ struct dm_pool_metadata; struct dm_thin_device; /* - * Device identifier - */ -typedef uint64_t dm_dev_id; - -/* * Reopens or creates a new, empty metadata volume. */ struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev, Index: linux-2.6/drivers/md/dm-thin.c =================================================================== --- linux-2.6.orig/drivers/md/dm-thin.c +++ linux-2.6/drivers/md/dm-thin.c @@ -4,6 +4,7 @@ * This file is released under the GPL. */ +#include "dm-bio-prison.h" #include "dm-thin-metadata.h" #include <linux/device-mapper.h> @@ -20,7 +21,6 @@ * Tunable constants */ #define ENDIO_HOOK_POOL_SIZE 10240 -#define DEFERRED_SET_SIZE 64 #define MAPPING_POOL_SIZE 1024 #define PRISON_CELLS 1024 #define COMMIT_PERIOD HZ @@ -98,372 +98,6 @@ /*----------------------------------------------------------------*/ /* - * Sometimes we can't deal with a bio straight away. We put them in prison - * where they can't cause any mischief. Bios are put in a cell identified - * by a key, multiple bios can be in the same cell. When the cell is - * subsequently unlocked the bios become available. - */ -struct bio_prison; - -struct cell_key { - int virtual; - dm_dev_id dev; - dm_block_t block; -}; - -struct cell { - struct hlist_node list; - struct bio_prison *prison; - struct cell_key key; - struct bio *holder; - struct bio_list bios; -}; - -struct bio_prison { - spinlock_t lock; - mempool_t *cell_pool; - - unsigned nr_buckets; - unsigned hash_mask; - struct hlist_head *cells; -}; - -static uint32_t calc_nr_buckets(unsigned nr_cells) -{ - uint32_t n = 128; - - nr_cells /= 4; - nr_cells = min(nr_cells, 8192u); - - while (n < nr_cells) - n <<= 1; - - return n; -} - -/* - * @nr_cells should be the number of cells you want in use _concurrently_. - * Don't confuse it with the number of distinct keys. - */ -static struct bio_prison *prison_create(unsigned nr_cells) -{ - unsigned i; - uint32_t nr_buckets = calc_nr_buckets(nr_cells); - size_t len = sizeof(struct bio_prison) + - (sizeof(struct hlist_head) * nr_buckets); - struct bio_prison *prison = kmalloc(len, GFP_KERNEL); - - if (!prison) - return NULL; - - spin_lock_init(&prison->lock); - prison->cell_pool = mempool_create_kmalloc_pool(nr_cells, - sizeof(struct cell)); - if (!prison->cell_pool) { - kfree(prison); - return NULL; - } - - prison->nr_buckets = nr_buckets; - prison->hash_mask = nr_buckets - 1; - prison->cells = (struct hlist_head *) (prison + 1); - for (i = 0; i < nr_buckets; i++) - INIT_HLIST_HEAD(prison->cells + i); - - return prison; -} - -static void prison_destroy(struct bio_prison *prison) -{ - mempool_destroy(prison->cell_pool); - kfree(prison); -} - -static uint32_t hash_key(struct bio_prison *prison, struct cell_key *key) -{ - const unsigned long BIG_PRIME = 4294967291UL; - uint64_t hash = key->block * BIG_PRIME; - - return (uint32_t) (hash & prison->hash_mask); -} - -static int keys_equal(struct cell_key *lhs, struct cell_key *rhs) -{ - return (lhs->virtual == rhs->virtual) && - (lhs->dev == rhs->dev) && - (lhs->block == rhs->block); -} - -static struct cell *__search_bucket(struct hlist_head *bucket, - struct cell_key *key) -{ - struct cell *cell; - struct hlist_node *tmp; - - hlist_for_each_entry(cell, tmp, bucket, list) - if (keys_equal(&cell->key, key)) - return cell; - - return NULL; -} - -/* - * This may block if a new cell needs allocating. You must ensure that - * cells will be unlocked even if the calling thread is blocked. - * - * Returns 1 if the cell was already held, 0 if @inmate is the new holder. - */ -static int bio_detain(struct bio_prison *prison, struct cell_key *key, - struct bio *inmate, struct cell **ref) -{ - int r; - unsigned long flags; - uint32_t hash = hash_key(prison, key); - struct cell *uninitialized_var(cell), *cell2 = NULL; - - BUG_ON(hash > prison->nr_buckets); - - spin_lock_irqsave(&prison->lock, flags); - cell = __search_bucket(prison->cells + hash, key); - - if (!cell) { - /* - * Allocate a new cell - */ - spin_unlock_irqrestore(&prison->lock, flags); - cell2 = mempool_alloc(prison->cell_pool, GFP_NOIO); - spin_lock_irqsave(&prison->lock, flags); - - /* - * We've been unlocked, so we have to double check that - * nobody else has inserted this cell in the meantime. - */ - cell = __search_bucket(prison->cells + hash, key); - - if (!cell) { - cell = cell2; - cell2 = NULL; - - cell->prison = prison; - memcpy(&cell->key, key, sizeof(cell->key)); - cell->holder = inmate; - bio_list_init(&cell->bios); - hlist_add_head(&cell->list, prison->cells + hash); - r = 0; - - } else { - mempool_free(cell2, prison->cell_pool); - cell2 = NULL; - r = 1; - bio_list_add(&cell->bios, inmate); - } - - } else { - r = 1; - bio_list_add(&cell->bios, inmate); - } - spin_unlock_irqrestore(&prison->lock, flags); - - *ref = cell; - return r; -} - -/* - * @inmates must have been initialised prior to this call - */ -static void __cell_release(struct cell *cell, struct bio_list *inmates) -{ - struct bio_prison *prison = cell->prison; - - hlist_del(&cell->list); - - if (inmates) { - bio_list_add(inmates, cell->holder); - bio_list_merge(inmates, &cell->bios); - } - - mempool_free(cell, prison->cell_pool); -} - -static void cell_release(struct cell *cell, struct bio_list *bios) -{ - unsigned long flags; - struct bio_prison *prison = cell->prison; - - spin_lock_irqsave(&prison->lock, flags); - __cell_release(cell, bios); - spin_unlock_irqrestore(&prison->lock, flags); -} - -/* - * There are a couple of places where we put a bio into a cell briefly - * before taking it out again. In these situations we know that no other - * bio may be in the cell. This function releases the cell, and also does - * a sanity check. - */ -static void __cell_release_singleton(struct cell *cell, struct bio *bio) -{ - hlist_del(&cell->list); - BUG_ON(cell->holder != bio); - BUG_ON(!bio_list_empty(&cell->bios)); -} - -static void cell_release_singleton(struct cell *cell, struct bio *bio) -{ - unsigned long flags; - struct bio_prison *prison = cell->prison; - - spin_lock_irqsave(&prison->lock, flags); - __cell_release_singleton(cell, bio); - spin_unlock_irqrestore(&prison->lock, flags); -} - -/* - * Sometimes we don't want the holder, just the additional bios. - */ -static void __cell_release_no_holder(struct cell *cell, struct bio_list *inmates) -{ - struct bio_prison *prison = cell->prison; - - hlist_del(&cell->list); - if (inmates) - bio_list_merge(inmates, &cell->bios); - - mempool_free(cell, prison->cell_pool); -} - -static void cell_release_no_holder(struct cell *cell, struct bio_list *inmates) -{ - unsigned long flags; - struct bio_prison *prison = cell->prison; - - spin_lock_irqsave(&prison->lock, flags); - __cell_release_no_holder(cell, inmates); - spin_unlock_irqrestore(&prison->lock, flags); -} - -static void cell_error(struct cell *cell) -{ - struct bio_prison *prison = cell->prison; - struct bio_list bios; - struct bio *bio; - unsigned long flags; - - bio_list_init(&bios); - - spin_lock_irqsave(&prison->lock, flags); - __cell_release(cell, &bios); - spin_unlock_irqrestore(&prison->lock, flags); - - while ((bio = bio_list_pop(&bios))) - bio_io_error(bio); -} - -/*----------------------------------------------------------------*/ - -/* - * We use the deferred set to keep track of pending reads to shared blocks. - * We do this to ensure the new mapping caused by a write isn't performed - * until these prior reads have completed. Otherwise the insertion of the - * new mapping could free the old block that the read bios are mapped to. - */ - -struct deferred_set; -struct deferred_entry { - struct deferred_set *ds; - unsigned count; - struct list_head work_items; -}; - -struct deferred_set { - spinlock_t lock; - unsigned current_entry; - unsigned sweeper; - struct deferred_entry entries[DEFERRED_SET_SIZE]; -}; - -static void ds_init(struct deferred_set *ds) -{ - int i; - - spin_lock_init(&ds->lock); - ds->current_entry = 0; - ds->sweeper = 0; - for (i = 0; i < DEFERRED_SET_SIZE; i++) { - ds->entries[i].ds = ds; - ds->entries[i].count = 0; - INIT_LIST_HEAD(&ds->entries[i].work_items); - } -} - -static struct deferred_entry *ds_inc(struct deferred_set *ds) -{ - unsigned long flags; - struct deferred_entry *entry; - - spin_lock_irqsave(&ds->lock, flags); - entry = ds->entries + ds->current_entry; - entry->count++; - spin_unlock_irqrestore(&ds->lock, flags); - - return entry; -} - -static unsigned ds_next(unsigned index) -{ - return (index + 1) % DEFERRED_SET_SIZE; -} - -static void __sweep(struct deferred_set *ds, struct list_head *head) -{ - while ((ds->sweeper != ds->current_entry) && - !ds->entries[ds->sweeper].count) { - list_splice_init(&ds->entries[ds->sweeper].work_items, head); - ds->sweeper = ds_next(ds->sweeper); - } - - if ((ds->sweeper == ds->current_entry) && !ds->entries[ds->sweeper].count) - list_splice_init(&ds->entries[ds->sweeper].work_items, head); -} - -static void ds_dec(struct deferred_entry *entry, struct list_head *head) -{ - unsigned long flags; - - spin_lock_irqsave(&entry->ds->lock, flags); - BUG_ON(!entry->count); - --entry->count; - __sweep(entry->ds, head); - spin_unlock_irqrestore(&entry->ds->lock, flags); -} - -/* - * Returns 1 if deferred or 0 if no pending items to delay job. - */ -static int ds_add_work(struct deferred_set *ds, struct list_head *work) -{ - int r = 1; - unsigned long flags; - unsigned next_entry; - - spin_lock_irqsave(&ds->lock, flags); - if ((ds->sweeper == ds->current_entry) && - !ds->entries[ds->current_entry].count) - r = 0; - else { - list_add(work, &ds->entries[ds->current_entry].work_items); - next_entry = ds_next(ds->current_entry); - if (!ds->entries[next_entry].count) - ds->current_entry = next_entry; - } - spin_unlock_irqrestore(&ds->lock, flags); - - return r; -} - -/*----------------------------------------------------------------*/ - -/* * Key building. */ static void build_data_key(struct dm_thin_device *td, @@ -816,7 +450,7 @@ static void cell_defer(struct thin_c *tc unsigned long flags; spin_lock_irqsave(&pool->lock, flags); - cell_release(cell, &pool->deferred_bios); + dm_cell_release(cell, &pool->deferred_bios); spin_unlock_irqrestore(&tc->pool->lock, flags); wake_worker(pool); @@ -835,7 +469,7 @@ static void cell_defer_except(struct thi bio_list_init(&bios); spin_lock_irqsave(&pool->lock, flags); - cell_release_no_holder(cell, &pool->deferred_bios); + dm_cell_release_no_holder(cell, &pool->deferred_bios); spin_unlock_irqrestore(&pool->lock, flags); wake_worker(pool); @@ -852,7 +486,7 @@ static void process_prepared_mapping(str bio->bi_end_io = m->saved_bi_end_io; if (m->err) { - cell_error(m->cell); + dm_cell_error(m->cell); return; } @@ -864,7 +498,7 @@ static void process_prepared_mapping(str r = dm_thin_insert_block(tc->td, m->virt_block, m->data_block); if (r) { DMERR("dm_thin_insert_block() failed"); - cell_error(m->cell); + dm_cell_error(m->cell); return; } @@ -983,7 +617,7 @@ static void schedule_copy(struct thin_c m->err = 0; m->bio = NULL; - if (!ds_add_work(&pool->shared_read_ds, &m->list)) + if (!dm_ds_add_work(&pool->shared_read_ds, &m->list)) m->quiesced = 1; /* @@ -1014,7 +648,7 @@ static void schedule_copy(struct thin_c if (r < 0) { mempool_free(m, pool->mapping_pool); DMERR("dm_kcopyd_copy() failed"); - cell_error(cell); + dm_cell_error(cell); } } } @@ -1079,7 +713,7 @@ static void schedule_zero(struct thin_c if (r < 0) { mempool_free(m, pool->mapping_pool); DMERR("dm_kcopyd_zero() failed"); - cell_error(cell); + dm_cell_error(cell); } } } @@ -1167,7 +801,7 @@ static void no_space(struct cell *cell) struct bio_list bios; bio_list_init(&bios); - cell_release(cell, &bios); + dm_cell_release(cell, &bios); while ((bio = bio_list_pop(&bios))) retry_on_resume(bio); @@ -1184,7 +818,7 @@ static void process_discard(struct thin_ struct new_mapping *m; build_virtual_key(tc->td, block, &key); - if (bio_detain(tc->pool->prison, &key, bio, &cell)) + if (dm_bio_detain(tc->pool->prison, &key, bio, &cell)) return; r = dm_thin_find_block(tc->td, block, 1, &lookup_result); @@ -1196,8 +830,8 @@ static void process_discard(struct thin_ * on this block. */ build_data_key(tc->td, lookup_result.block, &key2); - if (bio_detain(tc->pool->prison, &key2, bio, &cell2)) { - cell_release_singleton(cell, bio); + if (dm_bio_detain(tc->pool->prison, &key2, bio, &cell2)) { + dm_cell_release_singleton(cell, bio); break; } @@ -1216,7 +850,7 @@ static void process_discard(struct thin_ m->err = 0; m->bio = bio; - if (!ds_add_work(&pool->all_io_ds, &m->list)) { + if (!dm_ds_add_work(&pool->all_io_ds, &m->list)) { list_add(&m->list, &pool->prepared_discards); wake_worker(pool); } @@ -1231,8 +865,8 @@ static void process_discard(struct thin_ unsigned remaining = (pool->sectors_per_block - offset) << 9; bio->bi_size = min(bio->bi_size, remaining); - cell_release_singleton(cell, bio); - cell_release_singleton(cell2, bio); + dm_cell_release_singleton(cell, bio); + dm_cell_release_singleton(cell2, bio); remap_and_issue(tc, bio, lookup_result.block); } break; @@ -1241,13 +875,13 @@ static void process_discard(struct thin_ /* * It isn't provisioned, just forget it. */ - cell_release_singleton(cell, bio); + dm_cell_release_singleton(cell, bio); bio_endio(bio, 0); break; default: DMERR("discard: find block unexpectedly returned %d\n", r); - cell_release_singleton(cell, bio); + dm_cell_release_singleton(cell, bio); bio_io_error(bio); break; } @@ -1274,7 +908,7 @@ static void break_sharing(struct thin_c default: DMERR("%s: alloc_data_block() failed, error = %d", __func__, r); - cell_error(cell); + dm_cell_error(cell); break; } } @@ -1292,7 +926,7 @@ static void process_shared_bio(struct th * of being broken so we have nothing further to do here. */ build_data_key(tc->td, lookup_result->block, &key); - if (bio_detain(pool->prison, &key, bio, &cell)) + if (dm_bio_detain(pool->prison, &key, bio, &cell)) return; if (bio_data_dir(bio) == WRITE) @@ -1300,9 +934,9 @@ static void process_shared_bio(struct th else { struct endio_hook *h = dm_get_mapinfo(bio)->ptr; - h->shared_read_entry = ds_inc(&pool->shared_read_ds); + h->shared_read_entry = dm_ds_inc(&pool->shared_read_ds); - cell_release_singleton(cell, bio); + dm_cell_release_singleton(cell, bio); remap_and_issue(tc, bio, lookup_result->block); } } @@ -1317,7 +951,7 @@ static void provision_block(struct thin_ * Remap empty bios (flushes) immediately, without provisioning. */ if (!bio->bi_size) { - cell_release_singleton(cell, bio); + dm_cell_release_singleton(cell, bio); remap_and_issue(tc, bio, 0); return; } @@ -1327,7 +961,7 @@ static void provision_block(struct thin_ */ if (bio_data_dir(bio) == READ) { zero_fill_bio(bio); - cell_release_singleton(cell, bio); + dm_cell_release_singleton(cell, bio); bio_endio(bio, 0); return; } @@ -1347,7 +981,7 @@ static void provision_block(struct thin_ default: DMERR("%s: alloc_data_block() failed, error = %d", __func__, r); - cell_error(cell); + dm_cell_error(cell); break; } } @@ -1365,7 +999,7 @@ static void process_bio(struct thin_c *t * being provisioned so we have nothing further to do here. */ build_virtual_key(tc->td, block, &key); - if (bio_detain(tc->pool->prison, &key, bio, &cell)) + if (dm_bio_detain(tc->pool->prison, &key, bio, &cell)) return; r = dm_thin_find_block(tc->td, block, 1, &lookup_result); @@ -1380,7 +1014,7 @@ static void process_bio(struct thin_c *t * TODO: this will probably have to change when discard goes * back in. */ - cell_release_singleton(cell, bio); + dm_cell_release_singleton(cell, bio); if (lookup_result.shared) process_shared_bio(tc, bio, block, &lookup_result); @@ -1390,7 +1024,7 @@ static void process_bio(struct thin_c *t case -ENODATA: if (bio_data_dir(bio) == READ && tc->origin_dev) { - cell_release_singleton(cell, bio); + dm_cell_release_singleton(cell, bio); remap_to_origin_and_issue(tc, bio); } else provision_block(tc, bio, block, cell); @@ -1398,7 +1032,7 @@ static void process_bio(struct thin_c *t default: DMERR("dm_thin_find_block() failed, error = %d", r); - cell_release_singleton(cell, bio); + dm_cell_release_singleton(cell, bio); bio_io_error(bio); break; } @@ -1522,7 +1156,7 @@ static struct endio_hook *thin_hook_bio( h->tc = tc; h->shared_read_entry = NULL; - h->all_io_entry = bio->bi_rw & REQ_DISCARD ? NULL : ds_inc(&pool->all_io_ds); + h->all_io_entry = bio->bi_rw & REQ_DISCARD ? NULL : dm_ds_inc(&pool->all_io_ds); h->overwrite_mapping = NULL; return h; @@ -1652,7 +1286,7 @@ static void __pool_destroy(struct pool * if (dm_pool_metadata_close(pool->pmd) < 0) DMWARN("%s: dm_pool_metadata_close() failed.", __func__); - prison_destroy(pool->prison); + dm_prison_destroy(pool->prison); dm_kcopyd_client_destroy(pool->copier); if (pool->wq) @@ -1693,7 +1327,7 @@ static struct pool *pool_create(struct m pool->offset_mask = block_size - 1; pool->low_water_blocks = 0; pool_features_init(&pool->pf); - pool->prison = prison_create(PRISON_CELLS); + pool->prison = dm_prison_create(PRISON_CELLS); if (!pool->prison) { *error = "Error creating pool's bio prison"; err_p = ERR_PTR(-ENOMEM); @@ -1729,8 +1363,8 @@ static struct pool *pool_create(struct m pool->low_water_triggered = 0; pool->no_free_space = 0; bio_list_init(&pool->retry_on_resume_list); - ds_init(&pool->shared_read_ds); - ds_init(&pool->all_io_ds); + dm_ds_init(&pool->shared_read_ds); + dm_ds_init(&pool->all_io_ds); pool->next_mapping = NULL; pool->mapping_pool = @@ -1763,7 +1397,7 @@ bad_mapping_pool: bad_wq: dm_kcopyd_client_destroy(pool->copier); bad_kcopyd_client: - prison_destroy(pool->prison); + dm_prison_destroy(pool->prison); bad_prison: kfree(pool); bad_pool: @@ -2614,7 +2248,7 @@ static int thin_endio(struct dm_target * if (h->shared_read_entry) { INIT_LIST_HEAD(&work); - ds_dec(h->shared_read_entry, &work); + dm_ds_dec(h->shared_read_entry, &work); spin_lock_irqsave(&pool->lock, flags); list_for_each_entry_safe(m, tmp, &work, list) { @@ -2627,7 +2261,7 @@ static int thin_endio(struct dm_target * if (h->all_io_entry) { INIT_LIST_HEAD(&work); - ds_dec(h->all_io_entry, &work); + dm_ds_dec(h->all_io_entry, &work); list_for_each_entry_safe(m, tmp, &work, list) list_add(&m->list, &pool->prepared_discards); } ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] dm thin: rename dm_thin_id to more generic dm_dev_id 2012-03-02 23:59 [PATCH 1/2] dm thin: rename dm_thin_id to more generic dm_dev_id Mike Snitzer 2012-03-02 23:59 ` [PATCH 2/2] dm thin: elevate the bio_prison and deferred_set code to allow for re-use Mike Snitzer @ 2012-03-05 10:29 ` Joe Thornber 2012-03-05 14:08 ` Mike Snitzer 1 sibling, 1 reply; 6+ messages in thread From: Joe Thornber @ 2012-03-05 10:29 UTC (permalink / raw) To: Mike Snitzer; +Cc: dm-devel, ejt This is part of the hsm target development work. Let's keep this in git for now; there's no point spamming dm-devel with work in progress. I'm happy to give you write access to my github repo if you like. Otherwise I can merge from any git repo you wish. On Fri, Mar 02, 2012 at 06:59:29PM -0500, Mike Snitzer wrote: > dm_thin_id is too specific to thin devices. Rename to allow for reuse > by other targets. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] dm thin: rename dm_thin_id to more generic dm_dev_id 2012-03-05 10:29 ` [PATCH 1/2] dm thin: rename dm_thin_id to more generic dm_dev_id Joe Thornber @ 2012-03-05 14:08 ` Mike Snitzer 0 siblings, 0 replies; 6+ messages in thread From: Mike Snitzer @ 2012-03-05 14:08 UTC (permalink / raw) To: dm-devel, ejt On Mon, Mar 05 2012 at 5:29am -0500, Joe Thornber <thornber@redhat.com> wrote: > This is part of the hsm target development work. Let's keep this in > git for now; there's no point spamming dm-devel with work in progress. > I'm happy to give you write access to my github repo if you like. > Otherwise I can merge from any git repo you wish. Sure, dm-devel is just a public list (doubt people who care about DM see these patches as spam). I didn't see the need for a back channel exchange, but noted. ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2012-03-05 14:08 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-03-02 23:59 [PATCH 1/2] dm thin: rename dm_thin_id to more generic dm_dev_id Mike Snitzer 2012-03-02 23:59 ` [PATCH 2/2] dm thin: elevate the bio_prison and deferred_set code to allow for re-use Mike Snitzer 2012-03-03 0:26 ` Alasdair G Kergon 2012-03-03 3:13 ` [PATCH 2/2 v2] " Mike Snitzer 2012-03-05 10:29 ` [PATCH 1/2] dm thin: rename dm_thin_id to more generic dm_dev_id Joe Thornber 2012-03-05 14:08 ` Mike Snitzer
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.