/*
* Destroy a buffer registry session with the given domain.
+ *
+ * Should *NOT* be called with RCU read-side lock held.
*/
-void buffer_reg_session_destroy(struct buffer_reg_session *regp,
+static void buffer_reg_session_destroy(struct buffer_reg_session *regp,
enum lttng_domain_type domain)
{
int ret;
assert(!ret);
buffer_reg_channel_destroy(reg_chan, domain);
}
- lttng_ht_destroy(regp->channels);
rcu_read_unlock();
+ lttng_ht_destroy(regp->channels);
+
switch (domain) {
case LTTNG_DOMAIN_UST:
ust_registry_session_destroy(regp->reg.ust);
}
/*
- * Remove buffer registry UID object from the global hash table. RCU read side
- * lock MUST be acquired before calling this.
+ * Remove buffer registry UID object from the global hash table.
*/
void buffer_reg_uid_remove(struct buffer_reg_uid *regp)
{
assert(regp);
+ rcu_read_lock();
iter.iter.node = ®p->node.node;
ret = lttng_ht_del(buffer_registry_uid, &iter);
assert(!ret);
+ rcu_read_unlock();
}
static void rcu_free_buffer_reg_uid(struct rcu_head *head)
goto destroy;
}
+ rcu_read_lock();
/* Get the right socket from the consumer object. */
socket = consumer_find_socket_by_bitness(regp->bits_per_long,
consumer);
if (!socket) {
- goto destroy;
+ goto unlock;
}
switch (regp->domain) {
break;
default:
assert(0);
+ rcu_read_unlock();
return;
}
+unlock:
+ rcu_read_unlock();
destroy:
call_rcu(®p->node.head, rcu_free_buffer_reg_uid);
}
/*
* Destroy per PID and UID registry hash table.
+ *
+ * Should *NOT* be called with RCU read-side lock held.
*/
void buffer_reg_destroy_registries(void)
{
void buffer_reg_stream_destroy(struct buffer_reg_stream *regp,
enum lttng_domain_type domain);
-/* Session */
-void buffer_reg_session_destroy(struct buffer_reg_session *regp,
- enum lttng_domain_type domain);
-
/* Global registry. */
void buffer_reg_destroy_registries(void);
/*
* Delete the consumer_output object from the list and free the ptr.
+ *
+ * Should *NOT* be called with RCU read-side lock held.
*/
void consumer_destroy_output(struct consumer_output *obj)
{
/*
* Copy consumer output and returned the newly allocated copy.
+ *
+ * Should *NOT* be called with RCU read-side lock held.
*/
struct consumer_output *consumer_copy_output(struct consumer_output *obj)
{
* Copy consumer output from the tracing session to the domain session. The
* function also applies the right modification on a per domain basis for the
* trace files destination directory.
+ *
+ * Should *NOT* be called with RCU read-side lock held.
*/
static int copy_session_consumer(int domain, struct ltt_session *session)
{
/*
* Create an UST session and add it to the session ust list.
+ *
+ * Should *NOT* be called with RCU read-side lock held.
*/
static int create_ust_session(struct ltt_session *session,
struct lttng_domain *domain)
* Return any error encountered or 0 for success.
*
* "sock" is only used for special-case var. len data.
+ *
+ * Should *NOT* be called with RCU read-side lock held.
*/
static int process_client_msg(struct command_ctx *cmd_ctx, int sock,
int *sock_error)
* Delete session from the session list and free the memory.
*
* Return -1 if no session is found. On success, return 1;
+ * Should *NOT* be called with RCU read-side lock held.
*/
int session_destroy(struct ltt_session *session)
{
del_session_list(session);
pthread_mutex_destroy(&session->lock);
- rcu_read_lock();
consumer_destroy_output(session->consumer);
- rcu_read_unlock();
free(session);
return LTTNG_OK;
/*
* Cleanup kernel session structure
+ *
+ * Should *NOT* be called with RCU read-side lock held.
*/
void trace_kernel_destroy_session(struct ltt_kernel_session *session)
{
assert(ht);
+ rcu_read_lock();
cds_lfht_for_each_entry(ht->ht, &iter.iter, node, node) {
ret = lttng_ht_del(ht, &iter);
if (!ret) {
call_rcu(&node->head, destroy_context_rcu);
}
}
+ rcu_read_unlock();
lttng_ht_destroy(ht);
}
assert(events);
+ rcu_read_lock();
cds_lfht_for_each_entry(events->ht, &iter.iter, node, node) {
ret = lttng_ht_del(events, &iter);
assert(!ret);
call_rcu(&node->head, destroy_event_rcu);
}
+ rcu_read_unlock();
lttng_ht_destroy(events);
}
/*
* Cleanup ust channel structure.
+ *
+ * Should _NOT_ be called with RCU read lock held.
*/
-void trace_ust_destroy_channel(struct ltt_ust_channel *channel)
+static void _trace_ust_destroy_channel(struct ltt_ust_channel *channel)
{
assert(channel);
DBG2("Trace destroy UST channel %s", channel->name);
- rcu_read_lock();
-
/* Destroying all events of the channel */
destroy_events(channel->events);
/* Destroying all context of the channel */
destroy_contexts(channel->ctx);
free(channel);
-
- rcu_read_unlock();
}
/*
struct ltt_ust_channel *channel =
caa_container_of(node, struct ltt_ust_channel, node);
- trace_ust_destroy_channel(channel);
+ _trace_ust_destroy_channel(channel);
+}
+
+void trace_ust_destroy_channel(struct ltt_ust_channel *channel)
+{
+ call_rcu(&channel->node.head, destroy_channel_rcu);
}
/*
assert(!ret);
call_rcu(&node->head, destroy_channel_rcu);
}
+ rcu_read_unlock();
lttng_ht_destroy(channels);
-
- rcu_read_unlock();
}
/*
/*
* Cleanup ust session structure
+ *
+ * Should *NOT* be called with RCU read-side lock held.
*/
void trace_ust_destroy_session(struct ltt_ust_session *session)
{
assert(session);
- rcu_read_lock();
-
DBG2("Trace UST destroy session %u", session->id);
/* Cleaning up UST domain */
consumer_destroy_output(session->tmp_consumer);
free(session);
-
- rcu_read_unlock();
}
free(stream);
}
+/*
+ * We need to execute ht_destroy outside of RCU read-side critical
+ * section, so we postpone its execution using call_rcu. It is simpler
+ * than to change the semantic of the many callers of
+ * delete_ust_app_channel().
+ */
+static
+void delete_ust_app_channel_rcu(struct rcu_head *head)
+{
+ struct ust_app_channel *ua_chan =
+ caa_container_of(head, struct ust_app_channel, rcu_head);
+
+ lttng_ht_destroy(ua_chan->ctx);
+ lttng_ht_destroy(ua_chan->events);
+ free(ua_chan);
+}
+
/*
* Delete ust app channel safely. RCU read lock must be held before calling
* this function.
assert(!ret);
delete_ust_app_ctx(sock, ua_ctx);
}
- lttng_ht_destroy(ua_chan->ctx);
/* Wipe events */
cds_lfht_for_each_entry(ua_chan->events->ht, &iter.iter, ua_event,
assert(!ret);
delete_ust_app_event(sock, ua_event);
}
- lttng_ht_destroy(ua_chan->events);
/* Wipe and free registry from session registry. */
registry = get_session_registry(ua_chan->session);
lttng_fd_put(LTTNG_FD_APPS, 1);
free(ua_chan->obj);
}
- free(ua_chan);
+ call_rcu(&ua_chan->rcu_head, delete_ust_app_channel_rcu);
}
/*
return ret;
}
+/*
+ * We need to execute ht_destroy outside of RCU read-side critical
+ * section, so we postpone its execution using call_rcu. It is simpler
+ * than to change the semantic of the many callers of
+ * delete_ust_app_session().
+ */
+static
+void delete_ust_app_session_rcu(struct rcu_head *head)
+{
+ struct ust_app_session *ua_sess =
+ caa_container_of(head, struct ust_app_session, rcu_head);
+
+ lttng_ht_destroy(ua_sess->channels);
+ free(ua_sess);
+}
+
/*
* Delete ust app session safely. RCU read lock must be held before calling
* this function.
assert(!ret);
delete_ust_app_channel(sock, ua_chan, app);
}
- lttng_ht_destroy(ua_sess->channels);
/* In case of per PID, the registry is kept in the session. */
if (ua_sess->buffer_type == LTTNG_BUFFER_PER_PID) {
sock, ret);
}
}
- free(ua_sess);
+ call_rcu(&ua_sess->rcu_head, delete_ust_app_session_rcu);
}
/*
* Delete a traceable application structure from the global list. Never call
* this function outside of a call_rcu call.
+ *
+ * RCU read side lock should _NOT_ be held when calling this function.
*/
static
void delete_ust_app(struct ust_app *app)
int ret, sock;
struct ust_app_session *ua_sess, *tmp_ua_sess;
- rcu_read_lock();
-
/* Delete ust app sessions info */
sock = app->sock;
app->sock = -1;
- lttng_ht_destroy(app->sessions);
-
/* Wipe sessions */
cds_list_for_each_entry_safe(ua_sess, tmp_ua_sess, &app->teardown_head,
teardown_node) {
/* Free every object in the session and the session. */
+ rcu_read_lock();
delete_ust_app_session(sock, ua_sess, app);
+ rcu_read_unlock();
}
+
+ lttng_ht_destroy(app->sessions);
lttng_ht_destroy(app->ust_objd);
/*
DBG2("UST app pid %d deleted", app->pid);
free(app);
-
- rcu_read_unlock();
}
/*
* Create UST app channel and create it on the tracer. Set ua_chanp of the
* newly created channel if not NULL.
*
- * Called with UST app session lock held.
+ * Called with UST app session lock and RCU read-side lock held.
*
* Return 0 on success or else a negative value.
*/
/*
* Free and clean all traceable apps of the global list.
+ *
+ * Should _NOT_ be called with RCU read-side lock held.
*/
void ust_app_clean_list(void)
{
ret = lttng_ht_del(ust_app_ht_by_notify_sock, &iter);
assert(!ret);
}
+ rcu_read_unlock();
/* Destroy is done only when the ht is empty */
lttng_ht_destroy(ust_app_ht);
lttng_ht_destroy(ust_app_ht_by_sock);
lttng_ht_destroy(ust_app_ht_by_notify_sock);
-
- rcu_read_unlock();
}
/*
* ust_objd hash table in the ust_app object.
*/
struct lttng_ht_node_ulong ust_objd_node;
+ /* For delayed reclaim */
+ struct rcu_head rcu_head;
};
struct ust_app_session {
enum lttng_buffer_type buffer_type;
/* ABI of the session. Same value as the application. */
uint32_t bits_per_long;
+ /* For delayed reclaim */
+ struct rcu_head rcu_head;
};
/*
return;
}
+/*
+ * We need to execute ht_destroy outside of RCU read-side critical
+ * section, so we postpone its execution using call_rcu. It is simpler
+ * than to change the semantic of the many callers of
+ * destroy_channel().
+ */
+static
+void destroy_channel_rcu(struct rcu_head *head)
+{
+ struct ust_registry_channel *chan =
+ caa_container_of(head, struct ust_registry_channel, rcu_head);
+
+ lttng_ht_destroy(chan->ht);
+ free(chan);
+}
+
/*
* 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.
- *
- * This *MUST NOT* be called within a RCU read side lock section.
*/
static void destroy_channel(struct ust_registry_channel *chan)
{
ust_registry_destroy_event(chan, event);
}
rcu_read_unlock();
-
- lttng_ht_destroy(chan->ht);
- free(chan);
+ call_rcu(&chan->rcu_head, destroy_channel_rcu);
}
/*
ret = lttng_ht_del(session->channels, &iter);
assert(!ret);
rcu_read_unlock();
-
- /*
- * Destroying the hash table should be done without RCU
- * read-side lock held. Since we own "chan" now, it is OK to use
- * it outside of RCU read-side critical section.
- */
destroy_channel(chan);
end:
assert(!ret);
destroy_channel(chan);
}
- lttng_ht_destroy(reg->channels);
rcu_read_unlock();
+ lttng_ht_destroy(reg->channels);
free(reg->metadata);
}
size_t nr_ctx_fields;
struct ustctl_field *ctx_fields;
struct lttng_ht_node_u64 node;
+ /* For delayed reclaim */
+ struct rcu_head rcu_head;
};
/*
destroy_relayd(relayd);
}
- lttng_ht_destroy(consumer_data.relayd_ht);
-
rcu_read_unlock();
+
+ lttng_ht_destroy(consumer_data.relayd_ht);
}
/*