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

Commit 0870525c authored by Lukas Wunner's avatar Lukas Wunner Committed by Greg Kroah-Hartman
Browse files

spi: Introduce device-managed SPI controller allocation



[ Upstream commit 5e844cc37a5cbaa460e68f9a989d321d63088a89 ]

SPI driver probing currently comprises two steps, whereas removal
comprises only one step:

    spi_alloc_master()
    spi_register_master()

    spi_unregister_master()

That's because spi_unregister_master() calls device_unregister()
instead of device_del(), thereby releasing the reference on the
spi_master which was obtained by spi_alloc_master().

An SPI driver's private data is contained in the same memory allocation
as the spi_master struct.  Thus, once spi_unregister_master() has been
called, the private data is inaccessible.  But some drivers need to
access it after spi_unregister_master() to perform further teardown
steps.

Introduce devm_spi_alloc_master(), which releases a reference on the
spi_master struct only after the driver has unbound, thereby keeping the
memory allocation accessible.  Change spi_unregister_master() to not
release a reference if the spi_master was allocated by the new devm
function.

The present commit is small enough to be backportable to stable.
It allows fixing drivers which use the private data in their ->remove()
hook after it's been freed.  It also allows fixing drivers which neglect
to release a reference on the spi_master in the probe error path.

Long-term, most SPI drivers shall be moved over to the devm function
introduced herein.  The few that can't shall be changed in a treewide
commit to explicitly release the last reference on the master.
That commit shall amend spi_unregister_master() to no longer release
a reference, thereby completing the migration.

As a result, the behaviour will be less surprising and more consistent
with subsystems such as IIO, which also includes the private data in the
allocation of the generic iio_dev struct, but calls device_del() in
iio_device_unregister().

Signed-off-by: default avatarLukas Wunner <lukas@wunner.de>
Link: https://lore.kernel.org/r/272bae2ef08abd21388c98e23729886663d19192.1605121038.git.lukas@wunner.de


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 2b8d8c96
Loading
Loading
Loading
Loading
+53 −1
Original line number Diff line number Diff line
@@ -1827,6 +1827,46 @@ struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
}
EXPORT_SYMBOL_GPL(spi_alloc_master);

static void devm_spi_release_master(struct device *dev, void *master)
{
	spi_master_put(*(struct spi_master **)master);
}

/**
 * devm_spi_alloc_master - resource-managed spi_alloc_master()
 * @dev: physical device of SPI master
 * @size: how much zeroed driver-private data to allocate
 * Context: can sleep
 *
 * Allocate an SPI master and automatically release a reference on it
 * when @dev is unbound from its driver.  Drivers are thus relieved from
 * having to call spi_master_put().
 *
 * The arguments to this function are identical to spi_alloc_master().
 *
 * Return: the SPI master structure on success, else NULL.
 */
struct spi_master *devm_spi_alloc_master(struct device *dev, unsigned int size)
{
	struct spi_master **ptr, *master;

	ptr = devres_alloc(devm_spi_release_master, sizeof(*ptr),
			   GFP_KERNEL);
	if (!ptr)
		return NULL;

	master = spi_alloc_master(dev, size);
	if (master) {
		*ptr = master;
		devres_add(dev, ptr);
	} else {
		devres_free(ptr);
	}

	return master;
}
EXPORT_SYMBOL_GPL(devm_spi_alloc_master);

#ifdef CONFIG_OF
static int of_spi_register_master(struct spi_master *master)
{
@@ -2007,6 +2047,11 @@ int devm_spi_register_master(struct device *dev, struct spi_master *master)
}
EXPORT_SYMBOL_GPL(devm_spi_register_master);

static int devm_spi_match_master(struct device *dev, void *res, void *master)
{
	return *(struct spi_master **)res == master;
}

static int __unregister(struct device *dev, void *null)
{
	spi_unregister_device(to_spi_device(dev));
@@ -2036,7 +2081,14 @@ void spi_unregister_master(struct spi_master *master)
	list_del(&master->list);
	mutex_unlock(&board_lock);

	device_unregister(&master->dev);
	device_del(&master->dev);

	/* Release the last reference on the master if its driver
	 * has not yet been converted to devm_spi_alloc_master().
	 */
	if (!devres_find(master->dev.parent, devm_spi_release_master,
			 devm_spi_match_master, master))
		put_device(&master->dev);
}
EXPORT_SYMBOL_GPL(spi_unregister_master);

+2 −0
Original line number Diff line number Diff line
@@ -601,6 +601,8 @@ extern void spi_finalize_current_transfer(struct spi_master *master);
/* the spi driver core manages memory for the spi_master classdev */
extern struct spi_master *
spi_alloc_master(struct device *host, unsigned size);
extern struct spi_master *
devm_spi_alloc_master(struct device *dev, unsigned int size);

extern int spi_register_master(struct spi_master *master);
extern int devm_spi_register_master(struct device *dev,