Просмотр исходного кода

qnet: Add support for keep active partition vote

This patch adds qdevice-net part of keep active partition tie breaker
functionality. It's enabled by default.

When tie happens prefer partition with members of
previously active (quorate) partition. This is hard-coded
behavior of LMS algorithm so this setting affects only
FFSplit algorithm. By default it is disabled for backwards
compatibility.

This solves problem with FFSplit when node A (with lowest id) is killed,
node B gets vote and then node A starts up and creates single node
membership and gets vote.

Signed-off-by: Jan Friesse <jfriesse@redhat.com>
Jan Friesse 5 лет назад
Родитель
Сommit
fbc34f3b05

+ 3 - 2
man/corosync-qdevice-tool.8

@@ -1,5 +1,5 @@
 .\"/*
 .\"/*
-.\" * Copyright (C) 2016-2017 Red Hat, Inc.
+.\" * Copyright (C) 2016-2020 Red Hat, Inc.
 .\" *
 .\" *
 .\" * All rights reserved.
 .\" * All rights reserved.
 .\" *
 .\" *
@@ -31,7 +31,7 @@
 .\" * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 .\" * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 .\" * THE POSSIBILITY OF SUCH DAMAGE.
 .\" * THE POSSIBILITY OF SUCH DAMAGE.
 .\" */
 .\" */
-.TH COROSYNC-QDEVICE-TOOL 8 2017-10-17
+.TH COROSYNC-QDEVICE-TOOL 8 2020-10-27
 .SH NAME
 .SH NAME
 corosync-qdevice-tool \- corosync-qdevice control interface.
 corosync-qdevice-tool \- corosync-qdevice control interface.
 .SH SYNOPSIS
 .SH SYNOPSIS
@@ -95,6 +95,7 @@ VQ vote timer interval: 5000ms
 TLS:                    Supported
 TLS:                    Supported
 Algorithm:              Fifty-Fifty split
 Algorithm:              Fifty-Fifty split
 Tie-breaker:            Node with lowest node ID
 Tie-breaker:            Node with lowest node ID
+KAP Tie-breaker:        Enabled
 Poll timer running:     Yes (cast vote)
 Poll timer running:     Yes (cast vote)
 State:                  Connected
 State:                  Connected
 Heuristics result:      Pass (regular: Pass, membership: Fail, connect: Fail)
 Heuristics result:      Pass (regular: Pass, membership: Fail, connect: Fail)

+ 11 - 2
man/corosync-qdevice.8

@@ -1,5 +1,5 @@
 .\"/*
 .\"/*
-.\" * Copyright (C) 2016-2019 Red Hat, Inc.
+.\" * Copyright (C) 2016-2020 Red Hat, Inc.
 .\" *
 .\" *
 .\" * All rights reserved.
 .\" * All rights reserved.
 .\" *
 .\" *
@@ -31,7 +31,7 @@
 .\" * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 .\" * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 .\" * THE POSSIBILITY OF SUCH DAMAGE.
 .\" * THE POSSIBILITY OF SUCH DAMAGE.
 .\" */
 .\" */
-.TH COROSYNC-QDEVICE 8 2019-08-12
+.TH COROSYNC-QDEVICE 8 2020-10-27
 .SH NAME
 .SH NAME
 corosync-qdevice \- QDevice daemon
 corosync-qdevice \- QDevice daemon
 .SH SYNOPSIS
 .SH SYNOPSIS
@@ -226,6 +226,15 @@ can be one of
 and forces the software to use the given IP version.
 and forces the software to use the given IP version.
 .I 0
 .I 0
 (default value) means IPv6 is preferred and IPv4 should be used as a fallback.
 (default value) means IPv6 is preferred and IPv4 should be used as a fallback.
+.TP
+.B keep_active_partition_tie_breaker
+Can be one of
+.IR on " or " off
+and specifies if keep active partition tie breaker should be used. When this option is enabled
+and tie happens QNetd will prefer partition with members of previously active (quorate) partition.
+This is hard-coded behavior of LMS algorithm so this setting affects only FFSplit algorithm.
+Default is
+.IR on .
 
 
 .PP
 .PP
 Logging configuration is within the
 Logging configuration is within the

+ 4 - 4
man/corosync-qnetd-tool.8

@@ -1,5 +1,5 @@
 .\"/*
 .\"/*
-.\" * Copyright (C) 2016 Red Hat, Inc.
+.\" * Copyright (C) 2016-2020 Red Hat, Inc.
 .\" *
 .\" *
 .\" * All rights reserved.
 .\" * All rights reserved.
 .\" *
 .\" *
@@ -31,7 +31,7 @@
 .\" * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 .\" * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 .\" * THE POSSIBILITY OF SUCH DAMAGE.
 .\" * THE POSSIBILITY OF SUCH DAMAGE.
 .\" */
 .\" */
-.TH COROSYNC-QNETD-TOOL 8 2016-06-23
+.TH COROSYNC-QNETD-TOOL 8 2020-10-27
 .SH NAME
 .SH NAME
 corosync-qnetd-tool \- corosync-qnetd control interface.
 corosync-qnetd-tool \- corosync-qnetd control interface.
 .SH SYNOPSIS
 .SH SYNOPSIS
@@ -82,7 +82,7 @@ communication socket.
 .SH LIST COMMAND OUTPUT
 .SH LIST COMMAND OUTPUT
 .nf
 .nf
 Cluster "Cluster":
 Cluster "Cluster":
-    Algorithm:          Fifty-Fifty split
+    Algorithm:          Fifty-Fifty split (KAP Tie-breaker)
     Tie-breaker:        Node with lowest node ID
     Tie-breaker:        Node with lowest node ID
     Node ID 1:
     Node ID 1:
         Client address:         ::ffff:127.0.0.1:52000
         Client address:         ::ffff:127.0.0.1:52000
@@ -97,7 +97,7 @@ Cluster "Cluster":
 
 
 The output contains a list of clusters. Each cluster has the cluster common options
 The output contains a list of clusters. Each cluster has the cluster common options
 .I Algorithm
 .I Algorithm
-and
+(optionally with Keep Active Partition Tie Breaker information) and
 .I Tie-breaker
 .I Tie-breaker
 as configured in the corosync.conf file. Information about nodes follows.
 as configured in the corosync.conf file. Information about nodes follows.
 .I Client address
 .I Client address

