Commit a6378366 authored by Bart Van Assche's avatar Bart Van Assche Committed by Greg Kroah-Hartman

block: Fix a blk_exit_rl() regression

commit dc9edc44 upstream.

Avoid that the following complaint is reported:

 BUG: sleeping function called from invalid context at kernel/workqueue.c:2790
 in_atomic(): 1, irqs_disabled(): 0, pid: 41, name: rcuop/3
 1 lock held by rcuop/3/41:
  #0:  (rcu_callback){......}, at: [<ffffffff8111f9a2>] rcu_nocb_kthread+0x282/0x500
 Call Trace:
  dump_stack+0x86/0xcf
  ___might_sleep+0x174/0x260
  __might_sleep+0x4a/0x80
  flush_work+0x7e/0x2e0
  __cancel_work_timer+0x143/0x1c0
  cancel_work_sync+0x10/0x20
  blk_throtl_exit+0x25/0x60
  blkcg_exit_queue+0x35/0x40
  blk_release_queue+0x42/0x130
  kobject_put+0xa9/0x190

This happens since we invoke callbacks that need to block from the
queue release handler. Fix this by pushing the final release to
a workqueue.
Reported-by: default avatarRoss Zwisler <zwisler@gmail.com>
Fixes: commit b425e504 ("block: Avoid that blk_exit_rl() triggers a use-after-free")
Signed-off-by: default avatarBart Van Assche <bart.vanassche@sandisk.com>
Tested-by: default avatarRoss Zwisler <ross.zwisler@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Updated changelog
Signed-off-by: default avatarJens Axboe <axboe@fb.com>
Cc: Laura Abbott <labbott@redhat.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 27a85c4a
...@@ -791,24 +791,25 @@ static void blk_free_queue_rcu(struct rcu_head *rcu_head) ...@@ -791,24 +791,25 @@ static void blk_free_queue_rcu(struct rcu_head *rcu_head)
} }
/** /**
* blk_release_queue: - release a &struct request_queue when it is no longer needed * __blk_release_queue - release a request queue when it is no longer needed
* @kobj: the kobj belonging to the request queue to be released * @work: pointer to the release_work member of the request queue to be released
* *
* Description: * Description:
* blk_release_queue is the pair to blk_init_queue() or * blk_release_queue is the counterpart of blk_init_queue(). It should be
* blk_queue_make_request(). It should be called when a request queue is * called when a request queue is being released; typically when a block
* being released; typically when a block device is being de-registered. * device is being de-registered. Its primary task it to free the queue
* Currently, its primary task it to free all the &struct request * itself.
* structures that were allocated to the queue and the queue itself.
* *
* Note: * Notes:
* The low level driver must have finished any outstanding requests first * The low level driver must have finished any outstanding requests first
* via blk_cleanup_queue(). * via blk_cleanup_queue().
**/ *
static void blk_release_queue(struct kobject *kobj) * Although blk_release_queue() may be called with preemption disabled,
* __blk_release_queue() may sleep.
*/
static void __blk_release_queue(struct work_struct *work)
{ {
struct request_queue *q = struct request_queue *q = container_of(work, typeof(*q), release_work);
container_of(kobj, struct request_queue, kobj);
wbt_exit(q); wbt_exit(q);
bdi_put(q->backing_dev_info); bdi_put(q->backing_dev_info);
...@@ -844,6 +845,15 @@ static void blk_release_queue(struct kobject *kobj) ...@@ -844,6 +845,15 @@ static void blk_release_queue(struct kobject *kobj)
call_rcu(&q->rcu_head, blk_free_queue_rcu); call_rcu(&q->rcu_head, blk_free_queue_rcu);
} }
static void blk_release_queue(struct kobject *kobj)
{
struct request_queue *q =
container_of(kobj, struct request_queue, kobj);
INIT_WORK(&q->release_work, __blk_release_queue);
schedule_work(&q->release_work);
}
static const struct sysfs_ops queue_sysfs_ops = { static const struct sysfs_ops queue_sysfs_ops = {
.show = queue_attr_show, .show = queue_attr_show,
.store = queue_attr_store, .store = queue_attr_store,
......
...@@ -580,6 +580,8 @@ struct request_queue { ...@@ -580,6 +580,8 @@ struct request_queue {
size_t cmd_size; size_t cmd_size;
void *rq_alloc_data; void *rq_alloc_data;
struct work_struct release_work;
}; };
#define QUEUE_FLAG_QUEUED 1 /* uses generic tag queueing */ #define QUEUE_FLAG_QUEUED 1 /* uses generic tag queueing */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment