projects/linux·blob master · refs

377 lines · 8.8 KiB

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 <[email protected]>
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);