[Date Prev][Date Next] [Thread Prev][Thread Next]
[Thread Index]
[Date Index]
[Author Index]
[dm-devel] [PATCH 19/20] io-controller: Debug hierarchical IO scheduling
- From: Vivek Goyal <vgoyal redhat com>
- To: linux-kernel vger kernel org, containers lists linux-foundation org, dm-devel redhat com, jens axboe oracle com, nauman google com, dpshah google com, lizf cn fujitsu com, mikew google com, fchecconi gmail com, paolo valente unimore it, ryov valinux co jp, fernando oss ntt co jp, s-uchida ap jp nec com, taka valinux co jp, guijianfeng cn fujitsu com, jmoyer redhat com, dhaval linux vnet ibm com, balbir linux vnet ibm com, righi andrea gmail com, m-ikeda ds jp nec com, jbaron redhat com
- Cc: peterz infradead org, akpm linux-foundation org, snitzer redhat com, agk redhat com, vgoyal redhat com
- Subject: [dm-devel] [PATCH 19/20] io-controller: Debug hierarchical IO scheduling
- Date: Tue, 26 May 2009 18:42:08 -0400
o Littile debugging aid for hierarchical IO scheduling.
o Enabled under CONFIG_DEBUG_GROUP_IOSCHED
o Currently it outputs more debug messages in blktrace output which helps
a great deal in debugging in hierarchical setup. It also creates additional
cgroup interfaces io.disk_queue and io.disk_dequeue to output some more
debugging data.
Signed-off-by: Vivek Goyal <vgoyal redhat com>
---
block/Kconfig.iosched | 10 ++-
block/elevator-fq.c | 298 ++++++++++++++++++++++++++++++++++++++++++++++++-
block/elevator-fq.h | 14 +++
3 files changed, 318 insertions(+), 4 deletions(-)
diff --git a/block/Kconfig.iosched b/block/Kconfig.iosched
index 0677099..79f188c 100644
--- a/block/Kconfig.iosched
+++ b/block/Kconfig.iosched
@@ -140,6 +140,14 @@ config TRACK_ASYNC_CONTEXT
request, original owner of the bio is decided by using io tracking
patches otherwise we continue to attribute the request to the
submitting thread.
-endmenu
+config DEBUG_GROUP_IOSCHED
+ bool "Debug Hierarchical Scheduling support"
+ depends on CGROUPS && GROUP_IOSCHED
+ default n
+ ---help---
+ Enable some debugging hooks for hierarchical scheduling support.
+ Currently it just outputs more information in blktrace output.
+
+endmenu
endif
diff --git a/block/elevator-fq.c b/block/elevator-fq.c
index 6d97ad9..6dd8683 100644
--- a/block/elevator-fq.c
+++ b/block/elevator-fq.c
@@ -115,6 +115,126 @@ static inline int iog_deleting(struct io_group *iog)
return iog->deleting;
}
+static inline struct io_group *io_entity_to_iog(struct io_entity *entity)
+{
+ struct io_group *iog = NULL;
+
+ BUG_ON(entity == NULL);
+ if (entity->my_sched_data != NULL)
+ iog = container_of(entity, struct io_group, entity);
+ return iog;
+}
+
+/* Returns parent group of io group */
+static inline struct io_group *iog_parent(struct io_group *iog)
+{
+ struct io_group *piog;
+
+ if (!iog->entity.sched_data)
+ return NULL;
+
+ /*
+ * Not following entity->parent pointer as for top level groups
+ * this pointer is NULL.
+ */
+ piog = container_of(iog->entity.sched_data, struct io_group,
+ sched_data);
+ return piog;
+}
+
+#ifdef CONFIG_DEBUG_GROUP_IOSCHED
+static void io_group_path(struct io_group *iog, char *buf, int buflen)
+{
+ unsigned short id = iog->iocg_id;
+ struct cgroup_subsys_state *css;
+
+ rcu_read_lock();
+
+ if (!id)
+ goto out;
+
+ css = css_lookup(&io_subsys, id);
+ if (!css)
+ goto out;
+
+ if (!css_tryget(css))
+ goto out;
+
+ cgroup_path(css->cgroup, buf, buflen);
+
+ css_put(css);
+
+ rcu_read_unlock();
+ return;
+out:
+ rcu_read_unlock();
+ buf[0] = '\0';
+ return;
+}
+
+/*
+ * An entity has been freshly added to active tree. Either it came from
+ * idle tree or it was not on any of the trees. Do the accounting.
+ */
+static inline void bfq_account_for_entity_addition(struct io_entity *entity)
+{
+ struct io_group *iog = io_entity_to_iog(entity);
+
+ if (iog) {
+ struct elv_fq_data *efqd;
+ char path[128];
+
+ /*
+ * Keep track of how many times a group has been removed
+ * from active tree because it did not have any active
+ * backlogged ioq under it
+ */
+ iog->queue++;
+ iog->queue_start = jiffies;
+
+ /* Log group addition event */
+ rcu_read_lock();
+ efqd = rcu_dereference(iog->key);
+ if (efqd) {
+ io_group_path(iog, path, sizeof(path));
+ elv_log(efqd, "add group=%s weight=%ld", path,
+ iog->entity.weight);
+ }
+ rcu_read_unlock();
+ }
+}
+
+/*
+ * An entity got removed from active tree and either went to idle tree or
+ * not is on any of the tree. Do the accouting
+ */
+static inline void bfq_account_for_entity_deletion(struct io_entity *entity)
+{
+ struct io_group *iog = io_entity_to_iog(entity);
+
+ if (iog) {
+ struct elv_fq_data *efqd;
+ char path[128];
+
+ iog->dequeue++;
+ /* Keep a track of how long group was on active tree */
+ iog->queue_duration += jiffies_to_msecs(jiffies -
+ iog->queue_start);
+ iog->queue_start = 0;
+
+ /* Log group deletion event */
+ rcu_read_lock();
+ efqd = rcu_dereference(iog->key);
+ if (efqd) {
+ io_group_path(iog, path, sizeof(path));
+ elv_log(efqd, "del group=%s weight=%ld", path,
+ iog->entity.weight);
+ }
+ rcu_read_unlock();
+ }
+}
+#endif
+
#else /* GROUP_IOSCHED */
#define for_each_entity(entity) \
for (; entity != NULL; entity = NULL)
@@ -137,6 +257,12 @@ static inline int iog_deleting(struct io_group *iog)
/* In flat mode, root cgroup can't be deleted. */
return 0;
}
+
+static inline struct io_group *io_entity_to_iog(struct io_entity *entity)
+{
+ return NULL;
+}
+
#endif
/*
@@ -570,6 +696,7 @@ static void __bfq_activate_entity(struct io_entity *entity, int add_front)
{
struct io_sched_data *sd = entity->sched_data;
struct io_service_tree *st = io_entity_service_tree(entity);
+ int newly_added = 0;
if (entity == sd->active_entity) {
BUG_ON(entity->tree != NULL);
@@ -596,6 +723,7 @@ static void __bfq_activate_entity(struct io_entity *entity, int add_front)
bfq_idle_extract(st, entity);
entity->start = bfq_gt(st->vtime, entity->finish) ?
st->vtime : entity->finish;
+ newly_added = 1;
} else {
/*
* The finish time of the entity may be invalid, and
@@ -608,6 +736,7 @@ static void __bfq_activate_entity(struct io_entity *entity, int add_front)
BUG_ON(entity->on_st);
entity->on_st = 1;
+ newly_added = 1;
}
st = __bfq_entity_update_prio(st, entity);
@@ -645,6 +774,10 @@ static void __bfq_activate_entity(struct io_entity *entity, int add_front)
bfq_calc_finish(entity, entity->budget);
}
bfq_active_insert(st, entity);
+#ifdef CONFIG_DEBUG_GROUP_IOSCHED
+ if (newly_added)
+ bfq_account_for_entity_addition(entity);
+#endif
}
/**
@@ -715,6 +848,9 @@ int __bfq_deactivate_entity(struct io_entity *entity, int requeue)
BUG_ON(sd->active_entity == entity);
BUG_ON(sd->next_active == entity);
+#ifdef CONFIG_DEBUG_GROUP_IOSCHED
+ bfq_account_for_entity_deletion(entity);
+#endif
return ret;
}
@@ -1205,6 +1341,67 @@ static int io_cgroup_disk_sectors_read(struct cgroup *cgroup,
return 0;
}
+#ifdef CONFIG_DEBUG_GROUP_IOSCHED
+static int io_cgroup_disk_queue_read(struct cgroup *cgroup,
+ struct cftype *cftype, struct seq_file *m)
+{
+ struct io_cgroup *iocg = NULL;
+ struct io_group *iog = NULL;
+ struct hlist_node *n;
+
+ if (!cgroup_lock_live_group(cgroup))
+ return -ENODEV;
+
+ iocg = cgroup_to_io_cgroup(cgroup);
+ spin_lock_irq(&iocg->lock);
+ /* Loop through all the io groups and print statistics */
+ hlist_for_each_entry_rcu(iog, n, &iocg->group_data, group_node) {
+ /*
+ * There might be groups which are not functional and
+ * waiting to be reclaimed upon cgoup deletion.
+ */
+ if (iog->key) {
+ seq_printf(m, "%u %u %lu %lu\n", MAJOR(iog->dev),
+ MINOR(iog->dev), iog->queue,
+ iog->queue_duration);
+ }
+ }
+ spin_unlock_irq(&iocg->lock);
+ cgroup_unlock();
+
+ return 0;
+}
+
+static int io_cgroup_disk_dequeue_read(struct cgroup *cgroup,
+ struct cftype *cftype, struct seq_file *m)
+{
+ struct io_cgroup *iocg = NULL;
+ struct io_group *iog = NULL;
+ struct hlist_node *n;
+
+ if (!cgroup_lock_live_group(cgroup))
+ return -ENODEV;
+
+ iocg = cgroup_to_io_cgroup(cgroup);
+ spin_lock_irq(&iocg->lock);
+ /* Loop through all the io groups and print statistics */
+ hlist_for_each_entry_rcu(iog, n, &iocg->group_data, group_node) {
+ /*
+ * There might be groups which are not functional and
+ * waiting to be reclaimed upon cgoup deletion.
+ */
+ if (iog->key) {
+ seq_printf(m, "%u %u %lu\n", MAJOR(iog->dev),
+ MINOR(iog->dev), iog->dequeue);
+ }
+ }
+ spin_unlock_irq(&iocg->lock);
+ cgroup_unlock();
+
+ return 0;
+}
+#endif
+
/**
* bfq_group_chain_alloc - allocate a chain of groups.
* @bfqd: queue descriptor.
@@ -1754,6 +1951,16 @@ struct cftype bfqio_files[] = {
.name = "disk_sectors",
.read_seq_string = io_cgroup_disk_sectors_read,
},
+#ifdef CONFIG_DEBUG_GROUP_IOSCHED
+ {
+ .name = "disk_queue",
+ .read_seq_string = io_cgroup_disk_queue_read,
+ },
+ {
+ .name = "disk_dequeue",
+ .read_seq_string = io_cgroup_disk_dequeue_read,
+ },
+#endif
};
int iocg_populate(struct cgroup_subsys *subsys, struct cgroup *cgroup)
@@ -2078,6 +2285,7 @@ struct cgroup_subsys io_subsys = {
.destroy = iocg_destroy,
.populate = iocg_populate,
.subsys_id = io_subsys_id,
+ .use_id = 1,
};
/*
@@ -2361,6 +2569,25 @@ EXPORT_SYMBOL(elv_get_slice_idle);
void elv_ioq_served(struct io_queue *ioq, bfq_service_t served)
{
entity_served(&ioq->entity, served, ioq->nr_sectors);
+
+#ifdef CONFIG_DEBUG_GROUP_IOSCHED
+ {
+ struct elv_fq_data *efqd = ioq->efqd;
+ char path[128];
+ struct io_group *iog = ioq_to_io_group(ioq);
+ io_group_path(iog, path, sizeof(path));
+ elv_log_ioq(efqd, ioq, "ioq served: QSt=0x%lx QSs=0x%lx"
+ " QTt=0x%lx QTs=0x%lx grp=%s GTt=0x%lx "
+ " GTs=0x%lx rq_queued=%d",
+ served, ioq->nr_sectors,
+ ioq->entity.total_service,
+ ioq->entity.total_sector_service,
+ path,
+ iog->entity.total_service,
+ iog->entity.total_sector_service,
+ ioq->nr_queued);
+ }
+#endif
}
/* Tells whether ioq is queued in root group or not */
@@ -2835,10 +3062,32 @@ static void __elv_set_active_ioq(struct elv_fq_data *efqd, struct io_queue *ioq,
if (ioq) {
struct io_group *iog = ioq_to_io_group(ioq);
elv_log_ioq(efqd, ioq, "set_active, busy=%d ioprio=%d"
- " weight=%ld group_weight=%ld",
+ " weight=%ld rq_queued=%d group_weight=%ld",
efqd->busy_queues,
ioq->entity.ioprio, ioq->entity.weight,
- iog_weight(iog));
+ ioq->nr_queued, iog_weight(iog));
+
+#ifdef CONFIG_DEBUG_GROUP_IOSCHED
+ {
+ char path[128];
+ int nr_active = 0;
+ struct io_group *parent = NULL;
+
+ parent = iog_parent(iog);
+ if (parent)
+ nr_active = elv_iog_nr_active(parent);
+
+ io_group_path(iog, path, sizeof(path));
+ elv_log_ioq(efqd, ioq, "set_active, ioq grp=%s"
+ " nrgrps=%d QTt=0x%lx QTs=0x%lx GTt=0x%lx "
+ " GTs=0x%lx rq_queued=%d", path, nr_active,
+ ioq->entity.total_service,
+ ioq->entity.total_sector_service,
+ iog->entity.total_service,
+ iog->entity.total_sector_service,
+ ioq->nr_queued);
+ }
+#endif
ioq->slice_end = 0;
elv_clear_ioq_wait_request(ioq);
@@ -2920,6 +3169,22 @@ void elv_add_ioq_busy(struct elv_fq_data *efqd, struct io_queue *ioq)
efqd->busy_queues++;
if (elv_ioq_class_rt(ioq))
efqd->busy_rt_queues++;
+
+#ifdef CONFIG_DEBUG_GROUP_IOSCHED
+ {
+ char path[128];
+ struct io_group *iog = ioq_to_io_group(ioq);
+ io_group_path(iog, path, sizeof(path));
+ elv_log(efqd, "add to busy: QTt=0x%lx QTs=0x%lx "
+ "ioq grp=%s GTt=0x%lx GTs=0x%lx rq_queued=%d",
+ ioq->entity.total_service,
+ ioq->entity.total_sector_service,
+ path,
+ iog->entity.total_service,
+ iog->entity.total_sector_service,
+ ioq->nr_queued);
+ }
+#endif
}
void elv_del_ioq_busy(struct elevator_queue *e, struct io_queue *ioq,
@@ -2929,7 +3194,24 @@ void elv_del_ioq_busy(struct elevator_queue *e, struct io_queue *ioq,
BUG_ON(!elv_ioq_busy(ioq));
BUG_ON(ioq->nr_queued);
+#ifdef CONFIG_DEBUG_GROUP_IOSCHED
+ {
+ char path[128];
+ struct io_group *iog = ioq_to_io_group(ioq);
+ io_group_path(iog, path, sizeof(path));
+ elv_log_ioq(efqd, ioq, "del from busy: QTt=0x%lx "
+ "QTs=0x%lx ioq grp=%s GTt=0x%lx GTs=0x%lx "
+ "rq_queued=%d",
+ ioq->entity.total_service,
+ ioq->entity.total_sector_service,
+ path,
+ iog->entity.total_service,
+ iog->entity.total_sector_service,
+ ioq->nr_queued);
+ }
+#else
elv_log_ioq(efqd, ioq, "del from busy");
+#endif
elv_clear_ioq_busy(ioq);
BUG_ON(efqd->busy_queues == 0);
efqd->busy_queues--;
@@ -3157,6 +3439,16 @@ void elv_ioq_request_add(struct request_queue *q, struct request *rq)
elv_ioq_update_io_thinktime(ioq);
elv_ioq_update_idle_window(q->elevator, ioq, rq);
+#ifdef CONFIG_DEBUG_GROUP_IOSCHED
+ {
+ char path[128];
+ struct io_group *iog = ioq_to_io_group(ioq);
+
+ io_group_path(iog, path, sizeof(path));
+ elv_log_ioq(efqd, ioq, "add rq: group path=%s "
+ "rq_queued=%d", path, ioq->nr_queued);
+ }
+#endif
if (ioq == elv_active_ioq(q->elevator)) {
/*
@@ -3364,7 +3656,7 @@ void *elv_fq_select_ioq(struct request_queue *q, int force)
}
/* We are waiting for this queue to become busy before it expires.*/
- if (efqd->fairness && elv_ioq_wait_busy(ioq)) {
+ if (elv_ioq_wait_busy(ioq)) {
ioq = NULL;
goto keep_queue;
}
diff --git a/block/elevator-fq.h b/block/elevator-fq.h
index 9062bfd..de0f6b0 100644
--- a/block/elevator-fq.h
+++ b/block/elevator-fq.h
@@ -249,6 +249,20 @@ struct io_group {
/* request list associated with the group */
struct request_list rl;
+
+#ifdef CONFIG_DEBUG_GROUP_IOSCHED
+ /* How many times this group has been added to active tree */
+ unsigned long queue;
+
+ /* How long this group remained on active tree, in ms */
+ unsigned long queue_duration;
+
+ /* When was this group added to active tree */
+ unsigned long queue_start;
+
+ /* How many times this group has been removed from active tree */
+ unsigned long dequeue;
+#endif
};
struct io_policy_node {
--
1.6.0.1
[Date Prev][Date Next] [Thread Prev][Thread Next]
[Thread Index]
[Date Index]
[Author Index]