Skip to content

Commit 16844fb

Browse files
authored
Merge pull request #361 from suresh-thelkar/fix/slot-aware-placement-followups
Slot-aware placement follow-ups: tie-breaker, ENOSPC saturation marker, and BALANCE_NONE policy fix
2 parents e2a343f + a242d32 commit 16844fb

2 files changed

Lines changed: 43 additions & 9 deletions

File tree

activate.c

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ static const char *balance_level_str(int level)
167167
* - BALANCE_CORE: No fallback possible (single CPU, fail immediately)
168168
* - BALANCE_CACHE: Only search within same cache domain
169169
* - BALANCE_PACKAGE: Only search within same package
170-
* - BALANCE_NONE: Search within same NUMA node (widest scope)
170+
* - BALANCE_NONE: User opted out of balancing - decline fallback
171171
*
172172
* FIXES APPLIED:
173173
* 1. NUMA fallback now traverses topology tree to find actual CPUs
@@ -205,6 +205,22 @@ static int try_fallback_cpu(struct irq_info *info, cpumask_t applied_mask,
205205
return -1;
206206
}
207207

208+
/*
209+
* BALANCE_NONE means the user opted this IRQ out of balancing.
210+
* place_irq_in_node() only relocates such an IRQ when banned CPUs
211+
* forced it; we should not silently widen the scope further on
212+
* ENOSPC. Treat it like BALANCE_CORE: warn and decline.
213+
* This also avoids a NULL search_scope on non-NUMA systems where
214+
* no OBJ_TYPE_NODE ancestor exists in the topology.
215+
*/
216+
if (balance_level == BALANCE_NONE) {
217+
log(TO_ALL, LOG_WARNING,
218+
"IRQ %d: cannot fallback - balance level is 'none' "
219+
"(user policy forbids relocation)\n",
220+
info->irq);
221+
return -1;
222+
}
223+
208224
cpus_clear(tried_cpus);
209225

210226
/*
@@ -230,11 +246,6 @@ static int try_fallback_cpu(struct irq_info *info, cpumask_t applied_mask,
230246
while (search_scope && search_scope->obj_type != OBJ_TYPE_PACKAGE)
231247
search_scope = search_scope->parent;
232248
break;
233-
case BALANCE_NONE:
234-
search_scope = original->parent;
235-
while (search_scope && search_scope->obj_type != OBJ_TYPE_NODE)
236-
search_scope = search_scope->parent;
237-
break;
238249
default:
239250
search_scope = NULL;
240251
}
@@ -359,6 +370,23 @@ static int try_fallback_cpu(struct irq_info *info, cpumask_t applied_mask,
359370
*
360371
*/
361372
migrate_irq_obj(original, fallback, info);
373+
/*
374+
* migrate_irq_obj() unconditionally increments the source
375+
* object's slots_left (0 -> 1), which would make the original
376+
* CPU look eligible again. The kernel returned ENOSPC, so its
377+
* vector table is still full; re-clamp to SATURATED so we
378+
* don't immediately retry the same dead-end placement.
379+
*
380+
* Only re-clamp when the source is an actual CPU. For
381+
* domain-assigned IRQs (cache/package/NUMA), the original
382+
* is a domain object whose slots_left aggregates its CPUs
383+
* and was never marked SLOTS_SATURATED by the ENOSPC handler
384+
* (see activate_mapping(): the obj_type==CPU branch).
385+
* Force-saturating the whole domain would penalize every CPU
386+
* inside it on the next placement cycle.
387+
*/
388+
if (original->obj_type == OBJ_TYPE_CPU)
389+
original->slots_left = SLOTS_SATURATED;
362390
info->moved = 0;
363391
log(TO_ALL, LOG_DEBUG,
364392
"IRQ %d: successfully placed on fallback CPU %d "

placement.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,16 @@ static void find_best_object(struct topo_obj *d, void *data)
125125
best->best_cost = newload;
126126
} else if (adjusted_cost == best_adjusted_cost) {
127127
/*
128-
* Tie-breaker: prefer CPU with more slots_left (more headroom).
129-
* This avoids O(n) g_list_length() calls and uses already-available data.
128+
* Tie-breaker: first prefer the CPU with more slots_left
129+
* (more headroom). During normal operation slots_left is
130+
* INT_MAX for all CPUs (see clear_slots()), so fall back to
131+
* the original interrupt-count comparison to keep IRQs
132+
* spread across CPUs that currently hold fewer interrupts.
130133
*/
131-
if (!best->best || d->slots_left > best->best->slots_left) {
134+
if (!best->best ||
135+
d->slots_left > best->best->slots_left ||
136+
(d->slots_left == best->best->slots_left &&
137+
g_list_length(d->interrupts) < g_list_length(best->best->interrupts))) {
132138
best->best = d;
133139
best->best_cost = newload;
134140
}

0 commit comments

Comments
 (0)