Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0e04c641 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents 7550cfab 09869de5
Loading
Loading
Loading
Loading
+41 −29
Original line number Original line Diff line number Diff line
@@ -14,13 +14,17 @@


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


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

struct dm_bio_prison {
	mempool_t *cell_pool;
	mempool_t *cell_pool;


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


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


static struct kmem_cache *_cell_cache;
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_.
 * @nr_cells should be the number of cells you want in use _concurrently_.
 * Don't confuse it with the number of distinct keys.
 * Don't confuse it with the number of distinct keys.
@@ -49,13 +59,12 @@ struct dm_bio_prison *dm_bio_prison_create(unsigned nr_cells)
	unsigned i;
	unsigned i;
	uint32_t nr_buckets = calc_nr_buckets(nr_cells);
	uint32_t nr_buckets = calc_nr_buckets(nr_cells);
	size_t len = sizeof(struct dm_bio_prison) +
	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);
	struct dm_bio_prison *prison = kmalloc(len, GFP_KERNEL);


	if (!prison)
	if (!prison)
		return NULL;
		return NULL;


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


	prison->nr_buckets = nr_buckets;
	prison->nr_buckets = nr_buckets;
	prison->hash_mask = nr_buckets - 1;
	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++)
	for (i = 0; i < nr_buckets; i++)
		INIT_HLIST_HEAD(prison->cells + i);
		init_bucket(prison->buckets + i);


	return prison;
	return prison;
}
}
@@ -107,40 +116,44 @@ static int keys_equal(struct dm_cell_key *lhs, struct dm_cell_key *rhs)
		       (lhs->block == rhs->block);
		       (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_cell_key *key)
{
{
	struct dm_bio_prison_cell *cell;
	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))
		if (keys_equal(&cell->key, key))
			return cell;
			return cell;


	return NULL;
	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 dm_cell_key *key,
			     struct bio *holder,
			     struct bio *holder,
			     uint32_t hash,
			     struct dm_bio_prison_cell *cell)
			     struct dm_bio_prison_cell *cell)
{
{
	memcpy(&cell->key, key, sizeof(cell->key));
	memcpy(&cell->key, key, sizeof(cell->key));
	cell->holder = holder;
	cell->holder = holder;
	bio_list_init(&cell->bios);
	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 dm_cell_key *key,
			struct bio *inmate,
			struct bio *inmate,
			struct dm_bio_prison_cell *cell_prealloc,
			struct dm_bio_prison_cell *cell_prealloc,
			struct dm_bio_prison_cell **cell_result)
			struct dm_bio_prison_cell **cell_result)
{
{
	uint32_t hash = hash_key(prison, key);
	struct dm_bio_prison_cell *cell;
	struct dm_bio_prison_cell *cell;


	cell = __search_bucket(prison->cells + hash, key);
	cell = __search_bucket(b, key);
	if (cell) {
	if (cell) {
		if (inmate)
		if (inmate)
			bio_list_add(&cell->bios, inmate);
			bio_list_add(&cell->bios, inmate);
@@ -148,7 +161,7 @@ static int __bio_detain(struct dm_bio_prison *prison,
		return 1;
		return 1;
	}
	}


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


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


	return r;
	return r;
}
}
@@ -208,10 +222,11 @@ void dm_cell_release(struct dm_bio_prison *prison,
		     struct bio_list *bios)
		     struct bio_list *bios)
{
{
	unsigned long flags;
	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);
	__cell_release(cell, bios);
	spin_unlock_irqrestore(&prison->lock, flags);
	spin_unlock_irqrestore(&b->lock, flags);
}
}
EXPORT_SYMBOL_GPL(dm_cell_release);
EXPORT_SYMBOL_GPL(dm_cell_release);


@@ -230,28 +245,25 @@ void dm_cell_release_no_holder(struct dm_bio_prison *prison,
			       struct bio_list *inmates)
			       struct bio_list *inmates)
{
{
	unsigned long flags;
	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);
	__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);
EXPORT_SYMBOL_GPL(dm_cell_release_no_holder);


