aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean-Philippe Brucker <jean-philippe.brucker@arm.com>2016-09-05 09:09:53 -0400
committerWill Deacon <will.deacon@arm.com>2016-09-16 04:34:13 -0400
commitbcfced1580c40662d1c095899af9d0dd3ed9e7bc (patch)
tree9cc92ea495ea28421da9ff31c9b96323246a4d3d
parent6070529bebd26e0a80d9b9653a6f53275086603f (diff)
iommu/arm-smmu: Fix polling of command queue
When the SMMUv3 driver attempts to send a command, it adds an entry to the command queue. This is a circular buffer, where both the producer and consumer have a wrap bit. When producer.index == consumer.index and producer.wrap == consumer.wrap, the list is empty. When producer.index == consumer.index and producer.wrap != consumer.wrap, the list is full. If the list is full when the driver needs to add a command, it waits for the SMMU to consume one command, and advance the consumer pointer. The problem is that we currently rely on "X before Y" operation to know if entries have been consumed, which is a bit fiddly since it only makes sense when the distance between X and Y is less than or equal to the size of the queue. At the moment when the list is full, we use "Consumer before Producer + 1", which is out of range and returns a value opposite to what we expect: when the queue transitions to not full, we stay in the polling loop and time out, printing an error. Given that the actual bug was difficult to determine, simplify the polling logic by relying exclusively on queue_full and queue_empty, that don't have this range constraint. Polling the queue is now straightforward: * When we want to add a command and the list is full, wait until it isn't full and retry. * After adding a sync, wait for the list to be empty before returning. Suggested-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r--drivers/iommu/arm-smmu-v3.c28
1 files changed, 9 insertions, 19 deletions
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index d156c1e610d6..c040e246bc59 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -713,19 +713,15 @@ static void queue_inc_prod(struct arm_smmu_queue *q)
713 writel(q->prod, q->prod_reg); 713 writel(q->prod, q->prod_reg);
714} 714}
715 715
716static bool __queue_cons_before(struct arm_smmu_queue *q, u32 until) 716/*
717{ 717 * Wait for the SMMU to consume items. If drain is true, wait until the queue
718 if (Q_WRP(q, q->cons) == Q_WRP(q, until)) 718 * is empty. Otherwise, wait until there is at least one free slot.
719 return Q_IDX(q, q->cons) < Q_IDX(q, until); 719 */
720 720static int queue_poll_cons(struct arm_smmu_queue *q, bool drain, bool wfe)
721 return Q_IDX(q, q->cons) >= Q_IDX(q, until);
722}
723
724static int queue_poll_cons(struct arm_smmu_queue *q, u32 until, bool wfe)
725{ 721{
726 ktime_t timeout = ktime_add_us(ktime_get(), ARM_SMMU_POLL_TIMEOUT_US); 722 ktime_t timeout = ktime_add_us(ktime_get(), ARM_SMMU_POLL_TIMEOUT_US);
727 723
728 while (queue_sync_cons(q), __queue_cons_before(q, until)) { 724 while (queue_sync_cons(q), (drain ? !queue_empty(q) : queue_full(q))) {
729 if (ktime_compare(ktime_get(), timeout) > 0) 725 if (ktime_compare(ktime_get(), timeout) > 0)
730 return -ETIMEDOUT; 726 return -ETIMEDOUT;
731 727
@@ -896,7 +892,6 @@ static void arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu)
896static void arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu, 892static void arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
897 struct arm_smmu_cmdq_ent *ent) 893 struct arm_smmu_cmdq_ent *ent)
898{ 894{
899 u32 until;
900 u64 cmd[CMDQ_ENT_DWORDS]; 895 u64 cmd[CMDQ_ENT_DWORDS];
901 bool wfe = !!(smmu->features & ARM_SMMU_FEAT_SEV); 896 bool wfe = !!(smmu->features & ARM_SMMU_FEAT_SEV);
902 struct arm_smmu_queue *q = &smmu->cmdq.q; 897 struct arm_smmu_queue *q = &smmu->cmdq.q;
@@ -908,17 +903,12 @@ static void arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
908 } 903 }
909 904
910 spin_lock(&smmu->cmdq.lock); 905 spin_lock(&smmu->cmdq.lock);
911 while (until = q->prod + 1, queue_insert_raw(q, cmd) == -ENOSPC) { 906 while (queue_insert_raw(q, cmd) == -ENOSPC) {
912 /* 907 if (queue_poll_cons(q, false, wfe))
913 * Keep the queue locked, otherwise the producer could wrap
914 * twice and we could see a future consumer pointer that looks
915 * like it's behind us.
916 */
917 if (queue_poll_cons(q, until, wfe))
918 dev_err_ratelimited(smmu->dev, "CMDQ timeout\n"); 908 dev_err_ratelimited(smmu->dev, "CMDQ timeout\n");
919 } 909 }
920 910
921 if (ent->opcode == CMDQ_OP_CMD_SYNC && queue_poll_cons(q, until, wfe)) 911 if (ent->opcode == CMDQ_OP_CMD_SYNC && queue_poll_cons(q, true, wfe))
922 dev_err_ratelimited(smmu->dev, "CMD_SYNC timeout\n"); 912 dev_err_ratelimited(smmu->dev, "CMD_SYNC timeout\n");
923 spin_unlock(&smmu->cmdq.lock); 913 spin_unlock(&smmu->cmdq.lock);
924} 914}