aboutsummaryrefslogtreecommitdiffstats
path: root/virt
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@samba.org>2014-06-30 06:51:09 -0400
committerPaolo Bonzini <pbonzini@redhat.com>2014-08-05 08:24:23 -0400
commit56f89f3629ffd1a21d38c3d0bea23deac0e284ce (patch)
treef92eaa71d30a8d5c86356952294e3287b4de9094 /virt
parent478d66862559bade81cb653af52b9aa53bee2c8a (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.c41
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
294static int 302static 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 }