diff options
Diffstat (limited to 'drivers/pci/iov.c')
-rw-r--r-- | drivers/pci/iov.c | 161 |
1 files changed, 153 insertions, 8 deletions
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index b497daab3d4a..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> |
@@ -110,7 +111,7 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset) | |||
110 | } | 111 | } |
111 | 112 | ||
112 | if (reset) | 113 | if (reset) |
113 | pci_execute_reset_function(virtfn); | 114 | __pci_reset_function(virtfn); |
114 | 115 | ||
115 | pci_device_add(virtfn, virtfn->bus); | 116 | pci_device_add(virtfn, virtfn->bus); |
116 | mutex_unlock(&iov->dev->sriov->lock); | 117 | mutex_unlock(&iov->dev->sriov->lock); |
@@ -164,7 +165,7 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset) | |||
164 | 165 | ||
165 | if (reset) { | 166 | if (reset) { |
166 | device_release_driver(&virtfn->dev); | 167 | device_release_driver(&virtfn->dev); |
167 | pci_execute_reset_function(virtfn); | 168 | __pci_reset_function(virtfn); |
168 | } | 169 | } |
169 | 170 | ||
170 | sprintf(buf, "virtfn%u", id); | 171 | sprintf(buf, "virtfn%u", id); |
@@ -487,13 +488,15 @@ found: | |||
487 | iov->self = dev; | 488 | iov->self = dev; |
488 | pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap); | 489 | pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap); |
489 | pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link); | 490 | pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link); |
491 | if (dev->pcie_type == PCI_EXP_TYPE_RC_END) | ||
492 | iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link); | ||
490 | 493 | ||
491 | if (pdev) | 494 | if (pdev) |
492 | iov->dev = pci_dev_get(pdev); | 495 | iov->dev = pci_dev_get(pdev); |
493 | else { | 496 | else |
494 | iov->dev = dev; | 497 | iov->dev = dev; |
495 | mutex_init(&iov->lock); | 498 | |
496 | } | 499 | mutex_init(&iov->lock); |
497 | 500 | ||
498 | dev->sriov = iov; | 501 | dev->sriov = iov; |
499 | dev->is_physfn = 1; | 502 | dev->is_physfn = 1; |
@@ -513,11 +516,11 @@ static void sriov_release(struct pci_dev *dev) | |||
513 | { | 516 | { |
514 | BUG_ON(dev->sriov->nr_virtfn); | 517 | BUG_ON(dev->sriov->nr_virtfn); |
515 | 518 | ||
516 | if (dev == dev->sriov->dev) | 519 | if (dev != dev->sriov->dev) |
517 | mutex_destroy(&dev->sriov->lock); | ||
518 | else | ||
519 | pci_dev_put(dev->sriov->dev); | 520 | pci_dev_put(dev->sriov->dev); |
520 | 521 | ||
522 | mutex_destroy(&dev->sriov->lock); | ||
523 | |||
521 | kfree(dev->sriov); | 524 | kfree(dev->sriov); |
522 | dev->sriov = NULL; | 525 | dev->sriov = NULL; |
523 | } | 526 | } |
@@ -679,3 +682,145 @@ irqreturn_t pci_sriov_migration(struct pci_dev *dev) | |||
679 | return sriov_migration(dev) ? IRQ_HANDLED : IRQ_NONE; | 682 | return sriov_migration(dev) ? IRQ_HANDLED : IRQ_NONE; |
680 | } | 683 | } |
681 | 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 | } | ||