[<prev] [next>] [day] [month] [year] [list]
Message-Id: <dea0cf48c455378163c122266e99b6e844bac436.1751255054.git.khairul.anuar.romli@altera.com>
Date: Mon, 30 Jun 2025 12:01:30 +0800
From: khairul.anuar.romli@...era.com
To: Mark Brown <broonie@...nel.org>,
linux-spi@...r.kernel.org (open list:SPI SUBSYSTEM),
linux-kernel@...r.kernel.org (open list),
Matthew Gerlach <matthew.gerlach@...era.com>,
Khairul Anuar Romli <khairul.anuar.romli@...era.com>
Subject: [PATCH v2 1/1] spi: spi-cadence-quadspi: Implement refcount to handle unbind during busy
From: Khairul Anuar Romli <khairul.anuar.romli@...era.com>
Driver supports indirect read and write operations assuming no forced
device removal(unbind) operation. However, device removal(unbind) can
be forced by root user.
Driver removal(unbind) during an indirect read or write operation
causes a kernel crash. Ensure removal(unbind) is gracefully handled by
implementing a refcount and waiting until the attached devices remove
operations have completed before proceeding with removal operation.
Signed-off-by: Khairul Anuar Romli <khairul.anuar.romli@...era.com>
Reviewed-by: Matthew Gerlach <matthew.gerlach@...era.com>
---
changes in v2:
- refactor patch to align with latest mainline kernel.
drivers/spi/spi-cadence-quadspi.c | 33 +++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index aa1932ba17cb..aeccb33e621d 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -108,6 +108,8 @@ struct cqspi_st {
bool is_jh7110; /* Flag for StarFive JH7110 SoC */
bool disable_stig_mode;
+ refcount_t refcount;
+ refcount_t inflight_ops;
const struct cqspi_driver_platdata *ddata;
};
@@ -735,6 +737,9 @@ static int cqspi_indirect_read_execute(struct cqspi_flash_pdata *f_pdata,
u8 *rxbuf_end = rxbuf + n_rx;
int ret = 0;
+ if (!refcount_read(&cqspi->refcount))
+ return -ENODEV;
+
writel(from_addr, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR);
writel(remaining, reg_base + CQSPI_REG_INDIRECTRDBYTES);
@@ -1071,6 +1076,9 @@ static int cqspi_indirect_write_execute(struct cqspi_flash_pdata *f_pdata,
unsigned int write_bytes;
int ret;
+ if (!refcount_read(&cqspi->refcount))
+ return -ENODEV;
+
writel(to_addr, reg_base + CQSPI_REG_INDIRECTWRSTARTADDR);
writel(remaining, reg_base + CQSPI_REG_INDIRECTWRBYTES);
@@ -1461,12 +1469,26 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
struct cqspi_st *cqspi = spi_controller_get_devdata(mem->spi->controller);
struct device *dev = &cqspi->pdev->dev;
+ if (refcount_read(&cqspi->inflight_ops) == 0)
+ return -ENODEV;
+
ret = pm_runtime_resume_and_get(dev);
if (ret) {
dev_err(&mem->spi->dev, "resume failed with %d\n", ret);
return ret;
}
+ if (!refcount_read(&cqspi->refcount))
+ return -EBUSY;
+
+ refcount_inc(&cqspi->inflight_ops);
+
+ if (!refcount_read(&cqspi->refcount)) {
+ if (refcount_read(&cqspi->inflight_ops))
+ refcount_dec(&cqspi->inflight_ops);
+ return -EBUSY;
+ }
+
ret = cqspi_mem_process(mem, op);
pm_runtime_mark_last_busy(dev);
@@ -1475,6 +1497,9 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
if (ret)
dev_err(&mem->spi->dev, "operation failed with %d\n", ret);
+ if (refcount_read(&cqspi->inflight_ops) > 1)
+ refcount_dec(&cqspi->inflight_ops);
+
return ret;
}
@@ -1926,6 +1951,9 @@ static int cqspi_probe(struct platform_device *pdev)
}
}
+ refcount_set(&cqspi->refcount, 1);
+ refcount_set(&cqspi->inflight_ops, 1);
+
ret = devm_request_irq(dev, irq, cqspi_irq_handler, 0,
pdev->name, cqspi);
if (ret) {
@@ -1994,6 +2022,11 @@ static void cqspi_remove(struct platform_device *pdev)
{
struct cqspi_st *cqspi = platform_get_drvdata(pdev);
+ refcount_set(&cqspi->refcount, 0);
+
+ if (!refcount_dec_and_test(&cqspi->inflight_ops))
+ cqspi_wait_idle(cqspi);
+
spi_unregister_controller(cqspi->host);
cqspi_controller_enable(cqspi, 0);
--
2.35.3
Powered by blists - more mailing lists