brintos

brintos / linux-shallow public Read only

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