+ size_t i;
+ int ret;
+
+ /* For each field, ensure enum is part of the session. */
+ for (i = 0; i < nr_fields; i++) {
+ const struct lttng_type *type = &ctx_fields[i].event_field.type;
+
+ ret = lttng_create_enum_check(type, session);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * Ensure that a state-dump will be performed for this session at the end
+ * of the current handle_message().
+ */
+int lttng_session_statedump(struct lttng_session *session)
+{
+ session->priv->statedump_pending = 1;
+ lttng_ust_sockinfo_session_enabled(session->priv->owner);
+ return 0;
+}
+
+int lttng_session_enable(struct lttng_session *session)
+{
+ int ret = 0;
+ struct lttng_channel *chan;
+ int notify_socket;
+
+ if (session->active) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ notify_socket = lttng_get_notify_socket(session->priv->owner);
+ if (notify_socket < 0)
+ return notify_socket;
+
+ /* Set transient enabler state to "enabled" */
+ session->priv->tstate = 1;
+
+ /* We need to sync enablers with session before activation. */
+ lttng_session_sync_event_enablers(session);
+
+ /*
+ * Snapshot the number of events per channel to know the type of header
+ * we need to use.
+ */
+ cds_list_for_each_entry(chan, &session->priv->chan_head, node) {
+ const struct lttng_ctx *ctx;
+ const struct lttng_ctx_field *fields = NULL;
+ size_t nr_fields = 0;
+ uint32_t chan_id;
+
+ /* don't change it if session stop/restart */
+ if (chan->header_type)
+ continue;
+ ctx = chan->ctx;
+ if (ctx) {
+ nr_fields = ctx->nr_fields;
+ fields = ctx->fields;
+ ret = lttng_create_all_ctx_enums(nr_fields, fields,
+ session);
+ if (ret < 0) {
+ DBG("Error (%d) adding enum to session", ret);
+ return ret;
+ }
+ }
+ ret = ustcomm_register_channel(notify_socket,
+ session,
+ session->priv->objd,
+ chan->objd,
+ nr_fields,
+ fields,
+ &chan_id,
+ &chan->header_type);
+ if (ret) {
+ DBG("Error (%d) registering channel to sessiond", ret);
+ return ret;
+ }
+ if (chan_id != chan->id) {
+ DBG("Error: channel registration id (%u) does not match id assigned at creation (%u)",
+ chan_id, chan->id);
+ return -EINVAL;
+ }
+ }
+
+ /* Set atomically the state to "active" */
+ CMM_ACCESS_ONCE(session->active) = 1;
+ CMM_ACCESS_ONCE(session->priv->been_active) = 1;
+
+ ret = lttng_session_statedump(session);
+ if (ret)
+ return ret;
+end:
+ return ret;
+}
+
+int lttng_session_disable(struct lttng_session *session)
+{
+ int ret = 0;
+
+ if (!session->active) {
+ ret = -EBUSY;
+ goto end;
+ }
+ /* Set atomically the state to "inactive" */
+ CMM_ACCESS_ONCE(session->active) = 0;
+
+ /* Set transient enabler state to "disabled" */
+ session->priv->tstate = 0;
+ lttng_session_sync_event_enablers(session);
+end:
+ return ret;
+}
+
+int lttng_channel_enable(struct lttng_channel *channel)
+{
+ int ret = 0;
+
+ if (channel->enabled) {
+ ret = -EBUSY;
+ goto end;
+ }
+ /* Set transient enabler state to "enabled" */
+ channel->tstate = 1;
+ lttng_session_sync_event_enablers(channel->session);
+ /* Set atomically the state to "enabled" */
+ CMM_ACCESS_ONCE(channel->enabled) = 1;
+end:
+ return ret;
+}
+
+int lttng_channel_disable(struct lttng_channel *channel)
+{
+ int ret = 0;
+
+ if (!channel->enabled) {
+ ret = -EBUSY;
+ goto end;
+ }
+ /* Set atomically the state to "disabled" */
+ CMM_ACCESS_ONCE(channel->enabled) = 0;
+ /* Set transient enabler state to "enabled" */
+ channel->tstate = 0;
+ lttng_session_sync_event_enablers(channel->session);
+end:
+ return ret;
+}
+
+static inline
+struct cds_hlist_head *borrow_hash_table_bucket(
+ struct cds_hlist_head *hash_table,
+ unsigned int hash_table_size,
+ const struct lttng_event_desc *desc)
+{
+ const char *event_name;
+ size_t name_len;
+ uint32_t hash;
+
+ event_name = desc->name;
+ name_len = strlen(event_name);
+
+ hash = jhash(event_name, name_len, 0);
+ return &hash_table[hash & (hash_table_size - 1)];
+}
+
+/*
+ * Supports event creation while tracing session is active.
+ */
+static
+int lttng_event_create(const struct lttng_event_desc *desc,
+ struct lttng_channel *chan)
+{
+ struct lttng_event *event;
+ struct lttng_ust_event_private *event_priv;
+ struct lttng_session *session = chan->session;
+ struct cds_hlist_head *head;
+ int ret = 0;
+ int notify_socket, loglevel;
+ const char *uri;
+
+ head = borrow_hash_table_bucket(chan->session->priv->events_ht.table,
+ LTTNG_UST_EVENT_HT_SIZE, desc);
+
+ notify_socket = lttng_get_notify_socket(session->priv->owner);
+ if (notify_socket < 0) {
+ ret = notify_socket;
+ goto socket_error;
+ }
+
+ ret = lttng_create_all_event_enums(desc->nr_fields, desc->fields,
+ session);
+ if (ret < 0) {
+ DBG("Error (%d) adding enum to session", ret);
+ goto create_enum_error;
+ }
+
+ /*
+ * Check if loglevel match. Refuse to connect event if not.
+ */
+ event = zmalloc(sizeof(struct lttng_event));
+ if (!event) {
+ ret = -ENOMEM;
+ goto cache_error;
+ }
+ event_priv = zmalloc(sizeof(struct lttng_ust_event_private));
+ if (!event_priv) {
+ ret = -ENOMEM;
+ goto priv_error;
+ }
+ event->priv = event_priv;
+ event_priv->pub = event;
+ event->chan = chan;
+
+ /* Event will be enabled by enabler sync. */
+ event->enabled = 0;
+ event->priv->registered = 0;
+ CDS_INIT_LIST_HEAD(&event->filter_bytecode_runtime_head);
+ CDS_INIT_LIST_HEAD(&event->priv->enablers_ref_head);
+ event->priv->desc = desc;
+
+ if (desc->loglevel)
+ loglevel = *(*event->priv->desc->loglevel);
+ else
+ loglevel = TRACE_DEFAULT;
+ if (desc->u.ext.model_emf_uri)
+ uri = *(desc->u.ext.model_emf_uri);
+ else
+ uri = NULL;
+
+ /* Fetch event ID from sessiond */
+ ret = ustcomm_register_event(notify_socket,
+ session,
+ session->priv->objd,
+ chan->objd,
+ desc->name,
+ loglevel,
+ desc->signature,
+ desc->nr_fields,
+ desc->fields,
+ uri,
+ &event->id);
+ if (ret < 0) {
+ DBG("Error (%d) registering event to sessiond", ret);
+ goto sessiond_register_error;
+ }
+
+ cds_list_add(&event->priv->node, &chan->session->priv->events_head);
+ cds_hlist_add_head(&event->priv->hlist, head);
+ return 0;
+
+sessiond_register_error:
+ free(event_priv);
+priv_error:
+ free(event);
+cache_error:
+create_enum_error:
+socket_error:
+ return ret;
+}
+
+static
+int lttng_event_notifier_create(const struct lttng_event_desc *desc,
+ uint64_t token, uint64_t error_counter_index,
+ struct lttng_event_notifier_group *event_notifier_group)
+{
+ struct lttng_event_notifier *event_notifier;
+ struct cds_hlist_head *head;
+ int ret = 0;
+
+ /*
+ * Get the hashtable bucket the created lttng_event_notifier object
+ * should be inserted.
+ */
+ head = borrow_hash_table_bucket(
+ event_notifier_group->event_notifiers_ht.table,
+ LTTNG_UST_EVENT_NOTIFIER_HT_SIZE, desc);
+
+ event_notifier = zmalloc(sizeof(struct lttng_event_notifier));
+ if (!event_notifier) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ event_notifier->group = event_notifier_group;
+ event_notifier->user_token = token;
+ event_notifier->error_counter_index = error_counter_index;
+
+ /* Event notifier will be enabled by enabler sync. */
+ event_notifier->enabled = 0;
+ event_notifier->registered = 0;
+
+ CDS_INIT_LIST_HEAD(&event_notifier->filter_bytecode_runtime_head);
+ CDS_INIT_LIST_HEAD(&event_notifier->capture_bytecode_runtime_head);
+ CDS_INIT_LIST_HEAD(&event_notifier->enablers_ref_head);
+ event_notifier->desc = desc;
+ event_notifier->notification_send = lttng_event_notifier_notification_send;
+
+ cds_list_add(&event_notifier->node,
+ &event_notifier_group->event_notifiers_head);
+ cds_hlist_add_head(&event_notifier->hlist, head);
+
+ return 0;
+
+error:
+ return ret;
+}
+
+static
+void _lttng_event_notifier_destroy(struct lttng_event_notifier *event_notifier)
+{
+ struct lttng_enabler_ref *enabler_ref, *tmp_enabler_ref;
+
+ /* Remove from event_notifier list. */
+ cds_list_del(&event_notifier->node);
+ /* Remove from event_notifier hash table. */
+ cds_hlist_del(&event_notifier->hlist);
+
+ lttng_free_event_notifier_filter_runtime(event_notifier);
+
+ /* Free event_notifier enabler refs */
+ cds_list_for_each_entry_safe(enabler_ref, tmp_enabler_ref,
+ &event_notifier->enablers_ref_head, node)
+ free(enabler_ref);
+ free(event_notifier);
+}