From bafe277d6c5a8bc4b6b9ecafc35cc357c42e813d Mon Sep 17 00:00:00 2001 From: Praveen Talari Date: Wed, 1 Jul 2026 09:53:10 +0530 Subject: [PATCH] serial: qcom-geni: Pre-map RX DMA buffer at probe to avoid sleep-in-atomic geni_se_rx_dma_prep() calls dma_map_single() which can trigger IOMMU page table allocations under GFP_KERNEL. This is unsafe when called from qcom_geni_serial_start_rx_dma(), which runs in atomic context producing a "sleeping function called from invalid context" splat: __might_resched+0x15c/0x17c __alloc_pages_noprof+0xe4/0x4c8 qcom_io_pgtable_alloc_page+0x100/0x250 __arm_lpae_map+0x2d0/0x870 geni_se_rx_dma_prep+0xd8/0x158 qcom_geni_serial_start_rx_dma+0x84/0x16c qcom_geni_serial_startup+0x70/0x104 Fix this by mapping the RX DMA buffer once during probe, where sleeping is allowed, and keeping it mapped for the lifetime of the device. Replace the geni_se_rx_dma_prep() / geni_se_rx_dma_unprep() calls in the runtime paths with dma_sync_single_for_device() before initiating a transfer and dma_sync_single_for_cpu() on completion, using the persistent mapping. The buffer is unmapped in probe's error path and in remove(). Signed-off-by: Praveen Talari Link: https://patch.msgid.link/20260701-fix-sleep-in-atomic-context-during-rx-dma-setup-v1-1-95c208380c65@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 55 ++++++++++++++++----------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 7ead87b4eb65..3e460b0358eb 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -863,37 +864,31 @@ static void qcom_geni_serial_stop_rx_dma(struct uart_port *uport) uport->membase + SE_DMA_RX_IRQ_CLR); } - if (port->rx_dma_addr) { - geni_se_rx_dma_unprep(&port->se, port->rx_dma_addr, - DMA_RX_BUF_SIZE); - port->rx_dma_addr = 0; - } } static void qcom_geni_serial_start_rx_dma(struct uart_port *uport) { struct qcom_geni_serial_port *port = to_dev_port(uport); - int ret; if (qcom_geni_serial_secondary_active(uport)) qcom_geni_serial_stop_rx_dma(uport); geni_se_setup_s_cmd(&port->se, UART_START_READ, UART_PARAM_RFR_OPEN); - ret = geni_se_rx_dma_prep(&port->se, port->rx_buf, - DMA_RX_BUF_SIZE, - &port->rx_dma_addr); - if (ret) { - dev_err(uport->dev, "unable to start RX SE DMA: %d\n", ret); - qcom_geni_serial_stop_rx_dma(uport); + if (!port->rx_dma_addr) { + dev_err(uport->dev, "RX DMA buffer not mapped\n"); + return; } + + dma_sync_single_for_device(uport->dev->parent, port->rx_dma_addr, + DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); + geni_se_rx_init_dma(&port->se, port->rx_dma_addr, DMA_RX_BUF_SIZE); } static void qcom_geni_serial_handle_rx_dma(struct uart_port *uport, bool drop) { struct qcom_geni_serial_port *port = to_dev_port(uport); u32 rx_in; - int ret; if (!qcom_geni_serial_secondary_active(uport)) return; @@ -901,8 +896,8 @@ static void qcom_geni_serial_handle_rx_dma(struct uart_port *uport, bool drop) if (!port->rx_dma_addr) return; - geni_se_rx_dma_unprep(&port->se, port->rx_dma_addr, DMA_RX_BUF_SIZE); - port->rx_dma_addr = 0; + dma_sync_single_for_cpu(uport->dev->parent, port->rx_dma_addr, + DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); rx_in = readl(uport->membase + SE_DMA_RX_LEN_IN); if (!rx_in) @@ -910,13 +905,9 @@ static void qcom_geni_serial_handle_rx_dma(struct uart_port *uport, bool drop) else if (!drop) handle_rx_uart(uport, rx_in); - ret = geni_se_rx_dma_prep(&port->se, port->rx_buf, - DMA_RX_BUF_SIZE, - &port->rx_dma_addr); - if (ret) { - dev_err(uport->dev, "unable to start RX SE DMA: %d\n", ret); - qcom_geni_serial_stop_rx_dma(uport); - } + dma_sync_single_for_device(uport->dev->parent, port->rx_dma_addr, + DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); + geni_se_rx_init_dma(&port->se, port->rx_dma_addr, DMA_RX_BUF_SIZE); } static void qcom_geni_serial_start_rx(struct uart_port *uport) @@ -1864,6 +1855,14 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) ret = -ENOMEM; goto error; } + + port->rx_dma_addr = dma_map_single(pdev->dev.parent, port->rx_buf, + DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(pdev->dev.parent, port->rx_dma_addr)) { + ret = -EIO; + dev_err(&pdev->dev, "Failed to map RX DMA buffer: %d\n", ret); + goto error; + } } port->name = devm_kasprintf(uport->dev, GFP_KERNEL, @@ -1928,6 +1927,11 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) return 0; error: + if (port->rx_dma_addr) { + dma_unmap_single(pdev->dev.parent, port->rx_dma_addr, + DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); + port->rx_dma_addr = 0; + } dev_pm_domain_detach_list(port->pd_list); return ret; } @@ -1942,6 +1946,13 @@ static void qcom_geni_serial_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, false); ida_free(&port_ida, uport->line); uart_remove_one_port(drv, &port->uport); + + if (port->rx_dma_addr) { + dma_unmap_single(pdev->dev.parent, port->rx_dma_addr, + DMA_RX_BUF_SIZE, DMA_FROM_DEVICE); + port->rx_dma_addr = 0; + } + dev_pm_domain_detach_list(port->pd_list); }