+int ust_registry_map_create_event(struct ust_registry_session *session,
+ uint64_t map_key, int session_objd, int map_objd, char *name,
+ char *sig, size_t nr_fields, struct ustctl_field *fields,
+ int loglevel_value, char *model_emf_uri, int buffer_type,
+ uint64_t tracer_token, uint64_t *counter_index_p,
+ struct ust_app *app)
+{
+ int ret;
+ uint64_t counter_index;
+ struct ust_registry_map *map;
+ char *formated_key;
+ const struct lttng_map_key *key;
+
+ assert(session);
+ assert(name);
+ assert(sig);
+ assert(counter_index_p);
+
+ rcu_read_lock();
+
+ /*
+ * This should not happen but since it comes from the UST tracer, an
+ * external party, don't assert and simply validate values.
+ */
+ if (session_objd < 0 || map_objd < 0) {
+ ret = -EINVAL;
+ goto error_free;
+ }
+
+ map = ust_registry_map_find(session, map_key);
+ if (!map) {
+ ret = -EINVAL;
+ goto error_free;
+ }
+
+ /* Check if we've reached the maximum possible id. */
+ if (ust_registry_is_max_id(map->used_event_id)) {
+ ret = -ENOENT;
+ goto error_free;
+ }
+
+ key = ust_registry_map_find_key_for_token(map, tracer_token);
+ if (!key) {
+ ERR("Tracer token %"PRIu64" not found for map id = %"PRIu32,
+ tracer_token, map->map_id);
+ ret = -EINVAL;
+ goto error_unlock;
+ }
+
+ ret = format_event_key(key, name, &formated_key);
+ if (ret) {
+ ERR("Error formating key");
+ ret = -EINVAL;
+ goto error_unlock;
+ }
+
+ ret = ust_registry_map_find_or_create_index_for_key(map, formated_key,
+ &counter_index);
+ if (ret) {
+ ERR("Error finding or creating index for formated_key = '%s'",
+ formated_key);
+ free(formated_key);
+ ret = -EINVAL;
+ goto error_unlock;
+ }
+
+ DBG3("UST registry allocating counter index %"PRIu64 " to event: %s, "
+ "signature: %s, sess_objd: %u, map_objd: %u, map_id: %u",
+ counter_index, name, sig, session_objd, map_objd, map->map_id);
+
+ *counter_index_p = counter_index;
+
+ rcu_read_unlock();
+ return 0;
+
+error_free:
+ free(sig);
+ free(fields);
+ free(model_emf_uri);
+error_unlock:
+ rcu_read_unlock();
+ return ret;
+}
+
+
+/*
+ * For a given event in a registry, delete the entry and destroy the event.
+ * This MUST be called within a RCU read side lock section.
+ */
+void ust_registry_chan_destroy_event(struct ust_registry_channel *chan,
+ struct ust_registry_event *event)
+{
+ int ret;
+ struct lttng_ht_iter iter;
+
+ assert(chan);
+ assert(event);
+
+ /* Delete the node first. */
+ iter.iter.node = &event->node.node;
+ ret = lttng_ht_del(chan->ht, &iter);
+ assert(!ret);
+
+ call_rcu(&event->node.head, destroy_event_rcu);
+
+ return;
+}
+
+/*
+ * This MUST be called within a RCU read side lock section.
+ */
+static void ust_registry_map_key_entry_destroy(struct lttng_ht *ht,
+ struct ust_registry_map_key_ht_entry *entry)
+{
+ int ret;
+ struct lttng_ht_iter iter;
+
+ assert(ht);
+ assert(entry);
+
+ /* Delete the node first. */
+ iter.iter.node = &entry->node.node;
+ ret = lttng_ht_del(ht, &iter);
+ assert(!ret);
+
+ call_rcu(&entry->node.head, destroy_ust_registry_map_key_ht_entry);
+
+ return;
+}
+
+/*
+ * This MUST be called within a RCU read side lock section.
+ */
+static void ust_registry_map_index_ht_entry_destroy(struct lttng_ht *ht,
+ struct ust_registry_map_index_ht_entry *entry)
+{
+ int ret;
+ struct lttng_ht_iter iter;
+
+ assert(ht);
+ assert(entry);
+
+ /* Delete the node first. */
+ iter.iter.node = &entry->node.node;
+ ret = lttng_ht_del(ht, &iter);
+ assert(!ret);
+
+ call_rcu(&entry->node.head, destroy_ust_registry_map_index_ht_entry);
+
+ return;
+}
+
+/*
+ * For a given event in a registry, delete the entry and destroy the event.
+ * This MUST be called within a RCU read side lock section.
+ */
+void ust_registry_map_destroy_event(struct ust_registry_map *map,
+ struct ust_registry_event *event)
+{
+ int ret;
+ struct lttng_ht_iter iter;
+
+ assert(map);
+ assert(event);
+
+ /* Delete the node first. */
+ iter.iter.node = &event->node.node;
+ ret = lttng_ht_del(map->events_ht, &iter);
+ assert(!ret);
+
+ call_rcu(&event->node.head, destroy_event_rcu);
+
+ return;
+}
+
+static void destroy_enum(struct ust_registry_enum *reg_enum)
+{
+ if (!reg_enum) {
+ return;
+ }
+ free(reg_enum->entries);
+ free(reg_enum);
+}
+
+static void destroy_enum_rcu(struct rcu_head *head)
+{
+ struct ust_registry_enum *reg_enum =
+ caa_container_of(head, struct ust_registry_enum, rcu_head);
+
+ destroy_enum(reg_enum);
+}
+
+/*
+ * Lookup enumeration by name and comparing enumeration entries.
+ * Needs to be called from RCU read-side critical section.
+ */
+static struct ust_registry_enum *ust_registry_lookup_enum(
+ struct ust_registry_session *session,
+ const struct ust_registry_enum *reg_enum_lookup)
+{
+ struct ust_registry_enum *reg_enum = NULL;
+ struct lttng_ht_node_str *node;
+ struct lttng_ht_iter iter;
+
+ cds_lfht_lookup(session->enums->ht,
+ ht_hash_enum((void *) reg_enum_lookup, lttng_ht_seed),
+ ht_match_enum, reg_enum_lookup, &iter.iter);
+ node = lttng_ht_iter_get_node_str(&iter);
+ if (!node) {
+ goto end;
+ }
+ reg_enum = caa_container_of(node, struct ust_registry_enum, node);
+end:
+ return reg_enum;
+}
+
+/*
+ * Lookup enumeration by enum ID.
+ * Needs to be called from RCU read-side critical section.
+ */
+struct ust_registry_enum *
+ ust_registry_lookup_enum_by_id(struct ust_registry_session *session,
+ const char *enum_name, uint64_t enum_id)
+{
+ struct ust_registry_enum *reg_enum = NULL;
+ struct lttng_ht_node_str *node;
+ struct lttng_ht_iter iter;
+ struct ust_registry_enum reg_enum_lookup;
+
+ memset(®_enum_lookup, 0, sizeof(reg_enum_lookup));
+ strncpy(reg_enum_lookup.name, enum_name, LTTNG_UST_SYM_NAME_LEN);
+ reg_enum_lookup.name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0';
+ reg_enum_lookup.id = enum_id;
+ cds_lfht_lookup(session->enums->ht,
+ ht_hash_enum((void *) ®_enum_lookup, lttng_ht_seed),
+ ht_match_enum_id, ®_enum_lookup, &iter.iter);
+ node = lttng_ht_iter_get_node_str(&iter);
+ if (!node) {
+ goto end;
+ }
+ reg_enum = caa_container_of(node, struct ust_registry_enum, node);
+end:
+ return reg_enum;
+}
+
+/*
+ * Create a ust_registry_enum from the given parameters and add it to the
+ * registry hash table, or find it if already there.
+ *
+ * On success, return 0 else a negative value.
+ *
+ * Should be called with session registry mutex held.
+ *
+ * We receive ownership of entries.
+ */
+int ust_registry_create_or_find_enum(struct ust_registry_session *session,
+ int session_objd, char *enum_name,
+ struct ustctl_enum_entry *entries, size_t nr_entries,
+ uint64_t *enum_id)
+{
+ int ret = 0;
+ struct cds_lfht_node *nodep;
+ struct ust_registry_enum *reg_enum = NULL, *old_reg_enum;
+
+ assert(session);
+ assert(enum_name);
+
+ rcu_read_lock();
+
+ /*
+ * This should not happen but since it comes from the UST tracer, an
+ * external party, don't assert and simply validate values.
+ */
+ if (session_objd < 0) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* Check if the enumeration was already dumped */
+ reg_enum = zmalloc(sizeof(*reg_enum));
+ if (!reg_enum) {
+ PERROR("zmalloc ust registry enumeration");
+ ret = -ENOMEM;
+ goto end;
+ }
+ strncpy(reg_enum->name, enum_name, LTTNG_UST_SYM_NAME_LEN);
+ reg_enum->name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0';
+ /* entries will be owned by reg_enum. */
+ reg_enum->entries = entries;
+ reg_enum->nr_entries = nr_entries;
+ entries = NULL;
+
+ old_reg_enum = ust_registry_lookup_enum(session, reg_enum);
+ if (old_reg_enum) {
+ DBG("enum %s already in sess_objd: %u", enum_name, session_objd);
+ /* Fall through. Use prior enum. */
+ destroy_enum(reg_enum);
+ reg_enum = old_reg_enum;
+ } else {
+ DBG("UST registry creating enum: %s, sess_objd: %u",
+ enum_name, session_objd);
+ if (session->next_enum_id == -1ULL) {
+ ret = -EOVERFLOW;
+ destroy_enum(reg_enum);
+ goto end;
+ }
+ reg_enum->id = session->next_enum_id++;
+ cds_lfht_node_init(®_enum->node.node);
+ nodep = cds_lfht_add_unique(session->enums->ht,
+ ht_hash_enum(reg_enum, lttng_ht_seed),
+ ht_match_enum_id, reg_enum,
+ ®_enum->node.node);
+ assert(nodep == ®_enum->node.node);
+ }
+ DBG("UST registry reply with enum %s with id %" PRIu64 " in sess_objd: %u",
+ enum_name, reg_enum->id, session_objd);
+ *enum_id = reg_enum->id;
+end:
+ free(entries);
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * For a given enumeration in a registry, delete the entry and destroy
+ * the enumeration.
+ * This MUST be called within a RCU read side lock section.
+ */
+static void ust_registry_destroy_enum(struct ust_registry_session *reg_session,
+ struct ust_registry_enum *reg_enum)
+{
+ int ret;
+ struct lttng_ht_iter iter;
+
+ assert(reg_session);
+ assert(reg_enum);
+
+ /* Delete the node first. */
+ iter.iter.node = ®_enum->node.node;
+ ret = lttng_ht_del(reg_session->enums, &iter);
+ assert(!ret);
+ call_rcu(®_enum->rcu_head, destroy_enum_rcu);
+}
+
+/*
+ * We need to execute ht_destroy outside of RCU read-side critical
+ * section and outside of call_rcu thread, so we postpone its execution
+ * using ht_cleanup_push. It is simpler than to change the semantic of
+ * the many callers of delete_ust_app_session().
+ */
+static
+void destroy_channel_rcu(struct rcu_head *head)
+{
+ struct ust_registry_channel *chan =
+ caa_container_of(head, struct ust_registry_channel, rcu_head);
+
+ if (chan->ht) {
+ ht_cleanup_push(chan->ht);
+ }
+ free(chan->ctx_fields);
+ free(chan);
+}
+
+/*
+ * We need to execute ht_destroy outside of RCU read-side critical
+ * section and outside of call_rcu thread, so we postpone its execution
+ * using ht_cleanup_push. It is simpler than to mapge the semantic of
+ * the many callers of delete_ust_app_session().
+ */
+static
+void destroy_map_rcu(struct rcu_head *head)
+{
+ struct ust_registry_map *map =
+ caa_container_of(head, struct ust_registry_map, rcu_head);
+
+ if (map->events_ht) {
+ ht_cleanup_push(map->events_ht);
+ }
+
+ if (map->tracer_token_to_map_key_ht) {
+ ht_cleanup_push(map->tracer_token_to_map_key_ht);
+ }
+
+ if (map->key_string_to_bucket_index_ht) {
+ ht_cleanup_push(map->key_string_to_bucket_index_ht);
+ }
+
+ free(map);
+}
+
+/*
+ * Destroy every element of the registry and free the memory. This does NOT
+ * free the registry pointer since it might not have been allocated before so
+ * it's the caller responsability.
+ */
+static void destroy_channel(struct ust_registry_channel *chan, bool notif)
+{
+ struct lttng_ht_iter iter;
+ struct ust_registry_event *event;
+ enum lttng_error_code cmd_ret;
+
+ assert(chan);
+
+ if (notif) {
+ cmd_ret = notification_thread_command_remove_channel(
+ notification_thread_handle, chan->consumer_key,
+ LTTNG_DOMAIN_UST);
+ if (cmd_ret != LTTNG_OK) {
+ ERR("Failed to remove channel from notification thread");
+ }
+ }
+
+ if (chan->ht) {
+ rcu_read_lock();
+ /* Destroy all event associated with this registry. */
+ cds_lfht_for_each_entry(
+ chan->ht->ht, &iter.iter, event, node.node) {
+ /* Delete the node from the ht and free it. */
+ ust_registry_chan_destroy_event(chan, event);
+ }
+ rcu_read_unlock();
+ }
+ call_rcu(&chan->rcu_head, destroy_channel_rcu);
+}
+
+/*
+ * Destroy every element of the registry and free the memory. This does NOT
+ * free the registry pointer since it might not have been allocated before so
+ * it's the caller responsability.
+ */
+static void destroy_map(struct ust_registry_map *map)
+{
+ struct lttng_ht_iter iter;
+ struct ust_registry_event *event;
+ struct ust_registry_map_key_ht_entry *key_entry;
+ struct ust_registry_map_index_ht_entry *index_entry;
+
+ assert(map);
+
+ rcu_read_lock();
+ if (map->events_ht) {
+ /* Destroy all event associated with this registry. */
+ cds_lfht_for_each_entry(map->events_ht->ht, &iter.iter, event, node.node) {
+ /* Delete the node from the ht and free it. */
+ ust_registry_map_destroy_event(map, event);
+ }
+ }
+
+ /* Destroy all map_key entries associated with this registry. */
+ cds_lfht_for_each_entry (map->tracer_token_to_map_key_ht->ht,
+ &iter.iter, key_entry, node.node) {
+ ust_registry_map_key_entry_destroy(
+ map->tracer_token_to_map_key_ht,
+ key_entry);
+ }
+
+ /* Destroy all index entry associated with this registry. */
+ cds_lfht_for_each_entry(map->key_string_to_bucket_index_ht->ht,
+ &iter.iter, index_entry, node.node) {
+ ust_registry_map_index_ht_entry_destroy(
+ map->key_string_to_bucket_index_ht,
+ index_entry);
+ }
+ rcu_read_unlock();
+ call_rcu(&map->rcu_head, destroy_map_rcu);
+}
+
+/*
+ * Initialize registry with default values.
+ */
+int ust_registry_channel_add(struct ust_registry_session *session,
+ uint64_t key)
+{
+ int ret = 0;
+ struct ust_registry_channel *chan;
+
+ assert(session);
+
+ chan = zmalloc(sizeof(*chan));
+ if (!chan) {
+ PERROR("zmalloc ust registry channel");
+ ret = -ENOMEM;
+ goto error_alloc;
+ }
+
+ chan->ht = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
+ if (!chan->ht) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ /* Set custom match function. */
+ chan->ht->match_fct = ht_match_event;
+ chan->ht->hash_fct = ht_hash_event;
+
+ /*
+ * Assign a channel ID right now since the event notification comes
+ * *before* the channel notify so the ID needs to be set at this point so
+ * the metadata can be dumped for that event.
+ */
+ if (ust_registry_is_max_id(session->used_channel_id)) {
+ ret = -1;
+ goto error;
+ }
+ chan->chan_id = ust_registry_get_next_chan_id(session);
+
+ rcu_read_lock();
+ lttng_ht_node_init_u64(&chan->node, key);
+ lttng_ht_add_unique_u64(session->channels, &chan->node);
+ rcu_read_unlock();
+
+ return 0;
+
+error:
+ destroy_channel(chan, false);
+error_alloc:
+ return ret;
+}
+
+/*
+ * Initialize registry map entry with default values.
+ */
+int ust_registry_map_add(struct ust_registry_session *session,