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 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 481 out_cancel_nest: 482 mutex_unlock(&tz->lock); 483 484 return -EMSGSIZE; 485 } 486 487 static 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 512 static 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 538 static 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 552 static 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; 569 out_cancel_nest: 570 nla_nest_cancel(msg, start_cdev); 571 572 return ret; 573 } 574 575 static 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 583 static 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 604 out_cancel_msg: 605 genlmsg_cancel(skb, hdr); 606 607 return ret; 608 } 609 610 static 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 636 out_cancel_msg: 637 genlmsg_cancel(msg, hdr); 638 out_free_msg: 639 nlmsg_free(msg); 640 641 return ret; 642 } 643 644 static 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 655 static 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 665 static 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 693 static 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 708 int thermal_genl_register_notifier(struct notifier_block *nb) 709 { 710 return blocking_notifier_chain_register(&thermal_genl_chain, nb); 711 } 712 713 int thermal_genl_unregister_notifier(struct notifier_block *nb) 714 { 715 return blocking_notifier_chain_unregister(&thermal_genl_chain, nb); 716 } 717 718 int __init thermal_netlink_init(void) 719 { 720 return genl_register_family(&thermal_genl_family); 721 } 722 723 void __init thermal_netlink_exit(void) 724 { 725 genl_unregister_family(&thermal_genl_family); 726 } 727