* [PATCH v3] swiotlb: fix the check whether a device has used software IO TLB
@ 2023-09-26 18:55 Petr Tesarik
2023-09-26 20:23 ` Catalin Marinas
0 siblings, 1 reply; 2+ messages in thread
From: Petr Tesarik @ 2023-09-26 18:55 UTC (permalink / raw)
To: Christoph Hellwig, Marek Szyprowski, Robin Murphy,
open list:DMA MAPPING HELPERS, open list
Cc: Roberto Sassu, Catalin Marinas, Petr Tesarik, Jonathan Corbet
When CONFIG_SWIOTLB_DYNAMIC=y, devices which do not use the software IO TLB
can avoid swiotlb lookup. A flag is added by commit 1395706a1490 ("swiotlb:
search the software IO TLB only if the device makes use of it"), the flag
is correctly set, but it is then never checked. Add the actual check here.
Note that this code is an alternative to the default pool check, not an
additional check, because:
1. swiotlb_find_pool() also searches the default pool;
2. if dma_uses_io_tlb is false, the default swiotlb pool is not used.
Tested in a KVM guest against a QEMU RAM-backed SATA disk over virtio and
*not* using software IO TLB, this patch increases IOPS by approx 2% for
4-way parallel I/O.
The write memory barrier in swiotlb_dyn_alloc() is not needed, because a
newly allocated pool must always be observed by swiotlb_find_slots() before
an address from that pool is passed to is_swiotlb_buffer().
Correctness was verified using the following litmus test:
C swiotlb-new-pool
(*
* Result: Never
*
* Check that a newly allocated pool is always visible when the
* corresponding swiotlb buffer is visible.
*)
{
mem_pools = default;
}
P0(int **mem_pools, int *pool)
{
/* add_mem_pool() */
WRITE_ONCE(*pool, 999);
rcu_assign_pointer(*mem_pools, pool);
}
P1(int **mem_pools, int *flag, int *buf)
{
/* swiotlb_find_slots() */
int *r0;
int r1;
rcu_read_lock();
r0 = READ_ONCE(*mem_pools);
r1 = READ_ONCE(*r0);
rcu_read_unlock();
if (r1) {
WRITE_ONCE(*flag, 1);
smp_mb();
}
/* device driver (presumed) */
WRITE_ONCE(*buf, r1);
}
P2(int **mem_pools, int *flag, int *buf)
{
/* device driver (presumed) */
int r0 = READ_ONCE(*buf);
/* is_swiotlb_buffer() */
int r1;
int *r2;
int r3;
smp_rmb();
r1 = READ_ONCE(*flag);
if (r1) {
/* swiotlb_find_pool() */
rcu_read_lock();
r2 = READ_ONCE(*mem_pools);
r3 = READ_ONCE(*r2);
rcu_read_unlock();
}
}
exists (2:r0<>0 /\ 2:r3=0) (* Not found. *)
Fixes: 1395706a1490 ("swiotlb: search the software IO TLB only if the device makes use of it")
Reported-by: Jonathan Corbet <corbet@lwn.net>
Closes: https://lore.kernel.org/linux-iommu/87a5uz3ob8.fsf@meer.lwn.net/
Signed-off-by: Petr Tesarik <petr@tesarici.cz>
---
include/linux/swiotlb.h | 23 ++++++++++++++++-------
kernel/dma/swiotlb.c | 26 ++++++++++++++++++++------
2 files changed, 36 insertions(+), 13 deletions(-)
diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h
index b4536626f8ff..ecde0312dd52 100644
--- a/include/linux/swiotlb.h
+++ b/include/linux/swiotlb.h
@@ -172,14 +172,23 @@ static inline bool is_swiotlb_buffer(struct device *dev, phys_addr_t paddr)
if (!mem)
return false;
- if (IS_ENABLED(CONFIG_SWIOTLB_DYNAMIC)) {
- /* Pairs with smp_wmb() in swiotlb_find_slots() and
- * swiotlb_dyn_alloc(), which modify the RCU lists.
- */
- smp_rmb();
- return swiotlb_find_pool(dev, paddr);
- }
+#ifdef CONFIG_SWIOTLB_DYNAMIC
+ /*
+ * All SWIOTLB buffer addresses must have been returned by
+ * swiotlb_tbl_map_single() and passed to a device driver.
+ * If a SWIOTLB address is checked on another CPU, then it was
+ * presumably loaded by the device driver from an unspecified private
+ * data structure. Make sure that this load is ordered before reading
+ * dev->dma_uses_io_tlb here and mem->pools in swiotlb_find_pool().
+ *
+ * This barrier pairs with smp_mb() in swiotlb_find_slots().
+ */
+ smp_rmb();
+ return READ_ONCE(dev->dma_uses_io_tlb) &&
+ swiotlb_find_pool(dev, paddr);
+#else
return paddr >= mem->defpool.start && paddr < mem->defpool.end;
+#endif
}
static inline bool is_swiotlb_force_bounce(struct device *dev)
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 394494a6b1f3..f8d2b79b5f21 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -729,9 +729,6 @@ static void swiotlb_dyn_alloc(struct work_struct *work)
}
add_mem_pool(mem, pool);
-
- /* Pairs with smp_rmb() in is_swiotlb_buffer(). */
- smp_wmb();
}
/**
@@ -1152,9 +1149,26 @@ static int swiotlb_find_slots(struct device *dev, phys_addr_t orig_addr,
spin_unlock_irqrestore(&dev->dma_io_tlb_lock, flags);
found:
- dev->dma_uses_io_tlb = true;
- /* Pairs with smp_rmb() in is_swiotlb_buffer() */
- smp_wmb();
+ WRITE_ONCE(dev->dma_uses_io_tlb, true);
+
+ /*
+ * The general barrier orders reads and writes against a presumed store
+ * of the SWIOTLB buffer address by a device driver (to a driver private
+ * data structure). It serves two purposes.
+ *
+ * First, the store to dev->dma_uses_io_tlb must be ordered before the
+ * presumed store. This guarantees that the returned buffer address
+ * cannot be passed to another CPU before updating dev->dma_uses_io_tlb.
+ *
+ * Second, the load from mem->pools must be ordered before the same
+ * presumed store. This guarantees that the returned buffer address
+ * cannot be observed by another CPU before an update of the RCU list
+ * that was made by swiotlb_dyn_alloc() on a third CPU (cf. multicopy
+ * atomicity).
+ *
+ * See also the comment in is_swiotlb_buffer().
+ */
+ smp_mb();
*retpool = pool;
return index;
--
2.42.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH v3] swiotlb: fix the check whether a device has used software IO TLB
2023-09-26 18:55 [PATCH v3] swiotlb: fix the check whether a device has used software IO TLB Petr Tesarik
@ 2023-09-26 20:23 ` Catalin Marinas
0 siblings, 0 replies; 2+ messages in thread
From: Catalin Marinas @ 2023-09-26 20:23 UTC (permalink / raw)
To: Petr Tesarik
Cc: Christoph Hellwig, Marek Szyprowski, Robin Murphy,
open list:DMA MAPPING HELPERS, open list, Roberto Sassu,
Jonathan Corbet
On Tue, Sep 26, 2023 at 08:55:56PM +0200, Petr Tesarik wrote:
> When CONFIG_SWIOTLB_DYNAMIC=y, devices which do not use the software IO TLB
> can avoid swiotlb lookup. A flag is added by commit 1395706a1490 ("swiotlb:
> search the software IO TLB only if the device makes use of it"), the flag
> is correctly set, but it is then never checked. Add the actual check here.
>
> Note that this code is an alternative to the default pool check, not an
> additional check, because:
>
> 1. swiotlb_find_pool() also searches the default pool;
> 2. if dma_uses_io_tlb is false, the default swiotlb pool is not used.
>
> Tested in a KVM guest against a QEMU RAM-backed SATA disk over virtio and
> *not* using software IO TLB, this patch increases IOPS by approx 2% for
> 4-way parallel I/O.
>
> The write memory barrier in swiotlb_dyn_alloc() is not needed, because a
> newly allocated pool must always be observed by swiotlb_find_slots() before
> an address from that pool is passed to is_swiotlb_buffer().
>
> Correctness was verified using the following litmus test:
>
> C swiotlb-new-pool
>
> (*
> * Result: Never
> *
> * Check that a newly allocated pool is always visible when the
> * corresponding swiotlb buffer is visible.
> *)
>
> {
> mem_pools = default;
> }
>
> P0(int **mem_pools, int *pool)
> {
> /* add_mem_pool() */
> WRITE_ONCE(*pool, 999);
> rcu_assign_pointer(*mem_pools, pool);
> }
>
> P1(int **mem_pools, int *flag, int *buf)
> {
> /* swiotlb_find_slots() */
> int *r0;
> int r1;
>
> rcu_read_lock();
> r0 = READ_ONCE(*mem_pools);
> r1 = READ_ONCE(*r0);
> rcu_read_unlock();
>
> if (r1) {
> WRITE_ONCE(*flag, 1);
> smp_mb();
> }
>
> /* device driver (presumed) */
> WRITE_ONCE(*buf, r1);
> }
>
> P2(int **mem_pools, int *flag, int *buf)
> {
> /* device driver (presumed) */
> int r0 = READ_ONCE(*buf);
>
> /* is_swiotlb_buffer() */
> int r1;
> int *r2;
> int r3;
>
> smp_rmb();
> r1 = READ_ONCE(*flag);
> if (r1) {
> /* swiotlb_find_pool() */
> rcu_read_lock();
> r2 = READ_ONCE(*mem_pools);
> r3 = READ_ONCE(*r2);
> rcu_read_unlock();
> }
> }
>
> exists (2:r0<>0 /\ 2:r3=0) (* Not found. *)
>
> Fixes: 1395706a1490 ("swiotlb: search the software IO TLB only if the device makes use of it")
> Reported-by: Jonathan Corbet <corbet@lwn.net>
> Closes: https://lore.kernel.org/linux-iommu/87a5uz3ob8.fsf@meer.lwn.net/
> Signed-off-by: Petr Tesarik <petr@tesarici.cz>
I added this for v2, here it is again:
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2023-09-26 20:23 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-09-26 18:55 [PATCH v3] swiotlb: fix the check whether a device has used software IO TLB Petr Tesarik
2023-09-26 20:23 ` Catalin Marinas
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox