Blame SOURCES/0005-Implement-Netlink-helper-functions-to-subscribe-ther.patch

9425f0
From c65cda183609116760c54f1c2ad458eef9f44d56 Mon Sep 17 00:00:00 2001
9425f0
From: "Chang S. Bae" <chang.seok.bae@intel.com>
9425f0
Date: Fri, 11 Feb 2022 12:27:00 -0800
9425f0
Subject: [PATCH 05/14] Implement Netlink helper functions to subscribe thermal
9425f0
 events
9425f0
9425f0
Establish Netlink socket connection in a nonblocking mode and callback
9425f0
notification.
9425f0
9425f0
Tolerate a few times on failures from receiving events. In practice, such
9425f0
delivery failure is rare. But it may indicate more fundamental issues if
9425f0
it repeats. So disconnect it unless the failure is transient.
9425f0
9425f0
Event-specific handler will be implemented in the next patch.
9425f0
9425f0
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
9425f0
---
9425f0
 thermal.c | 327 +++++++++++++++++++++++++++++++++++++++++++++++++++---
9425f0
 1 file changed, 313 insertions(+), 14 deletions(-)
9425f0
9425f0
diff --git a/thermal.c b/thermal.c
9425f0
index 308bc48..16a6f39 100644
9425f0
--- a/thermal.c
9425f0
+++ b/thermal.c
9425f0
@@ -14,43 +14,342 @@
9425f0
 
9425f0
 cpumask_t thermal_banned_cpus;
9425f0
 
9425f0
+#define INVALID_NL_FD	-1
9425f0
+#define MAX_RECV_ERRS	2
9425f0
+
9425f0
+#define THERMAL_GENL_FAMILY_NAME	"thermal"
9425f0
+#define THERMAL_GENL_EVENT_GROUP_NAME	"event"
9425f0
+#define NL_FAMILY_NAME			"nlctrl"
9425f0
+
9425f0
+struct family_data {
9425f0
+	const char *group;
9425f0
+	int id;
9425f0
+};
9425f0
+
9425f0
+static struct nl_sock *sock;
9425f0
+static struct nl_cb *callback;
9425f0
+
9425f0
+/*
9425f0
+ * return value: TRUE with an error; otherwise, FALSE
9425f0
+ */
9425f0
 static gboolean prepare_netlink(void)
9425f0
 {
9425f0
-	gboolean error = TRUE;
9425f0
+	int rc;
9425f0
 
9425f0
-	log(TO_ALL, LOG_ERR, "thermal: not yet implement to alloc memory for netlink.\n");
9425f0
-	return error;
9425f0
+	sock = nl_socket_alloc();
9425f0
+	if (!sock) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: socket allocation failed.\n");
9425f0
+		return TRUE;
9425f0
+	}
9425f0
+
9425f0
+	rc = genl_connect(sock);
9425f0
+	if (rc) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: socket bind failed.\n");
9425f0
+		return TRUE;
9425f0
+	}
9425f0
+
9425f0
+	callback = nl_cb_alloc(NL_CB_DEFAULT);
9425f0
+	if (!callback) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: callback allocation failed.\n");
9425f0
+		return TRUE;
9425f0
+	}
9425f0
+
9425f0
+	return FALSE;
9425f0
 }
9425f0
 
