* [PATCH v3 1/3] bus: mhi: ep: Add mhi_cntrl->flush_async() callback to flush the async read/write
2026-07-22 5:54 [PATCH v3 0/3] bus: mhi: ep: Implement flush_async() callback to flush async read/write Manivannan Sadhasivam via B4 Relay
@ 2026-07-22 5:54 ` Manivannan Sadhasivam via B4 Relay
2026-07-22 15:36 ` Frank Li
2026-07-22 5:54 ` [PATCH v3 2/3] bus: mhi: ep: Flush async transfers before notifying disconnect in mhi_ep_abort_transfer() Manivannan Sadhasivam via B4 Relay
` (2 subsequent siblings)
3 siblings, 1 reply; 8+ messages in thread
From: Manivannan Sadhasivam via B4 Relay @ 2026-07-22 5:54 UTC (permalink / raw)
To: Manivannan Sadhasivam, Frank Li, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas
Cc: linux-kernel, mhi, linux-arm-msm, linux-pci,
Manivannan Sadhasivam, stable+noautosel
From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
MHI EP stack makes use of the MHI controller drivers like MHI EPF to do
read/write to the host memory. And that driver is free to use mechanisms
like DMA to offload the read/write operations.
So if DMA is used for offload, then there is no guarantee that those DMA
operations would be completed by the time mhi_ep_remove() gets called. This
can lead to UAF (Use-After-Free) issues as the DMA callback can trigger
xfer_cb() even after mhi_ep_remove() has returned.
So to fix this issue, introduce the mhi_cntrl->flush_async() callback and
call it in mhi_ep_remove() to drain all the in-flight async transfers
before disconnecting the channels.
The completion handlers keep triggering xfer_cb() as long as it is set. So
flushing the transfers after notifying the client about the disconnect
(-ENOTCONN) would still let a success callback slip through afterwards and
lead to the same UAF. So disable the channels first to prevent new
transfers, then flush the in-flight transfers so that their completions are
delivered while xfer_cb() is still valid and only then notify the
disconnect and clear xfer_cb().
Cc: <stable+noautosel@kernel.org> # Needs dmaengine driver fix as well
Fixes: 2547beb00ddb ("bus: mhi: ep: Add support for async DMA read operation")
Fixes: ee08acb58fe4 ("bus: mhi: ep: Add support for async DMA write operation")
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
---
drivers/bus/mhi/ep/main.c | 18 +++++++++++++++++-
include/linux/mhi_ep.h | 2 ++
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
index 0277e1ab1198..b94c571f01d8 100644
--- a/drivers/bus/mhi/ep/main.c
+++ b/drivers/bus/mhi/ep/main.c
@@ -1612,6 +1612,7 @@ static void mhi_ep_remove(struct device *dev)
{
struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver);
+ struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl;
struct mhi_result result = {};
struct mhi_ep_chan *mhi_chan;
int dir;
@@ -1620,6 +1621,22 @@ static void mhi_ep_remove(struct device *dev)
if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
return;
+ /* Disable the channels to prevent new transfers */
+ for (dir = 0; dir < 2; dir++) {
+ mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
+
+ if (!mhi_chan)
+ continue;
+
+ mutex_lock(&mhi_chan->lock);
+ mhi_chan->state = MHI_CH_STATE_DISABLED;
+ mutex_unlock(&mhi_chan->lock);
+ }
+
+ /* Flush in-flight transfers before notifying disconnect */
+ if (mhi_cntrl->flush_async)
+ mhi_cntrl->flush_async(mhi_cntrl);
+
/* Disconnect the channels associated with the driver */
for (dir = 0; dir < 2; dir++) {
mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
@@ -1635,7 +1652,6 @@ static void mhi_ep_remove(struct device *dev)
mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
}
- mhi_chan->state = MHI_CH_STATE_DISABLED;
mhi_chan->xfer_cb = NULL;
mutex_unlock(&mhi_chan->lock);
}
diff --git a/include/linux/mhi_ep.h b/include/linux/mhi_ep.h
index 7b40fc8cbe77..f6383a57a872 100644
--- a/include/linux/mhi_ep.h
+++ b/include/linux/mhi_ep.h
@@ -107,6 +107,7 @@ struct mhi_ep_buf_info {
* @write_sync: CB function for writing to host memory synchronously
* @read_async: CB function for reading from host memory asynchronously
* @write_async: CB function for writing to host memory asynchronously
+ * @flush_async: CB function for flushing asynchronous read/writes
* @mhi_state: MHI Endpoint state
* @max_chan: Maximum channels supported by the endpoint controller
* @mru: MRU (Maximum Receive Unit) value of the endpoint controller
@@ -164,6 +165,7 @@ struct mhi_ep_cntrl {
int (*write_sync)(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_buf_info *buf_info);
int (*read_async)(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_buf_info *buf_info);
int (*write_async)(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_buf_info *buf_info);
+ void (*flush_async)(struct mhi_ep_cntrl *mhi_cntrl);
enum mhi_state mhi_state;
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH v3 1/3] bus: mhi: ep: Add mhi_cntrl->flush_async() callback to flush the async read/write
2026-07-22 5:54 ` [PATCH v3 1/3] bus: mhi: ep: Add mhi_cntrl->flush_async() callback to flush the " Manivannan Sadhasivam via B4 Relay
@ 2026-07-22 15:36 ` Frank Li
0 siblings, 0 replies; 8+ messages in thread
From: Frank Li @ 2026-07-22 15:36 UTC (permalink / raw)
To: manivannan.sadhasivam
Cc: Manivannan Sadhasivam, Frank Li, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, linux-kernel, mhi,
linux-arm-msm, linux-pci, stable+noautosel
On Wed, Jul 22, 2026 at 07:54:44AM +0200, Manivannan Sadhasivam via B4 Relay wrote:
> [You don't often get email from devnull+manivannan.sadhasivam.oss.qualcomm.com@kernel.org. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
>
> MHI EP stack makes use of the MHI controller drivers like MHI EPF to do
> read/write to the host memory. And that driver is free to use mechanisms
> like DMA to offload the read/write operations.
>
> So if DMA is used for offload, then there is no guarantee that those DMA
> operations would be completed by the time mhi_ep_remove() gets called. This
> can lead to UAF (Use-After-Free) issues as the DMA callback can trigger
> xfer_cb() even after mhi_ep_remove() has returned.
>
> So to fix this issue, introduce the mhi_cntrl->flush_async() callback and
> call it in mhi_ep_remove() to drain all the in-flight async transfers
> before disconnecting the channels.
>
> The completion handlers keep triggering xfer_cb() as long as it is set. So
> flushing the transfers after notifying the client about the disconnect
> (-ENOTCONN) would still let a success callback slip through afterwards and
> lead to the same UAF. So disable the channels first to prevent new
> transfers, then flush the in-flight transfers so that their completions are
> delivered while xfer_cb() is still valid and only then notify the
> disconnect and clear xfer_cb().
>
> Cc: <stable+noautosel@kernel.org> # Needs dmaengine driver fix as well
> Fixes: 2547beb00ddb ("bus: mhi: ep: Add support for async DMA read operation")
> Fixes: ee08acb58fe4 ("bus: mhi: ep: Add support for async DMA write operation")
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/bus/mhi/ep/main.c | 18 +++++++++++++++++-
> include/linux/mhi_ep.h | 2 ++
> 2 files changed, 19 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
> index 0277e1ab1198..b94c571f01d8 100644
> --- a/drivers/bus/mhi/ep/main.c
> +++ b/drivers/bus/mhi/ep/main.c
> @@ -1612,6 +1612,7 @@ static void mhi_ep_remove(struct device *dev)
> {
> struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
> struct mhi_ep_driver *mhi_drv = to_mhi_ep_driver(dev->driver);
> + struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl;
> struct mhi_result result = {};
> struct mhi_ep_chan *mhi_chan;
> int dir;
> @@ -1620,6 +1621,22 @@ static void mhi_ep_remove(struct device *dev)
> if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
> return;
>
> + /* Disable the channels to prevent new transfers */
> + for (dir = 0; dir < 2; dir++) {
> + mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
> +
> + if (!mhi_chan)
> + continue;
> +
> + mutex_lock(&mhi_chan->lock);
> + mhi_chan->state = MHI_CH_STATE_DISABLED;
> + mutex_unlock(&mhi_chan->lock);
> + }
> +
> + /* Flush in-flight transfers before notifying disconnect */
> + if (mhi_cntrl->flush_async)
> + mhi_cntrl->flush_async(mhi_cntrl);
> +
> /* Disconnect the channels associated with the driver */
> for (dir = 0; dir < 2; dir++) {
> mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;
> @@ -1635,7 +1652,6 @@ static void mhi_ep_remove(struct device *dev)
> mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
> }
>
> - mhi_chan->state = MHI_CH_STATE_DISABLED;
> mhi_chan->xfer_cb = NULL;
> mutex_unlock(&mhi_chan->lock);
> }
> diff --git a/include/linux/mhi_ep.h b/include/linux/mhi_ep.h
> index 7b40fc8cbe77..f6383a57a872 100644
> --- a/include/linux/mhi_ep.h
> +++ b/include/linux/mhi_ep.h
> @@ -107,6 +107,7 @@ struct mhi_ep_buf_info {
> * @write_sync: CB function for writing to host memory synchronously
> * @read_async: CB function for reading from host memory asynchronously
> * @write_async: CB function for writing to host memory asynchronously
> + * @flush_async: CB function for flushing asynchronous read/writes
> * @mhi_state: MHI Endpoint state
> * @max_chan: Maximum channels supported by the endpoint controller
> * @mru: MRU (Maximum Receive Unit) value of the endpoint controller
> @@ -164,6 +165,7 @@ struct mhi_ep_cntrl {
> int (*write_sync)(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_buf_info *buf_info);
> int (*read_async)(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_buf_info *buf_info);
> int (*write_async)(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_buf_info *buf_info);
> + void (*flush_async)(struct mhi_ep_cntrl *mhi_cntrl);
>
> enum mhi_state mhi_state;
>
>
> --
> 2.43.0
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 2/3] bus: mhi: ep: Flush async transfers before notifying disconnect in mhi_ep_abort_transfer()
2026-07-22 5:54 [PATCH v3 0/3] bus: mhi: ep: Implement flush_async() callback to flush async read/write Manivannan Sadhasivam via B4 Relay
2026-07-22 5:54 ` [PATCH v3 1/3] bus: mhi: ep: Add mhi_cntrl->flush_async() callback to flush the " Manivannan Sadhasivam via B4 Relay
@ 2026-07-22 5:54 ` Manivannan Sadhasivam via B4 Relay
2026-07-22 15:38 ` Frank Li
2026-07-22 5:54 ` [PATCH v3 3/3] PCI: epf-mhi: Implement mhi_cntrl->flush_async() to flush DMA read/write Manivannan Sadhasivam via B4 Relay
2026-07-29 4:58 ` [PATCH v3 0/3] bus: mhi: ep: Implement flush_async() callback to flush async read/write Manivannan Sadhasivam
3 siblings, 1 reply; 8+ messages in thread
From: Manivannan Sadhasivam via B4 Relay @ 2026-07-22 5:54 UTC (permalink / raw)
To: Manivannan Sadhasivam, Frank Li, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas
Cc: linux-kernel, mhi, linux-arm-msm, linux-pci, Manivannan Sadhasivam
From: Manivannan Sadhasivam <mani@kernel.org>
mhi_ep_abort_transfer() notifies the client drivers about the channel
disconnect using -ENOTCONN and only then flushes the ring workqueue to
drain the in-flight transfers. But the async DMA transfers issued by the
ring workers can still complete after the notification. And the completion
handlers trigger the client xfer_cb() as long as it is set.
So a transfer completing during the flush can deliver a success callback to
the client even after it has been notified about the disconnect. This can
lead to UAF (Use-After-Free) issues as the client can free its per-transfer
resources in response to the -ENOTCONN notification and the trailing
success callback would then reference the freed resources.
So to fix this issue, disable all the channels first to prevent new
transfers and then drain both the ring workqueue and the in-flight async
transfers before notifying the disconnect. The completion and queue paths
bail out once the channel state is not MHI_CH_STATE_RUNNING, so disabling
the channels upfront makes sure that no new transfer sneaks in during the
drain and all the pending completions are delivered while xfer_cb() is
still valid.
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
---
drivers/bus/mhi/ep/main.c | 23 +++++++++++++++++------
1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
index b94c571f01d8..51735f87017f 100644
--- a/drivers/bus/mhi/ep/main.c
+++ b/drivers/bus/mhi/ep/main.c
@@ -1025,26 +1025,37 @@ static void mhi_ep_abort_transfer(struct mhi_ep_cntrl *mhi_cntrl)
struct mhi_ep_chan *mhi_chan;
int i;
- /* Stop all the channels */
+ /* Disable all the channels to prevent new transfers */
+ for (i = 0; i < mhi_cntrl->max_chan; i++) {
+ mhi_chan = &mhi_cntrl->mhi_chan[i];
+ if (!mhi_chan->ring.started)
+ continue;
+
+ mutex_lock(&mhi_chan->lock);
+ mhi_chan->state = MHI_CH_STATE_DISABLED;
+ mutex_unlock(&mhi_chan->lock);
+ }
+
+ /* Drain ring workers and in-flight transfers before notifying disconnect */
+ flush_workqueue(mhi_cntrl->wq);
+ if (mhi_cntrl->flush_async)
+ mhi_cntrl->flush_async(mhi_cntrl);
+
+ /* Send channel disconnect status to client drivers */
for (i = 0; i < mhi_cntrl->max_chan; i++) {
mhi_chan = &mhi_cntrl->mhi_chan[i];
if (!mhi_chan->ring.started)
continue;
mutex_lock(&mhi_chan->lock);
- /* Send channel disconnect status to client drivers */
if (mhi_chan->xfer_cb) {
result.transaction_status = -ENOTCONN;
result.bytes_xferd = 0;
mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
}
-
- mhi_chan->state = MHI_CH_STATE_DISABLED;
mutex_unlock(&mhi_chan->lock);
}
- flush_workqueue(mhi_cntrl->wq);
-
/* Destroy devices associated with all channels */
device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_ep_destroy_device);
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH v3 2/3] bus: mhi: ep: Flush async transfers before notifying disconnect in mhi_ep_abort_transfer()
2026-07-22 5:54 ` [PATCH v3 2/3] bus: mhi: ep: Flush async transfers before notifying disconnect in mhi_ep_abort_transfer() Manivannan Sadhasivam via B4 Relay
@ 2026-07-22 15:38 ` Frank Li
0 siblings, 0 replies; 8+ messages in thread
From: Frank Li @ 2026-07-22 15:38 UTC (permalink / raw)
To: manivannan.sadhasivam
Cc: Manivannan Sadhasivam, Frank Li, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, linux-kernel, mhi,
linux-arm-msm, linux-pci
On Wed, Jul 22, 2026 at 07:54:45AM +0200, Manivannan Sadhasivam via B4 Relay wrote:
> From: Manivannan Sadhasivam <mani@kernel.org>
>
> mhi_ep_abort_transfer() notifies the client drivers about the channel
> disconnect using -ENOTCONN and only then flushes the ring workqueue to
> drain the in-flight transfers. But the async DMA transfers issued by the
> ring workers can still complete after the notification. And the completion
> handlers trigger the client xfer_cb() as long as it is set.
>
> So a transfer completing during the flush can deliver a success callback to
> the client even after it has been notified about the disconnect. This can
> lead to UAF (Use-After-Free) issues as the client can free its per-transfer
> resources in response to the -ENOTCONN notification and the trailing
> success callback would then reference the freed resources.
>
> So to fix this issue, disable all the channels first to prevent new
> transfers and then drain both the ring workqueue and the in-flight async
> transfers before notifying the disconnect. The completion and queue paths
> bail out once the channel state is not MHI_CH_STATE_RUNNING, so disabling
> the channels upfront makes sure that no new transfer sneaks in during the
> drain and all the pending completions are delivered while xfer_cb() is
> still valid.
>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/bus/mhi/ep/main.c | 23 +++++++++++++++++------
> 1 file changed, 17 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
> index b94c571f01d8..51735f87017f 100644
> --- a/drivers/bus/mhi/ep/main.c
> +++ b/drivers/bus/mhi/ep/main.c
> @@ -1025,26 +1025,37 @@ static void mhi_ep_abort_transfer(struct mhi_ep_cntrl *mhi_cntrl)
> struct mhi_ep_chan *mhi_chan;
> int i;
>
> - /* Stop all the channels */
> + /* Disable all the channels to prevent new transfers */
> + for (i = 0; i < mhi_cntrl->max_chan; i++) {
> + mhi_chan = &mhi_cntrl->mhi_chan[i];
> + if (!mhi_chan->ring.started)
> + continue;
> +
> + mutex_lock(&mhi_chan->lock);
> + mhi_chan->state = MHI_CH_STATE_DISABLED;
> + mutex_unlock(&mhi_chan->lock);
> + }
> +
> + /* Drain ring workers and in-flight transfers before notifying disconnect */
> + flush_workqueue(mhi_cntrl->wq);
> + if (mhi_cntrl->flush_async)
> + mhi_cntrl->flush_async(mhi_cntrl);
> +
> + /* Send channel disconnect status to client drivers */
> for (i = 0; i < mhi_cntrl->max_chan; i++) {
> mhi_chan = &mhi_cntrl->mhi_chan[i];
> if (!mhi_chan->ring.started)
> continue;
>
> mutex_lock(&mhi_chan->lock);
> - /* Send channel disconnect status to client drivers */
> if (mhi_chan->xfer_cb) {
> result.transaction_status = -ENOTCONN;
> result.bytes_xferd = 0;
> mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
> }
> -
> - mhi_chan->state = MHI_CH_STATE_DISABLED;
> mutex_unlock(&mhi_chan->lock);
> }
>
> - flush_workqueue(mhi_cntrl->wq);
> -
> /* Destroy devices associated with all channels */
> device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_ep_destroy_device);
>
>
> --
> 2.43.0
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 3/3] PCI: epf-mhi: Implement mhi_cntrl->flush_async() to flush DMA read/write
2026-07-22 5:54 [PATCH v3 0/3] bus: mhi: ep: Implement flush_async() callback to flush async read/write Manivannan Sadhasivam via B4 Relay
2026-07-22 5:54 ` [PATCH v3 1/3] bus: mhi: ep: Add mhi_cntrl->flush_async() callback to flush the " Manivannan Sadhasivam via B4 Relay
2026-07-22 5:54 ` [PATCH v3 2/3] bus: mhi: ep: Flush async transfers before notifying disconnect in mhi_ep_abort_transfer() Manivannan Sadhasivam via B4 Relay
@ 2026-07-22 5:54 ` Manivannan Sadhasivam via B4 Relay
2026-07-22 15:39 ` Frank Li
2026-07-29 4:58 ` [PATCH v3 0/3] bus: mhi: ep: Implement flush_async() callback to flush async read/write Manivannan Sadhasivam
3 siblings, 1 reply; 8+ messages in thread
From: Manivannan Sadhasivam via B4 Relay @ 2026-07-22 5:54 UTC (permalink / raw)
To: Manivannan Sadhasivam, Frank Li, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas
Cc: linux-kernel, mhi, linux-arm-msm, linux-pci, Manivannan Sadhasivam
From: Manivannan Sadhasivam <mani@kernel.org>
The MHI core needs to make sure that all the current DMA transactions are
completed before removing the channels. So implement the
mhi_cntrl->flush_async() callback by first making sure all the in-flight
DMA operations are completed and then flushing the DMA workqueue.
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
---
drivers/pci/endpoint/functions/pci-epf-mhi.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c
index 7f5326925ed5..8d2d9d01cfd2 100644
--- a/drivers/pci/endpoint/functions/pci-epf-mhi.c
+++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c
@@ -644,6 +644,15 @@ static int pci_epf_mhi_edma_write_async(struct mhi_ep_cntrl *mhi_cntrl,
return ret;
}
+static void pci_epf_mhi_edma_flush_async(struct mhi_ep_cntrl *mhi_cntrl)
+{
+ struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl);
+
+ dmaengine_synchronize(epf_mhi->dma_chan_rx);
+ dmaengine_synchronize(epf_mhi->dma_chan_tx);
+ flush_workqueue(epf_mhi->dma_wq);
+}
+
struct epf_dma_filter {
struct device *dev;
u32 dma_mask;
@@ -812,6 +821,7 @@ static int pci_epf_mhi_link_up(struct pci_epf *epf)
mhi_cntrl->write_sync = pci_epf_mhi_edma_write;
mhi_cntrl->read_async = pci_epf_mhi_edma_read_async;
mhi_cntrl->write_async = pci_epf_mhi_edma_write_async;
+ mhi_cntrl->flush_async = pci_epf_mhi_edma_flush_async;
}
/* Register the MHI EP controller */
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH v3 3/3] PCI: epf-mhi: Implement mhi_cntrl->flush_async() to flush DMA read/write
2026-07-22 5:54 ` [PATCH v3 3/3] PCI: epf-mhi: Implement mhi_cntrl->flush_async() to flush DMA read/write Manivannan Sadhasivam via B4 Relay
@ 2026-07-22 15:39 ` Frank Li
0 siblings, 0 replies; 8+ messages in thread
From: Frank Li @ 2026-07-22 15:39 UTC (permalink / raw)
To: manivannan.sadhasivam
Cc: Manivannan Sadhasivam, Frank Li, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, linux-kernel, mhi,
linux-arm-msm, linux-pci
On Wed, Jul 22, 2026 at 07:54:46AM +0200, Manivannan Sadhasivam via B4 Relay wrote:
>
> From: Manivannan Sadhasivam <mani@kernel.org>
>
> The MHI core needs to make sure that all the current DMA transactions are
> completed before removing the channels. So implement the
> mhi_cntrl->flush_async() callback by first making sure all the in-flight
> DMA operations are completed and then flushing the DMA workqueue.
>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/pci/endpoint/functions/pci-epf-mhi.c | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
> diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c
> index 7f5326925ed5..8d2d9d01cfd2 100644
> --- a/drivers/pci/endpoint/functions/pci-epf-mhi.c
> +++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c
> @@ -644,6 +644,15 @@ static int pci_epf_mhi_edma_write_async(struct mhi_ep_cntrl *mhi_cntrl,
> return ret;
> }
>
> +static void pci_epf_mhi_edma_flush_async(struct mhi_ep_cntrl *mhi_cntrl)
> +{
> + struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl);
> +
> + dmaengine_synchronize(epf_mhi->dma_chan_rx);
> + dmaengine_synchronize(epf_mhi->dma_chan_tx);
> + flush_workqueue(epf_mhi->dma_wq);
> +}
> +
> struct epf_dma_filter {
> struct device *dev;
> u32 dma_mask;
> @@ -812,6 +821,7 @@ static int pci_epf_mhi_link_up(struct pci_epf *epf)
> mhi_cntrl->write_sync = pci_epf_mhi_edma_write;
> mhi_cntrl->read_async = pci_epf_mhi_edma_read_async;
> mhi_cntrl->write_async = pci_epf_mhi_edma_write_async;
> + mhi_cntrl->flush_async = pci_epf_mhi_edma_flush_async;
> }
>
> /* Register the MHI EP controller */
>
> --
> 2.43.0
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 0/3] bus: mhi: ep: Implement flush_async() callback to flush async read/write
2026-07-22 5:54 [PATCH v3 0/3] bus: mhi: ep: Implement flush_async() callback to flush async read/write Manivannan Sadhasivam via B4 Relay
` (2 preceding siblings ...)
2026-07-22 5:54 ` [PATCH v3 3/3] PCI: epf-mhi: Implement mhi_cntrl->flush_async() to flush DMA read/write Manivannan Sadhasivam via B4 Relay
@ 2026-07-29 4:58 ` Manivannan Sadhasivam
3 siblings, 0 replies; 8+ messages in thread
From: Manivannan Sadhasivam @ 2026-07-29 4:58 UTC (permalink / raw)
To: Manivannan Sadhasivam, Frank Li, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Manivannan Sadhasivam
Cc: linux-kernel, mhi, linux-arm-msm, linux-pci, stable+noautosel
On Wed, 22 Jul 2026 07:54:43 +0200, Manivannan Sadhasivam wrote:
> This series introduces a new mhi_cntrl->flush_async() callback to flush the
> async read/write operations performed by the MHI controller using offload
> mechanisms such as DMA.
>
> The MHI EPF driver implements this callback by flushing the DMA wq. With this
> series, the MHI EP stack can guarnatee that the channel specific xfer_cb() won't
> be run after calling mhi_ep_remove() and mhi_ep_abort_transfer().
>
> [...]
Applied, thanks!
[1/3] bus: mhi: ep: Add mhi_cntrl->flush_async() callback to flush the async read/write
commit: d5a2ccf92c8ab3bcfadbae2f46349bf164c71eba
[2/3] bus: mhi: ep: Flush async transfers before notifying disconnect in mhi_ep_abort_transfer()
commit: 9daf648da0049c99fd4f2dd06b7dc7d4a43c6d8e
[3/3] PCI: epf-mhi: Implement mhi_cntrl->flush_async() to flush DMA read/write
commit: f502c0316c52b1f71fe544f99676805d90c554ba
Best regards,
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply [flat|nested] 8+ messages in thread