]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
dtrace: Add support for manual triggered cyclics
authorTomas Jedlicka <tomas.jedlicka@oracle.com>
Sun, 2 Jul 2017 16:17:01 +0000 (12:17 -0400)
committerTomas Jedlicka <tomas.jedlicka@oracle.com>
Wed, 12 Jul 2017 19:37:00 +0000 (21:37 +0200)
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 <tomas.jedlicka@oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees@oracle.com>
include/linux/cyclic.h
kernel/dtrace/cyclic.c

index e314bfcf666ed6b133f895b0b57d78cf68b06e94..5d8950222a3a64c6006579f09aab7cc136cccfac 100644 (file)
@@ -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_ */
index 2df05bcfe1bd854c47312fa6f3495d5e0e8051d4..99b23ccf22a5eac21414fabba4be3ad0b21cac50 100644 (file)
@@ -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 <linux/cpu.h>
 #include <linux/cyclic.h>
 #include <linux/hrtimer.h>
-#include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
@@ -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 %llns 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 %llns hdlr %pB arg %llx\n",
                           cyc->cpu,
                           cyc->cyc.hdlr.cyh_level == CY_HIGH_LEVEL
                                ? 'H' : 'l',