9425f0
-#define NL_FAMILY_NAME	"nlctrl"
9425f0
+static int handle_groupid(struct nl_msg *msg, void *arg)
9425f0
+{
9425f0
+	struct nlattr *attrhdr, *mcgrp, *cur_mcgrp;
9425f0
+	struct nlattr *attrs[CTRL_ATTR_MAX + 1];
9425f0
+	struct nla_policy *policy = NULL;
9425f0
+	struct genlmsghdr *gnlhdr = NULL;
9425f0
+	struct family_data *data = NULL;
9425f0
+	struct nlmsghdr *msghdr = NULL;
9425f0
+	int attrlen, rc, i;
9425f0
+
9425f0
+	if (!arg) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: group id - failed to receive argument.\n");
9425f0
+		return NL_SKIP;
9425f0
+	}
9425f0
+	data = arg;
9425f0
+
9425f0
+	/* get actual netlink message header */
9425f0
+	msghdr = nlmsg_hdr(msg);
9425f0
+
9425f0
+	/* get the start of the message payload */
9425f0
+	gnlhdr = nlmsg_data(msghdr);
9425f0
+
9425f0
+	/* get the start of the message attribute section */
9425f0
+	attrhdr = genlmsg_attrdata(gnlhdr, 0);
9425f0
+
9425f0
+	/* get the length of the message attribute section */
9425f0
+	attrlen = genlmsg_attrlen(gnlhdr, 0);
9425f0
+
9425f0
+	/* create attribute index based on a stream of attributes */
9425f0
+	rc = nla_parse(
9425f0
+		attrs,		/* index array to be filled */
9425f0
+		CTRL_ATTR_MAX,	/* the maximum acceptable attribute type */
9425f0
+		attrhdr,	/* head of attribute stream */
9425f0
+		attrlen,	/* length of attribute stream */
9425f0
+		policy);	/* validation policy */
9425f0
+	if (rc) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: group id - failed to create attributes.\n");
9425f0
+		return NL_SKIP;
9425f0
+	}
9425f0
+
9425f0
+	/* start of the multi-cast group attribute */
9425f0
+	mcgrp = attrs[CTRL_ATTR_MCAST_GROUPS];
9425f0
+	if (!mcgrp) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: group id - no multi-cast group attributes.\n");
9425f0
+		return NL_SKIP;
9425f0
+	}
9425f0
+
9425f0
+	/* iterate a stream of nested attributes to get the group id */
9425f0
+	nla_for_each_nested(cur_mcgrp, mcgrp, i) {
9425f0
+		struct nlattr *nested_attrs[CTRL_ATTR_MCAST_GRP_MAX + 1];
9425f0
+		struct nlattr *name, *id;
9425f0
+
9425f0
+		/* get start and length of payload section */
9425f0
+		attrhdr = nla_data(cur_mcgrp);
9425f0
+		attrlen = nla_len(cur_mcgrp);
9425f0
+
9425f0
+		rc = nla_parse(nested_attrs, CTRL_ATTR_MCAST_GRP_MAX, attrhdr, attrlen, policy);
9425f0
+		if (rc)
9425f0
+			continue;
9425f0
+
9425f0
+		name = nested_attrs[CTRL_ATTR_MCAST_GRP_NAME];
9425f0
+		id = nested_attrs[CTRL_ATTR_MCAST_GRP_ID];
9425f0
+		if (!name || !id)
9425f0
+			continue;
9425f0
+
9425f0
+		if (strncmp(nla_data(name), data->group, nla_len(name)) != 0)
9425f0
+			continue;
9425f0
+
9425f0
+		data->id = nla_get_u32(id);
9425f0
+		log(TO_ALL, LOG_DEBUG, "thermal: received group id (%d).\n", data->id);
9425f0
+		break;
9425f0
+	}
9425f0
+	return NL_OK;
9425f0
+}
9425f0
+
9425f0
+static int handle_error(struct sockaddr_nl *sk_addr __attribute__((unused)),
9425f0
+			struct nlmsgerr *err, void *arg)
9425f0
+{
9425f0
+	if (arg) {
9425f0
+		log(TO_ALL, LOG_INFO, "thermal: received a netlink error (%s).\n",
9425f0
+		    nl_geterror(err->error));
9425f0
+		*((int *)arg) = err->error;
9425f0
+	}
9425f0
+	return NL_SKIP;
9425f0
+}
9425f0
+
9425f0
+static int handle_end(struct nl_msg *msg __attribute__((unused)), void *arg)
9425f0
+{
9425f0
+	if (arg)
9425f0
+		*((int *)arg) = 0;
9425f0
+	return NL_SKIP;
9425f0
+}
9425f0
+
9425f0
+struct msgheader {
9425f0
+	unsigned char cmd, version;
9425f0
+	unsigned int port, seq;
9425f0
+	int id, hdrlen, flags;
9425f0
+};
9425f0
 
9425f0
 static gboolean establish_netlink(void)
