From 7a84477c4acebf6299b6a8bd6a1d5894eb838ffa Mon Sep 17 00:00:00 2001 From: Will Newton Date: Fri, 30 Mar 2012 11:51:02 +0100 Subject: [PATCH 1/3] mtd: fix oops in dataflash driver I'm seeing an oops in mtd_dataflash.c with Linux 3.3. What appears to be happening is that otp_select_filemode calls mtd_read_fact_prot_reg with -1 for offset and length and a NULL buffer to test if OTP operations are supported. This finds its way down to otp_read in mtd_dataflash.c and causes an oops when memcpying the returned data into the NULL buf. None of the checks in otp_read catches the negative length and offset. Changing the length of the dummy read to 0 prevents the oops. Cc: stable@kernel.org [3.3+] Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/mtdchar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 58fc65f5c817..f2f482bec573 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -376,7 +376,7 @@ static int otp_select_filemode(struct mtd_file_info *mfi, int mode) * Make a fake call to mtd_read_fact_prot_reg() to check if OTP * operations are supported. */ - if (mtd_read_fact_prot_reg(mtd, -1, -1, &retlen, NULL) == -EOPNOTSUPP) + if (mtd_read_fact_prot_reg(mtd, -1, 0, &retlen, NULL) == -EOPNOTSUPP) return -EOPNOTSUPP; switch (mode) { From 226bb7df3d22bcf4a1c0fe8206c80cc427498eae Mon Sep 17 00:00:00 2001 From: Josh Cartwright Date: Thu, 29 Mar 2012 19:34:53 -0400 Subject: [PATCH 2/3] jffs2: Fix lock acquisition order bug in gc path The locking policy is such that the erase_complete_block spinlock is nested within the alloc_sem mutex. This fixes a case in which the acquisition order was erroneously reversed. This issue was caught by the following lockdep splat: ======================================================= [ INFO: possible circular locking dependency detected ] 3.0.5 #1 ------------------------------------------------------- jffs2_gcd_mtd6/299 is trying to acquire lock: (&c->alloc_sem){+.+.+.}, at: [] jffs2_garbage_collect_pass+0x314/0x890 but task is already holding lock: (&(&c->erase_completion_lock)->rlock){+.+...}, at: [] jffs2_garbage_collect_pass+0x308/0x890 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (&(&c->erase_completion_lock)->rlock){+.+...}: [] validate_chain+0xe6c/0x10bc [] __lock_acquire+0x54c/0xba4 [] lock_acquire+0xa4/0x114 [] _raw_spin_lock+0x3c/0x4c [] jffs2_garbage_collect_pass+0x4c/0x890 [] jffs2_garbage_collect_thread+0x1b4/0x1cc [] kthread+0x98/0xa0 [] kernel_thread_exit+0x0/0x8 -> #0 (&c->alloc_sem){+.+.+.}: [] print_circular_bug+0x70/0x2c4 [] validate_chain+0x1034/0x10bc [] __lock_acquire+0x54c/0xba4 [] lock_acquire+0xa4/0x114 [] mutex_lock_nested+0x74/0x33c [] jffs2_garbage_collect_pass+0x314/0x890 [] jffs2_garbage_collect_thread+0x1b4/0x1cc [] kthread+0x98/0xa0 [] kernel_thread_exit+0x0/0x8 other info that might help us debug this: Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&(&c->erase_completion_lock)->rlock); lock(&c->alloc_sem); lock(&(&c->erase_completion_lock)->rlock); lock(&c->alloc_sem); *** DEADLOCK *** 1 lock held by jffs2_gcd_mtd6/299: #0: (&(&c->erase_completion_lock)->rlock){+.+...}, at: [] jffs2_garbage_collect_pass+0x308/0x890 stack backtrace: [] (unwind_backtrace+0x0/0x100) from [] (dump_stack+0x20/0x24) [] (dump_stack+0x20/0x24) from [] (print_circular_bug+0x1c8/0x2c4) [] (print_circular_bug+0x1c8/0x2c4) from [] (validate_chain+0x1034/0x10bc) [] (validate_chain+0x1034/0x10bc) from [] (__lock_acquire+0x54c/0xba4) [] (__lock_acquire+0x54c/0xba4) from [] (lock_acquire+0xa4/0x114) [] (lock_acquire+0xa4/0x114) from [] (mutex_lock_nested+0x74/0x33c) [] (mutex_lock_nested+0x74/0x33c) from [] (jffs2_garbage_collect_pass+0x314/0x890) [] (jffs2_garbage_collect_pass+0x314/0x890) from [] (jffs2_garbage_collect_thread+0x1b4/0x1cc) [] (jffs2_garbage_collect_thread+0x1b4/0x1cc) from [] (kthread+0x98/0xa0) [] (kthread+0x98/0xa0) from [] (kernel_thread_exit+0x0/0x8) This was introduce in '81cfc9f jffs2: Fix serious write stall due to erase'. Cc: stable@kernel.org [2.6.37+] Signed-off-by: Josh Cartwright Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- fs/jffs2/gc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index ad271c70aa25..5a2dec2b064c 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -234,8 +234,8 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) return 0; jffs2_dbg(1, "No progress from erasing block; doing GC anyway\n"); - spin_lock(&c->erase_completion_lock); mutex_lock(&c->alloc_sem); + spin_lock(&c->erase_completion_lock); } /* First, work out which block we're garbage-collecting */ From b027274d2e3a332683b73f15e5cea79c240bc9a3 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Mon, 7 May 2012 22:51:37 +0200 Subject: [PATCH 3/3] mtd: ams-delta: fix request_mem_region() failure A call to request_mem_region() has been introduced in the omap-gpio driver recently (commit 96751fcbe5438e95514b025e9cee7a6d38038f40, "gpio/omap: Use devm_ API and add request_mem_region"). This change prevented the Amstrad Delta NAND driver, which was doing the same in order to take control over OMAP MPU I/O lines that the NAND device hangs off, from loading successfully. The I/O lines and corresponding registers used by the NAND driver are a subset of those used for the GPIO function. Then, to avoid run time collisions, all MPUIO GPIO lines should be marked as requested while initializing the NAND driver, and vice versa, a single MPUIO GPIO line already requested before the NAND driver initialization is attempted should prevent the NAND device from being started successfully. There is another driver, omap-keypad, which also manipulates MPUIO registers, but has never been calling request_mem_region() on startup, so it's not affected by the change in the gpio-omap and works correctly. It uses the depreciated omap_read/write functions for accessing MPUIO registers. Unlike the NAND driver, these I/O lines and registers are separate from those used by the GPIO driver. However, both register sets are non-contiguous and overlapping, so it would be impractical to request the two sets separately, one from the gpio-omap, the other form the omap-keypad driver. In order to solve all these issues correctly, a solution first suggested by Artem Bityutskiy, then closer specified by Tony Lindgren while they commented the initial version of this fix, should be implemented. The gpio-omap driver should export a few functions which would allow the other two drivers to access MPUIO registers in a safe manner instead of trying to manage them in parallel to the GPIO driver. However, such a big change, affecting 3 drivers all together, is not suitable for the rc cycle, and should be prepared for the merge window. Then, an alternative solution is proposed as a regression fix. For the ams-delta NAND driver to initialize correctly in coexistence with the changed GPIO driver, drop the request_mem_region() call from the former, especially as this call is going to be removed while the long-term solution is implemented. Tested on Amstrad Delta. Signed-off-by: Janusz Krzysztofik Acked-by: Tony Lindgren Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/ams-delta.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index 73416951f4c1..861ca8f7e47d 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -212,18 +212,17 @@ static int __devinit ams_delta_init(struct platform_device *pdev) /* Link the private data with the MTD structure */ ams_delta_mtd->priv = this; - if (!request_mem_region(res->start, resource_size(res), - dev_name(&pdev->dev))) { - dev_err(&pdev->dev, "request_mem_region failed\n"); - err = -EBUSY; - goto out_free; - } + /* + * Don't try to request the memory region from here, + * it should have been already requested from the + * gpio-omap driver and requesting it again would fail. + */ io_base = ioremap(res->start, resource_size(res)); if (io_base == NULL) { dev_err(&pdev->dev, "ioremap failed\n"); err = -EIO; - goto out_release_io; + goto out_free; } this->priv = io_base; @@ -271,8 +270,6 @@ static int __devinit ams_delta_init(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB); iounmap(io_base); -out_release_io: - release_mem_region(res->start, resource_size(res)); out_free: kfree(ams_delta_mtd); out: @@ -285,7 +282,6 @@ static int __devinit ams_delta_init(struct platform_device *pdev) static int __devexit ams_delta_cleanup(struct platform_device *pdev) { void __iomem *io_base = platform_get_drvdata(pdev); - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); /* Release resources, unregister device */ nand_release(ams_delta_mtd); @@ -293,7 +289,6 @@ static int __devexit ams_delta_cleanup(struct platform_device *pdev) gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB); iounmap(io_base); - release_mem_region(res->start, resource_size(res)); /* Free the MTD device structure */ kfree(ams_delta_mtd);