Skip to contentSkip to conversation

blk-mq: User defined HCTX CPU mapping

RFC PATCH In review Keith Busch 3 participants 3 messages Jun 18, 2018 – Jun 20, 2018

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 blk-mq: User defined HCTX CPU mapping

The default mapping of a cpu to a hardware context is often generally applicable, however a user may know of a more appropriate mapping for their specific access usage.

This patch allows a user to define their own policy by making the mq hctx cpu_list writable. The usage allows a user to append a comma separated and/or range list of CPUs to a given hctx's tag set mapping to reassign what hctx a cpu may map.

While the writable attribute exists under a specific request_queue, the settings will affect all request queues sharing the same tagset.

The user defined setting is lost if the block device is removed and re-added, or if the driver re-runs the queue mapping.

---
 block/blk-mq-debugfs.c | 16 ++++++----
 block/blk-mq-debugfs.h | 11 +++++++
 block/blk-mq-sysfs.c   | 80 +++++++++++++++++++++++++++++++++++++++++++++++++-
 block/blk-mq.c         |  9 ------
 block/blk-mq.h         | 12 ++++++++
 5 files changed, 112 insertions(+), 16 deletions(-)
block/blk-mq-debugfs.c +10 -6
index ffa622366922..df163a79511c 100644
@@ -870,18 +870,22 @@ void blk_mq_debugfs_unregister(struct request_queue *q)
 	q->debugfs_dir = NULL;
 }
 
