kernel/smp: Use a single-CPU ICI if multicasting to only one CPU.

This avoids the broadcast mailbox (and the incrementing of
other CPU's counters) entirely in this case.

As this is used for TLB invalidation, processes that are only
running two threads at once will benefit immensely from this.
It also seems to be triggered by the kernel's "idle_scan_active_pages"
logic, which sometimes sends invalidations to CPUs besides its own.

On the VM setup, this path was hit around 200 times during boot,
around 600 times during a rebuild of HaikuDepot + mime_db, and
50,000+ times while running Iceweasel (Firefox). On bare metal,
it was hit around 300 times during boot, 300 times during a rebuild,
and 90,000+ times while running Iceweasel (over a different length
of time, though.)
This commit is contained in:
Augustin Cavalier
2026-03-17 13:06:35 -04:00
parent 9c08c836bf
commit d4817afe36
+40 -22
View File
@@ -1130,10 +1130,17 @@ smp_multicast_ici(const CPUSet& cpuMask, int32 message, addr_t data,
return;
}
int32 targetCPUs = 0;
ASSERT(thread_get_current_thread()->pinned_to_cpu);
int32 currentCPU = smp_get_current_cpu();
bool self = cpuMask.GetBit(currentCPU);
int32 targetCPUs = 0, firstNonCurrentCPU = -1;
for (int32 i = 0; i < sNumCPUs; i++) {
if (cpuMask.GetBit(i))
if (cpuMask.GetBit(i)) {
targetCPUs++;
if (firstNonCurrentCPU < 0 && i != currentCPU)
firstNonCurrentCPU = i;
}
}
if (targetCPUs == 0) {
@@ -1155,28 +1162,39 @@ smp_multicast_ici(const CPUSet& cpuMask, int32 message, addr_t data,
msg->done = 0;
msg->proc_bitmap = cpuMask;
int32 currentCPU = smp_get_current_cpu();
bool self = cpuMask.GetBit(currentCPU);
bool broadcast = (!self && targetCPUs == sNumCPUs - 1)
|| (self && targetCPUs == sNumCPUs);
if ((!self && targetCPUs == 1) || (self && targetCPUs == 2)) {
// stick it in the appropriate cpu's mailbox
prepend_message(sCPUMessages[firstNonCurrentCPU], msg);
// stick it in the broadcast mailbox
acquire_read_spinlock_nocheck(&sBroadcastMessageSpinlock);
prepend_message(sBroadcastMessages, msg);
release_read_spinlock(&sBroadcastMessageSpinlock);
arch_smp_send_ici(firstNonCurrentCPU);
atomic_add(&sBroadcastMessageCounter, 1);
for (int32 i = 0; i < sNumCPUs; i++) {
if (!cpuMask.GetBit(i))
atomic_add(&gCPU[i].ici_counter, 1);
}
if (broadcast) {
arch_smp_send_broadcast_ici();
if (self) {
// invoke for ourselves
invoke_smp_msg(msg, currentCPU, NULL);
finish_message_processing(currentCPU, msg, MAILBOX_LOCAL);
}
} else {
CPUSet sendMask = cpuMask;
sendMask.ClearBit(currentCPU);
arch_smp_send_multicast_ici(sendMask);
bool broadcast = (!self && targetCPUs == sNumCPUs - 1)
|| (self && targetCPUs == sNumCPUs);
// stick it in the broadcast mailbox
acquire_read_spinlock_nocheck(&sBroadcastMessageSpinlock);
prepend_message(sBroadcastMessages, msg);
release_read_spinlock(&sBroadcastMessageSpinlock);
atomic_add(&sBroadcastMessageCounter, 1);
for (int32 i = 0; i < sNumCPUs; i++) {
if (!cpuMask.GetBit(i))
atomic_add(&gCPU[i].ici_counter, 1);
}
if (broadcast) {
arch_smp_send_broadcast_ici();
} else {
CPUSet sendMask = cpuMask;
sendMask.ClearBit(currentCPU);
arch_smp_send_multicast_ici(sendMask);
}
}
if ((flags & SMP_MSG_FLAG_SYNC) != 0) {
@@ -1192,7 +1210,7 @@ smp_multicast_ici(const CPUSet& cpuMask, int32 message, addr_t data,
// back into the free list
return_free_message(msg);
} else if (self) {
// make sure this CPU has processed the message at least
// if broadcast, make sure this CPU has processed the message at least
process_all_pending_ici(currentCPU);
}