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

Commit 527d1529 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'for-4.4/integrity' of git://git.kernel.dk/linux-block

Pull block integrity updates from Jens Axboe:
 ""This is the joint work of Dan and Martin, cleaning up and improving
  the support for block data integrity"

* 'for-4.4/integrity' of git://git.kernel.dk/linux-block:
  block, libnvdimm, nvme: provide a built-in blk_integrity nop profile
  block: blk_flush_integrity() for bio-based drivers
  block: move blk_integrity to request_queue
  block: generic request_queue reference counting
  nvme: suspend i/o during runtime blk_integrity_unregister
  md: suspend i/o during runtime blk_integrity_unregister
  md, dm, scsi, nvme, libnvdimm: drop blk_integrity_unregister() at shutdown
  block: Inline blk_integrity in struct gendisk
  block: Export integrity data interval size in sysfs
  block: Reduce the size of struct blk_integrity
  block: Consolidate static integrity profile properties
  block: Move integrity kobject to struct gendisk
parents effa04cc 4125a09b
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -60,6 +60,13 @@ Description:
		Indicates whether a storage device is capable of storing
		integrity metadata. Set if the device is T10 PI-capable.

What:		/sys/block/<disk>/integrity/protection_interval_bytes
Date:		July 2015
Contact:	Martin K. Petersen <martin.petersen@oracle.com>
Description:
		Describes the number of data bytes which are protected
		by one integrity tuple. Typically the device's logical
		block size.

What:		/sys/block/<disk>/integrity/write_generate
Date:		June 2008
+11 −6
Original line number Diff line number Diff line
@@ -32,6 +32,11 @@
static struct kmem_cache *bip_slab;
static struct workqueue_struct *kintegrityd_wq;

void blk_flush_integrity(void)
{
	flush_workqueue(kintegrityd_wq);
}

/**
 * bio_integrity_alloc - Allocate integrity payload and attach it to bio
 * @bio:	bio to attach integrity metadata to
@@ -177,11 +182,11 @@ bool bio_integrity_enabled(struct bio *bio)
	if (bi == NULL)
		return false;

	if (bio_data_dir(bio) == READ && bi->verify_fn != NULL &&
	if (bio_data_dir(bio) == READ && bi->profile->verify_fn != NULL &&
	    (bi->flags & BLK_INTEGRITY_VERIFY))
		return true;

	if (bio_data_dir(bio) == WRITE && bi->generate_fn != NULL &&
	if (bio_data_dir(bio) == WRITE && bi->profile->generate_fn != NULL &&
	    (bi->flags & BLK_INTEGRITY_GENERATE))
		return true;

@@ -202,7 +207,7 @@ EXPORT_SYMBOL(bio_integrity_enabled);
static inline unsigned int bio_integrity_intervals(struct blk_integrity *bi,
						   unsigned int sectors)
{
	return sectors >> (ilog2(bi->interval) - 9);
	return sectors >> (bi->interval_exp - 9);
}

static inline unsigned int bio_integrity_bytes(struct blk_integrity *bi,
@@ -229,7 +234,7 @@ static int bio_integrity_process(struct bio *bio,
		bip->bip_vec->bv_offset;

	iter.disk_name = bio->bi_bdev->bd_disk->disk_name;
	iter.interval = bi->interval;
	iter.interval = 1 << bi->interval_exp;
	iter.seed = bip_get_seed(bip);
	iter.prot_buf = prot_buf;

@@ -340,7 +345,7 @@ int bio_integrity_prep(struct bio *bio)

	/* Auto-generate integrity metadata if this is a write */
	if (bio_data_dir(bio) == WRITE)
		bio_integrity_process(bio, bi->generate_fn);
		bio_integrity_process(bio, bi->profile->generate_fn);

	return 0;
}
@@ -361,7 +366,7 @@ static void bio_integrity_verify_fn(struct work_struct *work)
	struct bio *bio = bip->bip_bio;
	struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev);

	bio->bi_error = bio_integrity_process(bio, bi->verify_fn);
	bio->bi_error = bio_integrity_process(bio, bi->profile->verify_fn);

	/* Restore original bio completion handler */
	bio->bi_end_io = bip->bip_end_io;