+ 30 - 2
qdevices/msg.c

@@ -420,7 +420,9 @@ small_buf_err:
 
 
 size_t
 size_t
 msg_create_set_option(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number,
 msg_create_set_option(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number,
-    int add_heartbeat_interval, uint32_t heartbeat_interval)
+    int add_heartbeat_interval, uint32_t heartbeat_interval,
+    int add_keep_active_partition_tie_breaker,
+    enum tlv_keep_active_partition_tie_breaker keep_active_partiton_tie_breaker)
 {
 {
 
 
 	dynar_clean(msg);
 	dynar_clean(msg);
@@ -440,6 +442,13 @@ msg_create_set_option(struct dynar *msg, int add_msg_seq_number, uint32_t msg_se
 		}
 		}
 	}
 	}
 
 
+	if (add_keep_active_partition_tie_breaker) {
+		if (tlv_add_keep_active_partition_tie_breaker(msg,
+		    keep_active_partiton_tie_breaker) == -1) {
+			goto small_buf_err;
+		}
+	}
+
 	msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
 	msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
 
 
 	return (dynar_size(msg));
 	return (dynar_size(msg));
@@ -450,7 +459,9 @@ small_buf_err:
 
 
 size_t
 size_t
 msg_create_set_option_reply(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number,
 msg_create_set_option_reply(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number,
-    int add_heartbeat_interval, uint32_t heartbeat_interval)
+    int add_heartbeat_interval, uint32_t heartbeat_interval,
+    int add_keep_active_partition_tie_breaker,
+    enum tlv_keep_active_partition_tie_breaker keep_active_partiton_tie_breaker)
 {
 {
 
 
 	dynar_clean(msg);
 	dynar_clean(msg);
@@ -470,6 +481,13 @@ msg_create_set_option_reply(struct dynar *msg, int add_msg_seq_number, uint32_t
 		}
 		}
 	}
 	}
 
 
+	if (add_keep_active_partition_tie_breaker) {
+		if (tlv_add_keep_active_partition_tie_breaker(msg,
+		    keep_active_partiton_tie_breaker) == -1) {
+			goto small_buf_err;
+		}
+	}
+
 	msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
 	msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
 
 
 	return (dynar_size(msg));
 	return (dynar_size(msg));
@@ -841,6 +859,7 @@ msg_decode(const struct dynar *msg, struct msg_decoded *decoded_msg)
 	enum tlv_opt_type opt_type;
 	enum tlv_opt_type opt_type;
 	int iter_res;
 	int iter_res;
 	int res;
 	int res;
+	enum tlv_keep_active_partition_tie_breaker keep_active_partition_tb;
 
 
 	msg_decoded_destroy(decoded_msg);
 	msg_decoded_destroy(decoded_msg);
 
 
@@ -1045,6 +1064,15 @@ msg_decode(const struct dynar *msg, struct msg_decoded *decoded_msg)
 				return (res);
 				return (res);
 			}
 			}
 			break;
 			break;
+		case TLV_OPT_KEEP_ACTIVE_PARTITION_TIE_BREAKER:
+			if ((res = tlv_iter_decode_keep_active_partition_tie_breaker(&tlv_iter,
+			    &keep_active_partition_tb)) != 0) {
+				return (res);
+			}
+
+			decoded_msg->keep_active_partition_tie_breaker_set = 1;
+			decoded_msg->keep_active_partition_tie_breaker = keep_active_partition_tb;
+			break;
 		/*
 		/*
 		 * Default is not defined intentionally. Compiler shows warning when
 		 * Default is not defined intentionally. Compiler shows warning when
 		 * new tlv option is added. Also protocol ignores unknown options so
 		 * new tlv option is added. Also protocol ignores unknown options so

+ 12 - 6
qdevices/msg.h

@@ -106,15 +106,17 @@ struct msg_decoded {
 	uint32_t data_center_id;	/* Valid only if != 0 */
 	uint32_t data_center_id;	/* Valid only if != 0 */
 	enum tlv_node_state node_state;	/* Valid only if != TLV_NODE_STATE_NOT_SET */
 	enum tlv_node_state node_state;	/* Valid only if != TLV_NODE_STATE_NOT_SET */
 	struct node_list nodes;		/* Valid only if node_list_is_empty(nodes) != 0 */
 	struct node_list nodes;		/* Valid only if node_list_is_empty(nodes) != 0 */
-	int node_list_type_set;
+	uint8_t node_list_type_set;
 	enum tlv_node_list_type node_list_type;	/* Valid only if node_list_type_set != 0 */
 	enum tlv_node_list_type node_list_type;	/* Valid only if node_list_type_set != 0 */
-	int vote_set;
+	uint8_t vote_set;
 	enum tlv_vote vote;	/* Valid only if vote_set != 0 */
 	enum tlv_vote vote;	/* Valid only if vote_set != 0 */
-	int quorate_set;
+	uint8_t quorate_set;
 	enum tlv_quorate quorate;	/* Valid only if quorate_set != 0 */
 	enum tlv_quorate quorate;	/* Valid only if quorate_set != 0 */
-	int tie_breaker_set;
+	uint8_t tie_breaker_set;
 	struct tlv_tie_breaker tie_breaker;
 	struct tlv_tie_breaker tie_breaker;
 	enum tlv_heuristics heuristics;	/* Always valid but can be TLV_HEURISTICS_UNDEFINED */
 	enum tlv_heuristics heuristics;	/* Always valid but can be TLV_HEURISTICS_UNDEFINED */
