Skip to content

Commit

Permalink
spi: Prevent adding devices below an unregistering controller
Browse files Browse the repository at this point in the history
commit ddf75be upstream.

CONFIG_OF_DYNAMIC and CONFIG_ACPI allow adding SPI devices at runtime
using a DeviceTree overlay or DSDT patch.  CONFIG_SPI_SLAVE allows the
same via sysfs.

But there are no precautions to prevent adding a device below a
controller that's being removed.  Such a device is unusable and may not
even be able to unbind cleanly as it becomes inaccessible once the
controller has been torn down.  E.g. it is then impossible to quiesce
the device's interrupt.

of_spi_notify() and acpi_spi_notify() do hold a ref on the controller,
but otherwise run lockless against spi_unregister_controller().

Fix by holding the spi_add_lock in spi_unregister_controller() and
bailing out of spi_add_device() if the controller has been unregistered
concurrently.

Fixes: ce79d54 ("spi/of: Add OF notifier handler")
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Cc: stable@vger.kernel.org # v3.19+
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Octavian Purdila <octavian.purdila@intel.com>
Cc: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
Link: https://lore.kernel.org/r/a8c3205088a969dc8410eec1eba9aface60f36af.1596451035.git.lukas@wunner.de
Signed-off-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
l1k authored and gregkh committed Aug 26, 2020
1 parent 143df6b commit 3e538c5
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 1 deletion.
3 changes: 3 additions & 0 deletions drivers/spi/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -944,4 +944,7 @@ config SPI_SLAVE_SYSTEM_CONTROL

endif # SPI_SLAVE

config SPI_DYNAMIC
def_bool ACPI || OF_DYNAMIC || SPI_SLAVE

endif # SPI
21 changes: 20 additions & 1 deletion drivers/spi/spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,12 @@ static LIST_HEAD(spi_controller_list);
*/
static DEFINE_MUTEX(board_lock);

/*
* Prevents addition of devices with same chip select and
* addition of devices below an unregistering controller.
*/
static DEFINE_MUTEX(spi_add_lock);

/**
* spi_alloc_device - Allocate a new SPI device
* @ctlr: Controller to which device is connected
Expand Down Expand Up @@ -553,7 +559,6 @@ static int spi_dev_check(struct device *dev, void *data)
*/
int spi_add_device(struct spi_device *spi)
{
static DEFINE_MUTEX(spi_add_lock);
struct spi_controller *ctlr = spi->controller;
struct device *dev = ctlr->dev.parent;
int status;
Expand Down Expand Up @@ -581,6 +586,13 @@ int spi_add_device(struct spi_device *spi)
goto done;
}

/* Controller may unregister concurrently */
if (IS_ENABLED(CONFIG_SPI_DYNAMIC) &&
!device_is_registered(&ctlr->dev)) {
status = -ENODEV;
goto done;
}

/* Descriptors take precedence */
if (ctlr->cs_gpiods)
spi->cs_gpiod = ctlr->cs_gpiods[spi->chip_select];
Expand Down Expand Up @@ -2582,6 +2594,10 @@ void spi_unregister_controller(struct spi_controller *ctlr)
struct spi_controller *found;
int id = ctlr->bus_num;

/* Prevent addition of new devices, unregister existing ones */
if (IS_ENABLED(CONFIG_SPI_DYNAMIC))
mutex_lock(&spi_add_lock);

device_for_each_child(&ctlr->dev, NULL, __unregister);

/* First make sure that this controller was ever added */
Expand All @@ -2602,6 +2618,9 @@ void spi_unregister_controller(struct spi_controller *ctlr)
if (found == ctlr)
idr_remove(&spi_master_idr, id);
mutex_unlock(&board_lock);

if (IS_ENABLED(CONFIG_SPI_DYNAMIC))
mutex_unlock(&spi_add_lock);
}
EXPORT_SYMBOL_GPL(spi_unregister_controller);

Expand Down

0 comments on commit 3e538c5

Please sign in to comment.