|
@@ -350,3 +350,161 @@ struct request_list *__blk_queue_next_rl(struct request_list *rl,
|
|
|
|
|
|
blkg = container_of(ent, struct blkcg_gq, q_node);
|
|
|
return &blkg->rl;
|
|
|
+}
|
|
|
+
|
|
|
+static int blkcg_reset_stats(struct cgroup *cgroup, struct cftype *cftype,
|
|
|
+ u64 val)
|
|
|
+{
|
|
|
+ struct blkcg *blkcg = cgroup_to_blkcg(cgroup);
|
|
|
+ struct blkcg_gq *blkg;
|
|
|
+ struct hlist_node *n;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ mutex_lock(&blkcg_pol_mutex);
|
|
|
+ spin_lock_irq(&blkcg->lock);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Note that stat reset is racy - it doesn't synchronize against
|
|
|
+ * stat updates. This is a debug feature which shouldn't exist
|
|
|
+ * anyway. If you get hit by a race, retry.
|
|
|
+ */
|
|
|
+ hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node) {
|
|
|
+ for (i = 0; i < BLKCG_MAX_POLS; i++) {
|
|
|
+ struct blkcg_policy *pol = blkcg_policy[i];
|
|
|
+
|
|
|
+ if (blkcg_policy_enabled(blkg->q, pol) &&
|
|
|
+ pol->pd_reset_stats_fn)
|
|
|
+ pol->pd_reset_stats_fn(blkg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_unlock_irq(&blkcg->lock);
|
|
|
+ mutex_unlock(&blkcg_pol_mutex);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const char *blkg_dev_name(struct blkcg_gq *blkg)
|
|
|
+{
|
|
|
+ /* some drivers (floppy) instantiate a queue w/o disk registered */
|
|
|
+ if (blkg->q->backing_dev_info.dev)
|
|
|
+ return dev_name(blkg->q->backing_dev_info.dev);
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * blkcg_print_blkgs - helper for printing per-blkg data
|
|
|
+ * @sf: seq_file to print to
|
|
|
+ * @blkcg: blkcg of interest
|
|
|
+ * @prfill: fill function to print out a blkg
|
|
|
+ * @pol: policy in question
|
|
|
+ * @data: data to be passed to @prfill
|
|
|
+ * @show_total: to print out sum of prfill return values or not
|
|
|
+ *
|
|
|
+ * This function invokes @prfill on each blkg of @blkcg if pd for the
|
|
|
+ * policy specified by @pol exists. @prfill is invoked with @sf, the
|
|
|
+ * policy data and @data. If @show_total is %true, the sum of the return
|
|
|
+ * values from @prfill is printed with "Total" label at the end.
|
|
|
+ *
|
|
|
+ * This is to be used to construct print functions for
|
|
|
+ * cftype->read_seq_string method.
|
|
|
+ */
|
|
|
+void blkcg_print_blkgs(struct seq_file *sf, struct blkcg *blkcg,
|
|
|
+ u64 (*prfill)(struct seq_file *,
|
|
|
+ struct blkg_policy_data *, int),
|
|
|
+ const struct blkcg_policy *pol, int data,
|
|
|
+ bool show_total)
|
|
|
+{
|
|
|
+ struct blkcg_gq *blkg;
|
|
|
+ struct hlist_node *n;
|
|
|
+ u64 total = 0;
|
|
|
+
|
|
|
+ spin_lock_irq(&blkcg->lock);
|
|
|
+ hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node)
|
|
|
+ if (blkcg_policy_enabled(blkg->q, pol))
|
|
|
+ total += prfill(sf, blkg->pd[pol->plid], data);
|
|
|
+ spin_unlock_irq(&blkcg->lock);
|
|
|
+
|
|
|
+ if (show_total)
|
|
|
+ seq_printf(sf, "Total %llu\n", (unsigned long long)total);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(blkcg_print_blkgs);
|
|
|
+
|
|
|
+/**
|
|
|
+ * __blkg_prfill_u64 - prfill helper for a single u64 value
|
|
|
+ * @sf: seq_file to print to
|
|
|
+ * @pd: policy private data of interest
|
|
|
+ * @v: value to print
|
|
|
+ *
|
|
|
+ * Print @v to @sf for the device assocaited with @pd.
|
|
|
+ */
|
|
|
+u64 __blkg_prfill_u64(struct seq_file *sf, struct blkg_policy_data *pd, u64 v)
|
|
|
+{
|
|
|
+ const char *dname = blkg_dev_name(pd->blkg);
|
|
|
+
|
|
|
+ if (!dname)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ seq_printf(sf, "%s %llu\n", dname, (unsigned long long)v);
|
|
|
+ return v;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(__blkg_prfill_u64);
|
|
|
+
|
|
|
+/**
|
|
|
+ * __blkg_prfill_rwstat - prfill helper for a blkg_rwstat
|
|
|
+ * @sf: seq_file to print to
|
|
|
+ * @pd: policy private data of interest
|
|
|
+ * @rwstat: rwstat to print
|
|
|
+ *
|
|
|
+ * Print @rwstat to @sf for the device assocaited with @pd.
|
|
|
+ */
|
|
|
+u64 __blkg_prfill_rwstat(struct seq_file *sf, struct blkg_policy_data *pd,
|
|
|
+ const struct blkg_rwstat *rwstat)
|
|
|
+{
|
|
|
+ static const char *rwstr[] = {
|
|
|
+ [BLKG_RWSTAT_READ] = "Read",
|
|
|
+ [BLKG_RWSTAT_WRITE] = "Write",
|
|
|
+ [BLKG_RWSTAT_SYNC] = "Sync",
|
|
|
+ [BLKG_RWSTAT_ASYNC] = "Async",
|
|
|
+ };
|
|
|
+ const char *dname = blkg_dev_name(pd->blkg);
|
|
|
+ u64 v;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (!dname)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ for (i = 0; i < BLKG_RWSTAT_NR; i++)
|
|
|
+ seq_printf(sf, "%s %s %llu\n", dname, rwstr[i],
|
|
|
+ (unsigned long long)rwstat->cnt[i]);
|
|
|
+
|
|
|
+ v = rwstat->cnt[BLKG_RWSTAT_READ] + rwstat->cnt[BLKG_RWSTAT_WRITE];
|
|
|
+ seq_printf(sf, "%s Total %llu\n", dname, (unsigned long long)v);
|
|
|
+ return v;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * blkg_prfill_stat - prfill callback for blkg_stat
|
|
|
+ * @sf: seq_file to print to
|
|
|
+ * @pd: policy private data of interest
|
|
|
+ * @off: offset to the blkg_stat in @pd
|
|
|
+ *
|
|
|
+ * prfill callback for printing a blkg_stat.
|
|
|
+ */
|
|
|
+u64 blkg_prfill_stat(struct seq_file *sf, struct blkg_policy_data *pd, int off)
|
|
|
+{
|
|
|
+ return __blkg_prfill_u64(sf, pd, blkg_stat_read((void *)pd + off));
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(blkg_prfill_stat);
|
|
|
+
|
|
|
+/**
|
|
|
+ * blkg_prfill_rwstat - prfill callback for blkg_rwstat
|
|
|
+ * @sf: seq_file to print to
|
|
|
+ * @pd: policy private data of interest
|
|
|
+ * @off: offset to the blkg_rwstat in @pd
|
|
|
+ *
|
|
|
+ * prfill callback for printing a blkg_rwstat.
|
|
|
+ */
|
|
|
+u64 blkg_prfill_rwstat(struct seq_file *sf, struct blkg_policy_data *pd,
|
|
|
+ int off)
|
|
|
+{
|
|
|
+ struct blkg_rwstat rwstat = blkg_rwstat_read((void *)pd + off);
|