Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

irqbypass: Take ownership of producer/consumer token tracking

Move ownership of IRQ bypass token tracking into irqbypass.ko, and
explicitly require callers to pass an eventfd_ctx structure instead of a
completely opaque token. Relying on producers and consumers to set the
token appropriately is error prone, and hiding the fact that the token must
be an eventfd_ctx pointer (for all intents and purposes) unnecessarily
obfuscates the code and makes it more brittle.

Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Alex Williamson <alex.williamson@redhat.com>
Link: https://lore.kernel.org/r/20250516230734.2564775-4-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>

+58 -49
+2 -2
arch/x86/kvm/x86.c
··· 13669 13669 ret = kvm_x86_call(pi_update_irte)(irqfd->kvm, 13670 13670 prod->irq, irqfd->gsi, 0); 13671 13671 if (ret) 13672 - printk(KERN_INFO "irq bypass consumer (token %p) unregistration" 13673 - " fails: %d\n", irqfd->consumer.token, ret); 13672 + printk(KERN_INFO "irq bypass consumer (eventfd %p) unregistration" 13673 + " fails: %d\n", irqfd->consumer.eventfd, ret); 13674 13674 13675 13675 spin_unlock_irq(&kvm->irqfds.lock); 13676 13676
+3 -6
drivers/vfio/pci/vfio_pci_intrs.c
··· 505 505 if (ret) 506 506 goto out_put_eventfd_ctx; 507 507 508 - ctx->producer.token = trigger; 509 508 ctx->producer.irq = irq; 510 - ret = irq_bypass_register_producer(&ctx->producer); 509 + ret = irq_bypass_register_producer(&ctx->producer, trigger); 511 510 if (unlikely(ret)) { 512 511 dev_info(&pdev->dev, 513 - "irq bypass producer (token %p) registration fails: %d\n", 514 - ctx->producer.token, ret); 515 - 516 - ctx->producer.token = NULL; 512 + "irq bypass producer (eventfd %p) registration fails: %d\n", 513 + trigger, ret); 517 514 } 518 515 ctx->trigger = trigger; 519 516
+3 -5
drivers/vhost/vdpa.c
··· 213 213 return; 214 214 215 215 vq->call_ctx.producer.irq = irq; 216 - ret = irq_bypass_register_producer(&vq->call_ctx.producer); 216 + ret = irq_bypass_register_producer(&vq->call_ctx.producer, vq->call_ctx.ctx); 217 217 if (unlikely(ret)) 218 - dev_info(&v->dev, "vq %u, irq bypass producer (token %p) registration fails, ret = %d\n", 219 - qid, vq->call_ctx.producer.token, ret); 218 + dev_info(&v->dev, "vq %u, irq bypass producer (eventfd %p) registration fails, ret = %d\n", 219 + qid, vq->call_ctx.ctx, ret); 220 220 } 221 221 222 222 static void vhost_vdpa_unsetup_vq_irq(struct vhost_vdpa *v, u16 qid) ··· 712 712 if (ops->get_status(vdpa) & 713 713 VIRTIO_CONFIG_S_DRIVER_OK) 714 714 vhost_vdpa_unsetup_vq_irq(v, idx); 715 - vq->call_ctx.producer.token = NULL; 716 715 } 717 716 break; 718 717 } ··· 752 753 cb.callback = vhost_vdpa_virtqueue_cb; 753 754 cb.private = vq; 754 755 cb.trigger = vq->call_ctx.ctx; 755 - vq->call_ctx.producer.token = vq->call_ctx.ctx; 756 756 if (ops->get_status(vdpa) & 757 757 VIRTIO_CONFIG_S_DRIVER_OK) 758 758 vhost_vdpa_setup_vq_irq(v, idx);
+19 -16
include/linux/irqbypass.h
··· 10 10 11 11 #include <linux/list.h> 12 12 13 + struct eventfd_ctx; 13 14 struct irq_bypass_consumer; 14 15 15 16 /* ··· 19 18 * The IRQ bypass manager is a simple set of lists and callbacks that allows 20 19 * IRQ producers (ex. physical interrupt sources) to be matched to IRQ 21 20 * consumers (ex. virtualization hardware that allows IRQ bypass or offload) 22 - * via a shared token (ex. eventfd_ctx). Producers and consumers register 23 - * independently. When a token match is found, the optional @stop callback 24 - * will be called for each participant. The pair will then be connected via 25 - * the @add_* callbacks, and finally the optional @start callback will allow 26 - * any final coordination. When either participant is unregistered, the 27 - * process is repeated using the @del_* callbacks in place of the @add_* 28 - * callbacks. Match tokens must be unique per producer/consumer, 1:N pairings 29 - * are not supported. 21 + * via a shared eventfd_ctx. Producers and consumers register independently. 22 + * When a producer and consumer are paired, i.e. an eventfd match is found, the 23 + * optional @stop callback will be called for each participant. The pair will 24 + * then be connected via the @add_* callbacks, and finally the optional @start 25 + * callback will allow any final coordination. When either participant is 26 + * unregistered, the process is repeated using the @del_* callbacks in place of 27 + * the @add_* callbacks. eventfds must be unique per producer/consumer, 1:N 28 + * pairings are not supported. 30 29 */ 31 30 32 31 /** 33 32 * struct irq_bypass_producer - IRQ bypass producer definition 34 33 * @node: IRQ bypass manager private list management 35 - * @token: opaque token to match between producer and consumer (non-NULL) 34 + * @eventfd: eventfd context used to match producers and consumers 36 35 * @irq: Linux IRQ number for the producer device 37 36 * @add_consumer: Connect the IRQ producer to an IRQ consumer (optional) 38 37 * @del_consumer: Disconnect the IRQ producer from an IRQ consumer (optional) ··· 45 44 */ 46 45 struct irq_bypass_producer { 47 46 struct list_head node; 48 - void *token; 47 + struct eventfd_ctx *eventfd; 49 48 int irq; 50 49 int (*add_consumer)(struct irq_bypass_producer *, 51 50 struct irq_bypass_consumer *); ··· 58 57 /** 59 58 * struct irq_bypass_consumer - IRQ bypass consumer definition 60 59 * @node: IRQ bypass manager private list management 61 - * @token: opaque token to match between producer and consumer (non-NULL) 60 + * @eventfd: eventfd context used to match producers and consumers 62 61 * @add_producer: Connect the IRQ consumer to an IRQ producer 63 62 * @del_producer: Disconnect the IRQ consumer from an IRQ producer 64 63 * @stop: Perform any quiesce operations necessary prior to add/del (optional) ··· 71 70 */ 72 71 struct irq_bypass_consumer { 73 72 struct list_head node; 74 - void *token; 73 + struct eventfd_ctx *eventfd; 75 74 int (*add_producer)(struct irq_bypass_consumer *, 76 75 struct irq_bypass_producer *); 77 76 void (*del_producer)(struct irq_bypass_consumer *, ··· 80 79 void (*start)(struct irq_bypass_consumer *); 81 80 }; 82 81 83 - int irq_bypass_register_producer(struct irq_bypass_producer *); 84 - void irq_bypass_unregister_producer(struct irq_bypass_producer *); 85 - int irq_bypass_register_consumer(struct irq_bypass_consumer *); 86 - void irq_bypass_unregister_consumer(struct irq_bypass_consumer *); 82 + int irq_bypass_register_producer(struct irq_bypass_producer *producer, 83 + struct eventfd_ctx *eventfd); 84 + void irq_bypass_unregister_producer(struct irq_bypass_producer *producer); 85 + int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer, 86 + struct eventfd_ctx *eventfd); 87 + void irq_bypass_unregister_consumer(struct irq_bypass_consumer *consumer); 87 88 88 89 #endif /* IRQBYPASS_H */
+3 -4
virt/kvm/eventfd.c
··· 426 426 427 427 #if IS_ENABLED(CONFIG_HAVE_KVM_IRQ_BYPASS) 428 428 if (kvm_arch_has_irq_bypass()) { 429 - irqfd->consumer.token = (void *)irqfd->eventfd; 430 429 irqfd->consumer.add_producer = kvm_arch_irq_bypass_add_producer; 431 430 irqfd->consumer.del_producer = kvm_arch_irq_bypass_del_producer; 432 431 irqfd->consumer.stop = kvm_arch_irq_bypass_stop; 433 432 irqfd->consumer.start = kvm_arch_irq_bypass_start; 434 - ret = irq_bypass_register_consumer(&irqfd->consumer); 433 + ret = irq_bypass_register_consumer(&irqfd->consumer, irqfd->eventfd); 435 434 if (ret) 436 - pr_info("irq bypass consumer (token %p) registration fails: %d\n", 437 - irqfd->consumer.token, ret); 435 + pr_info("irq bypass consumer (eventfd %p) registration fails: %d\n", 436 + irqfd->eventfd, ret); 438 437 } 439 438 #endif 440 439
+28 -16
virt/lib/irqbypass.c
··· 77 77 /** 78 78 * irq_bypass_register_producer - register IRQ bypass producer 79 79 * @producer: pointer to producer structure 80 + * @eventfd: pointer to the eventfd context associated with the producer 80 81 * 81 82 * Add the provided IRQ producer to the list of producers and connect 82 - * with any matching token found on the IRQ consumers list. 83 + * with any matching eventfd found on the IRQ consumers list. 83 84 */ 84 - int irq_bypass_register_producer(struct irq_bypass_producer *producer) 85 + int irq_bypass_register_producer(struct irq_bypass_producer *producer, 86 + struct eventfd_ctx *eventfd) 85 87 { 86 88 struct irq_bypass_producer *tmp; 87 89 struct irq_bypass_consumer *consumer; 88 90 int ret; 89 91 90 - if (!producer->token) 92 + if (WARN_ON_ONCE(producer->eventfd)) 91 93 return -EINVAL; 92 94 93 95 mutex_lock(&lock); 94 96 95 97 list_for_each_entry(tmp, &producers, node) { 96 - if (tmp->token == producer->token) { 98 + if (tmp->eventfd == eventfd) { 97 99 ret = -EBUSY; 98 100 goto out_err; 99 101 } 100 102 } 101 103 102 104 list_for_each_entry(consumer, &consumers, node) { 103 - if (consumer->token == producer->token) { 105 + if (consumer->eventfd == eventfd) { 104 106 ret = __connect(producer, consumer); 105 107 if (ret) 106 108 goto out_err; ··· 110 108 } 111 109 } 112 110 111 + producer->eventfd = eventfd; 113 112 list_add(&producer->node, &producers); 114 113 115 114 mutex_unlock(&lock); ··· 134 131 struct irq_bypass_producer *tmp; 135 132 struct irq_bypass_consumer *consumer; 136 133 137 - if (!producer->token) 134 + if (!producer->eventfd) 138 135 return; 139 136 140 137 mutex_lock(&lock); 141 138 142 139 list_for_each_entry(tmp, &producers, node) { 143 - if (tmp->token != producer->token) 140 + if (tmp->eventfd != producer->eventfd) 144 141 continue; 145 142 146 143 list_for_each_entry(consumer, &consumers, node) { 147 - if (consumer->token == producer->token) { 144 + if (consumer->eventfd == producer->eventfd) { 148 145 __disconnect(producer, consumer); 149 146 break; 150 147 } 151 148 } 152 149 150 + producer->eventfd = NULL; 153 151 list_del(&producer->node); 154 152 break; 155 153 } 156 154 155 + WARN_ON_ONCE(producer->eventfd); 157 156 mutex_unlock(&lock); 158 157 } 159 158 EXPORT_SYMBOL_GPL(irq_bypass_unregister_producer); ··· 163 158 /** 164 159 * irq_bypass_register_consumer - register IRQ bypass consumer 165 160 * @consumer: pointer to consumer structure 161 + * @eventfd: pointer to the eventfd context associated with the consumer 166 162 * 167 163 * Add the provided IRQ consumer to the list of consumers and connect 168 - * with any matching token found on the IRQ producer list. 164 + * with any matching eventfd found on the IRQ producer list. 169 165 */ 170 - int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer) 166 + int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer, 167 + struct eventfd_ctx *eventfd) 171 168 { 172 169 struct irq_bypass_consumer *tmp; 173 170 struct irq_bypass_producer *producer; 174 171 int ret; 175 172 176 - if (!consumer->token || 177 - !consumer->add_producer || !consumer->del_producer) 173 + if (WARN_ON_ONCE(consumer->eventfd)) 174 + return -EINVAL; 175 + 176 + if (!consumer->add_producer || !consumer->del_producer) 178 177 return -EINVAL; 179 178 180 179 mutex_lock(&lock); 181 180 182 181 list_for_each_entry(tmp, &consumers, node) { 183 - if (tmp->token == consumer->token || tmp == consumer) { 182 + if (tmp->eventfd == eventfd) { 184 183 ret = -EBUSY; 185 184 goto out_err; 186 185 } 187 186 } 188 187 189 188 list_for_each_entry(producer, &producers, node) { 190 - if (producer->token == consumer->token) { 189 + if (producer->eventfd == eventfd) { 191 190 ret = __connect(producer, consumer); 192 191 if (ret) 193 192 goto out_err; ··· 199 190 } 200 191 } 201 192 193 + consumer->eventfd = eventfd; 202 194 list_add(&consumer->node, &consumers); 203 195 204 196 mutex_unlock(&lock); ··· 223 213 struct irq_bypass_consumer *tmp; 224 214 struct irq_bypass_producer *producer; 225 215 226 - if (!consumer->token) 216 + if (!consumer->eventfd) 227 217 return; 228 218 229 219 mutex_lock(&lock); ··· 233 223 continue; 234 224 235 225 list_for_each_entry(producer, &producers, node) { 236 - if (producer->token == consumer->token) { 226 + if (producer->eventfd == consumer->eventfd) { 237 227 __disconnect(producer, consumer); 238 228 break; 239 229 } 240 230 } 241 231 232 + consumer->eventfd = NULL; 242 233 list_del(&consumer->node); 243 234 break; 244 235 } 245 236 237 + WARN_ON_ONCE(consumer->eventfd); 246 238 mutex_unlock(&lock); 247 239 } 248 240 EXPORT_SYMBOL_GPL(irq_bypass_unregister_consumer);