+	enum tlv_keep_active_partition_tie_breaker keep_active_partition_tie_breaker;
+	uint8_t keep_active_partition_tie_breaker_set;
 };
 };
 
 
 extern size_t		msg_create_preinit(struct dynar *msg, const char *cluster_name,
 extern size_t		msg_create_preinit(struct dynar *msg, const char *cluster_name,
@@ -146,11 +148,15 @@ extern size_t		msg_create_init_reply(struct dynar *msg, int add_msg_seq_number,
 
 
 extern size_t		msg_create_set_option(struct dynar *msg,
 extern size_t		msg_create_set_option(struct dynar *msg,
     int add_msg_seq_number, uint32_t msg_seq_number,
     int add_msg_seq_number, uint32_t msg_seq_number,
-    int add_heartbeat_interval, uint32_t heartbeat_interval);
+    int add_heartbeat_interval, uint32_t heartbeat_interval,
+    int add_keep_active_partition_tie_breaker,
+    enum tlv_keep_active_partition_tie_breaker keep_active_partiton_tie_breaker);
 
 
 extern size_t		msg_create_set_option_reply(struct dynar *msg,
 extern size_t		msg_create_set_option_reply(struct dynar *msg,
     int add_msg_seq_number, uint32_t msg_seq_number,
     int add_msg_seq_number, uint32_t msg_seq_number,
-    int add_heartbeat_interval, uint32_t heartbeat_interval);
+    int add_heartbeat_interval, uint32_t heartbeat_interval,
+    int add_keep_active_partition_tie_breaker,
+    enum tlv_keep_active_partition_tie_breaker keep_active_partiton_tie_breaker);
 
 
 extern size_t		msg_create_echo_request(struct dynar *msg, int add_msg_seq_number,
 extern size_t		msg_create_echo_request(struct dynar *msg, int add_msg_seq_number,
     uint32_t msg_seq_number);
     uint32_t msg_seq_number);

+ 12 - 0
qdevices/qdevice-net-heuristics.c

@@ -238,6 +238,18 @@ qdevice_net_connect_heuristics_exec_result_callback(uint32_t seq_number,
 		    tlv_vote_to_str(vote));
 		    tlv_vote_to_str(vote));
 	}
 	}
 
 
+	/*
+	 * Inform qnetd about connection options
+	 */
+	if (net_instance->server_supports_keep_active_partition_tie_breaker) {
+		if (qdevice_net_send_set_option(net_instance, 0, 0,
+		    net_instance->server_supports_keep_active_partition_tie_breaker,
+		    net_instance->keep_active_partition_tie_breaker) != 0) {
+			net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+			return (0);
+		}
+	}
+
 	/*
 	/*
 	 * Now we can finally really send node list, votequorum node list and update timer
 	 * Now we can finally really send node list, votequorum node list and update timer
 	 */
 	 */

+ 25 - 2
qdevices/qdevice-net-instance.c

@@ -48,7 +48,8 @@ qdevice_net_instance_init(struct qdevice_net_instance *instance,
     uint32_t sync_heartbeat_interval, uint32_t cast_vote_timer_interval,
     uint32_t sync_heartbeat_interval, uint32_t cast_vote_timer_interval,
     const char *host_addr, uint16_t host_port, const char *cluster_name,
     const char *host_addr, uint16_t host_port, const char *cluster_name,
     const struct tlv_tie_breaker *tie_breaker, uint32_t connect_timeout,
     const struct tlv_tie_breaker *tie_breaker, uint32_t connect_timeout,
-    int force_ip_version, int cmap_fd, int votequorum_fd, int local_socket_fd,
+    int force_ip_version, enum tlv_keep_active_partition_tie_breaker keep_active_partition_tie_breaker,
+    int cmap_fd, int votequorum_fd, int local_socket_fd,
     const struct qdevice_advanced_settings *advanced_settings,
     const struct qdevice_advanced_settings *advanced_settings,
     int heuristics_pipe_cmd_send_fd, int heuristics_pipe_cmd_recv_fd,
     int heuristics_pipe_cmd_send_fd, int heuristics_pipe_cmd_recv_fd,
     int heuristics_pipe_log_recv_fd)
     int heuristics_pipe_log_recv_fd)
@@ -72,6 +73,7 @@ qdevice_net_instance_init(struct qdevice_net_instance *instance,
 	instance->force_ip_version = force_ip_version;
 	instance->force_ip_version = force_ip_version;
 	instance->last_echo_reply_received_time = ((time_t) -1);
 	instance->last_echo_reply_received_time = ((time_t) -1);
 	instance->connected_since_time = ((time_t) -1);
 	instance->connected_since_time = ((time_t) -1);
+	instance->keep_active_partition_tie_breaker = keep_active_partition_tie_breaker;
 
 
 	memcpy(&instance->tie_breaker, tie_breaker, sizeof(*tie_breaker));
 	memcpy(&instance->tie_breaker, tie_breaker, sizeof(*tie_breaker));
 
 
@@ -139,6 +141,7 @@ qdevice_net_instance_init_from_cmap(struct qdevice_instance *instance)
 	uint32_t connect_timeout;
 	uint32_t connect_timeout;
 	struct qdevice_net_instance *net_instance;
 	struct qdevice_net_instance *net_instance;
 	int force_ip_version;
 	int force_ip_version;
+	enum tlv_keep_active_partition_tie_breaker keep_active_partition_tb;
 
 
 	cmap_handle = instance->cmap_handle;
 	cmap_handle = instance->cmap_handle;
 
 
@@ -314,6 +317,26 @@ qdevice_net_instance_init_from_cmap(struct qdevice_instance *instance)
 		free(str);
 		free(str);
 	}
 	}
 
 
+	keep_active_partition_tb = QDEVICE_NET_DEFAULT_KEEP_ACTIVE_PARTITION_TB;
+
+	if (cmap_get_string(cmap_handle, "quorum.device.net.keep_active_partition_tie_breaker",
+	    &str) == CS_OK) {
+		if ((i = utils_parse_bool_str(str)) == -1) {
+			log(LOG_ERR, "quorum.device.net.keep_active_partition_tie_breaker "
+			    "value is not valid.");
+			free(str);
+			goto error_free_cluster_name;
+		} else {
+			if (i == 1) {
+				keep_active_partition_tb = TLV_KEEP_ACTIVE_PARTITION_TIE_BREAKER_ENABLED;
+			} else {
+				keep_active_partition_tb = TLV_KEEP_ACTIVE_PARTITION_TIE_BREAKER_DISABLED;
+			}
+		}
+
+		free(str);
+	}
+
 	/*
 	/*
 	 * Really initialize instance
 	 * Really initialize instance
 	 */
 	 */
@@ -321,7 +344,7 @@ qdevice_net_instance_init_from_cmap(struct qdevice_instance *instance)
 	    tls_supported, decision_algorithm,
 	    tls_supported, decision_algorithm,
 	    heartbeat_interval, sync_heartbeat_interval, cast_vote_timer_interval,
 	    heartbeat_interval, sync_heartbeat_interval, cast_vote_timer_interval,
 	    host_addr, host_port, cluster_name, &tie_breaker, connect_timeout,
 	    host_addr, host_port, cluster_name, &tie_breaker, connect_timeout,
