tracing: Add a probe that attaches to trace events
[linux-2.6-microblaze.git] / kernel / tracepoint.c
index 8d772bd..64ea283 100644 (file)
@@ -28,6 +28,44 @@ extern tracepoint_ptr_t __stop___tracepoints_ptrs[];
 DEFINE_SRCU(tracepoint_srcu);
 EXPORT_SYMBOL_GPL(tracepoint_srcu);
 
+enum tp_transition_sync {
+       TP_TRANSITION_SYNC_1_0_1,
+       TP_TRANSITION_SYNC_N_2_1,
+
+       _NR_TP_TRANSITION_SYNC,
+};
+
+struct tp_transition_snapshot {
+       unsigned long rcu;
+       unsigned long srcu;
+       bool ongoing;
+};
+
+/* Protected by tracepoints_mutex */
+static struct tp_transition_snapshot tp_transition_snapshot[_NR_TP_TRANSITION_SYNC];
+
+static void tp_rcu_get_state(enum tp_transition_sync sync)
+{
+       struct tp_transition_snapshot *snapshot = &tp_transition_snapshot[sync];
+
+       /* Keep the latest get_state snapshot. */
+       snapshot->rcu = get_state_synchronize_rcu();
+       snapshot->srcu = start_poll_synchronize_srcu(&tracepoint_srcu);
+       snapshot->ongoing = true;
+}
+
+static void tp_rcu_cond_sync(enum tp_transition_sync sync)
+{
+       struct tp_transition_snapshot *snapshot = &tp_transition_snapshot[sync];
+
+       if (!snapshot->ongoing)
+               return;
+       cond_synchronize_rcu(snapshot->rcu);
+       if (!poll_state_synchronize_srcu(&tracepoint_srcu, snapshot->srcu))
+               synchronize_srcu(&tracepoint_srcu);
+       snapshot->ongoing = false;
+}
+
 /* Set to 1 to enable tracepoint debug output */
 static const int tracepoint_debug;
 
@@ -311,6 +349,11 @@ static int tracepoint_add_func(struct tracepoint *tp,
         */
        switch (nr_func_state(tp_funcs)) {
        case TP_FUNC_1:         /* 0->1 */
+               /*
+                * Make sure new static func never uses old data after a
+                * 1->0->1 transition sequence.
+                */
+               tp_rcu_cond_sync(TP_TRANSITION_SYNC_1_0_1);
                /* Set static call to first function */
                tracepoint_update_call(tp, tp_funcs);
                /* Both iterator and static call handle NULL tp->funcs */
@@ -325,10 +368,15 @@ static int tracepoint_add_func(struct tracepoint *tp,
                 * Requires ordering between RCU assign/dereference and
                 * static call update/call.
                 */
-               rcu_assign_pointer(tp->funcs, tp_funcs);
-               break;
+               fallthrough;
        case TP_FUNC_N:         /* N->N+1 (N>1) */
                rcu_assign_pointer(tp->funcs, tp_funcs);
+               /*
+                * Make sure static func never uses incorrect data after a
+                * N->...->2->1 (N>1) transition sequence.
+                */
+               if (tp_funcs[0].data != old[0].data)
+                       tp_rcu_get_state(TP_TRANSITION_SYNC_N_2_1);
                break;
        default:
                WARN_ON_ONCE(1);
@@ -372,24 +420,23 @@ static int tracepoint_remove_func(struct tracepoint *tp,
                /* Both iterator and static call handle NULL tp->funcs */
                rcu_assign_pointer(tp->funcs, NULL);
                /*
-                * Make sure new func never uses old data after a 1->0->1
-                * transition sequence.
-                * Considering that transition 0->1 is the common case
-                * and don't have rcu-sync, issue rcu-sync after
-                * transition 1->0 to break that sequence by waiting for
-                * readers to be quiescent.
+                * Make sure new static func never uses old data after a
+                * 1->0->1 transition sequence.
                 */
-               tracepoint_synchronize_unregister();
+               tp_rcu_get_state(TP_TRANSITION_SYNC_1_0_1);
                break;
        case TP_FUNC_1:         /* 2->1 */
                rcu_assign_pointer(tp->funcs, tp_funcs);
                /*
-                * On 2->1 transition, RCU sync is needed before setting
-                * static call to first callback, because the observer
-                * may have loaded any prior tp->funcs after the last one
-                * associated with an rcu-sync.
+                * Make sure static func never uses incorrect data after a
+                * N->...->2->1 (N>2) transition sequence. If the first
+                * element's data has changed, then force the synchronization
+                * to prevent current readers that have loaded the old data
+                * from calling the new function.
                 */
-               tracepoint_synchronize_unregister();
+               if (tp_funcs[0].data != old[0].data)
+                       tp_rcu_get_state(TP_TRANSITION_SYNC_N_2_1);
+               tp_rcu_cond_sync(TP_TRANSITION_SYNC_N_2_1);
                /* Set static call to first function */
                tracepoint_update_call(tp, tp_funcs);
                break;
@@ -397,6 +444,12 @@ static int tracepoint_remove_func(struct tracepoint *tp,
                fallthrough;
        case TP_FUNC_N:
                rcu_assign_pointer(tp->funcs, tp_funcs);
+               /*
+                * Make sure static func never uses incorrect data after a
+                * N->...->2->1 (N>2) transition sequence.
+                */
+               if (tp_funcs[0].data != old[0].data)
+                       tp_rcu_get_state(TP_TRANSITION_SYNC_N_2_1);
                break;
        default:
                WARN_ON_ONCE(1);
@@ -524,7 +577,7 @@ bool trace_module_has_bad_taint(struct module *mod)
 static BLOCKING_NOTIFIER_HEAD(tracepoint_notify_list);
 
 /**
- * register_tracepoint_notifier - register tracepoint coming/going notifier
+ * register_tracepoint_module_notifier - register tracepoint coming/going notifier
  * @nb: notifier block
  *
  * Notifiers registered with this function are called on module
@@ -550,7 +603,7 @@ end:
 EXPORT_SYMBOL_GPL(register_tracepoint_module_notifier);
 
 /**
- * unregister_tracepoint_notifier - unregister tracepoint coming/going notifier
+ * unregister_tracepoint_module_notifier - unregister tracepoint coming/going notifier
  * @nb: notifier block
  *
  * The notifier block callback should expect a "struct tp_module" data