aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen/xen-pciback
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/xen/xen-pciback')
-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 78f74b1852d..d0d2255b5da 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 dcd6dd964e3..22ad0f56066 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 ac8396d8206..c4d1071ebbe 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 c1e95e88ee9..5c140200a5e 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 011db675e43..6c398fde7a8 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}