Commit 81244074 authored by Matthew Rosato's avatar Matthew Rosato Committed by Joerg Roedel
Browse files

iommu/s390: allow larger region tables



Extend the aperture calculation to consider sizes beyond the maximum
size of a region third table.  Attempt to always use the smallest
table size possible to avoid unnecessary extra steps during translation.
Update reserved region calculations to use the appropriate table size.

Signed-off-by: default avatarMatthew Rosato <mjrosato@linux.ibm.com>
Reviewed-by: default avatarNiklas Schnelle <schnelle@linux.ibm.com>
Tested-by: default avatarNiklas Schnelle <schnelle@linux.ibm.com>
Link: https://lore.kernel.org/r/20250411202433.181683-6-mjrosato@linux.ibm.com


Signed-off-by: default avatarJoerg Roedel <jroedel@suse.de>
parent d5fbc5ef
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ enum zpci_ioat_dtype {
#define ZPCI_KEY			(PAGE_DEFAULT_KEY << 5)

#define ZPCI_TABLE_SIZE_RT	(1UL << 42)
#define ZPCI_TABLE_SIZE_RS	(1UL << 53)

#define ZPCI_IOTA_STO_FLAG	(ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_ST)
#define ZPCI_IOTA_RTTO_FLAG	(ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_RT)
+52 −18
Original line number Diff line number Diff line
@@ -511,9 +511,25 @@ static bool s390_iommu_capable(struct device *dev, enum iommu_cap cap)
	}
}

static inline u64 max_tbl_size(struct s390_domain *domain)
{
	switch (domain->origin_type) {
	case ZPCI_TABLE_TYPE_RTX:
		return ZPCI_TABLE_SIZE_RT - 1;
	case ZPCI_TABLE_TYPE_RSX:
		return ZPCI_TABLE_SIZE_RS - 1;
	case ZPCI_TABLE_TYPE_RFX:
		return U64_MAX;
	default:
		return 0;
	}
}

static struct iommu_domain *s390_domain_alloc_paging(struct device *dev)
{
	struct zpci_dev *zdev = to_zpci_dev(dev);
	struct s390_domain *s390_domain;
	u64 aperture_size;

	s390_domain = kzalloc(sizeof(*s390_domain), GFP_KERNEL);
	if (!s390_domain)
@@ -524,10 +540,26 @@ static struct iommu_domain *s390_domain_alloc_paging(struct device *dev)
		kfree(s390_domain);
		return NULL;
	}

	aperture_size = min(s390_iommu_aperture,
			    zdev->end_dma - zdev->start_dma + 1);
	if (aperture_size <= (ZPCI_TABLE_SIZE_RT - zdev->start_dma)) {
		s390_domain->origin_type = ZPCI_TABLE_TYPE_RTX;
	} else if (aperture_size <= (ZPCI_TABLE_SIZE_RS - zdev->start_dma) &&
		  (zdev->dtsm & ZPCI_IOTA_DT_RS)) {
		s390_domain->origin_type = ZPCI_TABLE_TYPE_RSX;
	} else if (zdev->dtsm & ZPCI_IOTA_DT_RF) {
		s390_domain->origin_type = ZPCI_TABLE_TYPE_RFX;
	} else {
		/* Assume RTX available */
		s390_domain->origin_type = ZPCI_TABLE_TYPE_RTX;
		aperture_size = ZPCI_TABLE_SIZE_RT - zdev->start_dma;
	}
	zdev->end_dma = zdev->start_dma + aperture_size - 1;

	s390_domain->domain.geometry.force_aperture = true;
	s390_domain->domain.geometry.aperture_start = 0;
	s390_domain->domain.geometry.aperture_end = ZPCI_TABLE_SIZE_RT - 1;
	s390_domain->origin_type = ZPCI_TABLE_TYPE_RTX;
	s390_domain->domain.geometry.aperture_end = max_tbl_size(s390_domain);

	spin_lock_init(&s390_domain->list_lock);
	INIT_LIST_HEAD_RCU(&s390_domain->devices);
@@ -680,6 +712,8 @@ static void s390_iommu_get_resv_regions(struct device *dev,
{
	struct zpci_dev *zdev = to_zpci_dev(dev);
	struct iommu_resv_region *region;
	u64 max_size, end_resv;
	unsigned long flags;

	if (zdev->start_dma) {
		region = iommu_alloc_resv_region(0, zdev->start_dma, 0,
@@ -689,10 +723,21 @@ static void s390_iommu_get_resv_regions(struct device *dev,
		list_add_tail(&region->list, list);
	}

	if (zdev->end_dma < ZPCI_TABLE_SIZE_RT - 1) {
		region = iommu_alloc_resv_region(zdev->end_dma + 1,
						 ZPCI_TABLE_SIZE_RT - zdev->end_dma - 1,
						 0, IOMMU_RESV_RESERVED, GFP_KERNEL);
	spin_lock_irqsave(&zdev->dom_lock, flags);
	if (zdev->s390_domain->type == IOMMU_DOMAIN_BLOCKED ||
	    zdev->s390_domain->type == IOMMU_DOMAIN_IDENTITY) {
		spin_unlock_irqrestore(&zdev->dom_lock, flags);
		return;
	}

	max_size = max_tbl_size(to_s390_domain(zdev->s390_domain));
	spin_unlock_irqrestore(&zdev->dom_lock, flags);

	if (zdev->end_dma < max_size) {
		end_resv = max_size - zdev->end_dma;
		region = iommu_alloc_resv_region(zdev->end_dma + 1, end_resv,
						 0, IOMMU_RESV_RESERVED,
						 GFP_KERNEL);
		if (!region)
			return;
		list_add_tail(&region->list, list);
@@ -708,13 +753,9 @@ static struct iommu_device *s390_iommu_probe_device(struct device *dev)

	zdev = to_zpci_dev(dev);

	if (zdev->start_dma > zdev->end_dma ||
	    zdev->start_dma > ZPCI_TABLE_SIZE_RT - 1)
	if (zdev->start_dma > zdev->end_dma)
		return ERR_PTR(-EINVAL);

	if (zdev->end_dma > ZPCI_TABLE_SIZE_RT - 1)
		zdev->end_dma = ZPCI_TABLE_SIZE_RT - 1;

	if (zdev->tlb_refresh)
		dev->iommu->shadow_on_flush = 1;

@@ -999,7 +1040,6 @@ struct zpci_iommu_ctrs *zpci_get_iommu_ctrs(struct zpci_dev *zdev)

int zpci_init_iommu(struct zpci_dev *zdev)
{
	u64 aperture_size;
	int rc = 0;

	rc = iommu_device_sysfs_add(&zdev->iommu_dev, NULL, NULL,
@@ -1017,12 +1057,6 @@ int zpci_init_iommu(struct zpci_dev *zdev)
	if (rc)
		goto out_sysfs;

	zdev->start_dma = PAGE_ALIGN(zdev->start_dma);
	aperture_size = min3(s390_iommu_aperture,
			     ZPCI_TABLE_SIZE_RT - zdev->start_dma,
			     zdev->end_dma - zdev->start_dma + 1);
	zdev->end_dma = zdev->start_dma + aperture_size - 1;

	return 0;

out_sysfs: