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

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