[PATCH] sanlock: host_message

David Teigland teigland at redhat.com
Wed Jan 15 17:32:46 UTC 2014


A host can send a predefined msg_num to another host.

The host messages are sent from one host to another via
a lockspace that both hosts are using.  If no lockspace
name is specified, the sanlock daemon will search for a
common lockspace to use.  (N.B. hosts do not necessarily
use the same host_id in all lockspaces, so not specifying
the lockspace could result in targeting the wrong host.)
The lockspace used to transmit the message may or may not
have any other relation to the message itself.

A host can send one message to a one other host at a time.
The message is placed in the sending host's delta lease,
and remains there for two renewals.  When the receiving
host renews its own delta lease, it checks the delta leases
of all other hosts, and sees itself addressed in the sending
host's lease.  It then processes the message from the
sending host.

If a message is currently active in a lockspace, the
sending host_message call will return -EBUSY.  After two
renewals (around 40 seconds), another message may be sent.

An optional host generation can be included, in which
case the receiving host_id will accept the message only
if its current generation matches.

The single msg_num defined here is WD_RESET (1), which
means that the host receiving the message should use
its watchdog device to reset itself as soon as possible.
The WD_RESET message has no effect on any lockspaces
or resources that may exist.  Existing lockspaces and
resources continue to operate as usual until the reset.
(A watchdog reset due to "standard" lockspace failure
could in fact occur before the watchdog reset caused
by the host message.)

Because host messages may not be received if the
destination host fails, or looses storage access,
there are no guaranteed times associated with the
delivery, processing or effect of a host message.
Guaranteed times for another host being dead should
continue to be based on either acquiring a resource,
or sanlock_get_hosts().

TODO: will be adding another msg_num to cause the
destination to use /proc/sysrq-trigger to reboot itself.
(After setting up the watchdog to reset the machine in
case the sysrq mechanism fails.)  The sysrq reboot is
immediate, whereas the watchdog takes a minute to reset.

Signed-off-by: David Teigland <teigland at redhat.com>
---
 src/client.c           | 51 +++++++++++++++++++++++++++
 src/cmd.c              | 30 ++++++++++++++++
 src/delta_lease.c      | 15 +++++++-
 src/delta_lease.h      |  1 +
 src/direct.c           |  1 +
 src/lockspace.c        | 93 ++++++++++++++++++++++++++++++++++++++++++++++++--
 src/lockspace.h        |  1 +
 src/main.c             | 23 +++++++++++--
 src/resource.c         | 58 +++++++++++++++++++++++++++++++
 src/resource.h         |  3 ++
 src/sanlock.8          | 15 ++++++++
 src/sanlock.h          | 10 ++++++
 src/sanlock_admin.h    | 15 ++++++++
 src/sanlock_internal.h |  9 +++--
 src/sanlock_sock.h     |  1 +
 src/watchdog.c         | 65 +++++++++++++++++++++++++++++++++++
 src/watchdog.h         |  1 +
 17 files changed, 383 insertions(+), 9 deletions(-)

diff --git a/src/client.c b/src/client.c
index 14c70d1788d4..87100e59cf5b 100644
--- a/src/client.c
+++ b/src/client.c
@@ -701,6 +701,57 @@ int sanlock_test_resource_owners(struct sanlk_resource *res GNUC_UNUSED,
 	return 0;
 }
 
