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