void dm_cell_error(struct dm_bio_prison *prison,
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_list bios;
	struct bio *bio;
	struct bio *bio;
	unsigned long flags;


	bio_list_init(&bios);
	bio_list_init(&bios);

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


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


+1 −1
Original line number Original line Diff line number Diff line
@@ -85,7 +85,7 @@ void dm_cell_release_no_holder(struct dm_bio_prison *prison,
			       struct dm_bio_prison_cell *cell,
			       struct dm_bio_prison_cell *cell,
			       struct bio_list *inmates);
			       struct bio_list *inmates);
void dm_cell_error(struct dm_bio_prison *prison,
void dm_cell_error(struct dm_bio_prison *prison,
		   struct dm_bio_prison_cell *cell);
		   struct dm_bio_prison_cell *cell, int error);


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


+2 −1
Original line number Original line Diff line number Diff line
@@ -1391,6 +1391,7 @@ static int era_is_congested(struct dm_target_callbacks *cb, int bdi_bits)


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


	if (era->wq)
	if (era->wq)
+1 −10
Original line number Original line Diff line number Diff line
@@ -1242,17 +1242,8 @@ static int do_end_io(struct multipath *m, struct request *clone,
	if (!error && !clone->errors)
	if (!error && !clone->errors)
		return 0;	/* I/O complete */
		return 0;	/* I/O complete */


	if (noretry_error(error)) {
	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;
		}
		return error;
		return error;
	}


	if (mpio->pgpath)
	if (mpio->pgpath)
		fail_path(mpio->pgpath);
		fail_path(mpio->pgpath);
+48 −19
Original line number Original line Diff line number Diff line
@@ -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.
 * 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>
 * Construct an origin mapping: <dev_path>
 * The context for an origin is merely a 'struct dm_dev *'
 * The context for an origin is merely a 'struct dm_dev *'
@@ -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)
static int origin_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
{
	int r;
	int r;
	struct dm_dev *dev;
	struct dm_origin *o;


	if (argc != 1) {
	if (argc != 1) {
		ti->error = "origin: incorrect number of arguments";
		ti->error = "origin: incorrect number of arguments";
		return -EINVAL;
		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) {
	if (r) {
		ti->error = "Cannot get target device";
		ti->error = "Cannot get target device";
		return r;
		goto bad_open;
	}
	}


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


	return 0;
	return 0;

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


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


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

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


	if (bio->bi_rw & REQ_FLUSH)
	if (unlikely(bio->bi_rw & REQ_FLUSH))
		return DM_MAPIO_REMAPPED;
		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 */
	/* 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);
}
}


/*
/*
@@ -2192,15 +2221,15 @@ static int origin_map(struct dm_target *ti, struct bio *bio)
 */
 */
static void origin_resume(struct dm_target *ti)
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,
static void origin_status(struct dm_target *ti, status_type_t type,
			  unsigned status_flags, char *result, unsigned maxlen)
			  unsigned status_flags, char *result, unsigned maxlen)
{
{
	struct dm_dev *dev = ti->private;
	struct dm_origin *o = ti->private;


	switch (type) {
	switch (type) {
	case STATUSTYPE_INFO:
	case STATUSTYPE_INFO:
@@ -2208,7 +2237,7 @@ static void origin_status(struct dm_target *ti, status_type_t type,
		break;
		break;


	case STATUSTYPE_TABLE:
	case STATUSTYPE_TABLE:
		snprintf(result, maxlen, "%s", dev->name);
		snprintf(result, maxlen, "%s", o->dev->name);
		break;
		break;
	}
	}
}
}
@@ -2216,13 +2245,13 @@ static void origin_status(struct dm_target *ti, status_type_t type,
static int origin_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
static int origin_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
			struct bio_vec *biovec, int max_size)
			struct bio_vec *biovec, int max_size)
{
{
	struct dm_dev *dev = ti->private;
	struct dm_origin *o = ti->private;
	struct request_queue *q = bdev_get_queue(dev->bdev);
	struct request_queue *q = bdev_get_queue(o->dev->bdev);


	if (!q->merge_bvec_fn)
	if (!q->merge_bvec_fn)
		return max_size;
		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));
	return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
}
}
@@ -2230,9 +2259,9 @@ static int origin_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
static int origin_iterate_devices(struct dm_target *ti,
static int origin_iterate_devices(struct dm_target *ti,
				  iterate_devices_callout_fn fn, void *data)
				  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 = {
static struct target_type origin_target = {
Loading