|
| 1 | +From bd2dbbb1f3a05a5d77d18548d41cdb25c0b81912 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Steven Noonan <steven@edera.dev> |
| 3 | +Date: Tue, 19 May 2026 23:00:20 -0700 |
| 4 | +Subject: [PATCH 1/9] xen/events: add _on_node variants of the lateeoi bind |
| 5 | + helpers |
| 6 | + |
| 7 | +xen_allocate_irq_dynamic() unconditionally calls |
| 8 | +irq_alloc_desc_from(0, -1), so every Xen evtchn IRQ descriptor is |
| 9 | +allocated with NUMA_NO_NODE. This means /proc/irq/N/node always |
| 10 | +reads -1 even when the caller (netback, blkback, netfront, blkfront) |
| 11 | +already knows the right node for the IRQ. |
| 12 | + |
| 13 | +irqbalance treats node=-1 as "no NUMA preference" and distributes |
| 14 | +the IRQ across all CPUs for load balance, ignoring affinity_hint. |
| 15 | +With irqbalance running, the per-queue NUMA placement we install via |
| 16 | +irq_set_affinity_and_hint() gets overwritten almost immediately. |
| 17 | + |
| 18 | +Add _on_node variants of the four bind helpers Xen front/back ends |
| 19 | +use: |
| 20 | + |
| 21 | + bind_evtchn_to_irq_lateeoi_on_node(evtchn, node) |
| 22 | + bind_evtchn_to_irqhandler_lateeoi_on_node(..., node) |
| 23 | + bind_interdomain_evtchn_to_irq_lateeoi_on_node(..., node) |
| 24 | + bind_interdomain_evtchn_to_irqhandler_lateeoi_on_node(..., node) |
| 25 | + |
| 26 | +Each passes the caller's node through to the internal chip helpers |
| 27 | +and on to a new xen_allocate_irq_dynamic_node(node). The existing |
| 28 | +public functions become thin wrappers passing NUMA_NO_NODE, so every |
| 29 | +caller that hasn't been updated keeps today's behaviour. |
| 30 | + |
| 31 | +After this change, /proc/irq/N/node reflects the node the caller |
| 32 | +asked for, and irqbalance respects affinity_hint as a NUMA-local |
| 33 | +subset rather than treating the IRQ as floating. |
| 34 | + |
| 35 | +Signed-off-by: Steven Noonan <steven@edera.dev> |
| 36 | +--- |
| 37 | + drivers/xen/events/events_base.c | 86 ++++++++++++++++++++++++++------ |
| 38 | + include/xen/events.h | 15 ++++++ |
| 39 | + 2 files changed, 85 insertions(+), 16 deletions(-) |
| 40 | + |
| 41 | +diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c |
| 42 | +index 9478fae014e5..6368ff561472 100644 |
| 43 | +--- a/drivers/xen/events/events_base.c |
| 44 | ++++ b/drivers/xen/events/events_base.c |
| 45 | +@@ -28,6 +28,7 @@ |
| 46 | + #include <linux/interrupt.h> |
| 47 | + #include <linux/irq.h> |
| 48 | + #include <linux/moduleparam.h> |
| 49 | ++#include <linux/numa.h> |
| 50 | + #include <linux/string.h> |
| 51 | + #include <linux/memblock.h> |
| 52 | + #include <linux/slab.h> |
| 53 | +@@ -729,9 +730,9 @@ static struct irq_info *xen_irq_init(unsigned int irq) |
| 54 | + return info; |
| 55 | + } |
| 56 | + |
| 57 | +-static struct irq_info *xen_allocate_irq_dynamic(void) |
| 58 | ++static struct irq_info *xen_allocate_irq_dynamic_node(int node) |
| 59 | + { |
| 60 | +- int irq = irq_alloc_desc_from(0, -1); |
| 61 | ++ int irq = irq_alloc_desc_from(0, node); |
| 62 | + struct irq_info *info = NULL; |
| 63 | + |
| 64 | + if (irq >= 0) { |
| 65 | +@@ -743,6 +744,11 @@ static struct irq_info *xen_allocate_irq_dynamic(void) |
| 66 | + return info; |
| 67 | + } |
| 68 | + |
| 69 | ++static struct irq_info *xen_allocate_irq_dynamic(void) |
| 70 | ++{ |
| 71 | ++ return xen_allocate_irq_dynamic_node(NUMA_NO_NODE); |
| 72 | ++} |
| 73 | ++ |
| 74 | + static struct irq_info *xen_allocate_irq_gsi(unsigned int gsi) |
| 75 | + { |
| 76 | + int irq; |
| 77 | +@@ -1184,7 +1190,8 @@ int xen_pirq_from_irq(unsigned irq) |
| 78 | + EXPORT_SYMBOL_GPL(xen_pirq_from_irq); |
| 79 | + |
| 80 | + static int bind_evtchn_to_irq_chip(evtchn_port_t evtchn, struct irq_chip *chip, |
| 81 | +- struct xenbus_device *dev, bool shared) |
| 82 | ++ struct xenbus_device *dev, bool shared, |
| 83 | ++ int node) |
| 84 | + { |
| 85 | + int ret = -ENOMEM; |
| 86 | + struct irq_info *info; |
| 87 | +@@ -1197,7 +1204,7 @@ static int bind_evtchn_to_irq_chip(evtchn_port_t evtchn, struct irq_chip *chip, |
| 88 | + info = evtchn_to_info(evtchn); |
| 89 | + |
| 90 | + if (!info) { |
| 91 | +- info = xen_allocate_irq_dynamic(); |
| 92 | ++ info = xen_allocate_irq_dynamic_node(node); |
| 93 | + if (!info) |
| 94 | + goto out; |
| 95 | + |
| 96 | +@@ -1232,16 +1239,25 @@ static int bind_evtchn_to_irq_chip(evtchn_port_t evtchn, struct irq_chip *chip, |
| 97 | + |
| 98 | + int bind_evtchn_to_irq(evtchn_port_t evtchn) |
| 99 | + { |
| 100 | +- return bind_evtchn_to_irq_chip(evtchn, &xen_dynamic_chip, NULL, false); |
| 101 | ++ return bind_evtchn_to_irq_chip(evtchn, &xen_dynamic_chip, NULL, false, |
| 102 | ++ NUMA_NO_NODE); |
| 103 | + } |
| 104 | + EXPORT_SYMBOL_GPL(bind_evtchn_to_irq); |
| 105 | + |
| 106 | + int bind_evtchn_to_irq_lateeoi(evtchn_port_t evtchn) |
| 107 | + { |
| 108 | +- return bind_evtchn_to_irq_chip(evtchn, &xen_lateeoi_chip, NULL, false); |
| 109 | ++ return bind_evtchn_to_irq_chip(evtchn, &xen_lateeoi_chip, NULL, false, |
| 110 | ++ NUMA_NO_NODE); |
| 111 | + } |
| 112 | + EXPORT_SYMBOL_GPL(bind_evtchn_to_irq_lateeoi); |
| 113 | + |
| 114 | ++int bind_evtchn_to_irq_lateeoi_on_node(evtchn_port_t evtchn, int node) |
| 115 | ++{ |
| 116 | ++ return bind_evtchn_to_irq_chip(evtchn, &xen_lateeoi_chip, NULL, false, |
| 117 | ++ node); |
| 118 | ++} |
| 119 | ++EXPORT_SYMBOL_GPL(bind_evtchn_to_irq_lateeoi_on_node); |
| 120 | ++ |
| 121 | + static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu) |
| 122 | + { |
| 123 | + struct evtchn_bind_ipi bind_ipi; |
| 124 | +@@ -1291,7 +1307,7 @@ static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu) |
| 125 | + static int bind_interdomain_evtchn_to_irq_chip(struct xenbus_device *dev, |
| 126 | + evtchn_port_t remote_port, |
| 127 | + struct irq_chip *chip, |
| 128 | +- bool shared) |
| 129 | ++ bool shared, int node) |
| 130 | + { |
| 131 | + struct evtchn_bind_interdomain bind_interdomain; |
| 132 | + int err; |
| 133 | +@@ -1303,17 +1319,28 @@ static int bind_interdomain_evtchn_to_irq_chip(struct xenbus_device *dev, |
| 134 | + &bind_interdomain); |
| 135 | + |
| 136 | + return err ? : bind_evtchn_to_irq_chip(bind_interdomain.local_port, |
| 137 | +- chip, dev, shared); |
| 138 | ++ chip, dev, shared, node); |
| 139 | + } |
| 140 | + |
| 141 | + int bind_interdomain_evtchn_to_irq_lateeoi(struct xenbus_device *dev, |
| 142 | + evtchn_port_t remote_port) |
| 143 | + { |
| 144 | + return bind_interdomain_evtchn_to_irq_chip(dev, remote_port, |
| 145 | +- &xen_lateeoi_chip, false); |
| 146 | ++ &xen_lateeoi_chip, false, |
| 147 | ++ NUMA_NO_NODE); |
| 148 | + } |
| 149 | + EXPORT_SYMBOL_GPL(bind_interdomain_evtchn_to_irq_lateeoi); |
| 150 | + |
| 151 | ++int bind_interdomain_evtchn_to_irq_lateeoi_on_node(struct xenbus_device *dev, |
| 152 | ++ evtchn_port_t remote_port, |
| 153 | ++ int node) |
| 154 | ++{ |
| 155 | ++ return bind_interdomain_evtchn_to_irq_chip(dev, remote_port, |
| 156 | ++ &xen_lateeoi_chip, false, |
| 157 | ++ node); |
| 158 | ++} |
| 159 | ++EXPORT_SYMBOL_GPL(bind_interdomain_evtchn_to_irq_lateeoi_on_node); |
| 160 | ++ |
| 161 | + static int find_virq(unsigned int virq, unsigned int cpu, evtchn_port_t *evtchn, |
| 162 | + bool percpu) |
| 163 | + { |
| 164 | +@@ -1432,12 +1459,12 @@ static int bind_evtchn_to_irqhandler_chip(evtchn_port_t evtchn, |
| 165 | + irq_handler_t handler, |
| 166 | + unsigned long irqflags, |
| 167 | + const char *devname, void *dev_id, |
| 168 | +- struct irq_chip *chip) |
| 169 | ++ struct irq_chip *chip, int node) |
| 170 | + { |
| 171 | + int irq, retval; |
| 172 | + |
| 173 | + irq = bind_evtchn_to_irq_chip(evtchn, chip, NULL, |
| 174 | +- irqflags & IRQF_SHARED); |
| 175 | ++ irqflags & IRQF_SHARED, node); |
| 176 | + if (irq < 0) |
| 177 | + return irq; |
| 178 | + retval = request_irq(irq, handler, irqflags, devname, dev_id); |
| 179 | +@@ -1456,7 +1483,8 @@ int bind_evtchn_to_irqhandler(evtchn_port_t evtchn, |
| 180 | + { |
| 181 | + return bind_evtchn_to_irqhandler_chip(evtchn, handler, irqflags, |
| 182 | + devname, dev_id, |
| 183 | +- &xen_dynamic_chip); |
| 184 | ++ &xen_dynamic_chip, |
| 185 | ++ NUMA_NO_NODE); |
| 186 | + } |
| 187 | + EXPORT_SYMBOL_GPL(bind_evtchn_to_irqhandler); |
| 188 | + |
| 189 | +@@ -1467,19 +1495,34 @@ int bind_evtchn_to_irqhandler_lateeoi(evtchn_port_t evtchn, |
| 190 | + { |
| 191 | + return bind_evtchn_to_irqhandler_chip(evtchn, handler, irqflags, |
| 192 | + devname, dev_id, |
| 193 | +- &xen_lateeoi_chip); |
| 194 | ++ &xen_lateeoi_chip, |
| 195 | ++ NUMA_NO_NODE); |
| 196 | + } |
| 197 | + EXPORT_SYMBOL_GPL(bind_evtchn_to_irqhandler_lateeoi); |
| 198 | + |
| 199 | ++int bind_evtchn_to_irqhandler_lateeoi_on_node(evtchn_port_t evtchn, |
| 200 | ++ irq_handler_t handler, |
| 201 | ++ unsigned long irqflags, |
| 202 | ++ const char *devname, |
| 203 | ++ void *dev_id, int node) |
| 204 | ++{ |
| 205 | ++ return bind_evtchn_to_irqhandler_chip(evtchn, handler, irqflags, |
| 206 | ++ devname, dev_id, |
| 207 | ++ &xen_lateeoi_chip, node); |
| 208 | ++} |
| 209 | ++EXPORT_SYMBOL_GPL(bind_evtchn_to_irqhandler_lateeoi_on_node); |
| 210 | ++ |
| 211 | + static int bind_interdomain_evtchn_to_irqhandler_chip( |
| 212 | + struct xenbus_device *dev, evtchn_port_t remote_port, |
| 213 | + irq_handler_t handler, unsigned long irqflags, |
| 214 | +- const char *devname, void *dev_id, struct irq_chip *chip) |
| 215 | ++ const char *devname, void *dev_id, struct irq_chip *chip, |
| 216 | ++ int node) |
| 217 | + { |
| 218 | + int irq, retval; |
| 219 | + |
| 220 | + irq = bind_interdomain_evtchn_to_irq_chip(dev, remote_port, chip, |
| 221 | +- irqflags & IRQF_SHARED); |
| 222 | ++ irqflags & IRQF_SHARED, |
| 223 | ++ node); |
| 224 | + if (irq < 0) |
| 225 | + return irq; |
| 226 | + |
| 227 | +@@ -1501,10 +1544,21 @@ int bind_interdomain_evtchn_to_irqhandler_lateeoi(struct xenbus_device *dev, |
| 228 | + { |
| 229 | + return bind_interdomain_evtchn_to_irqhandler_chip(dev, |
| 230 | + remote_port, handler, irqflags, devname, |
| 231 | +- dev_id, &xen_lateeoi_chip); |
| 232 | ++ dev_id, &xen_lateeoi_chip, NUMA_NO_NODE); |
| 233 | + } |
| 234 | + EXPORT_SYMBOL_GPL(bind_interdomain_evtchn_to_irqhandler_lateeoi); |
| 235 | + |
| 236 | ++int bind_interdomain_evtchn_to_irqhandler_lateeoi_on_node( |
| 237 | ++ struct xenbus_device *dev, evtchn_port_t remote_port, |
| 238 | ++ irq_handler_t handler, unsigned long irqflags, |
| 239 | ++ const char *devname, void *dev_id, int node) |
| 240 | ++{ |
| 241 | ++ return bind_interdomain_evtchn_to_irqhandler_chip(dev, |
| 242 | ++ remote_port, handler, irqflags, devname, |
| 243 | ++ dev_id, &xen_lateeoi_chip, node); |
| 244 | ++} |
| 245 | ++EXPORT_SYMBOL_GPL(bind_interdomain_evtchn_to_irqhandler_lateeoi_on_node); |
| 246 | ++ |
| 247 | + int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu, |
| 248 | + irq_handler_t handler, |
| 249 | + unsigned long irqflags, const char *devname, void *dev_id) |
| 250 | +diff --git a/include/xen/events.h b/include/xen/events.h |
| 251 | +index de5da58a0205..1abc068557b4 100644 |
| 252 | +--- a/include/xen/events.h |
| 253 | ++++ b/include/xen/events.h |
| 254 | +@@ -18,6 +18,7 @@ unsigned xen_evtchn_nr_channels(void); |
| 255 | + |
| 256 | + int bind_evtchn_to_irq(evtchn_port_t evtchn); |
| 257 | + int bind_evtchn_to_irq_lateeoi(evtchn_port_t evtchn); |
| 258 | ++int bind_evtchn_to_irq_lateeoi_on_node(evtchn_port_t evtchn, int node); |
| 259 | + int bind_evtchn_to_irqhandler(evtchn_port_t evtchn, |
| 260 | + irq_handler_t handler, |
| 261 | + unsigned long irqflags, const char *devname, |
| 262 | +@@ -26,6 +27,10 @@ int bind_evtchn_to_irqhandler_lateeoi(evtchn_port_t evtchn, |
| 263 | + irq_handler_t handler, |
| 264 | + unsigned long irqflags, const char *devname, |
| 265 | + void *dev_id); |
| 266 | ++int bind_evtchn_to_irqhandler_lateeoi_on_node(evtchn_port_t evtchn, |
| 267 | ++ irq_handler_t handler, |
| 268 | ++ unsigned long irqflags, const char *devname, |
| 269 | ++ void *dev_id, int node); |
| 270 | + int bind_virq_to_irq(unsigned int virq, unsigned int cpu, bool percpu); |
| 271 | + int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu, |
| 272 | + irq_handler_t handler, |
| 273 | +@@ -39,12 +44,22 @@ int bind_ipi_to_irqhandler(enum ipi_vector ipi, |
| 274 | + void *dev_id); |
| 275 | + int bind_interdomain_evtchn_to_irq_lateeoi(struct xenbus_device *dev, |
| 276 | + evtchn_port_t remote_port); |
| 277 | ++int bind_interdomain_evtchn_to_irq_lateeoi_on_node(struct xenbus_device *dev, |
| 278 | ++ evtchn_port_t remote_port, |
| 279 | ++ int node); |
| 280 | + int bind_interdomain_evtchn_to_irqhandler_lateeoi(struct xenbus_device *dev, |
| 281 | + evtchn_port_t remote_port, |
| 282 | + irq_handler_t handler, |
| 283 | + unsigned long irqflags, |
| 284 | + const char *devname, |
| 285 | + void *dev_id); |
| 286 | ++int bind_interdomain_evtchn_to_irqhandler_lateeoi_on_node( |
| 287 | ++ struct xenbus_device *dev, |
| 288 | ++ evtchn_port_t remote_port, |
| 289 | ++ irq_handler_t handler, |
| 290 | ++ unsigned long irqflags, |
| 291 | ++ const char *devname, |
| 292 | ++ void *dev_id, int node); |
| 293 | + |
| 294 | + /* |
| 295 | + * Common unbind function for all event sources. Takes IRQ to unbind from. |
| 296 | +-- |
| 297 | +2.54.0 |
| 298 | + |
0 commit comments