diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-23 00:38:22 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-23 00:38:22 -0400 |
commit | 687d680985b1438360a9ba470ece8b57cd205c3b (patch) | |
tree | ae253608531e5c3e823600974c610e722e7de759 /drivers/pci/iov.c | |
parent | 1053414068bad659479e6efa62a67403b8b1ec0a (diff) | |
parent | 008fe148cb0fb51d266baabe2c09997b21cf90c6 (diff) |
Merge git://git.infradead.org/~dwmw2/iommu-2.6.31
* git://git.infradead.org/~dwmw2/iommu-2.6.31:
intel-iommu: Fix one last ia64 build problem in Pass Through Support
VT-d: support the device IOTLB
VT-d: cleanup iommu_flush_iotlb_psi and flush_unmaps
VT-d: add device IOTLB invalidation support
VT-d: parse ATSR in DMA Remapping Reporting Structure
PCI: handle Virtual Function ATS enabling
PCI: support the ATS capability
intel-iommu: dmar_set_interrupt return error value
intel-iommu: Tidy up iommu->gcmd handling
intel-iommu: Fix tiny theoretical race in write-buffer flush.
intel-iommu: Clean up handling of "caching mode" vs. IOTLB flushing.
intel-iommu: Clean up handling of "caching mode" vs. context flushing.
VT-d: fix invalid domain id for KVM context flush
Fix !CONFIG_DMAR build failure introduced by Intel IOMMU Pass Through Support
Intel IOMMU Pass Through Support
Fix up trivial conflicts in drivers/pci/{intel-iommu.c,intr_remapping.c}
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 | } | ||