[PATCH] mailbox: pcc: Synchronize channel IRQ before unmapping shared memory

Christian Loehle <[email protected]>
Newsgroups org.kernel.vger.linux-acpi,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <[email protected]>
pcc_mbox_free_channel() unmaps the PCC shared-memory region before
mbox_free_channel() invokes the controller shutdown callback. For
interrupt-capable extended subspaces, an in-flight handler may
consequently access the mapping after it has been invalidated.

Release the mailbox channel first so its IRQ is disabled and synchronized
before unmapping the shared-memory region. Serialize PCC channel
acquisition and release across this sequence: once mbox_free_channel()
makes the channel available, another client must not replace the
shared-memory mapping until the old one has been unmapped.

Fixes: 7f9e19f207be ("mailbox: pcc: Check before sending MCTP PCC response ACK")
Cc: [email protected]
Signed-off-by: Christian Loehle <[email protected]>
---
 drivers/mailbox/pcc.c | 26 +++++++++++++++++++++-----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c
index 9888dab64639..4654b028df9f 100644
--- a/drivers/mailbox/pcc.c
+++ b/drivers/mailbox/pcc.c
@@ -47,12 +47,14 @@
  */
 
 #include <linux/acpi.h>
+#include <linux/cleanup.h>
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/list.h>
 #include <linux/log2.h>
+#include <linux/mutex.h>
 #include <linux/platform_device.h>
 #include <linux/mailbox_controller.h>
 #include <linux/mailbox_client.h>
@@ -113,6 +115,8 @@ struct pcc_chan_info {
 #define to_pcc_chan_info(c) container_of(c, struct pcc_chan_info, chan)
 static struct pcc_chan_info *chan_info;
 static int pcc_chan_count;
+/* Serializes PCC channel ownership changes with shared-memory teardown. */
+static DEFINE_MUTEX(pcc_mbox_lock);
 
 /*
  * PCC can be used with perf critical drivers such as CPPC
@@ -392,18 +396,23 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
 	if (subspace_id < 0 || subspace_id >= pcc_chan_count)
 		return ERR_PTR(-ENOENT);
 
+	mutex_lock(&pcc_mbox_lock);
+
 	pchan = chan_info + subspace_id;
 	chan = pchan->chan.mchan;
 	if (IS_ERR(chan) || chan->cl) {
 		pr_err("Channel not found for idx: %d\n", subspace_id);
-		return ERR_PTR(-EBUSY);
+		rc = -EBUSY;
+		goto err_unlock;
 	}
 
 	pcc_mchan = &pchan->chan;
 	pcc_mchan->shmem = acpi_os_ioremap(pcc_mchan->shmem_base_addr,
 					   pcc_mchan->shmem_size);
-	if (!pcc_mchan->shmem)
-		return ERR_PTR(-ENXIO);
+	if (!pcc_mchan->shmem) {
+		rc = -ENXIO;
+		goto err_unlock;
+	}
 
 	rc = pcc_mbox_validate_signature(pcc_mchan, subspace_id);
 	if (rc)
@@ -413,11 +422,14 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
 	if (rc)
 		goto err_unmap_shmem;
 
+	mutex_unlock(&pcc_mbox_lock);
 	return pcc_mchan;
 
 err_unmap_shmem:
 	iounmap(pcc_mchan->shmem);
 	pcc_mchan->shmem = NULL;
+err_unlock:
+	mutex_unlock(&pcc_mbox_lock);
 	return ERR_PTR(rc);
 }
 EXPORT_SYMBOL_GPL(pcc_mbox_request_channel);
@@ -436,14 +448,18 @@ void pcc_mbox_free_channel(struct pcc_mbox_chan *pchan)
 
 	if (!chan || !chan->cl)
 		return;
+	guard(mutex)(&pcc_mbox_lock);
+
 	pchan_info = chan->con_priv;
 	pcc_mbox_chan = &pchan_info->chan;
+
+	/* Disable and synchronize the channel IRQ before unmapping its data. */
+	mbox_free_channel(chan);
+
 	if (pcc_mbox_chan->shmem) {
 		iounmap(pcc_mbox_chan->shmem);
 		pcc_mbox_chan->shmem = NULL;
 	}
-
-	mbox_free_channel(chan);
 }
 EXPORT_SYMBOL_GPL(pcc_mbox_free_channel);
 
-- 
2.34.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.