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/energy_model.h> 16 17 #include "em_netlink.h" 18 #include "em_netlink_autogen.h" 19 20 #define EM_A_PD_CPUS_LEN 256 21 22 /*************************** Command encoding ********************************/ 23 static int __em_nl_get_pd_size(struct em_perf_domain *pd, void *data) 24 { 25 char cpus_buf[EM_A_PD_CPUS_LEN]; 26 int *tot_msg_sz = data; 27 int msg_sz, cpus_sz; 28 29 cpus_sz = snprintf(cpus_buf, sizeof(cpus_buf), "%*pb", 30 cpumask_pr_args(to_cpumask(pd->cpus))); 31 32 msg_sz = nla_total_size(0) + /* EM_A_PDS_PD */ 33 nla_total_size(sizeof(u32)) + /* EM_A_PD_PD_ID */ 34 nla_total_size_64bit(sizeof(u64)) + /* EM_A_PD_FLAGS */ 35 nla_total_size(cpus_sz); /* EM_A_PD_CPUS */ 36 37 *tot_msg_sz += nlmsg_total_size(genlmsg_msg_size(msg_sz)); 38 return 0; 39 } 40 41 static int __em_nl_get_pd(struct em_perf_domain *pd, void *data) 42 { 43 char cpus_buf[EM_A_PD_CPUS_LEN]; 44 struct sk_buff *msg = data; 45 struct nlattr *entry; 46 47 entry = nla_nest_start(msg, EM_A_PDS_PD); 48 if (!entry) 49 goto out_cancel_nest; 50 51 if (nla_put_u32(msg, EM_A_PD_PD_ID, pd->id)) 52 goto out_cancel_nest; 53 54 if (nla_put_u64_64bit(msg, EM_A_PD_FLAGS, pd->flags, EM_A_PD_PAD)) 55 goto out_cancel_nest; 56 57 snprintf(cpus_buf, sizeof(cpus_buf), "%*pb", 58 cpumask_pr_args(to_cpumask(pd->cpus))); 59 if (nla_put_string(msg, EM_A_PD_CPUS, cpus_buf)) 60 goto out_cancel_nest; 61 62 nla_nest_end(msg, entry); 63 64 return 0; 65 66 out_cancel_nest: 67 nla_nest_cancel(msg, entry); 68 69 return -EMSGSIZE; 70 } 71 72 int em_nl_get_pds_doit(struct sk_buff *skb, struct genl_info *info) 73 { 74 struct sk_buff *msg; 75 void *hdr; 76 int cmd = info->genlhdr->cmd; 77 int ret = -EMSGSIZE, msg_sz = 0; 78 79 for_each_em_perf_domain(__em_nl_get_pd_size, &msg_sz); 80 81 msg = genlmsg_new(msg_sz, GFP_KERNEL); 82 if (!msg) 83 return -ENOMEM; 84 85 hdr = genlmsg_put_reply(msg, info, &em_nl_family, 0, cmd); 86 if (!hdr) 87 goto out_free_msg; 88 89 ret = for_each_em_perf_domain(__em_nl_get_pd, msg); 90 if (ret) 91 goto out_cancel_msg; 92 93 genlmsg_end(msg, hdr); 94 95 return genlmsg_reply(msg, info); 96 97 out_cancel_msg: 98 genlmsg_cancel(msg, hdr); 99 out_free_msg: 100 nlmsg_free(msg); 101 102 return ret; 103 } 104 105 int em_nl_get_pd_table_doit(struct sk_buff *skb, struct genl_info *info) 106 { 107 return -EOPNOTSUPP; 108 } 109 110 static int __init em_netlink_init(void) 111 { 112 return genl_register_family(&em_nl_family); 113 } 114 postcore_initcall(em_netlink_init); 115