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 113 __em_nl_get_pd_size(pd, &msg_sz); 114 msg = genlmsg_new(msg_sz, GFP_KERNEL); 115 if (!msg) 116 return -ENOMEM; 117 118 hdr = genlmsg_put_reply(msg, info, &dev_energymodel_nl_family, 0, cmd); 119 if (!hdr) 120 goto out_free_msg; 121 122 ret = __em_nl_get_pd(pd, msg); 123 if (ret) 124 goto out_cancel_msg; 125 genlmsg_end(msg, hdr); 126 127 return genlmsg_reply(msg, info); 128 129 out_cancel_msg: 130 genlmsg_cancel(msg, hdr); 131 out_free_msg: 132 nlmsg_free(msg); 133 return ret; 134 } 135 136 int dev_energymodel_nl_get_perf_domains_dumpit(struct sk_buff *skb, 137 struct netlink_callback *cb) 138 { 139 struct dump_ctx ctx = { 140 .idx = 0, 141 .start = cb->args[0], 142 .skb = skb, 143 .cb = cb, 144 }; 145 146 return for_each_em_perf_domain(__em_nl_get_pd_for_dump, &ctx); 147 } 148 149 static struct em_perf_domain *__em_nl_get_pd_table_id(struct nlattr **attrs) 150 { 151 struct em_perf_domain *pd; 152 int id; 153 154 if (!attrs[DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID]) 155 return NULL; 156 157 id = nla_get_u32(attrs[DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID]); 158 pd = em_perf_domain_get_by_id(id); 159 return pd; 160 } 161 162 static int __em_nl_get_pd_table_size(const struct em_perf_domain *pd) 163 { 164 int id_sz, ps_sz; 165 166 id_sz = nla_total_size(sizeof(u32)); 167 /* DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID */ 168 ps_sz = nla_total_size(0) + 169 /* DEV_ENERGYMODEL_A_PERF_TABLE_PERF_STATE */ 170 nla_total_size_64bit(sizeof(u64)) + 171 /* DEV_ENERGYMODEL_A_PERF_STATE_PERFORMANCE */ 172 nla_total_size_64bit(sizeof(u64)) + 173 /* DEV_ENERGYMODEL_A_PERF_STATE_FREQUENCY */ 174 nla_total_size_64bit(sizeof(u64)) + 175 /* DEV_ENERGYMODEL_A_PERF_STATE_POWER */ 176 nla_total_size_64bit(sizeof(u64)) + 177 /* DEV_ENERGYMODEL_A_PERF_STATE_COST */ 178 nla_total_size_64bit(sizeof(u64)); 179 /* DEV_ENERGYMODEL_A_PERF_STATE_FLAGS */ 180 ps_sz *= pd->nr_perf_states; 181 182 return nlmsg_total_size(genlmsg_msg_size(id_sz + ps_sz)); 183 } 184 185 static 186 int __em_nl_get_pd_table(struct sk_buff *msg, const struct em_perf_domain *pd) 187 { 188 struct em_perf_state *table, *ps; 189 struct nlattr *entry; 190 int i; 191 192 if (nla_put_u32(msg, DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID, 193 pd->id)) 194 goto out_err; 195 196 rcu_read_lock(); 197 table = em_perf_state_from_pd((struct em_perf_domain *)pd); 198 199 for (i = 0; i < pd->nr_perf_states; i++) { 200 ps = &table[i]; 201 202 entry = nla_nest_start(msg, 203 DEV_ENERGYMODEL_A_PERF_TABLE_PERF_STATE); 204 if (!entry) 205 goto out_unlock_ps; 206 207 if (nla_put_u64_64bit(msg, 208 DEV_ENERGYMODEL_A_PERF_STATE_PERFORMANCE, 209 ps->performance, 210 DEV_ENERGYMODEL_A_PERF_STATE_PAD)) 211 goto out_cancel_ps_nest; 212 if (nla_put_u64_64bit(msg, 213 DEV_ENERGYMODEL_A_PERF_STATE_FREQUENCY, 214 ps->frequency, 215 DEV_ENERGYMODEL_A_PERF_STATE_PAD)) 216 goto out_cancel_ps_nest; 217 if (nla_put_u64_64bit(msg, 218 DEV_ENERGYMODEL_A_PERF_STATE_POWER, 219 ps->power, 220 DEV_ENERGYMODEL_A_PERF_STATE_PAD)) 221 goto out_cancel_ps_nest; 222 if (nla_put_u64_64bit(msg, 223 DEV_ENERGYMODEL_A_PERF_STATE_COST, 224 ps->cost, 225 DEV_ENERGYMODEL_A_PERF_STATE_PAD)) 226 goto out_cancel_ps_nest; 227 if (nla_put_u64_64bit(msg, 228 DEV_ENERGYMODEL_A_PERF_STATE_FLAGS, 229 ps->flags, 230 DEV_ENERGYMODEL_A_PERF_STATE_PAD)) 231 goto out_cancel_ps_nest; 232 233 nla_nest_end(msg, entry); 234 } 235 rcu_read_unlock(); 236 return 0; 237 238 out_cancel_ps_nest: 239 nla_nest_cancel(msg, entry); 240 out_unlock_ps: 241 rcu_read_unlock(); 242 out_err: 243 return -EMSGSIZE; 244 } 245 246 int dev_energymodel_nl_get_perf_table_doit(struct sk_buff *skb, 247 struct genl_info *info) 248 { 249 int cmd = info->genlhdr->cmd; 250 int msg_sz, ret = -EMSGSIZE; 251 struct em_perf_domain *pd; 252 struct sk_buff *msg; 253 void *hdr; 254 255 pd = __em_nl_get_pd_table_id(info->attrs); 256 if (!pd) 257 return -EINVAL; 258 259 msg_sz = __em_nl_get_pd_table_size(pd); 260 261 msg = genlmsg_new(msg_sz, GFP_KERNEL); 262 if (!msg) 263 return -ENOMEM; 264 265 hdr = genlmsg_put_reply(msg, info, &dev_energymodel_nl_family, 0, cmd); 266 if (!hdr) 267 goto out_free_msg; 268 269 ret = __em_nl_get_pd_table(msg, pd); 270 if (ret) 271 goto out_free_msg; 272 273 genlmsg_end(msg, hdr); 274 return genlmsg_reply(msg, info); 275 276 out_free_msg: 277 nlmsg_free(msg); 278 return ret; 279 } 280 281 282 /**************************** Event encoding *********************************/ 283 static void __em_notify_pd_table(const struct em_perf_domain *pd, int ntf_type) 284 { 285 struct sk_buff *msg; 286 int msg_sz, ret = -EMSGSIZE; 287 void *hdr; 288 289 if (!genl_has_listeners(&dev_energymodel_nl_family, &init_net, DEV_ENERGYMODEL_NLGRP_EVENT)) 290 return; 291 292 msg_sz = __em_nl_get_pd_table_size(pd); 293 294 msg = genlmsg_new(msg_sz, GFP_KERNEL); 295 if (!msg) 296 return; 297 298 hdr = genlmsg_put(msg, 0, 0, &dev_energymodel_nl_family, 0, ntf_type); 299 if (!hdr) 300 goto out_free_msg; 301 302 ret = __em_nl_get_pd_table(msg, pd); 303 if (ret) 304 goto out_free_msg; 305 306 genlmsg_end(msg, hdr); 307 308 genlmsg_multicast(&dev_energymodel_nl_family, msg, 0, 309 DEV_ENERGYMODEL_NLGRP_EVENT, GFP_KERNEL); 310 311 return; 312 313 out_free_msg: 314 nlmsg_free(msg); 315 } 316 317 void em_notify_pd_created(const struct em_perf_domain *pd) 318 { 319 __em_notify_pd_table(pd, DEV_ENERGYMODEL_CMD_PERF_DOMAIN_CREATED); 320 } 321 322 void em_notify_pd_updated(const struct em_perf_domain *pd) 323 { 324 __em_notify_pd_table(pd, DEV_ENERGYMODEL_CMD_PERF_DOMAIN_UPDATED); 325 } 326 327 static int __em_notify_pd_deleted_size(const struct em_perf_domain *pd) 328 { 329 int id_sz = nla_total_size(sizeof(u32)); /* DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID */ 330 331 return nlmsg_total_size(genlmsg_msg_size(id_sz)); 332 } 333 334 void em_notify_pd_deleted(const struct em_perf_domain *pd) 335 { 336 struct sk_buff *msg; 337 void *hdr; 338 int msg_sz; 339 340 if (!genl_has_listeners(&dev_energymodel_nl_family, &init_net, 341 DEV_ENERGYMODEL_NLGRP_EVENT)) 342 return; 343 344 msg_sz = __em_notify_pd_deleted_size(pd); 345 346 msg = genlmsg_new(msg_sz, GFP_KERNEL); 347 if (!msg) 348 return; 349 350 hdr = genlmsg_put(msg, 0, 0, &dev_energymodel_nl_family, 0, 351 DEV_ENERGYMODEL_CMD_PERF_DOMAIN_DELETED); 352 if (!hdr) 353 goto out_free_msg; 354 355 if (nla_put_u32(msg, DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID, 356 pd->id)) 357 goto out_free_msg; 358 359 genlmsg_end(msg, hdr); 360 361 genlmsg_multicast(&dev_energymodel_nl_family, msg, 0, 362 DEV_ENERGYMODEL_NLGRP_EVENT, GFP_KERNEL); 363 364 return; 365 366 out_free_msg: 367 nlmsg_free(msg); 368 } 369 370 /**************************** Initialization *********************************/ 371 static int __init em_netlink_init(void) 372 { 373 return genl_register_family(&dev_energymodel_nl_family); 374 } 375 postcore_initcall(em_netlink_init); 376