diff options
author | Paul Mackerras <paulus@samba.org> | 2014-06-30 06:51:09 -0400 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2014-08-05 08:24:23 -0400 |
commit | 56f89f3629ffd1a21d38c3d0bea23deac0e284ce (patch) | |
tree | f92eaa71d30a8d5c86356952294e3287b4de9094 /virt | |
parent | 478d66862559bade81cb653af52b9aa53bee2c8a (diff) |
KVM: Don't keep reference to irq routing table in irqfd struct
This makes the irqfd code keep a copy of the irq routing table entry
for each irqfd, rather than a reference to the copy in the actual
irq routing table maintained in kvm/virt/irqchip.c. This will enable
us to change the routing table structure in future, or even not have a
routing table at all on some platforms.
The synchronization that was previously achieved using srcu_dereference
on the read side is now achieved using a seqcount_t structure. That
ensures that we don't get a halfway-updated copy of the structure if
we read it while another thread is updating it.
We still use srcu_read_lock/unlock around the read side so that when
changing the routing table we can be sure that after calling
synchronize_srcu, nothing will be using the old routing.
Signed-off-by: Paul Mackerras <paulus@samba.org>
Tested-by: Eric Auger <eric.auger@linaro.org>
Tested-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'virt')
-rw-r--r-- | virt/kvm/eventfd.c | 41 |
1 files changed, 25 insertions, 16 deletions
diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c index 20c3af7692c5..bae593a545c5 100644 --- a/virt/kvm/eventfd.c +++ b/virt/kvm/eventfd.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/kernel.h> | 33 | #include <linux/kernel.h> |
34 | #include <linux/srcu.h> | 34 | #include <linux/srcu.h> |
35 | #include <linux/slab.h> | 35 | #include <linux/slab.h> |
36 | #include <linux/seqlock.h> | ||
36 | 37 | ||
37 | #include "iodev.h" | 38 | #include "iodev.h" |
38 | 39 | ||
@@ -75,7 +76,8 @@ struct _irqfd { | |||
75 | struct kvm *kvm; | 76 | struct kvm *kvm; |
76 | wait_queue_t wait; | 77 | wait_queue_t wait; |
77 | /* Update side is protected by irqfds.lock */ | 78 | /* Update side is protected by irqfds.lock */ |
78 | struct kvm_kernel_irq_routing_entry __rcu *irq_entry; | 79 | struct kvm_kernel_irq_routing_entry irq_entry; |
80 | seqcount_t irq_entry_sc; | ||
79 | /* Used for level IRQ fast-path */ | 81 | /* Used for level IRQ fast-path */ |
80 | int gsi; | 82 | int gsi; |
81 | struct work_struct inject; | 83 | struct work_struct inject; |
@@ -223,16 +225,20 @@ irqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key) | |||
223 | { | 225 | { |
224 | struct _irqfd *irqfd = container_of(wait, struct _irqfd, wait); | 226 | struct _irqfd *irqfd = container_of(wait, struct _irqfd, wait); |
225 | unsigned long flags = (unsigned long)key; | 227 | unsigned long flags = (unsigned long)key; |
226 | struct kvm_kernel_irq_routing_entry *irq; | 228 | struct kvm_kernel_irq_routing_entry irq; |
227 | struct kvm *kvm = irqfd->kvm; | 229 | struct kvm *kvm = irqfd->kvm; |
230 | unsigned seq; | ||
228 | int idx; | 231 | int idx; |
229 | 232 | ||
230 | if (flags & POLLIN) { | 233 | if (flags & POLLIN) { |
231 | idx = srcu_read_lock(&kvm->irq_srcu); | 234 | idx = srcu_read_lock(&kvm->irq_srcu); |
232 | irq = srcu_dereference(irqfd->irq_entry, &kvm->irq_srcu); | 235 | do { |
236 | seq = read_seqcount_begin(&irqfd->irq_entry_sc); | ||
237 | irq = irqfd->irq_entry; | ||
238 | } while (read_seqcount_retry(&irqfd->irq_entry_sc, seq)); | ||
233 | /* An event has been signaled, inject an interrupt */ | 239 | /* An event has been signaled, inject an interrupt */ |
234 | if (irq) | 240 | if (irq.type == KVM_IRQ_ROUTING_MSI) |
235 | kvm_set_msi(irq, kvm, KVM_USERSPACE_IRQ_SOURCE_ID, 1, | 241 | kvm_set_msi(&irq, kvm, KVM_USERSPACE_IRQ_SOURCE_ID, 1, |
236 | false); | 242 | false); |
237 | else | 243 | else |
238 | schedule_work(&irqfd->inject); | 244 | schedule_work(&irqfd->inject); |
@@ -277,18 +283,20 @@ static void irqfd_update(struct kvm *kvm, struct _irqfd *irqfd, | |||
277 | { | 283 | { |
278 | struct kvm_kernel_irq_routing_entry *e; | 284 | struct kvm_kernel_irq_routing_entry *e; |
279 | 285 | ||
280 | if (irqfd->gsi >= irq_rt->nr_rt_entries) { | 286 | write_seqcount_begin(&irqfd->irq_entry_sc); |
281 | rcu_assign_pointer(irqfd->irq_entry, NULL); | 287 | |
282 | return; | 288 | irqfd->irq_entry.type = 0; |
283 | } | 289 | if (irqfd->gsi >= irq_rt->nr_rt_entries) |
290 | goto out; | ||
284 | 291 | ||
285 | hlist_for_each_entry(e, &irq_rt->map[irqfd->gsi], link) { | 292 | hlist_for_each_entry(e, &irq_rt->map[irqfd->gsi], link) { |
286 | /* Only fast-path MSI. */ | 293 | /* Only fast-path MSI. */ |
287 | if (e->type == KVM_IRQ_ROUTING_MSI) | 294 | if (e->type == KVM_IRQ_ROUTING_MSI) |
288 | rcu_assign_pointer(irqfd->irq_entry, e); | 295 | irqfd->irq_entry = *e; |
289 | else | ||
290 | rcu_assign_pointer(irqfd->irq_entry, NULL); | ||
291 | } | 296 | } |
297 | |||
298 | out: | ||
299 | write_seqcount_end(&irqfd->irq_entry_sc); | ||
292 | } | 300 | } |
293 | 301 | ||
294 | static int | 302 | static int |
@@ -310,6 +318,7 @@ kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args) | |||
310 | INIT_LIST_HEAD(&irqfd->list); | 318 | INIT_LIST_HEAD(&irqfd->list); |
311 | INIT_WORK(&irqfd->inject, irqfd_inject); | 319 | INIT_WORK(&irqfd->inject, irqfd_inject); |
312 | INIT_WORK(&irqfd->shutdown, irqfd_shutdown); | 320 | INIT_WORK(&irqfd->shutdown, irqfd_shutdown); |
321 | seqcount_init(&irqfd->irq_entry_sc); | ||
313 | 322 | ||
314 | f = fdget(args->fd); | 323 | f = fdget(args->fd); |
315 | if (!f.file) { | 324 | if (!f.file) { |
@@ -466,14 +475,14 @@ kvm_irqfd_deassign(struct kvm *kvm, struct kvm_irqfd *args) | |||
466 | list_for_each_entry_safe(irqfd, tmp, &kvm->irqfds.items, list) { | 475 | list_for_each_entry_safe(irqfd, tmp, &kvm->irqfds.items, list) { |
467 | if (irqfd->eventfd == eventfd && irqfd->gsi == args->gsi) { | 476 | if (irqfd->eventfd == eventfd && irqfd->gsi == args->gsi) { |
468 | /* | 477 | /* |
469 | * This rcu_assign_pointer is needed for when | 478 | * This clearing of irq_entry.type is needed for when |
470 | * another thread calls kvm_irq_routing_update before | 479 | * another thread calls kvm_irq_routing_update before |
471 | * we flush workqueue below (we synchronize with | 480 | * we flush workqueue below (we synchronize with |
472 | * kvm_irq_routing_update using irqfds.lock). | 481 | * kvm_irq_routing_update using irqfds.lock). |
473 | * It is paired with synchronize_srcu done by caller | ||
474 | * of that function. | ||
475 | */ | 482 | */ |
476 | rcu_assign_pointer(irqfd->irq_entry, NULL); | 483 | write_seqcount_begin(&irqfd->irq_entry_sc); |
484 | irqfd->irq_entry.type = 0; | ||
485 | write_seqcount_end(&irqfd->irq_entry_sc); | ||
477 | irqfd_deactivate(irqfd); | 486 | irqfd_deactivate(irqfd); |
478 | } | 487 | } |
479 | } | 488 | } |