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/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 17 static 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 22 static 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 54 struct 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 71 typedef int (*cb_t)(struct param *); 72 73 static struct genl_family thermal_genl_family; 74 static BLOCKING_NOTIFIER_HEAD(thermal_genl_chain); 75 76 static 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 83 int 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; 111 out_cancel: 112 genlmsg_cancel(skb, hdr); 113 out_free: 114 nlmsg_free(skb); 115 116 return -EMSGSIZE; 117 } 118 119 /**************************** Event encoding *********************************/ 120 121 static 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 130 static 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 138 static 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 148 static 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 160 static 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 173 static 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 181 static 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 192 static 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 201 static 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; 231 out_cancel_nest: 232 nla_nest_cancel(msg, start_cap); 233 234 return -EMSGSIZE; 235 } 236 237 int thermal_genl_event_tz_delete(struct param *p) 238 __attribute__((alias("thermal_genl_event_tz"))); 239 240 int thermal_genl_event_tz_enable(struct param *p) 241 __attribute__((alias("thermal_genl_event_tz"))); 242 243 int thermal_genl_event_tz_disable(struct param *p) 244 __attribute__((alias("thermal_genl_event_tz"))); 245 246 int thermal_genl_event_tz_trip_down(struct param *p) 247 __attribute__((alias("thermal_genl_event_tz_trip_up"))); 248 249 static 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 encoding 266 */ 267 static 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 296 out_cancel_msg: 297 genlmsg_cancel(msg, hdr); 298 out_free_msg: 299 nlmsg_free(msg); 300 301 return ret; 302 } 303 304 int 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 311 int 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 318 int 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 325 int 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 332 int 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 342 int 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 352 int 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 364 int 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 372 int 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 380 int 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 387 int 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 395 int 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 } 402 EXPORT_SYMBOL_GPL(thermal_genl_cpu_capability_event); 403 404 /*************************** Command encoding ********************************/ 405 406 static 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 418 static 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 436 out_cancel_nest: 437 nla_nest_cancel(msg, start_tz); 438 439 return ret; 440 } 441 442 static 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 thermal_zone_device *tz; 447 struct nlattr *start_trip; 448 int id; 449 450 if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID]) 451 return -EINVAL; 452 453 id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]); 454 455 tz = thermal_zone_get_by_id(id); 456 if (!tz) 457 return -EINVAL; 458 459 start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ_TRIP); 460 if (!start_trip) 461 return -EMSGSIZE; 462 463 mutex_lock(&tz->lock); 464 465 for_each_trip_desc(tz, td) { 466 const struct thermal_trip *trip = &td->trip; 467 468 if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, 469 thermal_zone_trip_id(tz, trip)) || 470 nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, trip->type) || 471 nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, trip->temperature) || 472 nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, trip->hysteresis)) 473 goto out_cancel_nest; 474 } 475 476 mutex_unlock(&tz->lock); 477 478 nla_nest_end(msg, start_trip); 479 480 return 0; 481 482 out_cancel_nest: 483 mutex_unlock(&tz->lock); 484 485 return -EMSGSIZE; 486 } 487 488 static int thermal_genl_cmd_tz_get_temp(struct param *p) 489 { 490 struct sk_buff *msg = p->msg; 491 struct thermal_zone_device *tz; 492 int temp, ret, id; 493 494 if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID]) 495 return -EINVAL; 496 497 id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]); 498 499 tz = thermal_zone_get_by_id(id); 500 if (!tz) 501 return -EINVAL; 502 503 ret = thermal_zone_get_temp(tz, &temp); 504 if (ret) 505 return ret; 506 507 if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) || 508 nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TEMP, temp)) 509 return -EMSGSIZE; 510 511 return 0; 512 } 513 514 static int thermal_genl_cmd_tz_get_gov(struct param *p) 515 { 516 struct sk_buff *msg = p->msg; 517 struct thermal_zone_device *tz; 518 int id, ret = 0; 519 520 if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID]) 521 return -EINVAL; 522 523 id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]); 524 525 tz = thermal_zone_get_by_id(id); 526 if (!tz) 527 return -EINVAL; 528 529 mutex_lock(&tz->lock); 530 531 if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) || 532 nla_put_string(msg, THERMAL_GENL_ATTR_TZ_GOV_NAME, 533 tz->governor->name)) 534 ret = -EMSGSIZE; 535 536 mutex_unlock(&tz->lock); 537 538 return ret; 539 } 540 541 static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device *cdev, 542 void *data) 543 { 544 struct sk_buff *msg = data; 545 546 if (nla_put_u32(msg, THERMAL_GENL_ATTR_CDEV_ID, cdev->id)) 547 return -EMSGSIZE; 548 549 if (nla_put_string(msg, THERMAL_GENL_ATTR_CDEV_NAME, cdev->type)) 550 return -EMSGSIZE; 551 552 return 0; 553 } 554 555 static int thermal_genl_cmd_cdev_get(struct param *p) 556 { 557 struct sk_buff *msg = p->msg; 558 struct nlattr *start_cdev; 559 int ret; 560 561 start_cdev = nla_nest_start(msg, THERMAL_GENL_ATTR_CDEV); 562 if (!start_cdev) 563 return -EMSGSIZE; 564 565 ret = for_each_thermal_cooling_device(__thermal_genl_cmd_cdev_get, msg); 566 if (ret) 567 goto out_cancel_nest; 568 569 nla_nest_end(msg, start_cdev); 570 571 return 0; 572 out_cancel_nest: 573 nla_nest_cancel(msg, start_cdev); 574 575 return ret; 576 } 577 578 static cb_t cmd_cb[] = { 579 [THERMAL_GENL_CMD_TZ_GET_ID] = thermal_genl_cmd_tz_get_id, 580 [THERMAL_GENL_CMD_TZ_GET_TRIP] = thermal_genl_cmd_tz_get_trip, 581 [THERMAL_GENL_CMD_TZ_GET_TEMP] = thermal_genl_cmd_tz_get_temp, 582 [THERMAL_GENL_CMD_TZ_GET_GOV] = thermal_genl_cmd_tz_get_gov, 583 [THERMAL_GENL_CMD_CDEV_GET] = thermal_genl_cmd_cdev_get, 584 }; 585 586 static int thermal_genl_cmd_dumpit(struct sk_buff *skb, 587 struct netlink_callback *cb) 588 { 589 struct param p = { .msg = skb }; 590 const struct genl_dumpit_info *info = genl_dumpit_info(cb); 591 int cmd = info->op.cmd; 592 int ret; 593 void *hdr; 594 595 hdr = genlmsg_put(skb, 0, 0, &thermal_genl_family, 0, cmd); 596 if (!hdr) 597 return -EMSGSIZE; 598 599 ret = cmd_cb[cmd](&p); 600 if (ret) 601 goto out_cancel_msg; 602 603 genlmsg_end(skb, hdr); 604 605 return 0; 606 607 out_cancel_msg: 608 genlmsg_cancel(skb, hdr); 609 610 return ret; 611 } 612 613 static int thermal_genl_cmd_doit(struct sk_buff *skb, 614 struct genl_info *info) 615 { 616 struct param p = { .attrs = info->attrs }; 617 struct sk_buff *msg; 618 void *hdr; 619 int cmd = info->genlhdr->cmd; 620 int ret = -EMSGSIZE; 621 622 msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 623 if (!msg) 624 return -ENOMEM; 625 p.msg = msg; 626 627 hdr = genlmsg_put_reply(msg, info, &thermal_genl_family, 0, cmd); 628 if (!hdr) 629 goto out_free_msg; 630 631 ret = cmd_cb[cmd](&p); 632 if (ret) 633 goto out_cancel_msg; 634 635 genlmsg_end(msg, hdr); 636 637 return genlmsg_reply(msg, info); 638 639 out_cancel_msg: 640 genlmsg_cancel(msg, hdr); 641 out_free_msg: 642 nlmsg_free(msg); 643 644 return ret; 645 } 646 647 static int thermal_genl_bind(int mcgrp) 648 { 649 struct thermal_genl_notify n = { .mcgrp = mcgrp }; 650 651 if (WARN_ON_ONCE(mcgrp > THERMAL_GENL_MAX_GROUP)) 652 return -EINVAL; 653 654 blocking_notifier_call_chain(&thermal_genl_chain, THERMAL_NOTIFY_BIND, &n); 655 return 0; 656 } 657 658 static void thermal_genl_unbind(int mcgrp) 659 { 660 struct thermal_genl_notify n = { .mcgrp = mcgrp }; 661 662 if (WARN_ON_ONCE(mcgrp > THERMAL_GENL_MAX_GROUP)) 663 return; 664 665 blocking_notifier_call_chain(&thermal_genl_chain, THERMAL_NOTIFY_UNBIND, &n); 666 } 667 668 static const struct genl_small_ops thermal_genl_ops[] = { 669 { 670 .cmd = THERMAL_GENL_CMD_TZ_GET_ID, 671 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 672 .dumpit = thermal_genl_cmd_dumpit, 673 }, 674 { 675 .cmd = THERMAL_GENL_CMD_TZ_GET_TRIP, 676 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 677 .doit = thermal_genl_cmd_doit, 678 }, 679 { 680 .cmd = THERMAL_GENL_CMD_TZ_GET_TEMP, 681 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 682 .doit = thermal_genl_cmd_doit, 683 }, 684 { 685 .cmd = THERMAL_GENL_CMD_TZ_GET_GOV, 686 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 687 .doit = thermal_genl_cmd_doit, 688 }, 689 { 690 .cmd = THERMAL_GENL_CMD_CDEV_GET, 691 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 692 .dumpit = thermal_genl_cmd_dumpit, 693 }, 694 }; 695 696 static struct genl_family thermal_genl_family __ro_after_init = { 697 .hdrsize = 0, 698 .name = THERMAL_GENL_FAMILY_NAME, 699 .version = THERMAL_GENL_VERSION, 700 .maxattr = THERMAL_GENL_ATTR_MAX, 701 .policy = thermal_genl_policy, 702 .bind = thermal_genl_bind, 703 .unbind = thermal_genl_unbind, 704 .small_ops = thermal_genl_ops, 705 .n_small_ops = ARRAY_SIZE(thermal_genl_ops), 706 .resv_start_op = THERMAL_GENL_CMD_CDEV_GET + 1, 707 .mcgrps = thermal_genl_mcgrps, 708 .n_mcgrps = ARRAY_SIZE(thermal_genl_mcgrps), 709 }; 710 711 int thermal_genl_register_notifier(struct notifier_block *nb) 712 { 713 return blocking_notifier_chain_register(&thermal_genl_chain, nb); 714 } 715 716 int thermal_genl_unregister_notifier(struct notifier_block *nb) 717 { 718 return blocking_notifier_chain_unregister(&thermal_genl_chain, nb); 719 } 720 721 int __init thermal_netlink_init(void) 722 { 723 return genl_register_family(&thermal_genl_family); 724 } 725 726 void __init thermal_netlink_exit(void) 727 { 728 genl_unregister_family(&thermal_genl_family); 729 } 730