Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 30872ac7 authored by Lukas Wunner's avatar Lukas Wunner Committed by Greg Kroah-Hartman
Browse files

spi: Prevent adding devices below an unregistering controller



[ Upstream commit ddf75be47ca748f8b12d28ac64d624354fddf189 ]

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: ce79d54a ("spi/of: Add OF notifier handler")
Signed-off-by: default avatarLukas 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: default avatarMark Brown <broonie@kernel.org>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 1c263d0e
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -817,4 +817,7 @@ config SPI_SLAVE_SYSTEM_CONTROL


endif # SPI_SLAVE
endif # SPI_SLAVE


config SPI_DYNAMIC
	def_bool ACPI || OF_DYNAMIC || SPI_SLAVE

endif # SPI
endif # SPI
+20 −1
Original line number Original line Diff line number Diff line
@@ -432,6 +432,12 @@ static LIST_HEAD(spi_controller_list);
 */
 */
static DEFINE_MUTEX(board_lock);
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
 * spi_alloc_device - Allocate a new SPI device
 * @ctlr: Controller to which device is connected
 * @ctlr: Controller to which device is connected
@@ -510,7 +516,6 @@ static int spi_dev_check(struct device *dev, void *data)
 */
 */
int spi_add_device(struct spi_device *spi)
int spi_add_device(struct spi_device *spi)
{
{
	static DEFINE_MUTEX(spi_add_lock);
	struct spi_controller *ctlr = spi->controller;
	struct spi_controller *ctlr = spi->controller;
	struct device *dev = ctlr->dev.parent;
	struct device *dev = ctlr->dev.parent;
	int status;
	int status;
@@ -538,6 +543,13 @@ int spi_add_device(struct spi_device *spi)
		goto done;
		goto done;
	}
	}


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

	if (ctlr->cs_gpios)
	if (ctlr->cs_gpios)
		spi->cs_gpio = ctlr->cs_gpios[spi->chip_select];
		spi->cs_gpio = ctlr->cs_gpios[spi->chip_select];


@@ -2306,6 +2318,10 @@ void spi_unregister_controller(struct spi_controller *ctlr)
	struct spi_controller *found;
	struct spi_controller *found;
	int id = ctlr->bus_num;
	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);
	device_for_each_child(&ctlr->dev, NULL, __unregister);


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

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