Commit 450adcbe authored by Vivek Goyal's avatar Vivek Goyal Committed by Jens Axboe

blk-throttle: Do not use kblockd workqueue for throtl work

o Dominik Klein reported a system hang issue while doing some blkio
  throttling testing.

  https://lkml.org/lkml/2011/2/24/173

o Some tracing revealed that CFQ was not dispatching any more jobs as
  queue unplug was not happening. And queue unplug was not happening
  because unplug work was not being called as there was one throttling
  work on same cpu which as not finished yet. And throttling work had not
  finished as it was tyring to dispatch a bio to CFQ but all the request
  descriptors were consume to it was put to sleep.

o So basically it is a cyclic dependecny between CFQ unplug work and
  throtl dispatch work. Tejun suggested that use separate workqueue for
  such cases.

o This patch uses a separate workqueue for throttle related work and
  does not rely on kblockd workqueue anymore.

Cc: stable@kernel.org
Reported-by: default avatarDominik Klein <dk@in-telegence.net>
Signed-off-by: default avatarVivek Goyal <vgoyal@redhat.com>
Acked-by: default avatarTejun Heo <tj@kernel.org>
Signed-off-by: default avatarJens Axboe <jaxboe@fusionio.com>
parent 3e1f2356
...@@ -2610,13 +2610,6 @@ int kblockd_schedule_work(struct request_queue *q, struct work_struct *work) ...@@ -2610,13 +2610,6 @@ int kblockd_schedule_work(struct request_queue *q, struct work_struct *work)
} }
EXPORT_SYMBOL(kblockd_schedule_work); EXPORT_SYMBOL(kblockd_schedule_work);
int kblockd_schedule_delayed_work(struct request_queue *q,
struct delayed_work *dwork, unsigned long delay)
{
return queue_delayed_work(kblockd_workqueue, dwork, delay);
}
EXPORT_SYMBOL(kblockd_schedule_delayed_work);
int __init blk_dev_init(void) int __init blk_dev_init(void)
{ {
BUILD_BUG_ON(__REQ_NR_BITS > 8 * BUILD_BUG_ON(__REQ_NR_BITS > 8 *
......
...@@ -20,6 +20,11 @@ static int throtl_quantum = 32; ...@@ -20,6 +20,11 @@ static int throtl_quantum = 32;
/* Throttling is performed over 100ms slice and after that slice is renewed */ /* Throttling is performed over 100ms slice and after that slice is renewed */
static unsigned long throtl_slice = HZ/10; /* 100 ms */ static unsigned long throtl_slice = HZ/10; /* 100 ms */
/* A workqueue to queue throttle related work */
static struct workqueue_struct *kthrotld_workqueue;
static void throtl_schedule_delayed_work(struct throtl_data *td,
unsigned long delay);
struct throtl_rb_root { struct throtl_rb_root {
struct rb_root rb; struct rb_root rb;
struct rb_node *left; struct rb_node *left;
...@@ -345,10 +350,9 @@ static void throtl_schedule_next_dispatch(struct throtl_data *td) ...@@ -345,10 +350,9 @@ static void throtl_schedule_next_dispatch(struct throtl_data *td)
update_min_dispatch_time(st); update_min_dispatch_time(st);
if (time_before_eq(st->min_disptime, jiffies)) if (time_before_eq(st->min_disptime, jiffies))
throtl_schedule_delayed_work(td->queue, 0); throtl_schedule_delayed_work(td, 0);
else else
throtl_schedule_delayed_work(td->queue, throtl_schedule_delayed_work(td, (st->min_disptime - jiffies));
(st->min_disptime - jiffies));
} }
static inline void static inline void
...@@ -815,10 +819,10 @@ void blk_throtl_work(struct work_struct *work) ...@@ -815,10 +819,10 @@ void blk_throtl_work(struct work_struct *work)
} }
/* Call with queue lock held */ /* Call with queue lock held */
void throtl_schedule_delayed_work(struct request_queue *q, unsigned long delay) static void
throtl_schedule_delayed_work(struct throtl_data *td, unsigned long delay)
{ {
struct throtl_data *td = q->td;
struct delayed_work *dwork = &td->throtl_work; struct delayed_work *dwork = &td->throtl_work;
if (total_nr_queued(td) > 0) { if (total_nr_queued(td) > 0) {
...@@ -827,12 +831,11 @@ void throtl_schedule_delayed_work(struct request_queue *q, unsigned long delay) ...@@ -827,12 +831,11 @@ void throtl_schedule_delayed_work(struct request_queue *q, unsigned long delay)
* Cancel that and schedule a new one. * Cancel that and schedule a new one.
*/ */
__cancel_delayed_work(dwork); __cancel_delayed_work(dwork);
kblockd_schedule_delayed_work(q, dwork, delay); queue_delayed_work(kthrotld_workqueue, dwork, delay);
throtl_log(td, "schedule work. delay=%lu jiffies=%lu", throtl_log(td, "schedule work. delay=%lu jiffies=%lu",
delay, jiffies); delay, jiffies);
} }
} }
EXPORT_SYMBOL(throtl_schedule_delayed_work);
static void static void
throtl_destroy_tg(struct throtl_data *td, struct throtl_grp *tg) throtl_destroy_tg(struct throtl_data *td, struct throtl_grp *tg)
...@@ -920,7 +923,7 @@ static void throtl_update_blkio_group_read_bps(void *key, ...@@ -920,7 +923,7 @@ static void throtl_update_blkio_group_read_bps(void *key,
smp_mb__after_atomic_inc(); smp_mb__after_atomic_inc();
/* Schedule a work now to process the limit change */ /* Schedule a work now to process the limit change */
throtl_schedule_delayed_work(td->queue, 0); throtl_schedule_delayed_work(td, 0);
} }
static void throtl_update_blkio_group_write_bps(void *key, static void throtl_update_blkio_group_write_bps(void *key,
...@@ -934,7 +937,7 @@ static void throtl_update_blkio_group_write_bps(void *key, ...@@ -934,7 +937,7 @@ static void throtl_update_blkio_group_write_bps(void *key,
smp_mb__before_atomic_inc(); smp_mb__before_atomic_inc();
atomic_inc(&td->limits_changed); atomic_inc(&td->limits_changed);
smp_mb__after_atomic_inc(); smp_mb__after_atomic_inc();
throtl_schedule_delayed_work(td->queue, 0); throtl_schedule_delayed_work(td, 0);
} }
static void throtl_update_blkio_group_read_iops(void *key, static void throtl_update_blkio_group_read_iops(void *key,
...@@ -948,7 +951,7 @@ static void throtl_update_blkio_group_read_iops(void *key, ...@@ -948,7 +951,7 @@ static void throtl_update_blkio_group_read_iops(void *key,
smp_mb__before_atomic_inc(); smp_mb__before_atomic_inc();
atomic_inc(&td->limits_changed); atomic_inc(&td->limits_changed);
smp_mb__after_atomic_inc(); smp_mb__after_atomic_inc();
throtl_schedule_delayed_work(td->queue, 0); throtl_schedule_delayed_work(td, 0);
} }
static void throtl_update_blkio_group_write_iops(void *key, static void throtl_update_blkio_group_write_iops(void *key,
...@@ -962,7 +965,7 @@ static void throtl_update_blkio_group_write_iops(void *key, ...@@ -962,7 +965,7 @@ static void throtl_update_blkio_group_write_iops(void *key,
smp_mb__before_atomic_inc(); smp_mb__before_atomic_inc();
atomic_inc(&td->limits_changed); atomic_inc(&td->limits_changed);
smp_mb__after_atomic_inc(); smp_mb__after_atomic_inc();
throtl_schedule_delayed_work(td->queue, 0); throtl_schedule_delayed_work(td, 0);
} }
void throtl_shutdown_timer_wq(struct request_queue *q) void throtl_shutdown_timer_wq(struct request_queue *q)
...@@ -1135,6 +1138,10 @@ void blk_throtl_exit(struct request_queue *q) ...@@ -1135,6 +1138,10 @@ void blk_throtl_exit(struct request_queue *q)
static int __init throtl_init(void) static int __init throtl_init(void)
{ {
kthrotld_workqueue = alloc_workqueue("kthrotld", WQ_MEM_RECLAIM, 0);
if (!kthrotld_workqueue)
panic("Failed to create kthrotld\n");
blkio_policy_register(&blkio_policy_throtl); blkio_policy_register(&blkio_policy_throtl);
return 0; return 0;
} }
......
...@@ -1088,7 +1088,6 @@ static inline void put_dev_sector(Sector p) ...@@ -1088,7 +1088,6 @@ static inline void put_dev_sector(Sector p)
struct work_struct; struct work_struct;
int kblockd_schedule_work(struct request_queue *q, struct work_struct *work); int kblockd_schedule_work(struct request_queue *q, struct work_struct *work);
int kblockd_schedule_delayed_work(struct request_queue *q, struct delayed_work *dwork, unsigned long delay);
#ifdef CONFIG_BLK_CGROUP #ifdef CONFIG_BLK_CGROUP
/* /*
...@@ -1136,7 +1135,6 @@ static inline uint64_t rq_io_start_time_ns(struct request *req) ...@@ -1136,7 +1135,6 @@ static inline uint64_t rq_io_start_time_ns(struct request *req)
extern int blk_throtl_init(struct request_queue *q); extern int blk_throtl_init(struct request_queue *q);
extern void blk_throtl_exit(struct request_queue *q); extern void blk_throtl_exit(struct request_queue *q);
extern int blk_throtl_bio(struct request_queue *q, struct bio **bio); extern int blk_throtl_bio(struct request_queue *q, struct bio **bio);
extern void throtl_schedule_delayed_work(struct request_queue *q, unsigned long delay);
extern void throtl_shutdown_timer_wq(struct request_queue *q); extern void throtl_shutdown_timer_wq(struct request_queue *q);
#else /* CONFIG_BLK_DEV_THROTTLING */ #else /* CONFIG_BLK_DEV_THROTTLING */
static inline int blk_throtl_bio(struct request_queue *q, struct bio **bio) static inline int blk_throtl_bio(struct request_queue *q, struct bio **bio)
...@@ -1146,7 +1144,6 @@ static inline int blk_throtl_bio(struct request_queue *q, struct bio **bio) ...@@ -1146,7 +1144,6 @@ static inline int blk_throtl_bio(struct request_queue *q, struct bio **bio)
static inline int blk_throtl_init(struct request_queue *q) { return 0; } static inline int blk_throtl_init(struct request_queue *q) { return 0; }
static inline int blk_throtl_exit(struct request_queue *q) { return 0; } static inline int blk_throtl_exit(struct request_queue *q) { return 0; }
static inline void throtl_schedule_delayed_work(struct request_queue *q, unsigned long delay) {}
static inline void throtl_shutdown_timer_wq(struct request_queue *q) {} static inline void throtl_shutdown_timer_wq(struct request_queue *q) {}
#endif /* CONFIG_BLK_DEV_THROTTLING */ #endif /* CONFIG_BLK_DEV_THROTTLING */
......
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