+static
+struct lttng_counter_transport *lttng_counter_transport_find(const char *name)
+{
+ struct lttng_counter_transport *transport;
+
+ list_for_each_entry(transport, <tng_counter_transport_list, node) {
+ if (!strcmp(transport->name, name))
+ return transport;
+ }
+ return NULL;
+}
+
+static
+struct lttng_counter *lttng_kernel_counter_create(
+ const char *counter_transport_name,
+ size_t number_dimensions, const size_t *dimensions_sizes,
+ bool coalesce_hits)
+{
+ struct lttng_counter_transport *counter_transport = NULL;
+ struct lttng_counter *counter = NULL;
+ struct lttng_event_container *container;
+
+ counter_transport = lttng_counter_transport_find(counter_transport_name);
+ if (!counter_transport) {
+ printk(KERN_WARNING "LTTng: counter transport %s not found.\n",
+ counter_transport_name);
+ goto notransport;
+ }
+ if (!try_module_get(counter_transport->owner)) {
+ printk(KERN_WARNING "LTTng: Can't lock counter transport module.\n");
+ goto notransport;
+ }
+
+ counter = lttng_kvzalloc(sizeof(struct lttng_counter), GFP_KERNEL);
+ if (!counter)
+ goto nomem;
+ container = lttng_counter_get_event_container(counter);
+ container->type = LTTNG_EVENT_CONTAINER_COUNTER;
+ container->coalesce_hits = coalesce_hits;
+ /* Create event notifier error counter. */
+ counter->ops = &counter_transport->ops;
+ counter->transport = counter_transport;
+ mutex_init(&counter->map.lock);
+
+ counter->counter = counter->ops->counter_create(
+ number_dimensions, dimensions_sizes, 0);
+ if (!counter->counter) {
+ printk(KERN_WARNING "LTTng: Error creating counter");
+ goto create_error;
+ }
+
+ return counter;
+
+create_error:
+ lttng_kvfree(counter);
+nomem:
+ if (counter_transport)
+ module_put(counter_transport->owner);
+notransport:
+ return NULL;
+}
+
+static
+void lttng_kernel_counter_destroy(struct lttng_counter *counter)
+{
+ counter->ops->counter_destroy(counter->counter);
+ module_put(counter->transport->owner);
+ lttng_kvfree(counter->map.descriptors);
+ lttng_kvfree(counter);
+}
+
+int lttng_event_notifier_group_set_error_counter(
+ struct lttng_event_notifier_group *event_notifier_group,
+ const char *counter_transport_name,
+ size_t counter_len)
+{
+ struct lttng_counter *counter;
+ int ret;
+
+ /*
+ * Lock sessions to provide mutual exclusion against concurrent
+ * modification of trigger group, which would result in
+ * overwriting the error counter if set concurrently.
+ */
+ mutex_lock(&sessions_mutex);
+
+ if (event_notifier_group->error_counter) {
+ printk(KERN_ERR "Error counter already set in event notifier group\n");
+ ret = -EBUSY;
+ goto error;
+ }
+
+ counter = lttng_kernel_counter_create(counter_transport_name,
+ 1, &counter_len, false);
+ if (!counter) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ event_notifier_group->error_counter_len = counter_len;
+ /*
+ * store-release to publish error counter matches load-acquire
+ * in record_error. Ensures the counter is created and the
+ * error_counter_len is set before they are used.
+ */
+ lttng_smp_store_release(&event_notifier_group->error_counter,
+ counter);
+
+ mutex_unlock(&sessions_mutex);
+ return 0;
+
+error:
+ mutex_unlock(&sessions_mutex);
+ return ret;
+}
+
+struct lttng_event_notifier_group *lttng_event_notifier_group_create(void)
+{
+ struct lttng_transport *transport = NULL;
+ struct lttng_event_notifier_group *event_notifier_group;
+ const char *transport_name = "relay-event-notifier";
+ size_t subbuf_size = 4096; //TODO
+ size_t num_subbuf = 16; //TODO
+ unsigned int switch_timer_interval = 0;
+ unsigned int read_timer_interval = 0;
+ int i;
+
+ mutex_lock(&sessions_mutex);
+
+ transport = lttng_transport_find(transport_name);
+ if (!transport) {
+ printk(KERN_WARNING "LTTng: transport %s not found\n",
+ transport_name);
+ goto notransport;
+ }
+ if (!try_module_get(transport->owner)) {
+ printk(KERN_WARNING "LTTng: Can't lock transport %s module.\n",
+ transport_name);
+ goto notransport;
+ }
+
+ event_notifier_group = lttng_kvzalloc(sizeof(struct lttng_event_notifier_group),
+ GFP_KERNEL);
+ if (!event_notifier_group)
+ goto nomem;
+
+ /*
+ * Initialize the ring buffer used to store event notifier
+ * notifications.
+ */
+ event_notifier_group->ops = &transport->ops;
+ event_notifier_group->chan = transport->ops.channel_create(
+ transport_name, event_notifier_group, NULL,
+ subbuf_size, num_subbuf, switch_timer_interval,
+ read_timer_interval);
+ if (!event_notifier_group->chan)
+ goto create_error;
+
+ event_notifier_group->transport = transport;
+
+ INIT_LIST_HEAD(&event_notifier_group->enablers_head);
+ INIT_LIST_HEAD(&event_notifier_group->event_notifiers_head);
+ for (i = 0; i < LTTNG_EVENT_NOTIFIER_HT_SIZE; i++)
+ INIT_HLIST_HEAD(&event_notifier_group->event_notifiers_ht.table[i]);
+
+ list_add(&event_notifier_group->node, &event_notifier_groups);
+
+ mutex_unlock(&sessions_mutex);
+
+ return event_notifier_group;
+
+create_error:
+ lttng_kvfree(event_notifier_group);
+nomem:
+ if (transport)
+ module_put(transport->owner);
+notransport:
+ mutex_unlock(&sessions_mutex);
+ return NULL;
+}
+
+struct lttng_counter *lttng_session_create_counter(
+ struct lttng_session *session,
+ const char *counter_transport_name,
+ size_t number_dimensions, const size_t *dimensions_sizes,
+ bool coalesce_hits)
+{
+ struct lttng_counter *counter;
+ struct lttng_event_container *container;
+
+ counter = lttng_kernel_counter_create(counter_transport_name,
+ number_dimensions, dimensions_sizes,
+ coalesce_hits);
+ if (!counter) {
+ goto counter_error;
+ }
+ container = lttng_counter_get_event_container(counter);
+
+ mutex_lock(&sessions_mutex);
+ container->session = session;
+ list_add(&counter->node, &session->counters);
+ mutex_unlock(&sessions_mutex);
+
+ return counter;
+
+counter_error:
+ return NULL;
+}
+