Skip to content

Commit

Permalink
Merge tag 'dm-3.16-changes' of git://git.kernel.org/pub/scm/linux/ker…
Browse files Browse the repository at this point in the history
…nel/git/device-mapper/linux-dm

Pull device mapper updates from Mike Snitzer:
 "This pull request is later than I'd have liked because I was waiting
  for some performance data to help finally justify sending the
  long-standing dm-crypt cpu scalability improvements upstream.

  Unfortunately we came up short, so those dm-crypt changes will
  continue to wait, but it seems we're not far off.

   . Add dm_accept_partial_bio interface to DM core to allow DM targets
     to only process a portion of a bio, the remainder being sent in the
     next bio.  This enables the old dm snapshot-origin target to only
     split write bios on chunk boundaries, read bios are now sent to the
     origin device unchanged.

   . Add DM core support for disabling WRITE SAME if the underlying SCSI
     layer disables it due to command failure.

   . Reduce lock contention in DM's bio-prison.

   . A few small cleanups and fixes to dm-thin and dm-era"

* tag 'dm-3.16-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm:
  dm thin: update discard_granularity to reflect the thin-pool blocksize
  dm bio prison: implement per bucket locking in the dm_bio_prison hash table
  dm: remove symbol export for dm_set_device_limits
  dm: disable WRITE SAME if it fails
  dm era: check for a non-NULL metadata object before closing it
  dm thin: return ENOSPC instead of EIO when error_if_no_space enabled
  dm thin: cleanup noflush_work to use a proper completion
  dm snapshot: do not split read bios sent to snapshot-origin target
  dm snapshot: allocate a per-target structure for snapshot-origin target
  dm: introduce dm_accept_partial_bio
  dm: change sector_count member in clone_info from sector_t to unsigned
  • Loading branch information
Linus Torvalds committed Jun 12, 2014
2 parents 7550cfa + 09869de commit 0e04c64
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 117 deletions.
70 changes: 41 additions & 29 deletions drivers/md/dm-bio-prison.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@

/*----------------------------------------------------------------*/

