xref: /linux/kernel/power/em_netlink.c (revision b62ce2547fe8a8ba15857bb974bcad250c5420d6)
1e4ed8d26SChangwoo Min // SPDX-License-Identifier: GPL-2.0
2e4ed8d26SChangwoo Min /*
3e4ed8d26SChangwoo Min  *
4e4ed8d26SChangwoo Min  * Generic netlink for energy model.
5e4ed8d26SChangwoo Min  *
6e4ed8d26SChangwoo Min  * Copyright (c) 2025 Valve Corporation.
7e4ed8d26SChangwoo Min  * Author: Changwoo Min <changwoo@igalia.com>
8e4ed8d26SChangwoo Min  */
9e4ed8d26SChangwoo Min 
10e4ed8d26SChangwoo Min #define pr_fmt(fmt) "energy_model: " fmt
11e4ed8d26SChangwoo Min 
12e4ed8d26SChangwoo Min #include <linux/energy_model.h>
13e4ed8d26SChangwoo Min #include <net/sock.h>
14e4ed8d26SChangwoo Min #include <net/genetlink.h>
15caa07a81SChangwoo Min #include <uapi/linux/dev_energymodel.h>
16e4ed8d26SChangwoo Min 
17e4ed8d26SChangwoo Min #include "em_netlink.h"
18e4ed8d26SChangwoo Min #include "em_netlink_autogen.h"
19e4ed8d26SChangwoo Min 
20d8eef045SChangwoo Min /*************************** Command encoding ********************************/
21*380ff27aSChangwoo Min struct dump_ctx {
22*380ff27aSChangwoo Min 	int idx;
23*380ff27aSChangwoo Min 	int start;
24*380ff27aSChangwoo Min 	struct sk_buff *skb;
25*380ff27aSChangwoo Min 	struct netlink_callback *cb;
26*380ff27aSChangwoo Min };
27*380ff27aSChangwoo Min 
__em_nl_get_pd_size(struct em_perf_domain * pd,void * data)28d8eef045SChangwoo Min static int __em_nl_get_pd_size(struct em_perf_domain *pd, void *data)
29d8eef045SChangwoo Min {
30d29b900cSChangwoo Min 	int nr_cpus, msg_sz, cpus_sz;
31d8eef045SChangwoo Min 	int *tot_msg_sz = data;
32d8eef045SChangwoo Min 
33d29b900cSChangwoo Min 	nr_cpus = cpumask_weight(to_cpumask(pd->cpus));
34d29b900cSChangwoo Min 	cpus_sz = nla_total_size_64bit(sizeof(u64)) * nr_cpus;
35d8eef045SChangwoo Min 
36caa07a81SChangwoo Min 	msg_sz = nla_total_size(0) +
37caa07a81SChangwoo Min 		 /* DEV_ENERGYMODEL_A_PERF_DOMAINS_PERF_DOMAIN */
38caa07a81SChangwoo Min 		 nla_total_size(sizeof(u32)) +
39caa07a81SChangwoo Min 		 /* DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID */
40caa07a81SChangwoo Min 		 nla_total_size_64bit(sizeof(u64)) +
41caa07a81SChangwoo Min 		 /* DEV_ENERGYMODEL_A_PERF_DOMAIN_FLAGS */
42caa07a81SChangwoo Min 		 nla_total_size(cpus_sz);
43caa07a81SChangwoo Min 		 /* DEV_ENERGYMODEL_A_PERF_DOMAIN_CPUS */
44d8eef045SChangwoo Min 
45d8eef045SChangwoo Min 	*tot_msg_sz += nlmsg_total_size(genlmsg_msg_size(msg_sz));
46d8eef045SChangwoo Min 	return 0;
47d8eef045SChangwoo Min }
48d8eef045SChangwoo Min 
__em_nl_get_pd(struct em_perf_domain * pd,void * data)49d8eef045SChangwoo Min static int __em_nl_get_pd(struct em_perf_domain *pd, void *data)
50d8eef045SChangwoo Min {
51d8eef045SChangwoo Min 	struct sk_buff *msg = data;
52d29b900cSChangwoo Min 	struct cpumask *cpumask;
53d29b900cSChangwoo Min 	int cpu;
54d8eef045SChangwoo Min 
55caa07a81SChangwoo Min 	if (nla_put_u32(msg, DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID,
56caa07a81SChangwoo Min 			pd->id))
57d8eef045SChangwoo Min 		goto out_cancel_nest;
58d8eef045SChangwoo Min 
59caa07a81SChangwoo Min 	if (nla_put_u64_64bit(msg, DEV_ENERGYMODEL_A_PERF_DOMAIN_FLAGS,
60caa07a81SChangwoo Min 			      pd->flags, DEV_ENERGYMODEL_A_PERF_DOMAIN_PAD))
61d8eef045SChangwoo Min 		goto out_cancel_nest;
62d8eef045SChangwoo Min 
63d29b900cSChangwoo Min 	cpumask = to_cpumask(pd->cpus);
64d29b900cSChangwoo Min 	for_each_cpu(cpu, cpumask) {
65d29b900cSChangwoo Min 		if (nla_put_u64_64bit(msg, DEV_ENERGYMODEL_A_PERF_DOMAIN_CPUS,
66d29b900cSChangwoo Min 				      cpu, DEV_ENERGYMODEL_A_PERF_DOMAIN_PAD))
67d8eef045SChangwoo Min 			goto out_cancel_nest;
68d29b900cSChangwoo Min 	}
69d8eef045SChangwoo Min 
70d8eef045SChangwoo Min 	return 0;
71d8eef045SChangwoo Min 
72d8eef045SChangwoo Min out_cancel_nest:
73d8eef045SChangwoo Min 	return -EMSGSIZE;
74d8eef045SChangwoo Min }
75d8eef045SChangwoo Min 
__em_nl_get_pd_for_dump(struct em_perf_domain * pd,void * data)76*380ff27aSChangwoo Min static int __em_nl_get_pd_for_dump(struct em_perf_domain *pd, void *data)
77*380ff27aSChangwoo Min {
78*380ff27aSChangwoo Min 	const struct genl_info *info;
79*380ff27aSChangwoo Min 	struct dump_ctx *ctx = data;
80*380ff27aSChangwoo Min 	void *hdr;
81*380ff27aSChangwoo Min 	int ret;
82*380ff27aSChangwoo Min 
83*380ff27aSChangwoo Min 	if (ctx->idx++ < ctx->start)
84*380ff27aSChangwoo Min 		return 0;
85*380ff27aSChangwoo Min 
86*380ff27aSChangwoo Min 	info = genl_info_dump(ctx->cb);
87*380ff27aSChangwoo Min 	hdr = genlmsg_iput(ctx->skb, info);
88*380ff27aSChangwoo Min 	if (!hdr) {
89*380ff27aSChangwoo Min 		genlmsg_cancel(ctx->skb, hdr);
90*380ff27aSChangwoo Min 		return -EMSGSIZE;
91*380ff27aSChangwoo Min 	}
92*380ff27aSChangwoo Min 
93*380ff27aSChangwoo Min 	ret = __em_nl_get_pd(pd, ctx->skb);
94*380ff27aSChangwoo Min 	genlmsg_end(ctx->skb, hdr);
95*380ff27aSChangwoo Min 	return ret;
96*380ff27aSChangwoo Min }
97*380ff27aSChangwoo Min 
dev_energymodel_nl_get_perf_domains_doit(struct sk_buff * skb,struct genl_info * info)98caa07a81SChangwoo Min int dev_energymodel_nl_get_perf_domains_doit(struct sk_buff *skb,
99caa07a81SChangwoo Min 					      struct genl_info *info)
100e4ed8d26SChangwoo Min {
101*380ff27aSChangwoo Min 	int id, ret = -EMSGSIZE, msg_sz = 0;
102*380ff27aSChangwoo Min 	int cmd = info->genlhdr->cmd;
103*380ff27aSChangwoo Min 	struct em_perf_domain *pd;
104d8eef045SChangwoo Min 	struct sk_buff *msg;
105d8eef045SChangwoo Min 	void *hdr;
106d8eef045SChangwoo Min 
107*380ff27aSChangwoo Min 	if (!info->attrs[DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID])
108*380ff27aSChangwoo Min 		return -EINVAL;
109d8eef045SChangwoo Min 
110*380ff27aSChangwoo Min 	id = nla_get_u32(info->attrs[DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID]);
111*380ff27aSChangwoo Min 	pd = em_perf_domain_get_by_id(id);
112*380ff27aSChangwoo Min 
113*380ff27aSChangwoo Min 	__em_nl_get_pd_size(pd, &msg_sz);
114d8eef045SChangwoo Min 	msg = genlmsg_new(msg_sz, GFP_KERNEL);
115d8eef045SChangwoo Min 	if (!msg)
116d8eef045SChangwoo Min 		return -ENOMEM;
117d8eef045SChangwoo Min 
118caa07a81SChangwoo Min 	hdr = genlmsg_put_reply(msg, info, &dev_energymodel_nl_family, 0, cmd);
119d8eef045SChangwoo Min 	if (!hdr)
120d8eef045SChangwoo Min 		goto out_free_msg;
121d8eef045SChangwoo Min 
122*380ff27aSChangwoo Min 	ret = __em_nl_get_pd(pd, msg);
123d8eef045SChangwoo Min 	if (ret)
124d8eef045SChangwoo Min 		goto out_cancel_msg;
125d8eef045SChangwoo Min 	genlmsg_end(msg, hdr);
126d8eef045SChangwoo Min 
127d8eef045SChangwoo Min 	return genlmsg_reply(msg, info);
128d8eef045SChangwoo Min 
129d8eef045SChangwoo Min out_cancel_msg:
130d8eef045SChangwoo Min 	genlmsg_cancel(msg, hdr);
131d8eef045SChangwoo Min out_free_msg:
132d8eef045SChangwoo Min 	nlmsg_free(msg);
133d8eef045SChangwoo Min 	return ret;
134e4ed8d26SChangwoo Min }
135e4ed8d26SChangwoo Min 
dev_energymodel_nl_get_perf_domains_dumpit(struct sk_buff * skb,struct netlink_callback * cb)136*380ff27aSChangwoo Min int dev_energymodel_nl_get_perf_domains_dumpit(struct sk_buff *skb,
137*380ff27aSChangwoo Min 						struct netlink_callback *cb)
138*380ff27aSChangwoo Min {
139*380ff27aSChangwoo Min 	struct dump_ctx ctx = {
140*380ff27aSChangwoo Min 		.idx = 0,
141*380ff27aSChangwoo Min 		.start = cb->args[0],
142*380ff27aSChangwoo Min 		.skb = skb,
143*380ff27aSChangwoo Min 		.cb = cb,
144*380ff27aSChangwoo Min 	};
145*380ff27aSChangwoo Min 
146*380ff27aSChangwoo Min 	return for_each_em_perf_domain(__em_nl_get_pd_for_dump, &ctx);
147*380ff27aSChangwoo Min }
148*380ff27aSChangwoo Min 
__em_nl_get_pd_table_id(struct nlattr ** attrs)149f2d2946eSChangwoo Min static struct em_perf_domain *__em_nl_get_pd_table_id(struct nlattr **attrs)
150f2d2946eSChangwoo Min {
151f2d2946eSChangwoo Min 	struct em_perf_domain *pd;
152f2d2946eSChangwoo Min 	int id;
153f2d2946eSChangwoo Min 
154caa07a81SChangwoo Min 	if (!attrs[DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID])
155f2d2946eSChangwoo Min 		return NULL;
156f2d2946eSChangwoo Min 
157caa07a81SChangwoo Min 	id = nla_get_u32(attrs[DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID]);
158f2d2946eSChangwoo Min 	pd = em_perf_domain_get_by_id(id);
159f2d2946eSChangwoo Min 	return pd;
160f2d2946eSChangwoo Min }
161f2d2946eSChangwoo Min 
__em_nl_get_pd_table_size(const struct em_perf_domain * pd)162f2d2946eSChangwoo Min static int __em_nl_get_pd_table_size(const struct em_perf_domain *pd)
163f2d2946eSChangwoo Min {
164f2d2946eSChangwoo Min 	int id_sz, ps_sz;
165f2d2946eSChangwoo Min 
166caa07a81SChangwoo Min 	id_sz = nla_total_size(sizeof(u32));
167caa07a81SChangwoo Min 		/* DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID */
168caa07a81SChangwoo Min 	ps_sz = nla_total_size(0) +
169caa07a81SChangwoo Min 		/* DEV_ENERGYMODEL_A_PERF_TABLE_PERF_STATE */
170caa07a81SChangwoo Min 		nla_total_size_64bit(sizeof(u64)) +
171caa07a81SChangwoo Min 		/* DEV_ENERGYMODEL_A_PERF_STATE_PERFORMANCE */
172caa07a81SChangwoo Min 		nla_total_size_64bit(sizeof(u64)) +
173caa07a81SChangwoo Min 		/* DEV_ENERGYMODEL_A_PERF_STATE_FREQUENCY */
174caa07a81SChangwoo Min 		nla_total_size_64bit(sizeof(u64)) +
175caa07a81SChangwoo Min 		/* DEV_ENERGYMODEL_A_PERF_STATE_POWER */
176caa07a81SChangwoo Min 		nla_total_size_64bit(sizeof(u64)) +
177caa07a81SChangwoo Min 		/* DEV_ENERGYMODEL_A_PERF_STATE_COST */
178caa07a81SChangwoo Min 		nla_total_size_64bit(sizeof(u64));
179caa07a81SChangwoo Min 		/* DEV_ENERGYMODEL_A_PERF_STATE_FLAGS */
180f2d2946eSChangwoo Min 	ps_sz *= pd->nr_perf_states;
181f2d2946eSChangwoo Min 
182f2d2946eSChangwoo Min 	return nlmsg_total_size(genlmsg_msg_size(id_sz + ps_sz));
183f2d2946eSChangwoo Min }
184f2d2946eSChangwoo Min 
185caa07a81SChangwoo Min static
__em_nl_get_pd_table(struct sk_buff * msg,const struct em_perf_domain * pd)186caa07a81SChangwoo Min int __em_nl_get_pd_table(struct sk_buff *msg, const struct em_perf_domain *pd)
187f2d2946eSChangwoo Min {
188f2d2946eSChangwoo Min 	struct em_perf_state *table, *ps;
189f2d2946eSChangwoo Min 	struct nlattr *entry;
190f2d2946eSChangwoo Min 	int i;
191f2d2946eSChangwoo Min 
192caa07a81SChangwoo Min 	if (nla_put_u32(msg, DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID,
193caa07a81SChangwoo Min 			pd->id))
194f2d2946eSChangwoo Min 		goto out_err;
195f2d2946eSChangwoo Min 
196f2d2946eSChangwoo Min 	rcu_read_lock();
197f2d2946eSChangwoo Min 	table = em_perf_state_from_pd((struct em_perf_domain *)pd);
198f2d2946eSChangwoo Min 
199f2d2946eSChangwoo Min 	for (i = 0; i < pd->nr_perf_states; i++) {
200f2d2946eSChangwoo Min 		ps = &table[i];
201f2d2946eSChangwoo Min 
202caa07a81SChangwoo Min 		entry = nla_nest_start(msg,
203caa07a81SChangwoo Min 				       DEV_ENERGYMODEL_A_PERF_TABLE_PERF_STATE);
204f2d2946eSChangwoo Min 		if (!entry)
205f2d2946eSChangwoo Min 			goto out_unlock_ps;
206f2d2946eSChangwoo Min 
207caa07a81SChangwoo Min 		if (nla_put_u64_64bit(msg,
208caa07a81SChangwoo Min 				      DEV_ENERGYMODEL_A_PERF_STATE_PERFORMANCE,
209caa07a81SChangwoo Min 				      ps->performance,
210caa07a81SChangwoo Min 				      DEV_ENERGYMODEL_A_PERF_STATE_PAD))
211f2d2946eSChangwoo Min 			goto out_cancel_ps_nest;
212caa07a81SChangwoo Min 		if (nla_put_u64_64bit(msg,
213caa07a81SChangwoo Min 				      DEV_ENERGYMODEL_A_PERF_STATE_FREQUENCY,
214caa07a81SChangwoo Min 				      ps->frequency,
215caa07a81SChangwoo Min 				      DEV_ENERGYMODEL_A_PERF_STATE_PAD))
216f2d2946eSChangwoo Min 			goto out_cancel_ps_nest;
217caa07a81SChangwoo Min 		if (nla_put_u64_64bit(msg,
218caa07a81SChangwoo Min 				      DEV_ENERGYMODEL_A_PERF_STATE_POWER,
219caa07a81SChangwoo Min 				      ps->power,
220caa07a81SChangwoo Min 				      DEV_ENERGYMODEL_A_PERF_STATE_PAD))
221f2d2946eSChangwoo Min 			goto out_cancel_ps_nest;
222caa07a81SChangwoo Min 		if (nla_put_u64_64bit(msg,
223caa07a81SChangwoo Min 				      DEV_ENERGYMODEL_A_PERF_STATE_COST,
224caa07a81SChangwoo Min 				      ps->cost,
225caa07a81SChangwoo Min 				      DEV_ENERGYMODEL_A_PERF_STATE_PAD))
226f2d2946eSChangwoo Min 			goto out_cancel_ps_nest;
227caa07a81SChangwoo Min 		if (nla_put_u64_64bit(msg,
228caa07a81SChangwoo Min 				      DEV_ENERGYMODEL_A_PERF_STATE_FLAGS,
229caa07a81SChangwoo Min 				      ps->flags,
230caa07a81SChangwoo Min 				      DEV_ENERGYMODEL_A_PERF_STATE_PAD))
231f2d2946eSChangwoo Min 			goto out_cancel_ps_nest;
232f2d2946eSChangwoo Min 
233f2d2946eSChangwoo Min 		nla_nest_end(msg, entry);
234f2d2946eSChangwoo Min 	}
235f2d2946eSChangwoo Min 	rcu_read_unlock();
236f2d2946eSChangwoo Min 	return 0;
237f2d2946eSChangwoo Min 
238f2d2946eSChangwoo Min out_cancel_ps_nest:
239f2d2946eSChangwoo Min 	nla_nest_cancel(msg, entry);
240f2d2946eSChangwoo Min out_unlock_ps:
241f2d2946eSChangwoo Min 	rcu_read_unlock();
242f2d2946eSChangwoo Min out_err:
243f2d2946eSChangwoo Min 	return -EMSGSIZE;
244f2d2946eSChangwoo Min }
245f2d2946eSChangwoo Min 
dev_energymodel_nl_get_perf_table_doit(struct sk_buff * skb,struct genl_info * info)246caa07a81SChangwoo Min int dev_energymodel_nl_get_perf_table_doit(struct sk_buff *skb,
247caa07a81SChangwoo Min 					    struct genl_info *info)
248e4ed8d26SChangwoo Min {
249f2d2946eSChangwoo Min 	int cmd = info->genlhdr->cmd;
250f2d2946eSChangwoo Min 	int msg_sz, ret = -EMSGSIZE;
251f2d2946eSChangwoo Min 	struct em_perf_domain *pd;
252f2d2946eSChangwoo Min 	struct sk_buff *msg;
253f2d2946eSChangwoo Min 	void *hdr;
254f2d2946eSChangwoo Min 
255f2d2946eSChangwoo Min 	pd = __em_nl_get_pd_table_id(info->attrs);
256f2d2946eSChangwoo Min 	if (!pd)
257f2d2946eSChangwoo Min 		return -EINVAL;
258f2d2946eSChangwoo Min 
259f2d2946eSChangwoo Min 	msg_sz = __em_nl_get_pd_table_size(pd);
260f2d2946eSChangwoo Min 
261f2d2946eSChangwoo Min 	msg = genlmsg_new(msg_sz, GFP_KERNEL);
262f2d2946eSChangwoo Min 	if (!msg)
263f2d2946eSChangwoo Min 		return -ENOMEM;
264f2d2946eSChangwoo Min 
265caa07a81SChangwoo Min 	hdr = genlmsg_put_reply(msg, info, &dev_energymodel_nl_family, 0, cmd);
266f2d2946eSChangwoo Min 	if (!hdr)
267f2d2946eSChangwoo Min 		goto out_free_msg;
268f2d2946eSChangwoo Min 
269f2d2946eSChangwoo Min 	ret = __em_nl_get_pd_table(msg, pd);
270f2d2946eSChangwoo Min 	if (ret)
271f2d2946eSChangwoo Min 		goto out_free_msg;
272f2d2946eSChangwoo Min 
273f2d2946eSChangwoo Min 	genlmsg_end(msg, hdr);
274f2d2946eSChangwoo Min 	return genlmsg_reply(msg, info);
275f2d2946eSChangwoo Min 
276f2d2946eSChangwoo Min out_free_msg:
277f2d2946eSChangwoo Min 	nlmsg_free(msg);
278f2d2946eSChangwoo Min 	return ret;
279e4ed8d26SChangwoo Min }
280e4ed8d26SChangwoo Min 
281b2b1bbcaSChangwoo Min 
282b2b1bbcaSChangwoo Min /**************************** Event encoding *********************************/
__em_notify_pd_table(const struct em_perf_domain * pd,int ntf_type)283b95a0c02SChangwoo Min static void __em_notify_pd_table(const struct em_perf_domain *pd, int ntf_type)
284b95a0c02SChangwoo Min {
285b95a0c02SChangwoo Min 	struct sk_buff *msg;
286b95a0c02SChangwoo Min 	int msg_sz, ret = -EMSGSIZE;
287b95a0c02SChangwoo Min 	void *hdr;
288b95a0c02SChangwoo Min 
289caa07a81SChangwoo Min 	if (!genl_has_listeners(&dev_energymodel_nl_family, &init_net, DEV_ENERGYMODEL_NLGRP_EVENT))
290b95a0c02SChangwoo Min 		return;
291b95a0c02SChangwoo Min 
292b95a0c02SChangwoo Min 	msg_sz = __em_nl_get_pd_table_size(pd);
293b95a0c02SChangwoo Min 
294b95a0c02SChangwoo Min 	msg = genlmsg_new(msg_sz, GFP_KERNEL);
295b95a0c02SChangwoo Min 	if (!msg)
296b95a0c02SChangwoo Min 		return;
297b95a0c02SChangwoo Min 
298caa07a81SChangwoo Min 	hdr = genlmsg_put(msg, 0, 0, &dev_energymodel_nl_family, 0, ntf_type);
299b95a0c02SChangwoo Min 	if (!hdr)
300b95a0c02SChangwoo Min 		goto out_free_msg;
301b95a0c02SChangwoo Min 
302b95a0c02SChangwoo Min 	ret = __em_nl_get_pd_table(msg, pd);
303b95a0c02SChangwoo Min 	if (ret)
304b95a0c02SChangwoo Min 		goto out_free_msg;
305b95a0c02SChangwoo Min 
306b95a0c02SChangwoo Min 	genlmsg_end(msg, hdr);
307b95a0c02SChangwoo Min 
308caa07a81SChangwoo Min 	genlmsg_multicast(&dev_energymodel_nl_family, msg, 0,
309caa07a81SChangwoo Min 			  DEV_ENERGYMODEL_NLGRP_EVENT, GFP_KERNEL);
310b95a0c02SChangwoo Min 
311b95a0c02SChangwoo Min 	return;
312b95a0c02SChangwoo Min 
313b95a0c02SChangwoo Min out_free_msg:
314b95a0c02SChangwoo Min 	nlmsg_free(msg);
315b95a0c02SChangwoo Min }
316b95a0c02SChangwoo Min 
em_notify_pd_created(const struct em_perf_domain * pd)317b95a0c02SChangwoo Min void em_notify_pd_created(const struct em_perf_domain *pd)
318b95a0c02SChangwoo Min {
319caa07a81SChangwoo Min 	__em_notify_pd_table(pd, DEV_ENERGYMODEL_CMD_PERF_DOMAIN_CREATED);
320b95a0c02SChangwoo Min }
321b95a0c02SChangwoo Min 
em_notify_pd_updated(const struct em_perf_domain * pd)322b95a0c02SChangwoo Min void em_notify_pd_updated(const struct em_perf_domain *pd)
323b95a0c02SChangwoo Min {
324caa07a81SChangwoo Min 	__em_notify_pd_table(pd, DEV_ENERGYMODEL_CMD_PERF_DOMAIN_UPDATED);
325b95a0c02SChangwoo Min }
326b95a0c02SChangwoo Min 
__em_notify_pd_deleted_size(const struct em_perf_domain * pd)327b2b1bbcaSChangwoo Min static int __em_notify_pd_deleted_size(const struct em_perf_domain *pd)
328b2b1bbcaSChangwoo Min {
329caa07a81SChangwoo Min 	int id_sz = nla_total_size(sizeof(u32)); /* DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID */
330b2b1bbcaSChangwoo Min 
331b2b1bbcaSChangwoo Min 	return nlmsg_total_size(genlmsg_msg_size(id_sz));
332b2b1bbcaSChangwoo Min }
333b2b1bbcaSChangwoo Min 
em_notify_pd_deleted(const struct em_perf_domain * pd)334b2b1bbcaSChangwoo Min void em_notify_pd_deleted(const struct em_perf_domain *pd)
335b2b1bbcaSChangwoo Min {
336b2b1bbcaSChangwoo Min 	struct sk_buff *msg;
337b2b1bbcaSChangwoo Min 	void *hdr;
338b2b1bbcaSChangwoo Min 	int msg_sz;
339b2b1bbcaSChangwoo Min 
340caa07a81SChangwoo Min 	if (!genl_has_listeners(&dev_energymodel_nl_family, &init_net,
341caa07a81SChangwoo Min 				DEV_ENERGYMODEL_NLGRP_EVENT))
342b2b1bbcaSChangwoo Min 		return;
343b2b1bbcaSChangwoo Min 
344b2b1bbcaSChangwoo Min 	msg_sz = __em_notify_pd_deleted_size(pd);
345b2b1bbcaSChangwoo Min 
346b2b1bbcaSChangwoo Min 	msg = genlmsg_new(msg_sz, GFP_KERNEL);
347b2b1bbcaSChangwoo Min 	if (!msg)
348b2b1bbcaSChangwoo Min 		return;
349b2b1bbcaSChangwoo Min 
350caa07a81SChangwoo Min 	hdr = genlmsg_put(msg, 0, 0, &dev_energymodel_nl_family, 0,
351caa07a81SChangwoo Min 			  DEV_ENERGYMODEL_CMD_PERF_DOMAIN_DELETED);
352b2b1bbcaSChangwoo Min 	if (!hdr)
353b2b1bbcaSChangwoo Min 		goto out_free_msg;
354b2b1bbcaSChangwoo Min 
355caa07a81SChangwoo Min 	if (nla_put_u32(msg, DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID,
356caa07a81SChangwoo Min 			pd->id))
357b2b1bbcaSChangwoo Min 		goto out_free_msg;
358b2b1bbcaSChangwoo Min 
359b2b1bbcaSChangwoo Min 	genlmsg_end(msg, hdr);
360b2b1bbcaSChangwoo Min 
361caa07a81SChangwoo Min 	genlmsg_multicast(&dev_energymodel_nl_family, msg, 0,
362caa07a81SChangwoo Min 			  DEV_ENERGYMODEL_NLGRP_EVENT, GFP_KERNEL);
363b2b1bbcaSChangwoo Min 
364b2b1bbcaSChangwoo Min 	return;
365b2b1bbcaSChangwoo Min 
366b2b1bbcaSChangwoo Min out_free_msg:
367b2b1bbcaSChangwoo Min 	nlmsg_free(msg);
368b2b1bbcaSChangwoo Min }
369b2b1bbcaSChangwoo Min 
370b2b1bbcaSChangwoo Min /**************************** Initialization *********************************/
em_netlink_init(void)371e4ed8d26SChangwoo Min static int __init em_netlink_init(void)
372e4ed8d26SChangwoo Min {
373caa07a81SChangwoo Min 	return genl_register_family(&dev_energymodel_nl_family);
374e4ed8d26SChangwoo Min }
375e4ed8d26SChangwoo Min postcore_initcall(em_netlink_init);
376