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