aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael S. Tsirkin <mst@redhat.com>2009-07-26 08:48:08 -0400
committerRusty Russell <rusty@rustcorp.com.au>2009-07-30 02:33:45 -0400
commite969fed542cae08cb11d666efac4f7c5d624d09f (patch)
tree7de7cb45ba6b3a28ef0054df71c7dc7676d4af65
parentf6c82507030d61e15928d5cad946d3eac1c4a384 (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>
-rw-r--r--drivers/virtio/virtio_pci.c212
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
281static int vp_enable_msix(struct pci_dev *dev, struct msix_entry *entries, 283static 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
291static 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
367static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, 359static 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
448out_assign: 426out_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 }
453out_request_irq:
454 vring_del_virtqueue(vq); 427 vring_del_virtqueue(vq);
455out_activate_queue: 428out_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 */
496static void vp_del_vqs(struct virtio_device *vdev) 466static 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 */ 483static int vp_try_to_find_vqs(struct virtio_device *vdev, unsigned nvqs,
507static 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
534error_request: 530error_request:
535 return PTR_ERR(vqs[i]); 531 return err;
532}
533
534/* the config->find_vqs() implementation */
535static 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
538static struct virtio_config_ops virtio_pci_config_ops = { 564static struct virtio_config_ops virtio_pci_config_ops = {