Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Michael S. Tsirkin and committed by
Rusty Russell
e969fed5 f6c82507

+119 -93
+119 -93
drivers/virtio/virtio_pci.c
··· 52 52 char (*msix_names)[256]; 53 53 /* Number of available vectors */ 54 54 unsigned msix_vectors; 55 - /* Vectors allocated */ 55 + /* Vectors allocated, excluding per-vq vectors if any */ 56 56 unsigned msix_used_vectors; 57 + /* Whether we have vector per vq */ 58 + bool per_vq_vectors; 57 59 }; 58 60 59 61 /* Constants for MSI-X */ ··· 280 278 vp_dev->msix_entries = NULL; 281 279 } 282 280 283 - static int vp_enable_msix(struct pci_dev *dev, struct msix_entry *entries, 284 - int *options, int noptions) 285 - { 286 - int i; 287 - for (i = 0; i < noptions; ++i) 288 - if (!pci_enable_msix(dev, entries, options[i])) 289 - return options[i]; 290 - return -EBUSY; 291 - } 292 - 293 - static int vp_request_vectors(struct virtio_device *vdev, unsigned max_vqs) 281 + static int vp_request_vectors(struct virtio_device *vdev, int nvectors, 282 + bool per_vq_vectors) 294 283 { 295 284 struct virtio_pci_device *vp_dev = to_vp_device(vdev); 296 285 const char *name = dev_name(&vp_dev->vdev.dev); 297 286 unsigned i, v; 298 287 int err = -ENOMEM; 299 - /* We want at most one vector per queue and one for config changes. 300 - * Fallback to separate vectors for config and a shared for queues. 301 - * Finally fall back to regular interrupts. */ 302 - int options[] = { max_vqs + 1, 2 }; 303 - int nvectors = max(options[0], options[1]); 288 + 289 + if (!nvectors) { 290 + /* Can't allocate MSI-X vectors, use regular interrupt */ 291 + vp_dev->msix_vectors = 0; 292 + err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, 293 + IRQF_SHARED, name, vp_dev); 294 + if (err) 295 + return err; 296 + vp_dev->intx_enabled = 1; 297 + return 0; 298 + } 304 299 305 300 vp_dev->msix_entries = kmalloc(nvectors * sizeof *vp_dev->msix_entries, 306 301 GFP_KERNEL); ··· 311 312 for (i = 0; i < nvectors; ++i) 312 313 vp_dev->msix_entries[i].entry = i; 313 314 314 - err = vp_enable_msix(vp_dev->pci_dev, vp_dev->msix_entries, 315 - options, ARRAY_SIZE(options)); 316 - if (err < 0) { 317 - /* Can't allocate enough MSI-X vectors, use regular interrupt */ 318 - vp_dev->msix_vectors = 0; 319 - err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, 320 - IRQF_SHARED, name, vp_dev); 321 - if (err) 322 - goto error; 323 - vp_dev->intx_enabled = 1; 324 - } else { 325 - vp_dev->msix_vectors = err; 326 - vp_dev->msix_enabled = 1; 315 + err = pci_enable_msix(vp_dev->pci_dev, vp_dev->msix_entries, nvectors); 316 + if (err > 0) 317 + err = -ENOSPC; 318 + if (err) 319 + goto error; 320 + vp_dev->msix_vectors = nvectors; 321 + vp_dev->msix_enabled = 1; 327 322 328 - /* Set the vector used for configuration */ 329 - v = vp_dev->msix_used_vectors; 330 - snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, 331 - "%s-config", name); 332 - err = request_irq(vp_dev->msix_entries[v].vector, 333 - vp_config_changed, 0, vp_dev->msix_names[v], 334 - vp_dev); 335 - if (err) 336 - goto error; 337 - ++vp_dev->msix_used_vectors; 323 + /* Set the vector used for configuration */ 324 + v = vp_dev->msix_used_vectors; 325 + snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, 326 + "%s-config", name); 327 + err = request_irq(vp_dev->msix_entries[v].vector, 328 + vp_config_changed, 0, vp_dev->msix_names[v], 329 + vp_dev); 330 + if (err) 331 + goto error; 332 + ++vp_dev->msix_used_vectors; 338 333 339 - iowrite16(v, vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); 340 - /* Verify we had enough resources to assign the vector */ 341 - v = ioread16(vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); 342 - if (v == VIRTIO_MSI_NO_VECTOR) { 343 - err = -EBUSY; 344 - goto error; 345 - } 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; 346 340 } 347 341 348 - if (vp_dev->msix_vectors && vp_dev->msix_vectors != max_vqs + 1) { 342 + if (!per_vq_vectors) { 349 343 /* Shared vector for all VQs */ 350 344 v = vp_dev->msix_used_vectors; 351 345 snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, ··· 358 366 359 367 static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, 360 368 void (*callback)(struct virtqueue *vq), 361 - const char *name) 369 + const char *name, 370 + u16 vector) 362 371 { 363 372 struct virtio_pci_device *vp_dev = to_vp_device(vdev); 364 373 struct virtio_pci_vq_info *info; 365 374 struct virtqueue *vq; 366 375 unsigned long flags, size; 367 - u16 num, vector; 376 + u16 num; 368 377 int err; 369 378 370 379 /* Select the queue we're interested in */ ··· 384 391 385 392 info->queue_index = index; 386 393 info->num = num; 387 - info->vector = VIRTIO_MSI_NO_VECTOR; 394 + info->vector = vector; 388 395 389 396 size = PAGE_ALIGN(vring_size(num, VIRTIO_PCI_VRING_ALIGN)); 390 397 info->queue = alloc_pages_exact(size, GFP_KERNEL|__GFP_ZERO); ··· 408 415 vq->priv = info; 409 416 info->vq = vq; 410 417 411 - /* allocate per-vq vector if available and necessary */ 412 - if (callback && vp_dev->msix_used_vectors < vp_dev->msix_vectors) { 413 - vector = vp_dev->msix_used_vectors; 414 - snprintf(vp_dev->msix_names[vector], sizeof *vp_dev->msix_names, 415 - "%s-%s", dev_name(&vp_dev->vdev.dev), name); 416 - err = request_irq(vp_dev->msix_entries[vector].vector, 417 - vring_interrupt, 0, 418 - vp_dev->msix_names[vector], vq); 419 - if (err) 420 - goto out_request_irq; 421 - info->vector = vector; 422 - ++vp_dev->msix_used_vectors; 423 - } else 424 - vector = VP_MSIX_VQ_VECTOR; 425 - 426 - if (callback && vp_dev->msix_enabled) { 418 + if (vector != VIRTIO_MSI_NO_VECTOR) { 427 419 iowrite16(vector, vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); 428 420 vector = ioread16(vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); 429 421 if (vector == VIRTIO_MSI_NO_VECTOR) { ··· 424 446 return vq; 425 447 426 448 out_assign: 427 - if (info->vector != VIRTIO_MSI_NO_VECTOR) { 428 - free_irq(vp_dev->msix_entries[info->vector].vector, vq); 429 - --vp_dev->msix_used_vectors; 430 - } 431 - out_request_irq: 432 449 vring_del_virtqueue(vq); 433 450 out_activate_queue: 434 451 iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN); ··· 445 472 446 473 iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL); 447 474 448 - if (info->vector != VIRTIO_MSI_NO_VECTOR) 449 - free_irq(vp_dev->msix_entries[info->vector].vector, vq); 450 - 451 475 if (vp_dev->msix_enabled) { 452 476 iowrite16(VIRTIO_MSI_NO_VECTOR, 453 477 vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); ··· 465 495 /* the config->del_vqs() implementation */ 466 496 static void vp_del_vqs(struct virtio_device *vdev) 467 497 { 498 + struct virtio_pci_device *vp_dev = to_vp_device(vdev); 468 499 struct virtqueue *vq, *n; 500 + struct virtio_pci_vq_info *info; 469 501 470 - list_for_each_entry_safe(vq, n, &vdev->vqs, list) 502 + list_for_each_entry_safe(vq, n, &vdev->vqs, list) { 503 + info = vq->priv; 504 + if (vp_dev->per_vq_vectors) 505 + free_irq(vp_dev->msix_entries[info->vector].vector, vq); 471 506 vp_del_vq(vq); 507 + } 508 + vp_dev->per_vq_vectors = false; 472 509 473 510 vp_free_vectors(vdev); 511 + } 512 + 513 + static int vp_try_to_find_vqs(struct virtio_device *vdev, unsigned nvqs, 514 + struct virtqueue *vqs[], 515 + vq_callback_t *callbacks[], 516 + const char *names[], 517 + int nvectors, 518 + bool per_vq_vectors) 519 + { 520 + struct virtio_pci_device *vp_dev = to_vp_device(vdev); 521 + u16 vector; 522 + int i, err, allocated_vectors; 523 + 524 + err = vp_request_vectors(vdev, nvectors, per_vq_vectors); 525 + if (err) 526 + goto error_request; 527 + 528 + vp_dev->per_vq_vectors = per_vq_vectors; 529 + allocated_vectors = vp_dev->msix_used_vectors; 530 + for (i = 0; i < nvqs; ++i) { 531 + if (!callbacks[i] || !vp_dev->msix_enabled) 532 + vector = VIRTIO_MSI_NO_VECTOR; 533 + else if (vp_dev->per_vq_vectors) 534 + vector = allocated_vectors++; 535 + else 536 + vector = VP_MSIX_VQ_VECTOR; 537 + vqs[i] = vp_find_vq(vdev, i, callbacks[i], names[i], vector); 538 + if (IS_ERR(vqs[i])) { 539 + err = PTR_ERR(vqs[i]); 540 + goto error_find; 541 + } 542 + /* allocate per-vq irq if available and necessary */ 543 + if (vp_dev->per_vq_vectors && vector != VIRTIO_MSI_NO_VECTOR) { 544 + snprintf(vp_dev->msix_names[vector], sizeof *vp_dev->msix_names, 545 + "%s-%s", dev_name(&vp_dev->vdev.dev), names[i]); 546 + err = request_irq(vp_dev->msix_entries[vector].vector, 547 + vring_interrupt, 0, 548 + vp_dev->msix_names[vector], vqs[i]); 549 + if (err) { 550 + vp_del_vq(vqs[i]); 551 + goto error_find; 552 + } 553 + } 554 + } 555 + return 0; 556 + 557 + error_find: 558 + vp_del_vqs(vdev); 559 + 560 + error_request: 561 + return err; 474 562 } 475 563 476 564 /* the config->find_vqs() implementation */ ··· 538 510 const char *names[]) 539 511 { 540 512 int vectors = 0; 541 - int i, err; 513 + int i, uninitialized_var(err); 542 514 543 515 /* How many vectors would we like? */ 544 516 for (i = 0; i < nvqs; ++i) 545 517 if (callbacks[i]) 546 518 ++vectors; 547 519 548 - err = vp_request_vectors(vdev, vectors); 549 - if (err) 550 - goto error_request; 551 - 552 - for (i = 0; i < nvqs; ++i) { 553 - vqs[i] = vp_find_vq(vdev, i, callbacks[i], names[i]); 554 - if (IS_ERR(vqs[i])) 555 - goto error_find; 556 - } 557 - return 0; 558 - 559 - error_find: 560 - vp_del_vqs(vdev); 561 - 562 - error_request: 563 - return PTR_ERR(vqs[i]); 520 + /* We want at most one vector per queue and one for config changes. */ 521 + err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, 522 + vectors + 1, true); 523 + if (!err) 524 + return 0; 525 + /* Fallback to separate vectors for config and a shared for queues. */ 526 + err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, 527 + 2, false); 528 + if (!err) 529 + return 0; 530 + /* Finally fall back to regular interrupts. */ 531 + err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, 532 + 0, false); 533 + return err; 564 534 } 565 535 566 536 static struct virtio_config_ops virtio_pci_config_ops = {