#include "rcu.h"
#include "list.h"
+#include "rculist.h"
/* Top 8 bits reserved for kernel tracer use. */
#if SIDE_BITS_PER_LONG == 64
void *priv;
};
+struct side_statedump_request_handle {
+ struct side_list_node node; /* RCU list. */
+ void (*cb)(void);
+};
+
struct side_callback {
union {
void (*call)(const struct side_event_description *desc,
void *key;
};
-static struct side_rcu_gp_state rcu_gp;
+static struct side_rcu_gp_state event_rcu_gp, statedump_rcu_gp;
/*
* Lazy initialization for early use within library constructors.
/*
* Recursive mutex to allow tracer callbacks to use the side API.
*/
-static pthread_mutex_t side_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+static pthread_mutex_t side_event_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+static pthread_mutex_t side_statedump_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
static DEFINE_SIDE_LIST_HEAD(side_events_list);
static DEFINE_SIDE_LIST_HEAD(side_tracer_list);
+static DEFINE_SIDE_LIST_HEAD(side_statedump_list);
/*
* Callback filter key for state dump.
if (side_unlikely(enabled & SIDE_EVENT_ENABLED_KERNEL_USER_EVENT_MASK)) {
// TODO: call kernel write.
}
- side_rcu_read_begin(&rcu_gp, &rcu_read_state);
+ side_rcu_read_begin(&event_rcu_gp, &rcu_read_state);
for (side_cb = side_rcu_dereference(es0->callbacks); side_cb->u.call != NULL; side_cb++) {
/* A NULL key is always a match. */
if (key && side_cb->key && side_cb->key != key)
continue;
side_cb->u.call(es0->desc, side_arg_vec, side_cb->priv);
}
- side_rcu_read_end(&rcu_gp, &rcu_read_state);
+ side_rcu_read_end(&event_rcu_gp, &rcu_read_state);
}
void side_call(const struct side_event_state *event_state, const struct side_arg_vec *side_arg_vec)
_side_call(event_state, side_arg_vec, NULL);
}
-void side_call_key(const struct side_event_state *event_state, const struct side_arg_vec *side_arg_vec)
+void side_statedump_call(const struct side_event_state *event_state, const struct side_arg_vec *side_arg_vec)
{
_side_call(event_state, side_arg_vec, filter_key);
}
if (side_unlikely(enabled & SIDE_EVENT_ENABLED_KERNEL_USER_EVENT_MASK)) {
// TODO: call kernel write.
}
- side_rcu_read_begin(&rcu_gp, &rcu_read_state);
+ side_rcu_read_begin(&event_rcu_gp, &rcu_read_state);
for (side_cb = side_rcu_dereference(es0->callbacks); side_cb->u.call_variadic != NULL; side_cb++) {
/* A NULL key is always a match. */
if (key && side_cb->key && side_cb->key != key)
continue;
side_cb->u.call_variadic(es0->desc, side_arg_vec, var_struct, side_cb->priv);
}
- side_rcu_read_end(&rcu_gp, &rcu_read_state);
+ side_rcu_read_end(&event_rcu_gp, &rcu_read_state);
}
void side_call_variadic(const struct side_event_state *event_state,
_side_call_variadic(event_state, side_arg_vec, var_struct, NULL);
}
-void side_call_variadic_key(const struct side_event_state *event_state,
+void side_statedump_call_variadic(const struct side_event_state *event_state,
const struct side_arg_vec *side_arg_vec,
const struct side_arg_dynamic_struct *var_struct)
{
return SIDE_ERROR_EXITING;
if (!initialized)
side_init();
- pthread_mutex_lock(&side_lock);
+ pthread_mutex_lock(&side_event_lock);
event_state = side_ptr_get(desc->state);
if (side_unlikely(event_state->version != 0))
abort();
new_cb[old_nr_cb].key = key;
/* High order bits are already zeroed. */
side_rcu_assign_pointer(es0->callbacks, new_cb);
- side_rcu_wait_grace_period(&rcu_gp);
+ side_rcu_wait_grace_period(&event_rcu_gp);
if (old_nr_cb)
free(old_cb);
es0->nr_callbacks++;
if (!old_nr_cb)
(void) __atomic_add_fetch(&es0->enabled, 1, __ATOMIC_RELAXED);
unlock:
- pthread_mutex_unlock(&side_lock);
+ pthread_mutex_unlock(&side_event_lock);
return ret;
}
return SIDE_ERROR_EXITING;
if (!initialized)
side_init();
- pthread_mutex_lock(&side_lock);
+ pthread_mutex_lock(&side_event_lock);
event_state = side_ptr_get(desc->state);
if (side_unlikely(event_state->version != 0))
abort();
}
/* High order bits are already zeroed. */
side_rcu_assign_pointer(es0->callbacks, new_cb);
- side_rcu_wait_grace_period(&rcu_gp);
+ side_rcu_wait_grace_period(&event_rcu_gp);
free(old_cb);
es0->nr_callbacks--;
/* Decrement concurrently with kernel setting the top bits. */
if (old_nr_cb == 1)
(void) __atomic_add_fetch(&es0->enabled, -1, __ATOMIC_RELAXED);
unlock:
- pthread_mutex_unlock(&side_lock);
+ pthread_mutex_unlock(&side_event_lock);
return ret;
}
events_handle->events = events;
events_handle->nr_events = nr_events;
- pthread_mutex_lock(&side_lock);
+ pthread_mutex_lock(&side_event_lock);
side_list_insert_node_tail(&side_events_list, &events_handle->node);
side_list_for_each_entry(tracer_handle, &side_tracer_list, node) {
tracer_handle->cb(SIDE_TRACER_NOTIFICATION_INSERT_EVENTS,
events, nr_events, tracer_handle->priv);
}
- pthread_mutex_unlock(&side_lock);
+ pthread_mutex_unlock(&side_event_lock);
//TODO: call event batch register ioctl
return events_handle;
}
return;
if (!initialized)
side_init();
- pthread_mutex_lock(&side_lock);
+ pthread_mutex_lock(&side_event_lock);
side_list_remove_node(&events_handle->node);
side_list_for_each_entry(tracer_handle, &side_tracer_list, node) {
tracer_handle->cb(SIDE_TRACER_NOTIFICATION_REMOVE_EVENTS,
continue;
side_event_remove_callbacks(event);
}
- pthread_mutex_unlock(&side_lock);
+ pthread_mutex_unlock(&side_event_lock);
//TODO: call event batch unregister ioctl
free(events_handle);
}
calloc(1, sizeof(struct side_tracer_handle));
if (!tracer_handle)
return NULL;
- pthread_mutex_lock(&side_lock);
+ pthread_mutex_lock(&side_event_lock);
tracer_handle->cb = cb;
tracer_handle->priv = priv;
side_list_insert_node_tail(&side_tracer_list, &tracer_handle->node);
cb(SIDE_TRACER_NOTIFICATION_INSERT_EVENTS,
events_handle->events, events_handle->nr_events, priv);
}
- pthread_mutex_unlock(&side_lock);
+ pthread_mutex_unlock(&side_event_lock);
return tracer_handle;
}
return;
if (!initialized)
side_init();
- pthread_mutex_lock(&side_lock);
+ pthread_mutex_lock(&side_event_lock);
side_list_for_each_entry(events_handle, &side_events_list, node) {
tracer_handle->cb(SIDE_TRACER_NOTIFICATION_REMOVE_EVENTS,
events_handle->events, events_handle->nr_events,
tracer_handle->priv);
}
side_list_remove_node(&tracer_handle->node);
- pthread_mutex_unlock(&side_lock);
+ pthread_mutex_unlock(&side_event_lock);
free(tracer_handle);
}
+struct side_statedump_request_handle *side_statedump_request_notification_register(void (*statedump_cb)(void))
+{
+ struct side_statedump_request_handle *handle;
+
+ if (finalized)
+ return NULL;
+ if (!initialized)
+ side_init();
+ /*
+ * The statedump request notification should not be registered
+ * from a notification callback.
+ */
+ assert(filter_key == NULL);
+ handle = (struct side_statedump_request_handle *)
+ calloc(1, sizeof(struct side_statedump_request_handle));
+ if (!handle)
+ return NULL;
+ handle->cb = statedump_cb;
+
+ pthread_mutex_lock(&side_statedump_lock);
+ side_list_insert_node_tail_rcu(&side_statedump_list, &handle->node);
+ pthread_mutex_unlock(&side_statedump_lock);
+
+ /* Invoke callback for all tracers. */
+ statedump_cb();
+
+ return handle;
+}
+
+void side_statedump_request_notification_unregister(struct side_statedump_request_handle *handle)
+{
+ if (finalized)
+ return;
+ if (!initialized)
+ side_init();
+ assert(filter_key == NULL);
+
+ pthread_mutex_lock(&side_statedump_lock);
+ side_list_remove_node_rcu(&handle->node);
+ pthread_mutex_unlock(&side_statedump_lock);
+
+ side_rcu_wait_grace_period(&statedump_rcu_gp);
+ free(handle);
+}
+
+void side_tracer_statedump_request(void *key)
+{
+ struct side_statedump_request_handle *handle;
+ struct side_rcu_read_state rcu_read_state;
+
+ /* Invoke the state dump callback specifically for the tracer key. */
+ filter_key = key;
+ side_rcu_read_begin(&statedump_rcu_gp, &rcu_read_state);
+ side_list_for_each_entry_rcu(handle, &side_statedump_list, node)
+ handle->cb();
+ side_rcu_read_end(&statedump_rcu_gp, &rcu_read_state);
+ filter_key = NULL;
+}
+
void side_init(void)
{
if (initialized)
return;
- side_rcu_gp_init(&rcu_gp);
+ side_rcu_gp_init(&event_rcu_gp);
+ side_rcu_gp_init(&statedump_rcu_gp);
initialized = true;
}
return;
side_list_for_each_entry_safe(handle, tmp, &side_events_list, node)
side_events_unregister(handle);
- side_rcu_gp_exit(&rcu_gp);
+ side_rcu_gp_exit(&event_rcu_gp);
+ side_rcu_gp_exit(&statedump_rcu_gp);
finalized = true;
}