counter: Provide alternative counter registration functions

The current implementation gets device lifetime tracking wrong. The
problem is that allocation of struct counter_device is controlled by the
individual drivers but this structure contains a struct device that
might have to live longer than a driver is bound. As a result a command
sequence like:

	{ sleep 5; echo bang; } > /dev/counter0 &
	sleep 1;
	echo 40000000.timer:counter > /sys/bus/platform/drivers/stm32-timer-counter/unbind

can keep a reference to the struct device and unbinding results in
freeing the memory occupied by this device resulting in an oops.

This commit provides two new functions (plus some helpers):
 - counter_alloc() to allocate a struct counter_device that is
   automatically freed once the embedded struct device is released
 - counter_add() to register such a device.

Note that this commit doesn't fix any issues, all drivers have to be
converted to these new functions to correct the lifetime problems.

Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Link: https://lore.kernel.org/r/20211230150300.72196-14-u.kleine-koenig@pengutronix.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Uwe Kleine-König
2021-12-30 16:02:50 +01:00
committed by Greg Kroah-Hartman
parent e152833b2c
commit c18e276030
2 changed files with 181 additions and 2 deletions

View File

@@ -327,14 +327,29 @@ struct counter_device {
spinlock_t events_in_lock;
struct mutex events_out_lock;
struct mutex ops_exist_lock;
/*
* This can go away once all drivers are converted to
* counter_alloc()/counter_add().
*/
bool legacy_device;
};
void *counter_priv(const struct counter_device *const counter);
int counter_register(struct counter_device *const counter);
struct counter_device *counter_alloc(size_t sizeof_priv);
void counter_put(struct counter_device *const counter);
int counter_add(struct counter_device *const counter);
void counter_unregister(struct counter_device *const counter);
int devm_counter_register(struct device *dev,
struct counter_device *const counter);
struct counter_device *devm_counter_alloc(struct device *dev,
size_t sizeof_priv);
int devm_counter_add(struct device *dev,
struct counter_device *const counter);
void counter_push_event(struct counter_device *const counter, const u8 event,
const u8 channel);