aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen
diff options
context:
space:
mode:
authorKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>2011-07-19 18:56:39 -0400
committerKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>2011-07-19 20:58:31 -0400
commit0513fe9e5b54e47e37217ea078dd870e3825e02d (patch)
treebec70fca92b64ea535539fc243b746c3a1ab7fd3 /drivers/xen
parenta2be65fd363831502afdf0babdf48149b3959cde (diff)
xen/pciback: Allocate IRQ handler for device that is shared with guest.
If the device that is to be shared with a guest is a level device and the IRQ is shared with the initial domain we need to take actions. Mainly we install a dummy IRQ handler that will ACK on the interrupt line so as to not have the initial domain disable the interrupt line. This dummy IRQ handler is not enabled when the device MSI/MSI-X lines are set, nor for edge interrupts. And also not for level interrupts that are not shared amongst devices. Lastly, if the user passes to the guest all of the PCI devices on the shared line the we won't install the dummy handler either. There is also SysFS instrumentation to check its state and turn IRQ ACKing on/off if necessary. Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Diffstat (limited to 'drivers/xen')
-rw-r--r--drivers/xen/xen-pciback/conf_space_capability_msi.c17
-rw-r--r--drivers/xen/xen-pciback/conf_space_header.c6
-rw-r--r--drivers/xen/xen-pciback/pci_stub.c90
-rw-r--r--drivers/xen/xen-pciback/pciback.h13
-rw-r--r--drivers/xen/xen-pciback/pciback_ops.c102
5 files changed, 223 insertions, 5 deletions
diff --git a/drivers/xen/xen-pciback/conf_space_capability_msi.c b/drivers/xen/xen-pciback/conf_space_capability_msi.c
index 78f74b1852d4..d0d2255b5da9 100644
--- a/drivers/xen/xen-pciback/conf_space_capability_msi.c
+++ b/drivers/xen/xen-pciback/conf_space_capability_msi.c
@@ -12,6 +12,7 @@
12int pciback_enable_msi(struct pciback_device *pdev, 12int pciback_enable_msi(struct pciback_device *pdev,
13 struct pci_dev *dev, struct xen_pci_op *op) 13 struct pci_dev *dev, struct xen_pci_op *op)
14{ 14{
15 struct pciback_dev_data *dev_data;
15 int otherend = pdev->xdev->otherend_id; 16 int otherend = pdev->xdev->otherend_id;
16 int status; 17 int status;
17 18
@@ -28,21 +29,29 @@ int pciback_enable_msi(struct pciback_device *pdev,
28 * the local domain's IRQ number. */ 29 * the local domain's IRQ number. */
29 30
30 op->value = dev->irq ? xen_pirq_from_irq(dev->irq) : 0; 31 op->value = dev->irq ? xen_pirq_from_irq(dev->irq) : 0;
32 dev_data = pci_get_drvdata(dev);
33 if (dev_data)
34 dev_data->ack_intr = 0;
31 return 0; 35 return 0;
32} 36}
33 37
34int pciback_disable_msi(struct pciback_device *pdev, 38int pciback_disable_msi(struct pciback_device *pdev,
35 struct pci_dev *dev, struct xen_pci_op *op) 39 struct pci_dev *dev, struct xen_pci_op *op)
36{ 40{
41 struct pciback_dev_data *dev_data;
37 pci_disable_msi(dev); 42 pci_disable_msi(dev);
38 43
39 op->value = dev->irq ? xen_pirq_from_irq(dev->irq) : 0; 44 op->value = dev->irq ? xen_pirq_from_irq(dev->irq) : 0;
45 dev_data = pci_get_drvdata(dev);
46 if (dev_data)
47 dev_data->ack_intr = 1;
40 return 0; 48 return 0;
41} 49}
42 50
43int pciback_enable_msix(struct pciback_device *pdev, 51int pciback_enable_msix(struct pciback_device *pdev,
44 struct pci_dev *dev, struct xen_pci_op *op) 52 struct pci_dev *dev, struct xen_pci_op *op)
45{ 53{
54 struct pciback_dev_data *dev_data;
46 int i, result; 55 int i, result;
47 struct msix_entry *entries; 56 struct msix_entry *entries;
48 57
@@ -74,6 +83,9 @@ int pciback_enable_msix(struct pciback_device *pdev,
74 kfree(entries); 83 kfree(entries);
75 84
76 op->value = result; 85 op->value = result;
86 dev_data = pci_get_drvdata(dev);
87 if (dev_data)
88 dev_data->ack_intr = 0;
77 89
78 return result; 90 return result;
79} 91}
@@ -81,6 +93,7 @@ int pciback_enable_msix(struct pciback_device *pdev,
81int pciback_disable_msix(struct pciback_device *pdev, 93int pciback_disable_msix(struct pciback_device *pdev,
82 struct pci_dev *dev, struct xen_pci_op *op) 94 struct pci_dev *dev, struct xen_pci_op *op)
83{ 95{
96 struct pciback_dev_data *dev_data;
84 97
85 pci_disable_msix(dev); 98 pci_disable_msix(dev);
86 99
@@ -89,6 +102,10 @@ int pciback_disable_msix(struct pciback_device *pdev,
89 * an undefined IRQ value of zero. 102 * an undefined IRQ value of zero.
90 */ 103 */
91 op->value = dev->irq ? xen_pirq_from_irq(dev->irq) : 0; 104 op->value = dev->irq ? xen_pirq_from_irq(dev->irq) : 0;
105 dev_data = pci_get_drvdata(dev);
106 if (dev_data)
107 dev_data->ack_intr = 1;
108
92 return 0; 109 return 0;
93} 110}
94 111
diff --git a/drivers/xen/xen-pciback/conf_space_header.c b/drivers/xen/xen-pciback/conf_space_header.c
index dcd6dd964e3b..22ad0f560669 100644
--- a/drivers/xen/xen-pciback/conf_space_header.c
+++ b/drivers/xen/xen-pciback/conf_space_header.c
@@ -39,8 +39,10 @@ static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data)
39 39
40static int command_write(struct pci_dev *dev, int offset, u16 value, void *data) 40static int command_write(struct pci_dev *dev, int offset, u16 value, void *data)
41{ 41{
42 struct pciback_dev_data *dev_data;
42 int err; 43 int err;
43 44
45 dev_data = pci_get_drvdata(dev);
44 if (!pci_is_enabled(dev) && is_enable_cmd(value)) { 46 if (!pci_is_enabled(dev) && is_enable_cmd(value)) {
45 if (unlikely(verbose_request)) 47 if (unlikely(verbose_request))
46 printk(KERN_DEBUG "pciback: %s: enable\n", 48 printk(KERN_DEBUG "pciback: %s: enable\n",
@@ -48,11 +50,15 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data)
48 err = pci_enable_device(dev); 50 err = pci_enable_device(dev);
49 if (err) 51 if (err)
50 return err; 52 return err;
53 if (dev_data)
54 dev_data->enable_intx = 1;
51 } else if (pci_is_enabled(dev) && !is_enable_cmd(value)) { 55 } else if (pci_is_enabled(dev) && !is_enable_cmd(value)) {
52 if (unlikely(verbose_request)) 56 if (unlikely(verbose_request))
53 printk(KERN_DEBUG "pciback: %s: disable\n", 57 printk(KERN_DEBUG "pciback: %s: disable\n",
54 pci_name(dev)); 58 pci_name(dev));
55 pci_disable_device(dev); 59 pci_disable_device(dev);
60 if (dev_data)
61 dev_data->enable_intx = 0;
56 } 62 }
57 63
58 if (!dev->is_busmaster && is_master_cmd(value)) { 64 if (!dev->is_busmaster && is_master_cmd(value)) {
diff --git a/drivers/xen/xen-pciback/pci_stub.c b/drivers/xen/xen-pciback/pci_stub.c
index ac8396d8206b..c4d1071ebbe6 100644
--- a/drivers/xen/xen-pciback/pci_stub.c
+++ b/drivers/xen/xen-pciback/pci_stub.c
@@ -21,6 +21,8 @@
21#include "conf_space.h" 21#include "conf_space.h"
22#include "conf_space_quirks.h" 22#include "conf_space_quirks.h"
23 23
24#define DRV_NAME "pciback"
25
24static char *pci_devs_to_hide; 26static char *pci_devs_to_hide;
25wait_queue_head_t aer_wait_queue; 27wait_queue_head_t aer_wait_queue;
26/*Add sem for sync AER handling and pciback remove/reconfigue ops, 28/*Add sem for sync AER handling and pciback remove/reconfigue ops,
@@ -290,13 +292,20 @@ static int __devinit pcistub_init_device(struct pci_dev *dev)
290 * would need to be called somewhere to free the memory allocated 292 * would need to be called somewhere to free the memory allocated
291 * here and then to call kfree(pci_get_drvdata(psdev->dev)). 293 * here and then to call kfree(pci_get_drvdata(psdev->dev)).
292 */ 294 */
293 dev_data = kzalloc(sizeof(*dev_data), GFP_ATOMIC); 295 dev_data = kzalloc(sizeof(*dev_data) + strlen(DRV_NAME "[]")
296 + strlen(pci_name(dev)) + 1, GFP_ATOMIC);
294 if (!dev_data) { 297 if (!dev_data) {
295 err = -ENOMEM; 298 err = -ENOMEM;
296 goto out; 299 goto out;
297 } 300 }
298 pci_set_drvdata(dev, dev_data); 301 pci_set_drvdata(dev, dev_data);
299 302
303 /*
304 * Setup name for fake IRQ handler. It will only be enabled
305 * once the device is turned on by the guest.
306 */
307 sprintf(dev_data->irq_name, DRV_NAME "[%s]", pci_name(dev));
308
300 dev_dbg(&dev->dev, "initializing config\n"); 309 dev_dbg(&dev->dev, "initializing config\n");
301 310
302 init_waitqueue_head(&aer_wait_queue); 311 init_waitqueue_head(&aer_wait_queue);
@@ -837,7 +846,7 @@ static struct pci_error_handlers pciback_error_handler = {
837 */ 846 */
838 847
839static struct pci_driver pciback_pci_driver = { 848static struct pci_driver pciback_pci_driver = {
840 .name = "pciback", 849 .name = DRV_NAME,
841 .id_table = pcistub_ids, 850 .id_table = pcistub_ids,
842 .probe = pcistub_probe, 851 .probe = pcistub_probe,
843 .remove = pcistub_remove, 852 .remove = pcistub_remove,
@@ -1029,6 +1038,72 @@ static ssize_t pcistub_slot_show(struct device_driver *drv, char *buf)
1029 1038
1030DRIVER_ATTR(slots, S_IRUSR, pcistub_slot_show, NULL); 1039DRIVER_ATTR(slots, S_IRUSR, pcistub_slot_show, NULL);
1031 1040
1041static ssize_t pcistub_irq_handler_show(struct device_driver *drv, char *buf)
1042{
1043 struct pcistub_device *psdev;
1044 struct pciback_dev_data *dev_data;
1045 size_t count = 0;
1046 unsigned long flags;
1047
1048 spin_lock_irqsave(&pcistub_devices_lock, flags);
1049 list_for_each_entry(psdev, &pcistub_devices, dev_list) {
1050 if (count >= PAGE_SIZE)
1051 break;
1052 if (!psdev->dev)
1053 continue;
1054 dev_data = pci_get_drvdata(psdev->dev);
1055 if (!dev_data)
1056 continue;
1057 count +=
1058 scnprintf(buf + count, PAGE_SIZE - count,
1059 "%s:%s:%sing:%ld\n",
1060 pci_name(psdev->dev),
1061 dev_data->isr_on ? "on" : "off",
1062 dev_data->ack_intr ? "ack" : "not ack",
1063 dev_data->handled);
1064 }
1065 spin_unlock_irqrestore(&pcistub_devices_lock, flags);
1066 return count;
1067}
1068
1069DRIVER_ATTR(irq_handlers, S_IRUSR, pcistub_irq_handler_show, NULL);
1070
1071static ssize_t pcistub_irq_handler_switch(struct device_driver *drv,
1072 const char *buf,
1073 size_t count)
1074{
1075 struct pcistub_device *psdev;
1076 struct pciback_dev_data *dev_data;
1077 int domain, bus, slot, func;
1078 int err = -ENOENT;
1079
1080 err = str_to_slot(buf, &domain, &bus, &slot, &func);
1081 if (err)
1082 goto out;
1083
1084 psdev = pcistub_device_find(domain, bus, slot, func);
1085
1086 if (!psdev)
1087 goto out;
1088
1089 dev_data = pci_get_drvdata(psdev->dev);
1090 if (!dev_data)
1091 goto out;
1092
1093 dev_dbg(&psdev->dev->dev, "%s fake irq handler: %d->%d\n",
1094 dev_data->irq_name, dev_data->isr_on,
1095 !dev_data->isr_on);
1096
1097 dev_data->isr_on = !(dev_data->isr_on);
1098 if (dev_data->isr_on)
1099 dev_data->ack_intr = 1;
1100out:
1101 if (!err)
1102 err = count;
1103 return err;
1104}
1105DRIVER_ATTR(irq_handler_state, S_IWUSR, NULL, pcistub_irq_handler_switch);
1106
1032static ssize_t pcistub_quirk_add(struct device_driver *drv, const char *buf, 1107static ssize_t pcistub_quirk_add(struct device_driver *drv, const char *buf,
1033 size_t count) 1108 size_t count)
1034{ 1109{
@@ -1168,7 +1243,10 @@ static void pcistub_exit(void)
1168 driver_remove_file(&pciback_pci_driver.driver, &driver_attr_slots); 1243 driver_remove_file(&pciback_pci_driver.driver, &driver_attr_slots);
1169 driver_remove_file(&pciback_pci_driver.driver, &driver_attr_quirks); 1244 driver_remove_file(&pciback_pci_driver.driver, &driver_attr_quirks);
1170 driver_remove_file(&pciback_pci_driver.driver, &driver_attr_permissive); 1245 driver_remove_file(&pciback_pci_driver.driver, &driver_attr_permissive);
1171 1246 driver_remove_file(&pciback_pci_driver.driver,
1247 &driver_attr_irq_handlers);
1248 driver_remove_file(&pciback_pci_driver.driver,
1249 &driver_attr_irq_handler_state);
1172 pci_unregister_driver(&pciback_pci_driver); 1250 pci_unregister_driver(&pciback_pci_driver);
1173} 1251}
1174 1252
@@ -1227,6 +1305,12 @@ static int __init pcistub_init(void)
1227 err = driver_create_file(&pciback_pci_driver.driver, 1305 err = driver_create_file(&pciback_pci_driver.driver,
1228 &driver_attr_permissive); 1306 &driver_attr_permissive);
1229 1307
1308 if (!err)
1309 err = driver_create_file(&pciback_pci_driver.driver,
1310 &driver_attr_irq_handlers);
1311 if (!err)
1312 err = driver_create_file(&pciback_pci_driver.driver,
1313 &driver_attr_irq_handler_state);
1230 if (err) 1314 if (err)
1231 pcistub_exit(); 1315 pcistub_exit();
1232 1316
diff --git a/drivers/xen/xen-pciback/pciback.h b/drivers/xen/xen-pciback/pciback.h
index c1e95e88ee9e..5c140200a5ea 100644
--- a/drivers/xen/xen-pciback/pciback.h
+++ b/drivers/xen/xen-pciback/pciback.h
@@ -45,8 +45,14 @@ struct pciback_device {
45 45
46struct pciback_dev_data { 46struct pciback_dev_data {
47 struct list_head config_fields; 47 struct list_head config_fields;
48 int permissive; 48 unsigned int permissive:1;
49 int warned_on_write; 49 unsigned int warned_on_write:1;
50 unsigned int enable_intx:1;
51 unsigned int isr_on:1; /* Whether the IRQ handler is installed. */
52 unsigned int ack_intr:1; /* .. and ACK-ing */
53 unsigned long handled;
54 unsigned int irq; /* Saved in case device transitions to MSI/MSI-X */
55 char irq_name[0]; /* pciback[000:04:00.0] */
50}; 56};
51 57
52/* Used by XenBus and pciback_ops.c */ 58/* Used by XenBus and pciback_ops.c */
@@ -131,3 +137,6 @@ extern int verbose_request;
131void test_and_schedule_op(struct pciback_device *pdev); 137void test_and_schedule_op(struct pciback_device *pdev);
132#endif 138#endif
133 139
140/* Handles shared IRQs that can to device domain and control domain. */
141void pciback_irq_handler(struct pci_dev *dev, int reset);
142irqreturn_t pciback_guest_interrupt(int irq, void *dev_id);
diff --git a/drivers/xen/xen-pciback/pciback_ops.c b/drivers/xen/xen-pciback/pciback_ops.c
index 011db675e437..6c398fde7a83 100644
--- a/drivers/xen/xen-pciback/pciback_ops.c
+++ b/drivers/xen/xen-pciback/pciback_ops.c
@@ -13,6 +13,77 @@
13int verbose_request; 13int verbose_request;
14module_param(verbose_request, int, 0644); 14module_param(verbose_request, int, 0644);
15 15
16/* Ensure a device is has the fake IRQ handler "turned on/off" and is
17 * ready to be exported. This MUST be run after pciback_reset_device
18 * which does the actual PCI device enable/disable.
19 */
20void pciback_control_isr(struct pci_dev *dev, int reset)
21{
22 struct pciback_dev_data *dev_data;
23 int rc;
24 int enable = 0;
25
26 dev_data = pci_get_drvdata(dev);
27 if (!dev_data)
28 return;
29
30 /* We don't deal with bridges */
31 if (dev->hdr_type != PCI_HEADER_TYPE_NORMAL)
32 return;
33
34 if (reset) {
35 dev_data->enable_intx = 0;
36 dev_data->ack_intr = 0;
37 }
38 enable = dev_data->enable_intx;
39
40 /* Asked to disable, but ISR isn't runnig */
41 if (!enable && !dev_data->isr_on)
42 return;
43
44 /* Squirrel away the IRQs in the dev_data. We need this
45 * b/c when device transitions to MSI, the dev->irq is
46 * overwritten with the MSI vector.
47 */
48 if (enable)
49 dev_data->irq = dev->irq;
50
51 dev_dbg(&dev->dev, "%s: #%d %s %s%s %s-> %s\n",
52 dev_data->irq_name,
53 dev_data->irq,
54 pci_is_enabled(dev) ? "on" : "off",
55 dev->msi_enabled ? "MSI" : "",
56 dev->msix_enabled ? "MSI/X" : "",
57 dev_data->isr_on ? "enable" : "disable",
58 enable ? "enable" : "disable");
59
60 if (enable) {
61 rc = request_irq(dev_data->irq,
62 pciback_guest_interrupt, IRQF_SHARED,
63 dev_data->irq_name, dev);
64 if (rc) {
65 dev_err(&dev->dev, "%s: failed to install fake IRQ " \
66 "handler for IRQ %d! (rc:%d)\n",
67 dev_data->irq_name, dev_data->irq, rc);
68 goto out;
69 }
70 } else {
71 free_irq(dev_data->irq, dev);
72 dev_data->irq = 0;
73 }
74 dev_data->isr_on = enable;
75 dev_data->ack_intr = enable;
76out:
77 dev_dbg(&dev->dev, "%s: #%d %s %s%s %s\n",
78 dev_data->irq_name,
79 dev_data->irq,
80 pci_is_enabled(dev) ? "on" : "off",
81 dev->msi_enabled ? "MSI" : "",
82 dev->msix_enabled ? "MSI/X" : "",
83 enable ? (dev_data->isr_on ? "enabled" : "failed to enable") :
84 (dev_data->isr_on ? "failed to disable" : "disabled"));
85}
86
16/* Ensure a device is "turned off" and ready to be exported. 87/* Ensure a device is "turned off" and ready to be exported.
17 * (Also see pciback_config_reset to ensure virtual configuration space is 88 * (Also see pciback_config_reset to ensure virtual configuration space is
18 * ready to be re-exported) 89 * ready to be re-exported)
@@ -21,6 +92,8 @@ void pciback_reset_device(struct pci_dev *dev)
21{ 92{
22 u16 cmd; 93 u16 cmd;
23 94
95 pciback_control_isr(dev, 1 /* reset device */);
96
24 /* Disable devices (but not bridges) */ 97 /* Disable devices (but not bridges) */
25 if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL) { 98 if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL) {
26#ifdef CONFIG_PCI_MSI 99#ifdef CONFIG_PCI_MSI
@@ -78,13 +151,18 @@ void pciback_do_op(struct work_struct *data)
78 struct pciback_device *pdev = 151 struct pciback_device *pdev =
79 container_of(data, struct pciback_device, op_work); 152 container_of(data, struct pciback_device, op_work);
80 struct pci_dev *dev; 153 struct pci_dev *dev;
154 struct pciback_dev_data *dev_data = NULL;
81 struct xen_pci_op *op = &pdev->sh_info->op; 155 struct xen_pci_op *op = &pdev->sh_info->op;
156 int test_intx = 0;
82 157
83 dev = pciback_get_pci_dev(pdev, op->domain, op->bus, op->devfn); 158 dev = pciback_get_pci_dev(pdev, op->domain, op->bus, op->devfn);
84 159
85 if (dev == NULL) 160 if (dev == NULL)
86 op->err = XEN_PCI_ERR_dev_not_found; 161 op->err = XEN_PCI_ERR_dev_not_found;
87 else { 162 else {
163 dev_data = pci_get_drvdata(dev);
164 if (dev_data)
165 test_intx = dev_data->enable_intx;
88 switch (op->cmd) { 166 switch (op->cmd) {
89 case XEN_PCI_OP_conf_read: 167 case XEN_PCI_OP_conf_read:
90 op->err = pciback_config_read(dev, 168 op->err = pciback_config_read(dev,
@@ -113,6 +191,11 @@ void pciback_do_op(struct work_struct *data)
113 break; 191 break;
114 } 192 }
115 } 193 }
194 if (!op->err && dev && dev_data) {
195 /* Transition detected */
196 if ((dev_data->enable_intx != test_intx))
197 pciback_control_isr(dev, 0 /* no reset */);
198 }
116 /* Tell the driver domain that we're done. */ 199 /* Tell the driver domain that we're done. */
117 wmb(); 200 wmb();
118 clear_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags); 201 clear_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags);
@@ -137,3 +220,22 @@ irqreturn_t pciback_handle_event(int irq, void *dev_id)
137 220
138 return IRQ_HANDLED; 221 return IRQ_HANDLED;
139} 222}
223irqreturn_t pciback_guest_interrupt(int irq, void *dev_id)
224{
225 struct pci_dev *dev = (struct pci_dev *)dev_id;
226 struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
227
228 if (dev_data->isr_on && dev_data->ack_intr) {
229 dev_data->handled++;
230 if ((dev_data->handled % 1000) == 0) {
231 if (xen_test_irq_shared(irq)) {
232 printk(KERN_INFO "%s IRQ line is not shared "
233 "with other domains. Turning ISR off\n",
234 dev_data->irq_name);
235 dev_data->ack_intr = 0;
236 }
237 }
238 return IRQ_HANDLED;
239 }
240 return IRQ_NONE;
241}