@@ -15,6 +15,7 @@
#include <linux/blkdev.h>
#include "elevator-fq.h"
#include <linux/blktrace_api.h>
+#include <linux/seq_file.h>
/* Values taken from cfq */
const int elv_slice_sync = HZ / 10;
@@ -971,13 +972,16 @@ update:
}
}
-static void entity_served(struct io_entity *entity, unsigned long served)
+void entity_served(struct io_entity *entity, unsigned long served,
+ unsigned long nr_sectors)
{
struct io_service_tree *st;
for_each_entity(entity) {
st = io_entity_service_tree(entity);
entity->service += served;
+ entity->total_service += served;
+ entity->total_sector_service += nr_sectors;
BUG_ON(st->wsum == 0);
st->vtime += bfq_delta(served, st->wsum);
bfq_forget_idle(st);
@@ -1140,6 +1144,66 @@ STORE_FUNCTION(weight, 1, WEIGHT_MAX);
STORE_FUNCTION(ioprio_class, IOPRIO_CLASS_RT, IOPRIO_CLASS_IDLE);
#undef STORE_FUNCTION
+static int io_cgroup_disk_time_read(struct cgroup *cgroup,
+ struct cftype *cftype, struct seq_file *m)
+{
+ struct io_cgroup *iocg;
+ struct io_group *iog;
+ struct hlist_node *n;
+
+ if (!cgroup_lock_live_group(cgroup))
+ return -ENODEV;
+
+ iocg = cgroup_to_io_cgroup(cgroup);
+
+ rcu_read_lock();
+ 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->entity.total_service);
+ }
+ }
+ rcu_read_unlock();
+ cgroup_unlock();
+
+ return 0;
+}
+
+static int io_cgroup_disk_sectors_read(struct cgroup *cgroup,
+ struct cftype *cftype, struct seq_file *m)
+{
+ struct io_cgroup *iocg;
+ struct io_group *iog;
+ struct hlist_node *n;
+
+ if (!cgroup_lock_live_group(cgroup))
+ return -ENODEV;
+
+ iocg = cgroup_to_io_cgroup(cgroup);
+
+ rcu_read_lock();
+ 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->entity.total_sector_service);
+ }
+ }
+ rcu_read_unlock();
+ cgroup_unlock();
+
+ return 0;
+}
+
struct cftype bfqio_files[] = {
{
.name = "weight",
@@ -1151,6 +1215,14 @@ struct cftype bfqio_files[] = {
.read_u64 = io_cgroup_ioprio_class_read,
.write_u64 = io_cgroup_ioprio_class_write,
},
+ {
+ .name = "disk_time",
+ .read_seq_string = io_cgroup_disk_time_read,
+ },
+ {
+ .name = "disk_sectors",
+ .read_seq_string = io_cgroup_disk_sectors_read,
+ },
};
static int iocg_populate(struct cgroup_subsys *subsys, struct cgroup *cgroup)
@@ -1252,6 +1324,8 @@ io_group_chain_alloc(struct request_queue *q, void *key, struct cgroup *cgroup)
struct io_cgroup *iocg;
struct io_group *iog, *leaf = NULL, *prev = NULL;
gfp_t flags = GFP_ATOMIC | __GFP_ZERO;
+ unsigned int major, minor;
+ struct backing_dev_info *bdi = &q->backing_dev_info;
for (; cgroup != NULL; cgroup = cgroup->parent) {
iocg = cgroup_to_io_cgroup(cgroup);
@@ -1272,6 +1346,9 @@ io_group_chain_alloc(struct request_queue *q, void *key, struct cgroup *cgroup)
iog->iocg_id = css_id(&iocg->css);
+ sscanf(dev_name(bdi->dev), "%u:%u", &major, &minor);
+ iog->dev = MKDEV(major, minor);
+
io_group_init_entity(iocg, iog);
iog->my_entity = &iog->entity;
@@ -1873,7 +1950,7 @@ EXPORT_SYMBOL(elv_get_slice_idle);
static void elv_ioq_served(struct io_queue *ioq, unsigned long served)
{
- entity_served(&ioq->entity, served);
+ entity_served(&ioq->entity, served, ioq->nr_sectors);
}
/* Tells whether ioq is queued in root group or not */
@@ -147,6 +147,13 @@ struct io_entity {
unsigned short ioprio_class, new_ioprio_class;
int ioprio_changed;
+
+ /*
+ * Keep track of total service received by this entity. Keep the
+ * stats both for time slices and number of sectors dispatched
+ */
+ unsigned long total_service;
+ unsigned long total_sector_service;
};
/*
@@ -244,6 +251,9 @@ struct io_group {
int deleting;
unsigned short iocg_id;
+
+ /* The device MKDEV(major, minor), this group has been created for */
+ dev_t dev;
};
/**