struct dm_bio_prison {
struct bucket {
spinlock_t lock;
struct hlist_head cells;
};

struct dm_bio_prison {
mempool_t *cell_pool;

unsigned nr_buckets;
unsigned hash_mask;
struct hlist_head *cells;
struct bucket *buckets;
};

/*----------------------------------------------------------------*/
Expand All @@ -40,6 +44,12 @@ static uint32_t calc_nr_buckets(unsigned nr_cells)

static struct kmem_cache *_cell_cache;

static void init_bucket(struct bucket *b)
{
spin_lock_init(&b->lock);
INIT_HLIST_HEAD(&b->cells);
}

/*
* @nr_cells should be the number of cells you want in use _concurrently_.
* Don't confuse it with the number of distinct keys.
Expand All @@ -49,13 +59,12 @@ struct dm_bio_prison *dm_bio_prison_create(unsigned nr_cells)
unsigned i;
uint32_t nr_buckets = calc_nr_buckets(nr_cells);
size_t len = sizeof(struct dm_bio_prison) +
(sizeof(struct hlist_head) * nr_buckets);
(sizeof(struct bucket) * nr_buckets);
struct dm_bio_prison *prison = kmalloc(len, GFP_KERNEL);

if (!prison)
return NULL;

spin_lock_init(&prison->lock);
prison->cell_pool = mempool_create_slab_pool(nr_cells, _cell_cache);
if (!prison->cell_pool) {
kfree(prison);
Expand All @@ -64,9 +73,9 @@ struct dm_bio_prison *dm_bio_prison_create(unsigned nr_cells)

prison->nr_buckets = nr_buckets;
prison->hash_mask = nr_buckets - 1;
prison->cells = (struct hlist_head *) (prison + 1);
prison->buckets = (struct bucket *) (prison + 1);
for (i = 0; i < nr_buckets; i++)
INIT_HLIST_HEAD(prison->cells + i);
init_bucket(prison->buckets + i);

return prison;
}
Expand Down Expand Up @@ -107,48 +116,52 @@ static int keys_equal(struct dm_cell_key *lhs, struct dm_cell_key *rhs)
(lhs->block == rhs->block);
}

static struct dm_bio_prison_cell *__search_bucket(struct hlist_head *bucket,
static struct bucket *get_bucket(struct dm_bio_prison *prison,
struct dm_cell_key *key)
{
return prison->buckets + hash_key(prison, key);
}

static struct dm_bio_prison_cell *__search_bucket(struct bucket *b,
struct dm_cell_key *key)
{
struct dm_bio_prison_cell *cell;

hlist_for_each_entry(cell, bucket, list)
hlist_for_each_entry(cell, &b->cells, list)
if (keys_equal(&cell->key, key))
return cell;

return NULL;
}

static void __setup_new_cell(struct dm_bio_prison *prison,
static void __setup_new_cell(struct bucket *b,
struct dm_cell_key *key,
struct bio *holder,
uint32_t hash,
struct dm_bio_prison_cell *cell)
{
memcpy(&cell->key, key, sizeof(cell->key));
cell->holder = holder;
bio_list_init(&cell->bios);
hlist_add_head(&cell->list, prison->cells + hash);
hlist_add_head(&cell->list, &b->cells);
}

static int __bio_detain(struct dm_bio_prison *prison,
static int __bio_detain(struct bucket *b,
struct dm_cell_key *key,
struct bio *inmate,
struct dm_bio_prison_cell *cell_prealloc,
struct dm_bio_prison_cell **cell_result)
{
uint32_t hash = hash_key(prison, key);
struct dm_bio_prison_cell *cell;

cell = __search_bucket(prison->cells + hash, key);
cell = __search_bucket(b, key);
if (cell) {
if (inmate)
bio_list_add(&cell->bios, inmate);
*cell_result = cell;
return 1;
}

__setup_new_cell(prison, key, inmate, hash, cell_prealloc);
__setup_new_cell(b, key, inmate, cell_prealloc);
*cell_result = cell_prealloc;
return 0;
}
Expand All @@ -161,10 +174,11 @@ static int bio_detain(struct dm_bio_prison *prison,
{
int r;
unsigned long flags;
struct bucket *b = get_bucket(prison, key);

spin_lock_irqsave(&prison->lock, flags);
r = __bio_detain(prison, key, inmate, cell_prealloc, cell_result);
spin_unlock_irqrestore(&prison->lock, flags);
spin_lock_irqsave(&b->lock, flags);
r = __bio_detain(b, key, inmate, cell_prealloc, cell_result);
spin_unlock_irqrestore(&b->lock, flags);

return r;
}
Expand Down Expand Up @@ -208,10 +222,11 @@ void dm_cell_release(struct dm_bio_prison *prison,
struct bio_list *bios)
{
unsigned long flags;
struct bucket *b = get_bucket(prison, &cell->key);

spin_lock_irqsave(&prison->lock, flags);
spin_lock_irqsave(&b->lock, flags);
__cell_release(cell, bios);
spin_unlock_irqrestore(&prison->lock, flags);
spin_unlock_irqrestore(&b->lock, flags);
}
EXPORT_SYMBOL_GPL(dm_cell_release);

Expand All @@ -230,28 +245,25 @@ void dm_cell_release_no_holder(struct dm_bio_prison *prison,
struct bio_list *inmates)
{
unsigned long flags;
struct bucket *b = get_bucket(prison, &cell->key);

spin_lock_irqsave(&prison->lock, flags);
spin_lock_irqsave(&b->lock, flags);
__cell_release_no_holder(cell, inmates);
spin_unlock_irqrestore(&prison->lock, flags);
spin_unlock_irqrestore(&b->lock, flags);
}
EXPORT_SYMBOL_GPL(dm_cell_release_no_holder);

void dm_cell_error(struct dm_bio_prison *prison,
struct dm_bio_prison_cell *cell)
struct dm_bio_prison_cell *cell, int error)
{
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);
dm_cell_release(prison, cell, &bios);

while ((bio = bio_list_pop(&bios)))
bio_io_error(bio);
bio_endio(bio, error);
}
EXPORT_SYMBOL_GPL(dm_cell_error);

Expand Down
2 changes: 1 addition & 1 deletion drivers/md/dm-bio-prison.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ void dm_cell_release_no_holder(struct dm_bio_prison *prison,
struct dm_bio_prison_cell *cell,
struct bio_list *inmates);
void dm_cell_error(struct dm_bio_prison *prison,
struct dm_bio_prison_cell *cell);
struct dm_bio_prison_cell *cell, int error);

/*----------------------------------------------------------------*/

Expand Down
3 changes: 2 additions & 1 deletion drivers/md/dm-era-target.c
Original file line number Diff line number Diff line change
Expand Up @@ -1391,7 +1391,8 @@ static int era_is_congested(struct dm_target_callbacks *cb, int bdi_bits)

static void era_destroy(struct era *era)
{
metadata_close(era->md);
if (era->md)
metadata_close(era->md);

if (era->wq)
destroy_workqueue(era->wq);
Expand Down
11 changes: 1 addition & 10 deletions drivers/md/dm-mpath.c
Original file line number Diff line number Diff line change
Expand Up @@ -1242,17 +1242,8 @@ static int do_end_io(struct multipath *m, struct request *clone,
if (!error && !clone->errors)
return 0; /* I/O complete */

if (noretry_error(error)) {
if ((clone->cmd_flags & REQ_WRITE_SAME) &&
!clone->q->limits.max_write_same_sectors) {
struct queue_limits *limits;

/* device doesn't really support WRITE SAME, disable it */
limits = dm_get_queue_limits(dm_table_get_md(m->ti->table));
limits->max_write_same_sectors = 0;
}
if (noretry_error(error))
return error;
}

if (mpio->pgpath)
fail_path(mpio->pgpath);
Expand Down
67 changes: 48 additions & 19 deletions drivers/md/dm-snap.c
Original file line number Diff line number Diff line change
Expand Up @@ -2141,6 +2141,11 @@ static int origin_write_extent(struct dm_snapshot *merging_snap,
* Origin: maps a linear range of a device, with hooks for snapshotting.
*/

struct dm_origin {
struct dm_dev *dev;
unsigned split_boundary;
};

/*
* Construct an origin mapping: <dev_path>
* The context for an origin is merely a 'struct dm_dev *'
Expand All @@ -2149,41 +2154,65 @@ static int origin_write_extent(struct dm_snapshot *merging_snap,
static int origin_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
int r;
struct dm_dev *dev;
struct dm_origin *o;

if (argc != 1) {
ti->error = "origin: incorrect number of arguments";
return -EINVAL;
}

r = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &dev);
o = kmalloc(sizeof(struct dm_origin), GFP_KERNEL);
if (!o) {
ti->error = "Cannot allocate private origin structure";
r = -ENOMEM;
goto bad_alloc;
}

r = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &o->dev);
if (r) {
ti->error = "Cannot get target device";
return r;
goto bad_open;
}

ti->private = dev;
ti->private = o;
ti->num_flush_bios = 1;

return 0;

bad_open:
kfree(o);
bad_alloc:
return r;
}

static void origin_dtr(struct dm_target *ti)
{
struct dm_dev *dev = ti->private;
dm_put_device(ti, dev);
struct dm_origin *o = ti->private;
dm_put_device(ti, o->dev);
kfree(o);
}

static int origin_map(struct dm_target *ti, struct bio *bio)
{
struct dm_dev *dev = ti->private;
bio->bi_bdev = dev->bdev;
struct dm_origin *o = ti->private;
unsigned available_sectors;

if (bio->bi_rw & REQ_FLUSH)
bio->bi_bdev = o->dev->bdev;

if (unlikely(bio->bi_rw & REQ_FLUSH))
return DM_MAPIO_REMAPPED;

if (bio_rw(bio) != WRITE)
return DM_MAPIO_REMAPPED;

available_sectors = o->split_boundary -
((unsigned)bio->bi_iter.bi_sector & (o->split_boundary - 1));

if (bio_sectors(bio) > available_sectors)
dm_accept_partial_bio(bio, available_sectors);

/* Only tell snapshots if this is a write */
return (bio_rw(bio) == WRITE) ? do_origin(dev, bio) : DM_MAPIO_REMAPPED;
return do_origin(o->dev, bio);
}

/*
Expand All @@ -2192,47 +2221,47 @@ static int origin_map(struct dm_target *ti, struct bio *bio)
*/
static void origin_resume(struct dm_target *ti)
{
struct dm_dev *dev = ti->private;
struct dm_origin *o = ti->private;

ti->max_io_len = get_origin_minimum_chunksize(dev->bdev);
o->split_boundary = get_origin_minimum_chunksize(o->dev->bdev);
}

static void origin_status(struct dm_target *ti, status_type_t type,
unsigned status_flags, char *result, unsigned maxlen)
{
struct dm_dev *dev = ti->private;
struct dm_origin *o = ti->private;

switch (type) {
case STATUSTYPE_INFO:
result[0] = '\0';
break;

case STATUSTYPE_TABLE:
snprintf(result, maxlen, "%s", dev->name);
snprintf(result, maxlen, "%s", o->dev->name);
break;
}
}

static int origin_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
struct bio_vec *biovec, int max_size)
{
struct dm_dev *dev = ti->private;
struct request_queue *q = bdev_get_queue(dev->bdev);
struct dm_origin *o = ti->private;
struct request_queue *q = bdev_get_queue(o->dev->bdev);

if (!q->merge_bvec_fn)
return max_size;

bvm->bi_bdev = dev->bdev;
bvm->bi_bdev = o->dev->bdev;

return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
}

static int origin_iterate_devices(struct dm_target *ti,
iterate_devices_callout_fn fn, void *data)
{
struct dm_dev *dev = ti->private;
struct dm_origin *o = ti->private;

return fn(ti, dev, 0, ti->len, data);
return fn(ti, o->dev, 0, ti->len, data);
}

static struct target_type origin_target = {
Expand Down
5 changes: 2 additions & 3 deletions drivers/md/dm-table.c
Original file line number Diff line number Diff line change
Expand Up @@ -465,8 +465,8 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
}
EXPORT_SYMBOL(dm_get_device);

int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
sector_t start, sector_t len, void *data)
static int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
sector_t start, sector_t len, void *data)
{
struct queue_limits *limits = data;
struct block_device *bdev = dev->bdev;
Expand Down Expand Up @@ -499,7 +499,6 @@ int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
(unsigned int) (PAGE_SIZE >> 9));
return 0;
}
EXPORT_SYMBOL_GPL(dm_set_device_limits);

/*
* Decrement a device's use count and remove it if necessary.
Expand Down
Loading

0 comments on commit 0e04c64

Please sign in to comment.