diff options
Diffstat (limited to 'drivers/virtio/virtio_pci.c')
-rw-r--r-- | drivers/virtio/virtio_pci.c | 242 |
1 files changed, 137 insertions, 105 deletions
diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index 193c8f0e5cc5..248e00ec4dc1 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c | |||
@@ -52,8 +52,10 @@ struct virtio_pci_device | |||
52 | char (*msix_names)[256]; | 52 | char (*msix_names)[256]; |
53 | /* Number of available vectors */ | 53 | /* Number of available vectors */ |
54 | unsigned msix_vectors; | 54 | unsigned msix_vectors; |
55 | /* Vectors allocated */ | 55 | /* Vectors allocated, excluding per-vq vectors if any */ |
56 | unsigned msix_used_vectors; | 56 | unsigned msix_used_vectors; |
57 | /* Whether we have vector per vq */ | ||
58 | bool per_vq_vectors; | ||
57 | }; | 59 | }; |
58 | 60 | ||
59 | /* Constants for MSI-X */ | 61 | /* Constants for MSI-X */ |
@@ -258,7 +260,6 @@ static void vp_free_vectors(struct virtio_device *vdev) | |||
258 | 260 | ||
259 | for (i = 0; i < vp_dev->msix_used_vectors; ++i) | 261 | for (i = 0; i < vp_dev->msix_used_vectors; ++i) |
260 | free_irq(vp_dev->msix_entries[i].vector, vp_dev); | 262 | free_irq(vp_dev->msix_entries[i].vector, vp_dev); |
261 | vp_dev->msix_used_vectors = 0; | ||
262 | 263 | ||
263 | if (vp_dev->msix_enabled) { | 264 | if (vp_dev->msix_enabled) { |
264 | /* Disable the vector used for configuration */ | 265 | /* Disable the vector used for configuration */ |
@@ -267,80 +268,77 @@ static void vp_free_vectors(struct virtio_device *vdev) | |||
267 | /* Flush the write out to device */ | 268 | /* Flush the write out to device */ |
268 | ioread16(vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); | 269 | ioread16(vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); |
269 | 270 | ||
270 | vp_dev->msix_enabled = 0; | ||
271 | pci_disable_msix(vp_dev->pci_dev); | 271 | pci_disable_msix(vp_dev->pci_dev); |
272 | vp_dev->msix_enabled = 0; | ||
273 | vp_dev->msix_vectors = 0; | ||
272 | } | 274 | } |
273 | } | ||
274 | 275 | ||
275 | static int vp_enable_msix(struct pci_dev *dev, struct msix_entry *entries, | 276 | vp_dev->msix_used_vectors = 0; |
276 | int *options, int noptions) | 277 | kfree(vp_dev->msix_names); |
277 | { | 278 | vp_dev->msix_names = NULL; |
278 | int i; | 279 | kfree(vp_dev->msix_entries); |
279 | for (i = 0; i < noptions; ++i) | 280 | vp_dev->msix_entries = NULL; |
280 | if (!pci_enable_msix(dev, entries, options[i])) | ||
281 | return options[i]; | ||
282 | return -EBUSY; | ||
283 | } | 281 | } |
284 | 282 | ||
285 | static int vp_request_vectors(struct virtio_device *vdev, unsigned max_vqs) | 283 | static int vp_request_vectors(struct virtio_device *vdev, int nvectors, |
284 | bool per_vq_vectors) | ||
286 | { | 285 | { |
287 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); | 286 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
288 | const char *name = dev_name(&vp_dev->vdev.dev); | 287 | const char *name = dev_name(&vp_dev->vdev.dev); |
289 | unsigned i, v; | 288 | unsigned i, v; |
290 | int err = -ENOMEM; | 289 | int err = -ENOMEM; |
291 | /* We want at most one vector per queue and one for config changes. | 290 | |
292 | * Fallback to separate vectors for config and a shared for queues. | 291 | if (!nvectors) { |
293 | * Finally fall back to regular interrupts. */ | 292 | /* Can't allocate MSI-X vectors, use regular interrupt */ |
294 | int options[] = { max_vqs + 1, 2 }; | 293 | vp_dev->msix_vectors = 0; |
295 | int nvectors = max(options[0], options[1]); | 294 | err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, |
295 | IRQF_SHARED, name, vp_dev); | ||
296 | if (err) | ||
297 | return err; | ||
298 | vp_dev->intx_enabled = 1; | ||
299 | return 0; | ||
300 | } | ||
296 | 301 | ||
297 | vp_dev->msix_entries = kmalloc(nvectors * sizeof *vp_dev->msix_entries, | 302 | vp_dev->msix_entries = kmalloc(nvectors * sizeof *vp_dev->msix_entries, |
298 | GFP_KERNEL); | 303 | GFP_KERNEL); |
299 | if (!vp_dev->msix_entries) | 304 | if (!vp_dev->msix_entries) |
300 | goto error_entries; | 305 | goto error; |
301 | vp_dev->msix_names = kmalloc(nvectors * sizeof *vp_dev->msix_names, | 306 | vp_dev->msix_names = kmalloc(nvectors * sizeof *vp_dev->msix_names, |
302 | GFP_KERNEL); | 307 | GFP_KERNEL); |
303 | if (!vp_dev->msix_names) | 308 | if (!vp_dev->msix_names) |
304 | goto error_names; | 309 | goto error; |
305 | 310 | ||
306 | for (i = 0; i < nvectors; ++i) | 311 | for (i = 0; i < nvectors; ++i) |
307 | vp_dev->msix_entries[i].entry = i; | 312 | vp_dev->msix_entries[i].entry = i; |
308 | 313 | ||
309 | err = vp_enable_msix(vp_dev->pci_dev, vp_dev->msix_entries, | 314 | err = pci_enable_msix(vp_dev->pci_dev, vp_dev->msix_entries, nvectors); |
310 | options, ARRAY_SIZE(options)); | 315 | if (err > 0) |
311 | if (err < 0) { | 316 | err = -ENOSPC; |
312 | /* Can't allocate enough MSI-X vectors, use regular interrupt */ | 317 | if (err) |
313 | vp_dev->msix_vectors = 0; | 318 | goto error; |
314 | err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, | 319 | vp_dev->msix_vectors = nvectors; |
315 | IRQF_SHARED, name, vp_dev); | 320 | vp_dev->msix_enabled = 1; |
316 | if (err) | 321 | |
317 | goto error_irq; | 322 | /* Set the vector used for configuration */ |
318 | vp_dev->intx_enabled = 1; | 323 | v = vp_dev->msix_used_vectors; |
319 | } else { | 324 | snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, |
320 | vp_dev->msix_vectors = err; | 325 | "%s-config", name); |
321 | vp_dev->msix_enabled = 1; | 326 | err = request_irq(vp_dev->msix_entries[v].vector, |
322 | 327 | vp_config_changed, 0, vp_dev->msix_names[v], | |
323 | /* Set the vector used for configuration */ | 328 | vp_dev); |
324 | v = vp_dev->msix_used_vectors; | 329 | if (err) |
325 | snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, | 330 | goto error; |
326 | "%s-config", name); | 331 | ++vp_dev->msix_used_vectors; |
327 | err = request_irq(vp_dev->msix_entries[v].vector, | 332 | |
328 | vp_config_changed, 0, vp_dev->msix_names[v], | 333 | iowrite16(v, vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); |
329 | vp_dev); | 334 | /* Verify we had enough resources to assign the vector */ |
330 | if (err) | 335 | v = ioread16(vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); |
331 | goto error_irq; | 336 | if (v == VIRTIO_MSI_NO_VECTOR) { |
332 | ++vp_dev->msix_used_vectors; | 337 | err = -EBUSY; |
333 | 338 | goto error; | |
334 | iowrite16(v, vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); | ||
335 | /* Verify we had enough resources to assign the vector */ | ||
336 | v = ioread16(vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); | ||
337 | if (v == VIRTIO_MSI_NO_VECTOR) { | ||
338 | err = -EBUSY; | ||
339 | goto error_irq; | ||
340 | } | ||
341 | } | 339 | } |
342 | 340 | ||
343 | if (vp_dev->msix_vectors && vp_dev->msix_vectors != max_vqs + 1) { | 341 | if (!per_vq_vectors) { |
344 | /* Shared vector for all VQs */ | 342 | /* Shared vector for all VQs */ |
345 | v = vp_dev->msix_used_vectors; | 343 | v = vp_dev->msix_used_vectors; |
346 | snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, | 344 | snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, |
@@ -349,28 +347,25 @@ static int vp_request_vectors(struct virtio_device *vdev, unsigned max_vqs) | |||
349 | vp_vring_interrupt, 0, vp_dev->msix_names[v], | 347 | vp_vring_interrupt, 0, vp_dev->msix_names[v], |
350 | vp_dev); | 348 | vp_dev); |
351 | if (err) | 349 | if (err) |
352 | goto error_irq; | 350 | goto error; |
353 | ++vp_dev->msix_used_vectors; | 351 | ++vp_dev->msix_used_vectors; |
354 | } | 352 | } |
355 | return 0; | 353 | return 0; |
356 | error_irq: | 354 | error: |
357 | vp_free_vectors(vdev); | 355 | vp_free_vectors(vdev); |
358 | kfree(vp_dev->msix_names); | ||
359 | error_names: | ||
360 | kfree(vp_dev->msix_entries); | ||
361 | error_entries: | ||
362 | return err; | 356 | return err; |
363 | } | 357 | } |
364 | 358 | ||
365 | static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, | 359 | static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, |
366 | void (*callback)(struct virtqueue *vq), | 360 | void (*callback)(struct virtqueue *vq), |
367 | const char *name) | 361 | const char *name, |
362 | u16 vector) | ||
368 | { | 363 | { |
369 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); | 364 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
370 | struct virtio_pci_vq_info *info; | 365 | struct virtio_pci_vq_info *info; |
371 | struct virtqueue *vq; | 366 | struct virtqueue *vq; |
372 | unsigned long flags, size; | 367 | unsigned long flags, size; |
373 | u16 num, vector; | 368 | u16 num; |
374 | int err; | 369 | int err; |
375 | 370 | ||
376 | /* Select the queue we're interested in */ | 371 | /* Select the queue we're interested in */ |
@@ -389,7 +384,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, | |||
389 | 384 | ||
390 | info->queue_index = index; | 385 | info->queue_index = index; |
391 | info->num = num; | 386 | info->num = num; |
392 | info->vector = VIRTIO_MSI_NO_VECTOR; | 387 | info->vector = vector; |
393 | 388 | ||
394 | size = PAGE_ALIGN(vring_size(num, VIRTIO_PCI_VRING_ALIGN)); | 389 | size = PAGE_ALIGN(vring_size(num, VIRTIO_PCI_VRING_ALIGN)); |
395 | info->queue = alloc_pages_exact(size, GFP_KERNEL|__GFP_ZERO); | 390 | info->queue = alloc_pages_exact(size, GFP_KERNEL|__GFP_ZERO); |
@@ -413,22 +408,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, | |||
413 | vq->priv = info; | 408 | vq->priv = info; |
414 | info->vq = vq; | 409 | info->vq = vq; |
415 | 410 | ||
416 | /* allocate per-vq vector if available and necessary */ | 411 | if (vector != VIRTIO_MSI_NO_VECTOR) { |
417 | if (callback && vp_dev->msix_used_vectors < vp_dev->msix_vectors) { | ||
418 | vector = vp_dev->msix_used_vectors; | ||
419 | snprintf(vp_dev->msix_names[vector], sizeof *vp_dev->msix_names, | ||
420 | "%s-%s", dev_name(&vp_dev->vdev.dev), name); | ||
421 | err = request_irq(vp_dev->msix_entries[vector].vector, | ||
422 | vring_interrupt, 0, | ||
423 | vp_dev->msix_names[vector], vq); | ||
424 | if (err) | ||
425 | goto out_request_irq; | ||
426 | info->vector = vector; | ||
427 | ++vp_dev->msix_used_vectors; | ||
428 | } else | ||
429 | vector = VP_MSIX_VQ_VECTOR; | ||
430 | |||
431 | if (callback && vp_dev->msix_enabled) { | ||
432 | iowrite16(vector, vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); | 412 | iowrite16(vector, vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); |
433 | vector = ioread16(vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); | 413 | vector = ioread16(vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); |
434 | if (vector == VIRTIO_MSI_NO_VECTOR) { | 414 | if (vector == VIRTIO_MSI_NO_VECTOR) { |
@@ -444,11 +424,6 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, | |||
444 | return vq; | 424 | return vq; |
445 | 425 | ||
446 | out_assign: | 426 | out_assign: |
447 | if (info->vector != VIRTIO_MSI_NO_VECTOR) { | ||
448 | free_irq(vp_dev->msix_entries[info->vector].vector, vq); | ||
449 | --vp_dev->msix_used_vectors; | ||
450 | } | ||
451 | out_request_irq: | ||
452 | vring_del_virtqueue(vq); | 427 | vring_del_virtqueue(vq); |
453 | out_activate_queue: | 428 | out_activate_queue: |
454 | iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN); | 429 | iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN); |
@@ -462,12 +437,13 @@ static void vp_del_vq(struct virtqueue *vq) | |||
462 | { | 437 | { |
463 | struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); | 438 | struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); |
464 | struct virtio_pci_vq_info *info = vq->priv; | 439 | struct virtio_pci_vq_info *info = vq->priv; |
465 | unsigned long size; | 440 | unsigned long flags, size; |
466 | 441 | ||
467 | iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL); | 442 | spin_lock_irqsave(&vp_dev->lock, flags); |
443 | list_del(&info->node); | ||
444 | spin_unlock_irqrestore(&vp_dev->lock, flags); | ||
468 | 445 | ||
469 | if (info->vector != VIRTIO_MSI_NO_VECTOR) | 446 | iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL); |
470 | free_irq(vp_dev->msix_entries[info->vector].vector, vq); | ||
471 | 447 | ||
472 | if (vp_dev->msix_enabled) { | 448 | if (vp_dev->msix_enabled) { |
473 | iowrite16(VIRTIO_MSI_NO_VECTOR, | 449 | iowrite16(VIRTIO_MSI_NO_VECTOR, |
@@ -489,36 +465,62 @@ static void vp_del_vq(struct virtqueue *vq) | |||
489 | /* the config->del_vqs() implementation */ | 465 | /* the config->del_vqs() implementation */ |
490 | static void vp_del_vqs(struct virtio_device *vdev) | 466 | static void vp_del_vqs(struct virtio_device *vdev) |
491 | { | 467 | { |
468 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); | ||
492 | struct virtqueue *vq, *n; | 469 | struct virtqueue *vq, *n; |
470 | struct virtio_pci_vq_info *info; | ||
493 | 471 | ||
494 | list_for_each_entry_safe(vq, n, &vdev->vqs, list) | 472 | list_for_each_entry_safe(vq, n, &vdev->vqs, list) { |
473 | info = vq->priv; | ||
474 | if (vp_dev->per_vq_vectors) | ||
475 | free_irq(vp_dev->msix_entries[info->vector].vector, vq); | ||
495 | vp_del_vq(vq); | 476 | vp_del_vq(vq); |
477 | } | ||
478 | vp_dev->per_vq_vectors = false; | ||
496 | 479 | ||
497 | vp_free_vectors(vdev); | 480 | vp_free_vectors(vdev); |
498 | } | 481 | } |
499 | 482 | ||
500 | /* the config->find_vqs() implementation */ | 483 | static int vp_try_to_find_vqs(struct virtio_device *vdev, unsigned nvqs, |
501 | static int vp_find_vqs(struct virtio_device *vdev, unsigned nvqs, | 484 | struct virtqueue *vqs[], |
502 | struct virtqueue *vqs[], | 485 | vq_callback_t *callbacks[], |
503 | vq_callback_t *callbacks[], | 486 | const char *names[], |
504 | const char *names[]) | 487 | int nvectors, |
488 | bool per_vq_vectors) | ||
505 | { | 489 | { |
506 | int vectors = 0; | 490 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
507 | int i, err; | 491 | u16 vector; |
508 | 492 | int i, err, allocated_vectors; | |
509 | /* How many vectors would we like? */ | ||
510 | for (i = 0; i < nvqs; ++i) | ||
511 | if (callbacks[i]) | ||
512 | ++vectors; | ||
513 | 493 | ||
514 | err = vp_request_vectors(vdev, vectors); | 494 | err = vp_request_vectors(vdev, nvectors, per_vq_vectors); |
515 | if (err) | 495 | if (err) |
516 | goto error_request; | 496 | goto error_request; |
517 | 497 | ||
498 | vp_dev->per_vq_vectors = per_vq_vectors; | ||
499 | allocated_vectors = vp_dev->msix_used_vectors; | ||
518 | for (i = 0; i < nvqs; ++i) { | 500 | for (i = 0; i < nvqs; ++i) { |
519 | vqs[i] = vp_find_vq(vdev, i, callbacks[i], names[i]); | 501 | if (!callbacks[i] || !vp_dev->msix_enabled) |
520 | if (IS_ERR(vqs[i])) | 502 | vector = VIRTIO_MSI_NO_VECTOR; |
503 | else if (vp_dev->per_vq_vectors) | ||
504 | vector = allocated_vectors++; | ||
505 | else | ||
506 | vector = VP_MSIX_VQ_VECTOR; | ||
507 | vqs[i] = vp_find_vq(vdev, i, callbacks[i], names[i], vector); | ||
508 | if (IS_ERR(vqs[i])) { | ||
509 | err = PTR_ERR(vqs[i]); | ||
521 | goto error_find; | 510 | goto error_find; |
511 | } | ||
512 | /* allocate per-vq irq if available and necessary */ | ||
513 | if (vp_dev->per_vq_vectors && vector != VIRTIO_MSI_NO_VECTOR) { | ||
514 | snprintf(vp_dev->msix_names[vector], sizeof *vp_dev->msix_names, | ||
515 | "%s-%s", dev_name(&vp_dev->vdev.dev), names[i]); | ||
516 | err = request_irq(vp_dev->msix_entries[vector].vector, | ||
517 | vring_interrupt, 0, | ||
518 | vp_dev->msix_names[vector], vqs[i]); | ||
519 | if (err) { | ||
520 | vp_del_vq(vqs[i]); | ||
521 | goto error_find; | ||
522 | } | ||
523 | } | ||
522 | } | 524 | } |
523 | return 0; | 525 | return 0; |
524 | 526 | ||
@@ -526,7 +528,37 @@ error_find: | |||
526 | vp_del_vqs(vdev); | 528 | vp_del_vqs(vdev); |
527 | 529 | ||
528 | error_request: | 530 | error_request: |
529 | return PTR_ERR(vqs[i]); | 531 | return err; |
532 | } | ||
533 | |||
534 | /* the config->find_vqs() implementation */ | ||
535 | static int vp_find_vqs(struct virtio_device *vdev, unsigned nvqs, | ||
536 | struct virtqueue *vqs[], | ||
537 | vq_callback_t *callbacks[], | ||
538 | const char *names[]) | ||
539 | { | ||
540 | int vectors = 0; | ||
541 | int i, uninitialized_var(err); | ||
542 | |||
543 | /* How many vectors would we like? */ | ||
544 | for (i = 0; i < nvqs; ++i) | ||
545 | if (callbacks[i]) | ||
546 | ++vectors; | ||
547 | |||
548 | /* We want at most one vector per queue and one for config changes. */ | ||
549 | err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, | ||
550 | vectors + 1, true); | ||
551 | if (!err) | ||
552 | return 0; | ||
553 | /* Fallback to separate vectors for config and a shared for queues. */ | ||
554 | err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, | ||
555 | 2, false); | ||
556 | if (!err) | ||
557 | return 0; | ||
558 | /* Finally fall back to regular interrupts. */ | ||
559 | err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, | ||
560 | 0, false); | ||
561 | return err; | ||
530 | } | 562 | } |
531 | 563 | ||
532 | static struct virtio_config_ops virtio_pci_config_ops = { | 564 | static struct virtio_config_ops virtio_pci_config_ops = { |
@@ -669,7 +701,7 @@ static int __init virtio_pci_init(void) | |||
669 | 701 | ||
670 | err = pci_register_driver(&virtio_pci_driver); | 702 | err = pci_register_driver(&virtio_pci_driver); |
671 | if (err) | 703 | if (err) |
672 | device_unregister(virtio_pci_root); | 704 | root_device_unregister(virtio_pci_root); |
673 | 705 | ||
674 | return err; | 706 | return err; |
675 | } | 707 | } |