+int sanlock_host_message(const char *ls_name, uint32_t flags,
+			 int hm_size, struct sanlk_host_message *hm)
+{
+	struct sanlk_lockspace ls;
+	struct sanlk_host_message msg;
+	int rv, fd;
+
+	if (!hm || !hm_size)
+		return -EINVAL;
+
+	memset(&ls, 0, sizeof(struct sanlk_lockspace));
+	if (ls_name)
+		strncpy(ls.name, ls_name, SANLK_NAME_LEN);
+
+	rv = connect_socket(&fd);
+	if (rv < 0)
+		return rv;
+
+	memset(&msg, 0, sizeof(msg));
+
+	if (hm_size < sizeof(msg))
+		memcpy(&msg, hm, hm_size);
+	else if (hm_size > sizeof(msg))
+		memcpy(&msg, hm, sizeof(msg));
+	else
+		memcpy(&msg, hm, sizeof(msg));
+
+	rv = send_header(fd, SM_CMD_HOST_MESSAGE, flags,
+			 sizeof(struct sanlk_lockspace) + sizeof(struct sanlk_host_message),
+			 0, 0);
+	if (rv < 0)
+		goto out;
+
+	rv = send(fd, &ls, sizeof(struct sanlk_lockspace), 0);
+	if (rv < 0) {
+		rv = -errno;
+		goto out;
+	}
+
+	rv = send(fd, &msg, sizeof(struct sanlk_host_message), 0);
+	if (rv < 0) {
+		rv = -errno;
+		goto out;
+	}
+
+	rv = recv_result(fd);
+out:
+	close(fd);
+	return rv;
+}
+
 /* old api */
 int sanlock_init(struct sanlk_lockspace *ls,
 		 struct sanlk_resource *res,
diff --git a/src/cmd.c b/src/cmd.c
index 13ad405670a0..d18d8daaba41 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -2351,6 +2351,32 @@ out:
 		send(fd, send_data_buf, len, MSG_NOSIGNAL);
 }
 
