+/*
+ * For a given application and session, push metadata to consumer. The session
+ * lock MUST be acquired here before calling this.
+ *
+ * Return 0 on success else a negative error.
+ */
+static int push_metadata(struct ust_app *app, struct ust_app_session *ua_sess)
+{
+ int ret;
+ char *metadata_str = NULL;
+ size_t len, offset;
+ struct consumer_socket *socket;
+
+ assert(app);
+ assert(ua_sess);
+
+ if (!ua_sess->consumer || !ua_sess->metadata) {
+ /* No consumer means no stream associated so just return gracefully. */
+ ret = 0;
+ goto end;
+ }
+
+ rcu_read_lock();
+
+ /* Get consumer socket to use to push the metadata.*/
+ socket = find_consumer_socket_by_bitness(app->bits_per_long,
+ ua_sess->consumer);
+ if (!socket) {
+ ret = -1;
+ goto error_rcu_unlock;
+ }
+
+ /*
+ * TODO: Currently, we hold the socket lock around sampling of the next
+ * metadata segment to ensure we send metadata over the consumer socket in
+ * the correct order. This makes the registry lock nest inside the socket
+ * lock.
+ *
+ * Please note that this is a temporary measure: we should move this lock
+ * back into ust_consumer_push_metadata() when the consumer gets the
+ * ability to reorder the metadata it receives.
+ */
+ pthread_mutex_lock(socket->lock);
+ pthread_mutex_lock(&ua_sess->registry.lock);
+
+ offset = ua_sess->registry.metadata_len_sent;
+ len = ua_sess->registry.metadata_len - ua_sess->registry.metadata_len_sent;
+ if (len == 0) {
+ DBG3("No metadata to push for session id %d", ua_sess->id);
+ ret = 0;
+ goto error_reg_unlock;
+ }
+ assert(len > 0);
+
+ /* Allocate only what we have to send. */
+ metadata_str = zmalloc(len);
+ if (!metadata_str) {
+ PERROR("zmalloc ust app metadata string");
+ ret = -ENOMEM;
+ goto error_reg_unlock;
+ }
+ /* Copy what we haven't send out. */
+ memcpy(metadata_str, ua_sess->registry.metadata + offset, len);
+
+ pthread_mutex_unlock(&ua_sess->registry.lock);
+
+ ret = ust_consumer_push_metadata(socket, ua_sess, metadata_str, len,
+ offset);
+ if (ret < 0) {
+ pthread_mutex_unlock(socket->lock);
+ goto error_rcu_unlock;
+ }
+
+ /* Update len sent of the registry. */
+ pthread_mutex_lock(&ua_sess->registry.lock);
+ ua_sess->registry.metadata_len_sent += len;
+ pthread_mutex_unlock(&ua_sess->registry.lock);
+ pthread_mutex_unlock(socket->lock);
+
+ rcu_read_unlock();
+ free(metadata_str);
+ return 0;
+
+error_reg_unlock:
+ pthread_mutex_unlock(&ua_sess->registry.lock);
+ pthread_mutex_unlock(socket->lock);
+error_rcu_unlock:
+ rcu_read_unlock();
+ free(metadata_str);
+end:
+ return ret;
+}
+
+/*
+ * Send to the consumer a close metadata command for the given session. Once
+ * done, the metadata channel is deleted and the session metadata pointer is
+ * nullified. The session lock MUST be acquired here unless the application is
+ * in the destroy path.
+ *
+ * Return 0 on success else a negative value.
+ */
+static int close_metadata(struct ust_app *app, struct ust_app_session *ua_sess)
+{
+ int ret;
+ struct consumer_socket *socket;
+
+ assert(app);
+ assert(ua_sess);
+
+ /* Ignore if no metadata. Valid since it can be called on unregister. */
+ if (!ua_sess->metadata) {
+ ret = 0;
+ goto error;
+ }
+
+ rcu_read_lock();
+
+ /* Get consumer socket to use to push the metadata.*/
+ socket = find_consumer_socket_by_bitness(app->bits_per_long,
+ ua_sess->consumer);
+ if (!socket) {
+ ret = -1;
+ goto error_rcu_unlock;
+ }
+
+ ret = ust_consumer_close_metadata(socket, ua_sess->metadata);
+ if (ret < 0) {
+ goto error_rcu_unlock;
+ }
+
+error_rcu_unlock:
+ /* Destroy metadata on our side since we must not use it anymore. */
+ delete_ust_app_channel(-1, ua_sess->metadata, app);
+ ua_sess->metadata = NULL;
+
+ rcu_read_unlock();
+error:
+ return ret;
+}
+