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