summaryrefslogtreecommitdiffstats
path: root/drivers/irqchip
diff options
context:
space:
mode:
authorHeyi Guo <guoheyi@huawei.com>2019-05-13 07:42:06 -0400
committerMarc Zyngier <marc.zyngier@arm.com>2019-06-05 09:57:59 -0400
commita050fa5476d418fc16b25abe168b3d38ba11e13c (patch)
tree8a2bc731802c62634e3508bab1991ca74689e2dc /drivers/irqchip
parent6d4d367d0e9ffab4d64a3436256a6a052dc1195d (diff)
irqchip/gic-v3-its: Fix command queue pointer comparison bug
When we run several VMs with PCI passthrough and GICv4 enabled, not pinning vCPUs, we will occasionally see below warnings in dmesg: ITS queue timeout (65440 65504 480) ITS cmd its_build_vmovp_cmd failed The reason for the above issue is that in BUILD_SINGLE_CMD_FUNC: 1. Post the write command. 2. Release the lock. 3. Start to read GITS_CREADR to get the reader pointer. 4. Compare the reader pointer to the target pointer. 5. If reader pointer does not reach the target, sleep 1us and continue to try. If we have several processors running the above concurrently, other CPUs will post write commands while the 1st CPU is waiting the completion. So we may have below issue: phase 1: ---rd_idx-----from_idx-----to_idx--0--------- wait 1us: phase 2: --------------from_idx-----to_idx--0-rd_idx-- That is the rd_idx may fly ahead of to_idx, and if in case to_idx is near the wrap point, rd_idx will wrap around. So the below condition will not be met even after 1s: if (from_idx < to_idx && rd_idx >= to_idx) There is another theoretical issue. For a slow and busy ITS, the initial rd_idx may fall behind from_idx a lot, just as below: ---rd_idx---0--from_idx-----to_idx----------- This will cause the wait function exit too early. Actually, it does not make much sense to use from_idx to judge if to_idx is wrapped, but we need a initial rd_idx when lock is still acquired, and it can be used to judge whether to_idx is wrapped and the current rd_idx is wrapped. We switch to a method of calculating the delta of two adjacent reads and accumulating it to get the sum, so that we can get the real rd_idx from the wrapped value even when the queue is almost full. Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jason Cooper <jason@lakedaemon.net> Signed-off-by: Heyi Guo <guoheyi@huawei.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Diffstat (limited to 'drivers/irqchip')
-rw-r--r--drivers/irqchip/irq-gic-v3-its.c35
1 files changed, 24 insertions, 11 deletions
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 1e364d3ad9c5..f0523916232d 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -744,32 +744,43 @@ static void its_flush_cmd(struct its_node *its, struct its_cmd_block *cmd)
744} 744}
745 745
746static int its_wait_for_range_completion(struct its_node *its, 746static int its_wait_for_range_completion(struct its_node *its,
747 struct its_cmd_block *from, 747 u64 prev_idx,
748 struct its_cmd_block *to) 748 struct its_cmd_block *to)
749{ 749{
750 u64 rd_idx, from_idx, to_idx; 750 u64 rd_idx, to_idx, linear_idx;
751 u32 count = 1000000; /* 1s! */ 751 u32 count = 1000000; /* 1s! */
752 752
753 from_idx = its_cmd_ptr_to_offset(its, from); 753 /* Linearize to_idx if the command set has wrapped around */
754 to_idx = its_cmd_ptr_to_offset(its, to); 754 to_idx = its_cmd_ptr_to_offset(its, to);
755 if (to_idx < prev_idx)
756 to_idx += ITS_CMD_QUEUE_SZ;
757
758 linear_idx = prev_idx;
755 759
756 while (1) { 760 while (1) {
761 s64 delta;
762
757 rd_idx = readl_relaxed(its->base + GITS_CREADR); 763 rd_idx = readl_relaxed(its->base + GITS_CREADR);
758 764
759 /* Direct case */ 765 /*
760 if (from_idx < to_idx && rd_idx >= to_idx) 766 * Compute the read pointer progress, taking the
761 break; 767 * potential wrap-around into account.
768 */
769 delta = rd_idx - prev_idx;
770 if (rd_idx < prev_idx)
771 delta += ITS_CMD_QUEUE_SZ;
762 772
763 /* Wrapped case */ 773 linear_idx += delta;
764 if (from_idx >= to_idx && rd_idx >= to_idx && rd_idx < from_idx) 774 if (linear_idx >= to_idx)
765 break; 775 break;
766 776
767 count--; 777 count--;
768 if (!count) { 778 if (!count) {
769 pr_err_ratelimited("ITS queue timeout (%llu %llu %llu)\n", 779 pr_err_ratelimited("ITS queue timeout (%llu %llu)\n",
770 from_idx, to_idx, rd_idx); 780 to_idx, linear_idx);
771 return -1; 781 return -1;
772 } 782 }
783 prev_idx = rd_idx;
773 cpu_relax(); 784 cpu_relax();
774 udelay(1); 785 udelay(1);
775 } 786 }
@@ -786,6 +797,7 @@ void name(struct its_node *its, \
786 struct its_cmd_block *cmd, *sync_cmd, *next_cmd; \ 797 struct its_cmd_block *cmd, *sync_cmd, *next_cmd; \
787 synctype *sync_obj; \ 798 synctype *sync_obj; \
788 unsigned long flags; \ 799 unsigned long flags; \
800 u64 rd_idx; \
789 \ 801 \
790 raw_spin_lock_irqsave(&its->lock, flags); \ 802 raw_spin_lock_irqsave(&its->lock, flags); \
791 \ 803 \
@@ -807,10 +819,11 @@ void name(struct its_node *its, \
807 } \ 819 } \
808 \ 820 \
809post: \ 821post: \
822 rd_idx = readl_relaxed(its->base + GITS_CREADR); \
810 next_cmd = its_post_commands(its); \ 823 next_cmd = its_post_commands(its); \
811 raw_spin_unlock_irqrestore(&its->lock, flags); \ 824 raw_spin_unlock_irqrestore(&its->lock, flags); \
812 \ 825 \
813 if (its_wait_for_range_completion(its, cmd, next_cmd)) \ 826 if (its_wait_for_range_completion(its, rd_idx, next_cmd)) \
814 pr_err_ratelimited("ITS cmd %ps failed\n", builder); \ 827 pr_err_ratelimited("ITS cmd %ps failed\n", builder); \
815} 828}
816 829