From: Tomas Jedlicka Date: Sun, 2 Jul 2017 16:17:01 +0000 (-0400) Subject: dtrace: Add support for manual triggered cyclics X-Git-Tag: v4.1.12-106.0.20170720_1900~72^2 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=8455be24d76b8af46ea0dac65d9ef07237cbf3aa;p=users%2Fjedix%2Flinux-maple.git dtrace: Add support for manual triggered cyclics In some scenarios it is better if a client of cyclic susbstem can reprogram cyclic on his own. This is not possible with current implementation. This chage adds cyclic_reprogram() that can be used to schedule cyclic from inside and outside of its handler. A manually triggered cyclic is distinguished from other types by having its interval set to -1. Orabug: 26384803 Signed-off-by: Tomas Jedlicka Reviewed-by: Kris Van Hees --- diff --git a/include/linux/cyclic.h b/include/linux/cyclic.h index e314bfcf666e..5d8950222a3a 100644 --- a/include/linux/cyclic.h +++ b/include/linux/cyclic.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, 2011 Oracle Corporation + * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved. */ #ifndef _CYCLIC_H_ @@ -26,6 +26,8 @@ typedef struct cyc_handler { cyc_level_t cyh_level; } cyc_handler_t; +#define CY_INTERVAL_INF (-1) + typedef struct cyc_time { ktime_t cyt_when; ktime_t cyt_interval; @@ -40,5 +42,6 @@ typedef struct cyc_omni_handler { extern cyclic_id_t cyclic_add(cyc_handler_t *, cyc_time_t *); extern cyclic_id_t cyclic_add_omni(cyc_omni_handler_t *); extern void cyclic_remove(cyclic_id_t); +extern void cyclic_reprogram(cyclic_id_t, ktime_t); #endif /* _CYCLIC_H_ */ diff --git a/kernel/dtrace/cyclic.c b/kernel/dtrace/cyclic.c index 2df05bcfe1bd..99b23ccf22a5 100644 --- a/kernel/dtrace/cyclic.c +++ b/kernel/dtrace/cyclic.c @@ -2,13 +2,12 @@ * FILE: cyclic.c * DESCRIPTION: Minimal cyclic implementation * - * Copyright (C) 2010, 2011, 2012, 2013 Oracle Corporation + * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved. */ #include #include #include -#include #include #include #include @@ -137,6 +136,9 @@ done: /* * Prepare the timer for the next expiration. */ + if (cyc->cyc.when.cyt_interval.tv64 == CY_INTERVAL_INF) + return HRTIMER_NORESTART; + hrtimer_forward_now(timr, cyc->cyc.when.cyt_interval); return HRTIMER_RESTART; @@ -167,6 +169,19 @@ cyclic_t *cyclic_new(int omni) return cyc; } +static inline void cyclic_restart(cyclic_t *cyc) +{ + if (cyc->cyc.when.cyt_interval.tv64 == CY_INTERVAL_INF) + return; + + if (cyc->cyc.when.cyt_when.tv64 == 0) + hrtimer_start(&cyc->cyc.timr, cyc->cyc.when.cyt_interval, + HRTIMER_MODE_REL_PINNED); + else + hrtimer_start(&cyc->cyc.timr, cyc->cyc.when.cyt_when, + HRTIMER_MODE_ABS_PINNED); +} + /* * Add a new cyclic to the system. */ @@ -186,12 +201,7 @@ cyclic_id_t cyclic_add(cyc_handler_t *hdlr, cyc_time_t *when) cyc->cyc.when = *when; cyc->cyc.hdlr = *hdlr; - if (cyc->cyc.when.cyt_when.tv64 == 0) - hrtimer_start(&cyc->cyc.timr, cyc->cyc.when.cyt_interval, - HRTIMER_MODE_REL_PINNED); - else - hrtimer_start(&cyc->cyc.timr, cyc->cyc.when.cyt_when, - HRTIMER_MODE_ABS_PINNED); + cyclic_restart(cyc); /* * Let the caller know when the cyclic was added. @@ -204,12 +214,7 @@ EXPORT_SYMBOL(cyclic_add); static void cyclic_omni_xcall(cyclic_t *cyc) { - if (cyc->cyc.when.cyt_when.tv64 == 0) - hrtimer_start(&cyc->cyc.timr, cyc->cyc.when.cyt_interval, - HRTIMER_MODE_REL_PINNED); - else - hrtimer_start(&cyc->cyc.timr, cyc->cyc.when.cyt_when, - HRTIMER_MODE_ABS_PINNED); + cyclic_restart(cyc); } /* @@ -385,6 +390,68 @@ void cyclic_remove(cyclic_id_t id) } EXPORT_SYMBOL(cyclic_remove); +typedef struct cyclic_reprog { + cyclic_id_t cycid; + ktime_t delta; +} cyclic_reprog_t; + +static void cyclic_reprogram_xcall(cyclic_reprog_t *creprog) +{ + cyclic_reprogram(creprog->cycid, creprog->delta); +} + +/* + * Reprogram cyclic to fire with given delta from now. + * + * The underlying design makes it safe to call cyclic_reprogram from whithin a + * cyclic handler without race with cylic_remove. If called from outside of the + * cyclic handler it is up to the owner to ensure to not call cyclic_reprogram + * after call to cyclic_remove. + * + * This function cannot be called from interrupt/bottom half contexts. + */ +void cyclic_reprogram(cyclic_id_t id, ktime_t delta) +{ + cyclic_t *cyc = (cyclic_t *)id; + + /* + * For omni present cyclic we reprogram child for current CPU. + */ + if (CYCLIC_IS_OMNI(cyc)) { + cyclic_t *c, *n; + + list_for_each_entry_safe(c, n, &cyc->omni.cycl, list) { + if (c->cpu != smp_processor_id()) + continue; + + hrtimer_start(&c->cyc.timr, delta, + HRTIMER_MODE_ABS_PINNED); + + break; + } + + return; + } + + /* + * Regular cyclic reprogram must ensure that the timer remains bound + * to the CPU it was registered on. In case we are called from + * different CPU we use xcall to trigger reprogram from correct cpu. + */ + if (cyc->cpu != smp_processor_id()) { + cyclic_reprog_t creprog = { + .cycid = id, + .delta = delta, + }; + + smp_call_function_single(cyc->cpu, (smp_call_func_t) + cyclic_reprogram_xcall, &creprog, 1); + } else { + hrtimer_start(&cyc->cyc.timr, delta, HRTIMER_MODE_REL_PINNED); + } +} +EXPORT_SYMBOL(cyclic_reprogram); + static void *s_start(struct seq_file *seq, loff_t *pos) { loff_t n = *pos; @@ -427,7 +494,7 @@ static int s_show(struct seq_file *seq, void *p) seq_printf(seq, "Omni-present cyclic:\n"); list_for_each_entry(c, &cyc->omni.cycl, list) seq_printf(seq, - " CPU-%d: %c %lluns hdlr %pB arg %llx\n", + " CPU-%d: %c %lld ns hdlr %pB arg %llx\n", c->cpu, c->cyc.hdlr.cyh_level == CY_HIGH_LEVEL ? 'H' : 'l', @@ -435,7 +502,7 @@ static int s_show(struct seq_file *seq, void *p) c->cyc.hdlr.cyh_func, (uint64_t)c->cyc.hdlr.cyh_arg); } else - seq_printf(seq, "CPU-%d: %c %lluns hdlr %pB arg %llx\n", + seq_printf(seq, "CPU-%d: %c %lld ns hdlr %pB arg %llx\n", cyc->cpu, cyc->cyc.hdlr.cyh_level == CY_HIGH_LEVEL ? 'H' : 'l',