+65 −9
Original line number Diff line number Diff line
@@ -554,22 +554,23 @@ void blk_cleanup_queue(struct request_queue *q)
	 * Drain all requests queued before DYING marking. Set DEAD flag to
	 * prevent that q->request_fn() gets invoked after draining finished.
	 */
	if (q->mq_ops) {
		blk_mq_freeze_queue(q);
		spin_lock_irq(lock);
	} else {
	blk_freeze_queue(q);
	spin_lock_irq(lock);
	if (!q->mq_ops)
		__blk_drain_queue(q, true);
	}
	queue_flag_set(QUEUE_FLAG_DEAD, q);
	spin_unlock_irq(lock);

	/* for synchronous bio-based driver finish in-flight integrity i/o */
	blk_flush_integrity();

	/* @q won't process any more request, flush async actions */
	del_timer_sync(&q->backing_dev_info.laptop_mode_wb_timer);
	blk_sync_queue(q);

	if (q->mq_ops)
		blk_mq_free_queue(q);
	percpu_ref_exit(&q->q_usage_counter);

	spin_lock_irq(lock);
	if (q->queue_lock != &q->__queue_lock)
@@ -629,6 +630,40 @@ struct request_queue *blk_alloc_queue(gfp_t gfp_mask)
}
EXPORT_SYMBOL(blk_alloc_queue);

int blk_queue_enter(struct request_queue *q, gfp_t gfp)
{
	while (true) {
		int ret;

		if (percpu_ref_tryget_live(&q->q_usage_counter))
			return 0;

		if (!(gfp & __GFP_WAIT))
			return -EBUSY;

		ret = wait_event_interruptible(q->mq_freeze_wq,
				!atomic_read(&q->mq_freeze_depth) ||
				blk_queue_dying(q));
		if (blk_queue_dying(q))
			return -ENODEV;
		if (ret)
			return ret;
	}
}

void blk_queue_exit(struct request_queue *q)
{
	percpu_ref_put(&q->q_usage_counter);
}

static void blk_queue_usage_counter_release(struct percpu_ref *ref)
{
	struct request_queue *q =
		container_of(ref, struct request_queue, q_usage_counter);

	wake_up_all(&q->mq_freeze_wq);
}

struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
{
	struct request_queue *q;
@@ -690,11 +725,22 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)

	init_waitqueue_head(&q->mq_freeze_wq);

	if (blkcg_init_queue(q))
	/*
	 * Init percpu_ref in atomic mode so that it's faster to shutdown.
	 * See blk_register_queue() for details.
	 */
	if (percpu_ref_init(&q->q_usage_counter,
				blk_queue_usage_counter_release,
				PERCPU_REF_INIT_ATOMIC, GFP_KERNEL))
		goto fail_bdi;

	if (blkcg_init_queue(q))
		goto fail_ref;

	return q;

fail_ref:
	percpu_ref_exit(&q->q_usage_counter);
fail_bdi:
	bdi_destroy(&q->backing_dev_info);
fail_split:
@@ -1992,9 +2038,19 @@ void generic_make_request(struct bio *bio)
	do {
		struct request_queue *q = bdev_get_queue(bio->bi_bdev);

		if (likely(blk_queue_enter(q, __GFP_WAIT) == 0)) {

			q->make_request_fn(q, bio);

			blk_queue_exit(q);

			bio = bio_list_pop(current->bio_list);
		} else {
			struct bio *bio_next = bio_list_pop(current->bio_list);

			bio_io_error(bio);
			bio = bio_next;
		}
	} while (bio);
	current->bio_list = NULL; /* deactivate */
}
+86 −106
Original line number Diff line number Diff line
@@ -30,10 +30,6 @@

#include "blk.h"

static struct kmem_cache *integrity_cachep;

static const char *bi_unsupported_name = "unsupported";

