]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
PM: EM: Implement em_nl_get_pds_doit()
authorChangwoo Min <changwoo@igalia.com>
Mon, 20 Oct 2025 22:09:10 +0000 (07:09 +0900)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Wed, 22 Oct 2025 19:44:37 +0000 (21:44 +0200)
When a userspace requests EM_CMD_GET_PDS, the kernel responds with
information on all performance domains. The message format of the
response is as follows:

EM_A_PDS_PD (NLA_NESTED)*
    EM_A_PD_PD_ID (NLA_U32)
    EM_A_PD_FLAGS (NLA_U64)
    EM_A_PD_CPUS (NLA_STRING)

where EM_A_PDS_PD can be repeated as many times as there are performance
domains, and EM_A_PD_CPUS is a hexadecimal string representing a CPU
bitmask.

Signed-off-by: Changwoo Min <changwoo@igalia.com>
Reviewed-by: Lukasz Luba <lukasz.luba@arm.com>
Link: https://patch.msgid.link/20251020220914.320832-7-changwoo@igalia.com
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
kernel/power/em_netlink.c

index f8c98ae96aca0bb6b78af800ed734252dc25a649..16bdcf47f4d89b3e3d361e35a1edc8edd534627d 100644 (file)
 #include "em_netlink.h"
 #include "em_netlink_autogen.h"
 
+#define EM_A_PD_CPUS_LEN               256
+
+/*************************** Command encoding ********************************/
+static int __em_nl_get_pd_size(struct em_perf_domain *pd, void *data)
+{
+       char cpus_buf[EM_A_PD_CPUS_LEN];
+       int *tot_msg_sz = data;
+       int msg_sz, cpus_sz;
+
+       cpus_sz = snprintf(cpus_buf, sizeof(cpus_buf), "%*pb",
+                          cpumask_pr_args(to_cpumask(pd->cpus)));
+
+       msg_sz = nla_total_size(0) +                    /* EM_A_PDS_PD */
+                nla_total_size(sizeof(u32)) +          /* EM_A_PD_PD_ID */
+                nla_total_size_64bit(sizeof(u64)) +    /* EM_A_PD_FLAGS */
+                nla_total_size(cpus_sz);               /* EM_A_PD_CPUS */
+
+       *tot_msg_sz += nlmsg_total_size(genlmsg_msg_size(msg_sz));
+       return 0;
+}
+
+static int __em_nl_get_pd(struct em_perf_domain *pd, void *data)
+{
+       char cpus_buf[EM_A_PD_CPUS_LEN];
+       struct sk_buff *msg = data;
+       struct nlattr *entry;
+
+       entry = nla_nest_start(msg, EM_A_PDS_PD);
+       if (!entry)
+               goto out_cancel_nest;
+
+       if (nla_put_u32(msg, EM_A_PD_PD_ID, pd->id))
+               goto out_cancel_nest;
+
+       if (nla_put_u64_64bit(msg, EM_A_PD_FLAGS, pd->flags, EM_A_PD_PAD))
+               goto out_cancel_nest;
+
+       snprintf(cpus_buf, sizeof(cpus_buf), "%*pb",
+                cpumask_pr_args(to_cpumask(pd->cpus)));
+       if (nla_put_string(msg, EM_A_PD_CPUS, cpus_buf))
+               goto out_cancel_nest;
+
+       nla_nest_end(msg, entry);
+
+       return 0;
+
+out_cancel_nest:
+       nla_nest_cancel(msg, entry);
+
+       return -EMSGSIZE;
+}
+
 int em_nl_get_pds_doit(struct sk_buff *skb, struct genl_info *info)
 {
-       return -EOPNOTSUPP;
+       struct sk_buff *msg;
+       void *hdr;
+       int cmd = info->genlhdr->cmd;
+       int ret = -EMSGSIZE, msg_sz = 0;
+
+       for_each_em_perf_domain(__em_nl_get_pd_size, &msg_sz);
+
+       msg = genlmsg_new(msg_sz, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_put_reply(msg, info, &em_nl_family, 0, cmd);
+       if (!hdr)
+               goto out_free_msg;
+
+       ret = for_each_em_perf_domain(__em_nl_get_pd, msg);
+       if (ret)
+               goto out_cancel_msg;
+
+       genlmsg_end(msg, hdr);
+
+       return genlmsg_reply(msg, info);
+
+out_cancel_msg:
+       genlmsg_cancel(msg, hdr);
+out_free_msg:
+       nlmsg_free(msg);
+
+       return ret;
 }
 
 int em_nl_get_pd_table_doit(struct sk_buff *skb, struct genl_info *info)