aboutsummaryrefslogtreecommitdiffstats
path: root/virt
diff options
context:
space:
mode:
authorJan Kiszka <jan.kiszka@siemens.com>2012-02-28 08:19:54 -0500
committerAvi Kivity <avi@redhat.com>2012-03-08 07:11:36 -0500
commit07700a94b00a4fcbbfb07d1b72dc112a0e036735 (patch)
treee4a8045d6a44906463102a04c7a87337c38e16aa /virt
parent3e515705a1f46beb1c942bb8043c16f8ac7b1e9e (diff)
KVM: Allow host IRQ sharing for assigned PCI 2.3 devices
PCI 2.3 allows to generically disable IRQ sources at device level. This enables us to share legacy IRQs of such devices with other host devices when passing them to a guest. The new IRQ sharing feature introduced here is optional, user space has to request it explicitly. Moreover, user space can inform us about its view of PCI_COMMAND_INTX_DISABLE so that we can avoid unmasking the interrupt and signaling it if the guest masked it via the virtualized PCI config space. Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> Acked-by: Alex Williamson <alex.williamson@redhat.com> Acked-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'virt')
-rw-r--r--virt/kvm/assigned-dev.c209
1 files changed, 180 insertions, 29 deletions
diff --git a/virt/kvm/assigned-dev.c b/virt/kvm/assigned-dev.c
index ece80612b594..08e05715df72 100644
--- a/virt/kvm/assigned-dev.c
+++ b/virt/kvm/assigned-dev.c
@@ -55,22 +55,66 @@ static int find_index_from_host_irq(struct kvm_assigned_dev_kernel
55 return index; 55 return index;
56} 56}
57 57
58static irqreturn_t kvm_assigned_dev_thread(int irq, void *dev_id) 58static irqreturn_t kvm_assigned_dev_intx(int irq, void *dev_id)
59{ 59{
60 struct kvm_assigned_dev_kernel *assigned_dev = dev_id; 60 struct kvm_assigned_dev_kernel *assigned_dev = dev_id;
61 int ret;
61 62
62 if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_INTX) { 63 spin_lock(&assigned_dev->intx_lock);
63 spin_lock(&assigned_dev->intx_lock); 64 if (pci_check_and_mask_intx(assigned_dev->dev)) {
65 assigned_dev->host_irq_disabled = true;
66 ret = IRQ_WAKE_THREAD;
67 } else
68 ret = IRQ_NONE;
69 spin_unlock(&assigned_dev->intx_lock);
70
71 return ret;
72}
73
74static void
75kvm_assigned_dev_raise_guest_irq(struct kvm_assigned_dev_kernel *assigned_dev,
76 int vector)
77{
78 if (unlikely(assigned_dev->irq_requested_type &
79 KVM_DEV_IRQ_GUEST_INTX)) {
80 mutex_lock(&assigned_dev->intx_mask_lock);
81 if (!(assigned_dev->flags & KVM_DEV_ASSIGN_MASK_INTX))
82 kvm_set_irq(assigned_dev->kvm,
83 assigned_dev->irq_source_id, vector, 1);
84 mutex_unlock(&assigned_dev->intx_mask_lock);
85 } else
86 kvm_set_irq(assigned_dev->kvm, assigned_dev->irq_source_id,
87 vector, 1);
88}
89
90static irqreturn_t kvm_assigned_dev_thread_intx(int irq, void *dev_id)
91{
92 struct kvm_assigned_dev_kernel *assigned_dev = dev_id;
93
94 if (!(assigned_dev->flags & KVM_DEV_ASSIGN_PCI_2_3)) {
95 spin_lock_irq(&assigned_dev->intx_lock);
64 disable_irq_nosync(irq); 96 disable_irq_nosync(irq);
65 assigned_dev->host_irq_disabled = true; 97 assigned_dev->host_irq_disabled = true;
66 spin_unlock(&assigned_dev->intx_lock); 98 spin_unlock_irq(&assigned_dev->intx_lock);
67 } 99 }
68 100
69 kvm_set_irq(assigned_dev->kvm, assigned_dev->irq_source_id, 101 kvm_assigned_dev_raise_guest_irq(assigned_dev,
70 assigned_dev->guest_irq, 1); 102 assigned_dev->guest_irq);
103
104 return IRQ_HANDLED;
105}
106
107#ifdef __KVM_HAVE_MSI
108static irqreturn_t kvm_assigned_dev_thread_msi(int irq, void *dev_id)
109{
110 struct kvm_assigned_dev_kernel *assigned_dev = dev_id;
111
112 kvm_assigned_dev_raise_guest_irq(assigned_dev,
113 assigned_dev->guest_irq);
71 114
72 return IRQ_HANDLED; 115 return IRQ_HANDLED;
73} 116}
117#endif
74 118
75#ifdef __KVM_HAVE_MSIX 119#ifdef __KVM_HAVE_MSIX
76static irqreturn_t kvm_assigned_dev_thread_msix(int irq, void *dev_id) 120static irqreturn_t kvm_assigned_dev_thread_msix(int irq, void *dev_id)
@@ -81,8 +125,7 @@ static irqreturn_t kvm_assigned_dev_thread_msix(int irq, void *dev_id)
81 125
82 if (index >= 0) { 126 if (index >= 0) {
83 vector = assigned_dev->guest_msix_entries[index].vector; 127 vector = assigned_dev->guest_msix_entries[index].vector;
84 kvm_set_irq(assigned_dev->kvm, assigned_dev->irq_source_id, 128 kvm_assigned_dev_raise_guest_irq(assigned_dev, vector);
85 vector, 1);
86 } 129 }
87 130
88 return IRQ_HANDLED; 131 return IRQ_HANDLED;
@@ -98,15 +141,31 @@ static void kvm_assigned_dev_ack_irq(struct kvm_irq_ack_notifier *kian)
98 141
99 kvm_set_irq(dev->kvm, dev->irq_source_id, dev->guest_irq, 0); 142 kvm_set_irq(dev->kvm, dev->irq_source_id, dev->guest_irq, 0);
100 143
101 /* The guest irq may be shared so this ack may be 144 mutex_lock(&dev->intx_mask_lock);
102 * from another device. 145
103 */ 146 if (!(dev->flags & KVM_DEV_ASSIGN_MASK_INTX)) {
104 spin_lock(&dev->intx_lock); 147 bool reassert = false;
105 if (dev->host_irq_disabled) { 148
106 enable_irq(dev->host_irq); 149 spin_lock_irq(&dev->intx_lock);
107 dev->host_irq_disabled = false; 150 /*
151 * The guest IRQ may be shared so this ack can come from an
152 * IRQ for another guest device.
153 */
154 if (dev->host_irq_disabled) {
155 if (!(dev->flags & KVM_DEV_ASSIGN_PCI_2_3))
156 enable_irq(dev->host_irq);
157 else if (!pci_check_and_unmask_intx(dev->dev))
158 reassert = true;
159 dev->host_irq_disabled = reassert;
160 }
161 spin_unlock_irq(&dev->intx_lock);
162
163 if (reassert)
164 kvm_set_irq(dev->kvm, dev->irq_source_id,
165 dev->guest_irq, 1);
108 } 166 }
109 spin_unlock(&dev->intx_lock); 167
168 mutex_unlock(&dev->intx_mask_lock);
110} 169}
111 170
112static void deassign_guest_irq(struct kvm *kvm, 171static void deassign_guest_irq(struct kvm *kvm,
@@ -154,7 +213,15 @@ static void deassign_host_irq(struct kvm *kvm,
154 pci_disable_msix(assigned_dev->dev); 213 pci_disable_msix(assigned_dev->dev);
155 } else { 214 } else {
156 /* Deal with MSI and INTx */ 215 /* Deal with MSI and INTx */
157 disable_irq(assigned_dev->host_irq); 216 if ((assigned_dev->irq_requested_type &
217 KVM_DEV_IRQ_HOST_INTX) &&
218 (assigned_dev->flags & KVM_DEV_ASSIGN_PCI_2_3)) {
219 spin_lock_irq(&assigned_dev->intx_lock);
220 pci_intx(assigned_dev->dev, false);
221 spin_unlock_irq(&assigned_dev->intx_lock);
222 synchronize_irq(assigned_dev->host_irq);
223 } else
224 disable_irq(assigned_dev->host_irq);
158 225
159 free_irq(assigned_dev->host_irq, assigned_dev); 226 free_irq(assigned_dev->host_irq, assigned_dev);
160 227
@@ -235,15 +302,34 @@ void kvm_free_all_assigned_devices(struct kvm *kvm)
235static int assigned_device_enable_host_intx(struct kvm *kvm, 302static int assigned_device_enable_host_intx(struct kvm *kvm,
236 struct kvm_assigned_dev_kernel *dev) 303 struct kvm_assigned_dev_kernel *dev)
237{ 304{
305 irq_handler_t irq_handler;
306 unsigned long flags;
307
238 dev->host_irq = dev->dev->irq; 308 dev->host_irq = dev->dev->irq;
239 /* Even though this is PCI, we don't want to use shared 309
240 * interrupts. Sharing host devices with guest-assigned devices 310 /*
241 * on the same interrupt line is not a happy situation: there 311 * We can only share the IRQ line with other host devices if we are
242 * are going to be long delays in accepting, acking, etc. 312 * able to disable the IRQ source at device-level - independently of
313 * the guest driver. Otherwise host devices may suffer from unbounded
314 * IRQ latencies when the guest keeps the line asserted.
243 */ 315 */
244 if (request_threaded_irq(dev->host_irq, NULL, kvm_assigned_dev_thread, 316 if (dev->flags & KVM_DEV_ASSIGN_PCI_2_3) {
245 IRQF_ONESHOT, dev->irq_name, dev)) 317 irq_handler = kvm_assigned_dev_intx;
318 flags = IRQF_SHARED;
319 } else {
320 irq_handler = NULL;
321 flags = IRQF_ONESHOT;
322 }
323 if (request_threaded_irq(dev->host_irq, irq_handler,
324 kvm_assigned_dev_thread_intx, flags,
325 dev->irq_name, dev))
246 return -EIO; 326 return -EIO;
327
328 if (dev->flags & KVM_DEV_ASSIGN_PCI_2_3) {
329 spin_lock_irq(&dev->intx_lock);
330 pci_intx(dev->dev, true);
331 spin_unlock_irq(&dev->intx_lock);
332 }
247 return 0; 333 return 0;
248} 334}
249 335
@@ -260,8 +346,9 @@ static int assigned_device_enable_host_msi(struct kvm *kvm,
260 } 346 }
261 347
262 dev->host_irq = dev->dev->irq; 348 dev->host_irq = dev->dev->irq;
263 if (request_threaded_irq(dev->host_irq, NULL, kvm_assigned_dev_thread, 349 if (request_threaded_irq(dev->host_irq, NULL,
264 0, dev->irq_name, dev)) { 350 kvm_assigned_dev_thread_msi, 0,
351 dev->irq_name, dev)) {
265 pci_disable_msi(dev->dev);