All of lore.kernel.org
 help / color / mirror / Atom feed
From: Julien Grall <julien.grall@linaro.org>
To: xen-devel@lists.xenproject.org
Cc: ian.campbell@citrix.com,
	Andreas Herrmann <herrmann.der.user@googlemail.com>,
	Julien Grall <julien.grall@linaro.org>,
	tim@xen.org, stefano.stabellini@citrix.com,
	Andreas Herrmann <andreas.herrmann@calxeda.com>
Subject: [PATCH v2 11/12] xen/iommu: smmu: Introduce automatic stream-id-masking
Date: Fri, 16 Jan 2015 14:24:06 +0000	[thread overview]
Message-ID: <1421418247-30068-12-git-send-email-julien.grall@linaro.org> (raw)
In-Reply-To: <1421418247-30068-1-git-send-email-julien.grall@linaro.org>

From: Andreas Herrmann <andreas.herrmann@calxeda.com>

Try to determine mask/id values that match several stream IDs of a
master device when doing Stream ID matching. Thus the number of used
SMR groups that are required to map all stream IDs of a master device
to a context should be less than the number of SMR groups used so far
(currently one SMR group is used for one stream ID).

Taken from the Linux ML:
http://lists.infradead.org/pipermail/linux-arm-kernel/2014-January/226100.html

Changes compare to the Linux ML version:
    - _fls doesn't exist on Xen so use fls
    - Use num_s2crs rather than num_streamids in the arm_smmu_free_smrs.
    This former is the field used to configure SRMS

Cc: Andreas Herrmann <herrmann.der.user@googlemail.com>
Signed-off-by: Andreas Herrmann <andreas.herrmann@calxeda.com>
Signed-off-by: Julien Grall <julien.grall@linaro.org>
---
 xen/drivers/passthrough/arm/smmu.c | 177 +++++++++++++++++++++++++++++++++----
 1 file changed, 162 insertions(+), 15 deletions(-)

diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c
index bfc1069..8a6514f 100644
--- a/xen/drivers/passthrough/arm/smmu.c
+++ b/xen/drivers/passthrough/arm/smmu.c
@@ -43,6 +43,7 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
+#include <linux/bitops.h>
 
 #include <linux/amba/bus.h>
 
@@ -346,8 +347,10 @@ struct arm_smmu_smr {
 };
 
 struct arm_smmu_master_cfg {
-	int				num_streamids;
+	u32				num_streamids;
 	u16				streamids[MAX_MASTER_STREAMIDS];
+	int				num_s2crs;
+
 	struct arm_smmu_smr		*smrs;
 };
 
@@ -392,6 +395,9 @@ struct arm_smmu_device {
 	u32				num_context_irqs;
 	unsigned int			*irqs;
 
+	u32				smr_mask_mask;
+	u32				smr_id_mask;
+
 	struct list_head		list;
 	struct rb_root			masters;
 };
@@ -1113,6 +1119,137 @@ static void arm_smmu_free_pgtables(struct arm_smmu_domain *smmu_domain)
 	kfree(pgd_base);
 }
 