/**
 * blk_rq_count_integrity_sg - Count number of integrity scatterlist elements
 * @q:		request queue
@@ -146,40 +142,40 @@ EXPORT_SYMBOL(blk_rq_map_integrity_sg);
 */
int blk_integrity_compare(struct gendisk *gd1, struct gendisk *gd2)
{
	struct blk_integrity *b1 = gd1->integrity;
	struct blk_integrity *b2 = gd2->integrity;
	struct blk_integrity *b1 = &gd1->queue->integrity;
	struct blk_integrity *b2 = &gd2->queue->integrity;

	if (!b1 && !b2)
	if (!b1->profile && !b2->profile)
		return 0;

	if (!b1 || !b2)
	if (!b1->profile || !b2->profile)
		return -1;

	if (b1->interval != b2->interval) {
	if (b1->interval_exp != b2->interval_exp) {
		pr_err("%s: %s/%s protection interval %u != %u\n",
		       __func__, gd1->disk_name, gd2->disk_name,
		       b1->interval, b2->interval);
		       1 << b1->interval_exp, 1 << b2->interval_exp);
		return -1;
	}

	if (b1->tuple_size != b2->tuple_size) {
		printk(KERN_ERR "%s: %s/%s tuple sz %u != %u\n", __func__,
		pr_err("%s: %s/%s tuple sz %u != %u\n", __func__,
		       gd1->disk_name, gd2->disk_name,
		       b1->tuple_size, b2->tuple_size);
		return -1;
	}

	if (b1->tag_size && b2->tag_size && (b1->tag_size != b2->tag_size)) {
		printk(KERN_ERR "%s: %s/%s tag sz %u != %u\n", __func__,
		pr_err("%s: %s/%s tag sz %u != %u\n", __func__,
		       gd1->disk_name, gd2->disk_name,
		       b1->tag_size, b2->tag_size);
		return -1;
	}

	if (strcmp(b1->name, b2->name)) {
		printk(KERN_ERR "%s: %s/%s type %s != %s\n", __func__,
	if (b1->profile != b2->profile) {
		pr_err("%s: %s/%s type %s != %s\n", __func__,
		       gd1->disk_name, gd2->disk_name,
		       b1->name, b2->name);
		       b1->profile->name, b2->profile->name);
		return -1;
	}

@@ -249,8 +245,8 @@ struct integrity_sysfs_entry {
static ssize_t integrity_attr_show(struct kobject *kobj, struct attribute *attr,
				   char *page)
{
	struct blk_integrity *bi =
		container_of(kobj, struct blk_integrity, kobj);
	struct gendisk *disk = container_of(kobj, struct gendisk, integrity_kobj);
	struct blk_integrity *bi = &disk->queue->integrity;
	struct integrity_sysfs_entry *entry =
		container_of(attr, struct integrity_sysfs_entry, attr);

@@ -261,8 +257,8 @@ static ssize_t integrity_attr_store(struct kobject *kobj,
				    struct attribute *attr, const char *page,
				    size_t count)
{
	struct blk_integrity *bi =
		container_of(kobj, struct blk_integrity, kobj);
	struct gendisk *disk = container_of(kobj, struct gendisk, integrity_kobj);
	struct blk_integrity *bi = &disk->queue->integrity;
	struct integrity_sysfs_entry *entry =
		container_of(attr, struct integrity_sysfs_entry, attr);
	ssize_t ret = 0;
@@ -275,18 +271,21 @@ static ssize_t integrity_attr_store(struct kobject *kobj,

static ssize_t integrity_format_show(struct blk_integrity *bi, char *page)
{
	if (bi != NULL && bi->name != NULL)
		return sprintf(page, "%s\n", bi->name);
	if (bi->profile && bi->profile->name)
		return sprintf(page, "%s\n", bi->profile->name);
	else
		return sprintf(page, "none\n");
}

static ssize_t integrity_tag_size_show(struct blk_integrity *bi, char *page)
{
	if (bi != NULL)
	return sprintf(page, "%u\n", bi->tag_size);
	else
		return sprintf(page, "0\n");
}

static ssize_t integrity_interval_show(struct blk_integrity *bi, char *page)
{
	return sprintf(page, "%u\n",
		       bi->interval_exp ? 1 << bi->interval_exp : 0);
}

static ssize_t integrity_verify_store(struct blk_integrity *bi,
@@ -343,6 +342,11 @@ static struct integrity_sysfs_entry integrity_tag_size_entry = {
	.show = integrity_tag_size_show,
};

static struct integrity_sysfs_entry integrity_interval_entry = {
	.attr = { .name = "protection_interval_bytes", .mode = S_IRUGO },
	.show = integrity_interval_show,
};

static struct integrity_sysfs_entry integrity_verify_entry = {
	.attr = { .name = "read_verify", .mode = S_IRUGO | S_IWUSR },
	.show = integrity_verify_show,
@@ -363,6 +367,7 @@ static struct integrity_sysfs_entry integrity_device_entry = {
static struct attribute *integrity_attrs[] = {
	&integrity_format_entry.attr,
	&integrity_tag_size_entry.attr,
	&integrity_interval_entry.attr,
	&integrity_verify_entry.attr,
	&integrity_generate_entry.attr,
	&integrity_device_entry.attr,
@@ -374,114 +379,89 @@ static const struct sysfs_ops integrity_ops = {
	.store	= &integrity_attr_store,
};

static int __init blk_dev_integrity_init(void)
{
	integrity_cachep = kmem_cache_create("blkdev_integrity",
					     sizeof(struct blk_integrity),
					     0, SLAB_PANIC, NULL);
	return 0;
}
subsys_initcall(blk_dev_integrity_init);

static void blk_integrity_release(struct kobject *kobj)
{
	struct blk_integrity *bi =
		container_of(kobj, struct blk_integrity, kobj);

	kmem_cache_free(integrity_cachep, bi);
}

static struct kobj_type integrity_ktype = {
	.default_attrs	= integrity_attrs,
	.sysfs_ops	= &integrity_ops,
	.release	= blk_integrity_release,
};

bool blk_integrity_is_initialized(struct gendisk *disk)
static int blk_integrity_nop_fn(struct blk_integrity_iter *iter)
{
	struct blk_integrity *bi = blk_get_integrity(disk);

	return (bi && bi->name && strcmp(bi->name, bi_unsupported_name) != 0);
	return 0;
}
EXPORT_SYMBOL(blk_integrity_is_initialized);

static struct blk_integrity_profile nop_profile = {
	.name = "nop",
	.generate_fn = blk_integrity_nop_fn,
	.verify_fn = blk_integrity_nop_fn,
};

/**
 * blk_integrity_register - Register a gendisk as being integrity-capable
 * @disk:	struct gendisk pointer to make integrity-aware
 * @template:	optional integrity profile to register
 * @template:	block integrity profile to register
 *
 * Description: When a device needs to advertise itself as being able
 * to send/receive integrity metadata it must use this function to
 * register the capability with the block layer.  The template is a
 * blk_integrity struct with values appropriate for the underlying
 * hardware.  If template is NULL the new profile is allocated but
 * not filled out. See Documentation/block/data-integrity.txt.
 * Description: When a device needs to advertise itself as being able to
 * send/receive integrity metadata it must use this function to register
 * the capability with the block layer. The template is a blk_integrity
 * struct with values appropriate for the underlying hardware. See
 * Documentation/block/data-integrity.txt.
 */
int blk_integrity_register(struct gendisk *disk, struct blk_integrity *template)
void blk_integrity_register(struct gendisk *disk, struct blk_integrity *template)
{
	struct blk_integrity *bi;

	BUG_ON(disk == NULL);
	struct blk_integrity *bi = &disk->queue->integrity;

	if (disk->integrity == NULL) {
		bi = kmem_cache_alloc(integrity_cachep,
				      GFP_KERNEL | __GFP_ZERO);
		if (!bi)
			return -1;

		if (kobject_init_and_add(&bi->kobj, &integrity_ktype,
					 &disk_to_dev(disk)->kobj,
					 "%s", "integrity")) {
			kmem_cache_free(integrity_cachep, bi);
			return -1;
		}

		kobject_uevent(&bi->kobj, KOBJ_ADD);

		bi->flags |= BLK_INTEGRITY_VERIFY | BLK_INTEGRITY_GENERATE;
		bi->interval = queue_logical_block_size(disk->queue);
		disk->integrity = bi;
	} else
		bi = disk->integrity;

	/* Use the provided profile as template */
	if (template != NULL) {
		bi->name = template->name;
		bi->generate_fn = template->generate_fn;
		bi->verify_fn = template->verify_fn;
	bi->flags = BLK_INTEGRITY_VERIFY | BLK_INTEGRITY_GENERATE |
		template->flags;
	bi->interval_exp = ilog2(queue_logical_block_size(disk->queue));
	bi->profile = template->profile ? template->profile : &nop_profile;
	bi->tuple_size = template->tuple_size;
	bi->tag_size = template->tag_size;
		bi->flags |= template->flags;
	} else
		bi->name = bi_unsupported_name;

	disk->queue->backing_dev_info.capabilities |= BDI_CAP_STABLE_WRITES;

	return 0;
	blk_integrity_revalidate(disk);
}
EXPORT_SYMBOL(blk_integrity_register);

/**
 * blk_integrity_unregister - Remove block integrity profile
 * @disk:	disk whose integrity profile to deallocate
 * blk_integrity_unregister - Unregister block integrity profile
 * @disk:	disk whose integrity profile to unregister
 *
 * Description: This function frees all memory used by the block
 * integrity profile.  To be called at device teardown.
 * Description: This function unregisters the integrity capability from
 * a block device.
 */
void blk_integrity_unregister(struct gendisk *disk)
{
	struct blk_integrity *bi;
	blk_integrity_revalidate(disk);
	memset(&disk->queue->integrity, 0, sizeof(struct blk_integrity));
}
EXPORT_SYMBOL(blk_integrity_unregister);

	if (!disk || !disk->integrity)
void blk_integrity_revalidate(struct gendisk *disk)
{
	struct blk_integrity *bi = &disk->queue->integrity;

	if (!(disk->flags & GENHD_FL_UP))
		return;

	disk->queue->backing_dev_info.capabilities &= ~BDI_CAP_STABLE_WRITES;
	if (bi->profile)
		disk->queue->backing_dev_info.capabilities |=
			BDI_CAP_STABLE_WRITES;
	else
		disk->queue->backing_dev_info.capabilities &=
			~BDI_CAP_STABLE_WRITES;
}

void blk_integrity_add(struct gendisk *disk)
{
	if (kobject_init_and_add(&disk->integrity_kobj, &integrity_ktype,
				 &disk_to_dev(disk)->kobj, "%s", "integrity"))
		return;

	bi = disk->integrity;
	kobject_uevent(&disk->integrity_kobj, KOBJ_ADD);
}

	kobject_uevent(&bi->kobj, KOBJ_REMOVE);
	kobject_del(&bi->kobj);
	kobject_put(&bi->kobj);
	disk->integrity = NULL;
void blk_integrity_del(struct gendisk *disk)
{
	kobject_uevent(&disk->integrity_kobj, KOBJ_REMOVE);
	kobject_del(&disk->integrity_kobj);
	kobject_put(&disk->integrity_kobj);
}
EXPORT_SYMBOL(blk_integrity_unregister);
+0 −6
Original line number Diff line number Diff line
@@ -413,12 +413,6 @@ static void blk_mq_sysfs_init(struct request_queue *q)
		kobject_init(&ctx->kobj, &blk_mq_ctx_ktype);
}

/* see blk_register_queue() */
void blk_mq_finish_init(struct request_queue *q)
{
	percpu_ref_switch_to_percpu(&q->mq_usage_counter);
}

int blk_mq_register_disk(struct gendisk *disk)
{
	struct device *dev = disk_to_dev(disk);
Loading