#include <linux/err.h>
#include <linux/blkdev.h>
#include <linux/slab.h>
-#include "blk-cgroup.h"
#include <linux/genhd.h>
+#include <linux/delay.h>
+#include "blk-cgroup.h"
#define MAX_KEY_LEN 100
}
EXPORT_SYMBOL_GPL(blkiocg_lookup_group);
+void blkg_destroy_all(struct request_queue *q)
+{
+ struct blkio_policy_type *pol;
+
+ while (true) {
+ bool done = true;
+
+ spin_lock(&blkio_list_lock);
+ spin_lock_irq(q->queue_lock);
+
+ /*
+ * clear_queue_fn() might return with non-empty group list
+ * if it raced cgroup removal and lost. cgroup removal is
+ * guaranteed to make forward progress and retrying after a
+ * while is enough. This ugliness is scheduled to be
+ * removed after locking update.
+ */
+ list_for_each_entry(pol, &blkio_list, list)
+ if (!pol->ops.blkio_clear_queue_fn(q))
+ done = false;
+
+ spin_unlock_irq(q->queue_lock);
+ spin_unlock(&blkio_list_lock);
+
+ if (done)
+ break;
+
+ msleep(10); /* just some random duration I like */
+ }
+}
+
static void blkio_reset_stats_cpu(struct blkio_group *blkg)
{
struct blkio_group_stats_cpu *stats_cpu;
dev_t dev);
typedef void (blkio_unlink_group_fn) (void *key, struct blkio_group *blkg);
-
+typedef bool (blkio_clear_queue_fn)(struct request_queue *q);
typedef void (blkio_update_group_weight_fn) (void *key,
struct blkio_group *blkg, unsigned int weight);
typedef void (blkio_update_group_read_bps_fn) (void * key,
struct blkio_policy_ops {
blkio_unlink_group_fn *blkio_unlink_group_fn;
+ blkio_clear_queue_fn *blkio_clear_queue_fn;
blkio_update_group_weight_fn *blkio_update_group_weight_fn;
blkio_update_group_read_bps_fn *blkio_update_group_read_bps_fn;
blkio_update_group_write_bps_fn *blkio_update_group_write_bps_fn;
/* Blkio controller policy registration */
extern void blkio_policy_register(struct blkio_policy_type *);
extern void blkio_policy_unregister(struct blkio_policy_type *);
+extern void blkg_destroy_all(struct request_queue *q);
static inline char *blkg_path(struct blkio_group *blkg)
{
static inline void blkio_policy_register(struct blkio_policy_type *blkiop) { }
static inline void blkio_policy_unregister(struct blkio_policy_type *blkiop) { }
+static inline void blkg_destroy_all(struct request_queue *q) { }
static inline char *blkg_path(struct blkio_group *blkg) { return NULL; }
td->nr_undestroyed_grps--;
}
-static void throtl_release_tgs(struct throtl_data *td)
+static bool throtl_release_tgs(struct throtl_data *td, bool release_root)
{
struct hlist_node *pos, *n;
struct throtl_grp *tg;
+ bool empty = true;
hlist_for_each_entry_safe(tg, pos, n, &td->tg_list, tg_node) {
+ /* skip root? */
+ if (!release_root && tg == td->root_tg)
+ continue;
+
/*
* If cgroup removal path got to blk_group first and removed
* it from cgroup list, then it will take care of destroying
*/
if (!blkiocg_del_blkio_group(&tg->blkg))
throtl_destroy_tg(td, tg);
+ else
+ empty = false;
}
+ return empty;
}
/*
spin_unlock_irqrestore(td->queue->queue_lock, flags);
}
+static bool throtl_clear_queue(struct request_queue *q)
+{
+ lockdep_assert_held(q->queue_lock);
+
+ /*
+ * Clear tgs but leave the root one alone. This is necessary
+ * because root_tg is expected to be persistent and safe because
+ * blk-throtl can never be disabled while @q is alive. This is a
+ * kludge to prepare for unified blkg. This whole function will be
+ * removed soon.
+ */
+ return throtl_release_tgs(q->td, false);
+}
+
static void throtl_update_blkio_group_common(struct throtl_data *td,
struct throtl_grp *tg)
{
static struct blkio_policy_type blkio_policy_throtl = {
.ops = {
.blkio_unlink_group_fn = throtl_unlink_blkio_group,
+ .blkio_clear_queue_fn = throtl_clear_queue,
.blkio_update_group_read_bps_fn =
throtl_update_blkio_group_read_bps,
.blkio_update_group_write_bps_fn =
throtl_shutdown_wq(q);
spin_lock_irq(q->queue_lock);
- throtl_release_tgs(td);
+ throtl_release_tgs(td, true);
/* If there are other groups */
if (td->nr_undestroyed_grps > 0)
cfq_put_cfqg(cfqg);
}
-static void cfq_release_cfq_groups(struct cfq_data *cfqd)
+static bool cfq_release_cfq_groups(struct cfq_data *cfqd)
{
struct hlist_node *pos, *n;
struct cfq_group *cfqg;
+ bool empty = true;
hlist_for_each_entry_safe(cfqg, pos, n, &cfqd->cfqg_list, cfqd_node) {
/*
*/
if (!cfq_blkiocg_del_blkio_group(&cfqg->blkg))
cfq_destroy_cfqg(cfqd, cfqg);
+ else
+ empty = false;
}
+ return empty;
}
/*
spin_unlock_irqrestore(cfqd->queue->queue_lock, flags);
}
+static struct elevator_type iosched_cfq;
+
+static bool cfq_clear_queue(struct request_queue *q)
+{
+ lockdep_assert_held(q->queue_lock);
+
+ /* shoot down blkgs iff the current elevator is cfq */
+ if (!q->elevator || q->elevator->type != &iosched_cfq)
+ return true;
+
+ return cfq_release_cfq_groups(q->elevator->elevator_data);
+}
+
#else /* GROUP_IOSCHED */
static struct cfq_group *cfq_get_cfqg(struct cfq_data *cfqd)
{
static struct blkio_policy_type blkio_policy_cfq = {
.ops = {
.blkio_unlink_group_fn = cfq_unlink_blkio_group,
+ .blkio_clear_queue_fn = cfq_clear_queue,
.blkio_update_group_weight_fn = cfq_update_blkio_group_weight,
},
.plid = BLKIO_POLICY_PROP,
#include <trace/events/block.h>
#include "blk.h"
+#include "blk-cgroup.h"
static DEFINE_SPINLOCK(elv_list_lock);
static LIST_HEAD(elv_list);
ioc_clear_queue(q);
spin_unlock_irq(q->queue_lock);
+ blkg_destroy_all(q);
+
/* allocate, init and register new elevator */
err = -ENOMEM;
q->elevator = elevator_alloc(q, new_e);