X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fnotification-thread-events.c;h=052689a0ebd8439dbf11db0172df7eb520c68605;hp=bddede988e3edb6b5f8fa98613892c542f503b9d;hb=763de384811082b26bafbce1c65d38032ef6ed30;hpb=ea9a44f03f3f6e144b5f7f312eee2a5f7a849ecb diff --git a/src/bin/lttng-sessiond/notification-thread-events.c b/src/bin/lttng-sessiond/notification-thread-events.c index bddede988..052689a0e 100644 --- a/src/bin/lttng-sessiond/notification-thread-events.c +++ b/src/bin/lttng-sessiond/notification-thread-events.c @@ -51,6 +51,13 @@ #define CLIENT_POLL_MASK_IN (LPOLLIN | LPOLLERR | LPOLLHUP | LPOLLRDHUP) #define CLIENT_POLL_MASK_IN_OUT (CLIENT_POLL_MASK_IN | LPOLLOUT) +enum lttng_object_type { + LTTNG_OBJECT_TYPE_UNKNOWN, + LTTNG_OBJECT_TYPE_NONE, + LTTNG_OBJECT_TYPE_CHANNEL, + LTTNG_OBJECT_TYPE_SESSION, +}; + struct lttng_trigger_list_element { /* No ownership of the trigger object is assumed. */ const struct lttng_trigger *trigger; @@ -63,6 +70,8 @@ struct lttng_channel_trigger_list { struct cds_list_head list; /* Node in the channel_triggers_ht */ struct cds_lfht_node channel_triggers_ht_node; + /* call_rcu delayed reclaim. */ + struct rcu_head rcu_node; }; /* @@ -109,6 +118,8 @@ struct lttng_session_trigger_list { struct lttng_trigger_ht_element { struct lttng_trigger *trigger; struct cds_lfht_node node; + /* call_rcu delayed reclaim. */ + struct rcu_head rcu_node; }; struct lttng_condition_list_element { @@ -122,9 +133,11 @@ struct notification_client_list_element { }; struct notification_client_list { - struct lttng_trigger *trigger; + const struct lttng_trigger *trigger; struct cds_list_head list; struct cds_lfht_node notification_trigger_ht_node; + /* call_rcu delayed reclaim. */ + struct rcu_head rcu_node; }; struct notification_client { @@ -191,6 +204,8 @@ struct notification_client { struct lttng_dynamic_buffer buffer; } outbound; } communication; + /* call_rcu delayed reclaim. */ + struct rcu_head rcu_node; }; struct channel_state_sample { @@ -199,10 +214,12 @@ struct channel_state_sample { uint64_t highest_usage; uint64_t lowest_usage; uint64_t channel_total_consumed; + /* call_rcu delayed reclaim. */ + struct rcu_head rcu_node; }; static unsigned long hash_channel_key(struct channel_key *key); -static int evaluate_condition(const struct lttng_condition *condition, +static int evaluate_buffer_condition(const struct lttng_condition *condition, struct lttng_evaluation **evaluation, const struct notification_thread_state *state, const struct channel_state_sample *previous_sample, @@ -228,7 +245,8 @@ void session_info_put(struct session_info *session_info); static struct session_info *session_info_create(const char *name, uid_t uid, gid_t gid, - struct lttng_session_trigger_list *trigger_list); + struct lttng_session_trigger_list *trigger_list, + struct cds_lfht *sessions_ht); static void session_info_add_channel(struct session_info *session_info, struct channel_info *channel_info); @@ -279,6 +297,18 @@ int match_channel_trigger_list(struct cds_lfht_node *node, const void *key) (channel_key->domain == trigger_list->channel_key.domain)); } +static +int match_session_trigger_list(struct cds_lfht_node *node, const void *key) +{ + const char *session_name = (const char *) key; + struct lttng_session_trigger_list *trigger_list; + + trigger_list = caa_container_of(node, struct lttng_session_trigger_list, + session_triggers_ht_node); + + return !!(strcmp(trigger_list->session_name, session_name) == 0); +} + static int match_channel_state_sample(struct cds_lfht_node *node, const void *key) { @@ -321,37 +351,29 @@ int match_condition(struct cds_lfht_node *node, const void *key) } static -int match_client_list(struct cds_lfht_node *node, const void *key) +int match_client_list_condition(struct cds_lfht_node *node, const void *key) { - struct lttng_trigger *trigger_key = (struct lttng_trigger *) key; + struct lttng_condition *condition_key = (struct lttng_condition *) key; struct notification_client_list *client_list; - struct lttng_condition *condition; - struct lttng_condition *condition_key = lttng_trigger_get_condition( - trigger_key); + const struct lttng_condition *condition; assert(condition_key); client_list = caa_container_of(node, struct notification_client_list, notification_trigger_ht_node); - condition = lttng_trigger_get_condition(client_list->trigger); + condition = lttng_trigger_get_const_condition(client_list->trigger); return !!lttng_condition_is_equal(condition_key, condition); } static -int match_client_list_condition(struct cds_lfht_node *node, const void *key) +int match_session(struct cds_lfht_node *node, const void *key) { - struct lttng_condition *condition_key = (struct lttng_condition *) key; - struct notification_client_list *client_list; - struct lttng_condition *condition; - - assert(condition_key); - - client_list = caa_container_of(node, struct notification_client_list, - notification_trigger_ht_node); - condition = lttng_trigger_get_condition(client_list->trigger); + const char *name = key; + struct session_info *session_info = caa_container_of( + node, struct session_info, sessions_ht_node); - return !!lttng_condition_is_equal(condition_key, condition); + return !strcmp(session_info->name, name); } static @@ -414,6 +436,21 @@ unsigned long lttng_condition_session_consumed_size_hash( return hash; } +static +unsigned long lttng_condition_session_rotation_hash( + const struct lttng_condition *_condition) +{ + unsigned long hash, condition_type; + struct lttng_condition_session_rotation *condition; + + condition = container_of(_condition, + struct lttng_condition_session_rotation, parent); + condition_type = (unsigned long) condition->parent.type; + hash = hash_key_ulong((void *) condition_type, lttng_ht_seed); + assert(condition->session_name); + hash ^= hash_key_str(condition->session_name, lttng_ht_seed); + return hash; +} /* * The lttng_condition hashing code is kept in this file (rather than @@ -429,6 +466,9 @@ unsigned long lttng_condition_hash(const struct lttng_condition *condition) return lttng_condition_buffer_usage_hash(condition); case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: return lttng_condition_session_consumed_size_hash(condition); + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + return lttng_condition_session_rotation_hash(condition); default: ERR("[notification-thread] Unexpected condition type caught"); abort(); @@ -445,6 +485,37 @@ unsigned long hash_channel_key(struct channel_key *key) return key_hash ^ domain_hash; } +/* + * Get the type of object to which a given condition applies. Bindings let + * the notification system evaluate a trigger's condition when a given + * object's state is updated. + * + * For instance, a condition bound to a channel will be evaluated everytime + * the channel's state is changed by a channel monitoring sample. + */ +static +enum lttng_object_type get_condition_binding_object( + const struct lttng_condition *condition) +{ + switch (lttng_condition_get_type(condition)) { + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + return LTTNG_OBJECT_TYPE_CHANNEL; + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + return LTTNG_OBJECT_TYPE_SESSION; + default: + return LTTNG_OBJECT_TYPE_UNKNOWN; + } +} + +static +void free_channel_info_rcu(struct rcu_head *node) +{ + free(caa_container_of(node, struct channel_info, rcu_node)); +} + static void channel_info_destroy(struct channel_info *channel_info) { @@ -460,7 +531,13 @@ void channel_info_destroy(struct channel_info *channel_info) if (channel_info->name) { free(channel_info->name); } - free(channel_info); + call_rcu(&channel_info->rcu_node, free_channel_info_rcu); +} + +static +void free_session_info_rcu(struct rcu_head *node) +{ + free(caa_container_of(node, struct session_info, rcu_node)); } /* Don't call directly, use the ref-counting mechanism. */ @@ -478,8 +555,13 @@ void session_info_destroy(void *_data) } } lttng_session_trigger_list_destroy(session_info->trigger_list); + + rcu_read_lock(); + cds_lfht_del(session_info->sessions_ht, + &session_info->sessions_ht_node); + rcu_read_unlock(); free(session_info->name); - free(session_info); + call_rcu(&session_info->rcu_node, free_session_info_rcu); } static @@ -502,7 +584,8 @@ void session_info_put(struct session_info *session_info) static struct session_info *session_info_create(const char *name, uid_t uid, gid_t gid, - struct lttng_session_trigger_list *trigger_list) + struct lttng_session_trigger_list *trigger_list, + struct cds_lfht *sessions_ht) { struct session_info *session_info; @@ -528,6 +611,7 @@ struct session_info *session_info_create(const char *name, uid_t uid, gid_t gid, session_info->uid = uid; session_info->gid = gid; session_info->trigger_list = trigger_list; + session_info->sessions_ht = sessions_ht; end: return session_info; error: @@ -592,12 +676,34 @@ error: return NULL; } +/* RCU read lock must be held by the caller. */ +static +struct notification_client_list *get_client_list_from_condition( + struct notification_thread_state *state, + const struct lttng_condition *condition) +{ + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + + cds_lfht_lookup(state->notification_trigger_clients_ht, + lttng_condition_hash(condition), + match_client_list_condition, + condition, + &iter); + node = cds_lfht_iter_get_node(&iter); + + return node ? caa_container_of(node, + struct notification_client_list, + notification_trigger_ht_node) : NULL; +} + /* This function must be called with the RCU read lock held. */ static -int evaluate_condition_for_client(struct lttng_trigger *trigger, - struct lttng_condition *condition, - struct notification_client *client, - struct notification_thread_state *state) +int evaluate_channel_condition_for_client( + const struct lttng_condition *condition, + struct notification_thread_state *state, + struct lttng_evaluation **evaluation, + uid_t *session_uid, gid_t *session_gid) { int ret; struct cds_lfht_iter iter; @@ -606,23 +712,15 @@ int evaluate_condition_for_client(struct lttng_trigger *trigger, struct channel_key *channel_key = NULL; struct channel_state_sample *last_sample = NULL; struct lttng_channel_trigger_list *channel_trigger_list = NULL; - struct lttng_evaluation *evaluation = NULL; - struct notification_client_list client_list = { 0 }; - struct notification_client_list_element client_list_element = { 0 }; - assert(trigger); - assert(condition); - assert(client); - assert(state); - - /* Find the channel associated with the trigger. */ + /* Find the channel associated with the condition. */ cds_lfht_for_each_entry(state->channel_triggers_ht, &iter, - channel_trigger_list , channel_triggers_ht_node) { + channel_trigger_list, channel_triggers_ht_node) { struct lttng_trigger_list_element *element; cds_list_for_each_entry(element, &channel_trigger_list->list, node) { const struct lttng_condition *current_condition = - lttng_trigger_get_const_condition( + lttng_trigger_get_const_condition( element->trigger); assert(current_condition); @@ -643,7 +741,7 @@ int evaluate_condition_for_client(struct lttng_trigger *trigger, if (!channel_key){ /* No channel found; normal exit. */ - DBG("[notification-thread] No channel associated with newly subscribed-to condition"); + DBG("[notification-thread] No known channel associated with newly subscribed-to condition"); ret = 0; goto end; } @@ -677,7 +775,7 @@ int evaluate_condition_for_client(struct lttng_trigger *trigger, goto end; } - ret = evaluate_condition(condition, &evaluation, state, + ret = evaluate_buffer_condition(condition, evaluation, state, NULL, last_sample, 0, channel_info->session_info->consumed_data_size, channel_info); @@ -686,6 +784,151 @@ int evaluate_condition_for_client(struct lttng_trigger *trigger, goto end; } + *session_uid = channel_info->session_info->uid; + *session_gid = channel_info->session_info->gid; +end: + return ret; +} + +static +const char *get_condition_session_name(const struct lttng_condition *condition) +{ + const char *session_name = NULL; + enum lttng_condition_status status; + + switch (lttng_condition_get_type(condition)) { + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + status = lttng_condition_buffer_usage_get_session_name( + condition, &session_name); + break; + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + status = lttng_condition_session_consumed_size_get_session_name( + condition, &session_name); + break; + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + status = lttng_condition_session_rotation_get_session_name( + condition, &session_name); + break; + default: + abort(); + } + if (status != LTTNG_CONDITION_STATUS_OK) { + ERR("[notification-thread] Failed to retrieve session rotation condition's session name"); + goto end; + } +end: + return session_name; +} + +/* This function must be called with the RCU read lock held. */ +static +int evaluate_session_condition_for_client( + const struct lttng_condition *condition, + struct notification_thread_state *state, + struct lttng_evaluation **evaluation, + uid_t *session_uid, gid_t *session_gid) +{ + int ret; + struct cds_lfht_iter iter; + struct cds_lfht_node *node; + const char *session_name; + struct session_info *session_info = NULL; + + session_name = get_condition_session_name(condition); + + /* Find the session associated with the trigger. */ + cds_lfht_lookup(state->sessions_ht, + hash_key_str(session_name, lttng_ht_seed), + match_session, + session_name, + &iter); + node = cds_lfht_iter_get_node(&iter); + if (!node) { + DBG("[notification-thread] No known session matching name \"%s\"", + session_name); + ret = 0; + goto end; + } + + session_info = caa_container_of(node, struct session_info, + sessions_ht_node); + session_info_get(session_info); + + /* + * Evaluation is performed in-line here since only one type of + * session-bound condition is handled for the moment. + */ + switch (lttng_condition_get_type(condition)) { + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + if (!session_info->rotation.ongoing) { + ret = 0; + goto end_session_put; + } + + *evaluation = lttng_evaluation_session_rotation_ongoing_create( + session_info->rotation.id); + if (!*evaluation) { + /* Fatal error. */ + ERR("[notification-thread] Failed to create session rotation ongoing evaluation for session \"%s\"", + session_info->name); + ret = -1; + goto end_session_put; + } + ret = 0; + break; + default: + ret = 0; + goto end_session_put; + } + + *session_uid = session_info->uid; + *session_gid = session_info->gid; + +end_session_put: + session_info_put(session_info); +end: + return ret; +} + +/* This function must be called with the RCU read lock held. */ +static +int evaluate_condition_for_client(const struct lttng_trigger *trigger, + const struct lttng_condition *condition, + struct notification_client *client, + struct notification_thread_state *state) +{ + int ret; + struct lttng_evaluation *evaluation = NULL; + struct notification_client_list client_list = { 0 }; + struct notification_client_list_element client_list_element = { 0 }; + uid_t object_uid = 0; + gid_t object_gid = 0; + + assert(trigger); + assert(condition); + assert(client); + assert(state); + + switch (get_condition_binding_object(condition)) { + case LTTNG_OBJECT_TYPE_SESSION: + ret = evaluate_session_condition_for_client(condition, state, + &evaluation, &object_uid, &object_gid); + break; + case LTTNG_OBJECT_TYPE_CHANNEL: + ret = evaluate_channel_condition_for_client(condition, state, + &evaluation, &object_uid, &object_gid); + break; + case LTTNG_OBJECT_TYPE_NONE: + ret = 0; + goto end; + case LTTNG_OBJECT_TYPE_UNKNOWN: + default: + ret = -1; + goto end; + } + if (!evaluation) { /* Evaluation yielded nothing. Normal exit. */ DBG("[notification-thread] Newly subscribed-to condition evaluated to false, nothing to report to client"); @@ -708,8 +951,7 @@ int evaluate_condition_for_client(struct lttng_trigger *trigger, /* Send evaluation result to the newly-subscribed client. */ DBG("[notification-thread] Newly subscribed-to condition evaluated to true, notifying client"); ret = send_evaluation_to_clients(trigger, evaluation, &client_list, - state, channel_info->session_info->uid, - channel_info->session_info->gid); + state, object_uid, object_gid); end: return ret; @@ -722,8 +964,6 @@ int notification_thread_client_subscribe(struct notification_client *client, enum lttng_notification_channel_status *_status) { int ret = 0; - struct cds_lfht_iter iter; - struct cds_lfht_node *node; struct notification_client_list *client_list; struct lttng_condition_list_element *condition_list_element = NULL; struct notification_client_list_element *client_list_element = NULL; @@ -762,13 +1002,8 @@ int notification_thread_client_subscribe(struct notification_client *client, condition_list_element->condition = condition; cds_list_add(&condition_list_element->node, &client->condition_list); - cds_lfht_lookup(state->notification_trigger_clients_ht, - lttng_condition_hash(condition), - match_client_list_condition, - condition, - &iter); - node = cds_lfht_iter_get_node(&iter); - if (!node) { + client_list = get_client_list_from_condition(state, condition); + if (!client_list) { /* * No notification-emiting trigger registered with this * condition. We don't evaluate the condition right away @@ -778,8 +1013,6 @@ int notification_thread_client_subscribe(struct notification_client *client, goto end_unlock; } - client_list = caa_container_of(node, struct notification_client_list, - notification_trigger_ht_node); /* * The condition to which the client just subscribed is evaluated * at this point so that conditions that are already TRUE result @@ -821,8 +1054,6 @@ int notification_thread_client_unsubscribe( struct notification_thread_state *state, enum lttng_notification_channel_status *_status) { - struct cds_lfht_iter iter; - struct cds_lfht_node *node; struct notification_client_list *client_list; struct lttng_condition_list_element *condition_list_element, *condition_tmp; @@ -865,18 +1096,11 @@ int notification_thread_client_unsubscribe( * matching the condition. */ rcu_read_lock(); - cds_lfht_lookup(state->notification_trigger_clients_ht, - lttng_condition_hash(condition), - match_client_list_condition, - condition, - &iter); - node = cds_lfht_iter_get_node(&iter); - if (!node) { + client_list = get_client_list_from_condition(state, condition); + if (!client_list) { goto end_unlock; } - client_list = caa_container_of(node, struct notification_client_list, - notification_trigger_ht_node); cds_list_for_each_entry_safe(client_list_element, client_tmp, &client_list->list, node) { if (client_list_element->client->socket != client->socket) { @@ -896,6 +1120,12 @@ end: return 0; } +static +void free_notification_client_rcu(struct rcu_head *node) +{ + free(caa_container_of(node, struct notification_client, rcu_node)); +} + static void notification_client_destroy(struct notification_client *client, struct notification_thread_state *state) @@ -918,7 +1148,7 @@ void notification_client_destroy(struct notification_client *client, } lttng_dynamic_buffer_reset(&client->communication.inbound.buffer); lttng_dynamic_buffer_reset(&client->communication.outbound.buffer); - free(client); + call_rcu(&client->rcu_node, free_notification_client_rcu); } /* @@ -951,8 +1181,8 @@ end: static bool buffer_usage_condition_applies_to_channel( - struct lttng_condition *condition, - struct channel_info *channel_info) + const struct lttng_condition *condition, + const struct channel_info *channel_info) { enum lttng_condition_status status; enum lttng_domain_type condition_domain; @@ -988,8 +1218,8 @@ fail: static bool session_consumed_size_condition_applies_to_channel( - struct lttng_condition *condition, - struct channel_info *channel_info) + const struct lttng_condition *condition, + const struct channel_info *channel_info) { enum lttng_condition_status status; const char *condition_session_name = NULL; @@ -1008,13 +1238,13 @@ fail: } static -bool trigger_applies_to_channel(struct lttng_trigger *trigger, - struct channel_info *channel_info) +bool trigger_applies_to_channel(const struct lttng_trigger *trigger, + const struct channel_info *channel_info) { - struct lttng_condition *condition; + const struct lttng_condition *condition; bool trigger_applies; - condition = lttng_trigger_get_condition(trigger); + condition = lttng_trigger_get_const_condition(trigger); if (!condition) { goto fail; } @@ -1057,14 +1287,37 @@ bool trigger_applies_to_client(struct lttng_trigger *trigger, return applies; } +/* Must be called with RCU read lock held. */ static -int match_session(struct cds_lfht_node *node, const void *key) +struct lttng_session_trigger_list *get_session_trigger_list( + struct notification_thread_state *state, + const char *session_name) { - const char *name = key; - struct session_info *session_info = caa_container_of( - node, struct session_info, sessions_ht_node); + struct lttng_session_trigger_list *list = NULL; + struct cds_lfht_node *node; + struct cds_lfht_iter iter; - return !strcmp(session_info->name, name); + cds_lfht_lookup(state->session_triggers_ht, + hash_key_str(session_name, lttng_ht_seed), + match_session_trigger_list, + session_name, + &iter); + node = cds_lfht_iter_get_node(&iter); + if (!node) { + /* + * Not an error, the list of triggers applying to that session + * will be initialized when the session is created. + */ + DBG("[notification-thread] No trigger list found for session \"%s\" as it is not yet known to the notification system", + session_name); + goto end; + } + + list = caa_container_of(node, + struct lttng_session_trigger_list, + session_triggers_ht_node); +end: + return list; } /* @@ -1251,7 +1504,8 @@ struct session_info *find_or_create_session_info( sessions_ht_node); assert(session->uid == uid); assert(session->gid == gid); - goto error; + session_info_get(session); + goto end; } trigger_list = lttng_session_trigger_list_build(state, name); @@ -1259,16 +1513,19 @@ struct session_info *find_or_create_session_info( goto error; } - session = session_info_create(name, uid, gid, trigger_list); + session = session_info_create(name, uid, gid, trigger_list, + state->sessions_ht); if (!session) { ERR("[notification-thread] Failed to allocation session info for session \"%s\" (uid = %i, gid = %i)", name, uid, gid); + lttng_session_trigger_list_destroy(trigger_list); goto error; } trigger_list = NULL; cds_lfht_add(state->sessions_ht, hash_key_str(name, lttng_ht_seed), &session->sessions_ht_node); +end: rcu_read_unlock(); return session; error: @@ -1306,7 +1563,7 @@ int handle_notification_thread_command_add_channel( session_info = find_or_create_session_info(state, session_name, session_uid, session_gid); if (!session_info) { - /* Allocation error or an internal error occured. */ + /* Allocation error or an internal error occurred. */ goto error; } @@ -1316,6 +1573,7 @@ int handle_notification_thread_command_add_channel( goto error; } + rcu_read_lock(); /* Build a list of all triggers applying to the new channel. */ cds_lfht_for_each_entry(state->triggers_ht, &iter, trigger_ht_element, node) { @@ -1328,6 +1586,7 @@ int handle_notification_thread_command_add_channel( new_element = zmalloc(sizeof(*new_element)); if (!new_element) { + rcu_read_unlock(); goto error; } CDS_INIT_LIST_HEAD(&new_element->node); @@ -1335,6 +1594,7 @@ int handle_notification_thread_command_add_channel( cds_list_add(&new_element->node, &trigger_list); trigger_count++; } + rcu_read_unlock(); DBG("[notification-thread] Found %i triggers that apply to newly added channel", trigger_count); @@ -1360,6 +1620,7 @@ int handle_notification_thread_command_add_channel( hash_channel_key(&new_channel_info->key), &channel_trigger_list->channel_triggers_ht_node); rcu_read_unlock(); + session_info_put(session_info); *cmd_result = LTTNG_OK; return 0; error: @@ -1368,6 +1629,20 @@ error: return 1; } +static +void free_channel_trigger_list_rcu(struct rcu_head *node) +{ + free(caa_container_of(node, struct lttng_channel_trigger_list, + rcu_node)); +} + +static +void free_channel_state_sample_rcu(struct rcu_head *node) +{ + free(caa_container_of(node, struct channel_state_sample, + rcu_node)); +} + static int handle_notification_thread_command_remove_channel( struct notification_thread_state *state, @@ -1410,7 +1685,7 @@ int handle_notification_thread_command_remove_channel( free(trigger_list_element); } cds_lfht_del(state->channel_triggers_ht, node); - free(trigger_list); + call_rcu(&trigger_list->rcu_node, free_channel_trigger_list_rcu); /* Free sampled channel state. */ cds_lfht_lookup(state->channel_state_ht, @@ -1429,7 +1704,7 @@ int handle_notification_thread_command_remove_channel( channel_state_ht_node); cds_lfht_del(state->channel_state_ht, node); - free(sample); + call_rcu(&sample->rcu_node, free_channel_state_sample_rcu); } /* Remove the channel from the channels_ht and free it. */ @@ -1450,6 +1725,113 @@ end: return 0; } +static +int handle_notification_thread_command_session_rotation( + struct notification_thread_state *state, + enum notification_thread_command_type cmd_type, + const char *session_name, uid_t session_uid, gid_t session_gid, + uint64_t trace_archive_chunk_id, + struct lttng_trace_archive_location *location, + enum lttng_error_code *_cmd_result) +{ + int ret = 0; + enum lttng_error_code cmd_result = LTTNG_OK; + struct lttng_session_trigger_list *trigger_list; + struct lttng_trigger_list_element *trigger_list_element; + struct session_info *session_info; + + rcu_read_lock(); + + session_info = find_or_create_session_info(state, session_name, + session_uid, session_gid); + if (!session_info) { + /* Allocation error or an internal error occurred. */ + ret = -1; + cmd_result = LTTNG_ERR_NOMEM; + goto end; + } + + session_info->rotation.ongoing = + cmd_type == NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_ONGOING; + session_info->rotation.id = trace_archive_chunk_id; + trigger_list = get_session_trigger_list(state, session_name); + if (!trigger_list) { + DBG("[notification-thread] No triggers applying to session \"%s\" found", + session_name); + goto end; + } + + cds_list_for_each_entry(trigger_list_element, &trigger_list->list, + node) { + const struct lttng_condition *condition; + const struct lttng_action *action; + const struct lttng_trigger *trigger; + struct notification_client_list *client_list; + struct lttng_evaluation *evaluation = NULL; + enum lttng_condition_type condition_type; + + trigger = trigger_list_element->trigger; + condition = lttng_trigger_get_const_condition(trigger); + assert(condition); + condition_type = lttng_condition_get_type(condition); + + if (condition_type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING && + cmd_type != NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_ONGOING) { + continue; + } else if (condition_type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED && + cmd_type != NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_COMPLETED) { + continue; + } + + action = lttng_trigger_get_const_action(trigger); + + /* Notify actions are the only type currently supported. */ + assert(lttng_action_get_type_const(action) == + LTTNG_ACTION_TYPE_NOTIFY); + + client_list = get_client_list_from_condition(state, condition); + assert(client_list); + + if (cds_list_empty(&client_list->list)) { + /* + * No clients interested in the evaluation's result, + * skip it. + */ + continue; + } + + if (cmd_type == NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_ONGOING) { + evaluation = lttng_evaluation_session_rotation_ongoing_create( + trace_archive_chunk_id); + } else { + evaluation = lttng_evaluation_session_rotation_completed_create( + trace_archive_chunk_id, location); + } + + if (!evaluation) { + /* Internal error */ + ret = -1; + cmd_result = LTTNG_ERR_UNK; + goto end; + } + + /* Dispatch evaluation result to all clients. */ + ret = send_evaluation_to_clients(trigger_list_element->trigger, + evaluation, client_list, state, + session_info->uid, + session_info->gid); + lttng_evaluation_destroy(evaluation); + if (caa_unlikely(ret)) { + goto end; + } + } +end: + session_info_put(session_info); + *_cmd_result = cmd_result; + rcu_read_unlock(); + return ret; +} + static int condition_is_supported(struct lttng_condition *condition) { @@ -1489,13 +1871,105 @@ end: return ret; } +/* Must be called with RCU read lock held. */ +static +int bind_trigger_to_matching_session(const struct lttng_trigger *trigger, + struct notification_thread_state *state) +{ + int ret = 0; + const struct lttng_condition *condition; + const char *session_name; + struct lttng_session_trigger_list *trigger_list; + + condition = lttng_trigger_get_const_condition(trigger); + switch (lttng_condition_get_type(condition)) { + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + { + enum lttng_condition_status status; + + status = lttng_condition_session_rotation_get_session_name( + condition, &session_name); + if (status != LTTNG_CONDITION_STATUS_OK) { + ERR("[notification-thread] Failed to bind trigger to session: unable to get 'session_rotation' condition's session name"); + ret = -1; + goto end; + } + break; + } + default: + ret = -1; + goto end; + } + + trigger_list = get_session_trigger_list(state, session_name); + if (!trigger_list) { + DBG("[notification-thread] Unable to bind trigger applying to session \"%s\" as it is not yet known to the notification system", + session_name); + goto end; + + } + + DBG("[notification-thread] Newly registered trigger bound to session \"%s\"", + session_name); + ret = lttng_session_trigger_list_add(trigger_list, trigger); +end: + return ret; +} + +/* Must be called with RCU read lock held. */ +static +int bind_trigger_to_matching_channels(const struct lttng_trigger *trigger, + struct notification_thread_state *state) +{ + int ret = 0; + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + struct channel_info *channel; + + cds_lfht_for_each_entry(state->channels_ht, &iter, channel, + channels_ht_node) { + struct lttng_trigger_list_element *trigger_list_element; + struct lttng_channel_trigger_list *trigger_list; + struct cds_lfht_iter lookup_iter; + + if (!trigger_applies_to_channel(trigger, channel)) { + continue; + } + + cds_lfht_lookup(state->channel_triggers_ht, + hash_channel_key(&channel->key), + match_channel_trigger_list, + &channel->key, + &lookup_iter); + node = cds_lfht_iter_get_node(&lookup_iter); + assert(node); + trigger_list = caa_container_of(node, + struct lttng_channel_trigger_list, + channel_triggers_ht_node); + + trigger_list_element = zmalloc(sizeof(*trigger_list_element)); + if (!trigger_list_element) { + ret = -1; + goto end; + } + CDS_INIT_LIST_HEAD(&trigger_list_element->node); + trigger_list_element->trigger = trigger; + cds_list_add(&trigger_list_element->node, &trigger_list->list); + DBG("[notification-thread] Newly registered trigger bound to channel \"%s\"", + channel->name); + } +end: + return ret; +} + /* * FIXME A client's credentials are not checked when registering a trigger, nor * are they stored alongside with the trigger. * * The effects of this are benign since: * - The client will succeed in registering the trigger, as it is valid, - * - The trigger will, internally, be bound to the channel, + * - The trigger will, internally, be bound to the channel/session, * - The notifications will not be sent since the client's credentials * are checked against the channel at that moment. * @@ -1519,7 +1993,6 @@ int handle_notification_thread_command_register_trigger( struct notification_client_list_element *client_list_element, *tmp; struct cds_lfht_node *node; struct cds_lfht_iter iter; - struct channel_info *channel; bool free_trigger = true; rcu_read_lock(); @@ -1601,38 +2074,30 @@ int handle_notification_thread_command_register_trigger( lttng_condition_hash(condition), &client_list->notification_trigger_ht_node); - /* - * Add the trigger to list of triggers bound to the channels currently - * known. - */ - cds_lfht_for_each_entry(state->channels_ht, &iter, channel, - channels_ht_node) { - struct lttng_trigger_list_element *trigger_list_element; - struct lttng_channel_trigger_list *trigger_list; - - if (!trigger_applies_to_channel(trigger, channel)) { - continue; + switch (get_condition_binding_object(condition)) { + case LTTNG_OBJECT_TYPE_SESSION: + /* Add the trigger to the list if it matches a known session. */ + ret = bind_trigger_to_matching_session(trigger, state); + if (ret) { + goto error_free_client_list; } - - cds_lfht_lookup(state->channel_triggers_ht, - hash_channel_key(&channel->key), - match_channel_trigger_list, - &channel->key, - &iter); - node = cds_lfht_iter_get_node(&iter); - assert(node); - trigger_list = caa_container_of(node, - struct lttng_channel_trigger_list, - channel_triggers_ht_node); - - trigger_list_element = zmalloc(sizeof(*trigger_list_element)); - if (!trigger_list_element) { - ret = -1; + break; + case LTTNG_OBJECT_TYPE_CHANNEL: + /* + * Add the trigger to list of triggers bound to the channels + * currently known. + */ + ret = bind_trigger_to_matching_channels(trigger, state); + if (ret) { goto error_free_client_list; } - CDS_INIT_LIST_HEAD(&trigger_list_element->node); - trigger_list_element->trigger = trigger; - cds_list_add(&trigger_list_element->node, &trigger_list->list); + break; + case LTTNG_OBJECT_TYPE_NONE: + break; + default: + ERR("[notification-thread] Unknown object type on which to bind a newly registered trigger was encountered"); + ret = -1; + goto error_free_client_list; } /* @@ -1699,6 +2164,20 @@ error: return ret; } +static +void free_notification_client_list_rcu(struct rcu_head *node) +{ + free(caa_container_of(node, struct notification_client_list, + rcu_node)); +} + +static +void free_lttng_trigger_ht_element_rcu(struct rcu_head *node) +{ + free(caa_container_of(node, struct lttng_trigger_ht_element, + rcu_node)); +} + static int handle_notification_thread_command_unregister_trigger( struct notification_thread_state *state, @@ -1706,7 +2185,7 @@ int handle_notification_thread_command_unregister_trigger( enum lttng_error_code *_cmd_reply) { struct cds_lfht_iter iter; - struct cds_lfht_node *node, *triggers_ht_node; + struct cds_lfht_node *triggers_ht_node; struct lttng_channel_trigger_list *trigger_list; struct notification_client_list *client_list; struct notification_client_list_element *client_list_element, *tmp; @@ -1759,21 +2238,16 @@ int handle_notification_thread_command_unregister_trigger( * Remove and release the client list from * notification_trigger_clients_ht. */ - cds_lfht_lookup(state->notification_trigger_clients_ht, - lttng_condition_hash(condition), - match_client_list, - trigger, - &iter); - node = cds_lfht_iter_get_node(&iter); - assert(node); - client_list = caa_container_of(node, struct notification_client_list, - notification_trigger_ht_node); + client_list = get_client_list_from_condition(state, condition); + assert(client_list); + cds_list_for_each_entry_safe(client_list_element, tmp, &client_list->list, node) { free(client_list_element); } - cds_lfht_del(state->notification_trigger_clients_ht, node); - free(client_list); + cds_lfht_del(state->notification_trigger_clients_ht, + &client_list->notification_trigger_ht_node); + call_rcu(&client_list->rcu_node, free_notification_client_list_rcu); /* Remove trigger from triggers_ht. */ trigger_ht_element = caa_container_of(triggers_ht_node, @@ -1785,7 +2259,7 @@ int handle_notification_thread_command_unregister_trigger( action = lttng_trigger_get_action(trigger_ht_element->trigger); lttng_action_destroy(action); lttng_trigger_destroy(trigger_ht_element->trigger); - free(trigger_ht_element); + call_rcu(&trigger_ht_element->rcu_node, free_lttng_trigger_ht_element_rcu); end: rcu_read_unlock(); if (_cmd_reply) { @@ -1804,9 +2278,9 @@ int handle_notification_thread_command( struct notification_thread_command *cmd; /* Read the event pipe to put it back into a quiescent state. */ - ret = read(lttng_pipe_get_readfd(handle->cmd_queue.event_pipe), &counter, + ret = lttng_read(lttng_pipe_get_readfd(handle->cmd_queue.event_pipe), &counter, sizeof(counter)); - if (ret == -1) { + if (ret != sizeof(counter)) { goto error; } @@ -1846,6 +2320,21 @@ int handle_notification_thread_command( cmd->parameters.remove_channel.domain, &cmd->reply_code); break; + case NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_ONGOING: + case NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_COMPLETED: + DBG("[notification-thread] Received session rotation %s command", + cmd->type == NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_ONGOING ? + "ongoing" : "completed"); + ret = handle_notification_thread_command_session_rotation( + state, + cmd->type, + cmd->parameters.session_rotation.session_name, + cmd->parameters.session_rotation.uid, + cmd->parameters.session_rotation.gid, + cmd->parameters.session_rotation.trace_archive_chunk_id, + cmd->parameters.session_rotation.location, + &cmd->reply_code); + break; case NOTIFICATION_COMMAND_TYPE_QUIT: DBG("[notification-thread] Received quit command"); cmd->reply_code = LTTNG_OK; @@ -2055,6 +2544,7 @@ int handle_notification_thread_trigger_unregister_all( struct cds_lfht_iter iter; struct lttng_trigger_ht_element *trigger_ht_element; + rcu_read_lock(); cds_lfht_for_each_entry(state->triggers_ht, &iter, trigger_ht_element, node) { int ret = handle_notification_thread_command_unregister_trigger( @@ -2063,6 +2553,7 @@ int handle_notification_thread_trigger_unregister_all( error_occurred = true; } } + rcu_read_unlock(); return error_occurred ? -1 : 0; } @@ -2520,7 +3011,7 @@ bool evaluate_session_consumed_size_condition( } static -int evaluate_condition(const struct lttng_condition *condition, +int evaluate_buffer_condition(const struct lttng_condition *condition, struct lttng_evaluation **evaluation, const struct notification_thread_state *state, const struct channel_state_sample *previous_sample, @@ -2866,17 +3357,8 @@ int handle_notification_thread_channel_sample( * Check if any client is subscribed to the result of this * evaluation. */ - cds_lfht_lookup(state->notification_trigger_clients_ht, - lttng_condition_hash(condition), - match_client_list, - trigger, - &iter); - node = cds_lfht_iter_get_node(&iter); - assert(node); - - client_list = caa_container_of(node, - struct notification_client_list, - notification_trigger_ht_node); + client_list = get_client_list_from_condition(state, condition); + assert(client_list); if (cds_list_empty(&client_list->list)) { /* * No clients interested in the evaluation's result, @@ -2885,7 +3367,7 @@ int handle_notification_thread_channel_sample( continue; } - ret = evaluate_condition(condition, &evaluation, state, + ret = evaluate_buffer_condition(condition, &evaluation, state, previous_sample_available ? &previous_sample : NULL, &latest_sample, previous_session_consumed_total,