cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

thermal_netlink.c (18018B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright 2020 Linaro Limited
      4 *
      5 * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
      6 *
      7 * Generic netlink for thermal management framework
      8 */
      9#include <linux/module.h>
     10#include <linux/kernel.h>
     11#include <net/genetlink.h>
     12#include <uapi/linux/thermal.h>
     13
     14#include "thermal_core.h"
     15
     16static const struct genl_multicast_group thermal_genl_mcgrps[] = {
     17	{ .name = THERMAL_GENL_SAMPLING_GROUP_NAME, },
     18	{ .name = THERMAL_GENL_EVENT_GROUP_NAME,  },
     19};
     20
     21static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = {
     22	/* Thermal zone */
     23	[THERMAL_GENL_ATTR_TZ]			= { .type = NLA_NESTED },
     24	[THERMAL_GENL_ATTR_TZ_ID]		= { .type = NLA_U32 },
     25	[THERMAL_GENL_ATTR_TZ_TEMP]		= { .type = NLA_U32 },
     26	[THERMAL_GENL_ATTR_TZ_TRIP]		= { .type = NLA_NESTED },
     27	[THERMAL_GENL_ATTR_TZ_TRIP_ID]		= { .type = NLA_U32 },
     28	[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]	= { .type = NLA_U32 },
     29	[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]	= { .type = NLA_U32 },
     30	[THERMAL_GENL_ATTR_TZ_TRIP_HYST]	= { .type = NLA_U32 },
     31	[THERMAL_GENL_ATTR_TZ_MODE]		= { .type = NLA_U32 },
     32	[THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT]	= { .type = NLA_U32 },
     33	[THERMAL_GENL_ATTR_TZ_NAME]		= { .type = NLA_STRING,
     34						    .len = THERMAL_NAME_LENGTH },
     35	/* Governor(s) */
     36	[THERMAL_GENL_ATTR_TZ_GOV]		= { .type = NLA_NESTED },
     37	[THERMAL_GENL_ATTR_TZ_GOV_NAME]		= { .type = NLA_STRING,
     38						    .len = THERMAL_NAME_LENGTH },
     39	/* Cooling devices */
     40	[THERMAL_GENL_ATTR_CDEV]		= { .type = NLA_NESTED },
     41	[THERMAL_GENL_ATTR_CDEV_ID]		= { .type = NLA_U32 },
     42	[THERMAL_GENL_ATTR_CDEV_CUR_STATE]	= { .type = NLA_U32 },
     43	[THERMAL_GENL_ATTR_CDEV_MAX_STATE]	= { .type = NLA_U32 },
     44	[THERMAL_GENL_ATTR_CDEV_NAME]		= { .type = NLA_STRING,
     45						    .len = THERMAL_NAME_LENGTH },
     46	/* CPU capabilities */
     47	[THERMAL_GENL_ATTR_CPU_CAPABILITY]		= { .type = NLA_NESTED },
     48	[THERMAL_GENL_ATTR_CPU_CAPABILITY_ID]		= { .type = NLA_U32 },
     49	[THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE]	= { .type = NLA_U32 },
     50	[THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY]	= { .type = NLA_U32 },
     51};
     52
     53struct param {
     54	struct nlattr **attrs;
     55	struct sk_buff *msg;
     56	const char *name;
     57	int tz_id;
     58	int cdev_id;
     59	int trip_id;
     60	int trip_temp;
     61	int trip_type;
     62	int trip_hyst;
     63	int temp;
     64	int cdev_state;
     65	int cdev_max_state;
     66	struct thermal_genl_cpu_caps *cpu_capabilities;
     67	int cpu_capabilities_count;
     68};
     69
     70typedef int (*cb_t)(struct param *);
     71
     72static struct genl_family thermal_gnl_family;
     73
     74/************************** Sampling encoding *******************************/
     75
     76int thermal_genl_sampling_temp(int id, int temp)
     77{
     78	struct sk_buff *skb;
     79	void *hdr;
     80
     81	skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
     82	if (!skb)
     83		return -ENOMEM;
     84
     85	hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0,
     86			  THERMAL_GENL_SAMPLING_TEMP);
     87	if (!hdr)
     88		goto out_free;
     89
     90	if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_ID, id))
     91		goto out_cancel;
     92
     93	if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_TEMP, temp))
     94		goto out_cancel;
     95
     96	genlmsg_end(skb, hdr);
     97
     98	genlmsg_multicast(&thermal_gnl_family, skb, 0, 0, GFP_KERNEL);
     99
    100	return 0;
    101out_cancel:
    102	genlmsg_cancel(skb, hdr);
    103out_free:
    104	nlmsg_free(skb);
    105
    106	return -EMSGSIZE;
    107}
    108
    109/**************************** Event encoding *********************************/
    110
    111static int thermal_genl_event_tz_create(struct param *p)
    112{
    113	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
    114	    nla_put_string(p->msg, THERMAL_GENL_ATTR_TZ_NAME, p->name))
    115		return -EMSGSIZE;
    116
    117	return 0;
    118}
    119
    120static int thermal_genl_event_tz(struct param *p)
    121{
    122	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id))
    123		return -EMSGSIZE;
    124
    125	return 0;
    126}
    127
    128static int thermal_genl_event_tz_trip_up(struct param *p)
    129{
    130	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
    131	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) ||
    132	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TEMP, p->temp))
    133		return -EMSGSIZE;
    134
    135	return 0;
    136}
    137
    138static int thermal_genl_event_tz_trip_add(struct param *p)
    139{
    140	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
    141	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) ||
    142	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, p->trip_type) ||
    143	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, p->trip_temp) ||
    144	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, p->trip_hyst))
    145		return -EMSGSIZE;
    146
    147	return 0;
    148}
    149
    150static int thermal_genl_event_tz_trip_delete(struct param *p)
    151{
    152	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
    153	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id))
    154		return -EMSGSIZE;
    155
    156	return 0;
    157}
    158
    159static int thermal_genl_event_cdev_add(struct param *p)
    160{
    161	if (nla_put_string(p->msg, THERMAL_GENL_ATTR_CDEV_NAME,
    162			   p->name) ||
    163	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
    164			p->cdev_id) ||
    165	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_MAX_STATE,
    166			p->cdev_max_state))
    167		return -EMSGSIZE;
    168
    169	return 0;
    170}
    171
    172static int thermal_genl_event_cdev_delete(struct param *p)
    173{
    174	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID, p->cdev_id))
    175		return -EMSGSIZE;
    176
    177	return 0;
    178}
    179
    180static int thermal_genl_event_cdev_state_update(struct param *p)
    181{
    182	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
    183			p->cdev_id) ||
    184	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_CUR_STATE,
    185			p->cdev_state))
    186		return -EMSGSIZE;
    187
    188	return 0;
    189}
    190
    191static int thermal_genl_event_gov_change(struct param *p)
    192{
    193	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
    194	    nla_put_string(p->msg, THERMAL_GENL_ATTR_GOV_NAME, p->name))
    195		return -EMSGSIZE;
    196
    197	return 0;
    198}
    199
    200static int thermal_genl_event_cpu_capability_change(struct param *p)
    201{
    202	struct thermal_genl_cpu_caps *cpu_cap = p->cpu_capabilities;
    203	struct sk_buff *msg = p->msg;
    204	struct nlattr *start_cap;
    205	int i;
    206
    207	start_cap = nla_nest_start(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY);
    208	if (!start_cap)
    209		return -EMSGSIZE;
    210
    211	for (i = 0; i < p->cpu_capabilities_count; ++i) {
    212		if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_ID,
    213				cpu_cap->cpu))
    214			goto out_cancel_nest;
    215
    216		if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE,
    217				cpu_cap->performance))
    218			goto out_cancel_nest;
    219
    220		if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY,
    221				cpu_cap->efficiency))
    222			goto out_cancel_nest;
    223
    224		++cpu_cap;
    225	}
    226
    227	nla_nest_end(msg, start_cap);
    228
    229	return 0;
    230out_cancel_nest:
    231	nla_nest_cancel(msg, start_cap);
    232
    233	return -EMSGSIZE;
    234}
    235
    236int thermal_genl_event_tz_delete(struct param *p)
    237	__attribute__((alias("thermal_genl_event_tz")));
    238
    239int thermal_genl_event_tz_enable(struct param *p)
    240	__attribute__((alias("thermal_genl_event_tz")));
    241
    242int thermal_genl_event_tz_disable(struct param *p)
    243	__attribute__((alias("thermal_genl_event_tz")));
    244
    245int thermal_genl_event_tz_trip_down(struct param *p)
    246	__attribute__((alias("thermal_genl_event_tz_trip_up")));
    247
    248int thermal_genl_event_tz_trip_change(struct param *p)
    249	__attribute__((alias("thermal_genl_event_tz_trip_add")));
    250
    251static cb_t event_cb[] = {
    252	[THERMAL_GENL_EVENT_TZ_CREATE]		= thermal_genl_event_tz_create,
    253	[THERMAL_GENL_EVENT_TZ_DELETE]		= thermal_genl_event_tz_delete,
    254	[THERMAL_GENL_EVENT_TZ_ENABLE]		= thermal_genl_event_tz_enable,
    255	[THERMAL_GENL_EVENT_TZ_DISABLE]		= thermal_genl_event_tz_disable,
    256	[THERMAL_GENL_EVENT_TZ_TRIP_UP]		= thermal_genl_event_tz_trip_up,
    257	[THERMAL_GENL_EVENT_TZ_TRIP_DOWN]	= thermal_genl_event_tz_trip_down,
    258	[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE]	= thermal_genl_event_tz_trip_change,
    259	[THERMAL_GENL_EVENT_TZ_TRIP_ADD]	= thermal_genl_event_tz_trip_add,
    260	[THERMAL_GENL_EVENT_TZ_TRIP_DELETE]	= thermal_genl_event_tz_trip_delete,
    261	[THERMAL_GENL_EVENT_CDEV_ADD]		= thermal_genl_event_cdev_add,
    262	[THERMAL_GENL_EVENT_CDEV_DELETE]	= thermal_genl_event_cdev_delete,
    263	[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE]	= thermal_genl_event_cdev_state_update,
    264	[THERMAL_GENL_EVENT_TZ_GOV_CHANGE]	= thermal_genl_event_gov_change,
    265	[THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE] = thermal_genl_event_cpu_capability_change,
    266};
    267
    268/*
    269 * Generic netlink event encoding
    270 */
    271static int thermal_genl_send_event(enum thermal_genl_event event,
    272				   struct param *p)
    273{
    274	struct sk_buff *msg;
    275	int ret = -EMSGSIZE;
    276	void *hdr;
    277
    278	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
    279	if (!msg)
    280		return -ENOMEM;
    281	p->msg = msg;
    282
    283	hdr = genlmsg_put(msg, 0, 0, &thermal_gnl_family, 0, event);
    284	if (!hdr)
    285		goto out_free_msg;
    286
    287	ret = event_cb[event](p);
    288	if (ret)
    289		goto out_cancel_msg;
    290
    291	genlmsg_end(msg, hdr);
    292
    293	genlmsg_multicast(&thermal_gnl_family, msg, 0, 1, GFP_KERNEL);
    294
    295	return 0;
    296
    297out_cancel_msg:
    298	genlmsg_cancel(msg, hdr);
    299out_free_msg:
    300	nlmsg_free(msg);
    301
    302	return ret;
    303}
    304
    305int thermal_notify_tz_create(int tz_id, const char *name)
    306{
    307	struct param p = { .tz_id = tz_id, .name = name };
    308
    309	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_CREATE, &p);
    310}
    311
    312int thermal_notify_tz_delete(int tz_id)
    313{
    314	struct param p = { .tz_id = tz_id };
    315
    316	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DELETE, &p);
    317}
    318
    319int thermal_notify_tz_enable(int tz_id)
    320{
    321	struct param p = { .tz_id = tz_id };
    322
    323	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_ENABLE, &p);
    324}
    325
    326int thermal_notify_tz_disable(int tz_id)
    327{
    328	struct param p = { .tz_id = tz_id };
    329
    330	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DISABLE, &p);
    331}
    332
    333int thermal_notify_tz_trip_down(int tz_id, int trip_id, int temp)
    334{
    335	struct param p = { .tz_id = tz_id, .trip_id = trip_id, .temp = temp };
    336
    337	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DOWN, &p);
    338}
    339
    340int thermal_notify_tz_trip_up(int tz_id, int trip_id, int temp)
    341{
    342	struct param p = { .tz_id = tz_id, .trip_id = trip_id, .temp = temp };
    343
    344	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_UP, &p);
    345}
    346
    347int thermal_notify_tz_trip_add(int tz_id, int trip_id, int trip_type,
    348			       int trip_temp, int trip_hyst)
    349{
    350	struct param p = { .tz_id = tz_id, .trip_id = trip_id,
    351			   .trip_type = trip_type, .trip_temp = trip_temp,
    352			   .trip_hyst = trip_hyst };
    353
    354	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_ADD, &p);
    355}
    356
    357int thermal_notify_tz_trip_delete(int tz_id, int trip_id)
    358{
    359	struct param p = { .tz_id = tz_id, .trip_id = trip_id };
    360
    361	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DELETE, &p);
    362}
    363
    364int thermal_notify_tz_trip_change(int tz_id, int trip_id, int trip_type,
    365				  int trip_temp, int trip_hyst)
    366{
    367	struct param p = { .tz_id = tz_id, .trip_id = trip_id,
    368			   .trip_type = trip_type, .trip_temp = trip_temp,
    369			   .trip_hyst = trip_hyst };
    370
    371	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_CHANGE, &p);
    372}
    373
    374int thermal_notify_cdev_state_update(int cdev_id, int cdev_state)
    375{
    376	struct param p = { .cdev_id = cdev_id, .cdev_state = cdev_state };
    377
    378	return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, &p);
    379}
    380
    381int thermal_notify_cdev_add(int cdev_id, const char *name, int cdev_max_state)
    382{
    383	struct param p = { .cdev_id = cdev_id, .name = name,
    384			   .cdev_max_state = cdev_max_state };
    385
    386	return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_ADD, &p);
    387}
    388
    389int thermal_notify_cdev_delete(int cdev_id)
    390{
    391	struct param p = { .cdev_id = cdev_id };
    392
    393	return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_DELETE, &p);
    394}
    395
    396int thermal_notify_tz_gov_change(int tz_id, const char *name)
    397{
    398	struct param p = { .tz_id = tz_id, .name = name };
    399
    400	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_GOV_CHANGE, &p);
    401}
    402
    403int thermal_genl_cpu_capability_event(int count,
    404				      struct thermal_genl_cpu_caps *caps)
    405{
    406	struct param p = { .cpu_capabilities_count = count, .cpu_capabilities = caps };
    407
    408	return thermal_genl_send_event(THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE, &p);
    409}
    410EXPORT_SYMBOL_GPL(thermal_genl_cpu_capability_event);
    411
    412/*************************** Command encoding ********************************/
    413
    414static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz,
    415					void *data)
    416{
    417	struct sk_buff *msg = data;
    418
    419	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, tz->id) ||
    420	    nla_put_string(msg, THERMAL_GENL_ATTR_TZ_NAME, tz->type))
    421		return -EMSGSIZE;
    422
    423	return 0;
    424}
    425
    426static int thermal_genl_cmd_tz_get_id(struct param *p)
    427{
    428	struct sk_buff *msg = p->msg;
    429	struct nlattr *start_tz;
    430	int ret;
    431
    432	start_tz = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ);
    433	if (!start_tz)
    434		return -EMSGSIZE;
    435
    436	ret = for_each_thermal_zone(__thermal_genl_cmd_tz_get_id, msg);
    437	if (ret)
    438		goto out_cancel_nest;
    439
    440	nla_nest_end(msg, start_tz);
    441
    442	return 0;
    443
    444out_cancel_nest:
    445	nla_nest_cancel(msg, start_tz);
    446
    447	return ret;
    448}
    449
    450static int thermal_genl_cmd_tz_get_trip(struct param *p)
    451{
    452	struct sk_buff *msg = p->msg;
    453	struct thermal_zone_device *tz;
    454	struct nlattr *start_trip;
    455	int i, id;
    456
    457	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
    458		return -EINVAL;
    459
    460	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
    461
    462	tz = thermal_zone_get_by_id(id);
    463	if (!tz)
    464		return -EINVAL;
    465
    466	start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ_TRIP);
    467	if (!start_trip)
    468		return -EMSGSIZE;
    469
    470	mutex_lock(&tz->lock);
    471
    472	for (i = 0; i < tz->trips; i++) {
    473
    474		enum thermal_trip_type type;
    475		int temp, hyst = 0;
    476
    477		tz->ops->get_trip_type(tz, i, &type);
    478		tz->ops->get_trip_temp(tz, i, &temp);
    479		if (tz->ops->get_trip_hyst)
    480			tz->ops->get_trip_hyst(tz, i, &hyst);
    481
    482		if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, i) ||
    483		    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, type) ||
    484		    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, temp) ||
    485		    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, hyst))
    486			goto out_cancel_nest;
    487	}
    488
    489	mutex_unlock(&tz->lock);
    490
    491	nla_nest_end(msg, start_trip);
    492
    493	return 0;
    494
    495out_cancel_nest:
    496	mutex_unlock(&tz->lock);
    497
    498	return -EMSGSIZE;
    499}
    500
    501static int thermal_genl_cmd_tz_get_temp(struct param *p)
    502{
    503	struct sk_buff *msg = p->msg;
    504	struct thermal_zone_device *tz;
    505	int temp, ret, id;
    506
    507	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
    508		return -EINVAL;
    509
    510	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
    511
    512	tz = thermal_zone_get_by_id(id);
    513	if (!tz)
    514		return -EINVAL;
    515
    516	ret = thermal_zone_get_temp(tz, &temp);
    517	if (ret)
    518		return ret;
    519
    520	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
    521	    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TEMP, temp))
    522		return -EMSGSIZE;
    523
    524	return 0;
    525}
    526
    527static int thermal_genl_cmd_tz_get_gov(struct param *p)
    528{
    529	struct sk_buff *msg = p->msg;
    530	struct thermal_zone_device *tz;
    531	int id, ret = 0;
    532
    533	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
    534		return -EINVAL;
    535
    536	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
    537
    538	tz = thermal_zone_get_by_id(id);
    539	if (!tz)
    540		return -EINVAL;
    541
    542	mutex_lock(&tz->lock);
    543
    544	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
    545	    nla_put_string(msg, THERMAL_GENL_ATTR_TZ_GOV_NAME,
    546			   tz->governor->name))
    547		ret = -EMSGSIZE;
    548
    549	mutex_unlock(&tz->lock);
    550
    551	return ret;
    552}
    553
    554static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device *cdev,
    555				       void *data)
    556{
    557	struct sk_buff *msg = data;
    558
    559	if (nla_put_u32(msg, THERMAL_GENL_ATTR_CDEV_ID, cdev->id))
    560		return -EMSGSIZE;
    561
    562	if (nla_put_string(msg, THERMAL_GENL_ATTR_CDEV_NAME, cdev->type))
    563		return -EMSGSIZE;
    564
    565	return 0;
    566}
    567
    568static int thermal_genl_cmd_cdev_get(struct param *p)
    569{
    570	struct sk_buff *msg = p->msg;
    571	struct nlattr *start_cdev;
    572	int ret;
    573
    574	start_cdev = nla_nest_start(msg, THERMAL_GENL_ATTR_CDEV);
    575	if (!start_cdev)
    576		return -EMSGSIZE;
    577
    578	ret = for_each_thermal_cooling_device(__thermal_genl_cmd_cdev_get, msg);
    579	if (ret)
    580		goto out_cancel_nest;
    581
    582	nla_nest_end(msg, start_cdev);
    583
    584	return 0;
    585out_cancel_nest:
    586	nla_nest_cancel(msg, start_cdev);
    587
    588	return ret;
    589}
    590
    591static cb_t cmd_cb[] = {
    592	[THERMAL_GENL_CMD_TZ_GET_ID]	= thermal_genl_cmd_tz_get_id,
    593	[THERMAL_GENL_CMD_TZ_GET_TRIP]	= thermal_genl_cmd_tz_get_trip,
    594	[THERMAL_GENL_CMD_TZ_GET_TEMP]	= thermal_genl_cmd_tz_get_temp,
    595	[THERMAL_GENL_CMD_TZ_GET_GOV]	= thermal_genl_cmd_tz_get_gov,
    596	[THERMAL_GENL_CMD_CDEV_GET]	= thermal_genl_cmd_cdev_get,
    597};
    598
    599static int thermal_genl_cmd_dumpit(struct sk_buff *skb,
    600				   struct netlink_callback *cb)
    601{
    602	struct param p = { .msg = skb };
    603	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
    604	int cmd = info->op.cmd;
    605	int ret;
    606	void *hdr;
    607
    608	hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0, cmd);
    609	if (!hdr)
    610		return -EMSGSIZE;
    611
    612	ret = cmd_cb[cmd](&p);
    613	if (ret)
    614		goto out_cancel_msg;
    615
    616	genlmsg_end(skb, hdr);
    617
    618	return 0;
    619
    620out_cancel_msg:
    621	genlmsg_cancel(skb, hdr);
    622
    623	return ret;
    624}
    625
    626static int thermal_genl_cmd_doit(struct sk_buff *skb,
    627				 struct genl_info *info)
    628{
    629	struct param p = { .attrs = info->attrs };
    630	struct sk_buff *msg;
    631	void *hdr;
    632	int cmd = info->genlhdr->cmd;
    633	int ret = -EMSGSIZE;
    634
    635	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
    636	if (!msg)
    637		return -ENOMEM;
    638	p.msg = msg;
    639
    640	hdr = genlmsg_put_reply(msg, info, &thermal_gnl_family, 0, cmd);
    641	if (!hdr)
    642		goto out_free_msg;
    643
    644	ret = cmd_cb[cmd](&p);
    645	if (ret)
    646		goto out_cancel_msg;
    647
    648	genlmsg_end(msg, hdr);
    649
    650	return genlmsg_reply(msg, info);
    651
    652out_cancel_msg:
    653	genlmsg_cancel(msg, hdr);
    654out_free_msg:
    655	nlmsg_free(msg);
    656
    657	return ret;
    658}
    659
    660static const struct genl_small_ops thermal_genl_ops[] = {
    661	{
    662		.cmd = THERMAL_GENL_CMD_TZ_GET_ID,
    663		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
    664		.dumpit = thermal_genl_cmd_dumpit,
    665	},
    666	{
    667		.cmd = THERMAL_GENL_CMD_TZ_GET_TRIP,
    668		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
    669		.doit = thermal_genl_cmd_doit,
    670	},
    671	{
    672		.cmd = THERMAL_GENL_CMD_TZ_GET_TEMP,
    673		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
    674		.doit = thermal_genl_cmd_doit,
    675	},
    676	{
    677		.cmd = THERMAL_GENL_CMD_TZ_GET_GOV,
    678		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
    679		.doit = thermal_genl_cmd_doit,
    680	},
    681	{
    682		.cmd = THERMAL_GENL_CMD_CDEV_GET,
    683		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
    684		.dumpit = thermal_genl_cmd_dumpit,
    685	},
    686};
    687
    688static struct genl_family thermal_gnl_family __ro_after_init = {
    689	.hdrsize	= 0,
    690	.name		= THERMAL_GENL_FAMILY_NAME,
    691	.version	= THERMAL_GENL_VERSION,
    692	.maxattr	= THERMAL_GENL_ATTR_MAX,
    693	.policy		= thermal_genl_policy,
    694	.small_ops	= thermal_genl_ops,
    695	.n_small_ops	= ARRAY_SIZE(thermal_genl_ops),
    696	.mcgrps		= thermal_genl_mcgrps,
    697	.n_mcgrps	= ARRAY_SIZE(thermal_genl_mcgrps),
    698};
    699
    700int __init thermal_netlink_init(void)
    701{
    702	return genl_register_family(&thermal_gnl_family);
    703}