Skip to content

Commit da9e02a

Browse files
committed
dma-mapping: Implement link/unlink ranges API
JIRA: https://issues.redhat.com/browse/RHEL-113839 Upstream-Status: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Conflicts: iommu_dma_free_iova still takes cookie in RHEL9. commit 433a762 Author: Leon Romanovsky <leonro@nvidia.com> Date: Mon May 5 10:01:44 2025 +0300 dma-mapping: Implement link/unlink ranges API Introduce new DMA APIs to perform DMA linkage of buffers in layers higher than DMA. In proposed API, the callers will perform the following steps. In map path: if (dma_can_use_iova(...)) dma_iova_alloc() for (page in range) dma_iova_link_next(...) dma_iova_sync(...) else /* Fallback to legacy map pages */ for (all pages) dma_map_page(...) In unmap path: if (dma_can_use_iova(...)) dma_iova_destroy() else for (all pages) dma_unmap_page(...) Reviewed-by: Christoph Hellwig <hch@lst.de> Tested-by: Jens Axboe <axboe@kernel.dk> Reviewed-by: Luis Chamberlain <mcgrof@kernel.org> Signed-off-by: Leon Romanovsky <leonro@nvidia.com> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> (cherry picked from commit 433a762) Co-developed-by: Claude claude-sonnet-4 Signed-off-by: Jerry Snitselaar <jsnitsel@redhat.com>
1 parent bb196e3 commit da9e02a

File tree

2 files changed

+306
-1
lines changed

2 files changed

+306
-1
lines changed

drivers/iommu/dma-iommu.c

Lines changed: 274 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1198,6 +1198,17 @@ static phys_addr_t iommu_dma_map_swiotlb(struct device *dev, phys_addr_t phys,
11981198
return phys;
11991199
}
12001200

1201+
/*
1202+
* Checks if a physical buffer has unaligned boundaries with respect to
1203+
* the IOMMU granule. Returns non-zero if either the start or end
1204+
* address is not aligned to the granule boundary.
1205+
*/
1206+
static inline size_t iova_unaligned(struct iova_domain *iovad, phys_addr_t phys,
1207+
size_t size)
1208+
{
1209+
return iova_offset(iovad, phys | size);
1210+
}
1211+
12011212
dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
12021213
unsigned long offset, size_t size, enum dma_data_direction dir,
12031214
unsigned long attrs)
@@ -1215,7 +1226,7 @@ dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
12151226
* we don't need to use a bounce page.
12161227
*/
12171228
if (dev_use_swiotlb(dev, size, dir) &&
1218-
iova_offset(iovad, phys | size)) {
1229+
iova_unaligned(iovad, phys, size)) {
12191230
phys = iommu_dma_map_swiotlb(dev, phys, size, dir, attrs);
12201231
if (phys == (phys_addr_t)DMA_MAPPING_ERROR)
12211232
return DMA_MAPPING_ERROR;
@@ -1841,6 +1852,268 @@ void dma_iova_free(struct device *dev, struct dma_iova_state *state)
18411852
}
18421853
EXPORT_SYMBOL_GPL(dma_iova_free);
18431854

