diff options
author | Michael S. Tsirkin <mst@redhat.com> | 2009-07-26 08:48:08 -0400 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2009-07-30 02:33:45 -0400 |
commit | e969fed542cae08cb11d666efac4f7c5d624d09f (patch) | |
tree | 7de7cb45ba6b3a28ef0054df71c7dc7676d4af65 /drivers/virtio/virtio_pci.c | |
parent | f6c82507030d61e15928d5cad946d3eac1c4a384 (diff) |
virtio: refactor find_vqs
This refactors find_vqs, making it more readable and robust, and fixing
two regressions from 2.6.30:
- double free_irq causing BUG_ON on device removal
- probe failure when vq can't be assigned to msi-x vector
(reported on old host kernels)
Tested-by: Amit Shah <amit.shah@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'drivers/virtio/virtio_pci.c')
-rw-r--r-- | drivers/virtio/virtio_pci.c | 212 |
1 files changed, 119 insertions, 93 deletions
diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index a1cb1a1c6522..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 */ |
@@ -278,27 +280,24 @@ static void vp_free_vectors(struct virtio_device *vdev) | |||
278 | vp_dev->msix_entries = NULL; | 280 | vp_dev->msix_entries = NULL; |
279 | } | 281 | } |
280 | 282 | ||
281 | static int vp_enable_msix(struct pci_dev *dev, struct msix_entry *entries, | 283 | static int vp_request_vectors(struct virtio_device *vdev, int nvectors, |
282 | int *options, int noptions) | 284 | bool per_vq_vectors) |
283 | { | ||
284 | int i; | ||
285 | for (i = 0; i < noptions; ++i) | ||
286 | if (!pci_enable_msix(dev, entries, options[i])) | ||
287 | return options[i]; | ||
288 | return -EBUSY; | ||
289 | } | ||
290 | |||
291 | static int vp_request_vectors(struct virtio_device *vdev, unsigned max_vqs) | ||
292 | { | 285 | { |
293 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); | 286 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
294 | const char *name = dev_name(&vp_dev->vdev.dev); | 287 | const char *name = dev_name(&vp_dev->vdev.dev); |
295 | unsigned i, v; | 288 | unsigned i, v; |
296 | int err = -ENOMEM; | 289 | int err = -ENOMEM; |
297 | /* We want at most one vector per queue and one for config changes. | 290 | |
298 | * Fallback to separate vectors for config and a shared for queues. | 291 | if (!nvectors) { |
299 | * Finally fall back to regular interrupts. */ | 292 | /* Can't allocate MSI-X vectors, use regular interrupt */ |
300 | int options[] = { max_vqs + 1, 2 }; | 293 | vp_dev->msix_vectors = 0; |
301 | 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 | } | ||
302 | 301 | ||
303 | vp_dev->msix_entries = kmalloc(nvectors * sizeof *vp_dev->msix_entries, | 302 | vp_dev->msix_entries = kmalloc(nvectors * sizeof *vp_dev->msix_entries, |
304 | GFP_KERNEL); | 303 | GFP_KERNEL); |
@@ -312,41 +311,34 @@ static int vp_request_vectors(struct virtio_device *vdev, unsigned max_vqs) | |||
312 | for (i = 0; i < nvectors; ++i) | 311 | for (i = 0; i < nvectors; ++i) |
313 | vp_dev->msix_entries[i].entry = i; | 312 | vp_dev->msix_entries[i].entry = i; |
314 | 313 | ||
315 | 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); |
316 | options, ARRAY_SIZE(options)); | 315 | if (err > 0) |
317 | if (err < 0) { | 316 | err = -ENOSPC; |
318 | /* Can't allocate enough MSI-X vectors, use regular interrupt */ | 317 | if (err) |
319 | vp_dev->msix_vectors = 0; | 318 | goto error; |
320 | err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, | 319 | vp_dev->msix_vectors = nvectors; |
321 | IRQF_SHARED, name, vp_dev); | 320 | vp_dev->msix_enabled = 1; |
322 | if (err) | 321 | |
323 | goto error; | 322 | /* Set the vector used for configuration */ |
324 | vp_dev->intx_enabled = 1; | 323 | v = vp_dev->msix_used_vectors; |
325 | } else { | 324 | snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, |
326 | vp_dev->msix_vectors = err; | 325 | "%s-config", name); |
327 | vp_dev->msix_enabled = 1; | 326 | err = request_irq(vp_dev->msix_entries[v].vector, |
328 | 327 | vp_config_changed, 0, vp_dev->msix_names[v], | |
329 | /* Set the vector used for configuration */ | 328 | vp_dev); |
330 | v = vp_dev->msix_used_vectors; | 329 | if (err) |
331 | snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, | 330 | goto error; |
332 | "%s-config", name); | 331 | ++vp_dev->msix_used_vectors; |
333 | err = request_irq(vp_dev->msix_entries[v].vector, | ||
334 | vp_config_changed, 0, vp_dev->msix_names[v], | ||
335 | vp_dev); | ||
336 | if (err) | ||
337 | goto error; | ||
338 | ++vp_dev->msix_used_vectors; | ||
339 | 332 | ||
340 | iowrite16(v, vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); | 333 | iowrite16(v, vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); |
341 | /* Verify we had enough resources to assign the vector */ | 334 | /* Verify we had enough resources to assign the vector */ |
342 | v = ioread16(vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); | 335 | v = ioread16(vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); |
343 | if (v == VIRTIO_MSI_NO_VECTOR) { | 336 | if (v == VIRTIO_MSI_NO_VECTOR) { |
344 | err = -EBUSY; | 337 | err = -EBUSY; |
345 | goto error; | 338 | goto error; |
346 | } | ||
347 | } | 339 | } |
348 | 340 | ||
349 | if (vp_dev->msix_vectors && vp_dev->msix_vectors != max_vqs + 1) { | 341 | if (!per_vq_vectors) { |
350 | /* Shared vector for all VQs */ | 342 | /* Shared vector for all VQs */ |
351 | v = vp_dev->msix_used_vectors; | 343 | v = vp_dev->msix_used_vectors; |
352 | snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, | 344 | snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, |
@@ -366,13 +358,14 @@ error: | |||
366 | 358 | ||
367 | 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, |
368 | void (*callback)(struct virtqueue *vq), | 360 | void (*callback)(struct virtqueue *vq), |
369 | const char *name) | 361 | const char *name, |
362 | u16 vector) | ||
370 | { | 363 | { |
371 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); | 364 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
372 | struct virtio_pci_vq_info *info; | 365 | struct virtio_pci_vq_info *info; |
373 | struct virtqueue *vq; | 366 | struct virtqueue *vq; |
374 | unsigned long flags, size; | 367 | unsigned long flags, size; |
375 | u16 num, vector; | 368 | u16 num; |
376 | int err; | 369 | int err; |
377 | 370 | ||
378 | /* Select the queue we're interested in */ | 371 | /* Select the queue we're interested in */ |
@@ -391,7 +384,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, | |||
391 | 384 | ||
392 | info->queue_index = index; | 385 | info->queue_index = index; |
393 | info->num = num; | 386 | info->num = num; |
394 | info->vector = VIRTIO_MSI_NO_VECTOR; | 387 | info->vector = vector; |
395 | 388 | ||
396 | size = PAGE_ALIGN(vring_size(num, VIRTIO_PCI_VRING_ALIGN)); | 389 | size = PAGE_ALIGN(vring_size(num, VIRTIO_PCI_VRING_ALIGN)); |
397 | info->queue = alloc_pages_exact(size, GFP_KERNEL|__GFP_ZERO); | 390 | info->queue = alloc_pages_exact(size, GFP_KERNEL|__GFP_ZERO); |
@@ -415,22 +408,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, | |||
415 | vq->priv = info; | 408 | vq->priv = info; |
416 | info->vq = vq; | 409 | info->vq = vq; |
417 | 410 | ||
418 | /* allocate per-vq vector if available and necessary */ | 411 | if (vector != VIRTIO_MSI_NO_VECTOR) { |
419 | if (callback && vp_dev->msix_used_vectors < vp_dev->msix_vectors) { | ||
420 | vector = vp_dev->msix_used_vectors; | ||
421 | snprintf(vp_dev->msix_names[vector], sizeof *vp_dev->msix_names, | ||
422 | "%s-%s", dev_name(&vp_dev->vdev.dev), name); | ||
423 | err = request_irq(vp_dev->msix_entries[vector].vector, | ||
424 | vring_interrupt, 0, | ||
425 | vp_dev->msix_names[vector], vq); | ||
426 | if (err) | ||
427 | goto out_request_irq; | ||
428 | info->vector = vector; | ||
429 | ++vp_dev->msix_used_vectors; | ||
430 | } else | ||
431 | vector = VP_MSIX_VQ_VECTOR; | ||
432 | |||
433 | if (callback && vp_dev->msix_enabled) { | ||
434 | iowrite16(vector, vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); | 412 | iowrite16(vector, vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); |
435 | vector = ioread16(vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); | 413 | vector = ioread16(vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); |
436 | if (vector == VIRTIO_MSI_NO_VECTOR) { | 414 | if (vector == VIRTIO_MSI_NO_VECTOR) { |
@@ -446,11 +424,6 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, | |||
446 | return vq; | 424 | return vq; |
447 | 425 | ||
448 | out_assign: | 426 | out_assign: |
449 | if (info->vector != VIRTIO_MSI_NO_VECTOR) { | ||
450 | free_irq(vp_dev->msix_entries[info->vector].vector, vq); | ||
451 | --vp_dev->msix_used_vectors; | ||
452 | } | ||
453 | out_request_irq: | ||
454 | vring_del_virtqueue(vq); | 427 | vring_del_virtqueue(vq); |
455 | out_activate_queue: | 428 | out_activate_queue: |
456 | iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN); | 429 | iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN); |
@@ -472,9 +445,6 @@ static void vp_del_vq(struct virtqueue *vq) | |||
472 | 445 | ||
473 | iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL); | 446 | iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL); |
474 | 447 | ||
475 | if (info->vector != VIRTIO_MSI_NO_VECTOR) | ||
476 | free_irq(vp_dev->msix_entries[info->vector].vector, vq); | ||
477 | |||
478 | if (vp_dev->msix_enabled) { | 448 | if (vp_dev->msix_enabled) { |
479 | iowrite16(VIRTIO_MSI_NO_VECTOR, | 449 | iowrite16(VIRTIO_MSI_NO_VECTOR, |
480 | vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); | 450 | vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); |
@@ -495,36 +465,62 @@ static void vp_del_vq(struct virtqueue *vq) | |||
495 | /* the config->del_vqs() implementation */ | 465 | /* the config->del_vqs() implementation */ |
496 | static void vp_del_vqs(struct virtio_device *vdev) | 466 | static void vp_del_vqs(struct virtio_device *vdev) |
497 | { | 467 | { |
468 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); | ||
498 | struct virtqueue *vq, *n; | 469 | struct virtqueue *vq, *n; |
470 | struct virtio_pci_vq_info *info; | ||
499 | 471 | ||
500 | 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); | ||
501 | vp_del_vq(vq); | 476 | vp_del_vq(vq); |
477 | } | ||
478 | vp_dev->per_vq_vectors = false; | ||
502 | 479 | ||
503 | vp_free_vectors(vdev); | 480 | vp_free_vectors(vdev); |
504 | } | 481 | } |
505 | 482 | ||
506 | /* the config->find_vqs() implementation */ | 483 | static int vp_try_to_find_vqs(struct virtio_device *vdev, unsigned nvqs, |
507 | static int vp_find_vqs(struct virtio_device *vdev, unsigned nvqs, | 484 | struct virtqueue *vqs[], |
508 | struct virtqueue *vqs[], | 485 | vq_callback_t *callbacks[], |
509 | vq_callback_t *callbacks[], | 486 | const char *names[], |
510 | const char *names[]) | 487 | int nvectors, |
488 | bool per_vq_vectors) | ||
511 | { | 489 | { |
512 | int vectors = 0; | 490 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
513 | int i, err; | 491 | u16 vector; |
514 | 492 | int i, err, allocated_vectors; | |
515 | /* How many vectors would we like? */ | ||
516 | for (i = 0; i < nvqs; ++i) | ||
517 | if (callbacks[i]) | ||
518 | ++vectors; | ||
519 | 493 | ||
520 | err = vp_request_vectors(vdev, vectors); | 494 | err = vp_request_vectors(vdev, nvectors, per_vq_vectors); |
521 | if (err) | 495 | if (err) |
522 | goto error_request; | 496 | goto error_request; |
523 | 497 | ||
498 | vp_dev->per_vq_vectors = per_vq_vectors; | ||
499 | allocated_vectors = vp_dev->msix_used_vectors; | ||
524 | for (i = 0; i < nvqs; ++i) { | 500 | for (i = 0; i < nvqs; ++i) { |
525 | vqs[i] = vp_find_vq(vdev, i, callbacks[i], names[i]); | 501 | if (!callbacks[i] || !vp_dev->msix_enabled) |
526 | 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]); | ||
527 | 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 | } | ||
528 | } | 524 | } |
529 | return 0; | 525 | return 0; |
530 | 526 | ||
@@ -532,7 +528,37 @@ error_find: | |||
532 | vp_del_vqs(vdev); | 528 | vp_del_vqs(vdev); |
533 | 529 | ||
534 | error_request: | 530 | error_request: |
535 | 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; | ||
536 | } | 562 | } |
537 | 563 | ||
538 | static struct virtio_config_ops virtio_pci_config_ops = { | 564 | static struct virtio_config_ops virtio_pci_config_ops = { |