mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 03:01:39 -04:00
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
This commit is contained in:
commit
527d1529e3
28 changed files with 367 additions and 343 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
spin_lock_irq(lock);
|
||||
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);
|
||||
|
||||
q->make_request_fn(q, bio);
|
||||
if (likely(blk_queue_enter(q, __GFP_WAIT) == 0)) {
|
||||
|
||||
bio = bio_list_pop(current->bio_list);
|
||||
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 */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
return sprintf(page, "%u\n", bi->tag_size);
|
||||
}
|
||||
|
||||
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;
|
||||
struct blk_integrity *bi = &disk->queue->integrity;
|
||||
|
||||
BUG_ON(disk == NULL);
|
||||
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;
|
||||
|
||||
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->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;
|
||||
|
||||
if (!disk || !disk->integrity)
|
||||
return;
|
||||
|
||||
disk->queue->backing_dev_info.capabilities &= ~BDI_CAP_STABLE_WRITES;
|
||||
|
||||
bi = disk->integrity;
|
||||
|
||||
kobject_uevent(&bi->kobj, KOBJ_REMOVE);
|
||||
kobject_del(&bi->kobj);
|
||||
kobject_put(&bi->kobj);
|
||||
disk->integrity = NULL;
|
||||
blk_integrity_revalidate(disk);
|
||||
memset(&disk->queue->integrity, 0, sizeof(struct blk_integrity));
|
||||
}
|
||||
EXPORT_SYMBOL(blk_integrity_unregister);
|
||||
|
||||
void blk_integrity_revalidate(struct gendisk *disk)
|
||||
{
|
||||
struct blk_integrity *bi = &disk->queue->integrity;
|
||||
|
||||
if (!(disk->flags & GENHD_FL_UP))
|
||||
return;
|
||||
|
||||
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;
|
||||
|
||||
kobject_uevent(&disk->integrity_kobj, KOBJ_ADD);
|
||||
}
|
||||
|
||||
void blk_integrity_del(struct gendisk *disk)
|
||||
{
|
||||
kobject_uevent(&disk->integrity_kobj, KOBJ_REMOVE);
|
||||
kobject_del(&disk->integrity_kobj);
|
||||
kobject_put(&disk->integrity_kobj);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -78,47 +78,13 @@ static void blk_mq_hctx_clear_pending(struct blk_mq_hw_ctx *hctx,
|
|||
clear_bit(CTX_TO_BIT(hctx, ctx), &bm->word);
|
||||
}
|
||||
|
||||
static int blk_mq_queue_enter(struct request_queue *q, gfp_t gfp)
|
||||
{
|
||||
while (true) {
|
||||
int ret;
|
||||
|
||||
if (percpu_ref_tryget_live(&q->mq_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;
|
||||
}
|
||||
}
|
||||
|
||||
static void blk_mq_queue_exit(struct request_queue *q)
|
||||
{
|
||||
percpu_ref_put(&q->mq_usage_counter);
|
||||
}
|
||||
|
||||
static void blk_mq_usage_counter_release(struct percpu_ref *ref)
|
||||
{
|
||||
struct request_queue *q =
|
||||
container_of(ref, struct request_queue, mq_usage_counter);
|
||||
|
||||
wake_up_all(&q->mq_freeze_wq);
|
||||
}
|
||||
|
||||
void blk_mq_freeze_queue_start(struct request_queue *q)
|
||||
{
|
||||
int freeze_depth;
|
||||
|
||||
freeze_depth = atomic_inc_return(&q->mq_freeze_depth);
|
||||
if (freeze_depth == 1) {
|
||||
percpu_ref_kill(&q->mq_usage_counter);
|
||||
percpu_ref_kill(&q->q_usage_counter);
|
||||
blk_mq_run_hw_queues(q, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -126,18 +92,34 @@ EXPORT_SYMBOL_GPL(blk_mq_freeze_queue_start);
|
|||
|
||||
static void blk_mq_freeze_queue_wait(struct request_queue *q)
|
||||
{
|
||||
wait_event(q->mq_freeze_wq, percpu_ref_is_zero(&q->mq_usage_counter));
|
||||
wait_event(q->mq_freeze_wq, percpu_ref_is_zero(&q->q_usage_counter));
|
||||
}
|
||||
|
||||
/*
|
||||
* Guarantee no request is in use, so we can change any data structure of
|
||||
* the queue afterward.
|
||||
*/
|
||||
void blk_mq_freeze_queue(struct request_queue *q)
|
||||
void blk_freeze_queue(struct request_queue *q)
|
||||
{
|
||||
/*
|
||||
* In the !blk_mq case we are only calling this to kill the
|
||||
* q_usage_counter, otherwise this increases the freeze depth
|
||||
* and waits for it to return to zero. For this reason there is
|
||||
* no blk_unfreeze_queue(), and blk_freeze_queue() is not
|
||||
* exported to drivers as the only user for unfreeze is blk_mq.
|
||||
*/
|
||||
blk_mq_freeze_queue_start(q);
|
||||
blk_mq_freeze_queue_wait(q);
|
||||
}
|
||||
|
||||
void blk_mq_freeze_queue(struct request_queue *q)
|
||||
{
|
||||
/*
|
||||
* ...just an alias to keep freeze and unfreeze actions balanced
|
||||
* in the blk_mq_* namespace
|
||||
*/
|
||||
blk_freeze_queue(q);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_mq_freeze_queue);
|
||||
|
||||
void blk_mq_unfreeze_queue(struct request_queue *q)
|
||||
|
|
@ -147,7 +129,7 @@ void blk_mq_unfreeze_queue(struct request_queue *q)
|
|||
freeze_depth = atomic_dec_return(&q->mq_freeze_depth);
|
||||
WARN_ON_ONCE(freeze_depth < 0);
|
||||
if (!freeze_depth) {
|
||||
percpu_ref_reinit(&q->mq_usage_counter);
|
||||
percpu_ref_reinit(&q->q_usage_counter);
|
||||
wake_up_all(&q->mq_freeze_wq);
|
||||
}
|
||||
}
|
||||
|
|
@ -256,7 +238,7 @@ struct request *blk_mq_alloc_request(struct request_queue *q, int rw, gfp_t gfp,
|
|||
struct blk_mq_alloc_data alloc_data;
|
||||
int ret;
|
||||
|
||||
ret = blk_mq_queue_enter(q, gfp);
|
||||
ret = blk_queue_enter(q, gfp);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
|
|
@ -279,7 +261,7 @@ struct request *blk_mq_alloc_request(struct request_queue *q, int rw, gfp_t gfp,
|
|||
}
|
||||
blk_mq_put_ctx(ctx);
|
||||
if (!rq) {
|
||||
blk_mq_queue_exit(q);
|
||||
blk_queue_exit(q);
|
||||
return ERR_PTR(-EWOULDBLOCK);
|
||||
}
|
||||
return rq;
|
||||
|
|
@ -298,7 +280,7 @@ static void __blk_mq_free_request(struct blk_mq_hw_ctx *hctx,
|
|||
|
||||
clear_bit(REQ_ATOM_STARTED, &rq->atomic_flags);
|
||||
blk_mq_put_tag(hctx, tag, &ctx->last_tag);
|
||||
blk_mq_queue_exit(q);
|
||||
blk_queue_exit(q);
|
||||
}
|
||||
|
||||
void blk_mq_free_hctx_request(struct blk_mq_hw_ctx *hctx, struct request *rq)
|
||||
|
|
@ -1185,11 +1167,7 @@ static struct request *blk_mq_map_request(struct request_queue *q,
|
|||
int rw = bio_data_dir(bio);
|
||||
struct blk_mq_alloc_data alloc_data;
|
||||
|
||||
if (unlikely(blk_mq_queue_enter(q, GFP_KERNEL))) {
|
||||
bio_io_error(bio);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
blk_queue_enter_live(q);
|
||||
ctx = blk_mq_get_ctx(q);
|
||||
hctx = q->mq_ops->map_queue(q, ctx->cpu);
|
||||
|
||||
|
|
@ -2024,14 +2002,6 @@ struct request_queue *blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,
|
|||
hctxs[i]->queue_num = i;
|
||||
}
|
||||
|
||||
/*
|
||||
* Init percpu_ref in atomic mode so that it's faster to shutdown.
|
||||
* See blk_register_queue() for details.
|
||||
*/
|
||||
if (percpu_ref_init(&q->mq_usage_counter, blk_mq_usage_counter_release,
|
||||
PERCPU_REF_INIT_ATOMIC, GFP_KERNEL))
|
||||
goto err_hctxs;
|
||||
|
||||
setup_timer(&q->timeout, blk_mq_rq_timer, (unsigned long) q);
|
||||
blk_queue_rq_timeout(q, set->timeout ? set->timeout : 30 * HZ);
|
||||
|
||||
|
|
@ -2112,8 +2082,6 @@ void blk_mq_free_queue(struct request_queue *q)
|
|||
|
||||
blk_mq_exit_hw_queues(q, set, set->nr_hw_queues);
|
||||
blk_mq_free_hw_queues(q, set);
|
||||
|
||||
percpu_ref_exit(&q->mq_usage_counter);
|
||||
}
|
||||
|
||||
/* Basically redo blk_mq_init_queue with queue frozen */
|
||||
|
|
|
|||
|
|
@ -600,9 +600,8 @@ int blk_register_queue(struct gendisk *disk)
|
|||
*/
|
||||
if (!blk_queue_init_done(q)) {
|
||||
queue_flag_set_unlocked(QUEUE_FLAG_INIT_DONE, q);
|
||||
percpu_ref_switch_to_percpu(&q->q_usage_counter);
|
||||
blk_queue_bypass_end(q);
|
||||
if (q->mq_ops)
|
||||
blk_mq_finish_init(q);
|
||||
}
|
||||
|
||||
ret = blk_trace_init_sysfs(dev);
|
||||
|
|
|
|||
22
block/blk.h
22
block/blk.h
|
|
@ -72,6 +72,28 @@ void blk_dequeue_request(struct request *rq);
|
|||
void __blk_queue_free_tags(struct request_queue *q);
|
||||
bool __blk_end_bidi_request(struct request *rq, int error,
|
||||
unsigned int nr_bytes, unsigned int bidi_bytes);
|
||||
int blk_queue_enter(struct request_queue *q, gfp_t gfp);
|
||||
void blk_queue_exit(struct request_queue *q);
|
||||
void blk_freeze_queue(struct request_queue *q);
|
||||
|
||||
static inline void blk_queue_enter_live(struct request_queue *q)
|
||||
{
|
||||
/*
|
||||
* Given that running in generic_make_request() context
|
||||
* guarantees that a live reference against q_usage_counter has
|
||||
* been established, further references under that same context
|
||||
* need not check that the queue has been frozen (marked dead).
|
||||
*/
|
||||
percpu_ref_get(&q->q_usage_counter);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_INTEGRITY
|
||||
void blk_flush_integrity(void);
|
||||
#else
|
||||
static inline void blk_flush_integrity(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
void blk_rq_timed_out_timer(unsigned long data);
|
||||
unsigned long blk_rq_timeout(unsigned long timeout);
|
||||
|
|
|
|||
|
|
@ -630,6 +630,7 @@ void add_disk(struct gendisk *disk)
|
|||
WARN_ON(retval);
|
||||
|
||||
disk_add_events(disk);
|
||||
blk_integrity_add(disk);
|
||||
}
|
||||
EXPORT_SYMBOL(add_disk);
|
||||
|
||||
|
|
@ -638,6 +639,7 @@ void del_gendisk(struct gendisk *disk)
|
|||
struct disk_part_iter piter;
|
||||
struct hd_struct *part;
|
||||
|
||||
blk_integrity_del(disk);
|
||||
disk_del_events(disk);
|
||||
|
||||
/* invalidate stuff */
|
||||
|
|
|
|||
|
|
@ -428,6 +428,7 @@ rescan:
|
|||
|
||||
if (disk->fops->revalidate_disk)
|
||||
disk->fops->revalidate_disk(disk);
|
||||
blk_integrity_revalidate(disk);
|
||||
check_disk_size_change(disk, bdev);
|
||||
bdev->bd_invalidated = 0;
|
||||
if (!get_capacity(disk) || !(state = check_partition(disk, bdev)))
|
||||
|
|
|
|||
|
|
@ -160,38 +160,30 @@ static int t10_pi_type3_verify_ip(struct blk_integrity_iter *iter)
|
|||
return t10_pi_verify(iter, t10_pi_ip_fn, 3);
|
||||
}
|
||||
|
||||
struct blk_integrity t10_pi_type1_crc = {
|
||||
struct blk_integrity_profile t10_pi_type1_crc = {
|
||||
.name = "T10-DIF-TYPE1-CRC",
|
||||
.generate_fn = t10_pi_type1_generate_crc,
|
||||
.verify_fn = t10_pi_type1_verify_crc,
|
||||
.tuple_size = sizeof(struct t10_pi_tuple),
|
||||
.tag_size = 0,
|
||||
};
|
||||
EXPORT_SYMBOL(t10_pi_type1_crc);
|
||||
|
||||
struct blk_integrity t10_pi_type1_ip = {
|
||||
struct blk_integrity_profile t10_pi_type1_ip = {
|
||||
.name = "T10-DIF-TYPE1-IP",
|
||||
.generate_fn = t10_pi_type1_generate_ip,
|
||||
.verify_fn = t10_pi_type1_verify_ip,
|
||||
.tuple_size = sizeof(struct t10_pi_tuple),
|
||||
.tag_size = 0,
|
||||
};
|
||||
EXPORT_SYMBOL(t10_pi_type1_ip);
|
||||
|
||||
struct blk_integrity t10_pi_type3_crc = {
|
||||
struct blk_integrity_profile t10_pi_type3_crc = {
|
||||
.name = "T10-DIF-TYPE3-CRC",
|
||||
.generate_fn = t10_pi_type3_generate_crc,
|
||||
.verify_fn = t10_pi_type3_verify_crc,
|
||||
.tuple_size = sizeof(struct t10_pi_tuple),
|
||||
.tag_size = 0,
|
||||
};
|
||||
EXPORT_SYMBOL(t10_pi_type3_crc);
|
||||
|
||||
struct blk_integrity t10_pi_type3_ip = {
|
||||
struct blk_integrity_profile t10_pi_type3_ip = {
|
||||
.name = "T10-DIF-TYPE3-IP",
|
||||
.generate_fn = t10_pi_type3_generate_ip,
|
||||
.verify_fn = t10_pi_type3_verify_ip,
|
||||
.tuple_size = sizeof(struct t10_pi_tuple),
|
||||
.tag_size = 0,
|
||||
};
|
||||
EXPORT_SYMBOL(t10_pi_type3_ip);
|
||||
|
|
|
|||
Loading…
Reference in a new issue