+/*
+ * For a given set N of 2**order different stream IDs (no duplicates
+ * please!) we determine values mask and id such that
+ *
+ * (1)          (x & mask) == id
+ *
+ * for each stream ID x from the given set N.
+ *
+ * If the number of bits that are set in mask equals n, then there
+ * exist 2**n different values y for which
+ *
+ * (2)          (y & mask) == id
+ *
+ * Thus if n equals order we know that for the calculated mask and id
+ * values there are exactly 2**order == 2**n stream IDs for which (1)
+ * is true. And we finally can use mask and id to configure an SMR to
+ * match all stream IDs in the set N.
+ */
+static int determine_smr_mask(struct arm_smmu_device *smmu,
+			      struct arm_smmu_master_cfg *cfg,
+			      struct arm_smmu_smr *smr, int start, int order)
+{
+	u16 i, zero_bits_mask, one_bits_mask, const_mask;
+	int nr;
+
+	nr = 1 << order;
+
+	if (nr == 1) {
+		/* no mask, use streamid to match and be done with it */
+		smr->mask = 0;
+		smr->id = cfg->streamids[start];
+		return 0;
+	}
+
+	zero_bits_mask = 0;
+	one_bits_mask = 0xffff;
+	for (i = start; i < start + nr; i++) {
+		zero_bits_mask |= cfg->streamids[i];	/* const 0 bits */
+		one_bits_mask &= cfg->streamids[i];	/* const 1 bits */
+	}
+	zero_bits_mask = ~zero_bits_mask;
+
+	/* bits having constant values (either 0 or 1) */
+	const_mask = zero_bits_mask | one_bits_mask;
+
+	i = hweight16(~const_mask);
+	if (i == order) {
+		/*
+		 * We have found a mask/id pair that matches exactly
+		 * nr = 2**order stream IDs which we used for its
+		 * calculation.
+		 */
+		smr->mask = ~const_mask;
+		smr->id = one_bits_mask;
+	} else {
+		/*
+		 * No usable mask/id pair for this set of streamids.
+		 * If i > order then mask/id would match more than nr
+		 * streamids.
+		 * If i < order then mask/id would match less than nr
+		 * streamids. (In this case we potentially have used
+		 * some duplicate streamids for the calculation.)
+		 */
+		return 1;
+	}
+
+	if (((smr->mask & smmu->smr_mask_mask) != smr->mask) ||
+		((smr->id & smmu->smr_id_mask) != smr->id))
+		/* insufficient number of mask/id bits */
+		return 1;
+
+	return 0;
+}
+
+static int determine_smr_mapping(struct arm_smmu_device *smmu,
+				 struct arm_smmu_master_cfg *cfg,
+				 struct arm_smmu_smr *smrs, int max_smrs)
+{
+	int nr_sid, nr, i, bit, start;
+
+	/*
+	 * This function is called only once -- when a master is added
+	 * to a domain. If cfg->num_s2crs != 0 then this master
+	 * was already added to a domain.
+	 */
+	if (cfg->num_s2crs)
+		return -EINVAL;
+
+	start = nr = 0;
+	nr_sid = cfg->num_streamids;
+	do {
+		/*
+		 * largest power-of-2 number of streamids for which to
+		 * determine a usable mask/id pair for stream matching
+		 */
+		bit = fls(nr_sid) - 1;
+		if (bit < 0)
+			return 0;
+
+		/*
+		 * iterate over power-of-2 numbers to determine
+		 * largest possible mask/id pair for stream matching
+		 * of next 2**i streamids
+		 */
+		for (i = bit; i >= 0; i--) {
+			if (!determine_smr_mask(smmu, cfg,
+						&smrs[cfg->num_s2crs],
+						start, i))
+				break;
+		}
+
+		if (i < 0)
+			goto out;
+
+		nr = 1 << i;
+		nr_sid -= nr;
+		start += nr;
+		cfg->num_s2crs++;
+	} while (cfg->num_s2crs <= max_smrs);
+
+out:
+	if (nr_sid) {
+		/* not enough mapping groups available */
+		cfg->num_s2crs = 0;
+		return -ENOSPC;
+	}
+
+	return 0;
+}
+
+
 static void arm_smmu_domain_destroy(struct iommu_domain *domain)
 {
 	struct arm_smmu_domain *smmu_domain = domain->priv;
@@ -1129,7 +1266,7 @@ static void arm_smmu_domain_destroy(struct iommu_domain *domain)
 static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu,
 					  struct arm_smmu_master_cfg *cfg)
 {
-	int i;
+	int i, max_smrs, ret;
 	struct arm_smmu_smr *smrs;
 	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
 
@@ -1139,31 +1276,32 @@ static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu,
 	if (cfg->smrs)
 		return -EEXIST;
 
-	smrs = kmalloc_array(cfg->num_streamids, sizeof(*smrs), GFP_KERNEL);
+	max_smrs = min(smmu->num_mapping_groups, cfg->num_streamids);
+	smrs = kmalloc(sizeof(*smrs) * max_smrs, GFP_KERNEL);
 	if (!smrs) {
 		dev_err(smmu->dev, "failed to allocate %d SMRs\n",
-			cfg->num_streamids);
+			max_smrs);
 		return -ENOMEM;
 	}
 
