File Index
Files and directories
Sign in
FILE

em_netlink.c

File preview with syntax highlighting.

linux-6.19.14/kernel/power/em_netlink.c C
  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 ********************************/
 21struct dump_ctx {
 22	int idx;
 23	int start;
 24	struct sk_buff *skb;
 25	struct netlink_callback *cb;
 26};
 27
 28static 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
 49static 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
 72out_cancel_nest:
 73	return -EMSGSIZE;
 74}
 75
 76static 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
 98int 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
131out_cancel_msg:
132	genlmsg_cancel(msg, hdr);
133out_free_msg:
134	nlmsg_free(msg);
135	return ret;
136}
137
138int 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
151static 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
164static 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
187static
188int __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
240out_cancel_ps_nest:
241	nla_nest_cancel(msg, entry);
242out_unlock_ps:
243	rcu_read_unlock();
244out_err:
245	return -EMSGSIZE;
246}
247
248int 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
278out_free_msg:
279	nlmsg_free(msg);
280	return ret;
281}
282
283
284/**************************** Event encoding *********************************/
285static 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
315out_free_msg:
316	nlmsg_free(msg);
317}
318
319void 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
324void 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
329static 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
336void 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
368out_free_msg:
369	nlmsg_free(msg);
370}
371
372/**************************** Initialization *********************************/
373static int __init em_netlink_init(void)
374{
375	return genl_register_family(&dev_energymodel_nl_family);
376}
377postcore_initcall(em_netlink_init);
~/ Read-only file preview.