dm: introduce per request data Introduce a field per_request_data in struct dm_target. Targets can set this field in the constructor. If a target sets this field to non-zero value, "per_request_data" bytes of auxliliary data is allocated for each bio submitted to the target. These data can be used for any purpose by the target. Per-request data is accessed with dm_bio_get_per_request_data. The argument data_size must be the same as the value per_request_data in dm_target. If the target has a pointer to per request data, it can get a pointer to the bio with dm_per_request_data_get_bio function (data_size must be the same ast the value passed to dm_bio_get_per_request_data). Signed-off-by: Mikulas Patocka --- drivers/md/dm-table.c | 12 +++++++++++- drivers/md/dm.c | 33 +++++++++++++++------------------ drivers/md/dm.h | 2 +- include/linux/device-mapper.h | 30 ++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 20 deletions(-) Index: linux-3.7-rc6/drivers/md/dm.c =================================================================== --- linux-3.7-rc6.orig/drivers/md/dm.c 2012-11-17 21:28:57.000000000 +0100 +++ linux-3.7-rc6/drivers/md/dm.c 2012-11-17 21:31:33.000000000 +0100 @@ -63,18 +63,6 @@ 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. - */ -struct dm_target_io { - struct dm_io *io; - struct dm_target *ti; - union map_info info; - struct bio clone; -}; - -/* * For request-based dm. * One of these is allocated per request. */ @@ -1974,13 +1962,20 @@ static void free_dev(struct mapped_devic static void __bind_mempools(struct mapped_device *md, struct dm_table *t) { - struct dm_md_mempools *p; + struct dm_md_mempools *p = dm_table_get_md_mempools(t); - if (md->io_pool && (md->tio_pool || dm_table_get_type(t) == DM_TYPE_BIO_BASED) && md->bs) - /* the md already has necessary mempools */ + if (md->io_pool && (md->tio_pool || dm_table_get_type(t) == DM_TYPE_BIO_BASED) && md->bs) { + /* + * The md already has necessary mempools. Reload just the + * bioset because front_pad may have changed because + * a different table was loaded. + */ + bioset_free(md->bs); + md->bs = p->bs; + p->bs = NULL; goto out; + } - p = dm_table_get_md_mempools(t); BUG_ON(!p || md->io_pool || md->tio_pool || md->bs); md->io_pool = p->io_pool; @@ -2730,7 +2725,7 @@ int dm_noflush_suspending(struct dm_targ } EXPORT_SYMBOL_GPL(dm_noflush_suspending); -struct dm_md_mempools *dm_alloc_md_mempools(unsigned type, unsigned integrity) +struct dm_md_mempools *dm_alloc_md_mempools(unsigned type, unsigned integrity, unsigned per_request_data) { struct dm_md_mempools *pools = kmalloc(sizeof(*pools), GFP_KERNEL); unsigned int pool_size = (type == DM_TYPE_BIO_BASED) ? 16 : MIN_IOS; @@ -2738,6 +2733,8 @@ struct dm_md_mempools *dm_alloc_md_mempo if (!pools) return NULL; + per_request_data = roundup(per_request_data, __alignof__(struct dm_target_io)); + pools->io_pool = (type == DM_TYPE_BIO_BASED) ? mempool_create_slab_pool(MIN_IOS, _io_cache) : mempool_create_slab_pool(MIN_IOS, _rq_bio_info_cache); @@ -2753,7 +2750,7 @@ struct dm_md_mempools *dm_alloc_md_mempo pools->bs = (type == DM_TYPE_BIO_BASED) ? bioset_create(pool_size, - offsetof(struct dm_target_io, clone)) : + per_request_data + offsetof(struct dm_target_io, clone)) : bioset_create(pool_size, offsetof(struct dm_rq_clone_bio_info, clone)); if (!pools->bs) Index: linux-3.7-rc6/drivers/md/dm-table.c =================================================================== --- linux-3.7-rc6.orig/drivers/md/dm-table.c 2012-11-17 21:28:52.000000000 +0100 +++ linux-3.7-rc6/drivers/md/dm-table.c 2012-11-17 21:30:34.000000000 +0100 @@ -932,13 +932,23 @@ bool dm_table_request_based(struct dm_ta int dm_table_alloc_md_mempools(struct dm_table *t) { unsigned type = dm_table_get_type(t); + unsigned per_request_data = 0; if (unlikely(type == DM_TYPE_NONE)) { DMWARN("no table type is set, can't allocate mempools"); return -EINVAL; } - t->mempools = dm_alloc_md_mempools(type, t->integrity_supported); + if (type == DM_TYPE_BIO_BASED) { + unsigned i; + for (i = 0; i < t->num_targets; i++) { + struct dm_target *tgt = t->targets + i; + if (tgt->per_request_data > per_request_data) + per_request_data = tgt->per_request_data; + } + } + + t->mempools = dm_alloc_md_mempools(type, t->integrity_supported, per_request_data); if (!t->mempools) return -ENOMEM; Index: linux-3.7-rc6/drivers/md/dm.h =================================================================== --- linux-3.7-rc6.orig/drivers/md/dm.h 2012-11-17 21:11:11.000000000 +0100 +++ linux-3.7-rc6/drivers/md/dm.h 2012-11-17 21:30:34.000000000 +0100 @@ -159,7 +159,7 @@ void dm_kcopyd_exit(void); /* * Mempool operations */ -struct dm_md_mempools *dm_alloc_md_mempools(unsigned type, unsigned integrity); +struct dm_md_mempools *dm_alloc_md_mempools(unsigned type, unsigned integrity, unsigned per_request_data); void dm_free_md_mempools(struct dm_md_mempools *pools); #endif Index: linux-3.7-rc6/include/linux/device-mapper.h =================================================================== --- linux-3.7-rc6.orig/include/linux/device-mapper.h 2012-11-17 21:28:52.000000000 +0100 +++ linux-3.7-rc6/include/linux/device-mapper.h 2012-11-17 21:30:34.000000000 +0100 @@ -205,6 +205,12 @@ struct dm_target { */ unsigned num_discard_requests; + /* + * The size of data allocated for every bio sent to the target. + * The data is accessible with function dm_bio_get_per_request_data. + */ + unsigned per_request_data; + /* target specific data */ void *private; @@ -241,6 +247,30 @@ struct dm_target_callbacks { int (*congested_fn) (struct dm_target_callbacks *, int); }; +/* + * For bio-based dm. + * One of these is allocated per target within a bio. + * This structure shouldn't be touched by target drivers, it is here + * so that we can inline dm_bio_get_per_request_data and + * dm_per_request_data_get_bio. + */ +struct dm_target_io { + struct dm_io *io; + struct dm_target *ti; + union map_info info; + struct bio clone; +}; + +static inline void *dm_bio_get_per_request_data(struct bio *bio, size_t data_size) +{ + return (char *)bio - offsetof(struct dm_target_io, clone) - data_size; +} + +static inline struct bio *dm_per_request_data_get_bio(void *data, size_t data_size) +{ + return (struct bio *)((char *)data + data_size + offsetof(struct dm_target_io, clone)); +} + int dm_register_target(struct target_type *t); void dm_unregister_target(struct target_type *t);