Nodes that are put in a rculfhash hash table created with the
"auto resize" flag need to beware that a worker thread can access the
hash table nodes as a RCU reader concurrently, and that this worker
thread can modify the hash table content, effectively adding and
removing "bucket" nodes, and changing the size of the hash table
index.
Therefore, even though only a single thread reads and updates the hash
table, a grace period is needed before reclaiming the memory holding
the rculfhash nodes.
Moreover, handle_notification_thread_command_add_channel() misses a
RCU read-side lock around iteration on the triggers hash table. Failure
to hold this read-side lock could cause segmentation faults when
accessing hash table objects if a hash table resize is done by the
worker thread in parallel with iteration over the hash table.
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
struct cds_list_head list;
/* Node in the channel_triggers_ht */
struct cds_lfht_node channel_triggers_ht_node;
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;
struct lttng_trigger_ht_element {
struct lttng_trigger *trigger;
struct cds_lfht_node node;
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 {
};
struct lttng_condition_list_element {
const struct lttng_trigger *trigger;
struct cds_list_head list;
struct cds_lfht_node notification_trigger_ht_node;
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 {
};
struct notification_client {
struct lttng_dynamic_buffer buffer;
} outbound;
} communication;
struct lttng_dynamic_buffer buffer;
} outbound;
} communication;
+ /* call_rcu delayed reclaim. */
+ struct rcu_head rcu_node;
};
struct channel_state_sample {
};
struct channel_state_sample {
uint64_t highest_usage;
uint64_t lowest_usage;
uint64_t channel_total_consumed;
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 unsigned long hash_channel_key(struct channel_key *key);
+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)
{
static
void channel_info_destroy(struct channel_info *channel_info)
{
if (channel_info->name) {
free(channel_info->name);
}
if (channel_info->name) {
free(channel_info->name);
}
+ 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. */
}
/* Don't call directly, use the ref-counting mechanism. */
&session_info->sessions_ht_node);
rcu_read_unlock();
free(session_info->name);
&session_info->sessions_ht_node);
rcu_read_unlock();
free(session_info->name);
+ call_rcu(&session_info->rcu_node, free_session_info_rcu);
+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)
static
void notification_client_destroy(struct notification_client *client,
struct notification_thread_state *state)
}
lttng_dynamic_buffer_reset(&client->communication.inbound.buffer);
lttng_dynamic_buffer_reset(&client->communication.outbound.buffer);
}
lttng_dynamic_buffer_reset(&client->communication.inbound.buffer);
lttng_dynamic_buffer_reset(&client->communication.outbound.buffer);
+ call_rcu(&client->rcu_node, free_notification_client_rcu);
/* Build a list of all triggers applying to the new channel. */
cds_lfht_for_each_entry(state->triggers_ht, &iter, trigger_ht_element,
node) {
/* Build a list of all triggers applying to the new channel. */
cds_lfht_for_each_entry(state->triggers_ht, &iter, trigger_ht_element,
node) {
new_element = zmalloc(sizeof(*new_element));
if (!new_element) {
new_element = zmalloc(sizeof(*new_element));
if (!new_element) {
goto error;
}
CDS_INIT_LIST_HEAD(&new_element->node);
goto error;
}
CDS_INIT_LIST_HEAD(&new_element->node);
cds_list_add(&new_element->node, &trigger_list);
trigger_count++;
}
cds_list_add(&new_element->node, &trigger_list);
trigger_count++;
}
DBG("[notification-thread] Found %i triggers that apply to newly added channel",
trigger_count);
DBG("[notification-thread] Found %i triggers that apply to newly added channel",
trigger_count);
+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,
static
int handle_notification_thread_command_remove_channel(
struct notification_thread_state *state,
free(trigger_list_element);
}
cds_lfht_del(state->channel_triggers_ht, node);
free(trigger_list_element);
}
cds_lfht_del(state->channel_triggers_ht, node);
+ call_rcu(&trigger_list->rcu_node, free_channel_trigger_list_rcu);
/* Free sampled channel state. */
cds_lfht_lookup(state->channel_state_ht,
/* Free sampled channel state. */
cds_lfht_lookup(state->channel_state_ht,
channel_state_ht_node);
cds_lfht_del(state->channel_state_ht, node);
channel_state_ht_node);
cds_lfht_del(state->channel_state_ht, node);
+ call_rcu(&sample->rcu_node, free_channel_state_sample_rcu);
}
/* Remove the channel from the channels_ht and free it. */
}
/* Remove the channel from the channels_ht and free it. */
+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,
static
int handle_notification_thread_command_unregister_trigger(
struct notification_thread_state *state,
}
cds_lfht_del(state->notification_trigger_clients_ht,
&client_list->notification_trigger_ht_node);
}
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,
/* Remove trigger from triggers_ht. */
trigger_ht_element = caa_container_of(triggers_ht_node,
action = lttng_trigger_get_action(trigger_ht_element->trigger);
lttng_action_destroy(action);
lttng_trigger_destroy(trigger_ht_element->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) {
end:
rcu_read_unlock();
if (_cmd_reply) {
/* Identifier of the currently ongoing rotation. */
uint64_t id;
} rotation;
/* Identifier of the currently ongoing rotation. */
uint64_t id;
} rotation;
+ /* call_rcu delayed reclaim. */
+ struct rcu_head rcu_node;
struct cds_lfht_node channels_ht_node;
/* Node in the session_info's channels_ht. */
struct cds_lfht_node session_info_channels_ht_node;
struct cds_lfht_node channels_ht_node;
/* Node in the session_info's channels_ht. */
struct cds_lfht_node session_info_channels_ht_node;
+ /* call_rcu delayed reclaim. */
+ struct rcu_head rcu_node;
};
#endif /* NOTIFICATION_THREAD_INTERNAL_H */
};
#endif /* NOTIFICATION_THREAD_INTERNAL_H */