Blame view
drivers/xen/xen-pciback/pciback_ops.c
11.5 KB
30edc14bf xen/pciback: xen ... |
1 2 3 4 5 |
/* * PCI Backend Operations - respond to PCI requests from Frontend * * Author: Ryan Wilson <hap9@epoch.ncsc.mil> */ |
283c0972d xen: Convert prin... |
6 7 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
30edc14bf xen/pciback: xen ... |
8 9 10 11 12 13 14 15 16 |
#include <linux/module.h> #include <linux/wait.h> #include <linux/bitops.h> #include <xen/events.h> #include <linux/sched.h> #include "pciback.h" int verbose_request; module_param(verbose_request, int, 0644); |
a92336a11 xen/pciback: Drop... |
17 |
static irqreturn_t xen_pcibk_guest_interrupt(int irq, void *dev_id); |
0513fe9e5 xen/pciback: Allo... |
18 |
/* Ensure a device is has the fake IRQ handler "turned on/off" and is |
a92336a11 xen/pciback: Drop... |
19 |
* ready to be exported. This MUST be run after xen_pcibk_reset_device |
0513fe9e5 xen/pciback: Allo... |
20 21 |
* which does the actual PCI device enable/disable. */ |
a92336a11 xen/pciback: Drop... |
22 |
static void xen_pcibk_control_isr(struct pci_dev *dev, int reset) |
0513fe9e5 xen/pciback: Allo... |
23 |
{ |
a92336a11 xen/pciback: Drop... |
24 |
struct xen_pcibk_dev_data *dev_data; |
0513fe9e5 xen/pciback: Allo... |
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
int rc; int enable = 0; dev_data = pci_get_drvdata(dev); if (!dev_data) return; /* We don't deal with bridges */ if (dev->hdr_type != PCI_HEADER_TYPE_NORMAL) return; if (reset) { dev_data->enable_intx = 0; dev_data->ack_intr = 0; } enable = dev_data->enable_intx; /* Asked to disable, but ISR isn't runnig */ if (!enable && !dev_data->isr_on) return; /* Squirrel away the IRQs in the dev_data. We need this * b/c when device transitions to MSI, the dev->irq is * overwritten with the MSI vector. */ if (enable) dev_data->irq = dev->irq; |
e17ab35f0 xen/pciback: Don'... |
52 53 54 55 56 57 |
/* * SR-IOV devices in all use MSI-X and have no legacy * interrupts, so inhibit creating a fake IRQ handler for them. */ if (dev_data->irq == 0) goto out; |
0513fe9e5 xen/pciback: Allo... |
58 59 60 61 62 63 64 65 66 67 68 |
dev_dbg(&dev->dev, "%s: #%d %s %s%s %s-> %s ", dev_data->irq_name, dev_data->irq, pci_is_enabled(dev) ? "on" : "off", dev->msi_enabled ? "MSI" : "", dev->msix_enabled ? "MSI/X" : "", dev_data->isr_on ? "enable" : "disable", enable ? "enable" : "disable"); if (enable) { |
a396f3a21 xen/pciback: Do n... |
69 70 71 72 73 74 |
/* * The MSI or MSI-X should not have an IRQ handler. Otherwise * if the guest terminates we BUG_ON in free_msi_irqs. */ if (dev->msi_enabled || dev->msix_enabled) goto out; |
0513fe9e5 xen/pciback: Allo... |
75 |
rc = request_irq(dev_data->irq, |
a92336a11 xen/pciback: Drop... |
76 |
xen_pcibk_guest_interrupt, IRQF_SHARED, |
0513fe9e5 xen/pciback: Allo... |
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
dev_data->irq_name, dev); if (rc) { dev_err(&dev->dev, "%s: failed to install fake IRQ " \ "handler for IRQ %d! (rc:%d) ", dev_data->irq_name, dev_data->irq, rc); goto out; } } else { free_irq(dev_data->irq, dev); dev_data->irq = 0; } dev_data->isr_on = enable; dev_data->ack_intr = enable; out: dev_dbg(&dev->dev, "%s: #%d %s %s%s %s ", dev_data->irq_name, dev_data->irq, pci_is_enabled(dev) ? "on" : "off", dev->msi_enabled ? "MSI" : "", dev->msix_enabled ? "MSI/X" : "", enable ? (dev_data->isr_on ? "enabled" : "failed to enable") : (dev_data->isr_on ? "failed to disable" : "disabled")); } |
30edc14bf xen/pciback: xen ... |
102 |
/* Ensure a device is "turned off" and ready to be exported. |
a92336a11 xen/pciback: Drop... |
103 |
* (Also see xen_pcibk_config_reset to ensure virtual configuration space is |
30edc14bf xen/pciback: xen ... |
104 105 |
* ready to be re-exported) */ |
a92336a11 xen/pciback: Drop... |
106 |
void xen_pcibk_reset_device(struct pci_dev *dev) |
30edc14bf xen/pciback: xen ... |
107 108 |
{ u16 cmd; |
a92336a11 xen/pciback: Drop... |
109 |
xen_pcibk_control_isr(dev, 1 /* reset device */); |
0513fe9e5 xen/pciback: Allo... |
110 |
|
30edc14bf xen/pciback: xen ... |
111 112 |
/* Disable devices (but not bridges) */ if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL) { |
a2be65fd3 xen/pciback: Disa... |
113 114 115 116 117 118 119 120 |
#ifdef CONFIG_PCI_MSI /* The guest could have been abruptly killed without * disabling MSI/MSI-X interrupts.*/ if (dev->msix_enabled) pci_disable_msix(dev); if (dev->msi_enabled) pci_disable_msi(dev); #endif |
bdc5c1812 xen/pciback: Don'... |
121 122 |
if (pci_is_enabled(dev)) pci_disable_device(dev); |
30edc14bf xen/pciback: xen ... |
123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
pci_write_config_word(dev, PCI_COMMAND, 0); dev->is_busmaster = 0; } else { pci_read_config_word(dev, PCI_COMMAND, &cmd); if (cmd & (PCI_COMMAND_INVALIDATE)) { cmd &= ~(PCI_COMMAND_INVALIDATE); pci_write_config_word(dev, PCI_COMMAND, cmd); dev->is_busmaster = 0; } } } |
a92336a11 xen/pciback: Drop... |
137 138 139 140 141 142 143 |
#ifdef CONFIG_PCI_MSI static int xen_pcibk_enable_msi(struct xen_pcibk_device *pdev, struct pci_dev *dev, struct xen_pci_op *op) { struct xen_pcibk_dev_data *dev_data; |
a92336a11 xen/pciback: Drop... |
144 145 146 147 148 |
int status; if (unlikely(verbose_request)) printk(KERN_DEBUG DRV_NAME ": %s: enable MSI ", pci_name(dev)); |
56441f3c8 xen/pciback: Retu... |
149 150 151 152 153 154 |
if (dev->msi_enabled) status = -EALREADY; else if (dev->msix_enabled) status = -ENXIO; else status = pci_enable_msi(dev); |
a92336a11 xen/pciback: Drop... |
155 156 |
if (status) { |
283c0972d xen: Convert prin... |
157 158 |
pr_warn_ratelimited("%s: error enabling MSI for guest %u: err %d ", |
51ac8893a xen-pciback: rate... |
159 160 |
pci_name(dev), pdev->xdev->otherend_id, status); |
a92336a11 xen/pciback: Drop... |
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
op->value = 0; return XEN_PCI_ERR_op_failed; } /* The value the guest needs is actually the IDT vector, not the * the local domain's IRQ number. */ op->value = dev->irq ? xen_pirq_from_irq(dev->irq) : 0; if (unlikely(verbose_request)) printk(KERN_DEBUG DRV_NAME ": %s: MSI: %d ", pci_name(dev), op->value); dev_data = pci_get_drvdata(dev); if (dev_data) dev_data->ack_intr = 0; return 0; } static int xen_pcibk_disable_msi(struct xen_pcibk_device *pdev, struct pci_dev *dev, struct xen_pci_op *op) { |
a92336a11 xen/pciback: Drop... |
185 186 187 188 |
if (unlikely(verbose_request)) printk(KERN_DEBUG DRV_NAME ": %s: disable MSI ", pci_name(dev)); |
a92336a11 xen/pciback: Drop... |
189 |
|
7cfb905b9 xen/pciback: For ... |
190 191 192 193 194 195 196 197 198 |
if (dev->msi_enabled) { struct xen_pcibk_dev_data *dev_data; pci_disable_msi(dev); dev_data = pci_get_drvdata(dev); if (dev_data) dev_data->ack_intr = 1; } |
a92336a11 xen/pciback: Drop... |
199 200 201 202 203 |
op->value = dev->irq ? xen_pirq_from_irq(dev->irq) : 0; if (unlikely(verbose_request)) printk(KERN_DEBUG DRV_NAME ": %s: MSI: %d ", pci_name(dev), op->value); |
a92336a11 xen/pciback: Drop... |
204 205 206 207 208 209 210 211 212 213 |
return 0; } static int xen_pcibk_enable_msix(struct xen_pcibk_device *pdev, struct pci_dev *dev, struct xen_pci_op *op) { struct xen_pcibk_dev_data *dev_data; int i, result; struct msix_entry *entries; |
408fb0e5a xen/pciback: Don'... |
214 |
u16 cmd; |
a92336a11 xen/pciback: Drop... |
215 216 217 218 219 |
if (unlikely(verbose_request)) printk(KERN_DEBUG DRV_NAME ": %s: enable MSI-X ", pci_name(dev)); |
5e0ce1455 xen/pciback: Retu... |
220 |
|
a92336a11 xen/pciback: Drop... |
221 222 |
if (op->value > SH_INFO_MAX_VEC) return -EINVAL; |
5e0ce1455 xen/pciback: Retu... |
223 224 |
if (dev->msix_enabled) return -EALREADY; |
408fb0e5a xen/pciback: Don'... |
225 226 227 228 229 230 |
/* * PCI_COMMAND_MEMORY must be enabled, otherwise we may not be able * to access the BARs where the MSI-X entries reside. */ pci_read_config_word(dev, PCI_COMMAND, &cmd); if (dev->msi_enabled || !(cmd & PCI_COMMAND_MEMORY)) |
5e0ce1455 xen/pciback: Retu... |
231 |
return -ENXIO; |
a92336a11 xen/pciback: Drop... |
232 233 234 235 236 237 238 239 |
entries = kmalloc(op->value * sizeof(*entries), GFP_KERNEL); if (entries == NULL) return -ENOMEM; for (i = 0; i < op->value; i++) { entries[i].entry = op->msix_entries[i].entry; entries[i].vector = op->msix_entries[i].vector; } |
efdfa3eda xen-pciback: Use ... |
240 |
result = pci_enable_msix_exact(dev, entries, op->value); |
a92336a11 xen/pciback: Drop... |
241 242 243 |
if (result == 0) { for (i = 0; i < op->value; i++) { op->msix_entries[i].entry = entries[i].entry; |
c0914e616 xen-pciback: sile... |
244 |
if (entries[i].vector) { |
a92336a11 xen/pciback: Drop... |
245 246 247 248 249 250 251 252 |
op->msix_entries[i].vector = xen_pirq_from_irq(entries[i].vector); if (unlikely(verbose_request)) printk(KERN_DEBUG DRV_NAME ": %s: " \ "MSI-X[%d]: %d ", pci_name(dev), i, op->msix_entries[i].vector); |
c0914e616 xen-pciback: sile... |
253 |
} |
a92336a11 xen/pciback: Drop... |
254 |
} |
51ac8893a xen-pciback: rate... |
255 |
} else |
283c0972d xen: Convert prin... |
256 257 |
pr_warn_ratelimited("%s: error enabling MSI-X for guest %u: err %d! ", |
51ac8893a xen-pciback: rate... |
258 259 |
pci_name(dev), pdev->xdev->otherend_id, result); |
a92336a11 xen/pciback: Drop... |
260 261 262 263 264 265 |
kfree(entries); op->value = result; dev_data = pci_get_drvdata(dev); if (dev_data) dev_data->ack_intr = 0; |
0ee46eca0 xen/pciback: fix ... |
266 |
return result > 0 ? 0 : result; |
a92336a11 xen/pciback: Drop... |
267 268 269 270 271 272 |
} static int xen_pcibk_disable_msix(struct xen_pcibk_device *pdev, struct pci_dev *dev, struct xen_pci_op *op) { |
a92336a11 xen/pciback: Drop... |
273 274 275 276 |
if (unlikely(verbose_request)) printk(KERN_DEBUG DRV_NAME ": %s: disable MSI-X ", pci_name(dev)); |
a92336a11 xen/pciback: Drop... |
277 |
|
7cfb905b9 xen/pciback: For ... |
278 279 280 281 282 283 284 285 286 |
if (dev->msix_enabled) { struct xen_pcibk_dev_data *dev_data; pci_disable_msix(dev); dev_data = pci_get_drvdata(dev); if (dev_data) dev_data->ack_intr = 1; } |
a92336a11 xen/pciback: Drop... |
287 288 289 290 291 292 |
/* * SR-IOV devices (which don't have any legacy IRQ) have * an undefined IRQ value of zero. */ op->value = dev->irq ? xen_pirq_from_irq(dev->irq) : 0; if (unlikely(verbose_request)) |
7cfb905b9 xen/pciback: For ... |
293 294 295 |
printk(KERN_DEBUG DRV_NAME ": %s: MSI-X: %d ", pci_name(dev), op->value); |
a92336a11 xen/pciback: Drop... |
296 297 298 |
return 0; } #endif |
30edc14bf xen/pciback: xen ... |
299 300 301 |
/* * Now the same evtchn is used for both pcifront conf_read_write request * as well as pcie aer front end ack. We use a new work_queue to schedule |
a92336a11 xen/pciback: Drop... |
302 |
* xen_pcibk conf_read_write service for avoiding confict with aer_core |
30edc14bf xen/pciback: xen ... |
303 304 |
* do_recovery job which also use the system default work_queue */ |
a92336a11 xen/pciback: Drop... |
305 |
void xen_pcibk_test_and_schedule_op(struct xen_pcibk_device *pdev) |
30edc14bf xen/pciback: xen ... |
306 307 308 309 310 |
{ /* Check that frontend is requesting an operation and that we are not * already processing a request */ if (test_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags) && !test_and_set_bit(_PDEVF_op_active, &pdev->flags)) { |
a92336a11 xen/pciback: Drop... |
311 |
queue_work(xen_pcibk_wq, &pdev->op_work); |
30edc14bf xen/pciback: xen ... |
312 313 |
} /*_XEN_PCIB_active should have been cleared by pcifront. And also make |
a92336a11 xen/pciback: Drop... |
314 |
sure xen_pcibk is waiting for ack by checking _PCIB_op_pending*/ |
30edc14bf xen/pciback: xen ... |
315 316 |
if (!test_bit(_XEN_PCIB_active, (unsigned long *)&pdev->sh_info->flags) && test_bit(_PCIB_op_pending, &pdev->flags)) { |
a92336a11 xen/pciback: Drop... |
317 |
wake_up(&xen_pcibk_aer_wait_queue); |
30edc14bf xen/pciback: xen ... |
318 319 320 321 322 323 |
} } /* Performing the configuration space reads/writes must not be done in atomic * context because some of the pci_* functions can sleep (mostly due to ACPI * use of semaphores). This function is intended to be called from a work |
a92336a11 xen/pciback: Drop... |
324 |
* queue in process context taking a struct xen_pcibk_device as a parameter */ |
30edc14bf xen/pciback: xen ... |
325 |
|
a92336a11 xen/pciback: Drop... |
326 |
void xen_pcibk_do_op(struct work_struct *data) |
30edc14bf xen/pciback: xen ... |
327 |
{ |
a92336a11 xen/pciback: Drop... |
328 329 |
struct xen_pcibk_device *pdev = container_of(data, struct xen_pcibk_device, op_work); |
30edc14bf xen/pciback: xen ... |
330 |
struct pci_dev *dev; |
a92336a11 xen/pciback: Drop... |
331 |
struct xen_pcibk_dev_data *dev_data = NULL; |
8135cf8b0 xen/pciback: Save... |
332 |
struct xen_pci_op *op = &pdev->op; |
0513fe9e5 xen/pciback: Allo... |
333 |
int test_intx = 0; |
30edc14bf xen/pciback: xen ... |
334 |
|
8135cf8b0 xen/pciback: Save... |
335 336 |
*op = pdev->sh_info->op; barrier(); |
a92336a11 xen/pciback: Drop... |
337 |
dev = xen_pcibk_get_pci_dev(pdev, op->domain, op->bus, op->devfn); |
30edc14bf xen/pciback: xen ... |
338 339 340 341 |
if (dev == NULL) op->err = XEN_PCI_ERR_dev_not_found; else { |
0513fe9e5 xen/pciback: Allo... |
342 343 344 |
dev_data = pci_get_drvdata(dev); if (dev_data) test_intx = dev_data->enable_intx; |
30edc14bf xen/pciback: xen ... |
345 346 |
switch (op->cmd) { case XEN_PCI_OP_conf_read: |
a92336a11 xen/pciback: Drop... |
347 |
op->err = xen_pcibk_config_read(dev, |
30edc14bf xen/pciback: xen ... |
348 349 350 |
op->offset, op->size, &op->value); break; case XEN_PCI_OP_conf_write: |
a92336a11 xen/pciback: Drop... |
351 |
op->err = xen_pcibk_config_write(dev, |
30edc14bf xen/pciback: xen ... |
352 353 354 355 |
op->offset, op->size, op->value); break; #ifdef CONFIG_PCI_MSI case XEN_PCI_OP_enable_msi: |
a92336a11 xen/pciback: Drop... |
356 |
op->err = xen_pcibk_enable_msi(pdev, dev, op); |
30edc14bf xen/pciback: xen ... |
357 358 |
break; case XEN_PCI_OP_disable_msi: |
a92336a11 xen/pciback: Drop... |
359 |
op->err = xen_pcibk_disable_msi(pdev, dev, op); |
30edc14bf xen/pciback: xen ... |
360 361 |
break; case XEN_PCI_OP_enable_msix: |
a92336a11 xen/pciback: Drop... |
362 |
op->err = xen_pcibk_enable_msix(pdev, dev, op); |
30edc14bf xen/pciback: xen ... |
363 364 |
break; case XEN_PCI_OP_disable_msix: |
a92336a11 xen/pciback: Drop... |
365 |
op->err = xen_pcibk_disable_msix(pdev, dev, op); |
30edc14bf xen/pciback: xen ... |
366 367 368 369 370 371 372 |
break; #endif default: op->err = XEN_PCI_ERR_not_implemented; break; } } |
0513fe9e5 xen/pciback: Allo... |
373 374 375 |
if (!op->err && dev && dev_data) { /* Transition detected */ if ((dev_data->enable_intx != test_intx)) |
a92336a11 xen/pciback: Drop... |
376 |
xen_pcibk_control_isr(dev, 0 /* no reset */); |
0513fe9e5 xen/pciback: Allo... |
377 |
} |
8135cf8b0 xen/pciback: Save... |
378 379 380 381 382 383 384 385 386 387 388 |
pdev->sh_info->op.err = op->err; pdev->sh_info->op.value = op->value; #ifdef CONFIG_PCI_MSI if (op->cmd == XEN_PCI_OP_enable_msix && op->err == 0) { unsigned int i; for (i = 0; i < op->value; i++) pdev->sh_info->op.msix_entries[i].vector = op->msix_entries[i].vector; } #endif |
30edc14bf xen/pciback: xen ... |
389 390 391 392 393 394 |
/* Tell the driver domain that we're done. */ wmb(); clear_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags); notify_remote_via_irq(pdev->evtchn_irq); /* Mark that we're done. */ |
4e857c58e arch: Mass conver... |
395 |
smp_mb__before_atomic(); /* /after/ clearing PCIF_active */ |
30edc14bf xen/pciback: xen ... |
396 |
clear_bit(_PDEVF_op_active, &pdev->flags); |
4e857c58e arch: Mass conver... |
397 |
smp_mb__after_atomic(); /* /before/ final check for work */ |
30edc14bf xen/pciback: xen ... |
398 399 400 401 |
/* Check to see if the driver domain tried to start another request in * between clearing _XEN_PCIF_active and clearing _PDEVF_op_active. */ |
a92336a11 xen/pciback: Drop... |
402 |
xen_pcibk_test_and_schedule_op(pdev); |
30edc14bf xen/pciback: xen ... |
403 |
} |
a92336a11 xen/pciback: Drop... |
404 |
irqreturn_t xen_pcibk_handle_event(int irq, void *dev_id) |
30edc14bf xen/pciback: xen ... |
405 |
{ |
a92336a11 xen/pciback: Drop... |
406 |
struct xen_pcibk_device *pdev = dev_id; |
30edc14bf xen/pciback: xen ... |
407 |
|
a92336a11 xen/pciback: Drop... |
408 |
xen_pcibk_test_and_schedule_op(pdev); |
30edc14bf xen/pciback: xen ... |
409 410 411 |
return IRQ_HANDLED; } |
a92336a11 xen/pciback: Drop... |
412 |
static irqreturn_t xen_pcibk_guest_interrupt(int irq, void *dev_id) |
0513fe9e5 xen/pciback: Allo... |
413 414 |
{ struct pci_dev *dev = (struct pci_dev *)dev_id; |
a92336a11 xen/pciback: Drop... |
415 |
struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev); |
0513fe9e5 xen/pciback: Allo... |
416 417 418 419 420 |
if (dev_data->isr_on && dev_data->ack_intr) { dev_data->handled++; if ((dev_data->handled % 1000) == 0) { if (xen_test_irq_shared(irq)) { |
283c0972d xen: Convert prin... |
421 |
pr_info("%s IRQ line is not shared " |
0513fe9e5 xen/pciback: Allo... |
422 423 424 425 426 427 428 429 430 431 |
"with other domains. Turning ISR off ", dev_data->irq_name); dev_data->ack_intr = 0; } } return IRQ_HANDLED; } return IRQ_NONE; } |