In guests with many VFIO devices and MSI-X vectors, kvm_set_irq_routing() becomes a high-overhead operation. Each invocation walks the entire IRQ routing table and reallocates/frees every routing entry. As the routing table grows on each call, entry allocation and freeing dominate the execution time of this function. In scenarios such as VM live migration or live upgrade, this behavior can introduce unnecessary downtime. Allocate memory for all routing entries in one shot using kcalloc(), allowing them to be freed together with a single kfree() call. Example: On a VM with 120 vCPUs and 15 VFIO devices (virtio-net), the total number of calls to kzalloc and kfree is over 20000. With this change, it is reduced to around 30. Reported-by: Xiangfeng Cai Signed-off-by: Yanfei Xu --- include/linux/kvm_host.h | 1 + virt/kvm/irqchip.c | 21 +++++++++------------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 15656b7fba6c..aae6ea9940a0 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -690,6 +690,7 @@ struct kvm_kernel_irq_routing_entry { struct kvm_irq_routing_table { int chip[KVM_NR_IRQCHIPS][KVM_IRQCHIP_NUM_PINS]; u32 nr_rt_entries; + struct kvm_kernel_irq_routing_entry *entries_addr; /* * Array indexed by gsi. Each entry contains list of irq chips * the gsi is connected to. diff --git a/virt/kvm/irqchip.c b/virt/kvm/irqchip.c index 6ccabfd32287..0eac1c634fa5 100644 --- a/virt/kvm/irqchip.c +++ b/virt/kvm/irqchip.c @@ -109,10 +109,10 @@ static void free_irq_routing_table(struct kvm_irq_routing_table *rt) hlist_for_each_entry_safe(e, n, &rt->map[i], link) { hlist_del(&e->link); - kfree(e); } } + kfree(rt->entries_addr); kfree(rt); } @@ -186,6 +186,10 @@ int kvm_set_irq_routing(struct kvm *kvm, new = kzalloc(struct_size(new, map, nr_rt_entries), GFP_KERNEL_ACCOUNT); if (!new) return -ENOMEM; + e = kcalloc(nr, sizeof(*e), GFP_KERNEL_ACCOUNT); + if (!e) + goto out; + new->entries_addr = e; new->nr_rt_entries = nr_rt_entries; for (i = 0; i < KVM_NR_IRQCHIPS; i++) @@ -193,25 +197,20 @@ int kvm_set_irq_routing(struct kvm *kvm, new->chip[i][j] = -1; for (i = 0; i < nr; ++i) { - r = -ENOMEM; - e = kzalloc(sizeof(*e), GFP_KERNEL_ACCOUNT); - if (!e) - goto out; - r = -EINVAL; switch (ue->type) { case KVM_IRQ_ROUTING_MSI: if (ue->flags & ~KVM_MSI_VALID_DEVID) - goto free_entry; + goto out; break; default: if (ue->flags) - goto free_entry; + goto out; break; } - r = setup_routing_entry(kvm, new, e, ue); + r = setup_routing_entry(kvm, new, e + i, ue); if (r) - goto free_entry; + goto out; ++ue; } @@ -228,8 +227,6 @@ int kvm_set_irq_routing(struct kvm *kvm, r = 0; goto out; -free_entry: - kfree(e); out: free_irq_routing_table(new); -- 2.20.1