2 * Copyright (c) 2017-2020 Red Hat, Inc.
6 * Author: Jan Friesse (jfriesse@redhat.com)
8 * This software licensed under BSD license, the text of which follows:
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
13 * - Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * - Neither the name of the Red Hat, Inc. nor the names of its
19 * contributors may be used to endorse or promote products derived from this
20 * software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 * THE POSSIBILITY OF SUCH DAMAGE.
36 #include "qdevice-net-algorithm.h"
37 #include "qdevice-net-cast-vote-timer.h"
38 #include "qdevice-net-heuristics.h"
39 #include "qdevice-net-send.h"
40 #include "qdevice-net-votequorum.h"
43 qdevice_net_heuristics_exec_result_to_tlv(enum qdevice_heuristics_exec_result exec_result
)
45 enum tlv_heuristics res
;
47 switch (exec_result
) {
48 case QDEVICE_HEURISTICS_EXEC_RESULT_DISABLED
: res
= TLV_HEURISTICS_UNDEFINED
; break;
49 case QDEVICE_HEURISTICS_EXEC_RESULT_PASS
: res
= TLV_HEURISTICS_PASS
; break;
50 case QDEVICE_HEURISTICS_EXEC_RESULT_FAIL
: res
= TLV_HEURISTICS_FAIL
; break;
52 log(LOG_ERR
, "qdevice_net_heuristics_exec_result_to_tlv: Unhandled "
53 "heuristics exec result %s",
54 qdevice_heuristics_exec_result_to_str(exec_result
));
63 qdevice_net_regular_heuristics_exec_result_callback(uint32_t seq_number
,
64 enum qdevice_heuristics_exec_result exec_result
, void *user_data1
, void *user_data2
)
66 struct qdevice_heuristics_instance
*heuristics_instance
;
67 struct qdevice_instance
*instance
;
68 struct qdevice_net_instance
*net_instance
;
71 enum tlv_heuristics heuristics
;
73 instance
= (struct qdevice_instance
*)user_data1
;
74 heuristics_instance
= &instance
->heuristics_instance
;
75 net_instance
= instance
->model_data
;
77 if (qdevice_heuristics_result_notifier_list_set_active(&heuristics_instance
->exec_result_notifier_list
,
78 qdevice_net_regular_heuristics_exec_result_callback
, 0) != 0) {
79 log(LOG_ERR
, "Can't deactivate net regular heuristics exec callback notifier");
81 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_ACTIVATE_HEURISTICS_RESULT_NOTIFIER
;
82 net_instance
->schedule_disconnect
= 1;
87 heuristics
= qdevice_net_heuristics_exec_result_to_tlv(exec_result
);
89 if (exec_result
== QDEVICE_HEURISTICS_EXEC_RESULT_DISABLED
) {
91 * Can happen when user disables heuristics during runtime
96 if (net_instance
->latest_heuristics_result
!= heuristics
) {
97 log(heuristics
== TLV_HEURISTICS_PASS
? LOG_NOTICE
: LOG_ERR
,
98 "Heuristics result changed from %s to %s",
99 tlv_heuristics_to_str(net_instance
->latest_heuristics_result
),
100 tlv_heuristics_to_str(heuristics
));
102 if (net_instance
->state
!= QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS
) {
104 * Not connected to qnetd
111 vote
= TLV_VOTE_NO_CHANGE
;
113 if (qdevice_net_algorithm_heuristics_change(net_instance
, &heuristics
, &send_msg
,
115 log(LOG_ERR
, "Algorithm returned error. Disconnecting.");
117 net_instance
->disconnect_reason
=
118 QDEVICE_NET_DISCONNECT_REASON_ALGO_HEURISTICS_CHANGE_ERR
;
119 net_instance
->schedule_disconnect
= 1;
123 log(LOG_DEBUG
, "Algorithm decided to %s message with heuristics result "
124 "%s and result vote is %s", (send_msg
? "send" : "not send"),
125 tlv_heuristics_to_str(heuristics
), tlv_vote_to_str(vote
));
129 if (heuristics
== TLV_HEURISTICS_UNDEFINED
) {
130 log(LOG_ERR
, "Inconsistent algorithm result. "
131 "It's not possible to send message with undefined heuristics. "
134 net_instance
->disconnect_reason
=
135 QDEVICE_NET_DISCONNECT_REASON_ALGO_HEURISTICS_CHANGE_ERR
;
136 net_instance
->schedule_disconnect
= 1;
141 if (!net_instance
->server_supports_heuristics
) {
142 log(LOG_ERR
, "Server doesn't support heuristics. "
145 net_instance
->disconnect_reason
=
146 QDEVICE_NET_DISCONNECT_REASON_SERVER_DOESNT_SUPPORT_REQUIRED_OPT
;
147 net_instance
->schedule_disconnect
= 1;
152 if (qdevice_net_send_heuristics_change(net_instance
, heuristics
) != 0) {
153 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER
;
154 net_instance
->schedule_disconnect
= 1;
160 if (qdevice_net_cast_vote_timer_update(net_instance
, vote
) != 0) {
161 log(LOG_CRIT
, "qdevice_net_heuristics_exec_result_callback "
162 "Can't update cast vote timer");
164 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER
;
165 net_instance
->schedule_disconnect
= 1;
171 net_instance
->latest_regular_heuristics_result
= heuristics
;
172 net_instance
->latest_heuristics_result
= heuristics
;
174 if (qdevice_net_heuristics_schedule_timer(net_instance
) != 0) {
182 qdevice_net_connect_heuristics_exec_result_callback(uint32_t seq_number
,
183 enum qdevice_heuristics_exec_result exec_result
, void *user_data1
, void *user_data2
)
185 struct qdevice_heuristics_instance
*heuristics_instance
;
186 struct qdevice_instance
*instance
;
187 struct qdevice_net_instance
*net_instance
;
189 enum tlv_heuristics heuristics
;
190 int send_config_node_list
;
191 int send_membership_node_list
;
192 int send_quorum_node_list
;
193 struct tlv_ring_id tlv_rid
;
194 enum tlv_quorate quorate
;
196 instance
= (struct qdevice_instance
*)user_data1
;
197 heuristics_instance
= &instance
->heuristics_instance
;
198 net_instance
= instance
->model_data
;
200 if (qdevice_heuristics_result_notifier_list_set_active(&heuristics_instance
->exec_result_notifier_list
,
201 qdevice_net_connect_heuristics_exec_result_callback
, 0) != 0) {
202 log(LOG_ERR
, "Can't deactivate net connect heuristics exec callback notifier");
204 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_ACTIVATE_HEURISTICS_RESULT_NOTIFIER
;
205 net_instance
->schedule_disconnect
= 1;
210 if (net_instance
->state
!= QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS
) {
212 * Not connected to qnetd -> heuristics will be called again on new connect
217 heuristics
= qdevice_net_heuristics_exec_result_to_tlv(exec_result
);
219 send_config_node_list
= 1;
220 send_membership_node_list
= 1;
221 send_quorum_node_list
= 1;
222 vote
= TLV_VOTE_WAIT_FOR_REPLY
;
224 if (qdevice_net_algorithm_connected(net_instance
, &heuristics
, &send_config_node_list
,
225 &send_membership_node_list
, &send_quorum_node_list
, &vote
) != 0) {
226 log(LOG_DEBUG
, "Algorithm returned error. Disconnecting.");
227 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_ALGO_CONNECTED_ERR
;
230 log(LOG_DEBUG
, "Algorithm decided to %s config node list, %s membership "
231 "node list, %s quorum node list, heuristics is %s and result vote is %s",
232 (send_config_node_list
? "send" : "not send"),
233 (send_membership_node_list
? "send" : "not send"),
234 (send_quorum_node_list
? "send" : "not send"),
235 tlv_heuristics_to_str(heuristics
),
236 tlv_vote_to_str(vote
));
240 * Now we can finally really send node list, votequorum node list and update timer
242 if (send_config_node_list
) {
243 if (qdevice_net_send_config_node_list(net_instance
,
244 &instance
->config_node_list
,
245 instance
->config_node_list_version_set
,
246 instance
->config_node_list_version
, 1) != 0) {
247 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER
;
252 if (send_membership_node_list
) {
253 qdevice_net_votequorum_ring_id_to_tlv(&tlv_rid
,
254 &instance
->vq_node_list_ring_id
);
256 if (qdevice_net_send_membership_node_list(net_instance
, &tlv_rid
,
257 instance
->vq_node_list_entries
,
258 instance
->vq_node_list
,
260 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER
;
265 if (send_quorum_node_list
) {
266 quorate
= (instance
->vq_quorum_quorate
?
267 TLV_QUORATE_QUORATE
: TLV_QUORATE_INQUORATE
);
269 if (qdevice_net_send_quorum_node_list(net_instance
,
271 instance
->vq_quorum_node_list_entries
,
272 instance
->vq_quorum_node_list
) != 0) {
273 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER
;
278 if (qdevice_net_cast_vote_timer_update(net_instance
, vote
) != 0) {
279 log(LOG_CRIT
, "qdevice_net_msg_received_set_option_reply fatal error. "
280 " Can't update cast vote timer vote");
281 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER
;
284 net_instance
->state
= QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS
;
285 net_instance
->connected_since_time
= time(NULL
);
287 net_instance
->latest_connect_heuristics_result
= heuristics
;
288 net_instance
->latest_heuristics_result
= heuristics
;
294 qdevice_net_heuristics_timer_callback(void *data1
, void *data2
)
296 struct qdevice_net_instance
*net_instance
;
297 struct qdevice_heuristics_instance
*heuristics_instance
;
299 net_instance
= (struct qdevice_net_instance
*)data1
;
300 heuristics_instance
= &net_instance
->qdevice_instance_ptr
->heuristics_instance
;
302 if (qdevice_heuristics_waiting_for_result(heuristics_instance
)) {
303 log(LOG_DEBUG
, "Not executing regular heuristics because other heuristics is already running.");
308 net_instance
->regular_heuristics_timer
= NULL
;
310 log(LOG_DEBUG
, "Executing regular heuristics.");
312 if (qdevice_heuristics_result_notifier_list_set_active(&heuristics_instance
->exec_result_notifier_list
,
313 qdevice_net_regular_heuristics_exec_result_callback
, 1) != 0) {
314 log(LOG_ERR
, "Can't activate net regular heuristics exec callback notifier");
316 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_ACTIVATE_HEURISTICS_RESULT_NOTIFIER
;
317 net_instance
->schedule_disconnect
= 1;
322 if (qdevice_heuristics_exec(heuristics_instance
,
323 net_instance
->qdevice_instance_ptr
->sync_in_progress
) != 0) {
324 log(LOG_ERR
, "Can't execute regular heuristics.");
326 net_instance
->schedule_disconnect
= 1;
327 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_START_HEURISTICS
;
333 * Do not schedule this callback again. It's going to be scheduled in the
334 * qdevice_net_heuristics_exec_result_callback
340 qdevice_net_heuristics_stop_timer(struct qdevice_net_instance
*net_instance
)
342 struct qdevice_instance
*instance
;
343 struct qdevice_heuristics_instance
*heuristics_instance
;
345 instance
= net_instance
->qdevice_instance_ptr
;
346 heuristics_instance
= &instance
->heuristics_instance
;
348 if (net_instance
->regular_heuristics_timer
!= NULL
) {
349 log(LOG_DEBUG
, "Regular heuristics timer stopped");
351 timer_list_delete(&net_instance
->main_timer_list
, net_instance
->regular_heuristics_timer
);
352 net_instance
->regular_heuristics_timer
= NULL
;
354 if (qdevice_heuristics_result_notifier_list_set_active(&heuristics_instance
->exec_result_notifier_list
,
355 qdevice_net_regular_heuristics_exec_result_callback
, 0) != 0) {
356 log(LOG_ERR
, "Can't deactivate net regular heuristics exec callback notifier");
358 net_instance
->disconnect_reason
=
359 QDEVICE_NET_DISCONNECT_REASON_CANT_ACTIVATE_HEURISTICS_RESULT_NOTIFIER
;
360 net_instance
->schedule_disconnect
= 1;
369 qdevice_net_heuristics_schedule_timer(struct qdevice_net_instance
*net_instance
)
372 struct qdevice_instance
*instance
;
373 struct qdevice_heuristics_instance
*heuristics_instance
;
375 instance
= net_instance
->qdevice_instance_ptr
;
376 heuristics_instance
= &instance
->heuristics_instance
;
378 if (heuristics_instance
->mode
!= QDEVICE_HEURISTICS_MODE_ENABLED
) {
379 log(LOG_DEBUG
, "Not scheduling heuristics timer because mode is not enabled");
381 if (qdevice_net_heuristics_stop_timer(net_instance
) != 0) {
388 if (net_instance
->regular_heuristics_timer
!= NULL
) {
389 log(LOG_DEBUG
, "Not scheduling heuristics timer because it is already scheduled");
394 interval
= heuristics_instance
->interval
;
396 log(LOG_DEBUG
, "Scheduling next regular heuristics in %"PRIu32
"ms", interval
);
398 net_instance
->regular_heuristics_timer
= timer_list_add(&net_instance
->main_timer_list
,
400 qdevice_net_heuristics_timer_callback
,
401 (void *)net_instance
, NULL
);
403 if (net_instance
->regular_heuristics_timer
== NULL
) {
404 log(LOG_ERR
, "Can't schedule regular heuristics.");
406 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_HEURISTICS_TIMER
;
407 net_instance
->schedule_disconnect
= 1;
415 qdevice_net_heuristics_init(struct qdevice_net_instance
*net_instance
)
418 if (qdevice_heuristics_result_notifier_list_add(
419 &net_instance
->qdevice_instance_ptr
->heuristics_instance
.exec_result_notifier_list
,
420 qdevice_net_regular_heuristics_exec_result_callback
,
421 net_instance
->qdevice_instance_ptr
, NULL
) == NULL
) {
422 log(LOG_ERR
, "Can't add net regular heuristics exec callback into notifier");
427 if (qdevice_heuristics_result_notifier_list_add(
428 &net_instance
->qdevice_instance_ptr
->heuristics_instance
.exec_result_notifier_list
,
429 qdevice_net_connect_heuristics_exec_result_callback
,
430 net_instance
->qdevice_instance_ptr
, NULL
) == NULL
) {
431 log(LOG_ERR
, "Can't add net connect heuristics exec callback into notifier");
440 qdevice_net_heuristics_exec_after_connect(struct qdevice_net_instance
*net_instance
)
442 struct qdevice_instance
*instance
;
443 struct qdevice_heuristics_instance
*heuristics_instance
;
445 instance
= net_instance
->qdevice_instance_ptr
;
446 heuristics_instance
= &instance
->heuristics_instance
;
448 log(LOG_DEBUG
, "Executing after-connect heuristics.");
450 if (qdevice_heuristics_result_notifier_list_set_active(&heuristics_instance
->exec_result_notifier_list
,
451 qdevice_net_connect_heuristics_exec_result_callback
, 1) != 0) {
452 log(LOG_ERR
, "Can't activate net connect heuristics exec callback notifier");
454 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_ACTIVATE_HEURISTICS_RESULT_NOTIFIER
;
455 net_instance
->schedule_disconnect
= 1;
460 if (qdevice_heuristics_exec(heuristics_instance
,
461 instance
->sync_in_progress
) != 0) {
462 log(LOG_ERR
, "Can't execute connect heuristics.");
464 net_instance
->schedule_disconnect
= 1;
465 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_START_HEURISTICS
;