1855+
static int __dma_iova_link(struct device *dev, dma_addr_t addr,
1856+
phys_addr_t phys, size_t size, enum dma_data_direction dir,
1857+
unsigned long attrs)
1858+
{
1859+
bool coherent = dev_is_dma_coherent(dev);
1860+
1861+
if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
1862+
arch_sync_dma_for_device(phys, size, dir);
1863+
1864+
return iommu_map_nosync(iommu_get_dma_domain(dev), addr, phys, size,
1865+
dma_info_to_prot(dir, coherent, attrs), GFP_ATOMIC);
1866+
}
1867+
1868+
static int iommu_dma_iova_bounce_and_link(struct device *dev, dma_addr_t addr,
1869+
phys_addr_t phys, size_t bounce_len,
1870+
enum dma_data_direction dir, unsigned long attrs,
1871+
size_t iova_start_pad)
1872+
{
1873+
struct iommu_domain *domain = iommu_get_dma_domain(dev);
1874+
struct iova_domain *iovad = &domain->iova_cookie->iovad;
1875+
phys_addr_t bounce_phys;
1876+
int error;
1877+
1878+
bounce_phys = iommu_dma_map_swiotlb(dev, phys, bounce_len, dir, attrs);
1879+
if (bounce_phys == DMA_MAPPING_ERROR)
1880+
return -ENOMEM;
1881+
1882+
error = __dma_iova_link(dev, addr - iova_start_pad,
1883+
bounce_phys - iova_start_pad,
1884+
iova_align(iovad, bounce_len), dir, attrs);
1885+
if (error)
1886+
swiotlb_tbl_unmap_single(dev, bounce_phys, bounce_len, dir,
1887+
attrs);
1888+
return error;
1889+
}
1890+
1891+
static int iommu_dma_iova_link_swiotlb(struct device *dev,
1892+
struct dma_iova_state *state, phys_addr_t phys, size_t offset,
1893+
size_t size, enum dma_data_direction dir, unsigned long attrs)
1894+
{
1895+
struct iommu_domain *domain = iommu_get_dma_domain(dev);
1896+
struct iommu_dma_cookie *cookie = domain->iova_cookie;
1897+
struct iova_domain *iovad = &cookie->iovad;
1898+
size_t iova_start_pad = iova_offset(iovad, phys);
1899+
size_t iova_end_pad = iova_offset(iovad, phys + size);
1900+
dma_addr_t addr = state->addr + offset;
1901+
size_t mapped = 0;
1902+
int error;
1903+
1904+
if (iova_start_pad) {
1905+
size_t bounce_len = min(size, iovad->granule - iova_start_pad);
1906+
1907+
error = iommu_dma_iova_bounce_and_link(dev, addr, phys,
1908+
bounce_len, dir, attrs, iova_start_pad);
1909+
if (error)
1910+
return error;
1911+
state->__size |= DMA_IOVA_USE_SWIOTLB;
1912+
1913+
mapped += bounce_len;
1914+
size -= bounce_len;
1915+
if (!size)
1916+
return 0;
1917+
}
1918+
1919+
size -= iova_end_pad;
1920+
error = __dma_iova_link(dev, addr + mapped, phys + mapped, size, dir,
1921+
attrs);
1922+
if (error)
1923+
goto out_unmap;
1924+
mapped += size;
1925+
1926+
if (iova_end_pad) {
1927+
error = iommu_dma_iova_bounce_and_link(dev, addr + mapped,
1928+
phys + mapped, iova_end_pad, dir, attrs, 0);
1929+
if (error)
1930+
goto out_unmap;
1931+
state->__size |= DMA_IOVA_USE_SWIOTLB;
1932+
}
1933+
1934+
return 0;
1935+
1936+
out_unmap:
1937+
dma_iova_unlink(dev, state, 0, mapped, dir, attrs);
1938+
return error;
1939+
}
1940+
1941+
/**
1942+
* dma_iova_link - Link a range of IOVA space
1943+
* @dev: DMA device
1944+
* @state: IOVA state
1945+
* @phys: physical address to link
1946+
* @offset: offset into the IOVA state to map into
1947+
* @size: size of the buffer
1948+
* @dir: DMA direction
1949+
* @attrs: attributes of mapping properties
1950+
*
1951+
* Link a range of IOVA space for the given IOVA state without IOTLB sync.
1952+
* This function is used to link multiple physical addresses in contiguous
1953+
* IOVA space without performing costly IOTLB sync.
1954+
*
1955+
* The caller is responsible to call to dma_iova_sync() to sync IOTLB at
1956+
* the end of linkage.
1957+
*/
1958+
int dma_iova_link(struct device *dev, struct dma_iova_state *state,
1959+
phys_addr_t phys, size_t offset, size_t size,
1960+
enum dma_data_direction dir, unsigned long attrs)
1961+
{
1962+
struct iommu_domain *domain = iommu_get_dma_domain(dev);
1963+
struct iommu_dma_cookie *cookie = domain->iova_cookie;
1964+
struct iova_domain *iovad = &cookie->iovad;
1965+
size_t iova_start_pad = iova_offset(iovad, phys);
1966+
1967+
if (WARN_ON_ONCE(iova_start_pad && offset > 0))
1968+
return -EIO;
1969+
1970+
if (dev_use_swiotlb(dev, size, dir) &&
1971+
iova_unaligned(iovad, phys, size))
1972+
return iommu_dma_iova_link_swiotlb(dev, state, phys, offset,
1973+
size, dir, attrs);
1974+
1975+
return __dma_iova_link(dev, state->addr + offset - iova_start_pad,
1976+
phys - iova_start_pad,
1977+
iova_align(iovad, size + iova_start_pad), dir, attrs);
1978+
}
1979+
EXPORT_SYMBOL_GPL(dma_iova_link);
1980+
1981+
/**
1982+
* dma_iova_sync - Sync IOTLB
1983+
* @dev: DMA device
1984+
* @state: IOVA state
1985+
* @offset: offset into the IOVA state to sync
1986+
* @size: size of the buffer
1987+
*
1988+
* Sync IOTLB for the given IOVA state. This function should be called on
1989+
* the IOVA-contiguous range created by one ore more dma_iova_link() calls
1990+
* to sync the IOTLB.
1991+
*/
1992+
int dma_iova_sync(struct device *dev, struct dma_iova_state *state,
1993+
size_t offset, size_t size)
1994+
{
1995+
struct iommu_domain *domain = iommu_get_dma_domain(dev);
1996+
struct iommu_dma_cookie *cookie = domain->iova_cookie;
1997+
struct iova_domain *iovad = &cookie->iovad;
1998+
dma_addr_t addr = state->addr + offset;
1999+
size_t iova_start_pad = iova_offset(iovad, addr);
2000+
2001+
return iommu_sync_map(domain, addr - iova_start_pad,
2002+
iova_align(iovad, size + iova_start_pad));
2003+
}
2004+
EXPORT_SYMBOL_GPL(dma_iova_sync);
2005+
2006+
static void iommu_dma_iova_unlink_range_slow(struct device *dev,
2007+
dma_addr_t addr, size_t size, enum dma_data_direction dir,
2008+
unsigned long attrs)
2009+
{
2010+
struct iommu_domain *domain = iommu_get_dma_domain(dev);
2011+
struct iommu_dma_cookie *cookie = domain->iova_cookie;
2012+
struct iova_domain *iovad = &cookie->iovad;
2013+
size_t iova_start_pad = iova_offset(iovad, addr);
2014+
dma_addr_t end = addr + size;
2015+
2016+
do {
2017+
phys_addr_t phys;
2018+
size_t len;
2019+
2020+
phys = iommu_iova_to_phys(domain, addr);
2021+
if (WARN_ON(!phys))
2022+
/* Something very horrible happen here */
2023+
return;
2024+
2025+
len = min_t(size_t,
2026+
end - addr, iovad->granule - iova_start_pad);
2027+
2028+
if (!dev_is_dma_coherent(dev) &&
2029+
!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
2030+
arch_sync_dma_for_cpu(phys, len, dir);
2031+
2032+
swiotlb_tbl_unmap_single(dev, phys, len, dir, attrs);
2033+
2034+
addr += len;
2035+
iova_start_pad = 0;
2036+
} while (addr < end);
2037+
}
2038+
2039+
static void __iommu_dma_iova_unlink(struct device *dev,
2040+
struct dma_iova_state *state, size_t offset, size_t size,
2041+
enum dma_data_direction dir, unsigned long attrs,
2042+
bool free_iova)
2043+
{
2044+
struct iommu_domain *domain = iommu_get_dma_domain(dev);
2045+
struct iommu_dma_cookie *cookie = domain->iova_cookie;
2046+
struct iova_domain *iovad = &cookie->iovad;
2047+
dma_addr_t addr = state->addr + offset;
2048+
size_t iova_start_pad = iova_offset(iovad, addr);
2049+
struct iommu_iotlb_gather iotlb_gather;
2050+
size_t unmapped;
2051+
2052+
if ((state->__size & DMA_IOVA_USE_SWIOTLB) ||
2053+
(!dev_is_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)))
2054+
iommu_dma_iova_unlink_range_slow(dev, addr, size, dir, attrs);
2055+
2056+
iommu_iotlb_gather_init(&iotlb_gather);
2057+
iotlb_gather.queued = free_iova && READ_ONCE(cookie->fq_domain);
2058+
2059+
size = iova_align(iovad, size + iova_start_pad);
2060+
addr -= iova_start_pad;
2061+
unmapped = iommu_unmap_fast(domain, addr, size, &iotlb_gather);
2062+
WARN_ON(unmapped != size);
2063+
2064+
if (!iotlb_gather.queued)
2065+
iommu_iotlb_sync(domain, &iotlb_gather);
2066+
if (free_iova)
2067+
iommu_dma_free_iova(cookie, addr, size, &iotlb_gather);
2068+
}
2069+
2070+
/**
2071+
* dma_iova_unlink - Unlink a range of IOVA space
2072+
* @dev: DMA device
2073+
* @state: IOVA state
2074+
* @offset: offset into the IOVA state to unlink
2075+
* @size: size of the buffer
2076+
* @dir: DMA direction
2077+
* @attrs: attributes of mapping properties
2078+
*
2079+
* Unlink a range of IOVA space for the given IOVA state.
2080+
*/
2081+
void dma_iova_unlink(struct device *dev, struct dma_iova_state *state,
2082+
size_t offset, size_t size, enum dma_data_direction dir,
2083+
unsigned long attrs)
2084+
{
2085+
__iommu_dma_iova_unlink(dev, state, offset, size, dir, attrs, false);
2086+
}
2087+
EXPORT_SYMBOL_GPL(dma_iova_unlink);
2088+
2089+
/**
2090+
* dma_iova_destroy - Finish a DMA mapping transaction
2091+
* @dev: DMA device
2092+
* @state: IOVA state
2093+
* @mapped_len: number of bytes to unmap
2094+
* @dir: DMA direction
2095+
* @attrs: attributes of mapping properties
2096+
*
2097+
* Unlink the IOVA range up to @mapped_len and free the entire IOVA space. The
2098+
* range of IOVA from dma_addr to @mapped_len must all be linked, and be the
2099+
* only linked IOVA in state.
2100+
*/
2101+
void dma_iova_destroy(struct device *dev, struct dma_iova_state *state,
2102+
size_t mapped_len, enum dma_data_direction dir,
2103+
unsigned long attrs)
2104+
{
2105+
if (mapped_len)
2106+
__iommu_dma_iova_unlink(dev, state, 0, mapped_len, dir, attrs,
2107+
true);
2108+
else
2109+
/*
2110+
* We can be here if first call to dma_iova_link() failed and
2111+
* there is nothing to unlink, so let's be more clear.
2112+
*/
2113+
dma_iova_free(dev, state);
2114+
}
2115+
EXPORT_SYMBOL_GPL(dma_iova_destroy);
2116+
18442117
void iommu_setup_dma_ops(struct device *dev)
18452118
{
18462119
struct iommu_domain *domain = iommu_get_domain_for_dev(dev);

include/linux/dma-mapping.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,17 @@ static inline bool dma_use_iova(struct dma_iova_state *state)
309309
bool dma_iova_try_alloc(struct device *dev, struct dma_iova_state *state,
310310
phys_addr_t phys, size_t size);
311311
void dma_iova_free(struct device *dev, struct dma_iova_state *state);
312+
void dma_iova_destroy(struct device *dev, struct dma_iova_state *state,
313+
size_t mapped_len, enum dma_data_direction dir,
314+
unsigned long attrs);
315+
int dma_iova_sync(struct device *dev, struct dma_iova_state *state,
316+
size_t offset, size_t size);
317+
int dma_iova_link(struct device *dev, struct dma_iova_state *state,
318+
phys_addr_t phys, size_t offset, size_t size,
319+
enum dma_data_direction dir, unsigned long attrs);
320+
void dma_iova_unlink(struct device *dev, struct dma_iova_state *state,
321+
size_t offset, size_t size, enum dma_data_direction dir,
322+
unsigned long attrs);
312323
#else /* CONFIG_IOMMU_DMA */
313324
static inline bool dma_use_iova(struct dma_iova_state *state)
314325
{
@@ -323,6 +334,27 @@ static inline void dma_iova_free(struct device *dev,
323334
struct dma_iova_state *state)
324335
{
325336
}
337+
static inline void dma_iova_destroy(struct device *dev,
338+
struct dma_iova_state *state, size_t mapped_len,
339+
enum dma_data_direction dir, unsigned long attrs)
340+
{
341+
}
342+
static inline int dma_iova_sync(struct device *dev,
343+
struct dma_iova_state *state, size_t offset, size_t size)
344+
{
345+
return -EOPNOTSUPP;
346+
}
347+
static inline int dma_iova_link(struct device *dev,
348+
struct dma_iova_state *state, phys_addr_t phys, size_t offset,
349+
size_t size, enum dma_data_direction dir, unsigned long attrs)
350+
{
351+
return -EOPNOTSUPP;
352+
}
353+
static inline void dma_iova_unlink(struct device *dev,
354+
struct dma_iova_state *state, size_t offset, size_t size,
355+
enum dma_data_direction dir, unsigned long attrs)
356+
{
357+
}
326358
#endif /* CONFIG_IOMMU_DMA */
327359

328360
#if defined(CONFIG_HAS_DMA) && defined(CONFIG_DMA_NEED_SYNC)

0 commit comments

Comments
 (0)