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