Skip to content

Commit 3335a1b

Browse files
arndbgregkh
authored andcommitted
ALSA: qc_audio_offload: try to reduce address space confusion
uaudio_transfer_buffer_setup() allocates a buffer for the subs->dev device, and the returned address for the buffer is a CPU local virtual address that may or may not be in the linear mapping, as well as a DMA address token that is accessible by the USB device, and this in turn may or may not correspond to the physical address. The use in the driver however assumes that these addresses are the linear map and the CPU physical address, respectively. Both are nonportable here, but in the end only the virtual address gets used by converting it to a physical address that gets mapped into a second iommu. Make this more explicit by pulling the conversion out first and warning if it is not part of the linear map, and using the actual physical address to map into the iommu in place of the dma address that may already be iommu-mapped into the usb host. Signed-off-by: Arnd Bergmann <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 5c7ef50 commit 3335a1b

File tree

1 file changed

+21
-11
lines changed

1 file changed

+21
-11
lines changed

sound/usb/qcom/qc_audio_offload.c

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ struct intf_info {
7878
size_t data_xfer_ring_size;
7979
unsigned long sync_xfer_ring_va;
8080
size_t sync_xfer_ring_size;
81-
unsigned long xfer_buf_iova;
81+
dma_addr_t xfer_buf_iova;
8282
size_t xfer_buf_size;
83-
phys_addr_t xfer_buf_dma;
83+
dma_addr_t xfer_buf_dma;
8484
u8 *xfer_buf_cpu;
8585

8686
/* USB endpoint information */
@@ -1018,11 +1018,12 @@ static int uaudio_transfer_buffer_setup(struct snd_usb_substream *subs,
10181018
struct mem_info_v01 *mem_info)
10191019
{
10201020
struct sg_table xfer_buf_sgt;
1021+
dma_addr_t xfer_buf_dma;
10211022
void *xfer_buf;
10221023
phys_addr_t xfer_buf_pa;
10231024
u32 len = xfer_buf_len;
10241025
bool dma_coherent;
1025-
unsigned long iova;
1026+
dma_addr_t xfer_buf_dma_sysdev;
10261027
u32 remainder;
10271028
u32 mult;
10281029
int ret;
@@ -1045,29 +1046,38 @@ static int uaudio_transfer_buffer_setup(struct snd_usb_substream *subs,
10451046
len = MAX_XFER_BUFF_LEN;
10461047
}
10471048

1048-
xfer_buf = usb_alloc_coherent(subs->dev, len, GFP_KERNEL, &xfer_buf_pa);
1049+
/* get buffer mapped into subs->dev */
1050+
xfer_buf = usb_alloc_coherent(subs->dev, len, GFP_KERNEL, &xfer_buf_dma);
10491051
if (!xfer_buf)
10501052
return -ENOMEM;
10511053

1054+
/* Remapping is not possible if xfer_buf is outside of linear map */
1055+
xfer_buf_pa = virt_to_phys(xfer_buf);
1056+
if (WARN_ON(!page_is_ram(PFN_DOWN(xfer_buf_pa)))) {
1057+
ret = -ENXIO;
1058+
goto unmap_sync;
1059+
}
10521060
dma_get_sgtable(subs->dev->bus->sysdev, &xfer_buf_sgt, xfer_buf,
1053-
xfer_buf_pa, len);
1054-
iova = uaudio_iommu_map(MEM_XFER_BUF, dma_coherent, xfer_buf_pa, len,
1055-
&xfer_buf_sgt);
1056-
if (!iova) {
1061+
xfer_buf_dma, len);
1062+
1063+
/* map the physical buffer into sysdev as well */
1064+
xfer_buf_dma_sysdev = uaudio_iommu_map(MEM_XFER_BUF, dma_coherent,
1065+
xfer_buf_pa, len, &xfer_buf_sgt);
1066+
if (!xfer_buf_dma_sysdev) {
10571067
ret = -ENOMEM;
10581068
goto unmap_sync;
10591069
}
10601070

1061-
mem_info->dma = xfer_buf_pa;
1071+
mem_info->dma = xfer_buf_dma;
10621072
mem_info->size = len;
1063-
mem_info->iova = PREPEND_SID_TO_IOVA(iova, uaudio_qdev->data->sid);
1073+
mem_info->iova = PREPEND_SID_TO_IOVA(xfer_buf_dma_sysdev, uaudio_qdev->data->sid);
10641074
*xfer_buf_cpu = xfer_buf;
10651075
sg_free_table(&xfer_buf_sgt);
10661076

10671077
return 0;
10681078

10691079
unmap_sync:
1070-
usb_free_coherent(subs->dev, len, xfer_buf, xfer_buf_pa);
1080+
usb_free_coherent(subs->dev, len, xfer_buf, xfer_buf_dma);
10711081

10721082
return ret;
10731083
}

0 commit comments

Comments
 (0)