+	ret = determine_smr_mapping(smmu, cfg, smrs, max_smrs);
+	if (ret)
+		goto err_free_smrs;
+
 	/* Allocate the SMRs on the SMMU */
-	for (i = 0; i < cfg->num_streamids; ++i) {
+	for (i = 0; i < cfg->num_s2crs; ++i) {
 		int idx = __arm_smmu_alloc_bitmap(smmu->smr_map, 0,
 						  smmu->num_mapping_groups);
 		if (IS_ERR_VALUE(idx)) {
 			dev_err(smmu->dev, "failed to allocate free SMR\n");
-			goto err_free_smrs;
+			goto err_free_bitmap;
 		}
 
-		smrs[i] = (struct arm_smmu_smr) {
-			.idx	= idx,
-			.mask	= 0, /* We don't currently share SMRs */
-			.id	= cfg->streamids[i],
-		};
+		smrs[i].idx = idx;
 	}
 
 	/* It worked! Now, poke the actual hardware */
-	for (i = 0; i < cfg->num_streamids; ++i) {
+	for (i = 0; i < cfg->num_s2crs; ++i) {
 		u32 reg = SMR_VALID | smrs[i].id << SMR_ID_SHIFT |
 			  smrs[i].mask << SMR_MASK_SHIFT;
 		writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_SMR(smrs[i].idx));
@@ -1172,9 +1310,11 @@ static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu,
 	cfg->smrs = smrs;
 	return 0;
 
-err_free_smrs:
+err_free_bitmap:
 	while (--i >= 0)
 		__arm_smmu_free_bitmap(smmu->smr_map, smrs[i].idx);
+	cfg->num_s2crs = 0;
+err_free_smrs:
 	kfree(smrs);
 	return -ENOSPC;
 }
@@ -1190,13 +1330,15 @@ static void arm_smmu_master_free_smrs(struct arm_smmu_device *smmu,
 		return;
 
 	/* Invalidate the SMRs before freeing back to the allocator */
-	for (i = 0; i < cfg->num_streamids; ++i) {
+	for (i = 0; i < cfg->num_s2crs; ++i) {
 		u8 idx = smrs[i].idx;
 
 		writel_relaxed(~SMR_VALID, gr0_base + ARM_SMMU_GR0_SMR(idx));
 		__arm_smmu_free_bitmap(smmu->smr_map, idx);
 	}
 
+	cfg->num_s2crs = 0;
+
 	cfg->smrs = NULL;
 	kfree(smrs);
 }
@@ -1213,12 +1355,15 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 	if (ret)
 		return ret == -EEXIST ? 0 : ret;
 
-	for (i = 0; i < cfg->num_streamids; ++i) {
+	if (!cfg->num_s2crs)
+		cfg->num_s2crs = cfg->num_streamids;
+	for (i = 0; i < cfg->num_s2crs; ++i) {
 		u32 idx, s2cr;
 
 		idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
 		s2cr = S2CR_TYPE_TRANS |
 		       (smmu_domain->cfg.cbndx << S2CR_CBNDX_SHIFT);
+		dev_dbg(smmu->dev, "S2CR%d: 0x%x\n", idx, s2cr);
 		writel_relaxed(s2cr, gr0_base + ARM_SMMU_GR0_S2CR(idx));
 	}
 
@@ -1890,6 +2035,8 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 				mask, sid);
 			return -ENODEV;
 		}
+		smmu->smr_mask_mask = mask;
+		smmu->smr_id_mask = sid;
 
 		dev_notice(smmu->dev,
 			   "\tstream matching with %u register groups, mask 0x%x",
-- 
2.1.4

  parent reply	other threads:[~2015-01-16 14:25 UTC|newest]

Thread overview: 51+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-01-16 14:23 [PATCH v2 00/12] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
2015-01-16 14:23 ` [PATCH v2 01/12] xen/arm: gic-v2: Change the device name in DT_DEVICE_START Julien Grall
2015-01-27 15:52   ` Stefano Stabellini
2015-01-16 14:23 ` [PATCH v2 02/12] xen/arm: vgic: Drop unecessary include asm/device.h Julien Grall
2015-01-27 15:53   ` Stefano Stabellini
2015-01-16 14:23 ` [PATCH v2 03/12] xen/dt: Extend dt_device_match to possibly store data Julien Grall
2015-01-27 15:57   ` Stefano Stabellini
2015-01-27 16:07     ` Julien Grall
2015-01-27 16:10       ` Stefano Stabellini
2015-01-27 16:35         ` Julien Grall
2015-01-27 16:46           ` Stefano Stabellini
2015-01-27 16:49             ` Julien Grall
2015-01-16 14:23 ` [PATCH v2 04/12] xen/arm: device: Rename device_type into device_match Julien Grall
2015-01-27 16:05   ` Stefano Stabellini
2015-01-27 16:09     ` Julien Grall
2015-01-27 16:13       ` Stefano Stabellini
2015-01-27 22:26         ` Julien Grall
2015-01-16 14:24 ` [PATCH v2 05/12] xen/iommu: arm: Remove temporary the SMMU driver Julien Grall
2015-01-27 16:05   ` Stefano Stabellini
2015-01-16 14:24 ` [PATCH v2 06/12] xen/arm: Introduce a generic way to describe device Julien Grall
2015-01-16 14:59   ` Jan Beulich
2015-01-16 15:01     ` Julien Grall
2015-01-27 16:13   ` Stefano Stabellini
2015-01-16 14:24 ` [PATCH v2 07/12] xen/iommu: Consolidate device assignment ops into a single set Julien Grall
2015-01-16 15:03   ` Jan Beulich
2015-01-27 16:16   ` Stefano Stabellini
2015-01-16 14:24 ` [PATCH v2 08/12] xen/arm: Describe device supported by a driver with dt_match_node Julien Grall
2015-01-27 16:28   ` Stefano Stabellini
2015-01-16 14:24 ` [PATCH v2 09/12] xen/iommu: arm: Import the SMMU driver from Linux Julien Grall
2015-01-27 16:28   ` Stefano Stabellini
2015-01-27 16:51     ` Julien Grall
2015-01-16 14:24 ` [PATCH v2 10/12] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices Julien Grall
2015-01-27 16:30   ` Stefano Stabellini
2015-01-27 16:52     ` Julien Grall
2015-01-27 17:02       ` Stefano Stabellini
2015-01-27 17:33         ` Julien Grall
2015-01-27 17:36           ` Stefano Stabellini
2015-01-27 17:44           ` Ian Campbell
2015-01-27 17:51             ` Julien Grall
2015-01-27 20:54               ` Andreas Herrmann
2015-01-28 10:38               ` Ian Campbell
2015-01-28 11:39                 ` Julien Grall
2015-01-16 14:24 ` Julien Grall [this message]
2015-01-16 14:24 ` [PATCH v2 12/12] xen/iommu: smmu: Add Xen specific code to be able to use the driver Julien Grall
2015-01-27 16:46   ` Stefano Stabellini
2015-01-27 17:05     ` Julien Grall
2015-01-27 17:34       ` Stefano Stabellini
2015-01-28 10:31         ` Ian Campbell
2015-01-28 11:49           ` Julien Grall
2015-01-28 12:13             ` Ian Campbell
2015-01-28 12:15             ` Stefano Stabellini

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1421418247-30068-12-git-send-email-julien.grall@linaro.org \
    --to=julien.grall@linaro.org \
    --cc=andreas.herrmann@calxeda.com \
    --cc=herrmann.der.user@googlemail.com \
    --cc=ian.campbell@citrix.com \
    --cc=stefano.stabellini@citrix.com \
    --cc=tim@xen.org \
    --cc=xen-devel@lists.xenproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.