corosync  3.0.3
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  */
102 
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;
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 " CS_PRI_NODE_ID " 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;
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 
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: " CS_PRI_NODE_ID " us: " CS_PRI_NODE_ID, 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: " CS_PRI_NODE_ID " us: " CS_PRI_NODE_ID, 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 " CS_PRI_NODE_ID " 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 " CS_PRI_NODE_ID " 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 " CS_PRI_NODE_ID " 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 {
1647  struct iovec iov[1];
1648  int ret;
1649 
1650  ENTER();
1651 
1658 
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 {
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 
1691  decode_flags(node->flags);
1692  }
1693 
1695  req_exec_quorum_nodeinfo.header.size = sizeof(req_exec_quorum_nodeinfo);
1696 
1697  iov[0].iov_base = (void *)&req_exec_quorum_nodeinfo;
1698  iov[0].iov_len = sizeof(req_exec_quorum_nodeinfo);
1699 
1700  ret = corosync_api->totem_mcast (iov, 1, TOTEM_AGREED);
1701 
1702  LEAVE();
1703  return ret;
1704 }
1705 
1706 static int votequorum_exec_send_qdevice_reconfigure(const char *oldname, const char *newname)
1707 {
1709  struct iovec iov[1];
1710  int ret;
1711 
1712  ENTER();
1713 
1718 
1719  iov[0].iov_base = (void *)&req_exec_quorum_qdevice_reconfigure;
1720  iov[0].iov_len = sizeof(req_exec_quorum_qdevice_reconfigure);
1721 
1722  ret = corosync_api->totem_mcast (iov, 1, TOTEM_AGREED);
1723 
1724  LEAVE();
1725  return ret;
1726 }
1727 
1728 static int votequorum_exec_send_qdevice_reg(uint32_t operation, const char *qdevice_name_req)
1729 {
1731  struct iovec iov[1];
1732  int ret;
1733 
1734  ENTER();
1735 
1739  strcpy(req_exec_quorum_qdevice_reg.qdevice_name, qdevice_name_req);
1740 
1741  iov[0].iov_base = (void *)&req_exec_quorum_qdevice_reg;
1742  iov[0].iov_len = sizeof(req_exec_quorum_qdevice_reg);
1743 
1744  ret = corosync_api->totem_mcast (iov, 1, TOTEM_AGREED);
1745 
1746  LEAVE();
1747  return ret;
1748 }
1749 
1750 static int votequorum_exec_send_quorum_notification(void *conn, uint64_t context)
1751 {
1752  struct res_lib_votequorum_quorum_notification *res_lib_votequorum_notification;
1753  struct qb_list_head *tmp;
1754  struct cluster_node *node;
1755  int i = 0;
1756  int cluster_members = 0;
1757  int size;
1758  char buf[sizeof(struct res_lib_votequorum_quorum_notification) + sizeof(struct votequorum_node) * (PROCESSOR_COUNT_MAX + 2)];
1759 
1760  ENTER();
1761 
1762  log_printf(LOGSYS_LEVEL_DEBUG, "Sending quorum callback, quorate = %d", cluster_is_quorate);
1763 
1764  qb_list_for_each(tmp, &cluster_members_list) {
1765  node = qb_list_entry(tmp, struct cluster_node, list);
1766  cluster_members++;
1767  }
1769  cluster_members++;
1770  }
1771 
1772  size = sizeof(struct res_lib_votequorum_quorum_notification) + sizeof(struct votequorum_node) * cluster_members;
1773 
1774  res_lib_votequorum_notification = (struct res_lib_votequorum_quorum_notification *)&buf;
1775  res_lib_votequorum_notification->quorate = cluster_is_quorate;
1776  res_lib_votequorum_notification->context = context;
1777  res_lib_votequorum_notification->node_list_entries = cluster_members;
1778  res_lib_votequorum_notification->header.id = MESSAGE_RES_VOTEQUORUM_QUORUM_NOTIFICATION;
1779  res_lib_votequorum_notification->header.size = size;
1780  res_lib_votequorum_notification->header.error = CS_OK;
1781 
1782  /* Send all known nodes and their states */
1783  qb_list_for_each(tmp, &cluster_members_list) {
1784  node = qb_list_entry(tmp, struct cluster_node, list);
1785  res_lib_votequorum_notification->node_list[i].nodeid = node->node_id;
1786  res_lib_votequorum_notification->node_list[i++].state = node->state;
1787  }
1789  res_lib_votequorum_notification->node_list[i].nodeid = VOTEQUORUM_QDEVICE_NODEID;
1790  res_lib_votequorum_notification->node_list[i++].state = qdevice->state;
1791  }
1792 
1793  /* Send it to all interested parties */
1794  if (conn) {
1795  int ret = corosync_api->ipc_dispatch_send(conn, &buf, size);
1796  LEAVE();
1797  return ret;
1798  } else {
1799  struct quorum_pd *qpd;
1800 
1801  qb_list_for_each(tmp, &trackers_list) {
1802  qpd = qb_list_entry(tmp, struct quorum_pd, list);
1803  res_lib_votequorum_notification->context = qpd->tracking_context;
1804  corosync_api->ipc_dispatch_send(qpd->conn, &buf, size);
1805  }
1806  }
1807 
1808  LEAVE();
1809 
1810  return 0;
1811 }
1812 
1813 static int votequorum_exec_send_nodelist_notification(void *conn, uint64_t context)
1814 {
1815  struct res_lib_votequorum_nodelist_notification *res_lib_votequorum_notification;
1816  int i = 0;
1817  int size;
1818  struct qb_list_head *tmp;
1819  char buf[sizeof(struct res_lib_votequorum_nodelist_notification) + sizeof(uint32_t) * quorum_members_entries];
1820 
1821  ENTER();
1822 
1823  log_printf(LOGSYS_LEVEL_DEBUG, "Sending nodelist callback. ring_id = " CS_PRI_RING_ID, quorum_ringid.nodeid, quorum_ringid.seq);
1824 
1825  size = sizeof(struct res_lib_votequorum_nodelist_notification) + sizeof(uint32_t) * quorum_members_entries;
1826 
1827  res_lib_votequorum_notification = (struct res_lib_votequorum_nodelist_notification *)&buf;
1828  res_lib_votequorum_notification->node_list_entries = quorum_members_entries;
1829  res_lib_votequorum_notification->ring_id.nodeid = quorum_ringid.nodeid;
1830  res_lib_votequorum_notification->ring_id.seq = quorum_ringid.seq;
1831  res_lib_votequorum_notification->context = context;
1832 
1833  for (i=0; i<quorum_members_entries; i++) {
1834  res_lib_votequorum_notification->node_list[i] = quorum_members[i];
1835  }
1836 
1837  res_lib_votequorum_notification->header.id = MESSAGE_RES_VOTEQUORUM_NODELIST_NOTIFICATION;
1838  res_lib_votequorum_notification->header.size = size;
1839  res_lib_votequorum_notification->header.error = CS_OK;
1840 
1841  /* Send it to all interested parties */
1842  if (conn) {
1843  int ret = corosync_api->ipc_dispatch_send(conn, &buf, size);
1844  LEAVE();
1845  return ret;
1846  } else {
1847  struct quorum_pd *qpd;
1848 
1849  qb_list_for_each(tmp, &trackers_list) {
1850  qpd = qb_list_entry(tmp, struct quorum_pd, list);
1851  res_lib_votequorum_notification->context = qpd->tracking_context;
1852  corosync_api->ipc_dispatch_send(qpd->conn, &buf, size);
1853  }
1854  }
1855 
1856  LEAVE();
1857 
1858  return 0;
1859 }
1860 
1861 static void votequorum_exec_send_expectedvotes_notification(void)
1862 {
1864  struct quorum_pd *qpd;
1865  struct qb_list_head *tmp;
1866 
1867  ENTER();
1868 
1869  log_printf(LOGSYS_LEVEL_DEBUG, "Sending expected votes callback");
1870 
1875 
1876  qb_list_for_each(tmp, &trackers_list) {
1877  qpd = qb_list_entry(tmp, struct quorum_pd, list);
1881  }
1882 
1883  LEAVE();
1884 }
1885 
1886 static void exec_votequorum_qdevice_reconfigure_endian_convert (void *message)
1887 {
1888  ENTER();
1889 
1890  LEAVE();
1891 }
1892 
1893 static void message_handler_req_exec_votequorum_qdevice_reconfigure (
1894  const void *message,
1895  unsigned int nodeid)
1896 {
1898 
1899  ENTER();
1900 
1901  log_printf(LOGSYS_LEVEL_DEBUG, "Received qdevice name change req from node " CS_PRI_NODE_ID " [from: %s to: %s]",
1902  nodeid,
1905 
1906  if (!strcmp(req_exec_quorum_qdevice_reconfigure->oldname, qdevice_name)) {
1907  log_printf(LOGSYS_LEVEL_DEBUG, "Allowing qdevice rename");
1908  memset(qdevice_name, 0, VOTEQUORUM_QDEVICE_MAX_NAME_LEN);
1909  strcpy(qdevice_name, req_exec_quorum_qdevice_reconfigure->newname);
1910  /*
1911  * TODO: notify qdevices about name change?
1912  * this is not relevant for now and can wait later on since
1913  * qdevices are local only and libvotequorum is not final
1914  */
1915  }
1916 
1917  LEAVE();
1918 }
1919 
1920 static void exec_votequorum_qdevice_reg_endian_convert (void *message)
1921 {
1923 
1924  ENTER();
1925 
1927 
1928  LEAVE();
1929 }
1930 
1931 static void message_handler_req_exec_votequorum_qdevice_reg (
1932  const void *message,
1933  unsigned int nodeid)
1934 {
1937  int wipe_qdevice_name = 1;
1938  struct cluster_node *node = NULL;
1939  struct qb_list_head *tmp;
1940  cs_error_t error = CS_OK;
1941 
1942  ENTER();
1943 
1944  log_printf(LOGSYS_LEVEL_DEBUG, "Received qdevice op %u req from node " CS_PRI_NODE_ID " [%s]",
1947 
1949  {
1951  if (nodeid != us->node_id) {
1952  if (!strlen(qdevice_name)) {
1953  log_printf(LOGSYS_LEVEL_DEBUG, "Remote qdevice name recorded");
1954  strcpy(qdevice_name, req_exec_quorum_qdevice_reg->qdevice_name);
1955  }
1956  LEAVE();
1957  return;
1958  }
1959 
1960  /*
1961  * protect against the case where we broadcast qdevice registration
1962  * to new memebers, we receive the message back, but there is no registration
1963  * connection in progress
1964  */
1966  LEAVE();
1967  return;
1968  }
1969 
1970  /*
1971  * this should NEVER happen
1972  */
1973  if (!qdevice_reg_conn) {
1974  log_printf(LOGSYS_LEVEL_WARNING, "Unable to determine origin of the qdevice register call!");
1975  LEAVE();
1976  return;
1977  }
1978 
1979  /*
1980  * registering our own device in this case
1981  */
1982  if (!strlen(qdevice_name)) {
1983  strcpy(qdevice_name, req_exec_quorum_qdevice_reg->qdevice_name);
1984  }
1985 
1986  /*
1987  * check if it is our device or something else
1988  */
1990  qdevice_name, VOTEQUORUM_QDEVICE_MAX_NAME_LEN))) {
1992  votequorum_exec_send_nodeinfo(VOTEQUORUM_QDEVICE_NODEID);
1993  votequorum_exec_send_nodeinfo(us->node_id);
1994  } else {
1996  "A new qdevice with different name (new: %s old: %s) is trying to register!",
1997  req_exec_quorum_qdevice_reg->qdevice_name, qdevice_name);
1998  error = CS_ERR_EXIST;
1999  }
2000 
2003  res_lib_votequorum_status.header.error = error;
2004  corosync_api->ipc_response_send(qdevice_reg_conn, &res_lib_votequorum_status, sizeof(res_lib_votequorum_status));
2005  qdevice_reg_conn = NULL;
2006  break;
2008  qb_list_for_each(tmp, &cluster_members_list) {
2009  node = qb_list_entry(tmp, struct cluster_node, list);
2010  if ((node->state == NODESTATE_MEMBER) &&
2012  wipe_qdevice_name = 0;
2013  }
2014  }
2015 
2016  if (wipe_qdevice_name) {
2017  memset(qdevice_name, 0, VOTEQUORUM_QDEVICE_MAX_NAME_LEN);
2018  }
2019 
2020  break;
2021  }
2022  LEAVE();
2023 }
2024 
2025 static void exec_votequorum_nodeinfo_endian_convert (void *message)
2026 {
2027  struct req_exec_quorum_nodeinfo *nodeinfo = message;
2028 
2029  ENTER();
2030 
2031  nodeinfo->nodeid = swab32(nodeinfo->nodeid);
2032  nodeinfo->votes = swab32(nodeinfo->votes);
2033  nodeinfo->expected_votes = swab32(nodeinfo->expected_votes);
2034  nodeinfo->flags = swab32(nodeinfo->flags);
2035 
2036  LEAVE();
2037 }
2038 
2039 static void message_handler_req_exec_votequorum_nodeinfo (
2040  const void *message,
2041  unsigned int sender_nodeid)
2042 {
2043  const struct req_exec_quorum_nodeinfo *req_exec_quorum_nodeinfo = message;
2044  struct cluster_node *node = NULL;
2045  int old_votes;
2046  int old_expected;
2047  uint32_t old_flags;
2048  nodestate_t old_state;
2049  int new_node = 0;
2050  int allow_downgrade = 0;
2051  int by_node = 0;
2052  unsigned int nodeid = req_exec_quorum_nodeinfo->nodeid;
2053 
2054  ENTER();
2055 
2056  log_printf(LOGSYS_LEVEL_DEBUG, "got nodeinfo message from cluster node " CS_PRI_NODE_ID, sender_nodeid);
2057  log_printf(LOGSYS_LEVEL_DEBUG, "nodeinfo message[" CS_PRI_NODE_ID "]: votes: %d, expected: %d flags: %d",
2058  nodeid,
2062 
2064  decode_flags(req_exec_quorum_nodeinfo->flags);
2065  }
2066 
2067  node = find_node_by_nodeid(nodeid);
2068  if (!node) {
2069  node = allocate_node(nodeid);
2070  new_node = 1;
2071  }
2072  if (!node) {
2073  corosync_api->error_memory_failure();
2074  LEAVE();
2075  return;
2076  }
2077 
2078  if (new_node) {
2079  old_votes = 0;
2080  old_expected = 0;
2081  old_state = NODESTATE_DEAD;
2082  old_flags = 0;
2083  } else {
2084  old_votes = node->votes;
2085  old_expected = node->expected_votes;
2086  old_state = node->state;
2087  old_flags = node->flags;
2088  }
2089 
2091  struct cluster_node *sender_node = find_node_by_nodeid(sender_nodeid);
2092 
2093  assert(sender_node != NULL);
2094 
2095  if ((!cluster_is_quorate) &&
2096  (sender_node->flags & NODE_FLAGS_QUORATE)) {
2098  } else {
2099  node->votes = max(node->votes, req_exec_quorum_nodeinfo->votes);
2100  }
2101  goto recalculate;
2102  }
2103 
2104  /* Update node state */
2107  node->state = NODESTATE_MEMBER;
2108 
2109  if (node->flags & NODE_FLAGS_LEAVING) {
2110  node->state = NODESTATE_LEAVING;
2111  allow_downgrade = 1;
2112  by_node = 1;
2113  }
2114 
2115  if ((!cluster_is_quorate) &&
2116  (node->flags & NODE_FLAGS_QUORATE)) {
2117  allow_downgrade = 1;
2119  }
2120 
2121  if (node->flags & NODE_FLAGS_QUORATE || (ev_tracking)) {
2123  } else {
2124  node->expected_votes = us->expected_votes;
2125  }
2126 
2127  if ((last_man_standing) && (node->votes > 1)) {
2128  log_printf(LOGSYS_LEVEL_WARNING, "Last Man Standing feature is supported only when all"
2129  "cluster nodes votes are set to 1. Disabling LMS.");
2130  last_man_standing = 0;
2131  if (last_man_standing_timer_set) {
2132  corosync_api->timer_delete(last_man_standing_timer);
2133  last_man_standing_timer_set = 0;
2134  }
2135  }
2136 
2137 recalculate:
2138  if ((new_node) ||
2139  (nodeid == us->node_id) ||
2140  (node->flags & NODE_FLAGS_FIRST) ||
2141  (old_votes != node->votes) ||
2142  (old_expected != node->expected_votes) ||
2143  (old_flags != node->flags) ||
2144  (old_state != node->state)) {
2145  recalculate_quorum(allow_downgrade, by_node);
2146  }
2147 
2148  if ((wait_for_all) &&
2149  (!(node->flags & NODE_FLAGS_WFASTATUS)) &&
2150  (node->flags & NODE_FLAGS_QUORATE)) {
2151  update_wait_for_all_status(0);
2152  }
2153 
2154  LEAVE();
2155 }
2156 
2157 static void exec_votequorum_reconfigure_endian_convert (void *message)
2158 {
2159  struct req_exec_quorum_reconfigure *reconfigure = message;
2160 
2161  ENTER();
2162 
2163  reconfigure->nodeid = swab32(reconfigure->nodeid);
2164  reconfigure->value = swab32(reconfigure->value);
2165 
2166  LEAVE();
2167 }
2168 
2169 static void message_handler_req_exec_votequorum_reconfigure (
2170  const void *message,
2171  unsigned int nodeid)
2172 {
2174  struct cluster_node *node;
2175 
2176  ENTER();
2177 
2178  log_printf(LOGSYS_LEVEL_DEBUG, "got reconfigure message from cluster node " CS_PRI_NODE_ID " for " CS_PRI_NODE_ID,
2180 
2182  {
2184  update_node_expected_votes(req_exec_quorum_reconfigure->value);
2185  votequorum_exec_send_expectedvotes_notification();
2186  update_ev_barrier(req_exec_quorum_reconfigure->value);
2187  if (ev_tracking) {
2188  us->expected_votes = max(us->expected_votes, ev_tracking_barrier);
2189  }
2190  recalculate_quorum(1, 0); /* Allow decrease */
2191  break;
2192 
2194  node = find_node_by_nodeid(req_exec_quorum_reconfigure->nodeid);
2195  if (!node) {
2196  LEAVE();
2197  return;
2198  }
2200  recalculate_quorum(1, 0); /* Allow decrease */
2201  break;
2202 
2204  update_wait_for_all_status(0);
2205  log_printf(LOGSYS_LEVEL_INFO, "wait_for_all_status reset by user on node " CS_PRI_NODE_ID ".",
2207  recalculate_quorum(0, 0);
2208 
2209  break;
2210 
2211  }
2212 
2213  LEAVE();
2214 }
2215 
2216 static int votequorum_exec_exit_fn (void)
2217 {
2218  int ret = 0;
2219 
2220  ENTER();
2221 
2222  /*
2223  * tell the other nodes we are leaving
2224  */
2225 
2226  if (allow_downscale) {
2227  us->flags |= NODE_FLAGS_LEAVING;
2228  ret = votequorum_exec_send_nodeinfo(us->node_id);
2229  }
2230 
2231  if ((ev_tracking) && (ev_tracking_fd != -1)) {
2232  close(ev_tracking_fd);
2233  }
2234 
2235 
2236  LEAVE();
2237  return ret;
2238 }
2239 
2240 static void votequorum_set_icmap_ro_keys(void)
2241 {
2242  icmap_set_ro_access("quorum.allow_downscale", CS_FALSE, CS_TRUE);
2243  icmap_set_ro_access("quorum.wait_for_all", CS_FALSE, CS_TRUE);
2244  icmap_set_ro_access("quorum.last_man_standing", CS_FALSE, CS_TRUE);
2245  icmap_set_ro_access("quorum.last_man_standing_window", CS_FALSE, CS_TRUE);
2246  icmap_set_ro_access("quorum.expected_votes_tracking", CS_FALSE, CS_TRUE);
2247  icmap_set_ro_access("quorum.auto_tie_breaker", CS_FALSE, CS_TRUE);
2248  icmap_set_ro_access("quorum.auto_tie_breaker_node", CS_FALSE, CS_TRUE);
2249 }
2250 
2251 static char *votequorum_exec_init_fn (struct corosync_api_v1 *api)
2252 {
2253  char *error = NULL;
2254 
2255  ENTER();
2256 
2257  /*
2258  * make sure we start clean
2259  */
2260  qb_list_init(&cluster_members_list);
2261  qb_list_init(&trackers_list);
2262  qdevice = NULL;
2263  us = NULL;
2264  memset(cluster_nodes, 0, sizeof(cluster_nodes));
2265 
2266  /*
2267  * Allocate a cluster_node for qdevice
2268  */
2269  qdevice = allocate_node(VOTEQUORUM_QDEVICE_NODEID);
2270  if (!qdevice) {
2271  LEAVE();
2272  return ((char *)"Could not allocate node.");
2273  }
2274  qdevice->votes = 0;
2275  memset(qdevice_name, 0, VOTEQUORUM_QDEVICE_MAX_NAME_LEN);
2276 
2277  /*
2278  * Allocate a cluster_node for us
2279  */
2280  us = allocate_node(corosync_api->totem_nodeid_get());
2281  if (!us) {
2282  LEAVE();
2283  return ((char *)"Could not allocate node.");
2284  }
2285 
2286  icmap_set_uint32("runtime.votequorum.this_node_id", us->node_id);
2287 
2288  us->state = NODESTATE_MEMBER;
2289  us->votes = 1;
2290  us->flags |= NODE_FLAGS_FIRST;
2291 
2292  error = votequorum_readconfig(VOTEQUORUM_READCONFIG_STARTUP);
2293  if (error) {
2294  return error;
2295  }
2296  recalculate_quorum(0, 0);
2297 
2298  /*
2299  * Set RO keys in icmap
2300  */
2301  votequorum_set_icmap_ro_keys();
2302 
2303  /*
2304  * Listen for changes
2305  */
2306  votequorum_exec_add_config_notification();
2307 
2308  /*
2309  * Start us off with one node
2310  */
2311  votequorum_exec_send_nodeinfo(us->node_id);
2312 
2313  LEAVE();
2314 
2315  return (NULL);
2316 }
2317 
2318 /*
2319  * votequorum service core
2320  */
2321 
2322 static void votequorum_last_man_standing_timer_fn(void *arg)
2323 {
2324  ENTER();
2325 
2326  last_man_standing_timer_set = 0;
2327  if (cluster_is_quorate) {
2328  recalculate_quorum(1,1);
2329  }
2330 
2331  LEAVE();
2332 }
2333 
2334 static void votequorum_sync_init (
2335  const unsigned int *trans_list, size_t trans_list_entries,
2336  const unsigned int *member_list, size_t member_list_entries,
2337  const struct memb_ring_id *ring_id)
2338 {
2339  int i, j;
2340  int found;
2341  int left_nodes;
2342  struct cluster_node *node;
2343 
2344  ENTER();
2345 
2346  sync_in_progress = 1;
2347  sync_nodeinfo_sent = 0;
2348  sync_wait_for_poll_or_timeout = 0;
2349 
2350  if (member_list_entries > 1) {
2351  us->flags &= ~NODE_FLAGS_FIRST;
2352  }
2353 
2354  /*
2355  * we don't need to track which nodes have left directly,
2356  * since that info is in the node db, but we need to know
2357  * if somebody has left for last_man_standing
2358  */
2359  left_nodes = 0;
2360  for (i = 0; i < quorum_members_entries; i++) {
2361  found = 0;
2362  for (j = 0; j < member_list_entries; j++) {
2363  if (quorum_members[i] == member_list[j]) {
2364  found = 1;
2365  break;
2366  }
2367  }
2368  if (found == 0) {
2369  left_nodes = 1;
2370  node = find_node_by_nodeid(quorum_members[i]);
2371  if (node) {
2372  node->state = NODESTATE_DEAD;
2373  }
2374  }
2375  }
2376 
2377  if (last_man_standing) {
2378  if (((member_list_entries >= quorum) && (left_nodes)) ||
2379  ((member_list_entries <= quorum) && (auto_tie_breaker != ATB_NONE) && (check_low_node_id_partition() == 1))) {
2380  if (last_man_standing_timer_set) {
2381  corosync_api->timer_delete(last_man_standing_timer);
2382  last_man_standing_timer_set = 0;
2383  }
2384  corosync_api->timer_add_duration((unsigned long long)last_man_standing_window*1000000,
2385  NULL, votequorum_last_man_standing_timer_fn,
2386  &last_man_standing_timer);
2387  last_man_standing_timer_set = 1;
2388  }
2389  }
2390 
2391  memcpy(previous_quorum_members, quorum_members, sizeof(unsigned int) * quorum_members_entries);
2392  previous_quorum_members_entries = quorum_members_entries;
2393 
2394  memcpy(quorum_members, member_list, sizeof(unsigned int) * member_list_entries);
2395  quorum_members_entries = member_list_entries;
2396  memcpy(&quorum_ringid, ring_id, sizeof(*ring_id));
2397 
2399  /*
2400  * Reset poll timer. Sync waiting is interrupted on valid qdevice poll or after timeout
2401  */
2402  if (qdevice_timer_set) {
2403  corosync_api->timer_delete(qdevice_timer);
2404  }
2405  corosync_api->timer_add_duration((unsigned long long)qdevice_sync_timeout*1000000, qdevice,
2406  qdevice_timer_fn, &qdevice_timer);
2407  qdevice_timer_set = 1;
2408  sync_wait_for_poll_or_timeout = 1;
2409 
2410  log_printf(LOGSYS_LEVEL_INFO, "waiting for quorum device %s poll (but maximum for %u ms)",
2411  qdevice_name, qdevice_sync_timeout);
2412  }
2413 
2414  LEAVE();
2415 }
2416 
2417 static int votequorum_sync_process (void)
2418 {
2419  if (!sync_nodeinfo_sent) {
2420  votequorum_exec_send_nodeinfo(us->node_id);
2421  votequorum_exec_send_nodeinfo(VOTEQUORUM_QDEVICE_NODEID);
2422  if (strlen(qdevice_name)) {
2423  votequorum_exec_send_qdevice_reg(VOTEQUORUM_QDEVICE_OPERATION_REGISTER,
2424  qdevice_name);
2425  }
2426  votequorum_exec_send_nodelist_notification(NULL, 0LL);
2427  sync_nodeinfo_sent = 1;
2428  }
2429 
2430  if (us->flags & NODE_FLAGS_QDEVICE_REGISTERED && sync_wait_for_poll_or_timeout) {
2431  /*
2432  * Waiting for qdevice to poll with new ringid or timeout
2433  */
2434 
2435  return (-1);
2436  }
2437 
2438  return 0;
2439 }
2440 
2441 static void votequorum_sync_activate (void)
2442 {
2443  recalculate_quorum(0, 0);
2444  quorum_callback(quorum_members, quorum_members_entries,
2445  cluster_is_quorate, &quorum_ringid);
2446  votequorum_exec_send_quorum_notification(NULL, 0L);
2447 
2448  sync_in_progress = 0;
2449 }
2450 
2451 static void votequorum_sync_abort (void)
2452 {
2453 
2454 }
2455 
2457  quorum_set_quorate_fn_t q_set_quorate_fn)
2458 {
2459  char *error;
2460 
2461  ENTER();
2462 
2463  if (q_set_quorate_fn == NULL) {
2464  return ((char *)"Quorate function not set");
2465  }
2466 
2467  corosync_api = api;
2468  quorum_callback = q_set_quorate_fn;
2469 
2470  error = corosync_service_link_and_init(corosync_api,
2471  &votequorum_service[0]);
2472  if (error) {
2473  return (error);
2474  }
2475 
2476  LEAVE();
2477 
2478  return (NULL);
2479 }
2480 
2481 /*
2482  * Library Handler init/fini
2483  */
2484 
2485 static int quorum_lib_init_fn (void *conn)
2486 {
2487  struct quorum_pd *pd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
2488 
2489  ENTER();
2490 
2491  qb_list_init (&pd->list);
2492  pd->conn = conn;
2493 
2494  LEAVE();
2495  return (0);
2496 }
2497 
2498 static int quorum_lib_exit_fn (void *conn)
2499 {
2500  struct quorum_pd *quorum_pd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
2501 
2502  ENTER();
2503 
2504  if (quorum_pd->tracking_enabled) {
2505  qb_list_del (&quorum_pd->list);
2506  qb_list_init (&quorum_pd->list);
2507  }
2508 
2509  LEAVE();
2510 
2511  return (0);
2512 }
2513 
2514 /*
2515  * library internal functions
2516  */
2517 
2518 static void qdevice_timer_fn(void *arg)
2519 {
2520  ENTER();
2521 
2522  if ((!(us->flags & NODE_FLAGS_QDEVICE_ALIVE)) ||
2523  (!qdevice_timer_set)) {
2524  LEAVE();
2525  return;
2526  }
2527 
2530  log_printf(LOGSYS_LEVEL_INFO, "lost contact with quorum device %s", qdevice_name);
2531  votequorum_exec_send_nodeinfo(us->node_id);
2532 
2533  qdevice_timer_set = 0;
2534  sync_wait_for_poll_or_timeout = 0;
2535 
2536  LEAVE();
2537 }
2538 
2539 /*
2540  * Library Handler Functions
2541  */
2542 
2543 static void message_handler_req_lib_votequorum_getinfo (void *conn, const void *message)
2544 {
2547  struct cluster_node *node;
2548  unsigned int highest_expected = 0;
2549  unsigned int total_votes = 0;
2550  cs_error_t error = CS_OK;
2552 
2553  ENTER();
2554 
2555  log_printf(LOGSYS_LEVEL_DEBUG, "got getinfo request on %p for node " CS_PRI_NODE_ID, conn, req_lib_votequorum_getinfo->nodeid);
2556 
2558  nodeid = us->node_id;
2559  }
2560 
2561  node = find_node_by_nodeid(nodeid);
2562  if (node) {
2563  struct cluster_node *iternode;
2564  struct qb_list_head *nodelist;
2565 
2566  qb_list_for_each(nodelist, &cluster_members_list) {
2567  iternode = qb_list_entry(nodelist, struct cluster_node, list);
2568 
2569  if (iternode->state == NODESTATE_MEMBER) {
2570  highest_expected =
2571  max(highest_expected, iternode->expected_votes);
2572  total_votes += iternode->votes;
2573  }
2574  }
2575 
2576  if (node->flags & NODE_FLAGS_QDEVICE_CAST_VOTE) {
2577  total_votes += qdevice->votes;
2578  }
2579 
2580  switch(node->state) {
2581  case NODESTATE_MEMBER:
2583  break;
2584  case NODESTATE_DEAD:
2586  break;
2587  case NODESTATE_LEAVING:
2589  break;
2590  default:
2592  break;
2593  }
2597  res_lib_votequorum_getinfo.highest_expected = highest_expected;
2598 
2603 
2604  if (two_node) {
2606  }
2607  if (cluster_is_quorate) {
2609  }
2610  if (wait_for_all) {
2612  }
2613  if (last_man_standing) {
2615  }
2616  if (auto_tie_breaker != ATB_NONE) {
2618  }
2619  if (allow_downscale) {
2621  }
2622 
2624  strcpy(res_lib_votequorum_getinfo.qdevice_name, qdevice_name);
2626 
2627  if (node->flags & NODE_FLAGS_QDEVICE_REGISTERED) {
2629  }
2630  if (node->flags & NODE_FLAGS_QDEVICE_ALIVE) {
2632  }
2633  if (node->flags & NODE_FLAGS_QDEVICE_CAST_VOTE) {
2635  }
2636  if (node->flags & NODE_FLAGS_QDEVICE_MASTER_WINS) {
2638  }
2639  } else {
2640  error = CS_ERR_NOT_EXIST;
2641  }
2642 
2645  res_lib_votequorum_getinfo.header.error = error;
2647  log_printf(LOGSYS_LEVEL_DEBUG, "getinfo response error: %d", error);
2648 
2649  LEAVE();
2650 }
2651 
2652 static void message_handler_req_lib_votequorum_setexpected (void *conn, const void *message)
2653 {
2656  cs_error_t error = CS_OK;
2657  unsigned int newquorum;
2658  unsigned int total_votes;
2659  uint8_t allow_downscale_status = 0;
2660 
2661  ENTER();
2662 
2663  allow_downscale_status = allow_downscale;
2664  allow_downscale = 0;
2665 
2666  /*
2667  * Validate new expected votes
2668  */
2669  newquorum = calculate_quorum(1, req_lib_votequorum_setexpected->expected_votes, &total_votes);
2670  allow_downscale = allow_downscale_status;
2671  if (newquorum < total_votes / 2 ||
2672  newquorum > total_votes) {
2673  error = CS_ERR_INVALID_PARAM;
2674  goto error_exit;
2675  }
2676  update_node_expected_votes(req_lib_votequorum_setexpected->expected_votes);
2677 
2678  if (votequorum_exec_send_reconfigure(VOTEQUORUM_RECONFIG_PARAM_EXPECTED_VOTES, us->node_id,
2680  error = CS_ERR_NO_RESOURCES;
2681  }
2682 
2683 error_exit:
2686  res_lib_votequorum_status.header.error = error;
2688 
2689  LEAVE();
2690 }
2691 
2692 static void message_handler_req_lib_votequorum_setvotes (void *conn, const void *message)
2693 {
2696  struct cluster_node *node;
2697  unsigned int newquorum;
2698  unsigned int total_votes;
2699  unsigned int saved_votes;
2700  cs_error_t error = CS_OK;
2701  unsigned int nodeid;
2702 
2703  ENTER();
2704 
2706  node = find_node_by_nodeid(nodeid);
2707  if (!node) {
2708  error = CS_ERR_NAME_NOT_FOUND;
2709  goto error_exit;
2710  }
2711 
2712  /*
2713  * Check votes is valid
2714  */
2715  saved_votes = node->votes;
2717 
2718  newquorum = calculate_quorum(1, 0, &total_votes);
2719 
2720  if (newquorum < total_votes / 2 ||
2721  newquorum > total_votes) {
2722  node->votes = saved_votes;
2723  error = CS_ERR_INVALID_PARAM;
2724  goto error_exit;
2725  }
2726 
2727  if (votequorum_exec_send_reconfigure(VOTEQUORUM_RECONFIG_PARAM_NODE_VOTES, nodeid,
2729  error = CS_ERR_NO_RESOURCES;
2730  }
2731 
2732 error_exit:
2735  res_lib_votequorum_status.header.error = error;
2737 
2738  LEAVE();
2739 }
2740 
2741 static void message_handler_req_lib_votequorum_trackstart (void *conn,
2742  const void *message)
2743 {
2746  struct quorum_pd *quorum_pd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
2747  cs_error_t error = CS_OK;
2748 
2749  ENTER();
2750 
2751  /*
2752  * If an immediate listing of the current cluster membership
2753  * is requested, generate membership list
2754  */
2757  log_printf(LOGSYS_LEVEL_DEBUG, "sending initial status to %p", conn);
2758  votequorum_exec_send_nodelist_notification(conn, req_lib_votequorum_trackstart->context);
2759  votequorum_exec_send_quorum_notification(conn, req_lib_votequorum_trackstart->context);
2760  }
2761 
2762  if (quorum_pd->tracking_enabled) {
2763  error = CS_ERR_EXIST;
2764  goto response_send;
2765  }
2766 
2767  /*
2768  * Record requests for tracking
2769  */
2772 
2776 
2777  qb_list_add (&quorum_pd->list, &trackers_list);
2778  }
2779 
2780 response_send:
2783  res_lib_votequorum_status.header.error = error;
2785 
2786  LEAVE();
2787 }
2788 
2789 static void message_handler_req_lib_votequorum_trackstop (void *conn,
2790  const void *message)
2791 {
2793  struct quorum_pd *quorum_pd = (struct quorum_pd *)corosync_api->ipc_private_data_get (conn);
2794  int error = CS_OK;
2795 
2796  ENTER();
2797 
2798  if (quorum_pd->tracking_enabled) {
2799  error = CS_OK;
2801  qb_list_del (&quorum_pd->list);
2802  qb_list_init (&quorum_pd->list);
2803  } else {
2804  error = CS_ERR_NOT_EXIST;
2805  }
2806 
2809  res_lib_votequorum_status.header.error = error;
2811 
2812  LEAVE();
2813 }
2814 
2815 static void message_handler_req_lib_votequorum_qdevice_register (void *conn,
2816  const void *message)
2817 {
2820  cs_error_t error = CS_OK;
2821 
2822  ENTER();
2823 
2824  if (!qdevice_can_operate) {
2825  log_printf(LOGSYS_LEVEL_INFO, "Registration of quorum device is disabled by incorrect corosync.conf. See logs for more information");
2826  error = CS_ERR_ACCESS;
2827  goto out;
2828  }
2829 
2832  qdevice_name, VOTEQUORUM_QDEVICE_MAX_NAME_LEN))) {
2833  goto out;
2834  } else {
2836  "A new qdevice with different name (new: %s old: %s) is trying to re-register!",
2837  req_lib_votequorum_qdevice_register->name, qdevice_name);
2838  error = CS_ERR_EXIST;
2839  goto out;
2840  }
2841  } else {
2842  if (qdevice_reg_conn != NULL) {
2844  "Registration request already in progress");
2845  error = CS_ERR_TRY_AGAIN;
2846  goto out;
2847  }
2848  qdevice_reg_conn = conn;
2849  if (votequorum_exec_send_qdevice_reg(VOTEQUORUM_QDEVICE_OPERATION_REGISTER,
2852  "Unable to send qdevice registration request to cluster");
2853  error = CS_ERR_TRY_AGAIN;
2854  qdevice_reg_conn = NULL;
2855  } else {
2856  LEAVE();
2857  return;
2858  }
2859  }
2860 
2861 out:
2862 
2865  res_lib_votequorum_status.header.error = error;
2867 
2868  LEAVE();
2869 }
2870 
2871 static void message_handler_req_lib_votequorum_qdevice_unregister (void *conn,
2872  const void *message)
2873 {
2876  cs_error_t error = CS_OK;
2877 
2878  ENTER();
2879 
2882  error = CS_ERR_INVALID_PARAM;
2883  goto out;
2884  }
2885  if (qdevice_timer_set) {
2886  corosync_api->timer_delete(qdevice_timer);
2887  qdevice_timer_set = 0;
2888  sync_wait_for_poll_or_timeout = 0;
2889  }
2894  votequorum_exec_send_nodeinfo(us->node_id);
2895  votequorum_exec_send_qdevice_reg(VOTEQUORUM_QDEVICE_OPERATION_UNREGISTER,
2897  } else {
2898  error = CS_ERR_NOT_EXIST;
2899  }
2900 
2901 out:
2904  res_lib_votequorum_status.header.error = error;
2906 
2907  LEAVE();
2908 }
2909 
2910 static void message_handler_req_lib_votequorum_qdevice_update (void *conn,
2911  const void *message)
2912 {
2915  cs_error_t error = CS_OK;
2916 
2917  ENTER();
2918 
2921  error = CS_ERR_INVALID_PARAM;
2922  goto out;
2923  }
2924  votequorum_exec_send_qdevice_reconfigure(req_lib_votequorum_qdevice_update->oldname,
2926  } else {
2927  error = CS_ERR_NOT_EXIST;
2928  }
2929 
2930 out:
2933  res_lib_votequorum_status.header.error = error;
2935 
2936  LEAVE();
2937 }
2938 
2939 static void message_handler_req_lib_votequorum_qdevice_poll (void *conn,
2940  const void *message)
2941 {
2944  cs_error_t error = CS_OK;
2945  uint32_t oldflags;
2946 
2947  ENTER();
2948 
2949  if (!qdevice_can_operate) {
2950  error = CS_ERR_ACCESS;
2951  goto out;
2952  }
2953 
2955  if (!(req_lib_votequorum_qdevice_poll->ring_id.nodeid == quorum_ringid.nodeid &&
2956  req_lib_votequorum_qdevice_poll->ring_id.seq == quorum_ringid.seq)) {
2957  log_printf(LOGSYS_LEVEL_DEBUG, "Received poll ring id (" CS_PRI_RING_ID ") != last sync "
2958  "ring id (" CS_PRI_RING_ID "). Ignoring poll call.",
2960  quorum_ringid.nodeid, quorum_ringid.seq);
2961  error = CS_ERR_MESSAGE_ERROR;
2962  goto out;
2963  }
2965  error = CS_ERR_INVALID_PARAM;
2966  goto out;
2967  }
2968 
2969  if (qdevice_timer_set) {
2970  corosync_api->timer_delete(qdevice_timer);
2971  qdevice_timer_set = 0;
2972  }
2973 
2974  oldflags = us->flags;
2975 
2977 
2980  } else {
2982  }
2983 
2984  if (us->flags != oldflags) {
2985  votequorum_exec_send_nodeinfo(us->node_id);
2986  }
2987 
2988  corosync_api->timer_add_duration((unsigned long long)qdevice_timeout*1000000, qdevice,
2989  qdevice_timer_fn, &qdevice_timer);
2990  qdevice_timer_set = 1;
2991  sync_wait_for_poll_or_timeout = 0;
2992  } else {
2993  error = CS_ERR_NOT_EXIST;
2994  }
2995 
2996 out:
2999  res_lib_votequorum_status.header.error = error;
3001 
3002  LEAVE();
3003 }
3004 
3005 static void message_handler_req_lib_votequorum_qdevice_master_wins (void *conn,
3006  const void *message)
3007 {
3010  cs_error_t error = CS_OK;
3011  uint32_t oldflags = us->flags;
3012 
3013  ENTER();
3014 
3015  if (!qdevice_can_operate) {
3016  error = CS_ERR_ACCESS;
3017  goto out;
3018  }
3019 
3022  error = CS_ERR_INVALID_PARAM;
3023  goto out;
3024  }
3025 
3028  } else {
3030  }
3031 
3032  if (us->flags != oldflags) {
3033  votequorum_exec_send_nodeinfo(us->node_id);
3034  }
3035 
3036  update_qdevice_master_wins(req_lib_votequorum_qdevice_master_wins->allow);
3037  } else {
3038  error = CS_ERR_NOT_EXIST;
3039  }
3040 
3041 out:
3044  res_lib_votequorum_status.header.error = error;
3046 
3047  LEAVE();
3048 }
swab32
#define swab32(x)
The swab32 macro.
Definition: swab.h:51
corosync_api_v1
The corosync_api_v1 struct.
Definition: coroapi.h:225
VOTEQUORUM_NODESTATE_MEMBER
#define VOTEQUORUM_NODESTATE_MEMBER
Definition: ipc_votequorum.h:182
ATB_LIST
Definition: exec/votequorum.c:84
icmap_set_uint8
cs_error_t icmap_set_uint8(const char *key_name, uint8_t value)
Definition: icmap.c:571
default_service
Definition: service.h:42
req_lib_votequorum_trackstart::track_flags
unsigned int track_flags
Definition: ipc_votequorum.h:146
VOTEQUORUM_INFO_QDEVICE_MASTER_WINS
#define VOTEQUORUM_INFO_QDEVICE_MASTER_WINS
Definition: ipc_votequorum.h:180
req_lib_votequorum_qdevice_update
The req_lib_votequorum_qdevice_update struct.
Definition: ipc_votequorum.h:98
res_lib_votequorum_getinfo::total_votes
unsigned int total_votes
Definition: ipc_votequorum.h:196
MESSAGE_REQ_EXEC_VOTEQUORUM_RECONFIGURE
#define MESSAGE_REQ_EXEC_VOTEQUORUM_RECONFIGURE
Definition: exec/votequorum.c:144
CS_LIB_ALLOW_INQUORATE
Definition: coroapi.h:164
ATB_NONE
Definition: exec/votequorum.c:84
value
uint32_t value
Definition: exec/votequorum.c:100
LOGSYS_LEVEL_CRIT
#define LOGSYS_LEVEL_CRIT
Definition: logsys.h:71
req_exec_quorum_reconfigure::__attribute__
struct qb_ipc_request_header header __attribute__((aligned(8)))
VOTEQUORUM_QDEVICE_DEFAULT_TIMEOUT
#define VOTEQUORUM_QDEVICE_DEFAULT_TIMEOUT
Definition: ipc_votequorum.h:41
log_printf
#define log_printf(level, format, args...)
Definition: logsys.h:323
CS_TRACK_CURRENT
#define CS_TRACK_CURRENT
Definition: corotypes.h:90
CS_ERR_NO_RESOURCES
Definition: corotypes.h:115
NODE_FLAGS_WFASTATUS
#define NODE_FLAGS_WFASTATUS
Definition: exec/votequorum.c:170
coroapi.h
votequorum_init
char * votequorum_init(struct corosync_api_v1 *api, quorum_set_quorate_fn_t q_set_quorate_fn)
Definition: exec/votequorum.c:2456
NODE_FLAGS_QDEVICE_MASTER_WINS
#define NODE_FLAGS_QDEVICE_MASTER_WINS
Definition: exec/votequorum.c:175
quorum_pd::tracking_context
uint64_t tracking_context
Definition: exec/votequorum.c:225
req_lib_votequorum_getinfo
The req_lib_votequorum_getinfo struct.
Definition: ipc_votequorum.h:159
VOTEQUORUM_QDEVICE_OPERATION_REGISTER
#define VOTEQUORUM_QDEVICE_OPERATION_REGISTER
Definition: exec/votequorum.c:162
service.h
req_exec_quorum_nodeinfo::flags
uint32_t flags
Definition: exec/votequorum.c:108
VOTEQUORUM_INFO_TWONODE
#define VOTEQUORUM_INFO_TWONODE
Definition: ipc_votequorum.h:171
VOTEQUORUM_QDEVICE_OPERATION_UNREGISTER
#define VOTEQUORUM_QDEVICE_OPERATION_UNREGISTER
Definition: exec/votequorum.c:161
res_lib_votequorum_getinfo::expected_votes
unsigned int expected_votes
Definition: ipc_votequorum.h:194
CS_ERR_TRY_AGAIN
Definition: corotypes.h:103
TOTEM_AGREED
#define TOTEM_AGREED
Definition: coroapi.h:102
req_exec_quorum_reconfigure::_pad2
uint8_t _pad2
Definition: exec/votequorum.c:118
LOGSYS_LEVEL_ERROR
#define LOGSYS_LEVEL_ERROR
Definition: logsys.h:72
corosync_exec_handler
The corosync_exec_handler struct.
Definition: coroapi.h:475
quorum_pd
Definition: exec/votequorum.c:222
icmap_set_ro_access
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:1223
icmap_iter_finalize
void icmap_iter_finalize(icmap_iter_t iter)
Finalize iterator.
Definition: icmap.c:1114
req_lib_votequorum_qdevice_register
The req_lib_votequorum_qdevice_register struct.
Definition: ipc_votequorum.h:82
votequorum_node
The votequorum_node struct.
Definition: ipc_votequorum.h:206
LOGSYS_LEVEL_DEBUG
#define LOGSYS_LEVEL_DEBUG
Definition: logsys.h:76
MESSAGE_RES_VOTEQUORUM_STATUS
Definition: ipc_votequorum.h:63
CS_ERR_NOT_EXIST
Definition: corotypes.h:109
CS_ERR_MESSAGE_ERROR
Definition: corotypes.h:119
req_lib_votequorum_setvotes::votes
unsigned int votes
Definition: ipc_votequorum.h:128
MESSAGE_RES_VOTEQUORUM_EXPECTEDVOTES_NOTIFICATION
Definition: ipc_votequorum.h:67
NODE_FLAGS_QDEVICE_REGISTERED
#define NODE_FLAGS_QDEVICE_REGISTERED
Definition: exec/votequorum.c:172
res_lib_votequorum_status
The res_lib_votequorum_status struct.
Definition: ipc_votequorum.h:167
SERVICE_ID_MAKE
#define SERVICE_ID_MAKE(a, b)
Definition: coroapi.h:458
req_exec_quorum_reconfigure::param
uint8_t param
Definition: exec/votequorum.c:115
VOTEQUORUM_INFO_ALLOW_DOWNSCALE
#define VOTEQUORUM_INFO_ALLOW_DOWNSCALE
Definition: ipc_votequorum.h:176
ICMAP_TRACK_PREFIX
#define ICMAP_TRACK_PREFIX
Whole prefix is tracked, instead of key only (so "totem." tracking means that "totem....
Definition: icmap.h:85
memb_ring_id::nodeid
unsigned int nodeid
Definition: coroapi.h:123
quorate
uint32_t quorate
Definition: sam.c:134
res_lib_votequorum_getinfo::qdevice_name
char qdevice_name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
Definition: ipc_votequorum.h:200
votes
uint32_t votes
Definition: exec/votequorum.c:100
ENTER
#define ENTER
Definition: logsys.h:324
quorum_pd::track_flags
unsigned char track_flags
Definition: exec/votequorum.c:223
VOTEQUORUM_INFO_QDEVICE_ALIVE
#define VOTEQUORUM_INFO_QDEVICE_ALIVE
Definition: ipc_votequorum.h:178
req_exec_quorum_qdevice_reconfigure::oldname
char oldname[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
Definition: exec/votequorum.c:129
CS_PRI_NODE_ID
#define CS_PRI_NODE_ID
Definition: corotypes.h:59
ipc_votequorum.h
corosync_api_v1::ipc_response_send
int(* ipc_response_send)(void *conn, const void *msg, size_t mlen)
Definition: coroapi.h:258
nodeid
uint32_t nodeid
Definition: exec/votequorum.c:99
quorum_pd::list
struct qb_list_head list
Definition: exec/votequorum.c:226
req_exec_quorum_reconfigure
Definition: exec/votequorum.c:111
NODE_FLAGS_QUORATE
#define NODE_FLAGS_QUORATE
Definition: exec/votequorum.c:168
req_exec_quorum_qdevice_reconfigure
Definition: exec/votequorum.c:127
CS_TRUE
#define CS_TRUE
Definition: corotypes.h:54
VOTEQUORUM_NODESTATE_DEAD
#define VOTEQUORUM_NODESTATE_DEAD
Definition: ipc_votequorum.h:183
corosync_lib_handler
The corosync_lib_handler struct.
Definition: coroapi.h:467
quorum_set_quorate_fn_t
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
ICMAP_TRACK_ADD
#define ICMAP_TRACK_ADD
Definition: icmap.h:76
corosync_service_engine
The corosync_service_engine struct.
Definition: coroapi.h:490
icmap.h
req_lib_votequorum_qdevice_unregister::name
char name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
Definition: ipc_votequorum.h:92
header
struct totem_message_header header
Definition: totemsrp.c:260
param
uint8_t param
Definition: exec/votequorum.c:101
req_lib_votequorum_setexpected::expected_votes
unsigned int expected_votes
Definition: ipc_votequorum.h:137
newname
char newname[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
Definition: exec/votequorum.c:100
icmap_iter_next
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:1093
icmap_set_uint32
cs_error_t icmap_set_uint32(const char *key_name, uint32_t value)
Definition: icmap.c:595
cluster_node
Definition: exec/votequorum.c:183
CS_OK
Definition: corotypes.h:98
req_lib_votequorum_setexpected
The req_lib_votequorum_setexpected struct.
Definition: ipc_votequorum.h:135
LOGSYS_LEVEL_INFO
#define LOGSYS_LEVEL_INFO
Definition: logsys.h:75
MESSAGE_REQ_EXEC_VOTEQUORUM_QDEVICE_RECONFIGURE
#define MESSAGE_REQ_EXEC_VOTEQUORUM_QDEVICE_RECONFIGURE
Definition: exec/votequorum.c:146
MESSAGE_REQ_EXEC_VOTEQUORUM_QDEVICE_REG
#define MESSAGE_REQ_EXEC_VOTEQUORUM_QDEVICE_REG
Definition: exec/votequorum.c:145
res_lib_votequorum_quorum_notification
The res_lib_votequorum_quorum_notification struct.
Definition: ipc_votequorum.h:214
quorum_pd::tracking_enabled
int tracking_enabled
Definition: exec/votequorum.c:224
req_lib_votequorum_setvotes::nodeid
int nodeid
Definition: ipc_votequorum.h:129
VOTEQUORUM_INFO_QDEVICE_CAST_VOTE
#define VOTEQUORUM_INFO_QDEVICE_CAST_VOTE
Definition: ipc_votequorum.h:179
nodestate_t
nodestate_t
Definition: exec/votequorum.c:177
cluster_node::expected_votes
uint32_t expected_votes
Definition: exec/votequorum.c:187
req_exec_quorum_qdevice_reg::operation
uint32_t operation
Definition: exec/votequorum.c:123
VOTEQUORUM_RECONFIG_PARAM_NODE_VOTES
#define VOTEQUORUM_RECONFIG_PARAM_NODE_VOTES
Definition: exec/votequorum.c:153
ICMAP_KEYNAME_MAXLEN
#define ICMAP_KEYNAME_MAXLEN
Maximum length of key in icmap.
Definition: icmap.h:48
res_lib_votequorum_nodelist_notification
Definition: ipc_votequorum.h:222
cluster_node::votes
uint32_t votes
Definition: exec/votequorum.c:186
cs_error_t
cs_error_t
The cs_error_t enum.
Definition: corotypes.h:97
corosync_exec_handler::exec_handler_fn
void(* exec_handler_fn)(const void *msg, unsigned int nodeid)
Definition: coroapi.h:476
req_lib_votequorum_qdevice_register::name
char name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
Definition: ipc_votequorum.h:84
__attribute__
typedef __attribute__
res_lib_votequorum_getinfo::flags
unsigned int flags
Definition: ipc_votequorum.h:198
LEAVE
#define LEAVE
Definition: logsys.h:325
CS_ERR_ACCESS
Definition: corotypes.h:108
corosync_api_v1::timer_add_duration
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
VOTEQUORUM_INFO_QUORATE
#define VOTEQUORUM_INFO_QUORATE
Definition: ipc_votequorum.h:172
icmap_track_add
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:1157
VOTEQUORUM_INFO_LAST_MAN_STANDING
#define VOTEQUORUM_INFO_LAST_MAN_STANDING
Definition: ipc_votequorum.h:174
quorum_pd::conn
void * conn
Definition: exec/votequorum.c:227
get_state_dir
const char * get_state_dir(void)
Definition: util.c:172
DEFAULT_LMS_WIN
#define DEFAULT_LMS_WIN
Definition: exec/votequorum.c:88
votequorum.h
NODE_FLAGS_QDEVICE_ALIVE
#define NODE_FLAGS_QDEVICE_ALIVE
Definition: exec/votequorum.c:173
MESSAGE_RES_VOTEQUORUM_GETINFO
Definition: ipc_votequorum.h:64
req_exec_quorum_reconfigure::value
uint32_t value
Definition: exec/votequorum.c:114
LOGSYS_DECLARE_SUBSYS
LOGSYS_DECLARE_SUBSYS("VOTEQ")
flags
uint32_t flags
Definition: exec/votequorum.c:102
NODESTATE_MEMBER
Definition: exec/votequorum.c:178
cluster_node::list
struct qb_list_head list
Definition: exec/votequorum.c:189
MESSAGE_RES_VOTEQUORUM_QUORUM_NOTIFICATION
Definition: ipc_votequorum.h:66
req_lib_votequorum_qdevice_unregister
The req_lib_votequorum_qdevice_unregister struct.
Definition: ipc_votequorum.h:90
LOGSYS_LEVEL_NOTICE
#define LOGSYS_LEVEL_NOTICE
Definition: logsys.h:74
res_lib_votequorum_getinfo::votes
unsigned int votes
Definition: ipc_votequorum.h:193
res_lib_votequorum_getinfo::quorum
unsigned int quorum
Definition: ipc_votequorum.h:197
COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
#define COROSYNC_LIB_FLOW_CONTROL_NOT_REQUIRED
Definition: coroapi.h:157
corosync_api_v1::totem_mcast
int(* totem_mcast)(const struct iovec *iovec, unsigned int iov_len, unsigned int guarantee)
Definition: coroapi.h:279
req_lib_votequorum_qdevice_poll
The req_lib_votequorum_qdevice_poll struct.
Definition: ipc_votequorum.h:107
CS_ERR_EXIST
Definition: corotypes.h:111
req_lib_votequorum_trackstart::context
uint64_t context
Definition: ipc_votequorum.h:145
NODESTATE_DEAD
Definition: exec/votequorum.c:179
CS_PRI_RING_ID
#define CS_PRI_RING_ID
Definition: corotypes.h:61
icmap_get_uint32
cs_error_t icmap_get_uint32(const char *key_name, uint32_t *u32)
Definition: icmap.c:890
VOTEQUORUM_RECONFIG_PARAM_EXPECTED_VOTES
#define VOTEQUORUM_RECONFIG_PARAM_EXPECTED_VOTES
Definition: exec/votequorum.c:152
cluster_node::flags
uint32_t flags
Definition: exec/votequorum.c:188
req_lib_votequorum_qdevice_poll::name
char name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
Definition: ipc_votequorum.h:109
VOTEQUORUM_INFO_QDEVICE_REGISTERED
#define VOTEQUORUM_INFO_QDEVICE_REGISTERED
Definition: ipc_votequorum.h:177
VOTEQUORUM_NODESTATE_LEAVING
#define VOTEQUORUM_NODESTATE_LEAVING
Definition: ipc_votequorum.h:184
req_lib_votequorum_trackstart
The req_lib_votequorum_trackstart struct.
Definition: ipc_votequorum.h:143
expected_votes
uint32_t expected_votes
Definition: exec/votequorum.c:101
NODESTATE_LEAVING
Definition: exec/votequorum.c:180
req_exec_quorum_nodeinfo::expected_votes
uint32_t expected_votes
Definition: exec/votequorum.c:107
memb_ring_id
The memb_ring_id struct.
Definition: coroapi.h:122
req_exec_quorum_qdevice_reg::qdevice_name
char qdevice_name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
Definition: exec/votequorum.c:124
VOTEQUORUM_QDEVICE_NODEID
#define VOTEQUORUM_QDEVICE_NODEID
Definition: ipc_votequorum.h:39
res_lib_votequorum_getinfo
The res_lib_votequorum_getinfo struct.
Definition: ipc_votequorum.h:189
res_lib_votequorum_getinfo::qdevice_votes
unsigned int qdevice_votes
Definition: ipc_votequorum.h:199
icmap_iter_init
icmap_iter_t icmap_iter_init(const char *prefix)
Initialize iterator with given prefix.
Definition: icmap.c:1087
VOTEQUORUM_QDEVICE_MAX_NAME_LEN
#define VOTEQUORUM_QDEVICE_MAX_NAME_LEN
Definition: ipc_votequorum.h:40
ICMAP_TRACK_DELETE
#define ICMAP_TRACK_DELETE
Definition: icmap.h:77
user_data
void * user_data
Definition: sam.c:127
req_exec_quorum_reconfigure::nodeid
uint32_t nodeid
Definition: exec/votequorum.c:113
ICMAP_TRACK_MODIFY
#define ICMAP_TRACK_MODIFY
Definition: icmap.h:78
VOTEQUORUM_SERVICE
Definition: corodefs.h:49
icmap_get_string
cs_error_t icmap_get_string(const char *key_name, char **str)
Shortcut for icmap_get for string type.
Definition: icmap.c:854
corosync_api_v1::error_memory_failure
void(* error_memory_failure)(void) __attribute__((noreturn))
Definition: coroapi.h:422
quorum.h
res_lib_votequorum_getinfo::state
unsigned int state
Definition: ipc_votequorum.h:192
req_exec_quorum_nodeinfo::__attribute__
struct qb_ipc_request_header header __attribute__((aligned(8)))
COROSYNC_LIB_FLOW_CONTROL_REQUIRED
#define COROSYNC_LIB_FLOW_CONTROL_REQUIRED
Definition: coroapi.h:156
MESSAGE_REQ_EXEC_VOTEQUORUM_NODEINFO
#define MESSAGE_REQ_EXEC_VOTEQUORUM_NODEINFO
Definition: exec/votequorum.c:143
util.h
default_service::name
const char * name
Definition: service.h:43
NODE_FLAGS_LEAVING
#define NODE_FLAGS_LEAVING
Definition: exec/votequorum.c:169
req_lib_votequorum_qdevice_update::newname
char newname[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
Definition: ipc_votequorum.h:101
VOTEQUORUM_READCONFIG_RUNTIME
#define VOTEQUORUM_READCONFIG_RUNTIME
Definition: exec/votequorum.c:1240
VOTEQUORUM_INFO_AUTO_TIE_BREAKER
#define VOTEQUORUM_INFO_AUTO_TIE_BREAKER
Definition: ipc_votequorum.h:175
max
#define max(a, b)
Definition: exec/votequorum.c:432
VOTEQUORUM_READCONFIG_STARTUP
#define VOTEQUORUM_READCONFIG_STARTUP
Definition: exec/votequorum.c:1239
CS_FALSE
#define CS_FALSE
Definition: corotypes.h:53
NODE_FLAGS_FIRST
#define NODE_FLAGS_FIRST
Definition: exec/votequorum.c:171
corosync_api_v1::totem_nodeid_get
unsigned int(* totem_nodeid_get)(void)
Definition: coroapi.h:275
corosync_service_engine::name
const char * name
Definition: coroapi.h:491
VOTEQUORUM_QDEVICE_DEFAULT_SYNC_TIMEOUT
#define VOTEQUORUM_QDEVICE_DEFAULT_SYNC_TIMEOUT
Definition: include/corosync/votequorum.h:64
CS_TRACK_CHANGES
#define CS_TRACK_CHANGES
Definition: corotypes.h:91
votequorum_get_service_engine_ver0
struct corosync_service_engine * votequorum_get_service_engine_ver0(void)
Definition: exec/votequorum.c:415
memb_ring_id::seq
unsigned long long seq
Definition: coroapi.h:124
corosync_lib_handler::lib_handler_fn
void(* lib_handler_fn)(void *conn, const void *msg)
Definition: coroapi.h:468
ring_id
struct memb_ring_id ring_id
Definition: totemsrp.c:264
MESSAGE_RES_VOTEQUORUM_NODELIST_NOTIFICATION
Definition: ipc_votequorum.h:68
req_exec_quorum_nodeinfo::nodeid
uint32_t nodeid
Definition: exec/votequorum.c:105
ATB_HIGHEST
Definition: exec/votequorum.c:84
PROCESSOR_COUNT_MAX
#define PROCESSOR_COUNT_MAX
Definition: coroapi.h:96
config.h
CS_TRACK_CHANGES_ONLY
#define CS_TRACK_CHANGES_ONLY
Definition: corotypes.h:92
corosync_service_link_and_init
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
CS_ERR_INVALID_PARAM
Definition: corotypes.h:104
req_exec_quorum_qdevice_reg
Definition: exec/votequorum.c:121
req_exec_quorum_nodeinfo::votes
uint32_t votes
Definition: exec/votequorum.c:106
CS_ERR_NAME_NOT_FOUND
Definition: corotypes.h:114
logsys.h
res_lib_votequorum_getinfo::nodeid
unsigned int nodeid
Definition: ipc_votequorum.h:191
LOGSYS_LEVEL_WARNING
#define LOGSYS_LEVEL_WARNING
Definition: logsys.h:73
req_exec_quorum_nodeinfo
Definition: exec/votequorum.c:103
icmap_track
Definition: icmap.c:61
VOTEQUORUM_RECONFIG_PARAM_CANCEL_WFA
#define VOTEQUORUM_RECONFIG_PARAM_CANCEL_WFA
Definition: exec/votequorum.c:154
req_exec_quorum_reconfigure::_pad1
uint8_t _pad1
Definition: exec/votequorum.c:117
res_lib_votequorum_expectedvotes_notification
The res_lib_votequorum_expectedvotes_notification struct.
Definition: ipc_votequorum.h:233
req_lib_votequorum_qdevice_master_wins::allow
unsigned int allow
Definition: ipc_votequorum.h:120
corosync_timer_handle_t
qb_loop_timer_handle corosync_timer_handle_t
corosync_timer_handle_t
Definition: coroapi.h:74
cluster_node::node_id
int node_id
Definition: exec/votequorum.c:184
req_exec_quorum_qdevice_reconfigure::newname
char newname[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
Definition: exec/votequorum.c:130
icmap_notify_value
Structure passed as new_value and old_value in change callback.
Definition: icmap.h:91
VOTEQUORUM_INFO_WAIT_FOR_ALL
#define VOTEQUORUM_INFO_WAIT_FOR_ALL
Definition: ipc_votequorum.h:173
req_lib_votequorum_qdevice_master_wins
The req_lib_votequorum_qdevice_master_wins struct.
Definition: ipc_votequorum.h:117
oldname
char oldname[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
Definition: exec/votequorum.c:99
req_lib_votequorum_qdevice_poll::cast_vote
int cast_vote
Definition: ipc_votequorum.h:110
req_exec_quorum_qdevice_reconfigure::__attribute__
struct qb_ipc_request_header header __attribute__((aligned(8)))
req_lib_votequorum_getinfo::nodeid
int nodeid
Definition: ipc_votequorum.h:161
req_lib_votequorum_qdevice_master_wins::name
char name[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
Definition: ipc_votequorum.h:119
corosync_api_v1::timer_delete
void(* timer_delete)(corosync_timer_handle_t timer_handle)
Definition: coroapi.h:241
icmap_iter_t
qb_map_iter_t * icmap_iter_t
Itterator type.
Definition: icmap.h:123
icmap_get_uint8
cs_error_t icmap_get_uint8(const char *key_name, uint8_t *u8)
Definition: icmap.c:866
ATB_LOWEST
Definition: exec/votequorum.c:84
corosync_api_v1::ipc_dispatch_send
int(* ipc_dispatch_send)(void *conn, const void *msg, size_t mlen)
Definition: coroapi.h:263
res_lib_votequorum_getinfo::highest_expected
unsigned int highest_expected
Definition: ipc_votequorum.h:195
corosync_api_v1::ipc_private_data_get
void *(* ipc_private_data_get)(void *conn)
Definition: coroapi.h:256
cluster_node::state
nodestate_t state
Definition: exec/votequorum.c:185
NODE_FLAGS_QDEVICE_CAST_VOTE
#define NODE_FLAGS_QDEVICE_CAST_VOTE
Definition: exec/votequorum.c:174
req_lib_votequorum_qdevice_update::oldname
char oldname[VOTEQUORUM_QDEVICE_MAX_NAME_LEN]
Definition: ipc_votequorum.h:100
req_exec_quorum_reconfigure::_pad0
uint8_t _pad0
Definition: exec/votequorum.c:116
operation
uint32_t operation
Definition: exec/votequorum.c:99
req_exec_quorum_qdevice_reg::__attribute__
struct qb_ipc_request_header header __attribute__((aligned(8)))
corodefs.h
req_lib_votequorum_setvotes
The req_lib_votequorum_setvotes struct.
Definition: ipc_votequorum.h:126