xref: /linux/kernel/power/em_netlink.c (revision 9badc2a84e688be1275bb740942d5f6f51746908)
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/dev_energymodel.h>
16 
17 #include "em_netlink.h"
18 #include "em_netlink_autogen.h"
19 
20 /*************************** Command encoding ********************************/
21 struct dump_ctx {
22 	int idx;
23 	int start;
24 	struct sk_buff *skb;
25 	struct netlink_callback *cb;
26 };
27 
28 static int __em_nl_get_pd_size(struct em_perf_domain *pd, void *data)
29 {
30 	int nr_cpus, msg_sz, cpus_sz;
31 	int *tot_msg_sz = data;
32 
33 	nr_cpus = cpumask_weight(to_cpumask(pd->cpus));
34 	cpus_sz = nla_total_size_64bit(sizeof(u64)) * nr_cpus;
35 
36 	msg_sz = nla_total_size(0) +
37 		 /* DEV_ENERGYMODEL_A_PERF_DOMAINS_PERF_DOMAIN */
38 		 nla_total_size(sizeof(u32)) +
39 		 /* DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID */
40 		 nla_total_size_64bit(sizeof(u64)) +
41 		 /* DEV_ENERGYMODEL_A_PERF_DOMAIN_FLAGS */
42 		 nla_total_size(cpus_sz);
43 		 /* DEV_ENERGYMODEL_A_PERF_DOMAIN_CPUS */
44 
45 	*tot_msg_sz += nlmsg_total_size(genlmsg_msg_size(msg_sz));
46 	return 0;
47 }
48 
49 static int __em_nl_get_pd(struct em_perf_domain *pd, void *data)
50 {
51 	struct sk_buff *msg = data;
52 	struct cpumask *cpumask;
53 	int cpu;
54 
55 	if (nla_put_u32(msg, DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID,
56 			pd->id))
57 		goto out_cancel_nest;
58 
59 	if (nla_put_u64_64bit(msg, DEV_ENERGYMODEL_A_PERF_DOMAIN_FLAGS,
60 			      pd->flags, DEV_ENERGYMODEL_A_PERF_DOMAIN_PAD))
61 		goto out_cancel_nest;
62 
63 	cpumask = to_cpumask(pd->cpus);
64 	for_each_cpu(cpu, cpumask) {
65 		if (nla_put_u64_64bit(msg, DEV_ENERGYMODEL_A_PERF_DOMAIN_CPUS,
66 				      cpu, DEV_ENERGYMODEL_A_PERF_DOMAIN_PAD))
67 			goto out_cancel_nest;
68 	}
69 
70 	return 0;
71 
72 out_cancel_nest:
73 	return -EMSGSIZE;
74 }
75 
76 static int __em_nl_get_pd_for_dump(struct em_perf_domain *pd, void *data)
77 {
78 	const struct genl_info *info;
79 	struct dump_ctx *ctx = data;
80 	void *hdr;
81 	int ret;
82 
83 	if (ctx->idx++ < ctx->start)
84 		return 0;
85 
86 	info = genl_info_dump(ctx->cb);
87 	hdr = genlmsg_iput(ctx->skb, info);
88 	if (!hdr) {
89 		genlmsg_cancel(ctx->skb, hdr);
90 		return -EMSGSIZE;
91 	}
92 
93 	ret = __em_nl_get_pd(pd, ctx->skb);
94 	genlmsg_end(ctx->skb, hdr);
95 	return ret;
96 }
97 
98 int dev_energymodel_nl_get_perf_domains_doit(struct sk_buff *skb,
99 					      struct genl_info *info)
100 {
101 	int id, ret = -EMSGSIZE, msg_sz = 0;
102 	int cmd = info->genlhdr->cmd;
103 	struct em_perf_domain *pd;
104 	struct sk_buff *msg;
105 	void *hdr;
106 
107 	if (!info->attrs[DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID])
108 		return -EINVAL;
109 
110 	id = nla_get_u32(info->attrs[DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID]);
111 	pd = em_perf_domain_get_by_id(id);
112 	if (!pd)
113 		return -EINVAL;
114 
115 	__em_nl_get_pd_size(pd, &msg_sz);
116 	msg = genlmsg_new(msg_sz, GFP_KERNEL);
117 	if (!msg)
118 		return -ENOMEM;
119 
120 	hdr = genlmsg_put_reply(msg, info, &dev_energymodel_nl_family, 0, cmd);
121 	if (!hdr)
122 		goto out_free_msg;
123 
124 	ret = __em_nl_get_pd(pd, msg);
125 	if (ret)
126 		goto out_cancel_msg;
127 	genlmsg_end(msg, hdr);
128 
129 	return genlmsg_reply(msg, info);
130 
131 out_cancel_msg:
132 	genlmsg_cancel(msg, hdr);
133 out_free_msg:
134 	nlmsg_free(msg);
135 	return ret;
136 }
137 
138 int dev_energymodel_nl_get_perf_domains_dumpit(struct sk_buff *skb,
139 						struct netlink_callback *cb)
140 {
141 	struct dump_ctx ctx = {
142 		.idx = 0,
143 		.start = cb->args[0],
144 		.skb = skb,
145 		.cb = cb,
146 	};
147 
148 	return for_each_em_perf_domain(__em_nl_get_pd_for_dump, &ctx);
149 }
150 
151 static struct em_perf_domain *__em_nl_get_pd_table_id(struct nlattr **attrs)
152 {
153 	struct em_perf_domain *pd;
154 	int id;
155 
156 	if (!attrs[DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID])
157 		return NULL;
158 
159 	id = nla_get_u32(attrs[DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID]);
160 	pd = em_perf_domain_get_by_id(id);
161 	return pd;
162 }
163 
164 static int __em_nl_get_pd_table_size(const struct em_perf_domain *pd)
165 {
166 	int id_sz, ps_sz;
167 
168 	id_sz = nla_total_size(sizeof(u32));
169 		/* DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID */
170 	ps_sz = nla_total_size(0) +
171 		/* DEV_ENERGYMODEL_A_PERF_TABLE_PERF_STATE */
172 		nla_total_size_64bit(sizeof(u64)) +
173 		/* DEV_ENERGYMODEL_A_PERF_STATE_PERFORMANCE */
174 		nla_total_size_64bit(sizeof(u64)) +
175 		/* DEV_ENERGYMODEL_A_PERF_STATE_FREQUENCY */
176 		nla_total_size_64bit(sizeof(u64)) +
177 		/* DEV_ENERGYMODEL_A_PERF_STATE_POWER */
178 		nla_total_size_64bit(sizeof(u64)) +
179 		/* DEV_ENERGYMODEL_A_PERF_STATE_COST */
180 		nla_total_size_64bit(sizeof(u64));
181 		/* DEV_ENERGYMODEL_A_PERF_STATE_FLAGS */
182 	ps_sz *= pd->nr_perf_states;
183 
184 	return nlmsg_total_size(genlmsg_msg_size(id_sz + ps_sz));
185 }
186 
187 static
188 int __em_nl_get_pd_table(struct sk_buff *msg, const struct em_perf_domain *pd)
189 {
190 	struct em_perf_state *table, *ps;
191 	struct nlattr *entry;
192 	int i;
193 
194 	if (nla_put_u32(msg, DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID,
195 			pd->id))
196 		goto out_err;
197 
198 	rcu_read_lock();
199 	table = em_perf_state_from_pd((struct em_perf_domain *)pd);
200 
201 	for (i = 0; i < pd->nr_perf_states; i++) {
202 		ps = &table[i];
203 
204 		entry = nla_nest_start(msg,
205 				       DEV_ENERGYMODEL_A_PERF_TABLE_PERF_STATE);
206 		if (!entry)
207 			goto out_unlock_ps;
208 
209 		if (nla_put_u64_64bit(msg,
210 				      DEV_ENERGYMODEL_A_PERF_STATE_PERFORMANCE,
211 				      ps->performance,
212 				      DEV_ENERGYMODEL_A_PERF_STATE_PAD))
213 			goto out_cancel_ps_nest;
214 		if (nla_put_u64_64bit(msg,
215 				      DEV_ENERGYMODEL_A_PERF_STATE_FREQUENCY,
216 				      ps->frequency,
217 				      DEV_ENERGYMODEL_A_PERF_STATE_PAD))
218 			goto out_cancel_ps_nest;
219 		if (nla_put_u64_64bit(msg,
220 				      DEV_ENERGYMODEL_A_PERF_STATE_POWER,
221 				      ps->power,
222 				      DEV_ENERGYMODEL_A_PERF_STATE_PAD))
223 			goto out_cancel_ps_nest;
224 		if (nla_put_u64_64bit(msg,
225 				      DEV_ENERGYMODEL_A_PERF_STATE_COST,
226 				      ps->cost,
227 				      DEV_ENERGYMODEL_A_PERF_STATE_PAD))
228 			goto out_cancel_ps_nest;
229 		if (nla_put_u64_64bit(msg,
230 				      DEV_ENERGYMODEL_A_PERF_STATE_FLAGS,
231 				      ps->flags,
232 				      DEV_ENERGYMODEL_A_PERF_STATE_PAD))
233 			goto out_cancel_ps_nest;
234 
235 		nla_nest_end(msg, entry);
236 	}
237 	rcu_read_unlock();
238 	return 0;
239 
240 out_cancel_ps_nest:
241 	nla_nest_cancel(msg, entry);
242 out_unlock_ps:
243 	rcu_read_unlock();
244 out_err:
245 	return -EMSGSIZE;
246 }
247 
248 int dev_energymodel_nl_get_perf_table_doit(struct sk_buff *skb,
249 					    struct genl_info *info)
250 {
251 	int cmd = info->genlhdr->cmd;
252 	int msg_sz, ret = -EMSGSIZE;
253 	struct em_perf_domain *pd;
254 	struct sk_buff *msg;
255 	void *hdr;
256 
257 	pd = __em_nl_get_pd_table_id(info->attrs);
258 	if (!pd)
259 		return -EINVAL;
260 
261 	msg_sz = __em_nl_get_pd_table_size(pd);
262 
263 	msg = genlmsg_new(msg_sz, GFP_KERNEL);
264 	if (!msg)
265 		return -ENOMEM;
266 
267 	hdr = genlmsg_put_reply(msg, info, &dev_energymodel_nl_family, 0, cmd);
268 	if (!hdr)
269 		goto out_free_msg;
270 
271 	ret = __em_nl_get_pd_table(msg, pd);
272 	if (ret)
273 		goto out_free_msg;
274 
275 	genlmsg_end(msg, hdr);
276 	return genlmsg_reply(msg, info);
277 
278 out_free_msg:
279 	nlmsg_free(msg);
280 	return ret;
281 }
282 
283 
284 /**************************** Event encoding *********************************/
285 static void __em_notify_pd_table(const struct em_perf_domain *pd, int ntf_type)
286 {
287 	struct sk_buff *msg;
288 	int msg_sz, ret = -EMSGSIZE;
289 	void *hdr;
290 
291 	if (!genl_has_listeners(&dev_energymodel_nl_family, &init_net, DEV_ENERGYMODEL_NLGRP_EVENT))
292 		return;
293 
294 	msg_sz = __em_nl_get_pd_table_size(pd);
295 
296 	msg = genlmsg_new(msg_sz, GFP_KERNEL);
297 	if (!msg)
298 		return;
299 
300 	hdr = genlmsg_put(msg, 0, 0, &dev_energymodel_nl_family, 0, ntf_type);
301 	if (!hdr)
302 		goto out_free_msg;
303 
304 	ret = __em_nl_get_pd_table(msg, pd);
305 	if (ret)
306 		goto out_free_msg;
307 
308 	genlmsg_end(msg, hdr);
309 
310 	genlmsg_multicast(&dev_energymodel_nl_family, msg, 0,
311 			  DEV_ENERGYMODEL_NLGRP_EVENT, GFP_KERNEL);
312 
313 	return;
314 
315 out_free_msg:
316 	nlmsg_free(msg);
317 }
318 
319 void em_notify_pd_created(const struct em_perf_domain *pd)
320 {
321 	__em_notify_pd_table(pd, DEV_ENERGYMODEL_CMD_PERF_DOMAIN_CREATED);
322 }
323 
324 void em_notify_pd_updated(const struct em_perf_domain *pd)
325 {
326 	__em_notify_pd_table(pd, DEV_ENERGYMODEL_CMD_PERF_DOMAIN_UPDATED);
327 }
328 
329 static int __em_notify_pd_deleted_size(const struct em_perf_domain *pd)
330 {
331 	int id_sz = nla_total_size(sizeof(u32)); /* DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID */
332 
333 	return nlmsg_total_size(genlmsg_msg_size(id_sz));
334 }
335 
336 void em_notify_pd_deleted(const struct em_perf_domain *pd)
337 {
338 	struct sk_buff *msg;
339 	void *hdr;
340 	int msg_sz;
341 
342 	if (!genl_has_listeners(&dev_energymodel_nl_family, &init_net,
343 				DEV_ENERGYMODEL_NLGRP_EVENT))
344 		return;
345 
346 	msg_sz = __em_notify_pd_deleted_size(pd);
347 
348 	msg = genlmsg_new(msg_sz, GFP_KERNEL);
349 	if (!msg)
350 		return;
351 
352 	hdr = genlmsg_put(msg, 0, 0, &dev_energymodel_nl_family, 0,
353 			  DEV_ENERGYMODEL_CMD_PERF_DOMAIN_DELETED);
354 	if (!hdr)
355 		goto out_free_msg;
356 
357 	if (nla_put_u32(msg, DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID,
358 			pd->id))
359 		goto out_free_msg;
360 
361 	genlmsg_end(msg, hdr);
362 
363 	genlmsg_multicast(&dev_energymodel_nl_family, msg, 0,
364 			  DEV_ENERGYMODEL_NLGRP_EVENT, GFP_KERNEL);
365 
366 	return;
367 
368 out_free_msg:
369 	nlmsg_free(msg);
370 }
371 
372 /**************************** Initialization *********************************/
373 static int __init em_netlink_init(void)
374 {
375 	return genl_register_family(&dev_energymodel_nl_family);
376 }
377 postcore_initcall(em_netlink_init);
378