cyclic: make clients embed a struct cyclic_info in their own data structure
There are of course not a whole lot of examples in-tree yet, but before they appear, let's make this API change: Instead of separately allocating a 'struct cyclic_info', make the users embed such an instance in their own structure, and make the convention that the callback simply receives the 'struct cyclic_info *', from which the clients can get their own data using the container_of() macro. This has a number of advantages. First, it means cyclic_register() simply cannot fail, simplifying the code. The necessary storage will simply be allocated automatically when the client's own structure is allocated (often via uclass_priv_auto or similar). Second, code for which CONFIG_CYCLIC is just an option can more easily be written without #ifdefs, if we just provide an empty struct cyclic_info {}. For example, the nested CONFIG_IS_ENABLED()s in https://lore.kernel.org/u-boot/20240316201416.211480-1-marek.vasut+renesas@mailbox.org/ are mostly due to the existence of the 'struct cyclic_info *' member being guarded by #ifdef CONFIG_CYCLIC. And we do probably want to avoid the extra memory overhead of that member when !CONFIG_CYCLIC. But that is automatic if, instead of a 'struct cyclic_info *', one simply embeds a 'struct cyclic_info', which will have size 0 when !CONFIG_CYCLIC. Also, the no-op cyclic_register() function can just unconditionally be called, and the compiler will see that (1) the callback is referenced, so not emit a warning for a maybe-unused function and (2) see that it can actually never be reached, so not emit any code for it. Reviewed-by: Stefan Roese <sr@denx.de> Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
This commit is contained in:

committed by
Stefan Roese