9425f0
 {
9425f0
+	struct msgheader msghdr = { CTRL_CMD_GETFAMILY, 0, 0, 0, 0, 0, 0 };
9425f0
+	struct family_data nldata = { THERMAL_GENL_EVENT_GROUP_NAME, -ENOENT };
9425f0
+	struct nl_cb *cloned_callback = NULL;
9425f0
+	int rc, group_id, callback_rc = 1;
9425f0
+	struct nl_msg *msg = NULL;
9425f0
 	gboolean error = TRUE;
9425f0
+	void *hdr;
9425f0
+
9425f0
+	msg = nlmsg_alloc();
9425f0
+	if (!msg) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: message allocation failed.\n");
9425f0
+		goto err_out;
9425f0
+	}
9425f0
+
9425f0
+	msghdr.id = genl_ctrl_resolve(sock, NL_FAMILY_NAME);
9425f0
+	if (msghdr.id < 0) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: message id enumeration failed.\n");
9425f0
+		goto err_out;
9425f0
+	}
9425f0
+
9425f0
+	hdr = genlmsg_put(msg, msghdr.port, msghdr.seq, msghdr.id, msghdr.hdrlen,
9425f0
+			  msghdr.flags, msghdr.cmd, msghdr.version);
9425f0
+	if (!hdr) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: netlink header setup failed.\n");
9425f0
+		goto err_out;
9425f0
+	}
9425f0
+
9425f0
+	rc = nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, THERMAL_GENL_FAMILY_NAME);
9425f0
+	if (rc) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: message setup failed.\n");
9425f0
+		goto err_out;
9425f0
+	}
9425f0
+
9425f0
+	cloned_callback = nl_cb_clone(callback);
9425f0
+	if (!cloned_callback) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: callback handle duplication failed.\n");
9425f0
+		goto err_out;
9425f0
+	}
9425f0
+
9425f0
+	rc = nl_send_auto(sock, msg);
9425f0
+	if (rc < 0) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: failed to send the first message.\n");
9425f0
+		goto err_out;
9425f0
+	}
9425f0
 
9425f0
-	log(TO_ALL, LOG_ERR, "thermal: not yet implemented to establish netlink.\n");
9425f0
+	rc = nl_cb_err(cloned_callback, NL_CB_CUSTOM, handle_error, &callback_rc);
9425f0
+	if (rc) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: error callback setup failed.\n");
9425f0
+		goto err_out;
9425f0
+	}
9425f0
+
9425f0
+	rc = nl_cb_set(cloned_callback, NL_CB_ACK, NL_CB_CUSTOM, handle_end, &callback_rc);
9425f0
+	if (rc) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: ack callback setup failed.\n");
9425f0
+		goto err_out;
9425f0
+	}
9425f0
+
9425f0
+	rc = nl_cb_set(cloned_callback, NL_CB_FINISH, NL_CB_CUSTOM, handle_end, &callback_rc);
9425f0
+	if (rc) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: finish callback setup failed.\n");
9425f0
+		goto err_out;
9425f0
+	}
9425f0
+
9425f0
+	rc = nl_cb_set(cloned_callback, NL_CB_VALID, NL_CB_CUSTOM, handle_groupid, &nldata);
9425f0
+	if (rc) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: group id callback setup failed.\n");
9425f0
+		goto err_out;
9425f0
+	}
9425f0
+
9425f0
+	while (callback_rc != 0) {
9425f0
+		rc = nl_recvmsgs(sock, cloned_callback);
9425f0
+		if (rc < 0) {
9425f0
+			log(TO_ALL, LOG_ERR, "thermal: failed to receive messages.\n");
9425f0
+			goto err_out;
9425f0
+		}
9425f0
+	}
9425f0
+
9425f0
+	group_id = nldata.id;
9425f0
+	if (group_id < 0) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: invalid group_id was received.\n");
9425f0
+		goto err_out;
9425f0
+	}
9425f0
+
9425f0
+	rc = nl_socket_add_membership(sock, group_id);
9425f0
+	if (rc) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: failed to join the netlink group.\n");
9425f0
+		goto err_out;
9425f0
+	}
9425f0
+
9425f0
+	error = FALSE;
9425f0
+err_out:
9425f0
+	nl_cb_put(cloned_callback);
9425f0
+	nlmsg_free(msg);
9425f0
 	return error;
9425f0
 }
9425f0
 
9425f0
-static gboolean register_netlink_handler(nl_recvmsg_msg_cb_t handler __attribute__((unused)))
9425f0
+static int handle_thermal_event(struct nl_msg *msg __attribute__((unused)),
9425f0
+				void *arg __attribute__((unused)))
9425f0
 {
9425f0
-	gboolean error = TRUE;
9425f0
+	log(TO_ALL, LOG_ERR, "thermal: not yet implemented to process thermal event.\n");
9425f0
+	return NL_SKIP;
9425f0
+}
9425f0
 
