diff options
Diffstat (limited to 'drivers/pci/iov.c')
| -rw-r--r-- | drivers/pci/iov.c | 155 |
1 files changed, 149 insertions, 6 deletions
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 03c7706c0a09..e3a87210e947 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | * | 5 | * |
| 6 | * PCI Express I/O Virtualization (IOV) support. | 6 | * PCI Express I/O Virtualization (IOV) support. |
| 7 | * Single Root IOV 1.0 | 7 | * Single Root IOV 1.0 |
| 8 | * Address Translation Service 1.0 | ||
| 8 | */ | 9 | */ |
| 9 | 10 | ||
| 10 | #include <linux/pci.h> | 11 | #include <linux/pci.h> |
| @@ -492,10 +493,10 @@ found: | |||
| 492 | 493 | ||
| 493 | if (pdev) | 494 | if (pdev) |
| 494 | iov->dev = pci_dev_get(pdev); | 495 | iov->dev = pci_dev_get(pdev); |
| 495 | else { | 496 | else |
| 496 | iov->dev = dev; | 497 | iov->dev = dev; |
| 497 | mutex_init(&iov->lock); | 498 | |
| 498 | } | 499 | mutex_init(&iov->lock); |
| 499 | 500 | ||
| 500 | dev->sriov = iov; | 501 | dev->sriov = iov; |
| 501 | dev->is_physfn = 1; | 502 | dev->is_physfn = 1; |
| @@ -515,11 +516,11 @@ static void sriov_release(struct pci_dev *dev) | |||
| 515 | { | 516 | { |
| 516 | BUG_ON(dev->sriov->nr_virtfn); | 517 | BUG_ON(dev->sriov->nr_virtfn); |
| 517 | 518 | ||
| 518 | if (dev == dev->sriov->dev) | 519 | if (dev != dev->sriov->dev) |
| 519 | mutex_destroy(&dev->sriov->lock); | ||
| 520 | else | ||
| 521 | pci_dev_put(dev->sriov->dev); | 520 | pci_dev_put(dev->sriov->dev); |
| 522 | 521 | ||
| 522 | mutex_destroy(&dev->sriov->lock); | ||
| 523 | |||
| 523 | kfree(dev->sriov); | 524 | kfree(dev->sriov); |
| 524 | dev->sriov = NULL; | 525 | dev->sriov = NULL; |
| 525 | } | 526 | } |
| @@ -681,3 +682,145 @@ irqreturn_t pci_sriov_migration(struct pci_dev *dev) | |||
| 681 | return sriov_migration(dev) ? IRQ_HANDLED : IRQ_NONE; | 682 | return sriov_migration(dev) ? IRQ_HANDLED : IRQ_NONE; |
| 682 | } | 683 | } |
| 683 | EXPORT_SYMBOL_GPL(pci_sriov_migration); | 684 | EXPORT_SYMBOL_GPL(pci_sriov_migration); |
| 685 | |||
| 686 | static int ats_alloc_one(struct pci_dev *dev, int ps) | ||
| 687 | { | ||
| 688 | int pos; | ||
| 689 | u16 cap; | ||
| 690 | struct pci_ats *ats; | ||
| 691 | |||
| 692 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS); | ||
| 693 | if (!pos) | ||
| 694 | return -ENODEV; | ||
| 695 | |||
| 696 | ats = kzalloc(sizeof(*ats), GFP_KERNEL); | ||
| 697 | if (!ats) | ||
| 698 | return -ENOMEM; | ||
| 699 | |||
| 700 | ats->pos = pos; | ||
| 701 | ats->stu = ps; | ||
| 702 | pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap); | ||
| 703 | ats->qdep = PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : | ||
| 704 | PCI_ATS_MAX_QDEP; | ||
| 705 | dev->ats = ats; | ||
| 706 | |||
| 707 | return 0; | ||
| 708 | } | ||
| 709 | |||
| 710 | static void ats_free_one(struct pci_dev *dev) | ||
| 711 | { | ||
| 712 | kfree(dev->ats); | ||
| 713 | dev->ats = NULL; | ||
| 714 | } | ||
| 715 | |||
| 716 | /** | ||
| 717 | * pci_enable_ats - enable the ATS capability | ||
| 718 | * @dev: the PCI device | ||
| 719 | * @ps: the IOMMU page shift | ||
| 720 | * | ||
| 721 | * Returns 0 on success, or negative on failure. | ||
| 722 | */ | ||
| 723 | int pci_enable_ats(struct pci_dev *dev, int ps) | ||
| 724 | { | ||
| 725 | int rc; | ||
| 726 | u16 ctrl; | ||
| 727 | |||
| 728 | BUG_ON(dev->ats && dev->ats->is_enabled); | ||
| 729 | |||
| 730 | if (ps < PCI_ATS_MIN_STU) | ||
| 731 | return -EINVAL; | ||
| 732 | |||
| 733 | if (dev->is_physfn || dev->is_virtfn) { | ||
| 734 | struct pci_dev *pdev = dev->is_physfn ? dev : dev->physfn; | ||
| 735 | |||
| 736 | mutex_lock(&pdev->sriov->lock); | ||
| 737 | if (pdev->ats) | ||
| 738 | rc = pdev->ats->stu == ps ? 0 : -EINVAL; | ||
| 739 | else | ||
| 740 | rc = ats_alloc_one(pdev, ps); | ||
| 741 | |||
| 742 | if (!rc) | ||
| 743 | pdev->ats->ref_cnt++; | ||
| 744 | mutex_unlock(&pdev->sriov->lock); | ||
| 745 | if (rc) | ||
| 746 | return rc; | ||
| 747 | } | ||
| 748 | |||
| 749 | if (!dev->is_physfn) { | ||
| 750 | rc = ats_alloc_one(dev, ps); | ||
| 751 | if (rc) | ||
| 752 | return rc; | ||
| 753 | } | ||
| 754 | |||
| 755 | ctrl = PCI_ATS_CTRL_ENABLE; | ||
| 756 | if (!dev->is_virtfn) | ||
| 757 | ctrl |= PCI_ATS_CTRL_STU(ps - PCI_ATS_MIN_STU); | ||
| 758 | pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl); | ||
| 759 | |||
| 760 | dev->ats->is_enabled = 1; | ||
| 761 | |||
| 762 | return 0; | ||
| 763 | } | ||
| 764 | |||
| 765 | /** | ||
| 766 | * pci_disable_ats - disable the ATS capability | ||
| 767 | * @dev: the PCI device | ||
| 768 | */ | ||
| 769 | void pci_disable_ats(struct pci_dev *dev) | ||
| 770 | { | ||
| 771 | u16 ctrl; | ||
| 772 | |||
| 773 | BUG_ON(!dev->ats || !dev->ats->is_enabled); | ||
| 774 | |||
| 775 | pci_read_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, &ctrl); | ||
| 776 | ctrl &= ~PCI_ATS_CTRL_ENABLE; | ||
| 777 | pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl); | ||
| 778 | |||
| 779 | dev->ats->is_enabled = 0; | ||
| 780 | |||
| 781 | if (dev->is_physfn || dev->is_virtfn) { | ||
| 782 | struct pci_dev *pdev = dev->is_physfn ? dev : dev->physfn; | ||
| 783 | |||
| 784 | mutex_lock(&pdev->sriov->lock); | ||
| 785 | pdev->ats->ref_cnt--; | ||
| 786 | if (!pdev->ats->ref_cnt) | ||
| 787 | ats_free_one(pdev); | ||
| 788 | mutex_unlock(&pdev->sriov->lock); | ||
| 789 | } | ||
| 790 | |||
| 791 | if (!dev->is_physfn) | ||
| 792 | ats_free_one(dev); | ||
| 793 | } | ||
| 794 | |||
| 795 | /** | ||
| 796 | * pci_ats_queue_depth - query the ATS Invalidate Queue Depth | ||
| 797 | * @dev: the PCI device | ||
| 798 | * | ||
| 799 | * Returns the queue depth on success, or negative on failure. | ||
| 800 | * | ||
| 801 | * The ATS spec uses 0 in the Invalidate Queue Depth field to | ||
| 802 | * indicate that the function can accept 32 Invalidate Request. | ||
| 803 | * But here we use the `real' values (i.e. 1~32) for the Queue | ||
| 804 | * Depth; and 0 indicates the function shares the Queue with | ||
| 805 | * other functions (doesn't exclusively own a Queue). | ||
| 806 | */ | ||
| 807 | int pci_ats_queue_depth(struct pci_dev *dev) | ||
| 808 | { | ||
| 809 | int pos; | ||
| 810 | u16 cap; | ||
| 811 | |||
| 812 | if (dev->is_virtfn) | ||
| 813 | return 0; | ||
| 814 | |||
| 815 | if (dev->ats) | ||
| 816 | return dev->ats->qdep; | ||
| 817 | |||
| 818 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS); | ||
| 819 | if (!pos) | ||
| 820 | return -ENODEV; | ||
| 821 | |||
| 822 | pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap); | ||
| 823 | |||
| 824 | return PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : | ||
| 825 | PCI_ATS_MAX_QDEP; | ||
| 826 | } | ||