parent
df2b3829c6
commit
008c4b3c31
@@ -249,7 +249,7 @@ void board_configure_qlms(void)
|
|||||||
* read the incorrect device ID 0x9700 (reset value) instead of 0x9702
|
* read the incorrect device ID 0x9700 (reset value) instead of 0x9702
|
||||||
* (restored value).
|
* (restored value).
|
||||||
*/
|
*/
|
||||||
static void octeon_board_restore_pf(void *ctx)
|
static void octeon_board_restore_pf(struct cyclic_info *c)
|
||||||
{
|
{
|
||||||
union cvmx_spemx_flr_pf_stopreq stopreq;
|
union cvmx_spemx_flr_pf_stopreq stopreq;
|
||||||
static bool start_initialized[2] = {false, false};
|
static bool start_initialized[2] = {false, false};
|
||||||
@@ -357,10 +357,13 @@ int board_late_init(void)
|
|||||||
board_configure_qlms();
|
board_configure_qlms();
|
||||||
|
|
||||||
/* Register cyclic function for PCIe FLR fixup */
|
/* Register cyclic function for PCIe FLR fixup */
|
||||||
cyclic = cyclic_register(octeon_board_restore_pf, 100,
|
cyclic = calloc(1, sizeof(*cyclic));
|
||||||
"pcie_flr_fix", NULL);
|
if (cyclic) {
|
||||||
if (!cyclic)
|
cyclic_register(cyclic, octeon_board_restore_pf, 100,
|
||||||
|
"pcie_flr_fix");
|
||||||
|
} else {
|
||||||
printf("Registering of cyclic function failed\n");
|
printf("Registering of cyclic function failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
12
cmd/cyclic.c
12
cmd/cyclic.c
@@ -15,14 +15,16 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <vsprintf.h>
|
#include <vsprintf.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
|
||||||
struct cyclic_demo_info {
|
struct cyclic_demo_info {
|
||||||
|
struct cyclic_info cyclic;
|
||||||
uint delay_us;
|
uint delay_us;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void cyclic_demo(void *ctx)
|
static void cyclic_demo(struct cyclic_info *c)
|
||||||
{
|
{
|
||||||
struct cyclic_demo_info *info = ctx;
|
struct cyclic_demo_info *info = container_of(c, struct cyclic_demo_info, cyclic);
|
||||||
|
|
||||||
/* Just a small dummy delay here */
|
/* Just a small dummy delay here */
|
||||||
udelay(info->delay_us);
|
udelay(info->delay_us);
|
||||||
@@ -32,7 +34,6 @@ static int do_cyclic_demo(struct cmd_tbl *cmdtp, int flag, int argc,
|
|||||||
char *const argv[])
|
char *const argv[])
|
||||||
{
|
{
|
||||||
struct cyclic_demo_info *info;
|
struct cyclic_demo_info *info;
|
||||||
struct cyclic_info *cyclic;
|
|
||||||
uint time_ms;
|
uint time_ms;
|
||||||
|
|
||||||
if (argc < 3)
|
if (argc < 3)
|
||||||
@@ -48,10 +49,7 @@ static int do_cyclic_demo(struct cmd_tbl *cmdtp, int flag, int argc,
|
|||||||
info->delay_us = simple_strtoul(argv[2], NULL, 0);
|
info->delay_us = simple_strtoul(argv[2], NULL, 0);
|
||||||
|
|
||||||
/* Register demo cyclic function */
|
/* Register demo cyclic function */
|
||||||
cyclic = cyclic_register(cyclic_demo, time_ms * 1000, "cyclic_demo",
|
cyclic_register(&info->cyclic, cyclic_demo, time_ms * 1000, "cyclic_demo");
|
||||||
info);
|
|
||||||
if (!cyclic)
|
|
||||||
printf("Registering of cyclic_demo failed\n");
|
|
||||||
|
|
||||||
printf("Registered function \"%s\" to be executed all %dms\n",
|
printf("Registered function \"%s\" to be executed all %dms\n",
|
||||||
"cyclic_demo", time_ms);
|
"cyclic_demo", time_ms);
|
||||||
|
@@ -26,34 +26,22 @@ struct hlist_head *cyclic_get_list(void)
|
|||||||
return (struct hlist_head *)&gd->cyclic_list;
|
return (struct hlist_head *)&gd->cyclic_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct cyclic_info *cyclic_register(cyclic_func_t func, uint64_t delay_us,
|
void cyclic_register(struct cyclic_info *cyclic, cyclic_func_t func,
|
||||||
const char *name, void *ctx)
|
uint64_t delay_us, const char *name)
|
||||||
{
|
{
|
||||||
struct cyclic_info *cyclic;
|
memset(cyclic, 0, sizeof(*cyclic));
|
||||||
|
|
||||||
cyclic = calloc(1, sizeof(struct cyclic_info));
|
|
||||||
if (!cyclic) {
|
|
||||||
pr_debug("Memory allocation error\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Store values in struct */
|
/* Store values in struct */
|
||||||
cyclic->func = func;
|
cyclic->func = func;
|
||||||
cyclic->ctx = ctx;
|
|
||||||
cyclic->name = name;
|
cyclic->name = name;
|
||||||
cyclic->delay_us = delay_us;
|
cyclic->delay_us = delay_us;
|
||||||
cyclic->start_time_us = timer_get_us();
|
cyclic->start_time_us = timer_get_us();
|
||||||
hlist_add_head(&cyclic->list, cyclic_get_list());
|
hlist_add_head(&cyclic->list, cyclic_get_list());
|
||||||
|
|
||||||
return cyclic;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int cyclic_unregister(struct cyclic_info *cyclic)
|
void cyclic_unregister(struct cyclic_info *cyclic)
|
||||||
{
|
{
|
||||||
hlist_del(&cyclic->list);
|
hlist_del(&cyclic->list);
|
||||||
free(cyclic);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cyclic_run(void)
|
void cyclic_run(void)
|
||||||
@@ -76,7 +64,7 @@ void cyclic_run(void)
|
|||||||
if (time_after_eq64(now, cyclic->next_call)) {
|
if (time_after_eq64(now, cyclic->next_call)) {
|
||||||
/* Call cyclic function and account it's cpu-time */
|
/* Call cyclic function and account it's cpu-time */
|
||||||
cyclic->next_call = now + cyclic->delay_us;
|
cyclic->next_call = now + cyclic->delay_us;
|
||||||
cyclic->func(cyclic->ctx);
|
cyclic->func(cyclic);
|
||||||
cyclic->run_cnt++;
|
cyclic->run_cnt++;
|
||||||
cpu_time = timer_get_us() - now;
|
cpu_time = timer_get_us() - now;
|
||||||
cyclic->cpu_time_us += cpu_time;
|
cyclic->cpu_time_us += cpu_time;
|
||||||
|
@@ -19,20 +19,26 @@ Registering a cyclic function
|
|||||||
|
|
||||||
To register a cyclic function, use something like this::
|
To register a cyclic function, use something like this::
|
||||||
|
|
||||||
static void cyclic_demo(void *ctx)
|
struct donkey {
|
||||||
|
struct cyclic_info cyclic;
|
||||||
|
void (*say)(const char *s);
|
||||||
|
};
|
||||||
|
|
||||||
|
static void cyclic_demo(struct cyclic_info *c)
|
||||||
{
|
{
|
||||||
/* Just a small dummy delay here */
|
struct donkey *donkey = container_of(c, struct donkey, cyclic);
|
||||||
udelay(10);
|
|
||||||
|
donkey->say("Are we there yet?");
|
||||||
}
|
}
|
||||||
|
|
||||||
int board_init(void)
|
int donkey_init(void)
|
||||||
{
|
{
|
||||||
struct cyclic_info *cyclic;
|
struct donkey *donkey;
|
||||||
|
|
||||||
|
/* Initialize donkey ... */
|
||||||
|
|
||||||
/* Register demo cyclic function */
|
/* Register demo cyclic function */
|
||||||
cyclic = cyclic_register(cyclic_demo, 10 * 1000, "cyclic_demo", NULL);
|
cyclic_register(&donkey->cyclic, cyclic_demo, 10 * 1000, "cyclic_demo");
|
||||||
if (!cyclic)
|
|
||||||
printf("Registering of cyclic_demo failed\n");
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -17,12 +17,15 @@
|
|||||||
#include <asm/global_data.h>
|
#include <asm/global_data.h>
|
||||||
#include <dm/device-internal.h>
|
#include <dm/device-internal.h>
|
||||||
#include <dm/lists.h>
|
#include <dm/lists.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
|
||||||
DECLARE_GLOBAL_DATA_PTR;
|
DECLARE_GLOBAL_DATA_PTR;
|
||||||
|
|
||||||
#define WATCHDOG_TIMEOUT_SECS (CONFIG_WATCHDOG_TIMEOUT_MSECS / 1000)
|
#define WATCHDOG_TIMEOUT_SECS (CONFIG_WATCHDOG_TIMEOUT_MSECS / 1000)
|
||||||
|
|
||||||
struct wdt_priv {
|
struct wdt_priv {
|
||||||
|
/* The udevice owning this wdt_priv. */
|
||||||
|
struct udevice *dev;
|
||||||
/* Timeout, in seconds, to configure this device to. */
|
/* Timeout, in seconds, to configure this device to. */
|
||||||
u32 timeout;
|
u32 timeout;
|
||||||
/*
|
/*
|
||||||
@@ -40,18 +43,17 @@ struct wdt_priv {
|
|||||||
/* autostart */
|
/* autostart */
|
||||||
bool autostart;
|
bool autostart;
|
||||||
|
|
||||||
struct cyclic_info *cyclic;
|
struct cyclic_info cyclic;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void wdt_cyclic(void *ctx)
|
static void wdt_cyclic(struct cyclic_info *c)
|
||||||
{
|
{
|
||||||
struct udevice *dev = ctx;
|
struct wdt_priv *priv = container_of(c, struct wdt_priv, cyclic);
|
||||||
struct wdt_priv *priv;
|
struct udevice *dev = priv->dev;
|
||||||
|
|
||||||
if (!device_active(dev))
|
if (!device_active(dev))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
priv = dev_get_uclass_priv(dev);
|
|
||||||
if (!priv->running)
|
if (!priv->running)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -124,20 +126,14 @@ int wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
|
|||||||
memset(str, 0, 16);
|
memset(str, 0, 16);
|
||||||
if (IS_ENABLED(CONFIG_WATCHDOG)) {
|
if (IS_ENABLED(CONFIG_WATCHDOG)) {
|
||||||
if (priv->running)
|
if (priv->running)
|
||||||
cyclic_unregister(priv->cyclic);
|
cyclic_unregister(&priv->cyclic);
|
||||||
|
|
||||||
/* Register the watchdog driver as a cyclic function */
|
/* Register the watchdog driver as a cyclic function */
|
||||||
priv->cyclic = cyclic_register(wdt_cyclic,
|
cyclic_register(&priv->cyclic, wdt_cyclic,
|
||||||
priv->reset_period * 1000,
|
priv->reset_period * 1000,
|
||||||
dev->name, dev);
|
|
||||||
if (!priv->cyclic) {
|
|
||||||
printf("cyclic_register for %s failed\n",
|
|
||||||
dev->name);
|
dev->name);
|
||||||
return -ENODEV;
|
|
||||||
} else {
|
snprintf(str, 16, "every %ldms", priv->reset_period);
|
||||||
snprintf(str, 16, "every %ldms",
|
|
||||||
priv->reset_period);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->running = true;
|
priv->running = true;
|
||||||
@@ -162,7 +158,7 @@ int wdt_stop(struct udevice *dev)
|
|||||||
struct wdt_priv *priv = dev_get_uclass_priv(dev);
|
struct wdt_priv *priv = dev_get_uclass_priv(dev);
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_WATCHDOG) && priv->running)
|
if (IS_ENABLED(CONFIG_WATCHDOG) && priv->running)
|
||||||
cyclic_unregister(priv->cyclic);
|
cyclic_unregister(&priv->cyclic);
|
||||||
|
|
||||||
priv->running = false;
|
priv->running = false;
|
||||||
}
|
}
|
||||||
@@ -262,6 +258,7 @@ static int wdt_pre_probe(struct udevice *dev)
|
|||||||
autostart = true;
|
autostart = true;
|
||||||
}
|
}
|
||||||
priv = dev_get_uclass_priv(dev);
|
priv = dev_get_uclass_priv(dev);
|
||||||
|
priv->dev = dev;
|
||||||
priv->timeout = timeout;
|
priv->timeout = timeout;
|
||||||
priv->reset_period = reset_period;
|
priv->reset_period = reset_period;
|
||||||
priv->autostart = autostart;
|
priv->autostart = autostart;
|
||||||
|
@@ -18,7 +18,6 @@
|
|||||||
* struct cyclic_info - Information about cyclic execution function
|
* struct cyclic_info - Information about cyclic execution function
|
||||||
*
|
*
|
||||||
* @func: Function to call periodically
|
* @func: Function to call periodically
|
||||||
* @ctx: Context pointer to get passed to this function
|
|
||||||
* @name: Name of the cyclic function, e.g. shown in the commands
|
* @name: Name of the cyclic function, e.g. shown in the commands
|
||||||
* @delay_ns: Delay is ns after which this function shall get executed
|
* @delay_ns: Delay is ns after which this function shall get executed
|
||||||
* @start_time_us: Start time in us, when this function started its execution
|
* @start_time_us: Start time in us, when this function started its execution
|
||||||
@@ -27,10 +26,12 @@
|
|||||||
* @next_call: Next time in us, when the function shall be executed again
|
* @next_call: Next time in us, when the function shall be executed again
|
||||||
* @list: List node
|
* @list: List node
|
||||||
* @already_warned: Flag that we've warned about exceeding CPU time usage
|
* @already_warned: Flag that we've warned about exceeding CPU time usage
|
||||||
|
*
|
||||||
|
* When !CONFIG_CYCLIC, this struct is empty.
|
||||||
*/
|
*/
|
||||||
struct cyclic_info {
|
struct cyclic_info {
|
||||||
void (*func)(void *ctx);
|
#if defined(CONFIG_CYCLIC)
|
||||||
void *ctx;
|
void (*func)(struct cyclic_info *c);
|
||||||
const char *name;
|
const char *name;
|
||||||
uint64_t delay_us;
|
uint64_t delay_us;
|
||||||
uint64_t start_time_us;
|
uint64_t start_time_us;
|
||||||
@@ -39,31 +40,34 @@ struct cyclic_info {
|
|||||||
uint64_t next_call;
|
uint64_t next_call;
|
||||||
struct hlist_node list;
|
struct hlist_node list;
|
||||||
bool already_warned;
|
bool already_warned;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Function type for cyclic functions */
|
/** Function type for cyclic functions */
|
||||||
typedef void (*cyclic_func_t)(void *ctx);
|
typedef void (*cyclic_func_t)(struct cyclic_info *c);
|
||||||
|
|
||||||
#if defined(CONFIG_CYCLIC)
|
#if defined(CONFIG_CYCLIC)
|
||||||
/**
|
/**
|
||||||
* cyclic_register - Register a new cyclic function
|
* cyclic_register - Register a new cyclic function
|
||||||
*
|
*
|
||||||
|
* @cyclic: Cyclic info structure
|
||||||
* @func: Function to call periodically
|
* @func: Function to call periodically
|
||||||
* @delay_us: Delay is us after which this function shall get executed
|
* @delay_us: Delay is us after which this function shall get executed
|
||||||
* @name: Cyclic function name/id
|
* @name: Cyclic function name/id
|
||||||
* @ctx: Context to pass to the function
|
*
|
||||||
* @return: pointer to cyclic_struct if OK, NULL on error
|
* The function @func will be called with @cyclic as its
|
||||||
|
* argument. @cyclic will usually be embedded in some device-specific
|
||||||
|
* structure, which the callback can retrieve using container_of().
|
||||||
*/
|
*/
|
||||||
struct cyclic_info *cyclic_register(cyclic_func_t func, uint64_t delay_us,
|
void cyclic_register(struct cyclic_info *cyclic, cyclic_func_t func,
|
||||||
const char *name, void *ctx);
|
uint64_t delay_us, const char *name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cyclic_unregister - Unregister a cyclic function
|
* cyclic_unregister - Unregister a cyclic function
|
||||||
*
|
*
|
||||||
* @cyclic: Pointer to cyclic_struct of the function that shall be removed
|
* @cyclic: Pointer to cyclic_struct of the function that shall be removed
|
||||||
* @return: 0 if OK, -ve on error
|
|
||||||
*/
|
*/
|
||||||
int cyclic_unregister(struct cyclic_info *cyclic);
|
void cyclic_unregister(struct cyclic_info *cyclic);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cyclic_unregister_all() - Clean up cyclic functions
|
* cyclic_unregister_all() - Clean up cyclic functions
|
||||||
@@ -97,17 +101,14 @@ void cyclic_run(void);
|
|||||||
*/
|
*/
|
||||||
void schedule(void);
|
void schedule(void);
|
||||||
#else
|
#else
|
||||||
static inline struct cyclic_info *cyclic_register(cyclic_func_t func,
|
|
||||||
uint64_t delay_us,
|
static inline void cyclic_register(struct cyclic_info *cyclic, cyclic_func_t func,
|
||||||
const char *name,
|
uint64_t delay_us, const char *name)
|
||||||
void *ctx)
|
|
||||||
{
|
{
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int cyclic_unregister(struct cyclic_info *cyclic)
|
static inline void cyclic_unregister(struct cyclic_info *cyclic)
|
||||||
{
|
{
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cyclic_run(void)
|
static inline void cyclic_run(void)
|
||||||
|
@@ -12,22 +12,27 @@
|
|||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
|
||||||
/* Test that cyclic function is called */
|
/* Test that cyclic function is called */
|
||||||
static bool cyclic_active = false;
|
static struct cyclic_test {
|
||||||
|
struct cyclic_info cyclic;
|
||||||
|
bool called;
|
||||||
|
} cyclic_test;
|
||||||
|
|
||||||
static void cyclic_test(void *ctx)
|
static void test_cb(struct cyclic_info *c)
|
||||||
{
|
{
|
||||||
cyclic_active = true;
|
struct cyclic_test *t = container_of(c, struct cyclic_test, cyclic);
|
||||||
|
t->called = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dm_test_cyclic_running(struct unit_test_state *uts)
|
static int dm_test_cyclic_running(struct unit_test_state *uts)
|
||||||
{
|
{
|
||||||
cyclic_active = false;
|
cyclic_test.called = false;
|
||||||
ut_assertnonnull(cyclic_register(cyclic_test, 10 * 1000, "cyclic_demo",
|
cyclic_register(&cyclic_test.cyclic, test_cb, 10 * 1000, "cyclic_test");
|
||||||
NULL));
|
|
||||||
|
|
||||||
/* Execute all registered cyclic functions */
|
/* Execute all registered cyclic functions */
|
||||||
schedule();
|
schedule();
|
||||||
ut_asserteq(true, cyclic_active);
|
ut_asserteq(true, cyclic_test.called);
|
||||||
|
|
||||||
|
cyclic_unregister(&cyclic_test.cyclic);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user