Commit | Line | Data |
---|---|---|
7591bab1 | 1 | /* |
ab5be9fa MJ |
2 | * Copyright (C) 2013 Julien Desfossez <jdesfossez@efficios.com> |
3 | * Copyright (C) 2013 David Goulet <dgoulet@efficios.com> | |
4 | * Copyright (C) 2015 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | |
7591bab1 | 5 | * |
ab5be9fa | 6 | * SPDX-License-Identifier: GPL-2.0-only |
7591bab1 | 7 | * |
7591bab1 MD |
8 | */ |
9 | ||
7591bab1 | 10 | #define _LGPL_SOURCE |
c9e313bc | 11 | #include "ctf-trace.hpp" |
98b82dfa | 12 | #include "live.hpp" |
28ab034a | 13 | #include "lttng-relayd.hpp" |
c9e313bc | 14 | #include "session.hpp" |
28ab034a | 15 | #include "stream.hpp" |
c9e313bc SM |
16 | #include "viewer-session.hpp" |
17 | #include "viewer-stream.hpp" | |
28ab034a JG |
18 | |
19 | #include <common/common.hpp> | |
56047f5a | 20 | #include <common/urcu.hpp> |
28ab034a JG |
21 | |
22 | #include <urcu/rculist.h> | |
7591bab1 | 23 | |
98b82dfa KS |
24 | /* Global session id used in the session creation. */ |
25 | static uint64_t last_viewer_session_id; | |
26 | static pthread_mutex_t last_viewer_session_id_lock = PTHREAD_MUTEX_INITIALIZER; | |
27 | ||
cd9adb8b | 28 | struct relay_viewer_session *viewer_session_create() |
7591bab1 MD |
29 | { |
30 | struct relay_viewer_session *vsession; | |
31 | ||
64803277 | 32 | vsession = zmalloc<relay_viewer_session>(); |
7591bab1 MD |
33 | if (!vsession) { |
34 | goto end; | |
35 | } | |
98b82dfa KS |
36 | pthread_mutex_lock(&last_viewer_session_id_lock); |
37 | vsession->id = ++last_viewer_session_id; | |
38 | pthread_mutex_unlock(&last_viewer_session_id_lock); | |
7591bab1 | 39 | CDS_INIT_LIST_HEAD(&vsession->session_list); |
98b82dfa KS |
40 | CDS_INIT_LIST_HEAD(&vsession->unannounced_stream_list); |
41 | pthread_mutex_init(&vsession->unannounced_stream_list_lock, nullptr); | |
42 | lttng_ht_node_init_u64(&vsession->viewer_session_n, vsession->id); | |
43 | lttng_ht_add_unique_u64(viewer_sessions_ht, &vsession->viewer_session_n); | |
7591bab1 MD |
44 | end: |
45 | return vsession; | |
46 | } | |
47 | ||
b3ab5004 | 48 | int viewer_session_set_trace_chunk_copy(struct relay_viewer_session *vsession, |
28ab034a | 49 | struct lttng_trace_chunk *relay_session_trace_chunk) |
664eef54 JG |
50 | { |
51 | int ret = 0; | |
52 | struct lttng_trace_chunk *viewer_chunk; | |
53 | ||
ad8bec24 | 54 | lttng_trace_chunk_put(vsession->current_trace_chunk); |
cd9adb8b | 55 | vsession->current_trace_chunk = nullptr; |
664eef54 JG |
56 | |
57 | DBG("Copying relay session's current trace chunk to the viewer session"); | |
80516611 JG |
58 | if (!relay_session_trace_chunk) { |
59 | goto end; | |
60 | } | |
61 | ||
664eef54 JG |
62 | viewer_chunk = lttng_trace_chunk_copy(relay_session_trace_chunk); |
63 | if (!viewer_chunk) { | |
64 | ERR("Failed to create a viewer trace chunk from the relay session's current chunk"); | |
65 | ret = -1; | |
66 | goto end; | |
67 | } | |
68 | ||
69 | vsession->current_trace_chunk = viewer_chunk; | |
70 | end: | |
71 | return ret; | |
72 | } | |
73 | ||
7591bab1 | 74 | /* The existence of session must be guaranteed by the caller. */ |
28ab034a JG |
75 | enum lttng_viewer_attach_return_code viewer_session_attach(struct relay_viewer_session *vsession, |
76 | struct relay_session *session) | |
7591bab1 | 77 | { |
28ab034a | 78 | enum lttng_viewer_attach_return_code viewer_attach_status = LTTNG_VIEWER_ATTACH_OK; |
7591bab1 | 79 | |
c06fdd95 JG |
80 | ASSERT_LOCKED(session->lock); |
81 | ||
7591bab1 MD |
82 | /* Will not fail, as per the ownership guarantee. */ |
83 | if (!session_get(session)) { | |
dbd6665b | 84 | viewer_attach_status = LTTNG_VIEWER_ATTACH_UNK; |
7591bab1 MD |
85 | goto end; |
86 | } | |
7591bab1 | 87 | if (session->viewer_attached) { |
dbd6665b | 88 | viewer_attach_status = LTTNG_VIEWER_ATTACH_ALREADY; |
7591bab1 | 89 | } else { |
dbd6665b JG |
90 | int ret; |
91 | ||
7591bab1 | 92 | session->viewer_attached = true; |
dbd6665b | 93 | |
28ab034a | 94 | ret = viewer_session_set_trace_chunk_copy(vsession, session->current_trace_chunk); |
dbd6665b JG |
95 | if (ret) { |
96 | /* | |
97 | * The live protocol does not define a generic error | |
98 | * value for the "attach" command. The "unknown" | |
99 | * status is used so that the viewer may handle this | |
100 | * failure as if the session didn't exist anymore. | |
101 | */ | |
102 | DBG("Failed to create a viewer trace chunk from the current trace chunk of session \"%s\", returning LTTNG_VIEWER_ATTACH_UNK", | |
28ab034a | 103 | session->session_name); |
dbd6665b JG |
104 | viewer_attach_status = LTTNG_VIEWER_ATTACH_UNK; |
105 | } | |
7591bab1 MD |
106 | } |
107 | ||
dbd6665b | 108 | if (viewer_attach_status == LTTNG_VIEWER_ATTACH_OK) { |
7591bab1 MD |
109 | pthread_mutex_lock(&vsession->session_list_lock); |
110 | /* Ownership is transfered to the list. */ | |
28ab034a | 111 | cds_list_add_rcu(&session->viewer_session_node, &vsession->session_list); |
7591bab1 | 112 | pthread_mutex_unlock(&vsession->session_list_lock); |
98b82dfa KS |
113 | |
114 | /* | |
115 | * Immediately create new viewer streams for the attached session | |
116 | * so that the viewer streams hold a reference on the any relay | |
117 | * streams that could be unpublished between now and the next | |
118 | * GET_NEW_STREAMS command from the live viewer. | |
119 | */ | |
120 | uint32_t created = 0; | |
121 | uint32_t total = 0; | |
122 | uint32_t unsent = 0; | |
123 | bool closed = false; | |
124 | const int make_viewer_streams_ret = make_viewer_streams(session, | |
7c8d0f41 JG |
125 | vsession, |
126 | LTTNG_VIEWER_SEEK_BEGINNING, | |
127 | &total, | |
128 | &unsent, | |
129 | &created, | |
130 | &closed); | |
98b82dfa KS |
131 | |
132 | if (make_viewer_streams_ret == 0) { | |
7c8d0f41 JG |
133 | DBG("Created %d new viewer streams while attaching to relay session %" PRIu64, |
134 | created, | |
135 | session->id); | |
98b82dfa KS |
136 | } else { |
137 | /* | |
138 | * Warning, since the creation of the streams will be retried when | |
139 | * the viewer next sends the GET_NEW_STREAMS commands. | |
140 | */ | |
7c8d0f41 JG |
141 | WARN("Failed to create new viewer streams while attaching to relay session %" PRIu64 |
142 | ", ret=%d, total=%d, unsent=%d, created=%d, closed=%d", | |
98b82dfa KS |
143 | session->id, |
144 | make_viewer_streams_ret, | |
145 | total, | |
146 | unsent, | |
147 | created, | |
148 | closed); | |
149 | } | |
7591bab1 MD |
150 | } else { |
151 | /* Put our local ref. */ | |
152 | session_put(session); | |
153 | } | |
7591bab1 | 154 | end: |
dbd6665b | 155 | return viewer_attach_status; |
7591bab1 MD |
156 | } |
157 | ||
158 | /* The existence of session must be guaranteed by the caller. */ | |
159 | static int viewer_session_detach(struct relay_viewer_session *vsession, | |
28ab034a | 160 | struct relay_session *session) |
7591bab1 MD |
161 | { |
162 | int ret = 0; | |
163 | ||
164 | pthread_mutex_lock(&session->lock); | |
165 | if (!session->viewer_attached) { | |
166 | ret = -1; | |
167 | } else { | |
168 | session->viewer_attached = false; | |
169 | } | |
170 | ||
171 | if (!ret) { | |
172 | pthread_mutex_lock(&vsession->session_list_lock); | |
173 | cds_list_del_rcu(&session->viewer_session_node); | |
174 | pthread_mutex_unlock(&vsession->session_list_lock); | |
175 | /* Release reference held by the list. */ | |
176 | session_put(session); | |
177 | } | |
98b82dfa | 178 | |
7591bab1 MD |
179 | /* Safe since we know the session exists. */ |
180 | pthread_mutex_unlock(&session->lock); | |
181 | return ret; | |
182 | } | |
183 | ||
184 | void viewer_session_destroy(struct relay_viewer_session *vsession) | |
185 | { | |
98b82dfa KS |
186 | struct lttng_ht_iter iter; |
187 | ||
188 | LTTNG_ASSERT(cds_list_empty(&vsession->unannounced_stream_list)); | |
189 | ||
190 | iter.iter.node = &vsession->viewer_session_n.node; | |
191 | lttng_ht_del(viewer_sessions_ht, &iter); | |
b66a15d1 | 192 | lttng_trace_chunk_put(vsession->current_trace_chunk); |
7591bab1 MD |
193 | free(vsession); |
194 | } | |
195 | ||
d62023be JD |
196 | /* |
197 | * Release ownership of all the streams of one session and detach the viewer. | |
198 | */ | |
199 | void viewer_session_close_one_session(struct relay_viewer_session *vsession, | |
28ab034a | 200 | struct relay_session *session) |
d62023be | 201 | { |
d62023be JD |
202 | /* |
203 | * TODO: improvement: create more efficient list of | |
204 | * vstream per session. | |
205 | */ | |
33b7f82c JG |
206 | for (auto *vstream : |
207 | lttng::urcu::lfht_iteration_adapter<relay_viewer_stream, | |
208 | decltype(relay_viewer_stream::stream_n), | |
209 | &relay_viewer_stream::stream_n>( | |
210 | *viewer_streams_ht->ht)) { | |
211 | if (!viewer_stream_get(vstream)) { | |
212 | continue; | |
213 | } | |
98b82dfa | 214 | |
33b7f82c | 215 | if (vstream->stream->trace->session != session) { |
d62023be | 216 | viewer_stream_put(vstream); |
33b7f82c | 217 | continue; |
d62023be | 218 | } |
33b7f82c JG |
219 | /* Put local reference. */ |
220 | viewer_stream_put(vstream); | |
221 | /* | |
222 | * We have reached one of the viewer stream's lifetime | |
223 | * end condition. This "put" will cause the proper | |
224 | * teardown of the viewer stream. | |
225 | */ | |
226 | viewer_stream_put(vstream); | |
d62023be | 227 | } |
56047f5a | 228 | |
7c8d0f41 JG |
229 | for (auto *vstream : |
230 | lttng::urcu::rcu_list_iteration_adapter<relay_viewer_stream, | |
231 | &relay_viewer_stream::viewer_stream_node>( | |
232 | vsession->unannounced_stream_list)) { | |
98b82dfa KS |
233 | if (!viewer_stream_get(vstream)) { |
234 | continue; | |
235 | } | |
236 | if (vstream->stream->trace->session != session) { | |
237 | viewer_stream_put(vstream); | |
238 | continue; | |
239 | } | |
240 | pthread_mutex_lock(&vsession->unannounced_stream_list_lock); | |
241 | cds_list_del_rcu(&vstream->viewer_stream_node); | |
242 | pthread_mutex_unlock(&vsession->unannounced_stream_list_lock); | |
243 | /* Local reference */ | |
244 | viewer_stream_put(vstream); | |
245 | /* Reference from unannounced_stream_list */ | |
246 | viewer_stream_put(vstream); | |
247 | } | |
248 | ||
b3feb91b | 249 | lttng_trace_chunk_put(vsession->current_trace_chunk); |
cd9adb8b | 250 | vsession->current_trace_chunk = nullptr; |
d62023be JD |
251 | viewer_session_detach(vsession, session); |
252 | } | |
253 | ||
7591bab1 MD |
254 | void viewer_session_close(struct relay_viewer_session *vsession) |
255 | { | |
95c62b9d JG |
256 | for (auto *session : |
257 | lttng::urcu::rcu_list_iteration_adapter<relay_session, | |
258 | &relay_session::viewer_session_node>( | |
259 | vsession->session_list)) { | |
260 | viewer_session_close_one_session(vsession, session); | |
7591bab1 | 261 | } |
7591bab1 MD |
262 | } |
263 | ||
264 | /* | |
265 | * Check if a connection is attached to a session. | |
266 | * Return 1 if attached, 0 if not attached, a negative value on error. | |
267 | */ | |
28ab034a | 268 | int viewer_session_is_attached(struct relay_viewer_session *vsession, struct relay_session *session) |
7591bab1 | 269 | { |
7591bab1 MD |
270 | int found = 0; |
271 | ||
272 | pthread_mutex_lock(&session->lock); | |
273 | if (!vsession) { | |
274 | goto end; | |
275 | } | |
276 | if (!session->viewer_attached) { | |
277 | goto end; | |
278 | } | |
56047f5a | 279 | |
95c62b9d JG |
280 | for (auto *session_it : |
281 | lttng::urcu::rcu_list_iteration_adapter<relay_session, | |
282 | &relay_session::viewer_session_node>( | |
283 | vsession->session_list)) { | |
284 | if (session == session_it) { | |
285 | found = 1; | |
286 | break; | |
7591bab1 MD |
287 | } |
288 | } | |
56047f5a | 289 | |
7591bab1 MD |
290 | end: |
291 | pthread_mutex_unlock(&session->lock); | |
292 | return found; | |
293 | } |