-static int blk_mq_debugfs_register_ctx(struct blk_mq_hw_ctx *hctx,
-				       struct blk_mq_ctx *ctx)
+void blk_mq_debugfs_unregister_ctx(struct blk_mq_ctx *ctx)
+{
+	debugfs_remove_recursive(ctx->debugfs_dir);
+}
+
+int blk_mq_debugfs_register_ctx(struct blk_mq_hw_ctx *hctx,
+				struct blk_mq_ctx *ctx)
 {
-	struct dentry *ctx_dir;
 	char name[20];
 
 	snprintf(name, sizeof(name), "cpu%u", ctx->cpu);
-	ctx_dir = debugfs_create_dir(name, hctx->debugfs_dir);
-	if (!ctx_dir)
+	ctx->debugfs_dir = debugfs_create_dir(name, hctx->debugfs_dir);
+	if (!ctx->debugfs_dir)
 		return -ENOMEM;
 
-	if (!debugfs_create_files(ctx_dir, ctx, blk_mq_debugfs_ctx_attrs))
+	if (!debugfs_create_files(ctx->debugfs_dir, ctx, blk_mq_debugfs_ctx_attrs))
 		return -ENOMEM;
 
 	return 0;
block/blk-mq-debugfs.h +11 -0
index b9d366e57097..93df02eabf2b 100644
@@ -18,6 +18,9 @@ struct blk_mq_debugfs_attr {
 int __blk_mq_debugfs_rq_show(struct seq_file *m, struct request *rq);
 int blk_mq_debugfs_rq_show(struct seq_file *m, void *v);
 
+void blk_mq_debugfs_unregister_ctx(struct blk_mq_ctx *ctx);
+int blk_mq_debugfs_register_ctx(struct blk_mq_hw_ctx *hctx,
+				struct blk_mq_ctx *ctx);
 int blk_mq_debugfs_register(struct request_queue *q);
 void blk_mq_debugfs_unregister(struct request_queue *q);
 int blk_mq_debugfs_register_hctx(struct request_queue *q,
@@ -41,6 +44,14 @@ static inline void blk_mq_debugfs_unregister(struct request_queue *q)
 {
 }
 
+void blk_mq_debugfs_unregister_ctx(struct blk_mq_ctx *ctx) {}
+
+int blk_mq_debugfs_register_ctx(struct blk_mq_hw_ctx *hctx,
+				struct blk_mq_ctx *ctx)
+{
+	return 0;
+}
+
 static inline int blk_mq_debugfs_register_hctx(struct request_queue *q,
 					       struct blk_mq_hw_ctx *hctx)
 {
block/blk-mq-sysfs.c +79 -1

99 lines not on this page · raw message

block/blk-mq.c +0 -9
index d2de0a719ab8..a8dde5d70eb6 100644
@@ -1248,15 +1248,6 @@ static void __blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx)
 	hctx_unlock(hctx, srcu_idx);
 }
 
-static inline int blk_mq_first_mapped_cpu(struct blk_mq_hw_ctx *hctx)
-{
-	int cpu = cpumask_first_and(hctx->cpumask, cpu_online_mask);
-
-	if (cpu >= nr_cpu_ids)
-		cpu = cpumask_first(hctx->cpumask);
-	return cpu;
-}
-
 /*
  * It'd be great if the workqueue API had a way to pass
  * in a mask and had some smarts for more clever placement.
block/blk-mq.h +12 -0
index 89231e439b2f..34dc0baf62cc 100644
@@ -28,6 +28,9 @@ struct blk_mq_ctx {
 
 	struct request_queue	*queue;
 	struct kobject		kobj;
+#ifdef CONFIG_BLK_DEBUG_FS
+	struct dentry		*debugfs_dir;
+#endif
 } ____cacheline_aligned_in_smp;
 
 void blk_mq_freeze_queue(struct request_queue *q);
@@ -203,4 +206,13 @@ static inline void blk_mq_put_driver_tag(struct request *rq)
 	__blk_mq_put_driver_tag(hctx, rq);
 }
 
+static inline int blk_mq_first_mapped_cpu(struct blk_mq_hw_ctx *hctx)
+{
+	int cpu = cpumask_first_and(hctx->cpumask, cpu_online_mask);
+
+	if (cpu >= nr_cpu_ids)
+		cpu = cpumask_first(hctx->cpumask);
+	return cpu;
+}
+
 #endif
Signature · 1 line
2.14.3
#1 lore raw
Quoted 9 lines · Keith Busch

The default mapping of a cpu to a hardware context is often generally applicable, however a user may know of a more appropriate mapping for their specific access usage.

This patch allows a user to define their own policy by making the mq hctx cpu_list writable. The usage allows a user to append a comma separated and/or range list of CPUs to a given hctx's tag set mapping to reassign what hctx a cpu may map.

Quoted 5 lines

While the writable attribute exists under a specific request_queue, the settings will affect all request queues sharing the same tagset.

The user defined setting is lost if the block device is removed and re-added, or if the driver re-runs the queue mapping.

We can't do this without driver opt-in. Managed interrupt rely on the fact that we can't generate more interrupts once all cpus mapped to the interrupt line have been offlined.

So what exactly is the use case? What drivers do you care about?

#2 lore raw
Quoted 13 lines · Christoph Hellwig
Quoted 12 lines · Keith Busch

The default mapping of a cpu to a hardware context is often generally applicable, however a user may know of a more appropriate mapping for their specific access usage.

This patch allows a user to define their own policy by making the mq hctx cpu_list writable. The usage allows a user to append a comma separated and/or range list of CPUs to a given hctx's tag set mapping to reassign what hctx a cpu may map.

While the writable attribute exists under a specific request_queue, the settings will affect all request queues sharing the same tagset.

Quoted 8 lines
Quoted 2 lines

The user defined setting is lost if the block device is removed and re-added, or if the driver re-runs the queue mapping.

We can't do this without driver opt-in. Managed interrupt rely on the fact that we can't generate more interrupts once all cpus mapped to the interrupt line have been offlined.

So what exactly is the use case? What drivers do you care about?

This patch came at a customer request for NVMe. The controllers have 1:1 queues to CPUs, so currently a submission on CPU A will interrupt CPU A.

The user really wants their application to run in CPU A and have the interrupt run in CPU B. We can't change the IRQ affinity, so I thought changing the submission affinity would be less intrusive.

I think you're saying this will break if CPU B is offlined. I hadn't considered that, so it doesn't sound like this will work.