-	    force_ip_version,
+	    force_ip_version, keep_active_partition_tb,
 	    instance->cmap_poll_fd, instance->votequorum_poll_fd,
 	    instance->cmap_poll_fd, instance->votequorum_poll_fd,
 	    instance->local_ipc.socket, instance->advanced_settings,
 	    instance->local_ipc.socket, instance->advanced_settings,
 	    instance->heuristics_instance.pipe_cmd_send,
 	    instance->heuristics_instance.pipe_cmd_send,

+ 3 - 0
qdevices/qdevice-net-instance.h

@@ -103,6 +103,8 @@ struct qdevice_net_instance {
 	const struct qdevice_advanced_settings *advanced_settings;
 	const struct qdevice_advanced_settings *advanced_settings;
 	struct timer_list_entry *regular_heuristics_timer;
 	struct timer_list_entry *regular_heuristics_timer;
 	int server_supports_heuristics;
 	int server_supports_heuristics;
+	int server_supports_keep_active_partition_tie_breaker;
+	enum tlv_keep_active_partition_tie_breaker keep_active_partition_tie_breaker;
 	enum tlv_heuristics latest_regular_heuristics_result;
 	enum tlv_heuristics latest_regular_heuristics_result;
 	enum tlv_heuristics latest_connect_heuristics_result;
 	enum tlv_heuristics latest_connect_heuristics_result;
 	enum tlv_heuristics latest_vq_heuristics_result;
 	enum tlv_heuristics latest_vq_heuristics_result;
@@ -115,6 +117,7 @@ extern int		qdevice_net_instance_init(struct qdevice_net_instance *instance,
     uint32_t sync_heartbeat_interval, uint32_t cast_vote_timer_interval,
     uint32_t sync_heartbeat_interval, uint32_t cast_vote_timer_interval,
     const char *host_addr, uint16_t host_port, const char *cluster_name,
     const char *host_addr, uint16_t host_port, const char *cluster_name,
     const struct tlv_tie_breaker *tie_breaker, uint32_t connect_timeout, int force_ip_version,
     const struct tlv_tie_breaker *tie_breaker, uint32_t connect_timeout, int force_ip_version,
+    enum tlv_keep_active_partition_tie_breaker keep_active_partition_tie_breaker,
     int cmap_fd, int votequorum_fd, int local_socket_fd,
     int cmap_fd, int votequorum_fd, int local_socket_fd,
     const struct qdevice_advanced_settings *advanced_settings,
     const struct qdevice_advanced_settings *advanced_settings,
     int heuristics_pipe_cmd_send_fd, int heuristics_pipe_cmd_recv_fd,
     int heuristics_pipe_cmd_send_fd, int heuristics_pipe_cmd_recv_fd,

+ 39 - 1
qdevices/qdevice-net-ipc-cmd.c

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2015-2016 Red Hat, Inc.
+ * Copyright (c) 2015-2020 Red Hat, Inc.
  *
  *
  * All rights reserved.
  * All rights reserved.
  *
  *
@@ -77,6 +77,42 @@ qdevice_net_ipc_cmd_status_add_tie_breaker(struct qdevice_net_instance *instance
 	return (dynar_str_catf(outbuf, "\n") != -1);
 	return (dynar_str_catf(outbuf, "\n") != -1);
 }
 }
 
 
+static int
+qdevice_net_ipc_cmd_status_add_kap_tb_info(struct qdevice_net_instance *instance,
+    struct dynar *outbuf, int verbose)
+{
+	const char *kap_tb_str;
+
+	if (!verbose) {
+		return (1);
+	}
+
+	switch (instance->decision_algorithm) {
+	case TLV_DECISION_ALGORITHM_TYPE_TEST:
+		kap_tb_str = "Unsupported by algorithm";
+		break;
+	case TLV_DECISION_ALGORITHM_TYPE_2NODELMS:
+	case TLV_DECISION_ALGORITHM_TYPE_LMS:
+		kap_tb_str = "Algorithm hard-coded";
+		break;
+	case TLV_DECISION_ALGORITHM_TYPE_FFSPLIT:
+		if (instance->keep_active_partition_tie_breaker) {
+			if (instance->state !=
+			    QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS ||
+			    instance->server_supports_keep_active_partition_tie_breaker) {
+				kap_tb_str = "Enabled";
+			} else {
+				kap_tb_str = "Unsupported by QNetd";
+			}
+		} else {
+			kap_tb_str = "Disabled";
+		}
+		break;
+	}
+
+	return (dynar_str_catf(outbuf, "KAP Tie-breaker:\t%s\n", kap_tb_str) != -1);
+}
+
 static int
 static int
 qdevice_net_ipc_cmd_status_add_basic_info(struct qdevice_net_instance *instance,
 qdevice_net_ipc_cmd_status_add_basic_info(struct qdevice_net_instance *instance,
     struct dynar *outbuf, int verbose)
     struct dynar *outbuf, int verbose)
@@ -112,6 +148,7 @@ qdevice_net_ipc_cmd_status_add_basic_info(struct qdevice_net_instance *instance,
 		    tlv_tls_supported_to_str(instance->tls_supported)) == -1) {
 		    tlv_tls_supported_to_str(instance->tls_supported)) == -1) {
 			return (0);
 			return (0);
 		}
 		}
