| From foo@baz Mon Dec 14 04:45:35 PM CET 2020 |
| From: Lukas Wunner <lukas@wunner.de> |
| Date: Mon, 3 Aug 2020 13:09:01 +0200 |
| Subject: spi: Prevent adding devices below an unregistering controller |
| |
| From: Lukas Wunner <lukas@wunner.de> |
| |
| commit ddf75be47ca748f8b12d28ac64d624354fddf189 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: ce79d54ae447 ("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> |
| [sudip: adjust context] |
| Signed-off-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/spi/Kconfig | 3 +++ |
| drivers/spi/spi.c | 21 ++++++++++++++++++++- |
| 2 files changed, 23 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/spi/Kconfig |
| +++ b/drivers/spi/Kconfig |
| @@ -763,4 +763,7 @@ endif # SPI_MASTER |
| |
| # (slave support would go here) |
| |
| +config SPI_DYNAMIC |
| + def_bool ACPI || OF_DYNAMIC || SPI_SLAVE |
| + |
| endif # SPI |
| --- a/drivers/spi/spi.c |
| +++ b/drivers/spi/spi.c |
| @@ -422,6 +422,12 @@ static LIST_HEAD(spi_master_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 |
| * @master: Controller to which device is connected |
| @@ -500,7 +506,6 @@ static int spi_dev_check(struct device * |
| */ |
| int spi_add_device(struct spi_device *spi) |
| { |
| - static DEFINE_MUTEX(spi_add_lock); |
| struct spi_master *master = spi->master; |
| struct device *dev = master->dev.parent; |
| int status; |
| @@ -529,6 +534,13 @@ int spi_add_device(struct spi_device *sp |
| goto done; |
| } |
| |
| + /* Controller may unregister concurrently */ |
| + if (IS_ENABLED(CONFIG_SPI_DYNAMIC) && |
| + !device_is_registered(&master->dev)) { |
| + status = -ENODEV; |
| + goto done; |
| + } |
| + |
| if (master->cs_gpios) |
| spi->cs_gpio = master->cs_gpios[spi->chip_select]; |
| |
| @@ -2070,6 +2082,10 @@ static int __unregister(struct device *d |
| */ |
| void spi_unregister_master(struct spi_master *master) |
| { |
| + /* Prevent addition of new devices, unregister existing ones */ |
| + if (IS_ENABLED(CONFIG_SPI_DYNAMIC)) |
| + mutex_lock(&spi_add_lock); |
| + |
| device_for_each_child(&master->dev, NULL, __unregister); |
| |
| if (master->queued) { |
| @@ -2089,6 +2105,9 @@ void spi_unregister_master(struct spi_ma |
| if (!devres_find(master->dev.parent, devm_spi_release_master, |
| devm_spi_match_master, master)) |
| put_device(&master->dev); |
| + |
| + if (IS_ENABLED(CONFIG_SPI_DYNAMIC)) |
| + mutex_unlock(&spi_add_lock); |
| } |
| EXPORT_SYMBOL_GPL(spi_unregister_master); |
| |