powerpc/powernv/npu: Add release_ownership hook

In order to make ATS work and translate addresses for arbitrary
LPID and PID, we need to program an NPU with LPID and allow PID wildcard
matching with a specific MSR mask.

This implements a helper to assign a GPU to LPAR and program the NPU
with a wildcard for PID and a helper to do clean-up. The helper takes
MSR (only DR/HV/PR/SF bits are allowed) to program them into NPU2 for
ATS checkout requests support.

This exports pnv_npu2_unmap_lpar_dev() as following patches will use it
from the VFIO driver.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
This commit is contained in:
Alexey Kardashevskiy 2018-12-19 19:52:27 +11:00 committed by Michael Ellerman
parent 0bd971676e
commit 1b785611e1
1 changed files with 51 additions and 0 deletions

View File

@ -299,6 +299,7 @@ static void pnv_npu_take_ownership(struct iommu_table_group *table_group)
table_group);
struct pnv_phb *phb = npe->phb;
int64_t rc;
struct pci_dev *gpdev = NULL;
/*
* Note: NPU has just a single TVE in the hardware which means that
@ -320,12 +321,28 @@ static void pnv_npu_take_ownership(struct iommu_table_group *table_group)
return;
}
pnv_pci_ioda2_tce_invalidate_entire(npe->phb, false);
get_gpu_pci_dev_and_pe(npe, &gpdev);
if (gpdev)
pnv_npu2_unmap_lpar_dev(gpdev);
}
static void pnv_npu_release_ownership(struct iommu_table_group *table_group)
{
struct pnv_ioda_pe *npe = container_of(table_group, struct pnv_ioda_pe,
table_group);
struct pci_dev *gpdev = NULL;
get_gpu_pci_dev_and_pe(npe, &gpdev);
if (gpdev)
pnv_npu2_map_lpar_dev(gpdev, 0, MSR_DR | MSR_PR | MSR_HV);
}
static struct iommu_table_group_ops pnv_pci_npu_ops = {
.set_window = pnv_npu_set_window,
.unset_window = pnv_npu_unset_window,
.take_ownership = pnv_npu_take_ownership,
.release_ownership = pnv_npu_release_ownership,
};
#endif /* !CONFIG_IOMMU_API */
@ -1236,3 +1253,37 @@ void pnv_npu2_map_lpar(struct pnv_ioda_pe *gpe, unsigned long msr)
list_for_each_entry(gpdev, &gpe->pbus->devices, bus_list)
pnv_npu2_map_lpar_dev(gpdev, 0, msr);
}
int pnv_npu2_unmap_lpar_dev(struct pci_dev *gpdev)
{
int ret;
struct pci_dev *npdev = pnv_pci_get_npu_dev(gpdev, 0);
struct pci_controller *hose;
struct pnv_phb *nphb;
if (!npdev)
return -ENODEV;
hose = pci_bus_to_host(npdev->bus);
nphb = hose->private_data;
dev_dbg(&gpdev->dev, "destroy context opalid=%llu\n",
nphb->opal_id);
ret = opal_npu_destroy_context(nphb->opal_id, 0/*__unused*/,
PCI_DEVID(gpdev->bus->number, gpdev->devfn));
if (ret < 0) {
dev_err(&gpdev->dev, "Failed to destroy context: %d\n", ret);
return ret;
}
/* Set LPID to 0 anyway, just to be safe */
dev_dbg(&gpdev->dev, "Map LPAR opalid=%llu lparid=0\n", nphb->opal_id);
ret = opal_npu_map_lpar(nphb->opal_id,
PCI_DEVID(gpdev->bus->number, gpdev->devfn), 0 /*LPID*/,
0 /* LPCR bits */);
if (ret)
dev_err(&gpdev->dev, "Error %d mapping device to LPAR\n", ret);
return ret;
}
EXPORT_SYMBOL_GPL(pnv_npu2_unmap_lpar_dev);