+
 	}
 	}
 
 
 	if (dynar_str_catf(outbuf, "Algorithm:\t\t%s\n",
 	if (dynar_str_catf(outbuf, "Algorithm:\t\t%s\n",
@@ -261,6 +298,7 @@ qdevice_net_ipc_cmd_status(struct qdevice_net_instance *instance, struct dynar *
 	if (qdevice_net_ipc_cmd_status_add_header(outbuf, verbose) &&
 	if (qdevice_net_ipc_cmd_status_add_header(outbuf, verbose) &&
 	    qdevice_net_ipc_cmd_status_add_basic_info(instance, outbuf, verbose) &&
 	    qdevice_net_ipc_cmd_status_add_basic_info(instance, outbuf, verbose) &&
 	    qdevice_net_ipc_cmd_status_add_tie_breaker(instance, outbuf, verbose) &&
 	    qdevice_net_ipc_cmd_status_add_tie_breaker(instance, outbuf, verbose) &&
+	    qdevice_net_ipc_cmd_status_add_kap_tb_info(instance, outbuf, verbose) &&
 	    qdevice_net_ipc_cmd_status_add_poll_timer_status(instance, outbuf, verbose) &&
 	    qdevice_net_ipc_cmd_status_add_poll_timer_status(instance, outbuf, verbose) &&
 	    qdevice_net_ipc_cmd_status_add_state(instance, outbuf, verbose) &&
 	    qdevice_net_ipc_cmd_status_add_state(instance, outbuf, verbose) &&
 	    qdevice_net_ipc_cmd_status_add_heuristics(instance, outbuf, verbose) &&
 	    qdevice_net_ipc_cmd_status_add_heuristics(instance, outbuf, verbose) &&

+ 17 - 6
qdevices/qdevice-net-msg-received.c

@@ -348,18 +348,22 @@ qdevice_net_msg_received_init_reply(struct qdevice_net_instance *instance,
 	}
 	}
 
 
 	/*
 	/*
-	 * Check if server supports heuristics
+	 * Check if server supports heuristics/keep active partition tie breaker
 	 */
 	 */
-	res = 0;
+	instance->server_supports_heuristics = 0;
+	instance->server_supports_keep_active_partition_tie_breaker = 0;
+
 	for (zi = 0; zi < msg->no_supported_options; zi++) {
 	for (zi = 0; zi < msg->no_supported_options; zi++) {
 		if (msg->supported_options[zi] == TLV_OPT_HEURISTICS) {
 		if (msg->supported_options[zi] == TLV_OPT_HEURISTICS) {
-			res = 1;
+			instance->server_supports_heuristics = 1;
 		}
 		}
-	}
 
 
-	instance->server_supports_heuristics = res;
+		if (msg->supported_options[zi] == TLV_OPT_KEEP_ACTIVE_PARTITION_TIE_BREAKER) {
+			instance->server_supports_keep_active_partition_tie_breaker = 1;
+		}
+	}
 
 
-	if (!res) {
+	if (!instance->server_supports_heuristics) {
 		active_heuristics_mode = instance->qdevice_instance_ptr->heuristics_instance.mode;
 		active_heuristics_mode = instance->qdevice_instance_ptr->heuristics_instance.mode;
 
 
 		if (active_heuristics_mode == QDEVICE_HEURISTICS_MODE_ENABLED ||
 		if (active_heuristics_mode == QDEVICE_HEURISTICS_MODE_ENABLED ||
@@ -451,6 +455,13 @@ qdevice_net_msg_received_set_option_reply(struct qdevice_net_instance *instance,
 		return (-1);
 		return (-1);
 	}
 	}
 
 
+	log(LOG_DEBUG, "Received set option reply seq(%u) = "UTILS_PRI_MSG_SEQ ", "
+	    "HB(%u) = %" PRIu32 "ms, KAP Tie-breaker(%u) = %s",
+	    msg->seq_number_set, msg->seq_number,
+	    msg->heartbeat_interval_set, msg->heartbeat_interval,
+	    msg->keep_active_partition_tie_breaker_set,
+	    tlv_keep_active_partition_tie_breaker_to_str(msg->keep_active_partition_tie_breaker));
+
 	if (msg->heartbeat_interval_set) {
 	if (msg->heartbeat_interval_set) {
 		if (qdevice_net_echo_request_timer_schedule(instance) != 0) {
 		if (qdevice_net_echo_request_timer_schedule(instance) != 0) {
 			return (-1);
 			return (-1);

+ 9 - 4
qdevices/qdevice-net-send.c

@@ -355,7 +355,9 @@ qdevice_net_send_quorum_node_list(struct qdevice_net_instance *instance,
 
 
 int
 int
 qdevice_net_send_set_option(struct qdevice_net_instance *instance,
 qdevice_net_send_set_option(struct qdevice_net_instance *instance,
-    int add_heartbeat_interval, uint32_t heartbeat_interval)
+    int add_heartbeat_interval, uint32_t heartbeat_interval,
+    int add_keep_active_partition_tie_breaker,
+    enum tlv_keep_active_partition_tie_breaker keep_active_partition_tie_breaker)
 {
 {
 	struct send_buffer_list_entry *send_buffer;
 	struct send_buffer_list_entry *send_buffer;
 
 
@@ -369,11 +371,14 @@ qdevice_net_send_set_option(struct qdevice_net_instance *instance,
 	instance->last_msg_seq_num++;
 	instance->last_msg_seq_num++;
 
 
 	log(LOG_DEBUG, "Sending set option seq = "UTILS_PRI_MSG_SEQ ", "
 	log(LOG_DEBUG, "Sending set option seq = "UTILS_PRI_MSG_SEQ ", "
-	    "hb(%u) = %" PRIu32,
-	    instance->last_msg_seq_num, add_heartbeat_interval, heartbeat_interval);
+	    "HB(%u) = %" PRIu32 "ms, KAP Tie-breaker(%u) = %s",
+	    instance->last_msg_seq_num, add_heartbeat_interval, heartbeat_interval,
+	    add_keep_active_partition_tie_breaker,
+	    tlv_keep_active_partition_tie_breaker_to_str(keep_active_partition_tie_breaker));
 
 
 	if (msg_create_set_option(&send_buffer->buffer, 1, instance->last_msg_seq_num,
 	if (msg_create_set_option(&send_buffer->buffer, 1, instance->last_msg_seq_num,
-	    add_heartbeat_interval, heartbeat_interval) == 0) {
+	    add_heartbeat_interval, heartbeat_interval,
+	    add_keep_active_partition_tie_breaker, keep_active_partition_tie_breaker) == 0) {
 		log(LOG_ERR, "Can't allocate send buffer for set option msg");
 		log(LOG_ERR, "Can't allocate send buffer for set option msg");
 
 
 		send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer);
 		send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer);

+ 3 - 1
qdevices/qdevice-net-send.h

@@ -67,7 +67,9 @@ extern int		qdevice_net_send_quorum_node_list(
     uint32_t node_list_entries, votequorum_node_t node_list[]);
     uint32_t node_list_entries, votequorum_node_t node_list[]);
 
 
 extern int		qdevice_net_send_set_option(struct qdevice_net_instance *instance,
 extern int		qdevice_net_send_set_option(struct qdevice_net_instance *instance,
-    int add_heartbeat_interval, uint32_t heartbeat_interval);
+    int add_heartbeat_interval, uint32_t heartbeat_interval,
+    int add_keep_active_partition_tie_breaker,
+    enum tlv_keep_active_partition_tie_breaker keep_active_partition_tie_breaker);
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }

+ 3 - 1
qdevices/qnet-config.h

@@ -85,7 +85,7 @@ extern "C" {
 #define QNETD_DEFAULT_IPC_MAX_SEND_SIZE			(10*1024*1024)
 #define QNETD_DEFAULT_IPC_MAX_SEND_SIZE			(10*1024*1024)
 #define QNETD_MIN_IPC_RECEIVE_SEND_SIZE			1024
 #define QNETD_MIN_IPC_RECEIVE_SEND_SIZE			1024
 
 
-#define QNETD_DEFAULT_KEEP_ACTIVE_PARTITION_TIE_BREAKER	0
+#define QNETD_DEFAULT_KEEP_ACTIVE_PARTITION_TB		TLV_KEEP_ACTIVE_PARTITION_TIE_BREAKER_DISABLED
 
 
 #define QNETD_TOOL_PROGRAM_NAME				"corosync-qnetd-tool"
 #define QNETD_TOOL_PROGRAM_NAME				"corosync-qnetd-tool"
 
 
@@ -118,6 +118,8 @@ extern "C" {
 #define QDEVICE_NET_DEFAULT_MAX_CONNECT_TIMEOUT		(2*60*1000)
 #define QDEVICE_NET_DEFAULT_MAX_CONNECT_TIMEOUT		(2*60*1000)
 #define QDEVICE_NET_MIN_CONNECT_TIMEOUT			1
 #define QDEVICE_NET_MIN_CONNECT_TIMEOUT			1
 
 
+#define QDEVICE_NET_DEFAULT_KEEP_ACTIVE_PARTITION_TB	TLV_KEEP_ACTIVE_PARTITION_TIE_BREAKER_ENABLED
+
 #ifdef DEBUG
 #ifdef DEBUG
 #define QDEVICE_NET_DEFAULT_TEST_ALGORITHM_ENABLED	1
 #define QDEVICE_NET_DEFAULT_TEST_ALGORITHM_ENABLED	1
 #else
 #else

+ 1 - 1
qdevices/qnetd-advanced-settings.c

@@ -75,7 +75,7 @@ qnetd_advanced_settings_init(struct qnetd_advanced_settings *settings)
 	settings->ipc_max_receive_size = QNETD_DEFAULT_IPC_MAX_RECEIVE_SIZE;
 	settings->ipc_max_receive_size = QNETD_DEFAULT_IPC_MAX_RECEIVE_SIZE;
 	settings->ipc_max_send_size = QNETD_DEFAULT_IPC_MAX_SEND_SIZE;
 	settings->ipc_max_send_size = QNETD_DEFAULT_IPC_MAX_SEND_SIZE;
 
 
-	settings->keep_active_partition_tie_breaker = QNETD_DEFAULT_KEEP_ACTIVE_PARTITION_TIE_BREAKER;
+	settings->keep_active_partition_tie_breaker = QNETD_DEFAULT_KEEP_ACTIVE_PARTITION_TB;
 
 
 	return (0);
 	return (0);
 }
 }

+ 1 - 1
qdevices/qnetd-advanced-settings.h

@@ -56,7 +56,7 @@ struct qnetd_advanced_settings {
 	size_t ipc_max_clients;
 	size_t ipc_max_clients;
 	size_t ipc_max_send_size;
 	size_t ipc_max_send_size;
 	size_t ipc_max_receive_size;
 	size_t ipc_max_receive_size;
-	uint8_t keep_active_partition_tie_breaker;
+	enum tlv_keep_active_partition_tie_breaker keep_active_partition_tie_breaker;
 };
 };
 
 
 extern int		qnetd_advanced_settings_init(struct qnetd_advanced_settings *settings);
 extern int		qnetd_advanced_settings_init(struct qnetd_advanced_settings *settings);

+ 7 - 1
qdevices/qnetd-client-msg-received.c

@@ -571,6 +571,11 @@ qnetd_client_msg_received_set_option(struct qnetd_instance *instance, struct qne
 		client->heartbeat_interval = msg->heartbeat_interval;
 		client->heartbeat_interval = msg->heartbeat_interval;
 	}
 	}
 
 
+	if (msg->keep_active_partition_tie_breaker_set) {
+		client->keep_active_partition_tie_breaker =
+		    msg->keep_active_partition_tie_breaker;
+	}
+
 	send_buffer = send_buffer_list_get_new(&client->send_buffer_list);
 	send_buffer = send_buffer_list_get_new(&client->send_buffer_list);
 	if (send_buffer == NULL) {
 	if (send_buffer == NULL) {
 		log(LOG_ERR, "Can't alloc set option reply msg from list. "
 		log(LOG_ERR, "Can't alloc set option reply msg from list. "
@@ -580,7 +585,8 @@ qnetd_client_msg_received_set_option(struct qnetd_instance *instance, struct qne
 	}
 	}
 
 
 	if (msg_create_set_option_reply(&send_buffer->buffer, msg->seq_number_set, msg->seq_number,
 	if (msg_create_set_option_reply(&send_buffer->buffer, msg->seq_number_set, msg->seq_number,
-	    msg->heartbeat_interval_set, client->heartbeat_interval) == 0) {
+	    msg->heartbeat_interval_set, client->heartbeat_interval,
+	    msg->keep_active_partition_tie_breaker_set, client->keep_active_partition_tie_breaker) == 0) {
 		log(LOG_ERR, "Can't alloc set option reply msg. "
 		log(LOG_ERR, "Can't alloc set option reply msg. "
 		    "Disconnecting client connection.");
 		    "Disconnecting client connection.");
 
 

+ 1 - 1
qdevices/qnetd-client.h

@@ -90,7 +90,7 @@ struct qnetd_client {
 	enum tlv_heuristics last_membership_heuristics; /* Passed in membership node list */
 	enum tlv_heuristics last_membership_heuristics; /* Passed in membership node list */
 	enum tlv_heuristics last_regular_heuristics; /* Passed in heuristics change callback */
 	enum tlv_heuristics last_regular_heuristics; /* Passed in heuristics change callback */
 	enum tlv_heuristics last_heuristics; /* Latest heuristics both membership and regular */
 	enum tlv_heuristics last_heuristics; /* Latest heuristics both membership and regular */
-	uint8_t keep_active_partition_tie_breaker;
+	enum tlv_keep_active_partition_tie_breaker keep_active_partition_tie_breaker;
 	TAILQ_ENTRY(qnetd_client) entries;
 	TAILQ_ENTRY(qnetd_client) entries;
 	TAILQ_ENTRY(qnetd_client) cluster_entries;
 	TAILQ_ENTRY(qnetd_client) cluster_entries;
 };
 };

+ 30 - 3
qdevices/qnetd-ipc-cmd.c

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2015-2019 Red Hat, Inc.
+ * Copyright (c) 2015-2020 Red Hat, Inc.
  *
  *
  * All rights reserved.
  * All rights reserved.
  *
  *
@@ -258,6 +258,26 @@ qnetd_ipc_cmd_list_add_client_info(const struct qnetd_client *client, struct dyn
 	return (0);
 	return (0);
 }
 }
 
 
+static
+int qnetd_ipc_cmd_keep_active_partition_tie_breaker_active(const struct qnetd_cluster *cluster)
+{
+	struct qnetd_client *client;
+
+	/*
+	 * Check all clients in the cluster are configured with ffsplit algorithm and have
+	 * keep active partition tie breaker enabled.
+	 */
+	TAILQ_FOREACH(client, &cluster->client_list, cluster_entries) {
+		if (client->decision_algorithm != TLV_DECISION_ALGORITHM_TYPE_FFSPLIT ||
+		    client->keep_active_partition_tie_breaker !=
+		    TLV_KEEP_ACTIVE_PARTITION_TIE_BREAKER_ENABLED) {
+			return (0);
+		}
+	}
+
+	return (1);
+}
+
 int
 int
 qnetd_ipc_cmd_list(struct qnetd_instance *instance, struct dynar *outbuf, int verbose,
 qnetd_ipc_cmd_list(struct qnetd_instance *instance, struct dynar *outbuf, int verbose,
     const char *cluster_name)
     const char *cluster_name)
@@ -265,6 +285,7 @@ qnetd_ipc_cmd_list(struct qnetd_instance *instance, struct dynar *outbuf, int ve
 	struct qnetd_cluster *cluster;
 	struct qnetd_cluster *cluster;
 	struct qnetd_client *client;
 	struct qnetd_client *client;
 	size_t cluster_no, client_no;
 	size_t cluster_no, client_no;
+	const char *kap_tb_str;		/* Keep active partition tie breaker string */
 
 
 	cluster_no = 0;
 	cluster_no = 0;
 	TAILQ_FOREACH(cluster, &instance->clusters, entries) {
 	TAILQ_FOREACH(cluster, &instance->clusters, entries) {
@@ -281,9 +302,15 @@ qnetd_ipc_cmd_list(struct qnetd_instance *instance, struct dynar *outbuf, int ve
 
 
 		TAILQ_FOREACH(client, &cluster->client_list, cluster_entries) {
 		TAILQ_FOREACH(client, &cluster->client_list, cluster_entries) {
 			if (client_no == 0) {
 			if (client_no == 0) {
-				if (dynar_str_catf(outbuf, "    Algorithm:\t\t%s\n",
+				kap_tb_str = "";
+				if (qnetd_ipc_cmd_keep_active_partition_tie_breaker_active(cluster)) {
+					kap_tb_str = " (KAP Tie-breaker)";
+				}
+
+				if (dynar_str_catf(outbuf, "    Algorithm:\t\t%s%s\n",
 				    tlv_decision_algorithm_type_to_str(
 				    tlv_decision_algorithm_type_to_str(
-				    client->decision_algorithm)) == -1) {
+				    client->decision_algorithm),
+				    kap_tb_str) == -1) {
 					return (-1);
 					return (-1);
 				}
 				}
 
 

+ 46 - 2
qdevices/tlv.c

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2015-2017 Red Hat, Inc.
+ * Copyright (c) 2015-2020 Red Hat, Inc.
  *
  *
  * All rights reserved.
  * All rights reserved.
  *
  *
@@ -56,7 +56,7 @@
 #define TLV_TYPE_LENGTH		2
 #define TLV_TYPE_LENGTH		2
 #define TLV_LENGTH_LENGTH	2
 #define TLV_LENGTH_LENGTH	2
 
 
-#define TLV_STATIC_SUPPORTED_OPTIONS_SIZE	23
+#define TLV_STATIC_SUPPORTED_OPTIONS_SIZE	24
 
 
 enum tlv_opt_type tlv_static_supported_options[TLV_STATIC_SUPPORTED_OPTIONS_SIZE] = {
 enum tlv_opt_type tlv_static_supported_options[TLV_STATIC_SUPPORTED_OPTIONS_SIZE] = {
     TLV_OPT_MSG_SEQ_NUMBER,
     TLV_OPT_MSG_SEQ_NUMBER,
@@ -82,6 +82,7 @@ enum tlv_opt_type tlv_static_supported_options[TLV_STATIC_SUPPORTED_OPTIONS_SIZE
     TLV_OPT_QUORATE,
     TLV_OPT_QUORATE,
     TLV_OPT_TIE_BREAKER,
     TLV_OPT_TIE_BREAKER,
     TLV_OPT_HEURISTICS,
     TLV_OPT_HEURISTICS,
+    TLV_OPT_KEEP_ACTIVE_PARTITION_TIE_BREAKER,
 };
 };
 
 
 int
 int
@@ -419,6 +420,14 @@ tlv_add_heuristics(struct dynar *msg, enum tlv_heuristics heuristics)
 	return (tlv_add_u8(msg, TLV_OPT_HEURISTICS, heuristics));
 	return (tlv_add_u8(msg, TLV_OPT_HEURISTICS, heuristics));
 }
 }
 
 
+int
+tlv_add_keep_active_partition_tie_breaker(struct dynar *msg,
+    enum tlv_keep_active_partition_tie_breaker enabled)
+{
+
+	return (tlv_add_u8(msg, TLV_OPT_KEEP_ACTIVE_PARTITION_TIE_BREAKER, enabled));
+}
+
 void
 void
 tlv_iter_init_str(const char *msg, size_t msg_len, size_t msg_header_len,
 tlv_iter_init_str(const char *msg, size_t msg_len, size_t msg_header_len,
     struct tlv_iterator *tlv_iter)
     struct tlv_iterator *tlv_iter)
@@ -988,6 +997,29 @@ tlv_iter_decode_heuristics(struct tlv_iterator *tlv_iter, enum tlv_heuristics *h
 	return (0);
 	return (0);
 }
 }
 
 
+int
+tlv_iter_decode_keep_active_partition_tie_breaker(struct tlv_iterator *tlv_iter,
+    enum tlv_keep_active_partition_tie_breaker *keep_active_partition_tie_breaker)
+{
+	uint8_t u8;
+	enum tlv_keep_active_partition_tie_breaker tmp_keep_active_partition_tb;
+
+	if (tlv_iter_decode_u8(tlv_iter, &u8) != 0) {
+		return (-1);
+	}
+
+	tmp_keep_active_partition_tb = u8;
+
+	if (tmp_keep_active_partition_tb != TLV_KEEP_ACTIVE_PARTITION_TIE_BREAKER_DISABLED &&
+	    tmp_keep_active_partition_tb != TLV_KEEP_ACTIVE_PARTITION_TIE_BREAKER_ENABLED) {
+		return (-4);
+	}
+
+	*keep_active_partition_tie_breaker = tmp_keep_active_partition_tb;
+
+	return (0);
+}
+
 void
 void
 tlv_get_supported_options(enum tlv_opt_type **supported_options, size_t *no_supported_options)
 tlv_get_supported_options(enum tlv_opt_type **supported_options, size_t *no_supported_options)
 {
 {
@@ -1119,3 +1151,15 @@ tlv_heuristics_cmp(enum tlv_heuristics h1, enum tlv_heuristics h2)
 
 
 	return (res);
 	return (res);
 }
 }
+
+const char *
+tlv_keep_active_partition_tie_breaker_to_str(enum tlv_keep_active_partition_tie_breaker kap_tb)
+{
+
+	switch (kap_tb) {
+	case TLV_KEEP_ACTIVE_PARTITION_TIE_BREAKER_DISABLED: return ("Disabled"); break;
+	case TLV_KEEP_ACTIVE_PARTITION_TIE_BREAKER_ENABLED: return ("Enabled"); break;
+	}
+
+	return ("Unknown keep active partition tie breaker type");
+}

+ 17 - 1
qdevices/tlv.h

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2015-2017 Red Hat, Inc.
+ * Copyright (c) 2015-2020 Red Hat, Inc.
  *
  *
  * All rights reserved.
  * All rights reserved.
  *
  *
@@ -68,6 +68,7 @@ enum tlv_opt_type {
 	TLV_OPT_QUORATE = 20,
 	TLV_OPT_QUORATE = 20,
 	TLV_OPT_TIE_BREAKER = 21,
 	TLV_OPT_TIE_BREAKER = 21,
 	TLV_OPT_HEURISTICS = 22,
 	TLV_OPT_HEURISTICS = 22,
+	TLV_OPT_KEEP_ACTIVE_PARTITION_TIE_BREAKER = 23,
 };
 };
 
 
 enum tlv_tls_supported {
 enum tlv_tls_supported {
@@ -162,6 +163,11 @@ enum tlv_heuristics {
 	TLV_HEURISTICS_FAIL = 2,
 	TLV_HEURISTICS_FAIL = 2,
 };
 };
 
 
+enum tlv_keep_active_partition_tie_breaker {
+	TLV_KEEP_ACTIVE_PARTITION_TIE_BREAKER_DISABLED = 0,
+	TLV_KEEP_ACTIVE_PARTITION_TIE_BREAKER_ENABLED = 1,
+};
+
 struct tlv_iterator {
 struct tlv_iterator {
 	const char *msg;
 	const char *msg;
 	size_t msg_len;
 	size_t msg_len;
@@ -253,6 +259,9 @@ extern int			 tlv_add_quorate(struct dynar *msg, enum tlv_quorate quorate);
 extern int			 tlv_add_heuristics(struct dynar *msg,
 extern int			 tlv_add_heuristics(struct dynar *msg,
     enum tlv_heuristics heuristics);
     enum tlv_heuristics heuristics);
 
 
+extern int			 tlv_add_keep_active_partition_tie_breaker(struct dynar *msg,
+    enum tlv_keep_active_partition_tie_breaker enabled);
+
 extern void			 tlv_iter_init_str(const char *msg, size_t msg_len,
 extern void			 tlv_iter_init_str(const char *msg, size_t msg_len,
     size_t msg_header_len, struct tlv_iterator *tlv_iter);
     size_t msg_header_len, struct tlv_iterator *tlv_iter);
 
 
@@ -328,6 +337,10 @@ extern int			 tlv_iter_decode_quorate(struct tlv_iterator *tlv_iter,
 extern int			 tlv_iter_decode_heuristics(struct tlv_iterator *tlv_iter,
 extern int			 tlv_iter_decode_heuristics(struct tlv_iterator *tlv_iter,
     enum tlv_heuristics *heuristics);
     enum tlv_heuristics *heuristics);
 
 
+extern int			 tlv_iter_decode_keep_active_partition_tie_breaker(
+    struct tlv_iterator *tlv_iter,
+    enum tlv_keep_active_partition_tie_breaker *keep_active_partition_tie_breaker);
+
 extern void			 tlv_get_supported_options(enum tlv_opt_type **supported_options,
 extern void			 tlv_get_supported_options(enum tlv_opt_type **supported_options,
     size_t *no_supported_options);
     size_t *no_supported_options);
 
 
@@ -348,6 +361,9 @@ extern const char		*tlv_decision_algorithm_type_to_str(
 
 
 extern const char		*tlv_heuristics_to_str(enum tlv_heuristics heuristics);
 extern const char		*tlv_heuristics_to_str(enum tlv_heuristics heuristics);
 
 
+extern const char		*tlv_keep_active_partition_tie_breaker_to_str(
+    enum tlv_keep_active_partition_tie_breaker kap_tb);
+
 /*
 /*
  * Compare h1 and h2. Return -1 if h1 < h2, 0 if h1 == h2 and 1 if h1 > h2
  * Compare h1 and h2. Return -1 if h1 < h2, 0 if h1 == h2 and 1 if h1 > h2
  */
  */