]>
Commit | Line | Data |
---|---|---|
ef43a632 CH |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * MGMTD Frontend Client Connection Adapter | |
4 | * | |
5 | * Copyright (C) 2021 Vmware, Inc. | |
6 | * Pushpasis Sarkar <spushpasis@vmware.com> | |
7 | */ | |
8 | ||
9 | #include <zebra.h> | |
10 | #include "sockopt.h" | |
11 | #include "network.h" | |
12 | #include "libfrr.h" | |
13 | #include "mgmt_fe_client.h" | |
f82370b4 | 14 | #include "mgmt_msg.h" |
ef43a632 CH |
15 | #include "mgmt_pb.h" |
16 | #include "hash.h" | |
17 | #include "jhash.h" | |
18 | #include "mgmtd/mgmt.h" | |
19 | #include "mgmtd/mgmt_memory.h" | |
20 | #include "mgmtd/mgmt_fe_adapter.h" | |
21 | ||
22 | #ifdef REDIRECT_DEBUG_TO_STDERR | |
23 | #define MGMTD_FE_ADAPTER_DBG(fmt, ...) \ | |
24 | fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__) | |
25 | #define MGMTD_FE_ADAPTER_ERR(fmt, ...) \ | |
26 | fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__) | |
27 | #else /* REDIRECT_DEBUG_TO_STDERR */ | |
28 | #define MGMTD_FE_ADAPTER_DBG(fmt, ...) \ | |
29 | do { \ | |
30 | if (mgmt_debug_fe) \ | |
31 | zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__); \ | |
32 | } while (0) | |
33 | #define MGMTD_FE_ADAPTER_ERR(fmt, ...) \ | |
34 | zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__) | |
35 | #endif /* REDIRECT_DEBUG_TO_STDERR */ | |
36 | ||
37 | #define FOREACH_ADAPTER_IN_LIST(adapter) \ | |
38 | frr_each_safe (mgmt_fe_adapters, &mgmt_fe_adapters, (adapter)) | |
39 | ||
40 | enum mgmt_session_event { | |
41 | MGMTD_FE_SESSION_CFG_TXN_CLNUP = 1, | |
42 | MGMTD_FE_SESSION_SHOW_TXN_CLNUP, | |
43 | }; | |
44 | ||
45 | struct mgmt_fe_session_ctx { | |
46 | struct mgmt_fe_client_adapter *adapter; | |
47 | uint64_t session_id; | |
48 | uint64_t client_id; | |
49 | uint64_t txn_id; | |
50 | uint64_t cfg_txn_id; | |
51 | uint8_t ds_write_locked[MGMTD_DS_MAX_ID]; | |
52 | uint8_t ds_read_locked[MGMTD_DS_MAX_ID]; | |
53 | uint8_t ds_locked_implict[MGMTD_DS_MAX_ID]; | |
e6685141 DS |
54 | struct event *proc_cfg_txn_clnp; |
55 | struct event *proc_show_txn_clnp; | |
ef43a632 CH |
56 | |
57 | struct mgmt_fe_sessions_item list_linkage; | |
58 | }; | |
59 | ||
60 | DECLARE_LIST(mgmt_fe_sessions, struct mgmt_fe_session_ctx, list_linkage); | |
61 | ||
62 | #define FOREACH_SESSION_IN_LIST(adapter, session) \ | |
63 | frr_each_safe (mgmt_fe_sessions, &(adapter)->fe_sessions, (session)) | |
64 | ||
cd9d0537 | 65 | static struct event_loop *mgmt_fe_adapter_tm; |
ef43a632 CH |
66 | static struct mgmt_master *mgmt_fe_adapter_mm; |
67 | ||
68 | static struct mgmt_fe_adapters_head mgmt_fe_adapters; | |
69 | ||
70 | static struct hash *mgmt_fe_sessions; | |
71 | static uint64_t mgmt_fe_next_session_id; | |
72 | ||
73 | /* Forward declarations */ | |
74 | static void | |
75 | mgmt_fe_adapter_register_event(struct mgmt_fe_client_adapter *adapter, | |
76 | enum mgmt_fe_event event); | |
77 | static void | |
78 | mgmt_fe_adapter_disconnect(struct mgmt_fe_client_adapter *adapter); | |
79 | static void | |
80 | mgmt_fe_session_register_event(struct mgmt_fe_session_ctx *session, | |
81 | enum mgmt_session_event event); | |
82 | ||
83 | static int | |
84 | mgmt_fe_session_write_lock_ds(Mgmtd__DatastoreId ds_id, | |
85 | struct mgmt_ds_ctx *ds_ctx, | |
86 | struct mgmt_fe_session_ctx *session) | |
87 | { | |
88 | if (!session->ds_write_locked[ds_id]) { | |
89 | if (mgmt_ds_write_lock(ds_ctx) != 0) { | |
90 | MGMTD_FE_ADAPTER_DBG( | |
91 | "Failed to lock the DS %u for Sessn: %p from %s!", | |
92 | ds_id, session, session->adapter->name); | |
93 | return -1; | |
94 | } | |
95 | ||
96 | session->ds_write_locked[ds_id] = true; | |
97 | MGMTD_FE_ADAPTER_DBG( | |
98 | "Write-Locked the DS %u for Sessn: %p from %s!", ds_id, | |
99 | session, session->adapter->name); | |
100 | } | |
101 | ||
102 | return 0; | |
103 | } | |
104 | ||
105 | static int | |
106 | mgmt_fe_session_read_lock_ds(Mgmtd__DatastoreId ds_id, | |
107 | struct mgmt_ds_ctx *ds_ctx, | |
108 | struct mgmt_fe_session_ctx *session) | |
109 | { | |
110 | if (!session->ds_read_locked[ds_id]) { | |
111 | if (mgmt_ds_read_lock(ds_ctx) != 0) { | |
112 | MGMTD_FE_ADAPTER_DBG( | |
113 | "Failed to lock the DS %u for Sessn: %p from %s!", | |
114 | ds_id, session, session->adapter->name); | |
115 | return -1; | |
116 | } | |
117 | ||
118 | session->ds_read_locked[ds_id] = true; | |
119 | MGMTD_FE_ADAPTER_DBG( | |
120 | "Read-Locked the DS %u for Sessn: %p from %s!", ds_id, | |
121 | session, session->adapter->name); | |
122 | } | |
123 | ||
124 | return 0; | |
125 | } | |
126 | ||
127 | static int mgmt_fe_session_unlock_ds(Mgmtd__DatastoreId ds_id, | |
128 | struct mgmt_ds_ctx *ds_ctx, | |
129 | struct mgmt_fe_session_ctx *session, | |
130 | bool unlock_write, bool unlock_read) | |
131 | { | |
132 | if (unlock_write && session->ds_write_locked[ds_id]) { | |
133 | session->ds_write_locked[ds_id] = false; | |
134 | session->ds_locked_implict[ds_id] = false; | |
135 | if (mgmt_ds_unlock(ds_ctx) != 0) { | |
136 | MGMTD_FE_ADAPTER_DBG( | |
137 | "Failed to unlock the DS %u taken earlier by Sessn: %p from %s!", | |
138 | ds_id, session, session->adapter->name); | |
139 | return -1; | |
140 | } | |
141 | ||
142 | MGMTD_FE_ADAPTER_DBG( | |
143 | "Unlocked DS %u write-locked earlier by Sessn: %p from %s", | |
144 | ds_id, session, session->adapter->name); | |
145 | } else if (unlock_read && session->ds_read_locked[ds_id]) { | |
146 | session->ds_read_locked[ds_id] = false; | |
147 | session->ds_locked_implict[ds_id] = false; | |
148 | if (mgmt_ds_unlock(ds_ctx) != 0) { | |
149 | MGMTD_FE_ADAPTER_DBG( | |
150 | "Failed to unlock the DS %u taken earlier by Sessn: %p from %s!", | |
151 | ds_id, session, session->adapter->name); | |
152 | return -1; | |
153 | } | |
154 | ||
155 | MGMTD_FE_ADAPTER_DBG( | |
156 | "Unlocked DS %u read-locked earlier by Sessn: %p from %s", | |
157 | ds_id, session, session->adapter->name); | |
158 | } | |
159 | ||
160 | return 0; | |
161 | } | |
162 | ||
163 | static void | |
164 | mgmt_fe_session_cfg_txn_cleanup(struct mgmt_fe_session_ctx *session) | |
165 | { | |
166 | Mgmtd__DatastoreId ds_id; | |
167 | struct mgmt_ds_ctx *ds_ctx; | |
168 | ||
169 | /* | |
170 | * Ensure any uncommitted changes in Candidate DS | |
171 | * is discarded. | |
172 | */ | |
173 | mgmt_ds_copy_dss(mm->running_ds, mm->candidate_ds, false); | |
174 | ||
175 | for (ds_id = 0; ds_id < MGMTD_DS_MAX_ID; ds_id++) { | |
176 | ds_ctx = mgmt_ds_get_ctx_by_id(mgmt_fe_adapter_mm, ds_id); | |
177 | if (ds_ctx) { | |
178 | if (session->ds_locked_implict[ds_id]) | |
179 | mgmt_fe_session_unlock_ds( | |
180 | ds_id, ds_ctx, session, true, false); | |
181 | } | |
182 | } | |
183 | ||
74335ceb YR |
184 | /* |
185 | * Destroy the actual transaction created earlier. | |
ef43a632 | 186 | */ |
74335ceb YR |
187 | if (session->cfg_txn_id != MGMTD_TXN_ID_NONE) |
188 | mgmt_destroy_txn(&session->cfg_txn_id); | |
ef43a632 CH |
189 | } |
190 | ||
191 | static void | |
192 | mgmt_fe_session_show_txn_cleanup(struct mgmt_fe_session_ctx *session) | |
193 | { | |
194 | Mgmtd__DatastoreId ds_id; | |
195 | struct mgmt_ds_ctx *ds_ctx; | |
196 | ||
197 | for (ds_id = 0; ds_id < MGMTD_DS_MAX_ID; ds_id++) { | |
198 | ds_ctx = mgmt_ds_get_ctx_by_id(mgmt_fe_adapter_mm, ds_id); | |
199 | if (ds_ctx) { | |
200 | mgmt_fe_session_unlock_ds(ds_id, ds_ctx, session, | |
201 | false, true); | |
202 | } | |
203 | } | |
204 | ||
74335ceb YR |
205 | /* |
206 | * Destroy the transaction created recently. | |
ef43a632 | 207 | */ |
74335ceb YR |
208 | if (session->txn_id != MGMTD_TXN_ID_NONE) |
209 | mgmt_destroy_txn(&session->txn_id); | |
ef43a632 CH |
210 | } |
211 | ||
212 | static void | |
213 | mgmt_fe_adapter_compute_set_cfg_timers(struct mgmt_setcfg_stats *setcfg_stats) | |
214 | { | |
215 | setcfg_stats->last_exec_tm = timeval_elapsed(setcfg_stats->last_end, | |
216 | setcfg_stats->last_start); | |
217 | if (setcfg_stats->last_exec_tm > setcfg_stats->max_tm) | |
218 | setcfg_stats->max_tm = setcfg_stats->last_exec_tm; | |
219 | ||
220 | if (setcfg_stats->last_exec_tm < setcfg_stats->min_tm) | |
221 | setcfg_stats->min_tm = setcfg_stats->last_exec_tm; | |
222 | ||
223 | setcfg_stats->avg_tm = | |
224 | (((setcfg_stats->avg_tm * (setcfg_stats->set_cfg_count - 1)) | |
225 | + setcfg_stats->last_exec_tm) | |
226 | / setcfg_stats->set_cfg_count); | |
227 | } | |
228 | ||
229 | static void | |
230 | mgmt_fe_session_compute_commit_timers(struct mgmt_commit_stats *cmt_stats) | |
231 | { | |
232 | cmt_stats->last_exec_tm = | |
233 | timeval_elapsed(cmt_stats->last_end, cmt_stats->last_start); | |
234 | if (cmt_stats->last_exec_tm > cmt_stats->max_tm) { | |
235 | cmt_stats->max_tm = cmt_stats->last_exec_tm; | |
236 | cmt_stats->max_batch_cnt = cmt_stats->last_batch_cnt; | |
237 | } | |
238 | ||
239 | if (cmt_stats->last_exec_tm < cmt_stats->min_tm) { | |
240 | cmt_stats->min_tm = cmt_stats->last_exec_tm; | |
241 | cmt_stats->min_batch_cnt = cmt_stats->last_batch_cnt; | |
242 | } | |
243 | } | |
244 | ||
245 | static void mgmt_fe_cleanup_session(struct mgmt_fe_session_ctx **session) | |
246 | { | |
247 | if ((*session)->adapter) { | |
248 | mgmt_fe_session_cfg_txn_cleanup((*session)); | |
249 | mgmt_fe_session_show_txn_cleanup((*session)); | |
250 | mgmt_fe_session_unlock_ds(MGMTD_DS_CANDIDATE, | |
251 | mgmt_fe_adapter_mm->candidate_ds, | |
252 | *session, true, true); | |
253 | mgmt_fe_session_unlock_ds(MGMTD_DS_RUNNING, | |
254 | mgmt_fe_adapter_mm->running_ds, | |
255 | *session, true, true); | |
256 | ||
257 | mgmt_fe_sessions_del(&(*session)->adapter->fe_sessions, | |
258 | *session); | |
259 | mgmt_fe_adapter_unlock(&(*session)->adapter); | |
260 | } | |
261 | ||
262 | hash_release(mgmt_fe_sessions, *session); | |
263 | XFREE(MTYPE_MGMTD_FE_SESSION, *session); | |
264 | *session = NULL; | |
265 | } | |
266 | ||
267 | static struct mgmt_fe_session_ctx * | |
268 | mgmt_fe_find_session_by_client_id(struct mgmt_fe_client_adapter *adapter, | |
269 | uint64_t client_id) | |
270 | { | |
271 | struct mgmt_fe_session_ctx *session; | |
272 | ||
273 | FOREACH_SESSION_IN_LIST (adapter, session) { | |
274 | if (session->client_id == client_id) | |
275 | return session; | |
276 | } | |
277 | ||
278 | return NULL; | |
279 | } | |
280 | ||
281 | static unsigned int mgmt_fe_session_hash_key(const void *data) | |
282 | { | |
283 | const struct mgmt_fe_session_ctx *session = data; | |
284 | ||
285 | return jhash2((uint32_t *) &session->session_id, | |
286 | sizeof(session->session_id) / sizeof(uint32_t), 0); | |
287 | } | |
288 | ||
289 | static bool mgmt_fe_session_hash_cmp(const void *d1, const void *d2) | |
290 | { | |
291 | const struct mgmt_fe_session_ctx *session1 = d1; | |
292 | const struct mgmt_fe_session_ctx *session2 = d2; | |
293 | ||
294 | return (session1->session_id == session2->session_id); | |
295 | } | |
296 | ||
297 | static void mgmt_fe_session_hash_free(void *data) | |
298 | { | |
299 | struct mgmt_fe_session_ctx *session = data; | |
300 | ||
301 | mgmt_fe_cleanup_session(&session); | |
302 | } | |
303 | ||
304 | static void mgmt_fe_session_hash_destroy(void) | |
305 | { | |
306 | if (mgmt_fe_sessions == NULL) | |
307 | return; | |
308 | ||
309 | hash_clean(mgmt_fe_sessions, | |
310 | mgmt_fe_session_hash_free); | |
311 | hash_free(mgmt_fe_sessions); | |
312 | mgmt_fe_sessions = NULL; | |
313 | } | |
314 | ||
315 | static inline struct mgmt_fe_session_ctx * | |
316 | mgmt_session_id2ctx(uint64_t session_id) | |
317 | { | |
318 | struct mgmt_fe_session_ctx key = {0}; | |
319 | struct mgmt_fe_session_ctx *session; | |
320 | ||
321 | if (!mgmt_fe_sessions) | |
322 | return NULL; | |
323 | ||
324 | key.session_id = session_id; | |
325 | session = hash_lookup(mgmt_fe_sessions, &key); | |
326 | ||
327 | return session; | |
328 | } | |
329 | ||
330 | static struct mgmt_fe_session_ctx * | |
331 | mgmt_fe_create_session(struct mgmt_fe_client_adapter *adapter, | |
332 | uint64_t client_id) | |
333 | { | |
334 | struct mgmt_fe_session_ctx *session; | |
335 | ||
336 | session = mgmt_fe_find_session_by_client_id(adapter, client_id); | |
337 | if (session) | |
338 | mgmt_fe_cleanup_session(&session); | |
339 | ||
340 | session = XCALLOC(MTYPE_MGMTD_FE_SESSION, | |
341 | sizeof(struct mgmt_fe_session_ctx)); | |
342 | assert(session); | |
343 | session->client_id = client_id; | |
344 | session->adapter = adapter; | |
345 | session->txn_id = MGMTD_TXN_ID_NONE; | |
346 | session->cfg_txn_id = MGMTD_TXN_ID_NONE; | |
347 | mgmt_fe_adapter_lock(adapter); | |
348 | mgmt_fe_sessions_add_tail(&adapter->fe_sessions, session); | |
349 | if (!mgmt_fe_next_session_id) | |
350 | mgmt_fe_next_session_id++; | |
351 | session->session_id = mgmt_fe_next_session_id++; | |
352 | hash_get(mgmt_fe_sessions, session, hash_alloc_intern); | |
353 | ||
354 | return session; | |
355 | } | |
356 | ||
357 | static void | |
358 | mgmt_fe_cleanup_sessions(struct mgmt_fe_client_adapter *adapter) | |
359 | { | |
360 | struct mgmt_fe_session_ctx *session; | |
361 | ||
362 | FOREACH_SESSION_IN_LIST (adapter, session) | |
363 | mgmt_fe_cleanup_session(&session); | |
364 | } | |
365 | ||
366 | static inline void | |
367 | mgmt_fe_adapter_sched_msg_write(struct mgmt_fe_client_adapter *adapter) | |
368 | { | |
369 | if (!CHECK_FLAG(adapter->flags, MGMTD_FE_ADAPTER_FLAGS_WRITES_OFF)) | |
370 | mgmt_fe_adapter_register_event(adapter, | |
371 | MGMTD_FE_CONN_WRITE); | |
372 | } | |
373 | ||
374 | static inline void | |
375 | mgmt_fe_adapter_writes_on(struct mgmt_fe_client_adapter *adapter) | |
376 | { | |
377 | MGMTD_FE_ADAPTER_DBG("Resume writing msgs for '%s'", adapter->name); | |
378 | UNSET_FLAG(adapter->flags, MGMTD_FE_ADAPTER_FLAGS_WRITES_OFF); | |
f82370b4 | 379 | mgmt_fe_adapter_sched_msg_write(adapter); |
ef43a632 CH |
380 | } |
381 | ||
382 | static inline void | |
383 | mgmt_fe_adapter_writes_off(struct mgmt_fe_client_adapter *adapter) | |
384 | { | |
385 | SET_FLAG(adapter->flags, MGMTD_FE_ADAPTER_FLAGS_WRITES_OFF); | |
386 | MGMTD_FE_ADAPTER_DBG("Paused writing msgs for '%s'", adapter->name); | |
387 | } | |
388 | ||
389 | static int | |
390 | mgmt_fe_adapter_send_msg(struct mgmt_fe_client_adapter *adapter, | |
391 | Mgmtd__FeMessage *fe_msg) | |
392 | { | |
f82370b4 CH |
393 | if (adapter->conn_fd == -1) { |
394 | MGMTD_FE_ADAPTER_DBG("can't send message on closed connection"); | |
ef43a632 CH |
395 | return -1; |
396 | } | |
397 | ||
f82370b4 CH |
398 | int rv = mgmt_msg_send_msg( |
399 | &adapter->mstate, fe_msg, | |
400 | mgmtd__fe_message__get_packed_size(fe_msg), | |
401 | (size_t(*)(void *, void *))mgmtd__fe_message__pack, | |
402 | mgmt_debug_fe); | |
ef43a632 | 403 | mgmt_fe_adapter_sched_msg_write(adapter); |
f82370b4 | 404 | return rv; |
ef43a632 CH |
405 | } |
406 | ||
407 | static int | |
408 | mgmt_fe_send_session_reply(struct mgmt_fe_client_adapter *adapter, | |
409 | struct mgmt_fe_session_ctx *session, | |
410 | bool create, bool success) | |
411 | { | |
412 | Mgmtd__FeMessage fe_msg; | |
413 | Mgmtd__FeSessionReply session_reply; | |
414 | ||
415 | mgmtd__fe_session_reply__init(&session_reply); | |
416 | session_reply.create = create; | |
417 | if (create) { | |
418 | session_reply.has_client_conn_id = 1; | |
419 | session_reply.client_conn_id = session->client_id; | |
420 | } | |
421 | session_reply.session_id = session->session_id; | |
422 | session_reply.success = success; | |
423 | ||
424 | mgmtd__fe_message__init(&fe_msg); | |
425 | fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_SESSION_REPLY; | |
426 | fe_msg.session_reply = &session_reply; | |
427 | ||
428 | MGMTD_FE_ADAPTER_DBG( | |
429 | "Sending SESSION_REPLY message to MGMTD Frontend client '%s'", | |
430 | adapter->name); | |
431 | ||
432 | return mgmt_fe_adapter_send_msg(adapter, &fe_msg); | |
433 | } | |
434 | ||
435 | static int mgmt_fe_send_lockds_reply(struct mgmt_fe_session_ctx *session, | |
436 | Mgmtd__DatastoreId ds_id, | |
437 | uint64_t req_id, bool lock_ds, | |
438 | bool success, const char *error_if_any) | |
439 | { | |
440 | Mgmtd__FeMessage fe_msg; | |
441 | Mgmtd__FeLockDsReply lockds_reply; | |
442 | ||
443 | assert(session->adapter); | |
444 | ||
445 | mgmtd__fe_lock_ds_reply__init(&lockds_reply); | |
446 | lockds_reply.session_id = session->session_id; | |
447 | lockds_reply.ds_id = ds_id; | |
448 | lockds_reply.req_id = req_id; | |
449 | lockds_reply.lock = lock_ds; | |
450 | lockds_reply.success = success; | |
451 | if (error_if_any) | |
452 | lockds_reply.error_if_any = (char *)error_if_any; | |
453 | ||
454 | mgmtd__fe_message__init(&fe_msg); | |
455 | fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REPLY; | |
456 | fe_msg.lockds_reply = &lockds_reply; | |
457 | ||
458 | MGMTD_FE_ADAPTER_DBG( | |
459 | "Sending LOCK_DS_REPLY message to MGMTD Frontend client '%s'", | |
460 | session->adapter->name); | |
461 | ||
462 | return mgmt_fe_adapter_send_msg(session->adapter, &fe_msg); | |
463 | } | |
464 | ||
465 | static int mgmt_fe_send_setcfg_reply(struct mgmt_fe_session_ctx *session, | |
466 | Mgmtd__DatastoreId ds_id, | |
467 | uint64_t req_id, bool success, | |
468 | const char *error_if_any, | |
469 | bool implicit_commit) | |
470 | { | |
471 | Mgmtd__FeMessage fe_msg; | |
472 | Mgmtd__FeSetConfigReply setcfg_reply; | |
473 | ||
474 | assert(session->adapter); | |
475 | ||
476 | if (implicit_commit && session->cfg_txn_id) | |
477 | mgmt_fe_session_register_event( | |
478 | session, MGMTD_FE_SESSION_CFG_TXN_CLNUP); | |
479 | ||
480 | mgmtd__fe_set_config_reply__init(&setcfg_reply); | |
481 | setcfg_reply.session_id = session->session_id; | |
482 | setcfg_reply.ds_id = ds_id; | |
483 | setcfg_reply.req_id = req_id; | |
484 | setcfg_reply.success = success; | |
485 | if (error_if_any) | |
486 | setcfg_reply.error_if_any = (char *)error_if_any; | |
487 | ||
488 | mgmtd__fe_message__init(&fe_msg); | |
489 | fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REPLY; | |
490 | fe_msg.setcfg_reply = &setcfg_reply; | |
491 | ||
492 | MGMTD_FE_ADAPTER_DBG( | |
493 | "Sending SET_CONFIG_REPLY message to MGMTD Frontend client '%s'", | |
494 | session->adapter->name); | |
495 | ||
496 | if (implicit_commit) { | |
497 | if (mm->perf_stats_en) | |
498 | gettimeofday(&session->adapter->cmt_stats.last_end, NULL); | |
499 | mgmt_fe_session_compute_commit_timers( | |
500 | &session->adapter->cmt_stats); | |
501 | } | |
502 | ||
503 | if (mm->perf_stats_en) | |
504 | gettimeofday(&session->adapter->setcfg_stats.last_end, NULL); | |
505 | mgmt_fe_adapter_compute_set_cfg_timers(&session->adapter->setcfg_stats); | |
506 | ||
507 | return mgmt_fe_adapter_send_msg(session->adapter, &fe_msg); | |
508 | } | |
509 | ||
510 | static int mgmt_fe_send_commitcfg_reply( | |
511 | struct mgmt_fe_session_ctx *session, Mgmtd__DatastoreId src_ds_id, | |
512 | Mgmtd__DatastoreId dst_ds_id, uint64_t req_id, enum mgmt_result result, | |
513 | bool validate_only, const char *error_if_any) | |
514 | { | |
515 | Mgmtd__FeMessage fe_msg; | |
516 | Mgmtd__FeCommitConfigReply commcfg_reply; | |
517 | ||
518 | assert(session->adapter); | |
519 | ||
520 | mgmtd__fe_commit_config_reply__init(&commcfg_reply); | |
521 | commcfg_reply.session_id = session->session_id; | |
522 | commcfg_reply.src_ds_id = src_ds_id; | |
523 | commcfg_reply.dst_ds_id = dst_ds_id; | |
524 | commcfg_reply.req_id = req_id; | |
525 | commcfg_reply.success = | |
526 | (result == MGMTD_SUCCESS || result == MGMTD_NO_CFG_CHANGES) | |
527 | ? true | |
528 | : false; | |
529 | commcfg_reply.validate_only = validate_only; | |
530 | if (error_if_any) | |
531 | commcfg_reply.error_if_any = (char *)error_if_any; | |
532 | ||
533 | mgmtd__fe_message__init(&fe_msg); | |
534 | fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REPLY; | |
535 | fe_msg.commcfg_reply = &commcfg_reply; | |
536 | ||
537 | MGMTD_FE_ADAPTER_DBG( | |
538 | "Sending COMMIT_CONFIG_REPLY message to MGMTD Frontend client '%s'", | |
539 | session->adapter->name); | |
540 | ||
541 | /* | |
542 | * Cleanup the CONFIG transaction associated with this session. | |
543 | */ | |
544 | if (session->cfg_txn_id | |
545 | && ((result == MGMTD_SUCCESS && !validate_only) | |
546 | || (result == MGMTD_NO_CFG_CHANGES))) | |
547 | mgmt_fe_session_register_event( | |
548 | session, MGMTD_FE_SESSION_CFG_TXN_CLNUP); | |
549 | ||
550 | if (mm->perf_stats_en) | |
551 | gettimeofday(&session->adapter->cmt_stats.last_end, NULL); | |
552 | mgmt_fe_session_compute_commit_timers(&session->adapter->cmt_stats); | |
553 | return mgmt_fe_adapter_send_msg(session->adapter, &fe_msg); | |
554 | } | |
555 | ||
556 | static int mgmt_fe_send_getcfg_reply(struct mgmt_fe_session_ctx *session, | |
557 | Mgmtd__DatastoreId ds_id, | |
558 | uint64_t req_id, bool success, | |
559 | Mgmtd__YangDataReply *data, | |
560 | const char *error_if_any) | |
561 | { | |
562 | Mgmtd__FeMessage fe_msg; | |
563 | Mgmtd__FeGetConfigReply getcfg_reply; | |
564 | ||
565 | assert(session->adapter); | |
566 | ||
567 | mgmtd__fe_get_config_reply__init(&getcfg_reply); | |
568 | getcfg_reply.session_id = session->session_id; | |
569 | getcfg_reply.ds_id = ds_id; | |
570 | getcfg_reply.req_id = req_id; | |
571 | getcfg_reply.success = success; | |
572 | getcfg_reply.data = data; | |
573 | if (error_if_any) | |
574 | getcfg_reply.error_if_any = (char *)error_if_any; | |
575 | ||
576 | mgmtd__fe_message__init(&fe_msg); | |
577 | fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_GETCFG_REPLY; | |
578 | fe_msg.getcfg_reply = &getcfg_reply; | |
579 | ||
580 | MGMTD_FE_ADAPTER_DBG( | |
581 | "Sending GET_CONFIG_REPLY message to MGMTD Frontend client '%s'", | |
582 | session->adapter->name); | |
583 | ||
584 | /* | |
585 | * Cleanup the SHOW transaction associated with this session. | |
586 | */ | |
587 | if (session->txn_id && (!success || (data && data->next_indx < 0))) | |
588 | mgmt_fe_session_register_event( | |
589 | session, MGMTD_FE_SESSION_SHOW_TXN_CLNUP); | |
590 | ||
591 | return mgmt_fe_adapter_send_msg(session->adapter, &fe_msg); | |
592 | } | |
593 | ||
594 | static int mgmt_fe_send_getdata_reply(struct mgmt_fe_session_ctx *session, | |
595 | Mgmtd__DatastoreId ds_id, | |
596 | uint64_t req_id, bool success, | |
597 | Mgmtd__YangDataReply *data, | |
598 | const char *error_if_any) | |
599 | { | |
600 | Mgmtd__FeMessage fe_msg; | |
601 | Mgmtd__FeGetDataReply getdata_reply; | |
602 | ||
603 | assert(session->adapter); | |
604 | ||
605 | mgmtd__fe_get_data_reply__init(&getdata_reply); | |
606 | getdata_reply.session_id = session->session_id; | |
607 | getdata_reply.ds_id = ds_id; | |
608 | getdata_reply.req_id = req_id; | |
609 | getdata_reply.success = success; | |
610 | getdata_reply.data = data; | |
611 | if (error_if_any) | |
612 | getdata_reply.error_if_any = (char *)error_if_any; | |
613 | ||
614 | mgmtd__fe_message__init(&fe_msg); | |
615 | fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_GETDATA_REPLY; | |
616 | fe_msg.getdata_reply = &getdata_reply; | |
617 | ||
618 | MGMTD_FE_ADAPTER_DBG( | |
619 | "Sending GET_DATA_REPLY message to MGMTD Frontend client '%s'", | |
620 | session->adapter->name); | |
621 | ||
622 | /* | |
623 | * Cleanup the SHOW transaction associated with this session. | |
624 | */ | |
625 | if (session->txn_id && (!success || (data && data->next_indx < 0))) | |
626 | mgmt_fe_session_register_event( | |
627 | session, MGMTD_FE_SESSION_SHOW_TXN_CLNUP); | |
628 | ||
629 | return mgmt_fe_adapter_send_msg(session->adapter, &fe_msg); | |
630 | } | |
631 | ||
e6685141 | 632 | static void mgmt_fe_session_cfg_txn_clnup(struct event *thread) |
ef43a632 CH |
633 | { |
634 | struct mgmt_fe_session_ctx *session; | |
635 | ||
e16d030c | 636 | session = (struct mgmt_fe_session_ctx *)EVENT_ARG(thread); |
ef43a632 CH |
637 | |
638 | mgmt_fe_session_cfg_txn_cleanup(session); | |
639 | } | |
640 | ||
e6685141 | 641 | static void mgmt_fe_session_show_txn_clnup(struct event *thread) |
ef43a632 CH |
642 | { |
643 | struct mgmt_fe_session_ctx *session; | |
644 | ||
e16d030c | 645 | session = (struct mgmt_fe_session_ctx *)EVENT_ARG(thread); |
ef43a632 CH |
646 | |
647 | mgmt_fe_session_show_txn_cleanup(session); | |
648 | } | |
649 | ||
650 | static void | |
651 | mgmt_fe_session_register_event(struct mgmt_fe_session_ctx *session, | |
652 | enum mgmt_session_event event) | |
653 | { | |
654 | struct timeval tv = {.tv_sec = 0, | |
655 | .tv_usec = MGMTD_FE_MSG_PROC_DELAY_USEC}; | |
656 | ||
657 | switch (event) { | |
658 | case MGMTD_FE_SESSION_CFG_TXN_CLNUP: | |
907a2395 | 659 | event_add_timer_tv(mgmt_fe_adapter_tm, |
ef43a632 CH |
660 | mgmt_fe_session_cfg_txn_clnup, session, |
661 | &tv, &session->proc_cfg_txn_clnp); | |
ef43a632 CH |
662 | break; |
663 | case MGMTD_FE_SESSION_SHOW_TXN_CLNUP: | |
907a2395 | 664 | event_add_timer_tv(mgmt_fe_adapter_tm, |
ef43a632 CH |
665 | mgmt_fe_session_show_txn_clnup, session, |
666 | &tv, &session->proc_show_txn_clnp); | |
ef43a632 | 667 | break; |
ef43a632 CH |
668 | } |
669 | } | |
670 | ||
671 | static struct mgmt_fe_client_adapter * | |
672 | mgmt_fe_find_adapter_by_fd(int conn_fd) | |
673 | { | |
674 | struct mgmt_fe_client_adapter *adapter; | |
675 | ||
676 | FOREACH_ADAPTER_IN_LIST (adapter) { | |
677 | if (adapter->conn_fd == conn_fd) | |
678 | return adapter; | |
679 | } | |
680 | ||
681 | return NULL; | |
682 | } | |
683 | ||
684 | static struct mgmt_fe_client_adapter * | |
685 | mgmt_fe_find_adapter_by_name(const char *name) | |
686 | { | |
687 | struct mgmt_fe_client_adapter *adapter; | |
688 | ||
689 | FOREACH_ADAPTER_IN_LIST (adapter) { | |
690 | if (!strncmp(adapter->name, name, sizeof(adapter->name))) | |
691 | return adapter; | |
692 | } | |
693 | ||
694 | return NULL; | |
695 | } | |
696 | ||
697 | static void mgmt_fe_adapter_disconnect(struct mgmt_fe_client_adapter *adapter) | |
698 | { | |
699 | if (adapter->conn_fd >= 0) { | |
700 | close(adapter->conn_fd); | |
701 | adapter->conn_fd = -1; | |
702 | } | |
703 | ||
704 | /* TODO: notify about client disconnect for appropriate cleanup */ | |
705 | mgmt_fe_cleanup_sessions(adapter); | |
706 | mgmt_fe_sessions_fini(&adapter->fe_sessions); | |
707 | mgmt_fe_adapters_del(&mgmt_fe_adapters, adapter); | |
708 | ||
709 | mgmt_fe_adapter_unlock(&adapter); | |
710 | } | |
711 | ||
712 | static void | |
713 | mgmt_fe_adapter_cleanup_old_conn(struct mgmt_fe_client_adapter *adapter) | |
714 | { | |
715 | struct mgmt_fe_client_adapter *old; | |
716 | ||
717 | FOREACH_ADAPTER_IN_LIST (old) { | |
718 | if (old != adapter | |
719 | && !strncmp(adapter->name, old->name, sizeof(adapter->name))) { | |
720 | /* | |
721 | * We have a Zombie lingering around | |
722 | */ | |
723 | MGMTD_FE_ADAPTER_DBG( | |
724 | "Client '%s' (FD:%d) seems to have reconnected. Removing old connection (FD:%d)!", | |
725 | adapter->name, adapter->conn_fd, old->conn_fd); | |
726 | mgmt_fe_adapter_disconnect(old); | |
727 | } | |
728 | } | |
729 | } | |
730 | ||
731 | static void | |
732 | mgmt_fe_cleanup_adapters(void) | |
733 | { | |
734 | struct mgmt_fe_client_adapter *adapter; | |
735 | ||
736 | FOREACH_ADAPTER_IN_LIST (adapter) { | |
737 | mgmt_fe_cleanup_sessions(adapter); | |
738 | mgmt_fe_adapter_unlock(&adapter); | |
739 | } | |
740 | } | |
741 | ||
742 | static int | |
743 | mgmt_fe_session_handle_lockds_req_msg(struct mgmt_fe_session_ctx *session, | |
744 | Mgmtd__FeLockDsReq *lockds_req) | |
745 | { | |
746 | struct mgmt_ds_ctx *ds_ctx; | |
747 | ||
748 | /* | |
749 | * Next check first if the SET_CONFIG_REQ is for Candidate DS | |
750 | * or not. Report failure if its not. MGMTD currently only | |
751 | * supports editing the Candidate DS. | |
752 | */ | |
753 | if (lockds_req->ds_id != MGMTD_DS_CANDIDATE) { | |
754 | mgmt_fe_send_lockds_reply( | |
755 | session, lockds_req->ds_id, lockds_req->req_id, | |
756 | lockds_req->lock, false, | |
757 | "Lock/Unlock on datastores other than Candidate DS not permitted!"); | |
758 | return -1; | |
759 | } | |
760 | ||
761 | ds_ctx = | |
762 | mgmt_ds_get_ctx_by_id(mgmt_fe_adapter_mm, lockds_req->ds_id); | |
763 | if (!ds_ctx) { | |
764 | mgmt_fe_send_lockds_reply( | |
765 | session, lockds_req->ds_id, lockds_req->req_id, | |
766 | lockds_req->lock, false, | |
767 | "Failed to retrieve handle for DS!"); | |
768 | return -1; | |
769 | } | |
770 | ||
771 | if (lockds_req->lock) { | |
772 | if (mgmt_fe_session_write_lock_ds(lockds_req->ds_id, | |
773 | ds_ctx, session) | |
774 | != 0) { | |
775 | mgmt_fe_send_lockds_reply( | |
776 | session, lockds_req->ds_id, lockds_req->req_id, | |
777 | lockds_req->lock, false, | |
778 | "Lock already taken on DS by another session!"); | |
779 | return -1; | |
780 | } | |
781 | ||
782 | session->ds_locked_implict[lockds_req->ds_id] = false; | |
783 | } else { | |
784 | if (!session->ds_write_locked[lockds_req->ds_id]) { | |
785 | mgmt_fe_send_lockds_reply( | |
786 | session, lockds_req->ds_id, lockds_req->req_id, | |
787 | lockds_req->lock, false, | |
788 | "Lock on DS was not taken by this session!"); | |
789 | return 0; | |
790 | } | |
791 | ||
792 | (void)mgmt_fe_session_unlock_ds(lockds_req->ds_id, ds_ctx, | |
793 | session, true, false); | |
794 | } | |
795 | ||
796 | if (mgmt_fe_send_lockds_reply(session, lockds_req->ds_id, | |
797 | lockds_req->req_id, lockds_req->lock, | |
798 | true, NULL) | |
799 | != 0) { | |
800 | MGMTD_FE_ADAPTER_DBG( | |
801 | "Failed to send LOCK_DS_REPLY for DS %u Sessn: %p from %s", | |
802 | lockds_req->ds_id, session, session->adapter->name); | |
803 | } | |
804 | ||
805 | return 0; | |
806 | } | |
807 | ||
808 | static int | |
809 | mgmt_fe_session_handle_setcfg_req_msg(struct mgmt_fe_session_ctx *session, | |
810 | Mgmtd__FeSetConfigReq *setcfg_req) | |
811 | { | |
74335ceb | 812 | uint64_t cfg_session_id; |
ef43a632 CH |
813 | struct mgmt_ds_ctx *ds_ctx, *dst_ds_ctx; |
814 | ||
815 | if (mm->perf_stats_en) | |
816 | gettimeofday(&session->adapter->setcfg_stats.last_start, NULL); | |
817 | ||
818 | /* | |
819 | * Next check first if the SET_CONFIG_REQ is for Candidate DS | |
820 | * or not. Report failure if its not. MGMTD currently only | |
821 | * supports editing the Candidate DS. | |
822 | */ | |
823 | if (setcfg_req->ds_id != MGMTD_DS_CANDIDATE) { | |
824 | mgmt_fe_send_setcfg_reply( | |
825 | session, setcfg_req->ds_id, setcfg_req->req_id, false, | |
826 | "Set-Config on datastores other than Candidate DS not permitted!", | |
827 | setcfg_req->implicit_commit); | |
828 | return 0; | |
829 | } | |
830 | ||
831 | /* | |
832 | * Get the DS handle. | |
833 | */ | |
834 | ds_ctx = | |
835 | mgmt_ds_get_ctx_by_id(mgmt_fe_adapter_mm, setcfg_req->ds_id); | |
836 | if (!ds_ctx) { | |
837 | mgmt_fe_send_setcfg_reply( | |
838 | session, setcfg_req->ds_id, setcfg_req->req_id, false, | |
839 | "No such DS exists!", setcfg_req->implicit_commit); | |
840 | return 0; | |
841 | } | |
842 | ||
843 | if (session->cfg_txn_id == MGMTD_TXN_ID_NONE) { | |
844 | /* | |
74335ceb | 845 | * Check first if the current session can run a CONFIG |
ef43a632 CH |
846 | * transaction or not. Report failure if a CONFIG transaction |
847 | * from another session is already in progress. | |
ef43a632 | 848 | */ |
74335ceb YR |
849 | cfg_session_id = mgmt_config_txn_in_progress(); |
850 | if (cfg_session_id != MGMTD_SESSION_ID_NONE | |
851 | && cfg_session_id != session->session_id) { | |
852 | mgmt_fe_send_setcfg_reply( | |
853 | session, setcfg_req->ds_id, setcfg_req->req_id, | |
854 | false, | |
855 | "Configuration already in-progress through a different user session!", | |
856 | setcfg_req->implicit_commit); | |
857 | goto mgmt_fe_sess_handle_setcfg_req_failed; | |
858 | } | |
ef43a632 CH |
859 | |
860 | ||
861 | /* | |
862 | * Try taking write-lock on the requested DS (if not already). | |
863 | */ | |
864 | if (!session->ds_write_locked[setcfg_req->ds_id]) { | |
865 | if (mgmt_fe_session_write_lock_ds(setcfg_req->ds_id, | |
866 | ds_ctx, session) | |
867 | != 0) { | |
868 | mgmt_fe_send_setcfg_reply( | |
869 | session, setcfg_req->ds_id, | |
870 | setcfg_req->req_id, false, | |
871 | "Failed to lock the DS!", | |
872 | setcfg_req->implicit_commit); | |
873 | goto mgmt_fe_sess_handle_setcfg_req_failed; | |
874 | } | |
875 | ||
876 | session->ds_locked_implict[setcfg_req->ds_id] = true; | |
877 | } | |
878 | ||
879 | /* | |
74335ceb | 880 | * Start a CONFIG Transaction (if not started already) |
ef43a632 | 881 | */ |
74335ceb YR |
882 | session->cfg_txn_id = mgmt_create_txn(session->session_id, |
883 | MGMTD_TXN_TYPE_CONFIG); | |
884 | if (session->cfg_txn_id == MGMTD_SESSION_ID_NONE) { | |
885 | mgmt_fe_send_setcfg_reply( | |
886 | session, setcfg_req->ds_id, setcfg_req->req_id, | |
887 | false, | |
888 | "Failed to create a Configuration session!", | |
889 | setcfg_req->implicit_commit); | |
890 | goto mgmt_fe_sess_handle_setcfg_req_failed; | |
891 | } | |
ef43a632 CH |
892 | |
893 | MGMTD_FE_ADAPTER_DBG( | |
894 | "Created new Config Txn 0x%llx for session %p", | |
895 | (unsigned long long)session->cfg_txn_id, session); | |
896 | } else { | |
897 | MGMTD_FE_ADAPTER_DBG( | |
898 | "Config Txn 0x%llx for session %p already created", | |
899 | (unsigned long long)session->cfg_txn_id, session); | |
900 | ||
901 | if (setcfg_req->implicit_commit) { | |
902 | /* | |
903 | * In this scenario need to skip cleanup of the txn, | |
904 | * so setting implicit commit to false. | |
905 | */ | |
906 | mgmt_fe_send_setcfg_reply( | |
907 | session, setcfg_req->ds_id, setcfg_req->req_id, | |
908 | false, | |
909 | "A Configuration transaction is already in progress!", | |
910 | false); | |
911 | return 0; | |
912 | } | |
913 | } | |
914 | ||
915 | dst_ds_ctx = 0; | |
916 | if (setcfg_req->implicit_commit) { | |
917 | dst_ds_ctx = mgmt_ds_get_ctx_by_id(mgmt_fe_adapter_mm, | |
918 | setcfg_req->commit_ds_id); | |
919 | if (!dst_ds_ctx) { | |
920 | mgmt_fe_send_setcfg_reply( | |
921 | session, setcfg_req->ds_id, setcfg_req->req_id, | |
922 | false, "No such commit DS exists!", | |
923 | setcfg_req->implicit_commit); | |
924 | return 0; | |
925 | } | |
926 | } | |
927 | ||
74335ceb YR |
928 | /* |
929 | * Create the SETConfig request under the transaction. | |
ef43a632 | 930 | */ |
74335ceb YR |
931 | if (mgmt_txn_send_set_config_req( |
932 | session->cfg_txn_id, setcfg_req->req_id, setcfg_req->ds_id, | |
933 | ds_ctx, setcfg_req->data, setcfg_req->n_data, | |
934 | setcfg_req->implicit_commit, setcfg_req->commit_ds_id, | |
935 | dst_ds_ctx) | |
936 | != 0) { | |
937 | mgmt_fe_send_setcfg_reply( | |
938 | session, setcfg_req->ds_id, setcfg_req->req_id, false, | |
939 | "Request processing for SET-CONFIG failed!", | |
940 | setcfg_req->implicit_commit); | |
941 | goto mgmt_fe_sess_handle_setcfg_req_failed; | |
942 | } | |
ef43a632 CH |
943 | |
944 | return 0; | |
945 | ||
946 | mgmt_fe_sess_handle_setcfg_req_failed: | |
947 | ||
74335ceb YR |
948 | /* |
949 | * Delete transaction created recently. | |
ef43a632 | 950 | */ |
74335ceb YR |
951 | if (session->cfg_txn_id != MGMTD_TXN_ID_NONE) |
952 | mgmt_destroy_txn(&session->cfg_txn_id); | |
ef43a632 CH |
953 | if (ds_ctx && session->ds_write_locked[setcfg_req->ds_id]) |
954 | mgmt_fe_session_unlock_ds(setcfg_req->ds_id, ds_ctx, session, | |
955 | true, false); | |
956 | ||
957 | return 0; | |
958 | } | |
959 | ||
960 | static int | |
961 | mgmt_fe_session_handle_getcfg_req_msg(struct mgmt_fe_session_ctx *session, | |
962 | Mgmtd__FeGetConfigReq *getcfg_req) | |
963 | { | |
964 | struct mgmt_ds_ctx *ds_ctx; | |
965 | ||
966 | /* | |
967 | * Get the DS handle. | |
968 | */ | |
969 | ds_ctx = | |
970 | mgmt_ds_get_ctx_by_id(mgmt_fe_adapter_mm, getcfg_req->ds_id); | |
971 | if (!ds_ctx) { | |
972 | mgmt_fe_send_getcfg_reply(session, getcfg_req->ds_id, | |
973 | getcfg_req->req_id, false, NULL, | |
974 | "No such DS exists!"); | |
975 | return 0; | |
976 | } | |
977 | ||
978 | /* | |
979 | * Next check first if the SET_CONFIG_REQ is for Candidate DS | |
980 | * or not. Report failure if its not. MGMTD currently only | |
981 | * supports editing the Candidate DS. | |
982 | */ | |
983 | if (getcfg_req->ds_id != MGMTD_DS_CANDIDATE | |
984 | && getcfg_req->ds_id != MGMTD_DS_RUNNING) { | |
985 | mgmt_fe_send_getcfg_reply( | |
986 | session, getcfg_req->ds_id, getcfg_req->req_id, false, | |
987 | NULL, | |
988 | "Get-Config on datastores other than Candidate or Running DS not permitted!"); | |
989 | return 0; | |
990 | } | |
991 | ||
992 | if (session->txn_id == MGMTD_TXN_ID_NONE) { | |
993 | /* | |
994 | * Try taking read-lock on the requested DS (if not already | |
995 | * locked). If the DS has already been write-locked by a ongoing | |
996 | * CONFIG transaction we may allow reading the contents of the | |
997 | * same DS. | |
998 | */ | |
999 | if (!session->ds_read_locked[getcfg_req->ds_id] | |
1000 | && !session->ds_write_locked[getcfg_req->ds_id]) { | |
1001 | if (mgmt_fe_session_read_lock_ds(getcfg_req->ds_id, | |
1002 | ds_ctx, session) | |
1003 | != 0) { | |
1004 | mgmt_fe_send_getcfg_reply( | |
1005 | session, getcfg_req->ds_id, | |
1006 | getcfg_req->req_id, false, NULL, | |
1007 | "Failed to lock the DS! Another session might have locked it!"); | |
1008 | goto mgmt_fe_sess_handle_getcfg_req_failed; | |
1009 | } | |
1010 | ||
1011 | session->ds_locked_implict[getcfg_req->ds_id] = true; | |
1012 | } | |
1013 | ||
1014 | /* | |
74335ceb | 1015 | * Start a SHOW Transaction (if not started already) |
ef43a632 | 1016 | */ |
74335ceb YR |
1017 | session->txn_id = mgmt_create_txn(session->session_id, |
1018 | MGMTD_TXN_TYPE_SHOW); | |
1019 | if (session->txn_id == MGMTD_SESSION_ID_NONE) { | |
1020 | mgmt_fe_send_getcfg_reply( | |
1021 | session, getcfg_req->ds_id, getcfg_req->req_id, | |
1022 | false, NULL, | |
1023 | "Failed to create a Show transaction!"); | |
1024 | goto mgmt_fe_sess_handle_getcfg_req_failed; | |
1025 | } | |
ef43a632 CH |
1026 | |
1027 | MGMTD_FE_ADAPTER_DBG( | |
1028 | "Created new Show Txn 0x%llx for session %p", | |
1029 | (unsigned long long)session->txn_id, session); | |
1030 | } else { | |
1031 | MGMTD_FE_ADAPTER_DBG( | |
1032 | "Show Txn 0x%llx for session %p already created", | |
1033 | (unsigned long long)session->txn_id, session); | |
1034 | } | |
1035 | ||
74335ceb YR |
1036 | /* |
1037 | * Create a GETConfig request under the transaction. | |
ef43a632 | 1038 | */ |
74335ceb YR |
1039 | if (mgmt_txn_send_get_config_req(session->txn_id, getcfg_req->req_id, |
1040 | getcfg_req->ds_id, ds_ctx, | |
1041 | getcfg_req->data, getcfg_req->n_data) | |
1042 | != 0) { | |
1043 | mgmt_fe_send_getcfg_reply( | |
1044 | session, getcfg_req->ds_id, getcfg_req->req_id, false, | |
1045 | NULL, "Request processing for GET-CONFIG failed!"); | |
1046 | goto mgmt_fe_sess_handle_getcfg_req_failed; | |
1047 | } | |
ef43a632 CH |
1048 | |
1049 | return 0; | |
1050 | ||
1051 | mgmt_fe_sess_handle_getcfg_req_failed: | |
1052 | ||
74335ceb YR |
1053 | /* |
1054 | * Destroy the transaction created recently. | |
ef43a632 | 1055 | */ |
74335ceb YR |
1056 | if (session->txn_id != MGMTD_TXN_ID_NONE) |
1057 | mgmt_destroy_txn(&session->txn_id); | |
ef43a632 CH |
1058 | if (ds_ctx && session->ds_read_locked[getcfg_req->ds_id]) |
1059 | mgmt_fe_session_unlock_ds(getcfg_req->ds_id, ds_ctx, session, | |
1060 | false, true); | |
1061 | ||
1062 | return -1; | |
1063 | } | |
1064 | ||
1065 | static int | |
1066 | mgmt_fe_session_handle_getdata_req_msg(struct mgmt_fe_session_ctx *session, | |
1067 | Mgmtd__FeGetDataReq *getdata_req) | |
1068 | { | |
1069 | struct mgmt_ds_ctx *ds_ctx; | |
1070 | ||
1071 | /* | |
1072 | * Get the DS handle. | |
1073 | */ | |
1074 | ds_ctx = mgmt_ds_get_ctx_by_id(mgmt_fe_adapter_mm, | |
1075 | getdata_req->ds_id); | |
1076 | if (!ds_ctx) { | |
1077 | mgmt_fe_send_getdata_reply(session, getdata_req->ds_id, | |
1078 | getdata_req->req_id, false, NULL, | |
1079 | "No such DS exists!"); | |
1080 | return 0; | |
1081 | } | |
1082 | ||
1083 | if (session->txn_id == MGMTD_TXN_ID_NONE) { | |
1084 | /* | |
1085 | * Try taking read-lock on the requested DS (if not already | |
1086 | * locked). If the DS has already been write-locked by a ongoing | |
1087 | * CONFIG transaction we may allow reading the contents of the | |
1088 | * same DS. | |
1089 | */ | |
1090 | if (!session->ds_read_locked[getdata_req->ds_id] | |
1091 | && !session->ds_write_locked[getdata_req->ds_id]) { | |
1092 | if (mgmt_fe_session_read_lock_ds(getdata_req->ds_id, | |
1093 | ds_ctx, session) | |
1094 | != 0) { | |
1095 | mgmt_fe_send_getdata_reply( | |
1096 | session, getdata_req->ds_id, | |
1097 | getdata_req->req_id, false, NULL, | |
1098 | "Failed to lock the DS! Another session might have locked it!"); | |
1099 | goto mgmt_fe_sess_handle_getdata_req_failed; | |
1100 | } | |
1101 | ||
1102 | session->ds_locked_implict[getdata_req->ds_id] = true; | |
1103 | } | |
1104 | ||
1105 | /* | |
74335ceb | 1106 | * Start a SHOW Transaction (if not started already) |
ef43a632 | 1107 | */ |
74335ceb YR |
1108 | session->txn_id = mgmt_create_txn(session->session_id, |
1109 | MGMTD_TXN_TYPE_SHOW); | |
1110 | if (session->txn_id == MGMTD_SESSION_ID_NONE) { | |
1111 | mgmt_fe_send_getdata_reply( | |
1112 | session, getdata_req->ds_id, getdata_req->req_id, | |
1113 | false, NULL, | |
1114 | "Failed to create a Show transaction!"); | |
1115 | goto mgmt_fe_sess_handle_getdata_req_failed; | |
1116 | } | |
ef43a632 CH |
1117 | |
1118 | MGMTD_FE_ADAPTER_DBG( | |
1119 | "Created new Show Txn 0x%llx for session %p", | |
1120 | (unsigned long long)session->txn_id, session); | |
1121 | } else { | |
1122 | MGMTD_FE_ADAPTER_DBG( | |
1123 | "Show Txn 0x%llx for session %p already created", | |
1124 | (unsigned long long)session->txn_id, session); | |
1125 | } | |
1126 | ||
74335ceb YR |
1127 | /* |
1128 | * Create a GETData request under the transaction. | |
ef43a632 | 1129 | */ |
74335ceb YR |
1130 | if (mgmt_txn_send_get_data_req(session->txn_id, getdata_req->req_id, |
1131 | getdata_req->ds_id, ds_ctx, | |
1132 | getdata_req->data, getdata_req->n_data) | |
1133 | != 0) { | |
1134 | mgmt_fe_send_getdata_reply( | |
1135 | session, getdata_req->ds_id, getdata_req->req_id, false, | |
1136 | NULL, "Request processing for GET-CONFIG failed!"); | |
1137 | goto mgmt_fe_sess_handle_getdata_req_failed; | |
1138 | } | |
ef43a632 CH |
1139 | |
1140 | return 0; | |
1141 | ||
1142 | mgmt_fe_sess_handle_getdata_req_failed: | |
1143 | ||
74335ceb YR |
1144 | /* |
1145 | * Destroy the transaction created recently. | |
ef43a632 | 1146 | */ |
74335ceb YR |
1147 | if (session->txn_id != MGMTD_TXN_ID_NONE) |
1148 | mgmt_destroy_txn(&session->txn_id); | |
ef43a632 CH |
1149 | |
1150 | if (ds_ctx && session->ds_read_locked[getdata_req->ds_id]) | |
1151 | mgmt_fe_session_unlock_ds(getdata_req->ds_id, ds_ctx, | |
1152 | session, false, true); | |
1153 | ||
1154 | return -1; | |
1155 | } | |
1156 | ||
1157 | static int mgmt_fe_session_handle_commit_config_req_msg( | |
1158 | struct mgmt_fe_session_ctx *session, | |
1159 | Mgmtd__FeCommitConfigReq *commcfg_req) | |
1160 | { | |
1161 | struct mgmt_ds_ctx *src_ds_ctx, *dst_ds_ctx; | |
1162 | ||
1163 | if (mm->perf_stats_en) | |
1164 | gettimeofday(&session->adapter->cmt_stats.last_start, NULL); | |
1165 | session->adapter->cmt_stats.commit_cnt++; | |
1166 | /* | |
1167 | * Get the source DS handle. | |
1168 | */ | |
1169 | src_ds_ctx = mgmt_ds_get_ctx_by_id(mgmt_fe_adapter_mm, | |
1170 | commcfg_req->src_ds_id); | |
1171 | if (!src_ds_ctx) { | |
1172 | mgmt_fe_send_commitcfg_reply( | |
1173 | session, commcfg_req->src_ds_id, commcfg_req->dst_ds_id, | |
1174 | commcfg_req->req_id, MGMTD_INTERNAL_ERROR, | |
1175 | commcfg_req->validate_only, | |
1176 | "No such source DS exists!"); | |
1177 | return 0; | |
1178 | } | |
1179 | ||
1180 | /* | |
1181 | * Get the destination DS handle. | |
1182 | */ | |
1183 | dst_ds_ctx = mgmt_ds_get_ctx_by_id(mgmt_fe_adapter_mm, | |
1184 | commcfg_req->dst_ds_id); | |
1185 | if (!dst_ds_ctx) { | |
1186 | mgmt_fe_send_commitcfg_reply( | |
1187 | session, commcfg_req->src_ds_id, commcfg_req->dst_ds_id, | |
1188 | commcfg_req->req_id, MGMTD_INTERNAL_ERROR, | |
1189 | commcfg_req->validate_only, | |
1190 | "No such destination DS exists!"); | |
1191 | return 0; | |
1192 | } | |
1193 | ||
1194 | /* | |
1195 | * Next check first if the SET_CONFIG_REQ is for Candidate DS | |
1196 | * or not. Report failure if its not. MGMTD currently only | |
1197 | * supports editing the Candidate DS. | |
1198 | */ | |
1199 | if (commcfg_req->dst_ds_id != MGMTD_DS_RUNNING) { | |
1200 | mgmt_fe_send_commitcfg_reply( | |
1201 | session, commcfg_req->src_ds_id, commcfg_req->dst_ds_id, | |
1202 | commcfg_req->req_id, MGMTD_INTERNAL_ERROR, | |
1203 | commcfg_req->validate_only, | |
1204 | "Set-Config on datastores other than Running DS not permitted!"); | |
1205 | return 0; | |
1206 | } | |
1207 | ||
1208 | if (session->cfg_txn_id == MGMTD_TXN_ID_NONE) { | |
1209 | /* | |
74335ceb | 1210 | * Start a CONFIG Transaction (if not started already) |
ef43a632 | 1211 | */ |
74335ceb YR |
1212 | session->cfg_txn_id = mgmt_create_txn(session->session_id, |
1213 | MGMTD_TXN_TYPE_CONFIG); | |
1214 | if (session->cfg_txn_id == MGMTD_SESSION_ID_NONE) { | |
1215 | mgmt_fe_send_commitcfg_reply( | |
1216 | session, commcfg_req->src_ds_id, | |
1217 | commcfg_req->dst_ds_id, commcfg_req->req_id, | |
1218 | MGMTD_INTERNAL_ERROR, | |
1219 | commcfg_req->validate_only, | |
1220 | "Failed to create a Configuration session!"); | |
1221 | return 0; | |
1222 | } | |
0b645fd2 CH |
1223 | MGMTD_FE_ADAPTER_DBG("Created txn %" PRIu64 |
1224 | " for session %" PRIu64 | |
1225 | " for COMMIT-CFG-REQ", | |
1226 | session->cfg_txn_id, session->session_id); | |
ef43a632 CH |
1227 | } |
1228 | ||
1229 | ||
1230 | /* | |
1231 | * Try taking write-lock on the destination DS (if not already). | |
1232 | */ | |
1233 | if (!session->ds_write_locked[commcfg_req->dst_ds_id]) { | |
1234 | if (mgmt_fe_session_write_lock_ds(commcfg_req->dst_ds_id, | |
1235 | dst_ds_ctx, session) | |
1236 | != 0) { | |
1237 | mgmt_fe_send_commitcfg_reply( | |
1238 | session, commcfg_req->src_ds_id, | |
1239 | commcfg_req->dst_ds_id, commcfg_req->req_id, | |
1240 | MGMTD_DS_LOCK_FAILED, | |
1241 | commcfg_req->validate_only, | |
1242 | "Failed to lock the destination DS!"); | |
1243 | return 0; | |
1244 | } | |
1245 | ||
1246 | session->ds_locked_implict[commcfg_req->dst_ds_id] = true; | |
1247 | } | |
1248 | ||
74335ceb YR |
1249 | /* |
1250 | * Create COMMITConfig request under the transaction | |
ef43a632 | 1251 | */ |
74335ceb YR |
1252 | if (mgmt_txn_send_commit_config_req( |
1253 | session->cfg_txn_id, commcfg_req->req_id, | |
1254 | commcfg_req->src_ds_id, src_ds_ctx, commcfg_req->dst_ds_id, | |
1255 | dst_ds_ctx, commcfg_req->validate_only, commcfg_req->abort, | |
1256 | false) | |
1257 | != 0) { | |
1258 | mgmt_fe_send_commitcfg_reply( | |
1259 | session, commcfg_req->src_ds_id, commcfg_req->dst_ds_id, | |
1260 | commcfg_req->req_id, MGMTD_INTERNAL_ERROR, | |
1261 | commcfg_req->validate_only, | |
1262 | "Request processing for COMMIT-CONFIG failed!"); | |
1263 | return 0; | |
1264 | } | |
ef43a632 CH |
1265 | |
1266 | return 0; | |
1267 | } | |
1268 | ||
1269 | static int | |
1270 | mgmt_fe_adapter_handle_msg(struct mgmt_fe_client_adapter *adapter, | |
1271 | Mgmtd__FeMessage *fe_msg) | |
1272 | { | |
1273 | struct mgmt_fe_session_ctx *session; | |
1274 | ||
0b645fd2 CH |
1275 | /* |
1276 | * protobuf-c adds a max size enum with an internal, and changing by | |
1277 | * version, name; cast to an int to avoid unhandled enum warnings | |
1278 | */ | |
1279 | switch ((int)fe_msg->message_case) { | |
ef43a632 CH |
1280 | case MGMTD__FE_MESSAGE__MESSAGE_REGISTER_REQ: |
1281 | MGMTD_FE_ADAPTER_DBG("Got Register Req Msg from '%s'", | |
1282 | fe_msg->register_req->client_name); | |
1283 | ||
1284 | if (strlen(fe_msg->register_req->client_name)) { | |
1285 | strlcpy(adapter->name, | |
1286 | fe_msg->register_req->client_name, | |
1287 | sizeof(adapter->name)); | |
1288 | mgmt_fe_adapter_cleanup_old_conn(adapter); | |
1289 | } | |
1290 | break; | |
1291 | case MGMTD__FE_MESSAGE__MESSAGE_SESSION_REQ: | |
1292 | if (fe_msg->session_req->create | |
1293 | && fe_msg->session_req->id_case | |
1294 | == MGMTD__FE_SESSION_REQ__ID_CLIENT_CONN_ID) { | |
1295 | MGMTD_FE_ADAPTER_DBG( | |
1296 | "Got Session Create Req Msg for client-id %llu from '%s'", | |
1297 | (unsigned long long) | |
1298 | fe_msg->session_req->client_conn_id, | |
1299 | adapter->name); | |
1300 | ||
1301 | session = mgmt_fe_create_session( | |
1302 | adapter, fe_msg->session_req->client_conn_id); | |
1303 | mgmt_fe_send_session_reply(adapter, session, true, | |
1304 | session ? true : false); | |
1305 | } else if ( | |
1306 | !fe_msg->session_req->create | |
1307 | && fe_msg->session_req->id_case | |
1308 | == MGMTD__FE_SESSION_REQ__ID_SESSION_ID) { | |
1309 | MGMTD_FE_ADAPTER_DBG( | |
1310 | "Got Session Destroy Req Msg for session-id %llu from '%s'", | |
1311 | (unsigned long long) | |
1312 | fe_msg->session_req->session_id, | |
1313 | adapter->name); | |
1314 | ||
1315 | session = mgmt_session_id2ctx( | |
1316 | fe_msg->session_req->session_id); | |
1317 | mgmt_fe_send_session_reply(adapter, session, false, | |
1318 | true); | |
1319 | mgmt_fe_cleanup_session(&session); | |
1320 | } | |
1321 | break; | |
1322 | case MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REQ: | |
1323 | session = mgmt_session_id2ctx( | |
1324 | fe_msg->lockds_req->session_id); | |
1325 | MGMTD_FE_ADAPTER_DBG( | |
1326 | "Got %sockDS Req Msg for DS:%d for session-id %llx from '%s'", | |
1327 | fe_msg->lockds_req->lock ? "L" : "Unl", | |
1328 | fe_msg->lockds_req->ds_id, | |
1329 | (unsigned long long)fe_msg->lockds_req->session_id, | |
1330 | adapter->name); | |
1331 | mgmt_fe_session_handle_lockds_req_msg( | |
1332 | session, fe_msg->lockds_req); | |
1333 | break; | |
1334 | case MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REQ: | |
1335 | session = mgmt_session_id2ctx( | |
1336 | fe_msg->setcfg_req->session_id); | |
1337 | session->adapter->setcfg_stats.set_cfg_count++; | |
1338 | MGMTD_FE_ADAPTER_DBG( | |
1401ee8b | 1339 | "Got Set Config Req Msg (%d Xpaths, Implicit:%c) on DS:%d for session-id %llu from '%s'", |
ef43a632 | 1340 | (int)fe_msg->setcfg_req->n_data, |
1401ee8b | 1341 | fe_msg->setcfg_req->implicit_commit ? 'T':'F', |
ef43a632 CH |
1342 | fe_msg->setcfg_req->ds_id, |
1343 | (unsigned long long)fe_msg->setcfg_req->session_id, | |
1344 | adapter->name); | |
1345 | ||
1346 | mgmt_fe_session_handle_setcfg_req_msg( | |
1347 | session, fe_msg->setcfg_req); | |
1348 | break; | |
1349 | case MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REQ: | |
1350 | session = mgmt_session_id2ctx( | |
1351 | fe_msg->commcfg_req->session_id); | |
1352 | MGMTD_FE_ADAPTER_DBG( | |
1401ee8b | 1353 | "Got Commit Config Req Msg for src-DS:%d dst-DS:%d (Abort:%c) on session-id %llu from '%s'", |
ef43a632 CH |
1354 | fe_msg->commcfg_req->src_ds_id, |
1355 | fe_msg->commcfg_req->dst_ds_id, | |
1401ee8b | 1356 | fe_msg->commcfg_req->abort ? 'T':'F', |
ef43a632 CH |
1357 | (unsigned long long)fe_msg->commcfg_req->session_id, |
1358 | adapter->name); | |
1359 | mgmt_fe_session_handle_commit_config_req_msg( | |
1360 | session, fe_msg->commcfg_req); | |
1361 | break; | |
1362 | case MGMTD__FE_MESSAGE__MESSAGE_GETCFG_REQ: | |
1363 | session = mgmt_session_id2ctx( | |
1364 | fe_msg->getcfg_req->session_id); | |
1365 | MGMTD_FE_ADAPTER_DBG( | |
1366 | "Got Get-Config Req Msg for DS:%d (xpaths: %d) on session-id %llu from '%s'", | |
1367 | fe_msg->getcfg_req->ds_id, | |
1368 | (int)fe_msg->getcfg_req->n_data, | |
1369 | (unsigned long long)fe_msg->getcfg_req->session_id, | |
1370 | adapter->name); | |
1371 | mgmt_fe_session_handle_getcfg_req_msg( | |
1372 | session, fe_msg->getcfg_req); | |
1373 | break; | |
1374 | case MGMTD__FE_MESSAGE__MESSAGE_GETDATA_REQ: | |
1375 | session = mgmt_session_id2ctx( | |
1376 | fe_msg->getdata_req->session_id); | |
1377 | MGMTD_FE_ADAPTER_DBG( | |
1378 | "Got Get-Data Req Msg for DS:%d (xpaths: %d) on session-id %llu from '%s'", | |
1379 | fe_msg->getdata_req->ds_id, | |
1380 | (int)fe_msg->getdata_req->n_data, | |
1381 | (unsigned long long)fe_msg->getdata_req->session_id, | |
1382 | adapter->name); | |
1383 | mgmt_fe_session_handle_getdata_req_msg( | |
1384 | session, fe_msg->getdata_req); | |
1385 | break; | |
1386 | case MGMTD__FE_MESSAGE__MESSAGE_NOTIFY_DATA_REQ: | |
1387 | case MGMTD__FE_MESSAGE__MESSAGE_REGNOTIFY_REQ: | |
1388 | /* | |
1389 | * TODO: Add handling code in future. | |
1390 | */ | |
1391 | break; | |
1392 | /* | |
1393 | * NOTE: The following messages are always sent from MGMTD to | |
1394 | * Frontend clients only and/or need not be handled on MGMTd. | |
1395 | */ | |
1396 | case MGMTD__FE_MESSAGE__MESSAGE_SESSION_REPLY: | |
1397 | case MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REPLY: | |
1398 | case MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REPLY: | |
1399 | case MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REPLY: | |
1400 | case MGMTD__FE_MESSAGE__MESSAGE_GETCFG_REPLY: | |
1401 | case MGMTD__FE_MESSAGE__MESSAGE_GETDATA_REPLY: | |
1402 | case MGMTD__FE_MESSAGE__MESSAGE__NOT_SET: | |
ef43a632 CH |
1403 | default: |
1404 | /* | |
1405 | * A 'default' case is being added contrary to the | |
1406 | * FRR code guidelines to take care of build | |
1407 | * failures on certain build systems (courtesy of | |
1408 | * the proto-c package). | |
1409 | */ | |
1410 | break; | |
1411 | } | |
1412 | ||
1413 | return 0; | |
1414 | } | |
1415 | ||
f82370b4 CH |
1416 | static void mgmt_fe_adapter_process_msg(void *user_ctx, uint8_t *data, |
1417 | size_t len) | |
ef43a632 | 1418 | { |
f82370b4 | 1419 | struct mgmt_fe_client_adapter *adapter = user_ctx; |
ef43a632 | 1420 | Mgmtd__FeMessage *fe_msg; |
ef43a632 | 1421 | |
f82370b4 CH |
1422 | fe_msg = mgmtd__fe_message__unpack(NULL, len, data); |
1423 | if (!fe_msg) { | |
ef43a632 | 1424 | MGMTD_FE_ADAPTER_DBG( |
f82370b4 CH |
1425 | "Failed to decode %zu bytes for adapter: %s", len, |
1426 | adapter->name); | |
1427 | return; | |
ef43a632 | 1428 | } |
f82370b4 CH |
1429 | MGMTD_FE_ADAPTER_DBG( |
1430 | "Decoded %zu bytes of message: %u from adapter: %s", len, | |
1431 | fe_msg->message_case, adapter->name); | |
1432 | (void)mgmt_fe_adapter_handle_msg(adapter, fe_msg); | |
1433 | mgmtd__fe_message__free_unpacked(fe_msg, NULL); | |
ef43a632 CH |
1434 | } |
1435 | ||
e6685141 | 1436 | static void mgmt_fe_adapter_proc_msgbufs(struct event *thread) |
ef43a632 | 1437 | { |
e16d030c | 1438 | struct mgmt_fe_client_adapter *adapter = EVENT_ARG(thread); |
ef43a632 | 1439 | |
f82370b4 CH |
1440 | if (mgmt_msg_procbufs(&adapter->mstate, mgmt_fe_adapter_process_msg, |
1441 | adapter, mgmt_debug_fe)) | |
ef43a632 CH |
1442 | mgmt_fe_adapter_register_event(adapter, MGMTD_FE_PROC_MSG); |
1443 | } | |
1444 | ||
e6685141 | 1445 | static void mgmt_fe_adapter_read(struct event *thread) |
ef43a632 | 1446 | { |
e16d030c | 1447 | struct mgmt_fe_client_adapter *adapter = EVENT_ARG(thread); |
f82370b4 | 1448 | enum mgmt_msg_rsched rv; |
ef43a632 | 1449 | |
f82370b4 CH |
1450 | rv = mgmt_msg_read(&adapter->mstate, adapter->conn_fd, mgmt_debug_fe); |
1451 | if (rv == MSR_DISCONNECT) { | |
1452 | mgmt_fe_adapter_disconnect(adapter); | |
1453 | return; | |
ef43a632 | 1454 | } |
f82370b4 | 1455 | if (rv == MSR_SCHED_BOTH) |
ef43a632 | 1456 | mgmt_fe_adapter_register_event(adapter, MGMTD_FE_PROC_MSG); |
ef43a632 CH |
1457 | mgmt_fe_adapter_register_event(adapter, MGMTD_FE_CONN_READ); |
1458 | } | |
1459 | ||
e6685141 | 1460 | static void mgmt_fe_adapter_write(struct event *thread) |
ef43a632 | 1461 | { |
e16d030c | 1462 | struct mgmt_fe_client_adapter *adapter = EVENT_ARG(thread); |
f82370b4 | 1463 | enum mgmt_msg_wsched rv; |
ef43a632 | 1464 | |
f82370b4 CH |
1465 | rv = mgmt_msg_write(&adapter->mstate, adapter->conn_fd, mgmt_debug_fe); |
1466 | if (rv == MSW_SCHED_STREAM) | |
1467 | mgmt_fe_adapter_register_event(adapter, MGMTD_FE_CONN_WRITE); | |
1468 | else if (rv == MSW_DISCONNECT) | |
1469 | mgmt_fe_adapter_disconnect(adapter); | |
1470 | else if (rv == MSW_SCHED_WRITES_OFF) { | |
ef43a632 CH |
1471 | mgmt_fe_adapter_writes_off(adapter); |
1472 | mgmt_fe_adapter_register_event(adapter, | |
f82370b4 CH |
1473 | MGMTD_FE_CONN_WRITES_ON); |
1474 | } else | |
1475 | assert(rv == MSW_SCHED_NONE); | |
ef43a632 CH |
1476 | } |
1477 | ||
e6685141 | 1478 | static void mgmt_fe_adapter_resume_writes(struct event *thread) |
ef43a632 CH |
1479 | { |
1480 | struct mgmt_fe_client_adapter *adapter; | |
1481 | ||
e16d030c | 1482 | adapter = (struct mgmt_fe_client_adapter *)EVENT_ARG(thread); |
f82370b4 | 1483 | assert(adapter && adapter->conn_fd != -1); |
ef43a632 CH |
1484 | |
1485 | mgmt_fe_adapter_writes_on(adapter); | |
1486 | } | |
1487 | ||
1488 | static void | |
1489 | mgmt_fe_adapter_register_event(struct mgmt_fe_client_adapter *adapter, | |
1490 | enum mgmt_fe_event event) | |
1491 | { | |
1492 | struct timeval tv = {0}; | |
1493 | ||
1494 | switch (event) { | |
1495 | case MGMTD_FE_CONN_READ: | |
907a2395 | 1496 | event_add_read(mgmt_fe_adapter_tm, mgmt_fe_adapter_read, |
ef43a632 | 1497 | adapter, adapter->conn_fd, &adapter->conn_read_ev); |
ef43a632 CH |
1498 | break; |
1499 | case MGMTD_FE_CONN_WRITE: | |
907a2395 | 1500 | event_add_write(mgmt_fe_adapter_tm, |
ef43a632 CH |
1501 | mgmt_fe_adapter_write, adapter, |
1502 | adapter->conn_fd, &adapter->conn_write_ev); | |
ef43a632 CH |
1503 | break; |
1504 | case MGMTD_FE_PROC_MSG: | |
1505 | tv.tv_usec = MGMTD_FE_MSG_PROC_DELAY_USEC; | |
907a2395 | 1506 | event_add_timer_tv(mgmt_fe_adapter_tm, |
ef43a632 CH |
1507 | mgmt_fe_adapter_proc_msgbufs, adapter, |
1508 | &tv, &adapter->proc_msg_ev); | |
ef43a632 CH |
1509 | break; |
1510 | case MGMTD_FE_CONN_WRITES_ON: | |
907a2395 | 1511 | event_add_timer_msec(mgmt_fe_adapter_tm, |
ef43a632 CH |
1512 | mgmt_fe_adapter_resume_writes, adapter, |
1513 | MGMTD_FE_MSG_WRITE_DELAY_MSEC, | |
1514 | &adapter->conn_writes_on); | |
ef43a632 CH |
1515 | break; |
1516 | case MGMTD_FE_SERVER: | |
1517 | assert(!"mgmt_fe_adapter_post_event() called incorrectly"); | |
1518 | break; | |
1519 | } | |
1520 | } | |
1521 | ||
1522 | void mgmt_fe_adapter_lock(struct mgmt_fe_client_adapter *adapter) | |
1523 | { | |
1524 | adapter->refcount++; | |
1525 | } | |
1526 | ||
1527 | extern void | |
1528 | mgmt_fe_adapter_unlock(struct mgmt_fe_client_adapter **adapter) | |
1529 | { | |
1530 | assert(*adapter && (*adapter)->refcount); | |
1531 | ||
1532 | (*adapter)->refcount--; | |
1533 | if (!(*adapter)->refcount) { | |
1534 | mgmt_fe_adapters_del(&mgmt_fe_adapters, *adapter); | |
e16d030c DS |
1535 | EVENT_OFF((*adapter)->conn_read_ev); |
1536 | EVENT_OFF((*adapter)->conn_write_ev); | |
1537 | EVENT_OFF((*adapter)->proc_msg_ev); | |
1538 | EVENT_OFF((*adapter)->conn_writes_on); | |
f82370b4 | 1539 | mgmt_msg_destroy(&(*adapter)->mstate); |
ef43a632 CH |
1540 | XFREE(MTYPE_MGMTD_FE_ADPATER, *adapter); |
1541 | } | |
1542 | ||
1543 | *adapter = NULL; | |
1544 | } | |
1545 | ||
cd9d0537 | 1546 | int mgmt_fe_adapter_init(struct event_loop *tm, struct mgmt_master *mm) |
ef43a632 CH |
1547 | { |
1548 | if (!mgmt_fe_adapter_tm) { | |
1549 | mgmt_fe_adapter_tm = tm; | |
1550 | mgmt_fe_adapter_mm = mm; | |
1551 | mgmt_fe_adapters_init(&mgmt_fe_adapters); | |
1552 | ||
1553 | assert(!mgmt_fe_sessions); | |
1554 | mgmt_fe_sessions = hash_create(mgmt_fe_session_hash_key, | |
1555 | mgmt_fe_session_hash_cmp, | |
1556 | "MGMT Frontend Sessions"); | |
1557 | } | |
1558 | ||
1559 | return 0; | |
1560 | } | |
1561 | ||
1562 | void mgmt_fe_adapter_destroy(void) | |
1563 | { | |
1564 | mgmt_fe_cleanup_adapters(); | |
1565 | mgmt_fe_session_hash_destroy(); | |
1566 | } | |
1567 | ||
1568 | struct mgmt_fe_client_adapter * | |
1569 | mgmt_fe_create_adapter(int conn_fd, union sockunion *from) | |
1570 | { | |
1571 | struct mgmt_fe_client_adapter *adapter = NULL; | |
1572 | ||
1573 | adapter = mgmt_fe_find_adapter_by_fd(conn_fd); | |
1574 | if (!adapter) { | |
1575 | adapter = XCALLOC(MTYPE_MGMTD_FE_ADPATER, | |
1576 | sizeof(struct mgmt_fe_client_adapter)); | |
1577 | assert(adapter); | |
1578 | ||
1579 | adapter->conn_fd = conn_fd; | |
1580 | memcpy(&adapter->conn_su, from, sizeof(adapter->conn_su)); | |
1581 | snprintf(adapter->name, sizeof(adapter->name), "Unknown-FD-%d", | |
1582 | adapter->conn_fd); | |
1583 | mgmt_fe_sessions_init(&adapter->fe_sessions); | |
f82370b4 CH |
1584 | |
1585 | mgmt_msg_init(&adapter->mstate, MGMTD_FE_MAX_NUM_MSG_PROC, | |
1586 | MGMTD_FE_MAX_NUM_MSG_WRITE, MGMTD_FE_MSG_MAX_LEN, | |
1587 | "FE-adapter"); | |
ef43a632 CH |
1588 | mgmt_fe_adapter_lock(adapter); |
1589 | ||
1590 | mgmt_fe_adapter_register_event(adapter, MGMTD_FE_CONN_READ); | |
1591 | mgmt_fe_adapters_add_tail(&mgmt_fe_adapters, adapter); | |
1592 | ||
1593 | adapter->setcfg_stats.min_tm = ULONG_MAX; | |
1594 | adapter->cmt_stats.min_tm = ULONG_MAX; | |
1595 | MGMTD_FE_ADAPTER_DBG("Added new MGMTD Frontend adapter '%s'", | |
1596 | adapter->name); | |
1597 | } | |
1598 | ||
1599 | /* Make client socket non-blocking. */ | |
1600 | set_nonblocking(adapter->conn_fd); | |
1601 | setsockopt_so_sendbuf(adapter->conn_fd, | |
1602 | MGMTD_SOCKET_FE_SEND_BUF_SIZE); | |
1603 | setsockopt_so_recvbuf(adapter->conn_fd, | |
1604 | MGMTD_SOCKET_FE_RECV_BUF_SIZE); | |
1605 | return adapter; | |
1606 | } | |
1607 | ||
1608 | struct mgmt_fe_client_adapter *mgmt_fe_get_adapter(const char *name) | |
1609 | { | |
1610 | return mgmt_fe_find_adapter_by_name(name); | |
1611 | } | |
1612 | ||
1613 | int mgmt_fe_send_set_cfg_reply(uint64_t session_id, uint64_t txn_id, | |
1614 | Mgmtd__DatastoreId ds_id, uint64_t req_id, | |
1615 | enum mgmt_result result, | |
1616 | const char *error_if_any, | |
1617 | bool implicit_commit) | |
1618 | { | |
1619 | struct mgmt_fe_session_ctx *session; | |
1620 | ||
1621 | session = mgmt_session_id2ctx(session_id); | |
1622 | if (!session || session->cfg_txn_id != txn_id) { | |
1623 | if (session) | |
1624 | MGMTD_FE_ADAPTER_ERR( | |
1625 | "Txn_id doesnot match, session txn is 0x%llx, current txn 0x%llx", | |
1626 | (unsigned long long)session->cfg_txn_id, | |
1627 | (unsigned long long)txn_id); | |
1628 | return -1; | |
1629 | } | |
1630 | ||
1631 | return mgmt_fe_send_setcfg_reply( | |
1632 | session, ds_id, req_id, result == MGMTD_SUCCESS ? true : false, | |
1633 | error_if_any, implicit_commit); | |
1634 | } | |
1635 | ||
1636 | int mgmt_fe_send_commit_cfg_reply(uint64_t session_id, uint64_t txn_id, | |
1637 | Mgmtd__DatastoreId src_ds_id, | |
1638 | Mgmtd__DatastoreId dst_ds_id, | |
1639 | uint64_t req_id, bool validate_only, | |
1640 | enum mgmt_result result, | |
1641 | const char *error_if_any) | |
1642 | { | |
1643 | struct mgmt_fe_session_ctx *session; | |
1644 | ||
1645 | session = mgmt_session_id2ctx(session_id); | |
1646 | if (!session || session->cfg_txn_id != txn_id) | |
1647 | return -1; | |
1648 | ||
1649 | return mgmt_fe_send_commitcfg_reply(session, src_ds_id, dst_ds_id, | |
1650 | req_id, result, validate_only, | |
1651 | error_if_any); | |
1652 | } | |
1653 | ||
1654 | int mgmt_fe_send_get_cfg_reply(uint64_t session_id, uint64_t txn_id, | |
1655 | Mgmtd__DatastoreId ds_id, uint64_t req_id, | |
1656 | enum mgmt_result result, | |
1657 | Mgmtd__YangDataReply *data_resp, | |
1658 | const char *error_if_any) | |
1659 | { | |
1660 | struct mgmt_fe_session_ctx *session; | |
1661 | ||
1662 | session = mgmt_session_id2ctx(session_id); | |
1663 | if (!session || session->txn_id != txn_id) | |
1664 | return -1; | |
1665 | ||
1666 | return mgmt_fe_send_getcfg_reply(session, ds_id, req_id, | |
1667 | result == MGMTD_SUCCESS, data_resp, | |
1668 | error_if_any); | |
1669 | } | |
1670 | ||
1671 | int mgmt_fe_send_get_data_reply(uint64_t session_id, uint64_t txn_id, | |
1672 | Mgmtd__DatastoreId ds_id, uint64_t req_id, | |
1673 | enum mgmt_result result, | |
1674 | Mgmtd__YangDataReply *data_resp, | |
1675 | const char *error_if_any) | |
1676 | { | |
1677 | struct mgmt_fe_session_ctx *session; | |
1678 | ||
1679 | session = mgmt_session_id2ctx(session_id); | |
1680 | if (!session || session->txn_id != txn_id) | |
1681 | return -1; | |
1682 | ||
1683 | return mgmt_fe_send_getdata_reply(session, ds_id, req_id, | |
1684 | result == MGMTD_SUCCESS, | |
1685 | data_resp, error_if_any); | |
1686 | } | |
1687 | ||
1688 | int mgmt_fe_send_data_notify(Mgmtd__DatastoreId ds_id, | |
1689 | Mgmtd__YangData * data_resp[], int num_data) | |
1690 | { | |
1691 | /* struct mgmt_fe_session_ctx *session; */ | |
1692 | ||
1693 | return 0; | |
1694 | } | |
1695 | ||
1696 | struct mgmt_setcfg_stats * | |
1697 | mgmt_fe_get_session_setcfg_stats(uint64_t session_id) | |
1698 | { | |
1699 | struct mgmt_fe_session_ctx *session; | |
1700 | ||
1701 | session = mgmt_session_id2ctx(session_id); | |
1702 | if (!session || !session->adapter) | |
1703 | return NULL; | |
1704 | ||
1705 | return &session->adapter->setcfg_stats; | |
1706 | } | |
1707 | ||
1708 | struct mgmt_commit_stats * | |
1709 | mgmt_fe_get_session_commit_stats(uint64_t session_id) | |
1710 | { | |
1711 | struct mgmt_fe_session_ctx *session; | |
1712 | ||
1713 | session = mgmt_session_id2ctx(session_id); | |
1714 | if (!session || !session->adapter) | |
1715 | return NULL; | |
1716 | ||
1717 | return &session->adapter->cmt_stats; | |
1718 | } | |
1719 | ||
1720 | static void | |
1721 | mgmt_fe_adapter_cmt_stats_write(struct vty *vty, | |
1722 | struct mgmt_fe_client_adapter *adapter) | |
1723 | { | |
1724 | char buf[100] = {0}; | |
1725 | ||
1726 | if (!mm->perf_stats_en) | |
1727 | return; | |
1728 | ||
1729 | vty_out(vty, " Num-Commits: \t\t\t%lu\n", | |
1730 | adapter->cmt_stats.commit_cnt); | |
1731 | if (adapter->cmt_stats.commit_cnt > 0) { | |
1732 | if (mm->perf_stats_en) | |
1733 | vty_out(vty, " Max-Commit-Duration: \t\t%lu uSecs\n", | |
1734 | adapter->cmt_stats.max_tm); | |
1735 | vty_out(vty, " Max-Commit-Batch-Size: \t\t%lu\n", | |
1736 | adapter->cmt_stats.max_batch_cnt); | |
1737 | if (mm->perf_stats_en) | |
1738 | vty_out(vty, " Min-Commit-Duration: \t\t%lu uSecs\n", | |
1739 | adapter->cmt_stats.min_tm); | |
1740 | vty_out(vty, " Min-Commit-Batch-Size: \t\t%lu\n", | |
1741 | adapter->cmt_stats.min_batch_cnt); | |
1742 | if (mm->perf_stats_en) | |
1743 | vty_out(vty, | |
1744 | " Last-Commit-Duration: \t\t%lu uSecs\n", | |
1745 | adapter->cmt_stats.last_exec_tm); | |
1746 | vty_out(vty, " Last-Commit-Batch-Size: \t\t%lu\n", | |
1747 | adapter->cmt_stats.last_batch_cnt); | |
1748 | vty_out(vty, " Last-Commit-CfgData-Reqs: \t\t%lu\n", | |
1749 | adapter->cmt_stats.last_num_cfgdata_reqs); | |
1750 | vty_out(vty, " Last-Commit-CfgApply-Reqs: \t\t%lu\n", | |
1751 | adapter->cmt_stats.last_num_apply_reqs); | |
1752 | if (mm->perf_stats_en) { | |
1753 | vty_out(vty, " Last-Commit-Details:\n"); | |
1754 | vty_out(vty, " Commit Start: \t\t\t%s\n", | |
1755 | mgmt_realtime_to_string( | |
1756 | &adapter->cmt_stats.last_start, buf, | |
1757 | sizeof(buf))); | |
1758 | #ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED | |
1759 | vty_out(vty, " Config-Validate Start: \t\t%s\n", | |
1760 | mgmt_realtime_to_string( | |
1761 | &adapter->cmt_stats.validate_start, buf, | |
1762 | sizeof(buf))); | |
1763 | #endif | |
1764 | vty_out(vty, " Prep-Config Start: \t\t%s\n", | |
1765 | mgmt_realtime_to_string( | |
1766 | &adapter->cmt_stats.prep_cfg_start, buf, | |
1767 | sizeof(buf))); | |
1768 | vty_out(vty, " Txn-Create Start: \t\t%s\n", | |
1769 | mgmt_realtime_to_string( | |
1770 | &adapter->cmt_stats.txn_create_start, | |
1771 | buf, sizeof(buf))); | |
1772 | vty_out(vty, | |
1773 | #ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED | |
1774 | " Send-Config Start: \t\t%s\n", | |
1775 | #else | |
1776 | " Send-Config-Validate Start: \t%s\n", | |
1777 | #endif | |
1778 | mgmt_realtime_to_string( | |
1779 | &adapter->cmt_stats.send_cfg_start, buf, | |
1780 | sizeof(buf))); | |
1781 | vty_out(vty, " Apply-Config Start: \t\t%s\n", | |
1782 | mgmt_realtime_to_string( | |
1783 | &adapter->cmt_stats.apply_cfg_start, buf, | |
1784 | sizeof(buf))); | |
1785 | vty_out(vty, " Apply-Config End: \t\t%s\n", | |
1786 | mgmt_realtime_to_string( | |
1787 | &adapter->cmt_stats.apply_cfg_end, buf, | |
1788 | sizeof(buf))); | |
1789 | vty_out(vty, " Txn-Delete Start: \t\t%s\n", | |
1790 | mgmt_realtime_to_string( | |
1791 | &adapter->cmt_stats.txn_del_start, buf, | |
1792 | sizeof(buf))); | |
1793 | vty_out(vty, " Commit End: \t\t\t%s\n", | |
1794 | mgmt_realtime_to_string( | |
1795 | &adapter->cmt_stats.last_end, buf, | |
1796 | sizeof(buf))); | |
1797 | } | |
1798 | } | |
1799 | } | |
1800 | ||
1801 | static void | |
1802 | mgmt_fe_adapter_setcfg_stats_write(struct vty *vty, | |
1803 | struct mgmt_fe_client_adapter *adapter) | |
1804 | { | |
1805 | char buf[100] = {0}; | |
1806 | ||
1807 | if (!mm->perf_stats_en) | |
1808 | return; | |
1809 | ||
1810 | vty_out(vty, " Num-Set-Cfg: \t\t\t%lu\n", | |
1811 | adapter->setcfg_stats.set_cfg_count); | |
1812 | if (mm->perf_stats_en && adapter->setcfg_stats.set_cfg_count > 0) { | |
1813 | vty_out(vty, " Max-Set-Cfg-Duration: \t\t%lu uSec\n", | |
1814 | adapter->setcfg_stats.max_tm); | |
1815 | vty_out(vty, " Min-Set-Cfg-Duration: \t\t%lu uSec\n", | |
1816 | adapter->setcfg_stats.min_tm); | |
1817 | vty_out(vty, " Avg-Set-Cfg-Duration: \t\t%lu uSec\n", | |
1818 | adapter->setcfg_stats.avg_tm); | |
1819 | vty_out(vty, " Last-Set-Cfg-Details:\n"); | |
1820 | vty_out(vty, " Set-Cfg Start: \t\t\t%s\n", | |
1821 | mgmt_realtime_to_string(&adapter->setcfg_stats.last_start, | |
1822 | buf, sizeof(buf))); | |
1823 | vty_out(vty, " Set-Cfg End: \t\t\t%s\n", | |
1824 | mgmt_realtime_to_string(&adapter->setcfg_stats.last_end, | |
1825 | buf, sizeof(buf))); | |
1826 | } | |
1827 | } | |
1828 | ||
1829 | void mgmt_fe_adapter_status_write(struct vty *vty, bool detail) | |
1830 | { | |
1831 | struct mgmt_fe_client_adapter *adapter; | |
1832 | struct mgmt_fe_session_ctx *session; | |
1833 | Mgmtd__DatastoreId ds_id; | |
1834 | bool locked = false; | |
1835 | ||
1836 | vty_out(vty, "MGMTD Frontend Adpaters\n"); | |
1837 | ||
1838 | FOREACH_ADAPTER_IN_LIST (adapter) { | |
1839 | vty_out(vty, " Client: \t\t\t\t%s\n", adapter->name); | |
1840 | vty_out(vty, " Conn-FD: \t\t\t\t%d\n", adapter->conn_fd); | |
1841 | if (detail) { | |
1842 | mgmt_fe_adapter_setcfg_stats_write(vty, adapter); | |
1843 | mgmt_fe_adapter_cmt_stats_write(vty, adapter); | |
1844 | } | |
1845 | vty_out(vty, " Sessions\n"); | |
1846 | FOREACH_SESSION_IN_LIST (adapter, session) { | |
1847 | vty_out(vty, " Session: \t\t\t\t%p\n", session); | |
1848 | vty_out(vty, " Client-Id: \t\t\t%llu\n", | |
1849 | (unsigned long long)session->client_id); | |
1850 | vty_out(vty, " Session-Id: \t\t\t%llx\n", | |
1851 | (unsigned long long)session->session_id); | |
1852 | vty_out(vty, " DS-Locks:\n"); | |
1853 | FOREACH_MGMTD_DS_ID (ds_id) { | |
1854 | if (session->ds_write_locked[ds_id] | |
1855 | || session->ds_read_locked[ds_id]) { | |
1856 | locked = true; | |
1857 | vty_out(vty, | |
1858 | " %s\t\t\t%s, %s\n", | |
1859 | mgmt_ds_id2name(ds_id), | |
1860 | session->ds_write_locked[ds_id] | |
1861 | ? "Write" | |
1862 | : "Read", | |
1863 | session->ds_locked_implict[ds_id] | |
1864 | ? "Implicit" | |
1865 | : "Explicit"); | |
1866 | } | |
1867 | } | |
1868 | if (!locked) | |
1869 | vty_out(vty, " None\n"); | |
1870 | } | |
1871 | vty_out(vty, " Total-Sessions: \t\t\t%d\n", | |
1872 | (int)mgmt_fe_sessions_count(&adapter->fe_sessions)); | |
f82370b4 CH |
1873 | vty_out(vty, " Msg-Recvd: \t\t\t\t%" PRIu64 "\n", |
1874 | adapter->mstate.nrxm); | |
1875 | vty_out(vty, " Bytes-Recvd: \t\t\t%" PRIu64 "\n", | |
1876 | adapter->mstate.nrxb); | |
1877 | vty_out(vty, " Msg-Sent: \t\t\t\t%" PRIu64 "\n", | |
1878 | adapter->mstate.ntxm); | |
1879 | vty_out(vty, " Bytes-Sent: \t\t\t%" PRIu64 "\n", | |
1880 | adapter->mstate.ntxb); | |
ef43a632 CH |
1881 | } |
1882 | vty_out(vty, " Total: %d\n", | |
1883 | (int)mgmt_fe_adapters_count(&mgmt_fe_adapters)); | |
1884 | } | |
1885 | ||
1886 | void mgmt_fe_adapter_perf_measurement(struct vty *vty, bool config) | |
1887 | { | |
1888 | mm->perf_stats_en = config; | |
1889 | } | |
1890 | ||
1891 | void mgmt_fe_adapter_reset_perf_stats(struct vty *vty) | |
1892 | { | |
1893 | struct mgmt_fe_client_adapter *adapter; | |
1894 | struct mgmt_fe_session_ctx *session; | |
1895 | ||
1896 | FOREACH_ADAPTER_IN_LIST (adapter) { | |
1897 | memset(&adapter->setcfg_stats, 0, sizeof(adapter->setcfg_stats)); | |
1898 | FOREACH_SESSION_IN_LIST (adapter, session) { | |
1899 | memset(&adapter->cmt_stats, 0, sizeof(adapter->cmt_stats)); | |
1900 | } | |
1901 | } | |
1902 | } |