corosync  3.0.0
exec/votequorum.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2009-2015 Red Hat, Inc.
3  *
4  * All rights reserved.
5  *
6  * Authors: Christine Caulfield (ccaulfie@redhat.com)
7  * Fabio M. Di Nitto (fdinitto@redhat.com)
8  *
9  * This software licensed under BSD license, the text of which follows:
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions are met:
13  *
14  * - Redistributions of source code must retain the above copyright notice,
15  * this list of conditions and the following disclaimer.
16  * - Redistributions in binary form must reproduce the above copyright notice,
17  * this list of conditions and the following disclaimer in the documentation
18  * and/or other materials provided with the distribution.
19  * - Neither the name of the MontaVista Software, Inc. nor the names of its
20  * contributors may be used to endorse or promote products derived from this
21  * software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTIBUTORS "AS IS"
24  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
33  * THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include <config.h>
37 
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <stdint.h>
42 #include <unistd.h>
43 
44 #include <qb/qblist.h>
45 #include <qb/qbipc_common.h>
46 
47 #include "quorum.h"
48 #include <corosync/corodefs.h>
49 #include <corosync/logsys.h>
50 #include <corosync/coroapi.h>
51 #include <corosync/icmap.h>
52 #include <corosync/votequorum.h>
54 
55 #include "service.h"
56 #include "util.h"
57 
58 LOGSYS_DECLARE_SUBSYS ("VOTEQ");
59 
60 /*
61  * interface with corosync
62  */
63 
64 static struct corosync_api_v1 *corosync_api;
65 
66 /*
67  * votequorum global config vars
68  */
69 
70 
71 static char qdevice_name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN];
72 static struct cluster_node *qdevice = NULL;
73 static unsigned int qdevice_timeout = VOTEQUORUM_QDEVICE_DEFAULT_TIMEOUT;
74 static unsigned int qdevice_sync_timeout = VOTEQUORUM_QDEVICE_DEFAULT_SYNC_TIMEOUT;
75 static uint8_t qdevice_can_operate = 1;
76 static void *qdevice_reg_conn = NULL;
77 static uint8_t qdevice_master_wins = 0;
78 
79 static uint8_t two_node = 0;
80 
81 static uint8_t wait_for_all = 0;
82 static uint8_t wait_for_all_status = 0;
83 
84 static enum {ATB_NONE, ATB_LOWEST, ATB_HIGHEST, ATB_LIST} auto_tie_breaker = ATB_NONE, initial_auto_tie_breaker = ATB_NONE;
85 static int lowest_node_id = -1;
86 static int highest_node_id = -1;
87 
88 #define DEFAULT_LMS_WIN 10000
89 static uint8_t last_man_standing = 0;
90 static uint32_t last_man_standing_window = DEFAULT_LMS_WIN;
91 
92 static uint8_t allow_downscale = 0;
93 static uint32_t ev_barrier = 0;
94 
95 static uint8_t ev_tracking = 0;
96 static uint32_t ev_tracking_barrier = 0;
97 static int ev_tracking_fd = -1;
98 
99 /*
100  * votequorum_exec defines/structs/forward definitions
101  */
104  struct qb_ipc_request_header header __attribute__((aligned(8)));
105  uint32_t nodeid;
106  uint32_t votes;
107  uint32_t expected_votes;
108  uint32_t flags;
109 } __attribute__((packed));
110 
112  struct qb_ipc_request_header header __attribute__((aligned(8)));
113  uint32_t nodeid;
114  uint32_t value;
115  uint8_t param;
116  uint8_t _pad0;
117  uint8_t _pad1;
118  uint8_t _pad2;
119 } __attribute__((packed));
120 
122  struct qb_ipc_request_header header __attribute__((aligned(8)));
123  uint32_t operation;
125 } __attribute__((packed));
126 
128  struct qb_ipc_request_header header __attribute__((aligned(8)));
131 } __attribute__((packed));
132 
133 /*
134  * votequorum_exec onwire version (via totem)
135  */
136 
137 #include "votequorum.h"
138 
139 /*
140  * votequorum_exec onwire messages (via totem)
141  */
142 
143 #define MESSAGE_REQ_EXEC_VOTEQUORUM_NODEINFO 0
144 #define MESSAGE_REQ_EXEC_VOTEQUORUM_RECONFIGURE 1
145 #define MESSAGE_REQ_EXEC_VOTEQUORUM_QDEVICE_REG 2
146 #define MESSAGE_REQ_EXEC_VOTEQUORUM_QDEVICE_RECONFIGURE 3
147 
148 static void votequorum_exec_send_expectedvotes_notification(void);
149 static int votequorum_exec_send_quorum_notification(void *conn, uint64_t context);
150 static int votequorum_exec_send_nodelist_notification(void *conn, uint64_t context);
151 
152 #define VOTEQUORUM_RECONFIG_PARAM_EXPECTED_VOTES 1
153 #define VOTEQUORUM_RECONFIG_PARAM_NODE_VOTES 2
154 #define VOTEQUORUM_RECONFIG_PARAM_CANCEL_WFA 3
155 
156 static int votequorum_exec_send_reconfigure(uint8_t param, unsigned int nodeid, uint32_t value);
157 
158 /*
159  * used by req_exec_quorum_qdevice_reg
160  */
161 #define VOTEQUORUM_QDEVICE_OPERATION_UNREGISTER 0
162 #define VOTEQUORUM_QDEVICE_OPERATION_REGISTER 1
163 
164 /*
165  * votequorum internal node status/view
166  */
167 
168 #define NODE_FLAGS_QUORATE 1
169 #define NODE_FLAGS_LEAVING 2
170 #define NODE_FLAGS_WFASTATUS 4
171 #define NODE_FLAGS_FIRST 8
172 #define NODE_FLAGS_QDEVICE_REGISTERED 16
173 #define NODE_FLAGS_QDEVICE_ALIVE 32
174 #define NODE_FLAGS_QDEVICE_CAST_VOTE 64
175 #define NODE_FLAGS_QDEVICE_MASTER_WINS 128
176 
177 typedef enum {
181 } nodestate_t;
182 
183 struct cluster_node {
184  int node_id;
185  nodestate_t state;
186  uint32_t votes;
187  uint32_t expected_votes;
188  uint32_t flags;
189  struct qb_list_head list;
190 };
191 
192 /*
193  * votequorum internal quorum status
194  */
195 
196 static uint8_t quorum;
197 static uint8_t cluster_is_quorate;
198 
199 /*
200  * votequorum membership data
201  */
202 
203 static struct cluster_node *us;
204 static struct qb_list_head cluster_members_list;
205 static unsigned int quorum_members[PROCESSOR_COUNT_MAX];
206 static unsigned int previous_quorum_members[PROCESSOR_COUNT_MAX];
207 static unsigned int atb_nodelist[PROCESSOR_COUNT_MAX];
208 static int quorum_members_entries = 0;
209 static int previous_quorum_members_entries = 0;
210 static int atb_nodelist_entries = 0;
211 static struct memb_ring_id quorum_ringid;
212 
213 /*
214  * pre allocate all cluster_nodes + one for qdevice
215  */
216 static struct cluster_node cluster_nodes[PROCESSOR_COUNT_MAX+2];
217 static int cluster_nodes_entries = 0;
218 
219 /*
220  * votequorum tracking
221  */
222 struct quorum_pd {
223  unsigned char track_flags;
226  struct qb_list_head list;
227  void *conn;
228 };
229 
230 static struct qb_list_head trackers_list;
231 
232 /*
233  * votequorum timers
234  */
235 
236 static corosync_timer_handle_t qdevice_timer;
237 static int qdevice_timer_set = 0;
238 static corosync_timer_handle_t last_man_standing_timer;
239 static int last_man_standing_timer_set = 0;
240 static int sync_nodeinfo_sent = 0;
241 static int sync_wait_for_poll_or_timeout = 0;
242 
243 /*
244  * Service Interfaces required by service_message_handler struct
245  */
246 
247 static int sync_in_progress = 0;
248 
249 static void votequorum_sync_init (
250  const unsigned int *trans_list,
251  size_t trans_list_entries,
252  const unsigned int *member_list,
253  size_t member_list_entries,
254  const struct memb_ring_id *ring_id);
255 
256 static int votequorum_sync_process (void);
257 static void votequorum_sync_activate (void);
258 static void votequorum_sync_abort (void);
259 
260 static quorum_set_quorate_fn_t quorum_callback;
261 
262 /*
263  * votequorum_exec handler and definitions
264  */
265 
266 static char *votequorum_exec_init_fn (struct corosync_api_v1 *api);
267 static int votequorum_exec_exit_fn (void);
268 static int votequorum_exec_send_nodeinfo(uint32_t nodeid);
269 
270 static void message_handler_req_exec_votequorum_nodeinfo (
271  const void *message,
272  unsigned int nodeid);
273 static void exec_votequorum_nodeinfo_endian_convert (void *message);
274 
275 static void message_handler_req_exec_votequorum_reconfigure (
276  const void *message,
277  unsigned int nodeid);
278 static void exec_votequorum_reconfigure_endian_convert (void *message);
279 
280 static void message_handler_req_exec_votequorum_qdevice_reg (
281  const void *message,
282  unsigned int nodeid);
283 static void exec_votequorum_qdevice_reg_endian_convert (void *message);
284 
285 static void message_handler_req_exec_votequorum_qdevice_reconfigure (
286  const void *message,
287  unsigned int nodeid);
288 static void exec_votequorum_qdevice_reconfigure_endian_convert (void *message);
289 
290 static struct corosync_exec_handler votequorum_exec_engine[] =
291 {
292  { /* 0 */
293  .exec_handler_fn = message_handler_req_exec_votequorum_nodeinfo,
294  .exec_endian_convert_fn = exec_votequorum_nodeinfo_endian_convert
295  },
296  { /* 1 */
297  .exec_handler_fn = message_handler_req_exec_votequorum_reconfigure,
298  .exec_endian_convert_fn = exec_votequorum_reconfigure_endian_convert
299  },
300  { /* 2 */
301  .exec_handler_fn = message_handler_req_exec_votequorum_qdevice_reg,
302  .exec_endian_convert_fn = exec_votequorum_qdevice_reg_endian_convert
303  },
304  { /* 3 */
305  .exec_handler_fn = message_handler_req_exec_votequorum_qdevice_reconfigure,
306  .exec_endian_convert_fn = exec_votequorum_qdevice_reconfigure_endian_convert
307  },
308 };
309 
310 /*
311  * Library Handler and Functions Definitions
312  */
313 
314 static int quorum_lib_init_fn (void *conn);
315 
316 static int quorum_lib_exit_fn (void *conn);
317 
318 static void qdevice_timer_fn(void *arg);
319 
320 static void message_handler_req_lib_votequorum_getinfo (void *conn,
321  const void *message);
322 
323 static void message_handler_req_lib_votequorum_setexpected (void *conn,
324  const void *message);
325 
326 static void message_handler_req_lib_votequorum_setvotes (void *conn,
327  const void *message);
328 
329 static void message_handler_req_lib_votequorum_trackstart (void *conn,
330  const void *message);
331 
332 static void message_handler_req_lib_votequorum_trackstop (void *conn,
333  const void *message);
334 
335 static void message_handler_req_lib_votequorum_qdevice_register (void *conn,
336  const void *message);
337 
338 static void message_handler_req_lib_votequorum_qdevice_unregister (void *conn,
339  const void *message);
340 
341 static void message_handler_req_lib_votequorum_qdevice_update (void *conn,
342  const void *message);
343 
344 static void message_handler_req_lib_votequorum_qdevice_poll (void *conn,
345  const void *message);
346 
347 static void message_handler_req_lib_votequorum_qdevice_master_wins (void *conn,
348  const void *message);
349 
350 static struct corosync_lib_handler quorum_lib_service[] =
351 {
352  { /* 0 */
353  .lib_handler_fn = message_handler_req_lib_votequorum_getinfo,
355  },
356  { /* 1 */
357  .lib_handler_fn = message_handler_req_lib_votequorum_setexpected,
359  },
360  { /* 2 */
361  .lib_handler_fn = message_handler_req_lib_votequorum_setvotes,
363  },
364  { /* 3 */
365  .lib_handler_fn = message_handler_req_lib_votequorum_trackstart,
367  },
368  { /* 4 */
369  .lib_handler_fn = message_handler_req_lib_votequorum_trackstop,
371  },
372  { /* 5 */
373  .lib_handler_fn = message_handler_req_lib_votequorum_qdevice_register,
375  },
376  { /* 6 */
377  .lib_handler_fn = message_handler_req_lib_votequorum_qdevice_unregister,
379  },
380  { /* 7 */
381  .lib_handler_fn = message_handler_req_lib_votequorum_qdevice_update,
383  },
384  { /* 8 */
385  .lib_handler_fn = message_handler_req_lib_votequorum_qdevice_poll,
387  },
388  { /* 9 */
389  .lib_handler_fn = message_handler_req_lib_votequorum_qdevice_master_wins,
391  }
392 };
393 
394 static struct corosync_service_engine votequorum_service_engine = {
395  .name = "corosync vote quorum service v1.0",
396  .id = VOTEQUORUM_SERVICE,
397  .priority = 2,
398  .private_data_size = sizeof (struct quorum_pd),
399  .allow_inquorate = CS_LIB_ALLOW_INQUORATE,
400  .flow_control = COROSYNC_LIB_FLOW_CONTROL_REQUIRED,
401  .lib_init_fn = quorum_lib_init_fn,
402  .lib_exit_fn = quorum_lib_exit_fn,
403  .lib_engine = quorum_lib_service,
404  .lib_engine_count = sizeof (quorum_lib_service) / sizeof (struct corosync_lib_handler),
405  .exec_init_fn = votequorum_exec_init_fn,
406  .exec_exit_fn = votequorum_exec_exit_fn,
407  .exec_engine = votequorum_exec_engine,
408  .exec_engine_count = sizeof (votequorum_exec_engine) / sizeof (struct corosync_exec_handler),
409  .sync_init = votequorum_sync_init,
410  .sync_process = votequorum_sync_process,
411  .sync_activate = votequorum_sync_activate,
412  .sync_abort = votequorum_sync_abort
413 };
414 
416 {
417  return (&votequorum_service_engine);
418 }
419 
420 static struct default_service votequorum_service[] = {
421  {
422  .name = "corosync_votequorum",
423  .ver = 0,
425  },
426 };
427 
428 /*
429  * common/utility macros/functions
430  */
431 
432 #define max(a,b) (((a) > (b)) ? (a) : (b))
433 
434 static void node_add_ordered(struct cluster_node *newnode)
435 {
436  struct cluster_node *node = NULL;
437  struct qb_list_head *tmp;
438 
439  ENTER();
440 
441  qb_list_for_each(tmp, &cluster_members_list) {
442  node = qb_list_entry(tmp, struct cluster_node, list);
443  if (newnode->node_id < node->node_id) {
444  break;
445  }
446  }
447 
448  if (!node) {
449  qb_list_add(&newnode->list, &cluster_members_list);
450  } else {
451  qb_list_add_tail(&newnode->list, &node->list);
452  }
453 
454  LEAVE();
455 }
456 
457 static struct cluster_node *allocate_node(unsigned int nodeid)
458 {
459  struct cluster_node *cl = NULL;
460  struct qb_list_head *tmp;
461 
462  ENTER();
463 
464  if (cluster_nodes_entries <= PROCESSOR_COUNT_MAX + 1) {
465  cl = (struct cluster_node *)&cluster_nodes[cluster_nodes_entries];
466  cluster_nodes_entries++;
467  } else {
468  qb_list_for_each(tmp, &cluster_members_list) {
469  cl = qb_list_entry(tmp, struct cluster_node, list);
470  if (cl->state == NODESTATE_DEAD) {
471  break;
472  }
473  }
474  /*
475  * this should never happen
476  */
477  if (!cl) {
478  log_printf(LOGSYS_LEVEL_CRIT, "Unable to find memory for node %u data!!", nodeid);
479  goto out;
480  }
481  qb_list_del(tmp);
482  }
483 
484  memset(cl, 0, sizeof(struct cluster_node));
485  cl->node_id = nodeid;
486  if (nodeid != VOTEQUORUM_QDEVICE_NODEID) {
487  node_add_ordered(cl);
488  }
489 
490 out:
491  LEAVE();
492 
493  return cl;
494 }
495 
496 static struct cluster_node *find_node_by_nodeid(unsigned int nodeid)
497 {
498  struct cluster_node *node;
499  struct qb_list_head *tmp;
500 
501  ENTER();
502 
503  if (nodeid == us->node_id) {
504  LEAVE();
505  return us;
506  }
507 
508  if (nodeid == VOTEQUORUM_QDEVICE_NODEID) {
509  LEAVE();
510  return qdevice;
511  }
512 
513  qb_list_for_each(tmp, &cluster_members_list) {
514  node = qb_list_entry(tmp, struct cluster_node, list);
515  if (node->node_id == nodeid) {
516  LEAVE();
517  return node;
518  }
519  }
520 
521  LEAVE();
522  return NULL;
523 }
524 
525 static void get_lowest_node_id(void)
526 {
527  struct cluster_node *node = NULL;
528  struct qb_list_head *tmp;
529 
530  ENTER();
531 
532  lowest_node_id = us->node_id;
533 
534  qb_list_for_each(tmp, &cluster_members_list) {
535  node = qb_list_entry(tmp, struct cluster_node, list);
536  if ((node->state == NODESTATE_MEMBER) &&
537  (node->node_id < lowest_node_id)) {
538  lowest_node_id = node->node_id;
539  }
540  }
541  log_printf(LOGSYS_LEVEL_DEBUG, "lowest node id: %d us: %d", lowest_node_id, us->node_id);
542  icmap_set_uint32("runtime.votequorum.lowest_node_id", lowest_node_id);
543 
544  LEAVE();
545 }
546 
547 static void get_highest_node_id(void)
548 {
549  struct cluster_node *node = NULL;
550  struct qb_list_head *tmp;
551 
552  ENTER();
553 
554  highest_node_id = us->node_id;
555 
556  qb_list_for_each(tmp, &cluster_members_list) {
557  node = qb_list_entry(tmp, struct cluster_node, list);
558  if ((node->state == NODESTATE_MEMBER) &&
559  (node->node_id > highest_node_id)) {
560  highest_node_id = node->node_id;
561  }
562  }
563  log_printf(LOGSYS_LEVEL_DEBUG, "highest node id: %d us: %d", highest_node_id, us->node_id);
564  icmap_set_uint32("runtime.votequorum.highest_node_id", highest_node_id);
565 
566  LEAVE();
567 }
568 
569 static int check_low_node_id_partition(void)
570 {
571  struct cluster_node *node = NULL;
572  struct qb_list_head *tmp;
573  int found = 0;
574 
575  ENTER();
576 
577  qb_list_for_each(tmp, &cluster_members_list) {
578  node = qb_list_entry(tmp, struct cluster_node, list);
579  if ((node->state == NODESTATE_MEMBER) &&
580  (node->node_id == lowest_node_id)) {
581  found = 1;
582  }
583  }
584 
585  LEAVE();
586  return found;
587 }
588 
589 static int check_high_node_id_partition(void)
590 {
591  struct cluster_node *node = NULL;
592  struct qb_list_head *tmp;
593  int found = 0;
594 
595  ENTER();
596 
597  qb_list_for_each(tmp, &cluster_members_list) {
598  node = qb_list_entry(tmp, struct cluster_node, list);
599  if ((node->state == NODESTATE_MEMBER) &&
600  (node->node_id == highest_node_id)) {
601  found = 1;
602  }
603  }
604 
605  LEAVE();
606  return found;
607 }
608 
609 static int is_in_nodelist(int nodeid, unsigned int *members, int entries)
610 {
611  int i;
612  ENTER();
613 
614  for (i=0; i<entries; i++) {
615  if (nodeid == members[i]) {
616  LEAVE();
617  return 1;
618  }
619  }
620  LEAVE();
621  return 0;
622 }
623 
624 /*
625  * The algorithm for a list of tie-breaker nodes is:
626  * travel the list of nodes in the auto_tie_breaker list,
627  * if the node IS in our current partition, check if the
628  * nodes earlier in the atb list are in the 'previous' partition;
629  * If none are found then we are safe to be quorate, if any are
630  * then we cannot be as we don't know if that node is up or down.
631  * If we don't have a node in the current list we are NOT quorate.
632  * Obviously if we find the first node in the atb list in our
633  * partition then we are quorate.
634  *
635  * Special cases lowest nodeid, and highest nodeid are handled separately.
636  */
637 static int check_auto_tie_breaker(void)
638 {
639  int i, j;
640  int res;
641  ENTER();
642 
643  if (auto_tie_breaker == ATB_LOWEST) {
644  res = check_low_node_id_partition();
645  log_printf(LOGSYS_LEVEL_DEBUG, "ATB_LOWEST decision: %d", res);
646  LEAVE();
647  return res;
648  }
649  if (auto_tie_breaker == ATB_HIGHEST) {
650  res = check_high_node_id_partition();
651  log_printf(LOGSYS_LEVEL_DEBUG, "ATB_HIGHEST decision: %d", res);
652  LEAVE();
653  return res;
654  }
655 
656  /* Assume ATB_LIST, we should never be called for ATB_NONE */
657  for (i=0; i < atb_nodelist_entries; i++) {
658  if (is_in_nodelist(atb_nodelist[i], quorum_members, quorum_members_entries)) {
659  /*
660  * Node is in our partition, if any of its predecessors are
661  * in the previous quorum partition then it might be in the
662  * 'other half' (as we've got this far without seeing it here)
663  * and so we can't be quorate.
664  */
665  for (j=0; j<i; j++) {
666  if (is_in_nodelist(atb_nodelist[j], previous_quorum_members, previous_quorum_members_entries)) {
667  log_printf(LOGSYS_LEVEL_DEBUG, "ATB_LIST found node %d in previous partition but not here, quorum denied", atb_nodelist[j]);
668  LEAVE();
669  return 0;
670  }
671  }
672 
673  /*
674  * None of the other list nodes were in the previous partition, if there
675  * are enough votes, we can be quorate
676  */
677  log_printf(LOGSYS_LEVEL_DEBUG, "ATB_LIST found node %d in current partition, we can be quorate", atb_nodelist[i]);
678  LEAVE();
679  return 1;
680  }
681  }
682  log_printf(LOGSYS_LEVEL_DEBUG, "ATB_LIST found no list nodes in current partition, we cannot be quorate");
683  LEAVE();
684  return 0;
685 }
686 
687 /*
688  * atb_string can be either:
689  * 'lowest'
690  * 'highest'
691  * a list of nodeids
692  */
693 static void parse_atb_string(char *atb_string)
694 {
695  char *ptr;
696  long num;
697 
698  ENTER();
699  auto_tie_breaker = ATB_NONE;
700 
701  if (!strcmp(atb_string, "lowest"))
702  auto_tie_breaker = ATB_LOWEST;
703 
704  if (!strcmp(atb_string, "highest"))
705  auto_tie_breaker = ATB_HIGHEST;
706 
707  if (atoi(atb_string)) {
708 
709  atb_nodelist_entries = 0;
710  ptr = atb_string;
711  do {
712  num = strtol(ptr, &ptr, 10);
713  if (num) {
714  log_printf(LOGSYS_LEVEL_DEBUG, "ATB nodelist[%d] = %d", atb_nodelist_entries, num);
715  atb_nodelist[atb_nodelist_entries++] = num;
716  }
717  } while (num);
718 
719  if (atb_nodelist_entries) {
720  auto_tie_breaker = ATB_LIST;
721  }
722  }
723  icmap_set_uint32("runtime.votequorum.atb_type", auto_tie_breaker);
724  log_printf(LOGSYS_LEVEL_DEBUG, "ATB type = %d", auto_tie_breaker);
725 
726  /* Make sure we got something */
727  if (auto_tie_breaker == ATB_NONE) {
728  log_printf(LOGSYS_LEVEL_WARNING, "auto_tie_breaker_nodes is not valid. It must be 'lowest', 'highest' or a space-separated list of node IDs. auto_tie_breaker is disabled");
729  auto_tie_breaker = ATB_NONE;
730  }
731  LEAVE();
732 }
733 
734 static int check_qdevice_master(void)
735 {
736  struct cluster_node *node = NULL;
737  struct qb_list_head *tmp;
738  int found = 0;
739 
740  ENTER();
741 
742  qb_list_for_each(tmp, &cluster_members_list) {
743  node = qb_list_entry(tmp, struct cluster_node, list);
744  if ((node->state == NODESTATE_MEMBER) &&
747  found = 1;
748  }
749  }
750 
751  LEAVE();
752  return found;
753 }
754 
755 static void decode_flags(uint32_t flags)
756 {
757  ENTER();
758 
760  "flags: quorate: %s Leaving: %s WFA Status: %s First: %s Qdevice: %s QdeviceAlive: %s QdeviceCastVote: %s QdeviceMasterWins: %s",
761  (flags & NODE_FLAGS_QUORATE)?"Yes":"No",
762  (flags & NODE_FLAGS_LEAVING)?"Yes":"No",
763  (flags & NODE_FLAGS_WFASTATUS)?"Yes":"No",
764  (flags & NODE_FLAGS_FIRST)?"Yes":"No",
765  (flags & NODE_FLAGS_QDEVICE_REGISTERED)?"Yes":"No",
766  (flags & NODE_FLAGS_QDEVICE_ALIVE)?"Yes":"No",
767  (flags & NODE_FLAGS_QDEVICE_CAST_VOTE)?"Yes":"No",
768  (flags & NODE_FLAGS_QDEVICE_MASTER_WINS)?"Yes":"No");
769 
770  LEAVE();
771 }
772 
773 /*
774  * load/save are copied almost pristine from totemsrp,c
775  */
776 static int load_ev_tracking_barrier(void)
777 {
778  int res = 0;
779  char filename[PATH_MAX];
780 
781  ENTER();
782 
783  snprintf(filename, sizeof(filename) - 1, "%s/ev_tracking", get_state_dir());
784 
785  ev_tracking_fd = open(filename, O_RDWR, 0700);
786  if (ev_tracking_fd != -1) {
787  res = read (ev_tracking_fd, &ev_tracking_barrier, sizeof(uint32_t));
788  close(ev_tracking_fd);
789  if (res == sizeof (uint32_t)) {
790  LEAVE();
791  return 0;
792  }
793  }
794 
795  ev_tracking_barrier = 0;
796  umask(0);
797  ev_tracking_fd = open (filename, O_CREAT|O_RDWR, 0700);
798  if (ev_tracking_fd != -1) {
799  res = write (ev_tracking_fd, &ev_tracking_barrier, sizeof (uint32_t));
800  if ((res == -1) || (res != sizeof (uint32_t))) {
802  "Unable to write to %s", filename);
803  }
804  close(ev_tracking_fd);
805  LEAVE();
806  return 0;
807  }
809  "Unable to create %s file", filename);
810 
811  LEAVE();
812 
813  return -1;
814 }
815 
816 static void update_wait_for_all_status(uint8_t wfa_status)
817 {
818  ENTER();
819 
820  wait_for_all_status = wfa_status;
821  if (wait_for_all_status) {
823  } else {
824  us->flags &= ~NODE_FLAGS_WFASTATUS;
825  }
826  icmap_set_uint8("runtime.votequorum.wait_for_all_status",
827  wait_for_all_status);
828 
829  LEAVE();
830 }
831 
832 static void update_two_node(void)
833 {
834  ENTER();
835 
836  icmap_set_uint8("runtime.votequorum.two_node", two_node);
837 
838  LEAVE();
839 }
840 
841 static void update_ev_barrier(uint32_t expected_votes)
842 {
843  ENTER();
844 
845  ev_barrier = expected_votes;
846  icmap_set_uint32("runtime.votequorum.ev_barrier", ev_barrier);
847 
848  LEAVE();
849 }
850 
851 static void update_qdevice_can_operate(uint8_t status)
852 {
853  ENTER();
854 
855  qdevice_can_operate = status;
856  icmap_set_uint8("runtime.votequorum.qdevice_can_operate", qdevice_can_operate);
857 
858  LEAVE();
859 }
860 
861 static void update_qdevice_master_wins(uint8_t allow)
862 {
863  ENTER();
864 
865  qdevice_master_wins = allow;
866  icmap_set_uint8("runtime.votequorum.qdevice_master_wins", qdevice_master_wins);
867 
868  LEAVE();
869 }
870 
871 static void update_ev_tracking_barrier(uint32_t ev_t_barrier)
872 {
873  int res;
874 
875  ENTER();
876 
877  ev_tracking_barrier = ev_t_barrier;
878  icmap_set_uint32("runtime.votequorum.ev_tracking_barrier", ev_tracking_barrier);
879 
880  if (lseek (ev_tracking_fd, 0, SEEK_SET) != 0) {
882  "Unable to update ev_tracking_barrier on disk data!!!");
883  LEAVE();
884  return;
885  }
886 
887  res = write (ev_tracking_fd, &ev_tracking_barrier, sizeof (uint32_t));
888  if (res != sizeof (uint32_t)) {
890  "Unable to update ev_tracking_barrier on disk data!!!");
891  }
892 #ifdef HAVE_FDATASYNC
893  fdatasync(ev_tracking_fd);
894 #else
895  fsync(ev_tracking_fd);
896 #endif
897 
898  LEAVE();
899 }
900 
901 /*
902  * quorum calculation core bits
903  */
904 
905 static int calculate_quorum(int allow_decrease, unsigned int max_expected, unsigned int *ret_total_votes)
906 {
907  struct qb_list_head *nodelist;
908  struct cluster_node *node;
909  unsigned int total_votes = 0;
910  unsigned int highest_expected = 0;
911  unsigned int newquorum, q1, q2;
912  unsigned int total_nodes = 0;
913 
914  ENTER();
915 
916  if ((allow_downscale) && (allow_decrease) && (max_expected)) {
917  max_expected = max(ev_barrier, max_expected);
918  }
919 
920  qb_list_for_each(nodelist, &cluster_members_list) {
921  node = qb_list_entry(nodelist, struct cluster_node, list);
922 
923  log_printf(LOGSYS_LEVEL_DEBUG, "node %u state=%d, votes=%u, expected=%u",
924  node->node_id, node->state, node->votes, node->expected_votes);
925 
926  if (node->state == NODESTATE_MEMBER) {
927  highest_expected = max(highest_expected, node->expected_votes);
928  total_votes += node->votes;
929  total_nodes++;
930  }
931  }
932 
934  log_printf(LOGSYS_LEVEL_DEBUG, "node 0 state=1, votes=%u", qdevice->votes);
935  total_votes += qdevice->votes;
936  total_nodes++;
937  }
938 
939  if (max_expected > 0) {
940  highest_expected = max_expected;
941  }
942 
943  /*
944  * This quorum calculation is taken from the OpenVMS Cluster Systems
945  * manual, but, then, you guessed that didn't you
946  */
947  q1 = (highest_expected + 2) / 2;
948  q2 = (total_votes + 2) / 2;
949  newquorum = max(q1, q2);
950 
951  /*
952  * Normally quorum never decreases but the system administrator can
953  * force it down by setting expected votes to a maximum value
954  */
955  if (!allow_decrease) {
956  newquorum = max(quorum, newquorum);
957  }
958 
959  /*
960  * The special two_node mode allows each of the two nodes to retain
961  * quorum if the other fails. Only one of the two should live past
962  * fencing (as both nodes try to fence each other in split-brain.)
963  * Also: if there are more than two nodes, force us inquorate to avoid
964  * any damage or confusion.
965  */
966  if (two_node && total_nodes <= 2) {
967  newquorum = 1;
968  }
969 
970  if (ret_total_votes) {
971  *ret_total_votes = total_votes;
972  }
973 
974  LEAVE();
975  return newquorum;
976 }
977 
978 static void update_node_expected_votes(int new_expected_votes)
979 {
980  struct qb_list_head *nodelist;
981  struct cluster_node *node;
982 
983  if (new_expected_votes) {
984  qb_list_for_each(nodelist, &cluster_members_list) {
985  node = qb_list_entry(nodelist, struct cluster_node, list);
986 
987  if (node->state == NODESTATE_MEMBER) {
988  node->expected_votes = new_expected_votes;
989  }
990  }
991  }
992 }
993 
994 static void are_we_quorate(unsigned int total_votes)
995 {
996  int quorate;
997  int quorum_change = 0;
998 
999  ENTER();
1000 
1001  /*
1002  * wait for all nodes to show up before granting quorum
1003  */
1004 
1005  if ((wait_for_all) && (wait_for_all_status)) {
1006  if (total_votes != us->expected_votes) {
1008  "Waiting for all cluster members. "
1009  "Current votes: %d expected_votes: %d",
1010  total_votes, us->expected_votes);
1011  cluster_is_quorate = 0;
1012  return;
1013  }
1014  update_wait_for_all_status(0);
1015  }
1016 
1017  if (quorum > total_votes) {
1018  quorate = 0;
1019  } else {
1020  quorate = 1;
1021  get_lowest_node_id();
1022  get_highest_node_id();
1023  }
1024 
1025  if ((auto_tie_breaker != ATB_NONE) &&
1026  /* Must be a half (or half-1) split */
1027  (total_votes == (us->expected_votes / 2)) &&
1028  /* If the 'other' partition in a split might have quorum then we can't run ATB */
1029  (previous_quorum_members_entries - quorum_members_entries < quorum) &&
1030  (check_auto_tie_breaker() == 1)) {
1031  quorate = 1;
1032  }
1033 
1034  if ((qdevice_master_wins) &&
1035  (!quorate) &&
1036  (check_qdevice_master() == 1)) {
1037  log_printf(LOGSYS_LEVEL_DEBUG, "node is quorate as part of master_wins partition");
1038  quorate = 1;
1039  }
1040 
1041  if (cluster_is_quorate && !quorate) {
1042  quorum_change = 1;
1043  log_printf(LOGSYS_LEVEL_DEBUG, "quorum lost, blocking activity");
1044  }
1045  if (!cluster_is_quorate && quorate) {
1046  quorum_change = 1;
1047  log_printf(LOGSYS_LEVEL_DEBUG, "quorum regained, resuming activity");
1048  }
1049 
1050  cluster_is_quorate = quorate;
1051  if (cluster_is_quorate) {
1052  us->flags |= NODE_FLAGS_QUORATE;
1053  } else {
1054  us->flags &= ~NODE_FLAGS_QUORATE;
1055  }
1056 
1057  if (wait_for_all) {
1058  if (quorate) {
1059  update_wait_for_all_status(0);
1060  } else {
1061  update_wait_for_all_status(1);
1062  }
1063  }
1064 
1065  if ((quorum_change) &&
1066  (sync_in_progress == 0)) {
1067  quorum_callback(quorum_members, quorum_members_entries,
1068  cluster_is_quorate, &quorum_ringid);
1069  votequorum_exec_send_quorum_notification(NULL, 0L);
1070  }
1071 
1072  LEAVE();
1073 }
1074 
1075 static void get_total_votes(unsigned int *totalvotes, unsigned int *current_members)
1076 {
1077  unsigned int total_votes = 0;
1078  unsigned int cluster_members = 0;
1079  struct qb_list_head *nodelist;
1080  struct cluster_node *node;
1081 
1082  ENTER();
1083 
1084  qb_list_for_each(nodelist, &cluster_members_list) {
1085  node = qb_list_entry(nodelist, struct cluster_node, list);
1086  if (node->state == NODESTATE_MEMBER) {
1087  cluster_members++;
1088  total_votes += node->votes;
1089  }
1090  }
1091 
1092  if (qdevice->votes) {
1093  total_votes += qdevice->votes;
1094  cluster_members++;
1095  }
1096 
1097  *totalvotes = total_votes;
1098  *current_members = cluster_members;
1099 
1100  LEAVE();
1101 }
1102 
1103 /*
1104  * Recalculate cluster quorum, set quorate and notify changes
1105  */
1106 static void recalculate_quorum(int allow_decrease, int by_current_nodes)
1107 {
1108  unsigned int total_votes = 0;
1109  unsigned int cluster_members = 0;
1110 
1111  ENTER();
1112 
1113  get_total_votes(&total_votes, &cluster_members);
1114 
1115  if (!by_current_nodes) {
1116  cluster_members = 0;
1117  }
1118 
1119  /*
1120  * Keep expected_votes at the highest number of votes in the cluster
1121  */
1122  log_printf(LOGSYS_LEVEL_DEBUG, "total_votes=%d, expected_votes=%d", total_votes, us->expected_votes);
1123  if (total_votes > us->expected_votes) {
1124  us->expected_votes = total_votes;
1125  votequorum_exec_send_expectedvotes_notification();
1126  }
1127 
1128  if ((ev_tracking) &&
1129  (us->expected_votes > ev_tracking_barrier)) {
1130  update_ev_tracking_barrier(us->expected_votes);
1131  }
1132 
1133  quorum = calculate_quorum(allow_decrease, cluster_members, &total_votes);
1134  update_node_expected_votes(cluster_members);
1135 
1136  are_we_quorate(total_votes);
1137 
1138  LEAVE();
1139 }
1140 
1141 /*
1142  * configuration bits and pieces
1143  */
1144 
1145 static int votequorum_read_nodelist_configuration(uint32_t *votes,
1146  uint32_t *nodes,
1147  uint32_t *expected_votes)
1148 {
1149  icmap_iter_t iter;
1150  const char *iter_key;
1151  char tmp_key[ICMAP_KEYNAME_MAXLEN];
1152  uint32_t our_pos, node_pos, last_node_pos=-1;
1153  uint32_t nodecount = 0;
1154  uint32_t nodelist_expected_votes = 0;
1155  uint32_t node_votes = 0;
1156  int res = 0;
1157 
1158  ENTER();
1159 
1160  if (icmap_get_uint32("nodelist.local_node_pos", &our_pos) != CS_OK) {
1162  "No nodelist defined or our node is not in the nodelist");
1163  return 0;
1164  }
1165 
1166  iter = icmap_iter_init("nodelist.node.");
1167 
1168  while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) {
1169 
1170  res = sscanf(iter_key, "nodelist.node.%u.%s", &node_pos, tmp_key);
1171  if (res != 2) {
1172  continue;
1173  }
1174 
1175  /*
1176  * If current node_pos is the same as the last_node_pos then skip it
1177  * so we only do the code below once per node.
1178  * (icmap keys are always in order)
1179  */
1180  if (last_node_pos == node_pos) {
1181  continue;
1182  }
1183  last_node_pos = node_pos;
1184 
1185  nodecount++;
1186 
1187  snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.quorum_votes", node_pos);
1188  if (icmap_get_uint32(tmp_key, &node_votes) != CS_OK) {
1189  node_votes = 1;
1190  }
1191 
1192  nodelist_expected_votes = nodelist_expected_votes + node_votes;
1193 
1194  if (node_pos == our_pos) {
1195  *votes = node_votes;
1196  }
1197  }
1198 
1199  *expected_votes = nodelist_expected_votes;
1200  *nodes = nodecount;
1201 
1202  icmap_iter_finalize(iter);
1203 
1204  LEAVE();
1205 
1206  return 1;
1207 }
1208 
1209 static int votequorum_qdevice_is_configured(uint32_t *qdevice_votes)
1210 {
1211  char *qdevice_model = NULL;
1212  int ret = 0;
1213 
1214  ENTER();
1215 
1216  if (icmap_get_string("quorum.device.model", &qdevice_model) == CS_OK) {
1217  if (strlen(qdevice_model)) {
1218  if (icmap_get_uint32("quorum.device.votes", qdevice_votes) != CS_OK) {
1219  *qdevice_votes = -1;
1220  }
1221  if (icmap_get_uint32("quorum.device.timeout", &qdevice_timeout) != CS_OK) {
1222  qdevice_timeout = VOTEQUORUM_QDEVICE_DEFAULT_TIMEOUT;
1223  }
1224  if (icmap_get_uint32("quorum.device.sync_timeout", &qdevice_sync_timeout) != CS_OK) {
1225  qdevice_sync_timeout = VOTEQUORUM_QDEVICE_DEFAULT_SYNC_TIMEOUT;
1226  }
1227  update_qdevice_can_operate(1);
1228  ret = 1;
1229  }
1230 
1231  free(qdevice_model);
1232  }
1233 
1234  LEAVE();
1235 
1236  return ret;
1237 }
1238 
1239 #define VOTEQUORUM_READCONFIG_STARTUP 0
1240 #define VOTEQUORUM_READCONFIG_RUNTIME 1
1241 
1242 static char *votequorum_readconfig(int runtime)
1243 {
1244  uint32_t node_votes = 0, qdevice_votes = 0;
1245  uint32_t node_expected_votes = 0, expected_votes = 0;
1246  uint32_t node_count = 0;
1247  uint8_t atb = 0;
1248  int have_nodelist, have_qdevice;
1249  char *atb_string = NULL;
1250  char *error = NULL;
1251 
1252  ENTER();
1253 
1254  log_printf(LOGSYS_LEVEL_DEBUG, "Reading configuration (runtime: %d)", runtime);
1255 
1256  /*
1257  * Set the few things we re-read at runtime back to their defaults
1258  */
1259  if (runtime) {
1260  two_node = 0;
1261  expected_votes = 0;
1262  /* auto_tie_breaker cannot be changed by config reload, but
1263  * we automatically disable it on odd-sized clusters without
1264  * wait_for_all.
1265  * We may need to re-enable it when membership changes to ensure
1266  * that auto_tie_breaker is consistent across all nodes */
1267  auto_tie_breaker = initial_auto_tie_breaker;
1268  icmap_set_uint32("runtime.votequorum.atb_type", auto_tie_breaker);
1269  }
1270 
1271  /*
1272  * gather basic data here
1273  */
1274  icmap_get_uint32("quorum.expected_votes", &expected_votes);
1275  have_nodelist = votequorum_read_nodelist_configuration(&node_votes, &node_count, &node_expected_votes);
1276  have_qdevice = votequorum_qdevice_is_configured(&qdevice_votes);
1277  icmap_get_uint8("quorum.two_node", &two_node);
1278 
1279  /*
1280  * do config verification and enablement
1281  */
1282 
1283  if ((!have_nodelist) && (!expected_votes)) {
1284  if (!runtime) {
1285  error = (char *)"configuration error: nodelist or quorum.expected_votes must be configured!";
1286  } else {
1287  log_printf(LOGSYS_LEVEL_CRIT, "configuration error: nodelist or quorum.expected_votes must be configured!");
1288  log_printf(LOGSYS_LEVEL_CRIT, "will continue with current runtime data");
1289  }
1290  goto out;
1291  }
1292 
1293  /*
1294  * two_node and qdevice are not compatible in the same config.
1295  * try to make an educated guess of what to do
1296  */
1297 
1298  if ((two_node) && (have_qdevice)) {
1299  if (!runtime) {
1300  error = (char *)"configuration error: two_node and quorum device cannot be configured at the same time!";
1301  goto out;
1302  } else {
1303  log_printf(LOGSYS_LEVEL_CRIT, "configuration error: two_node and quorum device cannot be configured at the same time!");
1305  log_printf(LOGSYS_LEVEL_CRIT, "quorum device is registered, disabling two_node");
1306  two_node = 0;
1307  } else {
1308  log_printf(LOGSYS_LEVEL_CRIT, "quorum device is not registered, allowing two_node");
1309  update_qdevice_can_operate(0);
1310  }
1311  }
1312  }
1313 
1314  /*
1315  * Enable special features
1316  */
1317  if (!runtime) {
1318  if (two_node) {
1319  wait_for_all = 1;
1320  }
1321 
1322  icmap_get_uint8("quorum.allow_downscale", &allow_downscale);
1323  icmap_get_uint8("quorum.wait_for_all", &wait_for_all);
1324  icmap_get_uint8("quorum.last_man_standing", &last_man_standing);
1325  icmap_get_uint32("quorum.last_man_standing_window", &last_man_standing_window);
1326  icmap_get_uint8("quorum.expected_votes_tracking", &ev_tracking);
1327  icmap_get_uint8("quorum.auto_tie_breaker", &atb);
1328  icmap_get_string("quorum.auto_tie_breaker_node", &atb_string);
1329 
1330  /* auto_tie_breaker defaults to LOWEST */
1331  if (atb) {
1332  auto_tie_breaker = ATB_LOWEST;
1333  icmap_set_uint32("runtime.votequorum.atb_type", auto_tie_breaker);
1334  }
1335  else {
1336  auto_tie_breaker = ATB_NONE;
1337  if (atb_string) {
1339  "auto_tie_breaker_node: is meaningless if auto_tie_breaker is set to 0");
1340  }
1341  }
1342 
1343  if (atb && atb_string) {
1344  parse_atb_string(atb_string);
1345  }
1346  free(atb_string);
1347  initial_auto_tie_breaker = auto_tie_breaker;
1348 
1349  /* allow_downscale requires ev_tracking */
1350  if (allow_downscale) {
1351  ev_tracking = 1;
1352  }
1353 
1354  if (ev_tracking) {
1355  if (load_ev_tracking_barrier() < 0) {
1356  LEAVE();
1357  return ((char *)"Unable to load ev_tracking file!");
1358  }
1359  update_ev_tracking_barrier(ev_tracking_barrier);
1360  }
1361 
1362  }
1363 
1364  /* two_node and auto_tie_breaker are not compatible as two_node uses
1365  * a fence race to decide quorum whereas ATB decides based on node id
1366  */
1367  if (two_node && auto_tie_breaker != ATB_NONE) {
1368  log_printf(LOGSYS_LEVEL_CRIT, "two_node and auto_tie_breaker are both specified but are not compatible.");
1369  log_printf(LOGSYS_LEVEL_CRIT, "two_node has been disabled, please fix your corosync.conf");
1370  two_node = 0;
1371  }
1372 
1373  /* If ATB is set and the cluster has an odd number of nodes then wait_for_all needs
1374  * to be set so that an isolated half+1 without the tie breaker node
1375  * does not have quorum on reboot.
1376  */
1377  if ((auto_tie_breaker != ATB_NONE) && (node_expected_votes % 2) &&
1378  (!wait_for_all)) {
1379  if (last_man_standing) {
1380  /* if LMS is set too, it's a fatal configuration error. We can't dictate to the user what
1381  * they might want so we'll just quit.
1382  */
1383  log_printf(LOGSYS_LEVEL_CRIT, "auto_tie_breaker is set, the cluster has an odd number of nodes\n");
1384  log_printf(LOGSYS_LEVEL_CRIT, "and last_man_standing is also set. With this situation a better\n");
1385  log_printf(LOGSYS_LEVEL_CRIT, "solution would be to disable LMS, leave ATB enabled, and also\n");
1386  log_printf(LOGSYS_LEVEL_CRIT, "enable wait_for_all (mandatory for ATB in odd-numbered clusters).\n");
1387  log_printf(LOGSYS_LEVEL_CRIT, "Due to this ambiguity, corosync will fail to start. Please fix your corosync.conf\n");
1388  error = (char *)"configuration error: auto_tie_breaker & last_man_standing not available in odd sized cluster";
1389  goto out;
1390  }
1391  else {
1392  log_printf(LOGSYS_LEVEL_CRIT, "auto_tie_breaker is set and the cluster has an odd number of nodes.\n");
1393  log_printf(LOGSYS_LEVEL_CRIT, "wait_for_all needs to be set for this configuration but it is missing\n");
1394  log_printf(LOGSYS_LEVEL_CRIT, "Therefore auto_tie_breaker has been disabled. Please fix your corosync.conf\n");
1395  auto_tie_breaker = ATB_NONE;
1396  icmap_set_uint32("runtime.votequorum.atb_type", auto_tie_breaker);
1397  }
1398  }
1399 
1400  /*
1401  * quorum device is not compatible with last_man_standing and auto_tie_breaker
1402  * neither lms or atb can be set at runtime, so there is no need to check for
1403  * runtime incompatibilities, but qdevice can be configured _after_ LMS and ATB have
1404  * been enabled at startup.
1405  */
1406 
1407  if ((have_qdevice) && (last_man_standing)) {
1408  if (!runtime) {
1409  error = (char *)"configuration error: quorum.device is not compatible with last_man_standing";
1410  goto out;
1411  } else {
1412  log_printf(LOGSYS_LEVEL_CRIT, "configuration error: quorum.device is not compatible with last_man_standing");
1413  log_printf(LOGSYS_LEVEL_CRIT, "disabling quorum device operations");
1414  update_qdevice_can_operate(0);
1415  }
1416  }
1417 
1418  if ((have_qdevice) && (auto_tie_breaker != ATB_NONE)) {
1419  if (!runtime) {
1420  error = (char *)"configuration error: quorum.device is not compatible with auto_tie_breaker";
1421  goto out;
1422  } else {
1423  log_printf(LOGSYS_LEVEL_CRIT, "configuration error: quorum.device is not compatible with auto_tie_breaker");
1424  log_printf(LOGSYS_LEVEL_CRIT, "disabling quorum device operations");
1425  update_qdevice_can_operate(0);
1426  }
1427  }
1428 
1429  if ((have_qdevice) && (allow_downscale)) {
1430  if (!runtime) {
1431  error = (char *)"configuration error: quorum.device is not compatible with allow_downscale";
1432  goto out;
1433  } else {
1434  log_printf(LOGSYS_LEVEL_CRIT, "configuration error: quorum.device is not compatible with allow_downscale");
1435  log_printf(LOGSYS_LEVEL_CRIT, "disabling quorum device operations");
1436  update_qdevice_can_operate(0);
1437  }
1438  }
1439 
1440  /*
1441  * if user specifies quorum.expected_votes + quorum.device but NOT the device.votes
1442  * we don't know what the quorum device should vote.
1443  */
1444 
1445  if ((expected_votes) && (have_qdevice) && (qdevice_votes == -1)) {
1446  if (!runtime) {
1447  error = (char *)"configuration error: quorum.device.votes must be specified when quorum.expected_votes is set";
1448  goto out;
1449  } else {
1450  log_printf(LOGSYS_LEVEL_CRIT, "configuration error: quorum.device.votes must be specified when quorum.expected_votes is set");
1451  log_printf(LOGSYS_LEVEL_CRIT, "disabling quorum device operations");
1452  update_qdevice_can_operate(0);
1453  }
1454  }
1455 
1456  /*
1457  * if user specifies a node list with uneven votes and no device.votes
1458  * we cannot autocalculate the votes
1459  */
1460 
1461  if ((have_qdevice) &&
1462  (qdevice_votes == -1) &&
1463  (have_nodelist) &&
1464  (node_count != node_expected_votes)) {
1465  if (!runtime) {
1466  error = (char *)"configuration error: quorum.device.votes must be specified when not all nodes votes 1";
1467  goto out;
1468  } else {
1469  log_printf(LOGSYS_LEVEL_CRIT, "configuration error: quorum.device.votes must be specified when not all nodes votes 1");
1470  log_printf(LOGSYS_LEVEL_CRIT, "disabling quorum device operations");
1471  update_qdevice_can_operate(0);
1472  }
1473  }
1474 
1475  /*
1476  * validate quorum device votes vs expected_votes
1477  */
1478 
1479  if ((qdevice_votes > 0) && (expected_votes)) {
1480  int delta = expected_votes - qdevice_votes;
1481  if (delta < 2) {
1482  if (!runtime) {
1483  error = (char *)"configuration error: quorum.device.votes is too high or expected_votes is too low";
1484  goto out;
1485  } else {
1486  log_printf(LOGSYS_LEVEL_CRIT, "configuration error: quorum.device.votes is too high or expected_votes is too low");
1487  log_printf(LOGSYS_LEVEL_CRIT, "disabling quorum device operations");
1488  update_qdevice_can_operate(0);
1489  }
1490  }
1491  }
1492 
1493  /*
1494  * automatically calculate device votes and adjust expected_votes from nodelist
1495  */
1496 
1497  if ((have_qdevice) &&
1498  (qdevice_votes == -1) &&
1499  (!expected_votes) &&
1500  (have_nodelist) &&
1501  (node_count == node_expected_votes)) {
1502  qdevice_votes = node_expected_votes - 1;
1503  node_expected_votes = node_expected_votes + qdevice_votes;
1504  }
1505 
1506  /*
1507  * set this node votes and expected_votes
1508  */
1509  log_printf(LOGSYS_LEVEL_DEBUG, "ev_tracking=%d, ev_tracking_barrier = %d: expected_votes = %d\n", ev_tracking, ev_tracking_barrier, expected_votes);
1510 
1511  if (ev_tracking) {
1512  expected_votes = ev_tracking_barrier;
1513  }
1514 
1515  if (have_nodelist) {
1516  us->votes = node_votes;
1517  us->expected_votes = node_expected_votes;
1518  } else {
1519  us->votes = 1;
1520  icmap_get_uint32("quorum.votes", &us->votes);
1521  }
1522 
1523  if (expected_votes) {
1525  }
1526 
1527  /*
1528  * set qdevice votes
1529  */
1530 
1531  if (!have_qdevice) {
1532  qdevice->votes = 0;
1533  }
1534 
1535  if (qdevice_votes != -1) {
1536  qdevice->votes = qdevice_votes;
1537  }
1538 
1539  update_ev_barrier(us->expected_votes);
1540  update_two_node();
1541  if (wait_for_all) {
1542  update_wait_for_all_status(1);
1543  }
1544 
1545 out:
1546  LEAVE();
1547  return error;
1548 }
1549 
1550 static void votequorum_refresh_config(
1551  int32_t event,
1552  const char *key_name,
1553  struct icmap_notify_value new_val,
1554  struct icmap_notify_value old_val,
1555  void *user_data)
1556 {
1557  int old_votes, old_expected_votes;
1558  uint8_t reloading;
1559  uint8_t cancel_wfa;
1560 
1561  ENTER();
1562 
1563  /*
1564  * If a full reload is in progress then don't do anything until it's done and
1565  * can reconfigure it all atomically
1566  */
1567  if (icmap_get_uint8("config.totemconfig_reload_in_progress", &reloading) == CS_OK && reloading) {
1568  return ;
1569  }
1570 
1571  icmap_get_uint8("quorum.cancel_wait_for_all", &cancel_wfa);
1572  if (strcmp(key_name, "quorum.cancel_wait_for_all") == 0 &&
1573  cancel_wfa >= 1) {
1574  icmap_set_uint8("quorum.cancel_wait_for_all", 0);
1575  if (votequorum_exec_send_reconfigure(VOTEQUORUM_RECONFIG_PARAM_CANCEL_WFA,
1576  us->node_id, 0)) {
1577  log_printf(LOGSYS_LEVEL_ERROR, "Failed to send Cancel WFA message to other nodes");
1578  }
1579  return;
1580  }
1581 
1582  old_votes = us->votes;
1583  old_expected_votes = us->expected_votes;
1584 
1585  /*
1586  * Reload the configuration
1587  */
1588  votequorum_readconfig(VOTEQUORUM_READCONFIG_RUNTIME);
1589 
1590  /*
1591  * activate new config
1592  */
1593  votequorum_exec_send_nodeinfo(us->node_id);
1594  votequorum_exec_send_nodeinfo(VOTEQUORUM_QDEVICE_NODEID);
1595  if (us->votes != old_votes) {
1596  if (votequorum_exec_send_reconfigure(VOTEQUORUM_RECONFIG_PARAM_NODE_VOTES,
1597  us->node_id, us->votes)) {
1598  log_printf(LOGSYS_LEVEL_ERROR, "Failed to send new votes message to other nodes");
1599  }
1600  }
1601  if (us->expected_votes != old_expected_votes) {
1602  if (votequorum_exec_send_reconfigure(VOTEQUORUM_RECONFIG_PARAM_EXPECTED_VOTES,
1603  us->node_id, us->expected_votes)) {
1604  log_printf(LOGSYS_LEVEL_ERROR, "Failed to send expected votes message to other nodes");
1605  }
1606  }
1607 
1608  LEAVE();
1609 }
1610 
1611 static void votequorum_exec_add_config_notification(void)
1612 {
1613  icmap_track_t icmap_track_nodelist = NULL;
1614  icmap_track_t icmap_track_quorum = NULL;
1615  icmap_track_t icmap_track_reload = NULL;
1616 
1617  ENTER();
1618 
1619  icmap_track_add("nodelist.",
1621  votequorum_refresh_config,
1622  NULL,
1623  &icmap_track_nodelist);
1624 
1625  icmap_track_add("quorum.",
1627  votequorum_refresh_config,
1628  NULL,
1629  &icmap_track_quorum);
1630 
1631  icmap_track_add("config.totemconfig_reload_in_progress",
1633  votequorum_refresh_config,
1634  NULL,
1635  &icmap_track_reload);
1636 
1637  LEAVE();
1638 }
1639 
1640 /*
1641  * votequorum_exec core
1642  */
1643 
1644 static int votequorum_exec_send_reconfigure(uint8_t param, unsigned int nodeid, uint32_t value)
1645 {
1646  struct req_exec_quorum_reconfigure req_exec_quorum_reconfigure;
1647  struct iovec iov[1];
1648  int ret;
1649 
1650  ENTER();
1651 
1652  req_exec_quorum_reconfigure.nodeid = nodeid;
1653  req_exec_quorum_reconfigure.value = value;
1654  req_exec_quorum_reconfigure.param = param;
1655  req_exec_quorum_reconfigure._pad0 = 0;
1656  req_exec_quorum_reconfigure._pad1 = 0;
1657  req_exec_quorum_reconfigure._pad2 = 0;
1658 
1659  req_exec_quorum_reconfigure.header.id = SERVICE_ID_MAKE(VOTEQUORUM_SERVICE, MESSAGE_REQ_EXEC_VOTEQUORUM_RECONFIGURE);
1660  req_exec_quorum_reconfigure.header.size = sizeof(req_exec_quorum_reconfigure);
1661 
1662  iov[0].iov_base = (void *)&req_exec_quorum_reconfigure;
1663  iov[0].iov_len = sizeof(req_exec_quorum_reconfigure);
1664 
1665  ret = corosync_api->totem_mcast (iov, 1, TOTEM_AGREED);
1666 
1667  LEAVE();
1668  return ret;
1669 }
1670 
1671 static int votequorum_exec_send_nodeinfo(uint32_t nodeid)
1672 {
1673  struct req_exec_quorum_nodeinfo req_exec_quorum_nodeinfo;
1674  struct iovec iov[1];
1675  struct cluster_node *node;
1676  int ret;
1677 
1678  ENTER();
1679 
1680  node = find_node_by_nodeid(nodeid);
1681  if (!node) {
1682  return -1;
1683  }
1684 
1685  req_exec_quorum_nodeinfo.nodeid = nodeid;
1686  req_exec_quorum_nodeinfo.votes = node->votes;
1687  req_exec_quorum_nodeinfo.expected_votes = node->expected_votes;
1688  req_exec_quorum_nodeinfo.flags = node->flags;
1689  if (nodeid != VOTEQUORUM_QDEVICE_NODEID) {
1690  decode_flags(node->flags);
1691  }
1692 
1693  req_exec_quorum_nodeinfo.header.id = SERVICE_ID_MAKE(VOTEQUORUM_SERVICE, MESSAGE_REQ_EXEC_VOTEQUORUM_NODEINFO);
1694  req_exec_quorum_nodeinfo.header.size = sizeof(req_exec_quorum_nodeinfo);
1695 
1696  iov[0].iov_base = (void *)&req_exec_quorum_nodeinfo;
1697  iov[0].iov_len = sizeof(req_exec_quorum_nodeinfo);
1698 
1699  ret = corosync_api->totem_mcast (iov, 1, TOTEM_AGREED);
1700 
1701  LEAVE();
1702  return ret;
1703 }
1704 
1705 static int votequorum_exec_send_qdevice_reconfigure(const char *oldname, const char *newname)
1706 {
1707  struct req_exec_quorum_qdevice_reconfigure req_exec_quorum_qdevice_reconfigure;
1708  struct iovec iov[1];
1709  int ret;
1710 
1711  ENTER();
1712 
1713  req_exec_quorum_qdevice_reconfigure.header.id = SERVICE_ID_MAKE(VOTEQUORUM_SERVICE, MESSAGE_REQ_EXEC_VOTEQUORUM_QDEVICE_RECONFIGURE);
1714  req_exec_quorum_qdevice_reconfigure.header.size = sizeof(req_exec_quorum_qdevice_reconfigure);
1715  strcpy(req_exec_quorum_qdevice_reconfigure.oldname, oldname);
1716  strcpy(req_exec_quorum_qdevice_reconfigure.newname, newname);
1717 
1718  iov[0].iov_base = (void *)&req_exec_quorum_qdevice_reconfigure;
1719  iov[0].iov_len = sizeof(req_exec_quorum_qdevice_reconfigure);
1720 
1721  ret = corosync_api->totem_mcast (iov, 1, TOTEM_AGREED);
1722 
1723  LEAVE();
1724  return ret;
1725 }
1726 
1727 static int votequorum_exec_send_qdevice_reg(uint32_t operation, const char *qdevice_name_req)
1728 {
1729  struct req_exec_quorum_qdevice_reg req_exec_quorum_qdevice_reg;
1730  struct iovec iov[1];
1731  int ret;
1732 
1733  ENTER();
1734 
1735  req_exec_quorum_qdevice_reg.header.id = SERVICE_ID_MAKE(VOTEQUORUM_SERVICE, MESSAGE_REQ_EXEC_VOTEQUORUM_QDEVICE_REG);
1736  req_exec_quorum_qdevice_reg.header.size = sizeof(req_exec_quorum_qdevice_reg);
1737  req_exec_quorum_qdevice_reg.operation = operation;
1738  strcpy(req_exec_quorum_qdevice_reg.qdevice_name, qdevice_name_req);
1739 
1740  iov[0].iov_base = (void *)&req_exec_quorum_qdevice_reg;
1741  iov[0].iov_len = sizeof(req_exec_quorum_qdevice_reg);
1742 
1743  ret = corosync_api->totem_mcast (iov, 1, TOTEM_AGREED);
1744 
1745  LEAVE();
1746  return ret;
1747 }
1748 
1749 static int votequorum_exec_send_quorum_notification(void *conn, uint64_t context)
1750 {
1751  struct res_lib_votequorum_quorum_notification *res_lib_votequorum_notification;
1752  struct qb_list_head *tmp;
1753  struct cluster_node *node;
1754  int i = 0;
1755  int cluster_members = 0;
1756  int size;
1757  char buf[sizeof(struct res_lib_votequorum_quorum_notification) + sizeof(struct votequorum_node) * (PROCESSOR_COUNT_MAX + 2)];
1758 
1759  ENTER();
1760 
1761  log_printf(LOGSYS_LEVEL_DEBUG, "Sending quorum callback, quorate = %d", cluster_is_quorate);
1762 
1763  qb_list_for_each(tmp, &cluster_members_list) {
1764  node = qb_list_entry(tmp, struct cluster_node, list);
1765  cluster_members++;
1766  }
1768  cluster_members++;
1769  }
1770 
1771  size = sizeof(struct res_lib_votequorum_quorum_notification) + sizeof(struct votequorum_node) * cluster_members;
1772 
1773  res_lib_votequorum_notification = (struct res_lib_votequorum_quorum_notification *)&buf;
1774  res_lib_votequorum_notification->quorate = cluster_is_quorate;
1775  res_lib_votequorum_notification->context = context;
1776  res_lib_votequorum_notification->node_list_entries = cluster_members;
1777  res_lib_votequorum_notification->header.id = MESSAGE_RES_VOTEQUORUM_QUORUM_NOTIFICATION;
1778  res_lib_votequorum_notification->header.size = size;
1779  res_lib_votequorum_notification->header.error = CS_OK;
1780 
1781  /* Send all known nodes and their states */
1782  qb_list_for_each(tmp, &cluster_members_list) {
1783  node = qb_list_entry(tmp, struct cluster_node, list);
1784  res_lib_votequorum_notification->node_list[i].nodeid = node->node_id;
1785  res_lib_votequorum_notification->node_list[i++].state = node->state;
1786  }
1788  res_lib_votequorum_notification->node_list[i].nodeid = VOTEQUORUM_QDEVICE_NODEID;
1789  res_lib_votequorum_notification->node_list[i++].state = qdevice->state;
1790  }
1791 
1792  /* Send it to all interested parties */
1793  if (conn) {
1794  int ret = corosync_api->ipc_dispatch_send(conn, &buf, size);
1795  LEAVE();
1796  return ret;
1797  } else {
1798  struct quorum_pd *qpd;
1799 
1800  qb_list_for_each(tmp, &trackers_list) {
1801  qpd = qb_list_entry(tmp, struct quorum_pd, list);
1802  res_lib_votequorum_notification->context = qpd->tracking_context;
1803  corosync_api->ipc_dispatch_send(qpd->conn, &buf, size);
1804  }
1805  }
1806 
1807  LEAVE();
1808 
1809  return 0;
1810 }
1811 
1812 static int votequorum_exec_send_nodelist_notification(void *conn, uint64_t context)
1813 {
1814  struct res_lib_votequorum_nodelist_notification *res_lib_votequorum_notification;
1815  int i = 0;
1816  int size;
1817  struct qb_list_head *tmp;
1818  char buf[sizeof(struct res_lib_votequorum_nodelist_notification) + sizeof(uint32_t) * quorum_members_entries];
1819 
1820  ENTER();
1821 
1822  log_printf(LOGSYS_LEVEL_DEBUG, "Sending nodelist callback. ring_id = %d/%lld", quorum_ringid.nodeid, quorum_ringid.seq);
1823 
1824  size = sizeof(struct res_lib_votequorum_nodelist_notification) + sizeof(uint32_t) * quorum_members_entries;
1825 
1826  res_lib_votequorum_notification = (struct res_lib_votequorum_nodelist_notification *)&buf;
1827  res_lib_votequorum_notification->node_list_entries = quorum_members_entries;
1828  res_lib_votequorum_notification->ring_id.nodeid = quorum_ringid.nodeid;
1829  res_lib_votequorum_notification->ring_id.seq = quorum_ringid.seq;
1830  res_lib_votequorum_notification->context = context;
1831 
1832  for (i=0; i<quorum_members_entries; i++) {
1833  res_lib_votequorum_notification->node_list[i] = quorum_members[i];
1834  }
1835 
1836  res_lib_votequorum_notification->header.id = MESSAGE_RES_VOTEQUORUM_NODELIST_NOTIFICATION;
1837  res_lib_votequorum_notification->header.size = size;
1838  res_lib_votequorum_notification->header.error = CS_OK;
1839 
1840  /* Send it to all interested parties */
1841  if (conn) {
1842  int ret = corosync_api->ipc_dispatch_send(conn, &buf, size);
1843  LEAVE();
1844  return ret;
1845  } else {
1846  struct quorum_pd *qpd;
1847 
1848  qb_list_for_each(tmp, &trackers_list) {
1849  qpd = qb_list_entry(tmp, struct quorum_pd, list);
1850  res_lib_votequorum_notification->context = qpd->tracking_context;
1851  corosync_api->ipc_dispatch_send(qpd->conn, &buf, size);
1852  }
1853  }
1854 
1855  LEAVE();
1856 
1857  return 0;
1858 }
1859 
1860 static void votequorum_exec_send_expectedvotes_notification(void)
1861 {
1862  struct res_lib_votequorum_expectedvotes_notification res_lib_votequorum_expectedvotes_notification;
1863  struct quorum_pd *qpd;
1864  struct qb_list_head *tmp;
1865 
1866  ENTER();
1867 
1868  log_printf(LOGSYS_LEVEL_DEBUG, "Sending expected votes callback");
1869 
1870  res_lib_votequorum_expectedvotes_notification.header.id = MESSAGE_RES_VOTEQUORUM_EXPECTEDVOTES_NOTIFICATION;
1871  res_lib_votequorum_expectedvotes_notification.header.size = sizeof(res_lib_votequorum_expectedvotes_notification);
1872  res_lib_votequorum_expectedvotes_notification.header.error = CS_OK;
1873  res_lib_votequorum_expectedvotes_notification.expected_votes = us->expected_votes;
1874 
1875  qb_list_for_each(tmp, &trackers_list) {
1876  qpd = qb_list_entry(tmp, struct quorum_pd, list);
1877  res_lib_votequorum_expectedvotes_notification.context = qpd->tracking_context;
1878  corosync_api->ipc_dispatch_send(qpd->conn, &res_lib_votequorum_expectedvotes_notification,
1879  sizeof(struct res_lib_votequorum_expectedvotes_notification));
1880  }
1881 
1882  LEAVE();
1883 }
1884 
1885 static void exec_votequorum_qdevice_reconfigure_endian_convert (void *message)
1886 {
1887  ENTER();
1888 
1889  LEAVE();
1890 }
1891 
1892 static void message_handler_req_exec_votequorum_qdevice_reconfigure (
1893  const void *message,
1894  unsigned int nodeid)
1895 {
1897 
1898  ENTER();
1899 
1900  log_printf(LOGSYS_LEVEL_DEBUG, "Received qdevice name change req from node %u [from: %s to: %s]",
1901  nodeid,
1902  req_exec_quorum_qdevice_reconfigure->oldname,
1903  req_exec_quorum_qdevice_reconfigure->newname);
1904 
1905  if (!strcmp(req_exec_quorum_qdevice_reconfigure->oldname, qdevice_name)) {
1906  log_printf(LOGSYS_LEVEL_DEBUG, "Allowing qdevice rename");
1907  memset(qdevice_name, 0, VOTEQUORUM_QDEVICE_MAX_NAME_LEN);
1908  strcpy(qdevice_name, req_exec_quorum_qdevice_reconfigure->newname);
1909  /*
1910  * TODO: notify qdevices about name change?
1911  * this is not relevant for now and can wait later on since
1912  * qdevices are local only and libvotequorum is not final
1913  */
1914  }
1915 
1916  LEAVE();
1917 }
1918 
1919 static void exec_votequorum_qdevice_reg_endian_convert (void *message)
1920 {
1922 
1923  ENTER();
1924 
1925  req_exec_quorum_qdevice_reg->operation = swab32(req_exec_quorum_qdevice_reg->operation);
1926 
1927  LEAVE();
1928 }
1929 
1930 static void message_handler_req_exec_votequorum_qdevice_reg (
1931  const void *message,
1932  unsigned int nodeid)
1933 {
1935  struct res_lib_votequorum_status res_lib_votequorum_status;
1936  int wipe_qdevice_name = 1;
1937  struct cluster_node *node = NULL;
1938  struct qb_list_head *tmp;
1939  cs_error_t error = CS_OK;
1940 
1941  ENTER();
1942 
1943  log_printf(LOGSYS_LEVEL_DEBUG, "Received qdevice op %u req from node %u [%s]",
1944  req_exec_quorum_qdevice_reg->operation,
1945  nodeid, req_exec_quorum_qdevice_reg->qdevice_name);
1946 
1947  switch(req_exec_quorum_qdevice_reg->operation)
1948  {
1950  if (nodeid != us->node_id) {
1951  if (!strlen(qdevice_name)) {
1952  log_printf(LOGSYS_LEVEL_DEBUG, "Remote qdevice name recorded");
1953  strcpy(qdevice_name, req_exec_quorum_qdevice_reg->qdevice_name);
1954  }
1955  LEAVE();
1956  return;
1957  }
1958 
1959  /*
1960  * protect against the case where we broadcast qdevice registration
1961  * to new memebers, we receive the message back, but there is no registration
1962  * connection in progress
1963  */
1965  LEAVE();
1966  return;
1967  }
1968 
1969  /*
1970  * this should NEVER happen
1971  */
1972  if (!qdevice_reg_conn) {
1973  log_printf(LOGSYS_LEVEL_WARNING, "Unable to determine origin of the qdevice register call!");
1974  LEAVE();
1975  return;
1976  }
1977 
1978  /*
1979  * registering our own device in this case
1980  */
1981  if (!strlen(qdevice_name)) {
1982  strcpy(qdevice_name, req_exec_quorum_qdevice_reg->qdevice_name);
1983  }
1984 
1985  /*
1986  * check if it is our device or something else
1987  */
1988  if ((!strncmp(req_exec_quorum_qdevice_reg->qdevice_name,
1989  qdevice_name, VOTEQUORUM_QDEVICE_MAX_NAME_LEN))) {
1991  votequorum_exec_send_nodeinfo(VOTEQUORUM_QDEVICE_NODEID);
1992  votequorum_exec_send_nodeinfo(us->node_id);
1993  } else {
1995  "A new qdevice with different name (new: %s old: %s) is trying to register!",
1996  req_exec_quorum_qdevice_reg->qdevice_name, qdevice_name);
1997  error = CS_ERR_EXIST;
1998  }
1999 
2000  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
2001  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
2002  res_lib_votequorum_status.header.error = error;
2003  corosync_api->ipc_response_send(qdevice_reg_conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
2004  qdevice_reg_conn = NULL;
2005  break;
2007  qb_list_for_each(tmp, &cluster_members_list) {
2008  node = qb_list_entry(tmp, struct cluster_node, list);
2009  if ((node->state == NODESTATE_MEMBER) &&
2011  wipe_qdevice_name = 0;
2012  }
2013  }
2014 
2015  if (wipe_qdevice_name) {
2016  memset(qdevice_name, 0, VOTEQUORUM_QDEVICE_MAX_NAME_LEN);
2017  }
2018 
2019  break;
2020  }
2021  LEAVE();
2022 }
2023 
2024 static void exec_votequorum_nodeinfo_endian_convert (void *message)
2025 {
2026  struct req_exec_quorum_nodeinfo *nodeinfo = message;
2027 
2028  ENTER();
2029 
2030  nodeinfo->nodeid = swab32(nodeinfo->nodeid);
2031  nodeinfo->votes = swab32(nodeinfo->votes);
2032  nodeinfo->expected_votes = swab32(nodeinfo->expected_votes);
2033  nodeinfo->flags = swab32(nodeinfo->flags);
2034 
2035  LEAVE();
2036 }
2037 
2038 static void message_handler_req_exec_votequorum_nodeinfo (
2039  const void *message,
2040  unsigned int sender_nodeid)
2041 {
2042  const struct req_exec_quorum_nodeinfo *req_exec_quorum_nodeinfo = message;
2043  struct cluster_node *node = NULL;
2044  int old_votes;
2045  int old_expected;
2046  uint32_t old_flags;
2047  nodestate_t old_state;
2048  int new_node = 0;
2049  int allow_downgrade = 0;
2050  int by_node = 0;
2051  unsigned int nodeid = req_exec_quorum_nodeinfo->nodeid;
2052 
2053  ENTER();
2054 
2055  log_printf(LOGSYS_LEVEL_DEBUG, "got nodeinfo message from cluster node %u", sender_nodeid);
2056  log_printf(LOGSYS_LEVEL_DEBUG, "nodeinfo message[%u]: votes: %d, expected: %d flags: %d",
2057  nodeid,
2058  req_exec_quorum_nodeinfo->votes,
2059  req_exec_quorum_nodeinfo->expected_votes,
2060  req_exec_quorum_nodeinfo->flags);
2061 
2062  if (nodeid != VOTEQUORUM_QDEVICE_NODEID) {
2063  decode_flags(req_exec_quorum_nodeinfo->flags);
2064  }
2065 
2066  node = find_node_by_nodeid(nodeid);
2067  if (!node) {
2068  node = allocate_node(nodeid);
2069  new_node = 1;
2070  }
2071  if (!node) {
2072  corosync_api->error_memory_failure();
2073  LEAVE();
2074  return;
2075  }
2076 
2077  if (new_node) {
2078  old_votes = 0;
2079  old_expected = 0;
2080  old_state = NODESTATE_DEAD;
2081  old_flags = 0;
2082  } else {
2083  old_votes = node->votes;
2084  old_expected = node->expected_votes;
2085  old_state = node->state;
2086  old_flags = node->flags;
2087  }
2088 
2089  if (nodeid == VOTEQUORUM_QDEVICE_NODEID) {
2090  struct cluster_node *sender_node = find_node_by_nodeid(sender_nodeid);
2091 
2092  assert(sender_node != NULL);
2093 
2094  if ((!cluster_is_quorate) &&
2095  (sender_node->flags & NODE_FLAGS_QUORATE)) {
2096  node->votes = req_exec_quorum_nodeinfo->votes;
2097  } else {
2098  node->votes = max(node->votes, req_exec_quorum_nodeinfo->votes);
2099  }
2100  goto recalculate;
2101  }
2102 
2103  /* Update node state */
2104  node->flags = req_exec_quorum_nodeinfo->flags;
2105  node->votes = req_exec_quorum_nodeinfo->votes;
2106  node->state = NODESTATE_MEMBER;
2107 
2108  if (node->flags & NODE_FLAGS_LEAVING) {
2109  node->state = NODESTATE_LEAVING;
2110  allow_downgrade = 1;
2111  by_node = 1;
2112  }
2113 
2114  if ((!cluster_is_quorate) &&
2115  (node->flags & NODE_FLAGS_QUORATE)) {
2116  allow_downgrade = 1;
2117  us->expected_votes = req_exec_quorum_nodeinfo->expected_votes;
2118  }
2119 
2120  if (node->flags & NODE_FLAGS_QUORATE || (ev_tracking)) {
2121  node->expected_votes = req_exec_quorum_nodeinfo->expected_votes;
2122  } else {
2123  node->expected_votes = us->expected_votes;
2124  }
2125 
2126  if ((last_man_standing) && (node->votes > 1)) {
2127  log_printf(LOGSYS_LEVEL_WARNING, "Last Man Standing feature is supported only when all"
2128  "cluster nodes votes are set to 1. Disabling LMS.");
2129  last_man_standing = 0;
2130  if (last_man_standing_timer_set) {
2131  corosync_api->timer_delete(last_man_standing_timer);
2132  last_man_standing_timer_set = 0;
2133  }
2134  }
2135 
2136 recalculate:
2137  if ((new_node) ||
2138  (nodeid == us->node_id) ||
2139  (node->flags & NODE_FLAGS_FIRST) ||
2140  (old_votes != node->votes) ||
2141  (old_expected != node->expected_votes) ||
2142  (old_flags != node->flags) ||
2143  (old_state != node->state)) {
2144  recalculate_quorum(allow_downgrade, by_node);
2145  }
2146 
2147  if ((wait_for_all) &&
2148  (!(node->flags & NODE_FLAGS_WFASTATUS)) &&
2149  (node->flags & NODE_FLAGS_QUORATE)) {
2150  update_wait_for_all_status(0);
2151  }
2152 
2153  LEAVE();
2154 }
2155 
2156 static void exec_votequorum_reconfigure_endian_convert (void *message)
2157 {
2158  struct req_exec_quorum_reconfigure *reconfigure = message;
2159 
2160  ENTER();
2161 
2162  reconfigure->nodeid = swab32(reconfigure->nodeid);
2163  reconfigure->value = swab32(reconfigure->value);
2164 
2165  LEAVE();
2166 }
2167 
2168 static void message_handler_req_exec_votequorum_reconfigure (
2169  const void *message,
2170  unsigned int nodeid)
2171 {
2173  struct cluster_node *node;
2174 
2175  ENTER();
2176 
2177  log_printf(LOGSYS_LEVEL_DEBUG, "got reconfigure message from cluster node %u for %u",
2178  nodeid, req_exec_quorum_reconfigure->nodeid);
2179 
2180  switch(req_exec_quorum_reconfigure->param)
2181  {
2183  update_node_expected_votes(req_exec_quorum_reconfigure->value);
2184  votequorum_exec_send_expectedvotes_notification();
2185  update_ev_barrier(req_exec_quorum_reconfigure->value);
2186  if (ev_tracking) {
2187  us->expected_votes = max(us->expected_votes, ev_tracking_barrier);
2188  }
2189  recalculate_quorum(1, 0); /* Allow decrease */
2190  break;
2191 
2193  node = find_node_by_nodeid(req_exec_quorum_reconfigure->nodeid);
2194  if (!node) {
2195  LEAVE();
2196  return;
2197  }
2198  node->votes = req_exec_quorum_reconfigure->value;
2199  recalculate_quorum(1, 0); /* Allow decrease */
2200  break;
2201 
2203  update_wait_for_all_status(0);
2204  log_printf(LOGSYS_LEVEL_INFO, "wait_for_all_status reset by user on node %d.",
2205  req_exec_quorum_reconfigure->nodeid);
2206  recalculate_quorum(0, 0);
2207 
2208  break;
2209 
2210  }
2211 
2212  LEAVE();
2213 }
2214 
2215 static int votequorum_exec_exit_fn (void)
2216 {
2217  int ret = 0;
2218 
2219  ENTER();
2220 
2221  /*
2222  * tell the other nodes we are leaving
2223  */
2224 
2225  if (allow_downscale) {
2226  us->flags |= NODE_FLAGS_LEAVING;
2227  ret = votequorum_exec_send_nodeinfo(us->node_id);
2228  }
2229 
2230  if ((ev_tracking) && (ev_tracking_fd != -1)) {
2231  close(ev_tracking_fd);
2232  }
2233 
2234 
2235  LEAVE();
2236  return ret;
2237 }
2238 
2239 static void votequorum_set_icmap_ro_keys(void)
2240 {
2241  icmap_set_ro_access("quorum.allow_downscale", CS_FALSE, CS_TRUE);
2242  icmap_set_ro_access("quorum.wait_for_all", CS_FALSE, CS_TRUE);
2243  icmap_set_ro_access("quorum.last_man_standing", CS_FALSE, CS_TRUE);
2244  icmap_set_ro_access("quorum.last_man_standing_window", CS_FALSE, CS_TRUE);
2245  icmap_set_ro_access("quorum.expected_votes_tracking", CS_FALSE, CS_TRUE);
2246  icmap_set_ro_access("quorum.auto_tie_breaker", CS_FALSE, CS_TRUE);
2247  icmap_set_ro_access("quorum.auto_tie_breaker_node", CS_FALSE, CS_TRUE);
2248 }
2249 
2250 static char *votequorum_exec_init_fn (struct corosync_api_v1 *api)
2251 {
2252  char *error = NULL;
2253 
2254  ENTER();
2255 
2256  /*
2257  * make sure we start clean
2258  */
2259  qb_list_init(&cluster_members_list);
2260  qb_list_init(&trackers_list);
2261  qdevice = NULL;
2262  us = NULL;
2263  memset(cluster_nodes, 0, sizeof(cluster_nodes));
2264 
2265  /*
2266  * Allocate a cluster_node for qdevice
2267  */
2268  qdevice = allocate_node(VOTEQUORUM_QDEVICE_NODEID);
2269  if (!qdevice) {
2270  LEAVE();
2271  return ((char *)"Could not allocate node.");
2272  }
2273  qdevice->votes = 0;
2274  memset(qdevice_name, 0, VOTEQUORUM_QDEVICE_MAX_NAME_LEN);
2275 
2276  /*
2277  * Allocate a cluster_node for us
2278  */
2279  us = allocate_node(corosync_api->totem_nodeid_get());
2280  if (!us) {
2281  LEAVE();
2282  return ((char *)"Could not allocate node.");
2283  }
2284 
2285  icmap_set_uint32("runtime.votequorum.this_node_id", us->node_id);
2286 
2287  us->state = NODESTATE_MEMBER;
2288  us->votes = 1;
2289  us->flags |= NODE_FLAGS_FIRST;
2290 
2291  error = votequorum_readconfig(VOTEQUORUM_READCONFIG_STARTUP);
2292  if (error) {
2293  return error;
2294  }
2295  recalculate_quorum(0, 0);
2296 
2297  /*
2298  * Set RO keys in icmap
2299  */
2300  votequorum_set_icmap_ro_keys();
2301 
2302  /*
2303  * Listen for changes
2304  */
2305  votequorum_exec_add_config_notification();
2306 
2307  /*
2308  * Start us off with one node
2309  */
2310  votequorum_exec_send_nodeinfo(us->node_id);
2311 
2312  LEAVE();
2313 
2314  return (NULL);
2315 }
2316 
2317 /*
2318  * votequorum service core
2319  */
2320 
2321 static void votequorum_last_man_standing_timer_fn(void *arg)
2322 {
2323  ENTER();
2324 
2325  last_man_standing_timer_set = 0;
2326  if (cluster_is_quorate) {
2327  recalculate_quorum(1,1);
2328  }
2329 
2330  LEAVE();
2331 }
2332 
2333 static void votequorum_sync_init (
2334  const unsigned int *trans_list, size_t trans_list_entries,
2335  const unsigned int *member_list, size_t member_list_entries,
2336  const struct memb_ring_id *ring_id)
2337 {
2338  int i, j;
2339  int found;
2340  int left_nodes;
2341  struct cluster_node *node;
2342 
2343  ENTER();
2344 
2345  sync_in_progress = 1;
2346  sync_nodeinfo_sent = 0;
2347  sync_wait_for_poll_or_timeout = 0;
2348 
2349  if (member_list_entries > 1) {
2350  us->flags &= ~NODE_FLAGS_FIRST;
2351  }
2352 
2353  /*
2354  * we don't need to track which nodes have left directly,
2355  * since that info is in the node db, but we need to know
2356  * if somebody has left for last_man_standing
2357  */
2358  left_nodes = 0;
2359  for (i = 0; i < quorum_members_entries; i++) {
2360  found = 0;
2361  for (j = 0; j < member_list_entries; j++) {
2362  if (quorum_members[i] == member_list[j]) {
2363  found = 1;
2364  break;
2365  }
2366  }
2367  if (found == 0) {
2368  left_nodes = 1;
2369  node = find_node_by_nodeid(quorum_members[i]);
2370  if (node) {
2371  node->state = NODESTATE_DEAD;
2372  }
2373  }
2374  }
2375 
2376  if (last_man_standing) {
2377  if (((member_list_entries >= quorum) && (left_nodes)) ||
2378  ((member_list_entries <= quorum) && (auto_tie_breaker != ATB_NONE) && (check_low_node_id_partition() == 1))) {
2379  if (last_man_standing_timer_set) {
2380  corosync_api->timer_delete(last_man_standing_timer);
2381  last_man_standing_timer_set = 0;
2382  }
2383  corosync_api->timer_add_duration((unsigned long long)last_man_standing_window*1000000,
2384  NULL, votequorum_last_man_standing_timer_fn,
2385  &last_man_standing_timer);
2386  last_man_standing_timer_set = 1;
2387  }
2388  }
2389 
2390  memcpy(previous_quorum_members, quorum_members, sizeof(unsigned int) * quorum_members_entries);
2391  previous_quorum_members_entries = quorum_members_entries;
2392 
2393  memcpy(quorum_members, member_list, sizeof(unsigned int) * member_list_entries);
2394  quorum_members_entries = member_list_entries;
2395  memcpy(&quorum_ringid, ring_id, sizeof(*ring_id));
2396 
2398  /*
2399  * Reset poll timer. Sync waiting is interrupted on valid qdevice poll or after timeout
2400  */
2401  if (qdevice_timer_set) {
2402  corosync_api->timer_delete(qdevice_timer);
2403  }
2404  corosync_api->timer_add_duration((unsigned long long)qdevice_sync_timeout*1000000, qdevice,
2405  qdevice_timer_fn, &qdevice_timer);
2406  qdevice_timer_set = 1;
2407  sync_wait_for_poll_or_timeout = 1;
2408 
2409  log_printf(LOGSYS_LEVEL_INFO, "waiting for quorum device %s poll (but maximum for %u ms)",
2410  qdevice_name, qdevice_sync_timeout);
2411  }
2412 
2413  LEAVE();
2414 }
2415 
2416 static int votequorum_sync_process (void)
2417 {
2418  if (!sync_nodeinfo_sent) {
2419  votequorum_exec_send_nodeinfo(us->node_id);
2420  votequorum_exec_send_nodeinfo(VOTEQUORUM_QDEVICE_NODEID);
2421  if (strlen(qdevice_name)) {
2422  votequorum_exec_send_qdevice_reg(VOTEQUORUM_QDEVICE_OPERATION_REGISTER,
2423  qdevice_name);
2424  }
2425  votequorum_exec_send_nodelist_notification(NULL, 0LL);
2426  sync_nodeinfo_sent = 1;
2427  }
2428 
2429  if (us->flags & NODE_FLAGS_QDEVICE_REGISTERED && sync_wait_for_poll_or_timeout) {
2430  /*
2431  * Waiting for qdevice to poll with new ringid or timeout
2432  */
2433 
2434  return (-1);
2435  }
2436 
2437  return 0;
2438 }
2439 
2440 static void votequorum_sync_activate (void)
2441 {
2442  recalculate_quorum(0, 0);
2443  quorum_callback(quorum_members, quorum_members_entries,
2444  cluster_is_quorate, &quorum_ringid);
2445  votequorum_exec_send_quorum_notification(NULL, 0L);
2446 
2447  sync_in_progress = 0;
2448 }
2449 
2450 static void votequorum_sync_abort (void)
2451 {
2452 
2453 }
2454 
2456  quorum_set_quorate_fn_t q_set_quorate_fn)
2457 {
2458  char *error;
2459 
2460  ENTER();
2461 
2462  if (q_set_quorate_fn == NULL) {
2463  return ((char *)"Quorate function not set");
2464  }
2465 
2466  corosync_api = api;
2467  quorum_callback = q_set_quorate_fn;
2468 
2469  error = corosync_service_link_and_init(corosync_api,
2470  &votequorum_service[0]);
2471  if (error) {
2472  return (error);
2473  }
2474 
2475  LEAVE();
2476 
2477  return (NULL);
2478 }
2479 
2480 /*
2481  * Library Handler init/fini
2482  */
2483 
2484 static int quorum_lib_init_fn (void *conn)
2485 {
2486  struct quorum_pd *pd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
2487 
2488  ENTER();
2489 
2490  qb_list_init (&pd->list);
2491  pd->conn = conn;
2492 
2493  LEAVE();
2494  return (0);
2495 }
2496 
2497 static int quorum_lib_exit_fn (void *conn)
2498 {
2499  struct quorum_pd *quorum_pd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
2500 
2501  ENTER();
2502 
2503  if (quorum_pd->tracking_enabled) {
2504  qb_list_del (&quorum_pd->list);
2505  qb_list_init (&quorum_pd->list);
2506  }
2507 
2508  LEAVE();
2509 
2510  return (0);
2511 }
2512 
2513 /*
2514  * library internal functions
2515  */
2516 
2517 static void qdevice_timer_fn(void *arg)
2518 {
2519  ENTER();
2520 
2521  if ((!(us->flags & NODE_FLAGS_QDEVICE_ALIVE)) ||
2522  (!qdevice_timer_set)) {
2523  LEAVE();
2524  return;
2525  }
2526 
2529  log_printf(LOGSYS_LEVEL_INFO, "lost contact with quorum device %s", qdevice_name);
2530  votequorum_exec_send_nodeinfo(us->node_id);
2531 
2532  qdevice_timer_set = 0;
2533  sync_wait_for_poll_or_timeout = 0;
2534 
2535  LEAVE();
2536 }
2537 
2538 /*
2539  * Library Handler Functions
2540  */
2541 
2542 static void message_handler_req_lib_votequorum_getinfo (void *conn, const void *message)
2543 {
2545  struct res_lib_votequorum_getinfo res_lib_votequorum_getinfo;
2546  struct cluster_node *node;
2547  unsigned int highest_expected = 0;
2548  unsigned int total_votes = 0;
2549  cs_error_t error = CS_OK;
2550  uint32_t nodeid = req_lib_votequorum_getinfo->nodeid;
2551 
2552  ENTER();
2553 
2554  log_printf(LOGSYS_LEVEL_DEBUG, "got getinfo request on %p for node %u", conn, req_lib_votequorum_getinfo->nodeid);
2555 
2556  if (nodeid == VOTEQUORUM_QDEVICE_NODEID) {
2557  nodeid = us->node_id;
2558  }
2559 
2560  node = find_node_by_nodeid(nodeid);
2561  if (node) {
2562  struct cluster_node *iternode;
2563  struct qb_list_head *nodelist;
2564 
2565  qb_list_for_each(nodelist, &cluster_members_list) {
2566  iternode = qb_list_entry(nodelist, struct cluster_node, list);
2567 
2568  if (iternode->state == NODESTATE_MEMBER) {
2569  highest_expected =
2570  max(highest_expected, iternode->expected_votes);
2571  total_votes += iternode->votes;
2572  }
2573  }
2574 
2575  if (node->flags & NODE_FLAGS_QDEVICE_CAST_VOTE) {
2576  total_votes += qdevice->votes;
2577  }
2578 
2579  switch(node->state) {
2580  case NODESTATE_MEMBER:
2581  res_lib_votequorum_getinfo.state = VOTEQUORUM_NODESTATE_MEMBER;
2582  break;
2583  case NODESTATE_DEAD:
2584  res_lib_votequorum_getinfo.state = VOTEQUORUM_NODESTATE_DEAD;
2585  break;
2586  case NODESTATE_LEAVING:
2587  res_lib_votequorum_getinfo.state = VOTEQUORUM_NODESTATE_LEAVING;
2588  break;
2589  default:
2590  res_lib_votequorum_getinfo.state = node->state;
2591  break;
2592  }
2593  res_lib_votequorum_getinfo.state = node->state;
2594  res_lib_votequorum_getinfo.votes = node->votes;
2595  res_lib_votequorum_getinfo.expected_votes = node->expected_votes;
2596  res_lib_votequorum_getinfo.highest_expected = highest_expected;
2597 
2598  res_lib_votequorum_getinfo.quorum = quorum;
2599  res_lib_votequorum_getinfo.total_votes = total_votes;
2600  res_lib_votequorum_getinfo.flags = 0;
2601  res_lib_votequorum_getinfo.nodeid = node->node_id;
2602 
2603  if (two_node) {
2604  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_TWONODE;
2605  }
2606  if (cluster_is_quorate) {
2607  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_QUORATE;
2608  }
2609  if (wait_for_all) {
2610  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_WAIT_FOR_ALL;
2611  }
2612  if (last_man_standing) {
2613  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_LAST_MAN_STANDING;
2614  }
2615  if (auto_tie_breaker != ATB_NONE) {
2616  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_AUTO_TIE_BREAKER;
2617  }
2618  if (allow_downscale) {
2619  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_ALLOW_DOWNSCALE;
2620  }
2621 
2622  memset(res_lib_votequorum_getinfo.qdevice_name, 0, VOTEQUORUM_QDEVICE_MAX_NAME_LEN);
2623  strcpy(res_lib_votequorum_getinfo.qdevice_name, qdevice_name);
2624  res_lib_votequorum_getinfo.qdevice_votes = qdevice->votes;
2625 
2626  if (node->flags & NODE_FLAGS_QDEVICE_REGISTERED) {
2627  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_QDEVICE_REGISTERED;
2628  }
2629  if (node->flags & NODE_FLAGS_QDEVICE_ALIVE) {
2630  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_QDEVICE_ALIVE;
2631  }
2632  if (node->flags & NODE_FLAGS_QDEVICE_CAST_VOTE) {
2633  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_QDEVICE_CAST_VOTE;
2634  }
2635  if (node->flags & NODE_FLAGS_QDEVICE_MASTER_WINS) {
2636  res_lib_votequorum_getinfo.flags |= VOTEQUORUM_INFO_QDEVICE_MASTER_WINS;
2637  }
2638  } else {
2639  error = CS_ERR_NOT_EXIST;
2640  }
2641 
2642  res_lib_votequorum_getinfo.header.size = sizeof(res_lib_votequorum_getinfo);
2643  res_lib_votequorum_getinfo.header.id = MESSAGE_RES_VOTEQUORUM_GETINFO;
2644  res_lib_votequorum_getinfo.header.error = error;
2645  corosync_api->ipc_response_send(conn, &res_lib_votequorum_getinfo, sizeof(res_lib_votequorum_getinfo));
2646  log_printf(LOGSYS_LEVEL_DEBUG, "getinfo response error: %d", error);
2647 
2648  LEAVE();
2649 }
2650 
2651 static void message_handler_req_lib_votequorum_setexpected (void *conn, const void *message)
2652 {
2654  struct res_lib_votequorum_status res_lib_votequorum_status;
2655  cs_error_t error = CS_OK;
2656  unsigned int newquorum;
2657  unsigned int total_votes;
2658  uint8_t allow_downscale_status = 0;
2659 
2660  ENTER();
2661 
2662  allow_downscale_status = allow_downscale;
2663  allow_downscale = 0;
2664 
2665  /*
2666  * Validate new expected votes
2667  */
2668  newquorum = calculate_quorum(1, req_lib_votequorum_setexpected->expected_votes, &total_votes);
2669  allow_downscale = allow_downscale_status;
2670  if (newquorum < total_votes / 2 ||
2671  newquorum > total_votes) {
2672  error = CS_ERR_INVALID_PARAM;
2673  goto error_exit;
2674  }
2675  update_node_expected_votes(req_lib_votequorum_setexpected->expected_votes);
2676 
2677  if (votequorum_exec_send_reconfigure(VOTEQUORUM_RECONFIG_PARAM_EXPECTED_VOTES, us->node_id,
2678  req_lib_votequorum_setexpected->expected_votes)) {
2679  error = CS_ERR_NO_RESOURCES;
2680  }
2681 
2682 error_exit:
2683  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
2684  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
2685  res_lib_votequorum_status.header.error = error;
2686  corosync_api->ipc_response_send(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
2687 
2688  LEAVE();
2689 }
2690 
2691 static void message_handler_req_lib_votequorum_setvotes (void *conn, const void *message)
2692 {
2694  struct res_lib_votequorum_status res_lib_votequorum_status;
2695  struct cluster_node *node;
2696  unsigned int newquorum;
2697  unsigned int total_votes;
2698  unsigned int saved_votes;
2699  cs_error_t error = CS_OK;
2700  unsigned int nodeid;
2701 
2702  ENTER();
2703 
2704  nodeid = req_lib_votequorum_setvotes->nodeid;
2705  node = find_node_by_nodeid(nodeid);
2706  if (!node) {
2707  error = CS_ERR_NAME_NOT_FOUND;
2708  goto error_exit;
2709  }
2710 
2711  /*
2712  * Check votes is valid
2713  */
2714  saved_votes = node->votes;
2715  node->votes = req_lib_votequorum_setvotes->votes;
2716 
2717  newquorum = calculate_quorum(1, 0, &total_votes);
2718 
2719  if (newquorum < total_votes / 2 ||
2720  newquorum > total_votes) {
2721  node->votes = saved_votes;
2722  error = CS_ERR_INVALID_PARAM;
2723  goto error_exit;
2724  }
2725 
2726  if (votequorum_exec_send_reconfigure(VOTEQUORUM_RECONFIG_PARAM_NODE_VOTES, nodeid,
2727  req_lib_votequorum_setvotes->votes)) {
2728  error = CS_ERR_NO_RESOURCES;
2729  }
2730 
2731 error_exit:
2732  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
2733  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
2734  res_lib_votequorum_status.header.error = error;
2735  corosync_api->ipc_response_send(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
2736 
2737  LEAVE();
2738 }
2739 
2740 static void message_handler_req_lib_votequorum_trackstart (void *conn,
2741  const void *message)
2742 {
2744  struct res_lib_votequorum_status res_lib_votequorum_status;
2745  struct quorum_pd *quorum_pd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
2746  cs_error_t error = CS_OK;
2747 
2748  ENTER();
2749 
2750  /*
2751  * If an immediate listing of the current cluster membership
2752  * is requested, generate membership list
2753  */
2754  if (req_lib_votequorum_trackstart->track_flags & CS_TRACK_CURRENT ||
2755  req_lib_votequorum_trackstart->track_flags & CS_TRACK_CHANGES) {
2756  log_printf(LOGSYS_LEVEL_DEBUG, "sending initial status to %p", conn);
2757  votequorum_exec_send_nodelist_notification(conn, req_lib_votequorum_trackstart->context);
2758  votequorum_exec_send_quorum_notification(conn, req_lib_votequorum_trackstart->context);
2759  }
2760 
2761  if (quorum_pd->tracking_enabled) {
2762  error = CS_ERR_EXIST;
2763  goto response_send;
2764  }
2765 
2766  /*
2767  * Record requests for tracking
2768  */
2769  if (req_lib_votequorum_trackstart->track_flags & CS_TRACK_CHANGES ||
2770  req_lib_votequorum_trackstart->track_flags & CS_TRACK_CHANGES_ONLY) {
2771 
2772  quorum_pd->track_flags = req_lib_votequorum_trackstart->track_flags;
2773  quorum_pd->tracking_enabled = 1;
2774  quorum_pd->tracking_context = req_lib_votequorum_trackstart->context;
2775 
2776  qb_list_add (&quorum_pd->list, &trackers_list);
2777  }
2778 
2779 response_send:
2780  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
2781  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
2782  res_lib_votequorum_status.header.error = error;
2783  corosync_api->ipc_response_send(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
2784 
2785  LEAVE();
2786 }
2787 
2788 static void message_handler_req_lib_votequorum_trackstop (void *conn,
2789  const void *message)
2790 {
2791  struct res_lib_votequorum_status res_lib_votequorum_status;
2792  struct quorum_pd *quorum_pd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
2793  int error = CS_OK;
2794 
2795  ENTER();
2796 
2797  if (quorum_pd->tracking_enabled) {
2798  error = CS_OK;
2799  quorum_pd->tracking_enabled = 0;
2800  qb_list_del (&quorum_pd->list);
2801  qb_list_init (&quorum_pd->list);
2802  } else {
2803  error = CS_ERR_NOT_EXIST;
2804  }
2805 
2806  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
2807  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
2808  res_lib_votequorum_status.header.error = error;
2809  corosync_api->ipc_response_send(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
2810 
2811  LEAVE();
2812 }
2813 
2814 static void message_handler_req_lib_votequorum_qdevice_register (void *conn,
2815  const void *message)
2816 {
2818  struct res_lib_votequorum_status res_lib_votequorum_status;
2819  cs_error_t error = CS_OK;
2820 
2821  ENTER();
2822 
2823  if (!qdevice_can_operate) {
2824  log_printf(LOGSYS_LEVEL_INFO, "Registration of quorum device is disabled by incorrect corosync.conf. See logs for more information");
2825  error = CS_ERR_ACCESS;
2826  goto out;
2827  }
2828 
2830  if ((!strncmp(req_lib_votequorum_qdevice_register->name,
2831  qdevice_name, VOTEQUORUM_QDEVICE_MAX_NAME_LEN))) {
2832  goto out;
2833  } else {
2835  "A new qdevice with different name (new: %s old: %s) is trying to re-register!",
2836  req_lib_votequorum_qdevice_register->name, qdevice_name);
2837  error = CS_ERR_EXIST;
2838  goto out;
2839  }
2840  } else {
2841  if (qdevice_reg_conn != NULL) {
2843  "Registration request already in progress");
2844  error = CS_ERR_TRY_AGAIN;
2845  goto out;
2846  }
2847  qdevice_reg_conn = conn;
2848  if (votequorum_exec_send_qdevice_reg(VOTEQUORUM_QDEVICE_OPERATION_REGISTER,
2849  req_lib_votequorum_qdevice_register->name) != 0) {
2851  "Unable to send qdevice registration request to cluster");
2852  error = CS_ERR_TRY_AGAIN;
2853  qdevice_reg_conn = NULL;
2854  } else {
2855  LEAVE();
2856  return;
2857  }
2858  }
2859 
2860 out:
2861 
2862  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
2863  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
2864  res_lib_votequorum_status.header.error = error;
2865  corosync_api->ipc_response_send(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
2866 
2867  LEAVE();
2868 }
2869 
2870 static void message_handler_req_lib_votequorum_qdevice_unregister (void *conn,
2871  const void *message)
2872 {
2874  struct res_lib_votequorum_status res_lib_votequorum_status;
2875  cs_error_t error = CS_OK;
2876 
2877  ENTER();
2878 
2880  if (strncmp(req_lib_votequorum_qdevice_unregister->name, qdevice_name, VOTEQUORUM_QDEVICE_MAX_NAME_LEN)) {
2881  error = CS_ERR_INVALID_PARAM;
2882  goto out;
2883  }
2884  if (qdevice_timer_set) {
2885  corosync_api->timer_delete(qdevice_timer);
2886  qdevice_timer_set = 0;
2887  sync_wait_for_poll_or_timeout = 0;
2888  }
2893  votequorum_exec_send_nodeinfo(us->node_id);
2894  votequorum_exec_send_qdevice_reg(VOTEQUORUM_QDEVICE_OPERATION_UNREGISTER,
2895  req_lib_votequorum_qdevice_unregister->name);
2896  } else {
2897  error = CS_ERR_NOT_EXIST;
2898  }
2899 
2900 out:
2901  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
2902  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
2903  res_lib_votequorum_status.header.error = error;
2904  corosync_api->ipc_response_send(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
2905 
2906  LEAVE();
2907 }
2908 
2909 static void message_handler_req_lib_votequorum_qdevice_update (void *conn,
2910  const void *message)
2911 {
2913  struct res_lib_votequorum_status res_lib_votequorum_status;
2914  cs_error_t error = CS_OK;
2915 
2916  ENTER();
2917 
2919  if (strncmp(req_lib_votequorum_qdevice_update->oldname, qdevice_name, VOTEQUORUM_QDEVICE_MAX_NAME_LEN)) {
2920  error = CS_ERR_INVALID_PARAM;
2921  goto out;
2922  }
2923  votequorum_exec_send_qdevice_reconfigure(req_lib_votequorum_qdevice_update->oldname,
2924  req_lib_votequorum_qdevice_update->newname);
2925  } else {
2926  error = CS_ERR_NOT_EXIST;
2927  }
2928 
2929 out:
2930  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
2931  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
2932  res_lib_votequorum_status.header.error = error;
2933  corosync_api->ipc_response_send(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
2934 
2935  LEAVE();
2936 }
2937 
2938 static void message_handler_req_lib_votequorum_qdevice_poll (void *conn,
2939  const void *message)
2940 {
2942  struct res_lib_votequorum_status res_lib_votequorum_status;
2943  cs_error_t error = CS_OK;
2944  uint32_t oldflags;
2945 
2946  ENTER();
2947 
2948  if (!qdevice_can_operate) {
2949  error = CS_ERR_ACCESS;
2950  goto out;
2951  }
2952 
2954  if (!(req_lib_votequorum_qdevice_poll->ring_id.nodeid == quorum_ringid.nodeid &&
2955  req_lib_votequorum_qdevice_poll->ring_id.seq == quorum_ringid.seq)) {
2956  log_printf(LOGSYS_LEVEL_DEBUG, "Received poll ring id (%u.%"PRIu64") != last sync "
2957  "ring id (%u.%"PRIu64"). Ignoring poll call.",
2958  req_lib_votequorum_qdevice_poll->ring_id.nodeid, req_lib_votequorum_qdevice_poll->ring_id.seq,
2959  quorum_ringid.nodeid, quorum_ringid.seq);
2960  error = CS_ERR_MESSAGE_ERROR;
2961  goto out;
2962  }
2963  if (strncmp(req_lib_votequorum_qdevice_poll->name, qdevice_name, VOTEQUORUM_QDEVICE_MAX_NAME_LEN)) {
2964  error = CS_ERR_INVALID_PARAM;
2965  goto out;
2966  }
2967 
2968  if (qdevice_timer_set) {
2969  corosync_api->timer_delete(qdevice_timer);
2970  qdevice_timer_set = 0;
2971  }
2972 
2973  oldflags = us->flags;
2974 
2976 
2977  if (req_lib_votequorum_qdevice_poll->cast_vote) {
2979  } else {
2981  }
2982 
2983  if (us->flags != oldflags) {
2984  votequorum_exec_send_nodeinfo(us->node_id);
2985  }
2986 
2987  corosync_api->timer_add_duration((unsigned long long)qdevice_timeout*1000000, qdevice,
2988  qdevice_timer_fn, &qdevice_timer);
2989  qdevice_timer_set = 1;
2990  sync_wait_for_poll_or_timeout = 0;
2991  } else {
2992  error = CS_ERR_NOT_EXIST;
2993  }
2994 
2995 out:
2996  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
2997  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
2998  res_lib_votequorum_status.header.error = error;
2999  corosync_api->ipc_response_send(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
3000 
3001  LEAVE();
3002 }
3003 
3004 static void message_handler_req_lib_votequorum_qdevice_master_wins (void *conn,
3005  const void *message)
3006 {
3008  struct res_lib_votequorum_status res_lib_votequorum_status;
3009  cs_error_t error = CS_OK;
3010  uint32_t oldflags = us->flags;
3011 
3012  ENTER();
3013 
3014  if (!qdevice_can_operate) {
3015  error = CS_ERR_ACCESS;
3016  goto out;
3017  }
3018 
3020  if (strncmp(req_lib_votequorum_qdevice_master_wins->name, qdevice_name, VOTEQUORUM_QDEVICE_MAX_NAME_LEN)) {
3021  error = CS_ERR_INVALID_PARAM;
3022  goto out;
3023  }
3024 
3025  if (req_lib_votequorum_qdevice_master_wins->allow) {
3027  } else {
3029  }
3030 
3031  if (us->flags != oldflags) {
3032  votequorum_exec_send_nodeinfo(us->node_id);
3033  }
3034 
3035  update_qdevice_master_wins(req_lib_votequorum_qdevice_master_wins->allow);
3036  } else {
3037  error = CS_ERR_NOT_EXIST;
3038  }
3039 
3040 out:
3041  res_lib_votequorum_status.header.size = sizeof(res_lib_votequorum_status);
3042  res_lib_votequorum_status.header.id = MESSAGE_RES_VOTEQUORUM_STATUS;
3043  res_lib_votequorum_status.header.error = error;
3044  corosync_api->ipc_response_send(conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
3045 
3046  LEAVE();
3047 }
uint32_t expected_votes
char name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
char newname[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
void *(* ipc_private_data_get)(void *conn)
Definition: coroapi.h:256
#define VOTEQUORUM_INFO_QUORATE
#define TOTEM_AGREED
Definition: coroapi.h:102
#define CS_TRUE
Definition: corotypes.h:54
const char * name
Definition: coroapi.h:491
char oldname[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
#define VOTEQUORUM_READCONFIG_STARTUP
void(* timer_delete)(corosync_timer_handle_t timer_handle)
Definition: coroapi.h:241
int(* timer_add_duration)(unsigned long long nanoseconds_in_future, void *data, void(*timer_nf)(void *data), corosync_timer_handle_t *handle)
Definition: coroapi.h:229
const char * icmap_iter_next(icmap_iter_t iter, size_t *value_len, icmap_value_types_t *type)
Return next item in iterator iter.
Definition: icmap.c:1087
#define NODE_FLAGS_WFASTATUS
#define LOGSYS_LEVEL_INFO
Definition: logsys.h:75
uint32_t value
#define CS_FALSE
Definition: corotypes.h:53
#define NODE_FLAGS_QUORATE
#define VOTEQUORUM_QDEVICE_DEFAULT_SYNC_TIMEOUT
The corosync_service_engine struct.
Definition: coroapi.h:490
void icmap_iter_finalize(icmap_iter_t iter)
Finalize iterator.
Definition: icmap.c:1108
The req_lib_votequorum_qdevice_master_wins struct.
#define VOTEQUORUM_QDEVICE_OPERATION_UNREGISTER
#define MESSAGE_REQ_EXEC_VOTEQUORUM_RECONFIGURE
#define max(a, b)
int(* ipc_response_send)(void *conn, const void *msg, size_t mlen)
Definition: coroapi.h:258
char * votequorum_init(struct corosync_api_v1 *api, quorum_set_quorate_fn_t q_set_quorate_fn)
nodestate_t
#define VOTEQUORUM_RECONFIG_PARAM_CANCEL_WFA
int tracking_enabled
char name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
char qdevice_name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
#define CS_TRACK_CURRENT
Definition: corotypes.h:87
The req_lib_votequorum_qdevice_unregister struct.
#define NODE_FLAGS_QDEVICE_MASTER_WINS
nodestate_t state
The res_lib_votequorum_quorum_notification struct.
The corosync_lib_handler struct.
Definition: coroapi.h:467
#define VOTEQUORUM_INFO_LAST_MAN_STANDING
#define VOTEQUORUM_INFO_WAIT_FOR_ALL
#define NODE_FLAGS_QDEVICE_CAST_VOTE
uint32_t operation
The res_lib_votequorum_status struct.
char name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
#define MESSAGE_REQ_EXEC_VOTEQUORUM_QDEVICE_RECONFIGURE
The corosync_exec_handler struct.
Definition: coroapi.h:475
#define VOTEQUORUM_INFO_TWONODE
int(* totem_mcast)(const struct iovec *iovec, unsigned int iov_len, unsigned int guarantee)
Definition: coroapi.h:279
char qdevice_name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
#define VOTEQUORUM_INFO_QDEVICE_REGISTERED
#define log_printf(level, format, args...)
Definition: logsys.h:323
void(* exec_handler_fn)(const void *msg, unsigned int nodeid)
Definition: coroapi.h:476
char oldname[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
#define VOTEQUORUM_QDEVICE_NODEID
#define VOTEQUORUM_INFO_QDEVICE_MASTER_WINS
#define VOTEQUORUM_NODESTATE_MEMBER
#define CS_TRACK_CHANGES
Definition: corotypes.h:88
#define SERVICE_ID_MAKE(a, b)
Definition: coroapi.h:458
#define ICMAP_TRACK_DELETE
Definition: icmap.h:77
#define ICMAP_KEYNAME_MAXLEN
Maximum length of key in icmap.
Definition: icmap.h:48
void(* quorum_set_quorate_fn_t)(const unsigned int *view_list, size_t view_list_entries, int quorate, struct memb_ring_id *)
Definition: exec/quorum.h:42
#define VOTEQUORUM_QDEVICE_OPERATION_REGISTER
cs_error_t icmap_get_uint8(const char *key_name, uint8_t *u8)
Definition: icmap.c:826
void(* error_memory_failure)(void) __attribute__((noreturn))
Definition: coroapi.h:422
#define VOTEQUORUM_INFO_ALLOW_DOWNSCALE
#define LOGSYS_LEVEL_WARNING
Definition: logsys.h:73
#define ICMAP_TRACK_MODIFY
Definition: icmap.h:78
#define VOTEQUORUM_INFO_QDEVICE_ALIVE
cs_error_t icmap_set_uint32(const char *key_name, uint32_t value)
Definition: icmap.c:595
void * user_data
Definition: sam.c:127
unsigned int(* totem_nodeid_get)(void)
Definition: coroapi.h:275
#define CS_TRACK_CHANGES_ONLY
Definition: corotypes.h:89
#define ICMAP_TRACK_ADD
Definition: icmap.h:76
The req_lib_votequorum_getinfo struct.
char name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
#define LOGSYS_LEVEL_ERROR
Definition: logsys.h:72
#define COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
Definition: coroapi.h:157
The req_lib_votequorum_qdevice_update struct.
cs_error_t
The cs_error_t enum.
Definition: corotypes.h:94
unsigned char track_flags
#define LOGSYS_LEVEL_DEBUG
Definition: logsys.h:76
LOGSYS_DECLARE_SUBSYS("VOTEQ")
The req_lib_votequorum_setvotes struct.
The corosync_api_v1 struct.
Definition: coroapi.h:225
cs_error_t icmap_get_uint32(const char *key_name, uint32_t *u32)
Definition: icmap.c:850
struct qb_list_head list
uint8_t param
The req_lib_votequorum_setexpected struct.
struct totem_message_header header
Definition: totemsrp.c:260
uint32_t quorate
Definition: sam.c:134
#define swab32(x)
The swab32 macro.
Definition: swab.h:51
#define VOTEQUORUM_INFO_AUTO_TIE_BREAKER
struct corosync_service_engine * votequorum_get_service_engine_ver0(void)
The res_lib_votequorum_expectedvotes_notification struct.
#define ENTER
Definition: logsys.h:324
The req_lib_votequorum_qdevice_register struct.
char * corosync_service_link_and_init(struct corosync_api_v1 *corosync_api, struct default_service *service)
Link and initialize a service.
Definition: service.c:117
char newname[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
#define VOTEQUORUM_NODESTATE_LEAVING
#define PROCESSOR_COUNT_MAX
Definition: coroapi.h:96
#define MESSAGE_REQ_EXEC_VOTEQUORUM_QDEVICE_REG
The memb_ring_id struct.
Definition: coroapi.h:122
#define VOTEQUORUM_READCONFIG_RUNTIME
unsigned int nodeid
Definition: coroapi.h:123
#define MESSAGE_REQ_EXEC_VOTEQUORUM_NODEINFO
The req_lib_votequorum_trackstart struct.
const char * get_state_dir(void)
Definition: util.c:172
#define VOTEQUORUM_RECONFIG_PARAM_NODE_VOTES
#define VOTEQUORUM_QDEVICE_MAX_NAME_LEN
qb_loop_timer_handle corosync_timer_handle_t
corosync_timer_handle_t
Definition: coroapi.h:74
The req_lib_votequorum_qdevice_poll struct.
cs_error_t icmap_get_string(const char *key_name, char **str)
Shortcut for icmap_get for string type.
Definition: icmap.c:880
#define LOGSYS_LEVEL_CRIT
Definition: logsys.h:71
char oldname[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
#define NODE_FLAGS_LEAVING
char newname[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
#define COROSYNC_LIB_FLOW_CONTROL_REQUIRED
Definition: coroapi.h:156
#define LOGSYS_LEVEL_NOTICE
Definition: logsys.h:74
unsigned long long seq
Definition: coroapi.h:124
cs_error_t icmap_set_uint8(const char *key_name, uint8_t value)
Definition: icmap.c:571
void(* lib_handler_fn)(void *conn, const void *msg)
Definition: coroapi.h:468
The res_lib_votequorum_getinfo struct.
#define VOTEQUORUM_NODESTATE_DEAD
cs_error_t icmap_set_ro_access(const char *key_name, int prefix, int ro_access)
Set read-only access for given key (key_name) or prefix, If prefix is set.
Definition: icmap.c:1217
#define VOTEQUORUM_INFO_QDEVICE_CAST_VOTE
int(* ipc_dispatch_send)(void *conn, const void *msg, size_t mlen)
Definition: coroapi.h:263
struct qb_list_head list
#define VOTEQUORUM_QDEVICE_DEFAULT_TIMEOUT
const char * name
Definition: service.h:43
icmap_iter_t icmap_iter_init(const char *prefix)
Initialize iterator with given prefix.
Definition: icmap.c:1081
struct memb_ring_id ring_id
Definition: totemsrp.c:264
uint64_t tracking_context
#define VOTEQUORUM_RECONFIG_PARAM_EXPECTED_VOTES
#define DEFAULT_LMS_WIN
#define LEAVE
Definition: logsys.h:325
#define NODE_FLAGS_QDEVICE_ALIVE
qb_map_iter_t * icmap_iter_t
Itterator type.
Definition: icmap.h:123
Structure passed as new_value and old_value in change callback.
Definition: icmap.h:91
#define NODE_FLAGS_QDEVICE_REGISTERED
cs_error_t icmap_track_add(const char *key_name, int32_t track_type, icmap_notify_fn_t notify_fn, void *user_data, icmap_track_t *icmap_track)
Add tracking function for given key_name.
Definition: icmap.c:1151
#define NODE_FLAGS_FIRST
struct qb_ipc_request_header header __attribute__((aligned(8)))
#define ICMAP_TRACK_PREFIX
Whole prefix is tracked, instead of key only (so "totem." tracking means that "totem.nodeid", "totem.version", ...
Definition: icmap.h:85