+static void cmd_host_message(int fd, struct sm_header *h_recv)
+{
+	struct sanlk_lockspace lockspace;
+	struct sanlk_host_message msg;
+	int rv;
+
+	rv = recv(fd, &lockspace, sizeof(struct sanlk_lockspace), MSG_WAITALL);
+	if (rv != sizeof(struct sanlk_lockspace)) {
+		rv = -ENOTCONN;
+		goto out;
+	}
+
+	rv = recv(fd, &msg, sizeof(struct sanlk_host_message), MSG_WAITALL);
+	if (rv != sizeof(struct sanlk_host_message)) {
+		rv = -ENOTCONN;
+		goto out;
+	}
+
+	rv = host_message(&lockspace, &msg);
+out:
+	h_recv->length = sizeof(struct sm_header);
+	h_recv->data = rv;
+	h_recv->data2 = 0;
+	send(fd, h_recv, sizeof(struct sm_header), MSG_NOSIGNAL);
+}
+
 static void cmd_restrict(int ci, int fd, struct sm_header *h_recv)
 {
 	log_debug("cmd_restrict ci %d fd %d pid %d flags %x",
@@ -2445,6 +2471,10 @@ void call_cmd_daemon(int ci, struct sm_header *h_recv, int client_maxi)
 		strcpy(client[ci].owner_name, "get_hosts");
 		cmd_get_hosts(fd, h_recv);
 		break;
+	case SM_CMD_HOST_MESSAGE:
+		strcpy(client[ci].owner_name, "host_message");
+		cmd_host_message(fd, h_recv);
+		break;
 	};
 
 	if (auto_close)
diff --git a/src/delta_lease.c b/src/delta_lease.c
index 91d0a83bc811..d973094ae569 100644
--- a/src/delta_lease.c
+++ b/src/delta_lease.c
@@ -410,6 +410,7 @@ int delta_lease_renew(struct task *task,
 		      struct sync_disk *disk,
 		      char *space_name,
 		      char *bitmap,
+		      struct sanlk_host_message *message,
 		      int prev_result,
 		      int *read_result,
 		      struct leader_record *leader_last,
@@ -419,7 +420,7 @@ int delta_lease_renew(struct task *task,
 	char **p_iobuf;
 	char **p_wbuf;
 	char *wbuf;
-	uint64_t host_id, id_offset, new_ts;
+	uint64_t host_id, id_offset, new_ts, msg64;
 	int rv, iobuf_len, sector_size;
 
 	if (!leader_last) {
@@ -567,6 +568,18 @@ int delta_lease_renew(struct task *task,
 	leader.timestamp = new_ts;
 	leader.checksum = leader_checksum(&leader);
 
+	if (message && message->host_id) {
+		msg64 = message->msg_num;
+		msg64 = (msg64 << 32) | message->msg_data;
+		leader.write_id = message->host_id;
+		leader.write_generation = message->generation;
+		leader.write_timestamp = msg64;
+	} else {
+		leader.write_id = 0;
+		leader.write_generation = 0;
+		leader.write_timestamp = 0;
+	}
+
 	p_wbuf = &wbuf;
 	rv = posix_memalign((void *)p_wbuf, getpagesize(), sector_size);
 	if (rv) {
diff --git a/src/delta_lease.h b/src/delta_lease.h
index f015d1eecb23..243b22b19365 100644
--- a/src/delta_lease.h
+++ b/src/delta_lease.h
@@ -30,6 +30,7 @@ int delta_lease_renew(struct task *task,
                       struct sync_disk *disk,
                       char *space_name,
                       char *bitmap,
+		      struct sanlk_host_message *message,
                       int prev_result,
 		      int *read_result,
                       struct leader_record *leader_last,
diff --git a/src/direct.c b/src/direct.c
index ed6cca458364..3989da542877 100644
--- a/src/direct.c
+++ b/src/direct.c
@@ -240,6 +240,7 @@ static int do_delta_action(int action,
 		rv = delta_lease_renew(task, &space, &sd,
 				       ls->name,
 				       bitmap,
+				       NULL,
 				       -1,
 				       &read_result,
 				       &leader,
diff --git a/src/lockspace.c b/src/lockspace.c
index e96e1e1e7983..0dada49d9660 100644
--- a/src/lockspace.c
+++ b/src/lockspace.c
@@ -78,6 +78,20 @@ struct space *find_lockspace(const char *name)
 	return _search_space(name, NULL, 0, &spaces, &spaces_rem, &spaces_add, NULL);
 }
 
+/* find any space where host_id is a member */
+static struct space *get_hostid_space(uint64_t host_id)
+{
+	struct space *sp;
+	struct host_status *hs;
+
+	list_for_each_entry(sp, &spaces, list) {
+		hs = &sp->host_status[host_id-1];
+		if (hs->owner_id && hs->timestamp && hs->last_live)
+			return sp;
+	}
+	return NULL;
+}
+
 int _lockspace_info(const char *space_name, struct space_info *spi)
 {
 	struct space *sp;
@@ -222,7 +236,8 @@ int host_info(char *space_name, uint64_t host_id, struct host_status *hs_out)
 	return 0;
 }
 
-static void create_bitmap(struct space *sp, char *bitmap)
+static void create_bitmap_and_message(struct space *sp, char *bitmap,
+				      struct sanlk_host_message *message)
 {
 	uint64_t now;
 	int i;
@@ -247,6 +262,16 @@ static void create_bitmap(struct space *sp, char *bitmap)
 			log_space(sp, "bitmap set host_id %d byte %x", i+1, c);
 		}
 	}
+
+	if (sp->host_message.host_id) {
+		memcpy(message, &sp->host_message, sizeof(struct sanlk_host_message));
+		sp->host_message_written++;
+
+		if (sp->host_message_written >= 2) {
+			memset(&sp->host_message, 0, sizeof(struct sanlk_host_message));
+			sp->host_message_written = 0;
+		}
+	}
 	pthread_mutex_unlock(&sp->mutex);
 }
 
@@ -289,6 +314,23 @@ void check_other_leases(struct space *sp, char *buf)
 		if (i+1 == sp->host_id)
 			continue;
 
+		/*
+		 * check if we are target of message
+		 */
+
+		if (leader->write_id == sp->host_id &&
+		    leader->write_generation == sp->host_generation) {
+			log_space(sp, "message from host_id %d 0x%llx", i+1,
+				  (unsigned long long)leader->write_timestamp);
+			set_message_examine(sp->space_name, leader->owner_id,
+					    (leader->write_timestamp >> 32),
+					    (leader->write_timestamp & 0xFFFFFFFF));
+		}
+
+		/*
+		 * check if we are notified in bitmap
+		 */
+
 		bitmap = (char *)leader + HOSTID_BITMAP_OFFSET;
 
 		if (!test_id_bit(sp->host_id, bitmap))
@@ -384,6 +426,7 @@ static int corrupt_result(int result)
 
 static void *lockspace_thread(void *arg_in)
 {
+	struct sanlk_host_message message;
 	char bitmap[HOSTID_BITMAP_SIZE];
 	struct task task;
 	struct space *sp;
@@ -502,12 +545,14 @@ static void *lockspace_thread(void *arg_in)
 		 */
 
 		memset(bitmap, 0, sizeof(bitmap));
-		create_bitmap(sp, bitmap);
+		memset(&message, 0, sizeof(message));
+		create_bitmap_and_message(sp, bitmap, &message);
 
 		delta_begin = monotime();
 
 		delta_result = delta_lease_renew(&task, sp, &sp->host_id_disk,
-						 sp->space_name, bitmap,
+						 sp->space_name,
+						 bitmap, &message,
 						 delta_result, &read_result,
 						 &leader, &leader);
 		delta_length = monotime() - delta_begin;
@@ -1109,6 +1154,48 @@ int get_hosts(struct sanlk_lockspace *ls, char *buf, int *len, int *count, int m
 	return rv;
 }
 
+int host_message(struct sanlk_lockspace *ls, struct sanlk_host_message *hm)
+{
+	struct space *sp;
+	struct host_status *hs;
+
+	if (!hm->host_id)
+		return -EINVAL;
+
+	pthread_mutex_lock(&spaces_mutex);
+
+	if (!ls->name[0]) {
+		sp = get_hostid_space(hm->host_id);
+	} else {
+		sp = _search_space(ls->name, NULL, 0, &spaces, NULL, NULL, NULL);
+	}
+	if (!sp) {
+		pthread_mutex_unlock(&spaces_mutex);
+		return -ENOENT;
+	}
+
+	/* this lockspace is currently sending a message */
+	if (sp->host_message.host_id) {
+		pthread_mutex_unlock(&spaces_mutex);
+		return -EBUSY;
+	}
+
+	/* supply the current generation if caller did not specify the generation */
+	if (!hm->generation) {
+		hs = &(sp->host_status[hm->host_id-1]);
+		hm->generation = hs->owner_generation;
+	}
+
+	log_erros(sp, "set host_message %u for host %llu %llu", hm->msg_num,
+		  (unsigned long long)hm->host_id,
+		  (unsigned long long)hm->generation);
+
+	memcpy(&sp->host_message, hm, sizeof(struct sanlk_host_message));
+	pthread_mutex_unlock(&spaces_mutex);
+
+	return 0;
+}
+
 /*
  * we call stop_host_id() when all pids are gone and we're in a safe state, so
  * it's safe to unlink the watchdog right away here.  We want to sp the unlink
diff --git a/src/lockspace.h b/src/lockspace.h
index 3ffe17ffc048..0b7e22f83b94 100644
--- a/src/lockspace.h
+++ b/src/lockspace.h
@@ -27,5 +27,6 @@ int rem_lockspace_wait(struct sanlk_lockspace *ls, unsigned int space_id);
 void free_lockspaces(int wait);
 int get_lockspaces(char *buf, int *len, int *count, int maxlen);
 int get_hosts(struct sanlk_lockspace *ls, char *buf, int *len, int *count, int maxlen);
+int host_message(struct sanlk_lockspace *ls, struct sanlk_host_message *hm);
 
 #endif
diff --git a/src/main.c b/src/main.c
index 421ea670823d..b5e736744e5a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1170,6 +1170,7 @@ static void process_connection(int ci)
 	case SM_CMD_LOG_DUMP:
 	case SM_CMD_GET_LOCKSPACES:
 	case SM_CMD_GET_HOSTS:
+	case SM_CMD_HOST_MESSAGE:
 		call_cmd_daemon(ci, &h, client_maxi);
 		break;
 	case SM_CMD_ADD_LOCKSPACE:
@@ -1805,6 +1806,7 @@ static void print_usage(void)
 	printf("sanlock client status [-D] [-o p|s]\n");
 	printf("sanlock client gets [-h 0|1]\n");
 	printf("sanlock client host_status -s LOCKSPACE [-D]\n");
+	printf("sanlock client host_message -i <host_id> -n <num> [-g gen] [-m data] [-s LOCKSPACE]\n");
 	printf("sanlock client log_dump\n");
 	printf("sanlock client shutdown [-f 0|1]\n");
 	printf("sanlock client init -s LOCKSPACE | -r RESOURCE\n");
@@ -1939,6 +1941,8 @@ static int read_command_line(int argc, char *argv[])
 			com.action = ACT_CLIENT_INIT;
 		else if (!strcmp(act, "read"))
 			com.action = ACT_CLIENT_READ;
+		else if (!strcmp(act, "host_message"))
+			com.action = ACT_HOST_MESSAGE;
 		else {
 			log_tool("client action \"%s\" is unknown", act);
 			exit(EXIT_FAILURE);
@@ -2052,9 +2056,11 @@ static int read_command_line(int argc, char *argv[])
 			break;
 		case 'n':
 			com.num_hosts = atoi(optionarg);
+			com.msg_num = strtoul(optionarg, NULL, 0);
 			break;
 		case 'm':
 			com.max_hosts = atoi(optionarg);
+			com.msg_data = strtoul(optionarg, NULL, 0);
 			break;
 		case 'p':
 			com.pid = atoi(optionarg);
@@ -2063,7 +2069,7 @@ static int read_command_line(int argc, char *argv[])
 			strncpy(com.our_host_name, optionarg, NAME_ID_SIZE);
 			break;
 		case 'i':
-			com.local_host_id = atoll(optionarg);
+			com.host_id = atoll(optionarg);
 			break;
 		case 'g':
 			if (com.type == COM_DAEMON) {
@@ -2071,7 +2077,7 @@ static int read_command_line(int argc, char *argv[])
 				if (sec <= 60 && sec >= 0)
 					kill_grace_seconds = sec;
 			} else {
-				com.local_host_generation = atoll(optionarg);
+				com.host_generation = atoll(optionarg);
 			}
 			break;
 		case 'f':
@@ -2317,6 +2323,7 @@ static int do_client_read(void)
 
 static int do_client(void)
 {
+	struct sanlk_host_message hm;
 	struct sanlk_resource **res_args = NULL;
 	struct sanlk_resource *res;
 	char *res_state = NULL;
@@ -2499,6 +2506,16 @@ static int do_client(void)
 		rv = do_client_read();
 		break;
 
+	case ACT_HOST_MESSAGE:
+		log_tool("host_message %llu %u", (unsigned long long)com.host_id, com.msg_num);
+		hm.host_id = com.host_id;
+		hm.generation = com.host_generation;
+		hm.msg_num = com.msg_num;
+		hm.msg_data = com.msg_data;
+		rv = sanlock_host_message(com.lockspace.name, 0, sizeof(struct sanlk_host_message), &hm);
+		log_tool("host_message done %d", rv);
+		break;
+
 	default:
 		log_tool("action not implemented");
 		rv = -1;
@@ -2570,7 +2587,7 @@ static int do_direct(void)
 	case ACT_ACQUIRE:
 		rv = direct_acquire(&main_task, com.io_timeout_arg,
 				    com.res_args[0], com.num_hosts,
-				    com.local_host_id, com.local_host_generation,
+				    com.host_id, com.host_generation,
 				    &leader);
 		log_tool("acquire done %d", rv);
 		break;
diff --git a/src/resource.c b/src/resource.c
index 98a07114bf5c..c118fbb5ba29 100644
--- a/src/resource.c
+++ b/src/resource.c
@@ -33,6 +33,7 @@
 #include "timeouts.h"
 #include "mode_block.h"
 #include "helper.h"
+#include "watchdog.h"
 
 /* from cmd.c */
 void send_state_resource(int fd, struct resource *r, const char *list_name, int pid, uint32_t token_id);
@@ -49,6 +50,7 @@ static struct list_head resources_add;
 static struct list_head resources_rem;
 static pthread_mutex_t resource_mutex;
 static pthread_cond_t resource_cond;
+static struct list_head host_messages;
 
 
 static void free_resource(struct resource *r)
@@ -1693,6 +1695,57 @@ int set_resource_examine(char *space_name, char *res_name)
 	return count;
 }
 
+struct recv_message {
+	struct list_head list;
+	uint64_t from_host_id;
+	uint32_t msg_num;
+	uint32_t msg_data;
+};
+
+void set_message_examine(char *space_name GNUC_UNUSED, uint64_t from_host_id,
+			 uint32_t msg_num,  uint32_t msg_data)
+{
+	struct recv_message *rm;
+
+	rm = malloc(sizeof(struct recv_message));
+	if (!rm) {
+		log_error("set_message_examine no mem");
+		return;
+	}
+
+	memset(rm, 0, sizeof(struct recv_message));
+	rm->from_host_id = from_host_id;
+	rm->msg_num = msg_num;
+	rm->msg_data = msg_data;
+
+	pthread_mutex_lock(&resource_mutex);
+	list_add_tail(&rm->list, &host_messages);
+	resource_thread_work = 1;
+	pthread_cond_signal(&resource_cond);
+	pthread_mutex_unlock(&resource_mutex);
+}
+
+static void resource_thread_messages(void)
+{
+	struct recv_message *rm, *safe;
+
+	list_for_each_entry_safe(rm, safe, &host_messages, list) {
+		list_del(&rm->list);
+
+		log_debug("message %u %u from %llu",
+			  rm->msg_num, rm->msg_data,
+			  (unsigned long long)rm->from_host_id);
+
+		if (rm->msg_num == SANLK_HM_WD_RESET) {
+			log_error("Host reset request from host_id %llu",
+				  (unsigned long long)rm->from_host_id);
+			watchdog_reset_host();
+		}
+
+		free(rm);
+	}
+}
+
 /*
  * resource_thread
  * - on-disk lease release for pid's that exit without doing release
@@ -1896,6 +1949,10 @@ static void *resource_thread(void *arg GNUC_UNUSED)
 			pthread_cond_wait(&resource_cond, &resource_mutex);
 		}
 
+		/* process any host messages that have been received */
+		resource_thread_messages();
+
+
 		/* FIXME: it's not nice how we copy a bunch of stuff
 		 * from token to r so that we can later copy it back from
 		 * r into a temp token.  The whole duplication of stuff
@@ -1994,6 +2051,7 @@ int setup_token_manager(void)
 	INIT_LIST_HEAD(&resources_add);
 	INIT_LIST_HEAD(&resources_rem);
 	INIT_LIST_HEAD(&resources_held);
+	INIT_LIST_HEAD(&host_messages);
 
 	rv = pthread_create(&resource_pt, NULL, resource_thread, NULL);
 	if (rv)
diff --git a/src/resource.h b/src/resource.h
index e5c7d4184646..a062df712ccf 100644
--- a/src/resource.h
+++ b/src/resource.h
@@ -27,6 +27,9 @@ int request_token(struct task *task, struct token *token, uint32_t force_mode,
 
 int set_resource_examine(char *space_name, char *res_name);
 
+void set_message_examine(char *space_name, uint64_t from_host_id,
+			 uint32_t msg_num,  uint32_t msg_data);
+
 int res_set_lvb(struct sanlk_resource *res, char *lvb, int lvblen);
 int res_get_lvb(struct sanlk_resource *res, char **lvb_out, int *lvblen);
 
diff --git a/src/sanlock.8 b/src/sanlock.8
index 0d2b63ca60c3..08e6771e12e3 100644
--- a/src/sanlock.8
+++ b/src/sanlock.8
@@ -372,6 +372,21 @@ out the action specified by the requested force_mode.
 Examine requests for all resource leases currently held in the named
 lockspace.  Only lockspace_name is used from the LOCKSPACE argument.
 
+.BR "sanlock client host_message -s" " LOCKSPACE " \
+\fB-i\fP " " \fIhost_id\fP " " \
+\fB-n\fP " " \fImsg_num\fP
+
+Tell the sanlock daemon to send a msg_num type message to a host_id.
+Both hosts must be members of LOCKSPACE (only the name is used,
+the host_id in LOCKSPACE is unused).
+If no lockspace name is provided, any lockspace will
+be chosen that contains the host_id (N.B if hosts do not use the same
+id in all lockspaces, this could target the wrong host.)
+If an optional "-g host_generation" is specified, the message will only
+apply if the target host has the specified generation.
+.br
+msg_num 1: the host should use its watchdog to reset itself.
+
 .B "sanlock direct"
 .I action
 [options]
diff --git a/src/sanlock.h b/src/sanlock.h
index 58779b128e3f..b3f86101ccf9 100644
--- a/src/sanlock.h
+++ b/src/sanlock.h
@@ -101,6 +101,16 @@ struct sanlk_host {
 	uint32_t flags;
 };
 
+
+#define SANLK_HM_WD_RESET 0x00000001
+
+struct sanlk_host_message {
+	uint64_t host_id;
+	uint64_t generation;
+	uint32_t msg_num;
+	uint32_t msg_data;
+};
+
 size_t sanlock_path_export(char *dst, const char *src, size_t dstlen);
 size_t sanlock_path_import(char *dst, const char *src, size_t dstlen);
 
diff --git a/src/sanlock_admin.h b/src/sanlock_admin.h
index 1a781b1bda71..0e2671374469 100644
--- a/src/sanlock_admin.h
+++ b/src/sanlock_admin.h
@@ -276,4 +276,19 @@ int sanlock_test_resource_owners(struct sanlk_resource *res, uint32_t flags,
 				 struct sanlk_host *hosts, int hosts_count,
 				 uint32_t *test_flags);
 
+/*
+ * Send a predefined message to another host.
+ *
+ * If ls is NULL, then a suitable lockspace is chosen
+ * that both src and dst hosts are members of.
+ *
+ * msg_num values:
+ *
+ * WD_RESET: the host should use its watchdog device
+ * to reset itself as soon as possible.
+ */
+
+int sanlock_host_message(const char *ls_name, uint32_t flags,
+			 int len, struct sanlk_host_message *hm);
+
 #endif
diff --git a/src/sanlock_internal.h b/src/sanlock_internal.h
index a448a5cc79a3..4cceff87eaab 100644
--- a/src/sanlock_internal.h
+++ b/src/sanlock_internal.h
@@ -169,6 +169,8 @@ struct space {
 	pthread_mutex_t mutex; /* protects lease_status, thread_stop  */
 	struct lease_status lease_status;
 	struct host_status host_status[DEFAULT_MAX_HOSTS];
+	struct sanlk_host_message host_message;
+	int host_message_written;
 };
 
 /* Update lockspace_info() to copy any fields from struct space
@@ -278,13 +280,15 @@ struct command_line {
 	int gid;				/* -G */
 	int pid;				/* -p */
 	char sort_arg;
-	uint64_t local_host_id;			/* -i */
-	uint64_t local_host_generation;		/* -g */
+	uint64_t host_id;			/* -i */
+	uint64_t host_generation;		/* -g */
 	int num_hosts;				/* -n */
 	int max_hosts;				/* -m */
 	int res_count;
 	int sh_retries;
 	uint32_t force_mode;
+	uint32_t msg_num;  /* -n */
+	uint32_t msg_data; /* -m */
 	char our_host_name[SANLK_NAME_LEN+1];
 	char *dump_path;
 	struct sanlk_lockspace lockspace;	/* -s LOCKSPACE */
@@ -325,6 +329,7 @@ enum {
 	ACT_CLIENT_ALIGN,
 	ACT_EXAMINE,
 	ACT_GETS,
+	ACT_HOST_MESSAGE,
 };
 
 EXTERN int external_shutdown;
diff --git a/src/sanlock_sock.h b/src/sanlock_sock.h
index b2f73047e1b1..0fb24d466f8d 100644
--- a/src/sanlock_sock.h
+++ b/src/sanlock_sock.h
@@ -45,6 +45,7 @@ enum {
 	SM_CMD_SET_LVB		 = 25,
 	SM_CMD_GET_LVB		 = 26,
 	SM_CMD_CONVERT		 = 27,
+	SM_CMD_HOST_MESSAGE      = 28,
 };
 
 struct sm_header {
diff --git a/src/watchdog.c b/src/watchdog.c
index a8798802d75f..df569dbff57d 100644
--- a/src/watchdog.c
+++ b/src/watchdog.c
@@ -28,6 +28,8 @@
 #include "log.h"
 #include "watchdog.h"
 
+static int watchdog_reset_con;
+
 /*
  * Purpose of watchdog: to forcibly reset the host in the case where a
  * supervised pid is running but sanlock daemon does not renew its lease
@@ -152,3 +154,66 @@ void close_watchdog_file(struct space *sp)
 	close(sp->wd_fd);
 }
 
+/*
+ * Use the watchdog to reset the host as soon as possible.
+ * Intentionally set the expire time on the connection to
+ * the current time so that the watchdog will expire and
+ * reset as soon as possible.
+ */
+
+void watchdog_reset_host(void)
+{
+	char name[WDMD_NAME_SIZE];
+	uint64_t now;
+	int con, rv;
+
+	if (!com.use_watchdog)
+		return;
+
+	if (watchdog_reset_con) {
+		log_debug("watchdog_reset_host already active");
+		return;
+	}
+
+	con = wdmd_connect();
+	if (con < 0) {
+		log_error("watchdog_reset_host connect failed %d", con);
+		return;
+	}
+
+	memset(name, 0, sizeof(name));
+
+	snprintf(name, WDMD_NAME_SIZE - 1, "sanlock_reset_host");
+
+	rv = wdmd_register(con, name);
+	if (rv < 0) {
+		log_error("watchdog_reset_host register failed %d", rv);
+		goto fail_close;
+	}
+
+	/* the refcount tells wdmd that it should not cleanly exit */
+
+	rv = wdmd_refcount_set(con);
+	if (rv < 0) {
+		log_error("watchdog_reset_host refcount_set failed %d", rv);
+		goto fail_close;
+	}
+
+	now = monotime();
+
+	rv = wdmd_test_live(con, now, now);
+	if (rv < 0) {
+		log_error("watchdog_reset_host test_live failed %d", rv);
+		goto fail_clear;
+	}
+
+	log_error("Resetting host with watchdog");
+	watchdog_reset_con = con;
+	return;
+
+ fail_clear:
+	wdmd_refcount_clear(con);
+ fail_close:
+	close(con);
+}
+
diff --git a/src/watchdog.h b/src/watchdog.h
index 90e82eb0a44e..794ec30ff9ed 100644
--- a/src/watchdog.h
+++ b/src/watchdog.h
@@ -15,5 +15,6 @@ int create_watchdog_file(struct space *sp, uint64_t timestamp,
 			 int id_renewal_fail_seconds);
 void unlink_watchdog_file(struct space *sp);
 void close_watchdog_file(struct space *sp);
+void watchdog_reset_host(void);
 
 #endif
-- 
1.8.3.1



More information about the sanlock-devel mailing list