diff options
Diffstat (limited to 'drivers/xen/events.c')
-rw-r--r-- | drivers/xen/events.c | 147 |
1 files changed, 102 insertions, 45 deletions
diff --git a/drivers/xen/events.c b/drivers/xen/events.c index 97612f548a8e..31af0ac31a98 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c | |||
@@ -105,7 +105,6 @@ struct irq_info | |||
105 | 105 | ||
106 | static struct irq_info *irq_info; | 106 | static struct irq_info *irq_info; |
107 | static int *pirq_to_irq; | 107 | static int *pirq_to_irq; |
108 | static int nr_pirqs; | ||
109 | 108 | ||
110 | static int *evtchn_to_irq; | 109 | static int *evtchn_to_irq; |
111 | struct cpu_evtchn_s { | 110 | struct cpu_evtchn_s { |
@@ -278,17 +277,17 @@ static void bind_evtchn_to_cpu(unsigned int chn, unsigned int cpu) | |||
278 | cpumask_copy(irq_to_desc(irq)->affinity, cpumask_of(cpu)); | 277 | cpumask_copy(irq_to_desc(irq)->affinity, cpumask_of(cpu)); |
279 | #endif | 278 | #endif |
280 | 279 | ||
281 | __clear_bit(chn, cpu_evtchn_mask(cpu_from_irq(irq))); | 280 | clear_bit(chn, cpu_evtchn_mask(cpu_from_irq(irq))); |
282 | __set_bit(chn, cpu_evtchn_mask(cpu)); | 281 | set_bit(chn, cpu_evtchn_mask(cpu)); |
283 | 282 | ||
284 | irq_info[irq].cpu = cpu; | 283 | irq_info[irq].cpu = cpu; |
285 | } | 284 | } |
286 | 285 | ||
287 | static void init_evtchn_cpu_bindings(void) | 286 | static void init_evtchn_cpu_bindings(void) |
288 | { | 287 | { |
288 | int i; | ||
289 | #ifdef CONFIG_SMP | 289 | #ifdef CONFIG_SMP |
290 | struct irq_desc *desc; | 290 | struct irq_desc *desc; |
291 | int i; | ||
292 | 291 | ||
293 | /* By default all event channels notify CPU#0. */ | 292 | /* By default all event channels notify CPU#0. */ |
294 | for_each_irq_desc(i, desc) { | 293 | for_each_irq_desc(i, desc) { |
@@ -296,7 +295,10 @@ static void init_evtchn_cpu_bindings(void) | |||
296 | } | 295 | } |
297 | #endif | 296 | #endif |
298 | 297 | ||
299 | memset(cpu_evtchn_mask(0), ~0, sizeof(struct cpu_evtchn_s)); | 298 | for_each_possible_cpu(i) |
299 | memset(cpu_evtchn_mask(i), | ||
300 | (i == 0) ? ~0 : 0, sizeof(struct cpu_evtchn_s)); | ||
301 | |||
300 | } | 302 | } |
301 | 303 | ||
302 | static inline void clear_evtchn(int port) | 304 | static inline void clear_evtchn(int port) |
@@ -382,12 +384,17 @@ static int get_nr_hw_irqs(void) | |||
382 | return ret; | 384 | return ret; |
383 | } | 385 | } |
384 | 386 | ||
385 | /* callers of this function should make sure that PHYSDEVOP_get_nr_pirqs | 387 | static int find_unbound_pirq(int type) |
386 | * succeeded otherwise nr_pirqs won't hold the right value */ | ||
387 | static int find_unbound_pirq(void) | ||
388 | { | 388 | { |
389 | int i; | 389 | int rc, i; |
390 | for (i = nr_pirqs-1; i >= 0; i--) { | 390 | struct physdev_get_free_pirq op_get_free_pirq; |
391 | op_get_free_pirq.type = type; | ||
392 | |||
393 | rc = HYPERVISOR_physdev_op(PHYSDEVOP_get_free_pirq, &op_get_free_pirq); | ||
394 | if (!rc) | ||
395 | return op_get_free_pirq.pirq; | ||
396 | |||
397 | for (i = 0; i < nr_irqs; i++) { | ||
391 | if (pirq_to_irq[i] < 0) | 398 | if (pirq_to_irq[i] < 0) |
392 | return i; | 399 | return i; |
393 | } | 400 | } |
@@ -420,7 +427,7 @@ static int find_unbound_irq(void) | |||
420 | if (irq == start) | 427 | if (irq == start) |
421 | goto no_irqs; | 428 | goto no_irqs; |
422 | 429 | ||
423 | res = irq_alloc_desc_at(irq, 0); | 430 | res = irq_alloc_desc_at(irq, -1); |
424 | 431 | ||
425 | if (WARN_ON(res != irq)) | 432 | if (WARN_ON(res != irq)) |
426 | return -1; | 433 | return -1; |
@@ -608,10 +615,10 @@ int xen_map_pirq_gsi(unsigned pirq, unsigned gsi, int shareable, char *name) | |||
608 | 615 | ||
609 | spin_lock(&irq_mapping_update_lock); | 616 | spin_lock(&irq_mapping_update_lock); |
610 | 617 | ||
611 | if ((pirq > nr_pirqs) || (gsi > nr_irqs)) { | 618 | if ((pirq > nr_irqs) || (gsi > nr_irqs)) { |
612 | printk(KERN_WARNING "xen_map_pirq_gsi: %s %s is incorrect!\n", | 619 | printk(KERN_WARNING "xen_map_pirq_gsi: %s %s is incorrect!\n", |
613 | pirq > nr_pirqs ? "nr_pirqs" :"", | 620 | pirq > nr_irqs ? "pirq" :"", |
614 | gsi > nr_irqs ? "nr_irqs" : ""); | 621 | gsi > nr_irqs ? "gsi" : ""); |
615 | goto out; | 622 | goto out; |
616 | } | 623 | } |
617 | 624 | ||
@@ -627,7 +634,7 @@ int xen_map_pirq_gsi(unsigned pirq, unsigned gsi, int shareable, char *name) | |||
627 | if (identity_mapped_irq(gsi) || (!xen_initial_domain() && | 634 | if (identity_mapped_irq(gsi) || (!xen_initial_domain() && |
628 | xen_pv_domain())) { | 635 | xen_pv_domain())) { |
629 | irq = gsi; | 636 | irq = gsi; |
630 | irq_alloc_desc_at(irq, 0); | 637 | irq_alloc_desc_at(irq, -1); |
631 | } else | 638 | } else |
632 | irq = find_unbound_irq(); | 639 | irq = find_unbound_irq(); |
633 | 640 | ||
@@ -661,17 +668,21 @@ out: | |||
661 | #include <linux/msi.h> | 668 | #include <linux/msi.h> |
662 | #include "../pci/msi.h" | 669 | #include "../pci/msi.h" |
663 | 670 | ||
664 | void xen_allocate_pirq_msi(char *name, int *irq, int *pirq) | 671 | void xen_allocate_pirq_msi(char *name, int *irq, int *pirq, int alloc) |
665 | { | 672 | { |
666 | spin_lock(&irq_mapping_update_lock); | 673 | spin_lock(&irq_mapping_update_lock); |
667 | 674 | ||
668 | *irq = find_unbound_irq(); | 675 | if (alloc & XEN_ALLOC_IRQ) { |
669 | if (*irq == -1) | 676 | *irq = find_unbound_irq(); |
670 | goto out; | 677 | if (*irq == -1) |
678 | goto out; | ||
679 | } | ||
671 | 680 | ||
672 | *pirq = find_unbound_pirq(); | 681 | if (alloc & XEN_ALLOC_PIRQ) { |
673 | if (*pirq == -1) | 682 | *pirq = find_unbound_pirq(MAP_PIRQ_TYPE_MSI); |
674 | goto out; | 683 | if (*pirq == -1) |
684 | goto out; | ||
685 | } | ||
675 | 686 | ||
676 | set_irq_chip_and_handler_name(*irq, &xen_pirq_chip, | 687 | set_irq_chip_and_handler_name(*irq, &xen_pirq_chip, |
677 | handle_level_irq, name); | 688 | handle_level_irq, name); |
@@ -752,13 +763,14 @@ int xen_destroy_irq(int irq) | |||
752 | goto out; | 763 | goto out; |
753 | 764 | ||
754 | if (xen_initial_domain()) { | 765 | if (xen_initial_domain()) { |
755 | unmap_irq.pirq = info->u.pirq.gsi; | 766 | unmap_irq.pirq = info->u.pirq.pirq; |
756 | unmap_irq.domid = DOMID_SELF; | 767 | unmap_irq.domid = DOMID_SELF; |
757 | rc = HYPERVISOR_physdev_op(PHYSDEVOP_unmap_pirq, &unmap_irq); | 768 | rc = HYPERVISOR_physdev_op(PHYSDEVOP_unmap_pirq, &unmap_irq); |
758 | if (rc) { | 769 | if (rc) { |
759 | printk(KERN_WARNING "unmap irq failed %d\n", rc); | 770 | printk(KERN_WARNING "unmap irq failed %d\n", rc); |
760 | goto out; | 771 | goto out; |
761 | } | 772 | } |
773 | pirq_to_irq[info->u.pirq.pirq] = -1; | ||
762 | } | 774 | } |
763 | irq_info[irq] = mk_unbound_info(); | 775 | irq_info[irq] = mk_unbound_info(); |
764 | 776 | ||
@@ -779,6 +791,11 @@ int xen_gsi_from_irq(unsigned irq) | |||
779 | return gsi_from_irq(irq); | 791 | return gsi_from_irq(irq); |
780 | } | 792 | } |
781 | 793 | ||
794 | int xen_irq_from_pirq(unsigned pirq) | ||
795 | { | ||
796 | return pirq_to_irq[pirq]; | ||
797 | } | ||
798 | |||
782 | int bind_evtchn_to_irq(unsigned int evtchn) | 799 | int bind_evtchn_to_irq(unsigned int evtchn) |
783 | { | 800 | { |
784 | int irq; | 801 | int irq; |
@@ -1276,6 +1293,42 @@ static int retrigger_dynirq(unsigned int irq) | |||
1276 | return ret; | 1293 | return ret; |
1277 | } | 1294 | } |
1278 | 1295 | ||
1296 | static void restore_cpu_pirqs(void) | ||
1297 | { | ||
1298 | int pirq, rc, irq, gsi; | ||
1299 | struct physdev_map_pirq map_irq; | ||
1300 | |||
1301 | for (pirq = 0; pirq < nr_irqs; pirq++) { | ||
1302 | irq = pirq_to_irq[pirq]; | ||
1303 | if (irq == -1) | ||
1304 | continue; | ||
1305 | |||
1306 | /* save/restore of PT devices doesn't work, so at this point the | ||
1307 | * only devices present are GSI based emulated devices */ | ||
1308 | gsi = gsi_from_irq(irq); | ||
1309 | if (!gsi) | ||
1310 | continue; | ||
1311 | |||
1312 | map_irq.domid = DOMID_SELF; | ||
1313 | map_irq.type = MAP_PIRQ_TYPE_GSI; | ||
1314 | map_irq.index = gsi; | ||
1315 | map_irq.pirq = pirq; | ||
1316 | |||
1317 | rc = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq, &map_irq); | ||
1318 | if (rc) { | ||
1319 | printk(KERN_WARNING "xen map irq failed gsi=%d irq=%d pirq=%d rc=%d\n", | ||
1320 | gsi, irq, pirq, rc); | ||
1321 | irq_info[irq] = mk_unbound_info(); | ||
1322 | pirq_to_irq[pirq] = -1; | ||
1323 | continue; | ||
1324 | } | ||
1325 | |||
1326 | printk(KERN_DEBUG "xen: --> irq=%d, pirq=%d\n", irq, map_irq.pirq); | ||
1327 | |||
1328 | startup_pirq(irq); | ||
1329 | } | ||
1330 | } | ||
1331 | |||
1279 | static void restore_cpu_virqs(unsigned int cpu) | 1332 | static void restore_cpu_virqs(unsigned int cpu) |
1280 | { | 1333 | { |
1281 | struct evtchn_bind_virq bind_virq; | 1334 | struct evtchn_bind_virq bind_virq; |
@@ -1299,9 +1352,6 @@ static void restore_cpu_virqs(unsigned int cpu) | |||
1299 | evtchn_to_irq[evtchn] = irq; | 1352 | evtchn_to_irq[evtchn] = irq; |
1300 | irq_info[irq] = mk_virq_info(evtchn, virq); | 1353 | irq_info[irq] = mk_virq_info(evtchn, virq); |
1301 | bind_evtchn_to_cpu(evtchn, cpu); | 1354 | bind_evtchn_to_cpu(evtchn, cpu); |
1302 | |||
1303 | /* Ready for use. */ | ||
1304 | unmask_evtchn(evtchn); | ||
1305 | } | 1355 | } |
1306 | } | 1356 | } |
1307 | 1357 | ||
@@ -1327,10 +1377,6 @@ static void restore_cpu_ipis(unsigned int cpu) | |||
1327 | evtchn_to_irq[evtchn] = irq; | 1377 | evtchn_to_irq[evtchn] = irq; |
1328 | irq_info[irq] = mk_ipi_info(evtchn, ipi); | 1378 | irq_info[irq] = mk_ipi_info(evtchn, ipi); |
1329 | bind_evtchn_to_cpu(evtchn, cpu); | 1379 | bind_evtchn_to_cpu(evtchn, cpu); |
1330 | |||
1331 | /* Ready for use. */ | ||
1332 | unmask_evtchn(evtchn); | ||
1333 | |||
1334 | } | 1380 | } |
1335 | } | 1381 | } |
1336 | 1382 | ||
@@ -1390,6 +1436,7 @@ void xen_poll_irq(int irq) | |||
1390 | void xen_irq_resume(void) | 1436 | void xen_irq_resume(void) |
1391 | { | 1437 | { |
1392 | unsigned int cpu, irq, evtchn; | 1438 | unsigned int cpu, irq, evtchn; |
1439 | struct irq_desc *desc; | ||
1393 | 1440 | ||
1394 | init_evtchn_cpu_bindings(); | 1441 | init_evtchn_cpu_bindings(); |
1395 | 1442 | ||
@@ -1408,6 +1455,25 @@ void xen_irq_resume(void) | |||
1408 | restore_cpu_virqs(cpu); | 1455 | restore_cpu_virqs(cpu); |
1409 | restore_cpu_ipis(cpu); | 1456 | restore_cpu_ipis(cpu); |
1410 | } | 1457 | } |
1458 | |||
1459 | /* | ||
1460 | * Unmask any IRQF_NO_SUSPEND IRQs which are enabled. These | ||
1461 | * are not handled by the IRQ core. | ||
1462 | */ | ||
1463 | for_each_irq_desc(irq, desc) { | ||
1464 | if (!desc->action || !(desc->action->flags & IRQF_NO_SUSPEND)) | ||
1465 | continue; | ||
1466 | if (desc->status & IRQ_DISABLED) | ||
1467 | continue; | ||
1468 | |||
1469 | evtchn = evtchn_from_irq(irq); | ||
1470 | if (evtchn == -1) | ||
1471 | continue; | ||
1472 | |||
1473 | unmask_evtchn(evtchn); | ||
1474 | } | ||
1475 | |||
1476 | restore_cpu_pirqs(); | ||
1411 | } | 1477 | } |
1412 | 1478 | ||
1413 | static struct irq_chip xen_dynamic_chip __read_mostly = { | 1479 | static struct irq_chip xen_dynamic_chip __read_mostly = { |
@@ -1492,26 +1558,17 @@ void xen_callback_vector(void) {} | |||
1492 | 1558 | ||
1493 | void __init xen_init_IRQ(void) | 1559 | void __init xen_init_IRQ(void) |
1494 | { | 1560 | { |
1495 | int i, rc; | 1561 | int i; |
1496 | struct physdev_nr_pirqs op_nr_pirqs; | ||
1497 | 1562 | ||
1498 | cpu_evtchn_mask_p = kcalloc(nr_cpu_ids, sizeof(struct cpu_evtchn_s), | 1563 | cpu_evtchn_mask_p = kcalloc(nr_cpu_ids, sizeof(struct cpu_evtchn_s), |
1499 | GFP_KERNEL); | 1564 | GFP_KERNEL); |
1500 | irq_info = kcalloc(nr_irqs, sizeof(*irq_info), GFP_KERNEL); | 1565 | irq_info = kcalloc(nr_irqs, sizeof(*irq_info), GFP_KERNEL); |
1501 | 1566 | ||
1502 | rc = HYPERVISOR_physdev_op(PHYSDEVOP_get_nr_pirqs, &op_nr_pirqs); | 1567 | /* We are using nr_irqs as the maximum number of pirq available but |
1503 | if (rc < 0) { | 1568 | * that number is actually chosen by Xen and we don't know exactly |
1504 | nr_pirqs = nr_irqs; | 1569 | * what it is. Be careful choosing high pirq numbers. */ |
1505 | if (rc != -ENOSYS) | 1570 | pirq_to_irq = kcalloc(nr_irqs, sizeof(*pirq_to_irq), GFP_KERNEL); |
1506 | printk(KERN_WARNING "PHYSDEVOP_get_nr_pirqs returned rc=%d\n", rc); | 1571 | for (i = 0; i < nr_irqs; i++) |
1507 | } else { | ||
1508 | if (xen_pv_domain() && !xen_initial_domain()) | ||
1509 | nr_pirqs = max((int)op_nr_pirqs.nr_pirqs, nr_irqs); | ||
1510 | else | ||
1511 | nr_pirqs = op_nr_pirqs.nr_pirqs; | ||
1512 | } | ||
1513 | pirq_to_irq = kcalloc(nr_pirqs, sizeof(*pirq_to_irq), GFP_KERNEL); | ||
1514 | for (i = 0; i < nr_pirqs; i++) | ||
1515 | pirq_to_irq[i] = -1; | 1572 | pirq_to_irq[i] = -1; |
1516 | 1573 | ||
1517 | evtchn_to_irq = kcalloc(NR_EVENT_CHANNELS, sizeof(*evtchn_to_irq), | 1574 | evtchn_to_irq = kcalloc(NR_EVENT_CHANNELS, sizeof(*evtchn_to_irq), |