xref: /linux/kernel/power/em_netlink.c (revision d8eef0453132dc95354e4c7ae839815e679179c6)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  *
4  * Generic netlink for energy model.
5  *
6  * Copyright (c) 2025 Valve Corporation.
7  * Author: Changwoo Min <changwoo@igalia.com>
8  */
9 
10 #define pr_fmt(fmt) "energy_model: " fmt
11 
12 #include <linux/energy_model.h>
13 #include <net/sock.h>
14 #include <net/genetlink.h>
15 #include <uapi/linux/energy_model.h>
16 
17 #include "em_netlink.h"
18 #include "em_netlink_autogen.h"
19 
20 #define EM_A_PD_CPUS_LEN		256
21 
22 /*************************** Command encoding ********************************/
23 static int __em_nl_get_pd_size(struct em_perf_domain *pd, void *data)
24 {
25 	char cpus_buf[EM_A_PD_CPUS_LEN];
26 	int *tot_msg_sz = data;
27 	int msg_sz, cpus_sz;
28 
29 	cpus_sz = snprintf(cpus_buf, sizeof(cpus_buf), "%*pb",
30 			   cpumask_pr_args(to_cpumask(pd->cpus)));
31 
32 	msg_sz = nla_total_size(0) +			/* EM_A_PDS_PD */
33 		 nla_total_size(sizeof(u32)) +		/* EM_A_PD_PD_ID */
34 		 nla_total_size_64bit(sizeof(u64)) +	/* EM_A_PD_FLAGS */
35 		 nla_total_size(cpus_sz);		/* EM_A_PD_CPUS */
36 
37 	*tot_msg_sz += nlmsg_total_size(genlmsg_msg_size(msg_sz));
38 	return 0;
39 }
40 
41 static int __em_nl_get_pd(struct em_perf_domain *pd, void *data)
42 {
43 	char cpus_buf[EM_A_PD_CPUS_LEN];
44 	struct sk_buff *msg = data;
45 	struct nlattr *entry;
46 
47 	entry = nla_nest_start(msg, EM_A_PDS_PD);
48 	if (!entry)
49 		goto out_cancel_nest;
50 
51 	if (nla_put_u32(msg, EM_A_PD_PD_ID, pd->id))
52 		goto out_cancel_nest;
53 
54 	if (nla_put_u64_64bit(msg, EM_A_PD_FLAGS, pd->flags, EM_A_PD_PAD))
55 		goto out_cancel_nest;
56 
57 	snprintf(cpus_buf, sizeof(cpus_buf), "%*pb",
58 		 cpumask_pr_args(to_cpumask(pd->cpus)));
59 	if (nla_put_string(msg, EM_A_PD_CPUS, cpus_buf))
60 		goto out_cancel_nest;
61 
62 	nla_nest_end(msg, entry);
63 
64 	return 0;
65 
66 out_cancel_nest:
67 	nla_nest_cancel(msg, entry);
68 
69 	return -EMSGSIZE;
70 }
71 
72 int em_nl_get_pds_doit(struct sk_buff *skb, struct genl_info *info)
73 {
74 	struct sk_buff *msg;
75 	void *hdr;
76 	int cmd = info->genlhdr->cmd;
77 	int ret = -EMSGSIZE, msg_sz = 0;
78 
79 	for_each_em_perf_domain(__em_nl_get_pd_size, &msg_sz);
80 
81 	msg = genlmsg_new(msg_sz, GFP_KERNEL);
82 	if (!msg)
83 		return -ENOMEM;
84 
85 	hdr = genlmsg_put_reply(msg, info, &em_nl_family, 0, cmd);
86 	if (!hdr)
87 		goto out_free_msg;
88 
89 	ret = for_each_em_perf_domain(__em_nl_get_pd, msg);
90 	if (ret)
91 		goto out_cancel_msg;
92 
93 	genlmsg_end(msg, hdr);
94 
95 	return genlmsg_reply(msg, info);
96 
97 out_cancel_msg:
98 	genlmsg_cancel(msg, hdr);
99 out_free_msg:
100 	nlmsg_free(msg);
101 
102 	return ret;
103 }
104 
105 int em_nl_get_pd_table_doit(struct sk_buff *skb, struct genl_info *info)
106 {
107 	return -EOPNOTSUPP;
108 }
109 
110 static int __init em_netlink_init(void)
111 {
112 	return genl_register_family(&em_nl_family);
113 }
114 postcore_initcall(em_netlink_init);
115