[PATCH 18/58] mtd: rawnand: atmel: retain chips across controller removal
sashiko-bot at kernel.org
sashiko-bot at kernel.org
Sat Nov 7 12:31:28 PST 2026
Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Low] mtd: rawnand: atmel: initialize __free(kfree) variables at
declaration
It also noticed 1 related pre-existing issue this patch does not
introduce:
- [High] mtd: rawnand: atmel: unclocked register access in
atmel_nfc_interrupt() [1]
--
commit b77082ebe154b5b59053fa6c8fd897f19a5685b6
Author: James Hilliard <james.hilliard1 at gmail.com>
mtd: rawnand: atmel: retain chips across controller removal
This commit retains NAND chip and controller allocations until all MTD
references are released and disconnects chips before tearing down controller
resources.
> diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c
> index 0bdbcbb50d94c..10b725fa61d86 100644
> --- a/drivers/mtd/nand/raw/atmel/nand-controller.c
> +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c
[ ... ]
> @@ -1638,7 +1640,7 @@ static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc,
> struct device_node *np,
> int reg_cells)
> {
> - struct atmel_nand *nand;
> + struct atmel_nand *nand __free(kfree) = NULL;
> struct gpio_desc *gpio;
> int numcs, ret, i;
>
> @@ -1649,7 +1651,7 @@ static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc,
> return ERR_PTR(-EINVAL);
> }
>
> - nand = devm_kzalloc(nc->dev, struct_size(nand, cs, numcs), GFP_KERNEL);
> + nand = kzalloc(struct_size(nand, cs, numcs), GFP_KERNEL);
> if (!nand)
> return ERR_PTR(-ENOMEM);
[ ... ]
> @@ -1791,7 +1809,7 @@ atmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc)
> {
> struct device *dev = nc->dev;
> struct platform_device *pdev = to_platform_device(dev);
> - struct atmel_nand *nand;
> + struct atmel_nand *nand __free(kfree) = NULL;
> struct gpio_desc *gpio;
> struct resource *res;
>
> @@ -1799,7 +1817,7 @@ atmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc)
> * Legacy bindings only allow connecting a single NAND with a unique CS
> * line to the controller.
> */
> - nand = devm_kzalloc(nc->dev, struct_size(nand, cs, 1), GFP_KERNEL);
> + nand = kzalloc(struct_size(nand, cs, 1), GFP_KERNEL);
> if (!nand)
> return -ENOMEM;
[Severity: Low]
This isn't a bug, but per the Cleanup API conventions in
include/linux/cleanup.h, __free() variables should be defined and
initialized in a single statement at their allocation point rather than
declared as "= NULL" at the top of atmel_nand_create() and
atmel_nand_controller_legacy_add_nands() and assigned later.
[ ... ]
> @@ -2349,6 +2366,11 @@ atmel_hsmc_nand_controller_remove(struct atmel_nand_controller *nc)
> return 0;
> }
>
> +static void atmel_hsmc_nand_controller_release(struct nand_controller *controller)
> +{
> + kfree(to_hsmc_nand_controller(controller));
> +}
> +
> static int atmel_hsmc_nand_controller_probe(struct platform_device *pdev,
> const struct atmel_nand_controller_caps *caps)
> {
> @@ -2356,9 +2378,13 @@ static int atmel_hsmc_nand_controller_probe(struct platform_device *pdev,
> struct atmel_hsmc_nand_controller *nc;
> int ret;
>
> - nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL);
> + nc = kzalloc_obj(*nc);
> if (!nc)
> return -ENOMEM;
> + ret = devm_nand_controller_init(dev, &nc->base.base,
> + atmel_hsmc_nand_controller_release);
> + if (ret)
> + return ret;
[Severity: High]
This problem wasn't introduced by this patch, but can atmel_nfc_interrupt()
perform an unclocked register read during teardown [1]?
atmel_hsmc_nand_controller_probe() registers atmel_nfc_interrupt() with
devm_request_irq(..., IRQF_SHARED, ...), while
atmel_hsmc_nand_controller_remove() manually disables hsmc_nc->clk before
devres releases the IRQ:
drivers/mtd/nand/raw/atmel/nand-controller.c:atmel_hsmc_nand_controller_remove() {
...
if (hsmc_nc->clk) {
clk_disable_unprepare(hsmc_nc->clk);
clk_put(hsmc_nc->clk);
}
...
}
CPU0 CPU1
atmel_hsmc_nand_controller_remove()
clk_disable_unprepare(hsmc_nc->clk)
<shared IRQ fires>
atmel_nfc_interrupt()
regmap_read(..., ATMEL_HSMC_NFC_SR, ...)
<-- unclocked register access
<devres cleanup>
devm_irq_release()
If another device on the shared IRQ line raises an interrupt before devres
frees the handler, reading ATMEL_HSMC_NFC_SR with the clock disabled can
trigger a synchronous external abort.
[1] https://sashiko.dev/#/bug/linux-94d27cd6-f6fe-452a-863e-36cbb87e9adc
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261107-submit-rawnand-retained-handle-lifetime-v1-v1-0-0181ef0cc4c8@gmail.com?part=18
More information about the linux-mtd
mailing list