9425f0
-	log(TO_ALL, LOG_ERR, "thermal: not yet implemented to register thermal handler.\n");
9425f0
-	return error;
9425f0
+static int handler_for_debug(struct nl_msg *msg __attribute__((unused)),
9425f0
+			     void *arg __attribute__((unused)))
9425f0
+{
9425f0
+	return NL_SKIP;
9425f0
+}
9425f0
+
9425f0
+/*
9425f0
+ * return value: TRUE with an error; otherwise, FALSE
9425f0
+ */
9425f0
+static gboolean register_netlink_handler(void)
9425f0
+{
9425f0
+	int rc;
9425f0
+
9425f0
+	rc = nl_cb_set(callback, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, handler_for_debug, NULL);
9425f0
+	if (rc) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: debug handler registration failed.\n");
9425f0
+		return TRUE;
9425f0
+	}
9425f0
+
9425f0
+
9425f0
+	rc = nl_cb_set(callback, NL_CB_VALID, NL_CB_CUSTOM, handle_thermal_event, NULL);
9425f0
+	if (rc) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: thermal handler registration failed.\n");
9425f0
+		return TRUE;
9425f0
+	}
9425f0
+
9425f0
+	return FALSE;
9425f0
 }
9425f0
 
9425f0
+/*
9425f0
+ * return value: TRUE to keep the source; FALSE to disconnect.
9425f0
+ */
9425f0
+gboolean receive_thermal_event(gint fd __attribute__((unused)),
9425f0
+			       GIOCondition condition,
9425f0
+			       gpointer user_data __attribute__((unused)))
9425f0
+{
9425f0
+	if (condition == G_IO_IN) {
9425f0
+		static unsigned int retry = 0;
9425f0
+		int err;
9425f0
+
9425f0
+		err = nl_recvmsgs(sock, callback);
9425f0
+		if (err) {
9425f0
+			log(TO_ALL, LOG_ERR, "thermal: failed to receive messages (rc=%d).\n", err);
9425f0
+			retry++;
9425f0
+
9425f0
+			/*
9425f0
+			 * Pass a few failures then turn off if it keeps
9425f0
+			 * failing down.
9425f0
+			 */
9425f0
+			if (retry <= MAX_RECV_ERRS) {
9425f0
+				log(TO_ALL, LOG_ERR, "thermal: but keep the connection.\n");
9425f0
+			} else {
9425f0
+				log(TO_ALL, LOG_ERR, "thermal: disconnect now with %u failures.\n",
9425f0
+				    retry);
9425f0
+				return FALSE;
9425f0
+			}
9425f0
+		}
9425f0
+	}
9425f0
+	return TRUE;
9425f0
+}
9425f0
+
9425f0
+/*
9425f0
+ * return value: TRUE with an error; otherwise, FALSE
9425f0
+ */
9425f0
 static gboolean set_netlink_nonblocking(void)
9425f0
 {
9425f0
-	gboolean error = TRUE;
9425f0
+	int rc, fd;
9425f0
 
9425f0
-	log(TO_ALL, LOG_ERR, "thermal: not yet implemented to set nonblocking socket.\n");
9425f0
-	return error;
9425f0
+	rc = nl_socket_set_nonblocking(sock);
9425f0
+	if (rc) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: non-blocking mode setup failed.\n");
9425f0
+		return TRUE;
9425f0
+	}
9425f0
+
9425f0
+	fd = nl_socket_get_fd(sock);
9425f0
+	if (fd == INVALID_NL_FD) {
9425f0
+		log(TO_ALL, LOG_ERR, "thermal: file descriptor setup failed.\n");
9425f0
+		return TRUE;
9425f0
+	}
9425f0
+
9425f0
+	g_unix_fd_add(fd, G_IO_IN, receive_thermal_event, NULL);
9425f0
+
9425f0
+	return FALSE;
9425f0
 }
9425f0
 
9425f0
 void deinit_thermal(void)
9425f0
 {
9425f0
-	return;
9425f0
+	nl_cb_put(callback);
9425f0
+	nl_socket_free(sock);
9425f0
 }
9425f0
 
9425f0
 /*
9425f0
@@ -68,7 +367,7 @@ gboolean init_thermal(void)
9425f0
 	if (error)
9425f0
 		goto err_out;
9425f0
 
9425f0
-	error = register_netlink_handler(NULL);
9425f0
+	error = register_netlink_handler();
9425f0
 	if (error)
9425f0
 		goto err_out;
9425f0
 
9425f0
-- 
9425f0
2.33.1
9425f0