ia64/xen-unstable

view xen/arch/x86/hvm/vmsi.c @ 19311:e6b7b747d122

passthrough: fix some spinlock issues in vmsi

Apart from efficiency, I hasten to fix the assertion failure.

- acquire pcidevs_lock before calling pt_irq_xxx_bind_vtd
- allocate msixtbl_entry beforehand
- check return value from domain_spin_lock_irq_desc()
- typo: spin_unlock(&irq_desc->lock) ->
- spin_unlock_irq(&irq_desc->lock)
- acquire msixtbl_list_lock with irq_disabled

Signed-off-by: Kouya Shimura <kouya@jp.fujitsu.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Mar 11 10:09:21 2009 +0000 (2009-03-11)
parents 9c5b4efc934d
children
line source
1 /*
2 * Copyright (C) 2001 MandrakeSoft S.A.
3 *
4 * MandrakeSoft S.A.
5 * 43, rue d'Aboukir
6 * 75002 Paris - France
7 * http://www.linux-mandrake.com/
8 * http://www.mandrakesoft.com/
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 * Support for virtual MSI logic
25 * Will be merged it with virtual IOAPIC logic, since most is the same
26 */
28 #include <xen/config.h>
29 #include <xen/types.h>
30 #include <xen/mm.h>
31 #include <xen/xmalloc.h>
32 #include <xen/lib.h>
33 #include <xen/errno.h>
34 #include <xen/sched.h>
35 #include <public/hvm/ioreq.h>
36 #include <asm/hvm/io.h>
37 #include <asm/hvm/vpic.h>
38 #include <asm/hvm/vlapic.h>
39 #include <asm/hvm/support.h>
40 #include <asm/current.h>
41 #include <asm/event.h>
43 static uint32_t vmsi_get_delivery_bitmask(
44 struct domain *d, uint16_t dest, uint8_t dest_mode)
45 {
46 uint32_t mask = 0;
47 struct vcpu *v;
49 HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "ioapic_get_delivery_bitmask "
50 "dest %d dest_mode %d\n", dest, dest_mode);
52 if ( dest_mode == 0 ) /* Physical mode. */
53 {
54 if ( dest == 0xFF ) /* Broadcast. */
55 {
56 for_each_vcpu ( d, v )
57 mask |= 1 << v->vcpu_id;
58 goto out;
59 }
61 for_each_vcpu ( d, v )
62 {
63 if ( VLAPIC_ID(vcpu_vlapic(v)) == dest )
64 {
65 mask = 1 << v->vcpu_id;
66 break;
67 }
68 }
69 }
70 else if ( dest != 0 ) /* Logical mode, MDA non-zero. */
71 {
72 for_each_vcpu ( d, v )
73 if ( vlapic_match_logical_addr(vcpu_vlapic(v), dest) )
74 mask |= 1 << v->vcpu_id;
75 }
77 out:
78 HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "ioapic_get_delivery_bitmask mask %x\n",
79 mask);
80 return mask;
81 }
83 static void vmsi_inj_irq(
84 struct domain *d,
85 struct vlapic *target,
86 uint8_t vector,
87 uint8_t trig_mode,
88 uint8_t delivery_mode)
89 {
90 HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "ioapic_inj_irq "
91 "irq %d trig %d delive mode %d\n",
92 vector, trig_mode, delivery_mode);
94 switch ( delivery_mode )
95 {
96 case dest_Fixed:
97 case dest_LowestPrio:
98 if ( vlapic_set_irq(target, vector, trig_mode) )
99 vcpu_kick(vlapic_vcpu(target));
100 break;
101 default:
102 gdprintk(XENLOG_WARNING, "error delivery mode %d\n", delivery_mode);
103 break;
104 }
105 }
107 #define VMSI_DEST_ID_MASK 0xff
108 #define VMSI_RH_MASK 0x100
109 #define VMSI_DM_MASK 0x200
110 #define VMSI_DELIV_MASK 0x7000
111 #define VMSI_TRIG_MODE 0x8000
113 #define GFLAGS_SHIFT_DEST_ID 0
114 #define GFLAGS_SHIFT_RH 8
115 #define GFLAGS_SHIFT_DM 9
116 #define GLFAGS_SHIFT_DELIV_MODE 12
117 #define GLFAGS_SHIFT_TRG_MODE 15
119 int vmsi_deliver(struct domain *d, int pirq)
120 {
121 struct hvm_irq_dpci *hvm_irq_dpci = d->arch.hvm_domain.irq.dpci;
122 uint32_t flags = hvm_irq_dpci->mirq[pirq].gmsi.gflags;
123 int vector = hvm_irq_dpci->mirq[pirq].gmsi.gvec;
124 uint16_t dest = (flags & VMSI_DEST_ID_MASK) >> GFLAGS_SHIFT_DEST_ID;
125 uint8_t dest_mode = (flags & VMSI_DM_MASK) >> GFLAGS_SHIFT_DM;
126 uint8_t delivery_mode = (flags & VMSI_DELIV_MASK) >> GLFAGS_SHIFT_DELIV_MODE;
127 uint8_t trig_mode = (flags & VMSI_TRIG_MODE) >> GLFAGS_SHIFT_TRG_MODE;
128 uint32_t deliver_bitmask;
129 struct vlapic *target;
130 struct vcpu *v;
132 HVM_DBG_LOG(DBG_LEVEL_IOAPIC,
133 "msi: dest=%x dest_mode=%x delivery_mode=%x "
134 "vector=%x trig_mode=%x\n",
135 dest, dest_mode, delivery_mode, vector, trig_mode);
137 if ( !( hvm_irq_dpci->mirq[pirq].flags & HVM_IRQ_DPCI_GUEST_MSI ) )
138 {
139 gdprintk(XENLOG_WARNING, "pirq %x not msi \n", pirq);
140 return 0;
141 }
143 deliver_bitmask = vmsi_get_delivery_bitmask(d, dest, dest_mode);
144 if ( !deliver_bitmask )
145 {
146 HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "ioapic deliver "
147 "no target on destination\n");
148 return 0;
149 }
151 switch ( delivery_mode )
152 {
153 case dest_LowestPrio:
154 {
155 target = apic_lowest_prio(d, deliver_bitmask);
156 if ( target != NULL )
157 vmsi_inj_irq(d, target, vector, trig_mode, delivery_mode);
158 else
159 HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "null round robin: "
160 "mask=%x vector=%x delivery_mode=%x\n",
161 deliver_bitmask, vector, dest_LowestPrio);
162 break;
163 }
165 case dest_Fixed:
166 case dest_ExtINT:
167 {
168 uint8_t bit;
169 for ( bit = 0; deliver_bitmask != 0; bit++ )
170 {
171 if ( !(deliver_bitmask & (1 << bit)) )
172 continue;
173 deliver_bitmask &= ~(1 << bit);
174 v = d->vcpu[bit];
175 if ( v != NULL )
176 {
177 target = vcpu_vlapic(v);
178 vmsi_inj_irq(d, target, vector, trig_mode, delivery_mode);
179 }
180 }
181 break;
182 }
184 case dest_SMI:
185 case dest_NMI:
186 case dest_INIT:
187 case dest__reserved_2:
188 default:
189 gdprintk(XENLOG_WARNING, "Unsupported delivery mode %d\n",
190 delivery_mode);
191 break;
192 }
193 return 1;
194 }
196 /* MSI-X mask bit hypervisor interception */
197 struct msixtbl_entry
198 {
199 struct list_head list;
200 atomic_t refcnt; /* how many bind_pt_irq called for the device */
202 /* TODO: resolve the potential race by destruction of pdev */
203 struct pci_dev *pdev;
204 unsigned long gtable; /* gpa of msix table */
205 unsigned long table_len;
206 unsigned long table_flags[MAX_MSIX_TABLE_ENTRIES / BITS_PER_LONG + 1];
208 struct rcu_head rcu;
209 };
211 static struct msixtbl_entry *msixtbl_find_entry(
212 struct vcpu *v, unsigned long addr)
213 {
214 struct msixtbl_entry *entry;
215 struct domain *d = v->domain;
217 list_for_each_entry( entry, &d->arch.hvm_domain.msixtbl_list, list )
218 if ( addr >= entry->gtable &&
219 addr < entry->gtable + entry->table_len )
220 return entry;
222 return NULL;
223 }
225 static void __iomem *msixtbl_addr_to_virt(
226 struct msixtbl_entry *entry, unsigned long addr)
227 {
228 int idx, nr_page;
230 if ( !entry )
231 return NULL;
233 nr_page = (addr >> PAGE_SHIFT) -
234 (entry->gtable >> PAGE_SHIFT);
236 if ( !entry->pdev )
237 return NULL;
239 idx = entry->pdev->msix_table_idx[nr_page];
240 if ( !idx )
241 return NULL;
243 return (void *)(fix_to_virt(idx) +
244 (addr & ((1UL << PAGE_SHIFT) - 1)));
245 }
247 static int msixtbl_read(
248 struct vcpu *v, unsigned long address,
249 unsigned long len, unsigned long *pval)
250 {
251 unsigned long offset;
252 struct msixtbl_entry *entry;
253 void *virt;
254 int r = X86EMUL_UNHANDLEABLE;
256 rcu_read_lock();
258 if ( len != 4 )
259 goto out;
261 offset = address & (PCI_MSIX_ENTRY_SIZE - 1);
262 if ( offset != PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET)
263 goto out;
265 entry = msixtbl_find_entry(v, address);
266 virt = msixtbl_addr_to_virt(entry, address);
267 if ( !virt )
268 goto out;
270 *pval = readl(virt);
271 r = X86EMUL_OKAY;
273 out:
274 rcu_read_unlock();
275 return r;
276 }
278 static int msixtbl_write(struct vcpu *v, unsigned long address,
279 unsigned long len, unsigned long val)
280 {
281 unsigned long offset;
282 struct msixtbl_entry *entry;
283 void *virt;
284 int nr_entry;
285 int r = X86EMUL_UNHANDLEABLE;
287 rcu_read_lock();
289 if ( len != 4 )
290 goto out;
292 entry = msixtbl_find_entry(v, address);
293 nr_entry = (address - entry->gtable) % PCI_MSIX_ENTRY_SIZE;
295 offset = address & (PCI_MSIX_ENTRY_SIZE - 1);
296 if ( offset != PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET)
297 {
298 set_bit(nr_entry, &entry->table_flags);
299 goto out;
300 }
302 /* exit to device model if address/data has been modified */
303 if ( test_and_clear_bit(nr_entry, &entry->table_flags) )
304 goto out;
306 virt = msixtbl_addr_to_virt(entry, address);
307 if ( !virt )
308 goto out;
310 writel(val, virt);
311 r = X86EMUL_OKAY;
313 out:
314 rcu_read_unlock();
315 return r;
316 }
318 static int msixtbl_range(struct vcpu *v, unsigned long addr)
319 {
320 struct msixtbl_entry *entry;
321 void *virt;
323 rcu_read_lock();
325 entry = msixtbl_find_entry(v, addr);
326 virt = msixtbl_addr_to_virt(entry, addr);
328 rcu_read_unlock();
330 return !!virt;
331 }
333 struct hvm_mmio_handler msixtbl_mmio_handler = {
334 .check_handler = msixtbl_range,
335 .read_handler = msixtbl_read,
336 .write_handler = msixtbl_write
337 };
339 static void add_msixtbl_entry(struct domain *d,
340 struct pci_dev *pdev,
341 uint64_t gtable,
342 struct msixtbl_entry *entry)
343 {
344 u32 len;
346 memset(entry, 0, sizeof(struct msixtbl_entry));
348 INIT_LIST_HEAD(&entry->list);
349 INIT_RCU_HEAD(&entry->rcu);
350 atomic_set(&entry->refcnt, 0);
352 len = pci_msix_get_table_len(pdev);
353 entry->table_len = len;
354 entry->pdev = pdev;
355 entry->gtable = (unsigned long) gtable;
357 list_add_rcu(&entry->list, &d->arch.hvm_domain.msixtbl_list);
358 }
360 static void free_msixtbl_entry(struct rcu_head *rcu)
361 {
362 struct msixtbl_entry *entry;
364 entry = container_of (rcu, struct msixtbl_entry, rcu);
366 xfree(entry);
367 }
369 static void del_msixtbl_entry(struct msixtbl_entry *entry)
370 {
371 list_del_rcu(&entry->list);
372 call_rcu(&entry->rcu, free_msixtbl_entry);
373 }
375 int msixtbl_pt_register(struct domain *d, int pirq, uint64_t gtable)
376 {
377 irq_desc_t *irq_desc;
378 struct msi_desc *msi_desc;
379 struct pci_dev *pdev;
380 struct msixtbl_entry *entry, *new_entry;
381 int r = -EINVAL;
383 ASSERT(spin_is_locked(&pcidevs_lock));
385 /*
386 * xmalloc() with irq_disabled causes the failure of check_lock()
387 * for xenpool->lock. So we allocate an entry beforehand.
388 */
389 new_entry = xmalloc(struct msixtbl_entry);
390 if ( !new_entry )
391 return -ENOMEM;
393 irq_desc = domain_spin_lock_irq_desc(d, pirq, NULL);
394 if ( !irq_desc )
395 {
396 xfree(new_entry);
397 return r;
398 }
400 if ( irq_desc->handler != &pci_msi_type )
401 goto out;
403 msi_desc = irq_desc->msi_desc;
404 if ( !msi_desc )
405 goto out;
407 pdev = msi_desc->dev;
409 spin_lock(&d->arch.hvm_domain.msixtbl_list_lock);
411 list_for_each_entry( entry, &d->arch.hvm_domain.msixtbl_list, list )
412 if ( pdev == entry->pdev )
413 goto found;
415 entry = new_entry;
416 new_entry = NULL;
417 add_msixtbl_entry(d, pdev, gtable, entry);
419 found:
420 atomic_inc(&entry->refcnt);
421 spin_unlock(&d->arch.hvm_domain.msixtbl_list_lock);
422 r = 0;
424 out:
425 spin_unlock_irq(&irq_desc->lock);
426 xfree(new_entry);
427 return r;
428 }
430 void msixtbl_pt_unregister(struct domain *d, int pirq)
431 {
432 irq_desc_t *irq_desc;
433 struct msi_desc *msi_desc;
434 struct pci_dev *pdev;
435 struct msixtbl_entry *entry;
437 ASSERT(spin_is_locked(&pcidevs_lock));
439 irq_desc = domain_spin_lock_irq_desc(d, pirq, NULL);
440 if ( !irq_desc )
441 return;
443 if ( irq_desc->handler != &pci_msi_type )
444 goto out;
446 msi_desc = irq_desc->msi_desc;
447 if ( !msi_desc )
448 goto out;
450 pdev = msi_desc->dev;
452 spin_lock(&d->arch.hvm_domain.msixtbl_list_lock);
454 list_for_each_entry( entry, &d->arch.hvm_domain.msixtbl_list, list )
455 if ( pdev == entry->pdev )
456 goto found;
458 spin_unlock(&d->arch.hvm_domain.msixtbl_list_lock);
461 out:
462 spin_unlock_irq(&irq_desc->lock);
463 return;
465 found:
466 if ( !atomic_dec_and_test(&entry->refcnt) )
467 del_msixtbl_entry(entry);
469 spin_unlock(&d->arch.hvm_domain.msixtbl_list_lock);
470 spin_unlock_irq(&irq_desc->lock);
471 }
473 void msixtbl_pt_cleanup(struct domain *d, int pirq)
474 {
475 struct msixtbl_entry *entry, *temp;
476 unsigned long flags;
478 /* msixtbl_list_lock must be acquired with irq_disabled for check_lock() */
479 local_irq_save(flags);
480 spin_lock(&d->arch.hvm_domain.msixtbl_list_lock);
482 list_for_each_entry_safe( entry, temp,
483 &d->arch.hvm_domain.msixtbl_list, list )
484 del_msixtbl_entry(entry);
486 spin_unlock(&d->arch.hvm_domain.msixtbl_list_lock);
487 local_irq_restore(flags);
488 }