Pull thermal control updates from Rafael Wysocki: "These are thermal core changes, including the addition of support for temperature thresholds that can be set from user space, fixes related to thermal zone initialization, suspend/resume and exit, locking rework and rearrangement of the code handling thermal zone temperature updates. Specifics: - Add support for thermal thresholds that can be added and removed from user space via netlink along with a related library update (Daniel Lezcano) - Fix thermal zone initialization, suspend/resume and exit synchronization issues (Rafael Wysocki) - Rearrange locking in the thermal core to use guards (Rafael Wysocki) - Make the code handling thermal zone temperature updates use sorted lists of trip points to reduce the number of trip points table walks in the thermal core (Rafael Wysocki) - Fix and clean up the thermal testing facility code (Rafael Wysocki) - Fix a Power Allocator thermal governor issue (ZhengShaobo)" * tag 'thermal-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (45 commits) thermal: testing: Initialize some variables annoteded with _free() thermal: testing: Use DEFINE_FREE() and __free() to simplify code thermal: testing: Simplify tt_get_tt_zone() thermal: gov_power_allocator: Granted power set to max when nobody request power thermal: core: Relocate thermal zone initialization routine thermal: core: Use trip lists for trip crossing detection thermal: core: Eliminate thermal_zone_trip_down() thermal: core: Relocate functions that update trip points thermal: core: Move some trip processing to thermal_trip_crossed() thermal: core: Pass trip descriptor to thermal_trip_crossed() thermal: core: Rearrange __thermal_zone_device_update() thermal: core: Prepare for moving trips between sorted lists thermal: core: Rename trip list node in struct thermal_trip_desc thermal: core: Build sorted lists instead of sorting them later thermal/lib: Fix memory leak on error in thermal_genl_auto() thermal: thresholds: Fix thermal lock annotation issue tools/thermal/thermal-engine: Take into account the thresholds API tools/lib/thermal: Add the threshold netlink ABI tools/lib/thermal: Make more generic the command encoding function thermal: netlink: Add the commands and the events for the thresholds ...
Unified · Split
| @@ -6,6 +6,7 @@ CFLAGS_thermal_core.o := -I$(src) · 5 unmodified lines | ||
| 6 | 6 | obj-$(CONFIG_THERMAL) += thermal_sys.o |
| 7 | 7 | thermal_sys-y += thermal_core.o thermal_sysfs.o |
| 8 | 8 | thermal_sys-y += thermal_trip.o thermal_helpers.o |
| 9 | +thermal_sys-y += thermal_thresholds.o | |
| 9 | 10 | |
| 10 | 11 | # netlink interface to manage the thermal framework |
| 11 | 12 | thermal_sys-$(CONFIG_THERMAL_NETLINK) += thermal_netlink.o |
| @@ -30,9 +30,7 @@ static void bang_bang_set_instance_target(struct thermal_instance *instance, · 29 unmodified lines | ||
| 30 | 30 | |
| 31 | 31 | dev_dbg(&instance->cdev->device, "target=%ld\n", instance->target); |
| 32 | 32 | |
| 33 | − mutex_lock(&instance->cdev->lock); | |
| 34 | − __thermal_cdev_update(instance->cdev); | |
| 35 | − mutex_unlock(&instance->cdev->lock); | |
| 33 | + thermal_cdev_update_nocheck(instance->cdev); | |
| 36 | 34 | } |
| 37 | 35 | |
| 38 | 36 | /** |
| @@ -67,6 +65,7 @@ static void bang_bang_control(struct thermal_zone_device *tz, · 28 unmodified lines | ||
| 67 | 65 | const struct thermal_trip *trip, |
| 68 | 66 | bool crossed_up) |
| 69 | 67 | { |
| 68 | + const struct thermal_trip_desc *td = trip_to_trip_desc(trip); | |
| 70 | 69 | struct thermal_instance *instance; |
| 71 | 70 | |
| 72 | 71 | lockdep_assert_held(&tz->lock); |
| @@ -75,10 +74,8 @@ static void bang_bang_control(struct thermal_zone_device *tz, · 2 unmodified lines | ||
| 75 | 74 | thermal_zone_trip_id(tz, trip), trip->temperature, |
| 76 | 75 | tz->temperature, trip->hysteresis); |
| 77 | 76 | |
| 78 | − list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | |
| 79 | − if (instance->trip == trip) | |
| 80 | − bang_bang_set_instance_target(instance, crossed_up); | |
| 81 | − } | |
| 77 | + list_for_each_entry(instance, &td->thermal_instances, trip_node) | |
| 78 | + bang_bang_set_instance_target(instance, crossed_up); | |
| 82 | 79 | } |
| 83 | 80 | |
| 84 | 81 | static void bang_bang_manage(struct thermal_zone_device *tz) |
| @@ -104,8 +101,8 @@ static void bang_bang_manage(struct thermal_zone_device *tz) · 19 unmodified lines | ||
| 104 | 101 | * to the thermal zone temperature and the trip point threshold. |
| 105 | 102 | */ |
| 106 | 103 | turn_on = tz->temperature >= td->threshold; |
| 107 | − list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | |
| 108 | − if (!instance->initialized && instance->trip == trip) | |
| 104 | + list_for_each_entry(instance, &td->thermal_instances, trip_node) { | |
| 105 | + if (!instance->initialized) | |
| 109 | 106 | bang_bang_set_instance_target(instance, turn_on); |
| 110 | 107 | } |
| 111 | 108 | } |
| @@ -44,7 +44,7 @@ static int get_trip_level(struct thermal_zone_device *tz) · 43 unmodified lines | ||
| 44 | 44 | /** |
| 45 | 45 | * fair_share_throttle - throttles devices associated with the given zone |
| 46 | 46 | * @tz: thermal_zone_device |
| 47 | − * @trip: trip point | |
| 47 | + * @td: trip point descriptor | |
| 48 | 48 | * @trip_level: number of trips crossed by the zone temperature |
| 49 | 49 | * |
| 50 | 50 | * Throttling Logic: This uses three parameters to calculate the new |
| @@ -61,29 +61,23 @@ static int get_trip_level(struct thermal_zone_device *tz) · 10 unmodified lines | ||
| 61 | 61 | * new_state of cooling device = P3 * P2 * P1 |
| 62 | 62 | */ |
| 63 | 63 | static void fair_share_throttle(struct thermal_zone_device *tz, |
| 64 | − const struct thermal_trip *trip, | |
| 64 | + const struct thermal_trip_desc *td, | |
| 65 | 65 | int trip_level) |
| 66 | 66 | { |
| 67 | 67 | struct thermal_instance *instance; |
| 68 | 68 | int total_weight = 0; |
| 69 | 69 | int nr_instances = 0; |
| 70 | 70 | |
| 71 | − list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | |
| 72 | − if (instance->trip != trip) | |
| 73 | − continue; | |
| 74 | − | |
| 71 | + list_for_each_entry(instance, &td->thermal_instances, trip_node) { | |
| 75 | 72 | total_weight += instance->weight; |
| 76 | 73 | nr_instances++; |
| 77 | 74 | } |
| 78 | 75 | |
| 79 | − list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | |
| 76 | + list_for_each_entry(instance, &td->thermal_instances, trip_node) { | |
| 80 | 77 | struct thermal_cooling_device *cdev = instance->cdev; |
| 81 | 78 | u64 dividend; |
| 82 | 79 | u32 divisor; |
| 83 | 80 | |
| 84 | − if (instance->trip != trip) | |
| 85 | − continue; | |
| 86 | − | |
| 87 | 81 | dividend = trip_level; |
| 88 | 82 | dividend *= cdev->max_state; |
| 89 | 83 | divisor = tz->num_trips; |
| @@ -95,9 +89,7 @@ static void fair_share_throttle(struct thermal_zone_device *tz, · 5 unmodified lines | ||
| 95 | 89 | } |
| 96 | 90 | instance->target = div_u64(dividend, divisor); |
| 97 | 91 | |
| 98 | − mutex_lock(&cdev->lock); | |
| 99 | − __thermal_cdev_update(cdev); | |
| 100 | − mutex_unlock(&cdev->lock); | |
| 92 | + thermal_cdev_update_nocheck(cdev); | |
| 101 | 93 | } |
| 102 | 94 | } |
| 103 | 95 | |
| @@ -116,7 +108,7 @@ static void fair_share_manage(struct thermal_zone_device *tz) · 12 unmodified lines | ||
| 116 | 108 | trip->type == THERMAL_TRIP_HOT) |
| 117 | 109 | continue; |
| 118 | 110 | |
| 119 | − fair_share_throttle(tz, trip, trip_level); | |
| 111 | + fair_share_throttle(tz, td, trip_level); | |
| 120 | 112 | } |
| 121 | 113 | } |
| 122 | 114 | |
| @@ -97,11 +97,9 @@ struct power_allocator_params { · 96 unmodified lines | ||
| 97 | 97 | struct power_actor *power; |
| 98 | 98 | }; |
| 99 | 99 | |
| 100 | −static bool power_actor_is_valid(struct power_allocator_params *params, | |
| 101 | − struct thermal_instance *instance) | |
| 100 | +static bool power_actor_is_valid(struct thermal_instance *instance) | |
| 102 | 101 | { |
| 103 | − return (instance->trip == params->trip_max && | |
| 104 | − cdev_is_power_actor(instance->cdev)); | |
| 102 | + return cdev_is_power_actor(instance->cdev); | |
| 105 | 103 | } |
| 106 | 104 | |
| 107 | 105 | /** |
| @@ -118,13 +116,14 @@ static bool power_actor_is_valid(struct power_allocator_params *params, · 10 unmodified lines | ||
| 118 | 116 | static u32 estimate_sustainable_power(struct thermal_zone_device *tz) |
| 119 | 117 | { |
| 120 | 118 | struct power_allocator_params *params = tz->governor_data; |
| 119 | + const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max); | |
| 121 | 120 | struct thermal_cooling_device *cdev; |
| 122 | 121 | struct thermal_instance *instance; |
| 123 | 122 | u32 sustainable_power = 0; |
| 124 | 123 | u32 min_power; |
| 125 | 124 | |
| 126 | − list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | |
| 127 | − if (!power_actor_is_valid(params, instance)) | |
| 125 | + list_for_each_entry(instance, &td->thermal_instances, trip_node) { | |
| 126 | + if (!power_actor_is_valid(instance)) | |
| 128 | 127 | continue; |
| 129 | 128 | |
| 130 | 129 | cdev = instance->cdev; |
| @@ -323,9 +322,8 @@ power_actor_set_power(struct thermal_cooling_device *cdev, · 192 unmodified lines | ||
| 323 | 322 | return ret; |
| 324 | 323 | |
| 325 | 324 | instance->target = clamp_val(state, instance->lower, instance->upper); |
| 326 | − mutex_lock(&cdev->lock); | |
| 327 | − __thermal_cdev_update(cdev); | |
| 328 | − mutex_unlock(&cdev->lock); | |
| 325 | + | |
| 326 | + thermal_cdev_update_nocheck(cdev); | |
| 329 | 327 | |
| 330 | 328 | return 0; |
| 331 | 329 | } |
| @@ -356,11 +354,19 @@ static void divvy_up_power(struct power_actor *power, int num_actors, · 24 unmodified lines | ||
| 356 | 354 | u32 extra_power = 0; |
| 357 | 355 | int i; |
| 358 | 356 | |
| 359 | − /* | |
| 360 | − * Prevent division by 0 if none of the actors request power. | |
| 361 | − */ | |
| 362 | − if (!total_req_power) | |
| 363 | − total_req_power = 1; | |
| 357 | + if (!total_req_power) { | |
| 358 | + /* | |
| 359 | + * Nobody requested anything, just give everybody | |
| 360 | + * the maximum power | |
| 361 | + */ | |
| 362 | + for (i = 0; i < num_actors; i++) { | |
| 363 | + struct power_actor *pa = &power[i]; | |
| 364 | + | |
| 365 | + pa->granted_power = pa->max_power; | |
| 366 | + } | |
| 367 | + | |
| 368 | + return; | |
| 369 | + } | |
| 364 | 370 | |
| 365 | 371 | for (i = 0; i < num_actors; i++) { |
| 366 | 372 | struct power_actor *pa = &power[i]; |
| @@ -400,6 +406,7 @@ static void divvy_up_power(struct power_actor *power, int num_actors, · 33 unmodified lines | ||
| 400 | 406 | static void allocate_power(struct thermal_zone_device *tz, int control_temp) |
| 401 | 407 | { |
| 402 | 408 | struct power_allocator_params *params = tz->governor_data; |
| 409 | + const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max); | |
| 403 | 410 | unsigned int num_actors = params->num_actors; |
| 404 | 411 | struct power_actor *power = params->power; |
| 405 | 412 | struct thermal_cooling_device *cdev; |
| @@ -417,10 +424,10 @@ static void allocate_power(struct thermal_zone_device *tz, int control_temp) · 11 unmodified lines | ||
| 417 | 424 | /* Clean all buffers for new power estimations */ |
| 418 | 425 | memset(power, 0, params->buffer_size); |
| 419 | 426 | |
| 420 | − list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | |
| 427 | + list_for_each_entry(instance, &td->thermal_instances, trip_node) { | |
| 421 | 428 | struct power_actor *pa = &power[i]; |
| 422 | 429 | |
| 423 | − if (!power_actor_is_valid(params, instance)) | |
| 430 | + if (!power_actor_is_valid(instance)) | |
| 424 | 431 | continue; |
| 425 | 432 | |
| 426 | 433 | cdev = instance->cdev; |
| @@ -454,10 +461,10 @@ static void allocate_power(struct thermal_zone_device *tz, int control_temp) · 27 unmodified lines | ||
| 454 | 461 | power_range); |
| 455 | 462 | |
| 456 | 463 | i = 0; |
| 457 | − list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | |
| 464 | + list_for_each_entry(instance, &td->thermal_instances, trip_node) { | |
| 458 | 465 | struct power_actor *pa = &power[i]; |
| 459 | 466 | |
| 460 | − if (!power_actor_is_valid(params, instance)) | |
| 467 | + if (!power_actor_is_valid(instance)) | |
| 461 | 468 | continue; |
| 462 | 469 | |
| 463 | 470 | power_actor_set_power(instance->cdev, instance, |
| @@ -538,29 +545,29 @@ static void reset_pid_controller(struct power_allocator_params *params) · 74 unmodified lines | ||
| 538 | 545 | static void allow_maximum_power(struct thermal_zone_device *tz) |
| 539 | 546 | { |
| 540 | 547 | struct power_allocator_params *params = tz->governor_data; |
| 548 | + const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max); | |
| 541 | 549 | struct thermal_cooling_device *cdev; |
| 542 | 550 | struct thermal_instance *instance; |
| 543 | 551 | u32 req_power; |
| 544 | 552 | |
| 545 | − list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | |
| 546 | − if (!power_actor_is_valid(params, instance)) | |
| 553 | + list_for_each_entry(instance, &td->thermal_instances, trip_node) { | |
| 554 | + if (!power_actor_is_valid(instance)) | |
| 547 | 555 | continue; |
| 548 | 556 | |
| 549 | 557 | cdev = instance->cdev; |
| 550 | 558 | |
| 551 | 559 | instance->target = 0; |
| 552 | − mutex_lock(&cdev->lock); | |
| 553 | − /* | |
| 554 | − * Call for updating the cooling devices local stats and avoid | |
| 555 | − * periods of dozen of seconds when those have not been | |
| 556 | − * maintained. | |
| 557 | − */ | |
| 558 | − cdev->ops->get_requested_power(cdev, &req_power); | |
| 559 | − | |
| 560 | − if (params->update_cdevs) | |
| 561 | − __thermal_cdev_update(cdev); | |
| 562 | − | |
| 563 | − mutex_unlock(&cdev->lock); | |
| 560 | + scoped_guard(cooling_dev, cdev) { | |
| 561 | + /* | |
| 562 | + * Call for updating the cooling devices local stats and | |
| 563 | + * avoid periods of dozen of seconds when those have not | |
| 564 | + * been maintained. | |
| 565 | + */ | |
| 566 | + cdev->ops->get_requested_power(cdev, &req_power); | |
| 567 | + | |
| 568 | + if (params->update_cdevs) | |
| 569 | + __thermal_cdev_update(cdev); | |
| 570 | + } | |
| 564 | 571 | } |
| 565 | 572 | } |
| 566 | 573 | |
| @@ -581,13 +588,11 @@ static void allow_maximum_power(struct thermal_zone_device *tz) · 14 unmodified lines | ||
| 581 | 588 | static int check_power_actors(struct thermal_zone_device *tz, |
| 582 | 589 | struct power_allocator_params *params) |
| 583 | 590 | { |
| 591 | + const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max); | |
| 584 | 592 | struct thermal_instance *instance; |
| 585 | 593 | int ret = 0; |
| 586 | 594 | |
| 587 | − list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | |
| 588 | − if (instance->trip != params->trip_max) | |
| 589 | − continue; | |
| 590 | − | |
| 595 | + list_for_each_entry(instance, &td->thermal_instances, trip_node) { | |
| 591 | 596 | if (!cdev_is_power_actor(instance->cdev)) { |
| 592 | 597 | dev_warn(&tz->device, "power_allocator: %s is not a power actor\n", |
| 593 | 598 | instance->cdev->type); |
| @@ -635,14 +640,15 @@ static void power_allocator_update_tz(struct thermal_zone_device *tz, · 41 unmodified lines | ||
| 635 | 640 | enum thermal_notify_event reason) |
| 636 | 641 | { |
| 637 | 642 | struct power_allocator_params *params = tz->governor_data; |
| 643 | + const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max); | |
| 638 | 644 | struct thermal_instance *instance; |
| 639 | 645 | int num_actors = 0; |
| 640 | 646 | |
| 641 | 647 | switch (reason) { |
| 642 | 648 | case THERMAL_TZ_BIND_CDEV: |
| 643 | 649 | case THERMAL_TZ_UNBIND_CDEV: |
| 644 | − list_for_each_entry(instance, &tz->thermal_instances, tz_node) | |
| 645 | − if (power_actor_is_valid(params, instance)) | |
| 650 | + list_for_each_entry(instance, &td->thermal_instances, trip_node) | |
| 651 | + if (power_actor_is_valid(instance)) | |
| 646 | 652 | num_actors++; |
| 647 | 653 | |
| 648 | 654 | if (num_actors == params->num_actors) |
| @@ -652,8 +658,8 @@ static void power_allocator_update_tz(struct thermal_zone_device *tz, · 3 unmodified lines | ||
| 652 | 658 | break; |
| 653 | 659 | case THERMAL_INSTANCE_WEIGHT_CHANGED: |
| 654 | 660 | params->total_weight = 0; |
| 655 | − list_for_each_entry(instance, &tz->thermal_instances, tz_node) | |
| 656 | − if (power_actor_is_valid(params, instance)) | |
| 661 | + list_for_each_entry(instance, &td->thermal_instances, trip_node) | |
| 662 | + if (power_actor_is_valid(instance)) | |
| 657 | 663 | params->total_weight += instance->weight; |
| 658 | 664 | break; |
| 659 | 665 | default: |
| @@ -66,9 +66,10 @@ static unsigned long get_target_state(struct thermal_instance *instance, · 65 unmodified lines | ||
| 66 | 66 | } |
| 67 | 67 | |
| 68 | 68 | static void thermal_zone_trip_update(struct thermal_zone_device *tz, |
| 69 | − const struct thermal_trip *trip, | |
| 69 | + const struct thermal_trip_desc *td, | |
| 70 | 70 | int trip_threshold) |
| 71 | 71 | { |
| 72 | + const struct thermal_trip *trip = &td->trip; | |
| 72 | 73 | enum thermal_trend trend = get_tz_trend(tz, trip); |
| 73 | 74 | int trip_id = thermal_zone_trip_id(tz, trip); |
| 74 | 75 | struct thermal_instance *instance; |
| @@ -82,12 +83,9 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, · 7 unmodified lines | ||
| 82 | 83 | dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n", |
| 83 | 84 | trip_id, trip->type, trip_threshold, trend, throttle); |
| 84 | 85 | |
| 85 | − list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | |
| 86 | + list_for_each_entry(instance, &td->thermal_instances, trip_node) { | |
| 86 | 87 | int old_target; |
| 87 | 88 | |
| 88 | − if (instance->trip != trip) | |
| 89 | − continue; | |
| 90 | − | |
| 91 | 89 | old_target = instance->target; |
| 92 | 90 | instance->target = get_target_state(instance, trend, throttle); |
| 93 | 91 | |
| @@ -99,9 +97,9 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, · 5 unmodified lines | ||
| 99 | 97 | |
| 100 | 98 | instance->initialized = true; |
| 101 | 99 | |
| 102 | − mutex_lock(&instance->cdev->lock); | |
| 103 | − instance->cdev->updated = false; /* cdev needs update */ | |
| 104 | − mutex_unlock(&instance->cdev->lock); | |
| 100 | + scoped_guard(cooling_dev, instance->cdev) { | |
| 101 | + instance->cdev->updated = false; /* cdev needs update */ | |
| 102 | + } | |
| 105 | 103 | } |
| 106 | 104 | } |
| 107 | 105 | |
| @@ -127,11 +125,13 @@ static void step_wise_manage(struct thermal_zone_device *tz) · 19 unmodified lines | ||
| 127 | 125 | trip->type == THERMAL_TRIP_HOT) |
| 128 | 126 | continue; |
| 129 | 127 | |
| 130 | − thermal_zone_trip_update(tz, trip, td->threshold); | |
| 128 | + thermal_zone_trip_update(tz, td, td->threshold); | |
| 131 | 129 | } |
| 132 | 130 | |
| 133 | − list_for_each_entry(instance, &tz->thermal_instances, tz_node) | |
| 134 | − thermal_cdev_update(instance->cdev); | |
| 131 | + for_each_trip_desc(tz, td) { | |
| 132 | + list_for_each_entry(instance, &td->thermal_instances, trip_node) | |
| 133 | + thermal_cdev_update(instance->cdev); | |
| 134 | + } | |
| 135 | 135 | } |
| 136 | 136 | |
| 137 | 137 | static struct thermal_governor thermal_gov_step_wise = { |
| @@ -185,7 +185,7 @@ static void tt_add_tz_work_fn(struct work_struct *work) · 184 unmodified lines | ||
| 185 | 185 | int tt_add_tz(void) |
| 186 | 186 | { |
| 187 | 187 | struct tt_thermal_zone *tt_zone __free(kfree); |
| 188 | − struct tt_work *tt_work __free(kfree); | |
| 188 | + struct tt_work *tt_work __free(kfree) = NULL; | |
| 189 | 189 | int ret; |
| 190 | 190 | |
| 191 | 191 | tt_zone = kzalloc(sizeof(*tt_zone), GFP_KERNEL); |
| @@ -237,7 +237,7 @@ static void tt_zone_unregister_tz(struct tt_thermal_zone *tt_zone) · 45 unmodified lines | ||
| 237 | 237 | |
| 238 | 238 | int tt_del_tz(const char *arg) |
| 239 | 239 | { |
| 240 | − struct tt_work *tt_work __free(kfree); | |
| 240 | + struct tt_work *tt_work __free(kfree) = NULL; | |
| 241 | 241 | struct tt_thermal_zone *tt_zone, *aux; |
| 242 | 242 | int ret; |
| 243 | 243 | int id; |
| @@ -288,19 +288,14 @@ static struct tt_thermal_zone *tt_get_tt_zone(const char *arg) · 44 unmodified lines | ||
| 288 | 288 | |
| 289 | 289 | guard(mutex)(&tt_thermal_zones_lock); |
| 290 | 290 | |
| 291 | − ret = -EINVAL; | |
| 292 | 291 | list_for_each_entry(tt_zone, &tt_thermal_zones, list_node) { |
| 293 | 292 | if (tt_zone->id == id) { |
| 294 | 293 | tt_zone->refcount++; |
| 295 | − ret = 0; | |
| 296 | − break; | |
| 294 | + return tt_zone; | |
| 297 | 295 | } |
| 298 | 296 | } |
| 299 | 297 | |
| 300 | − if (ret) | |
| 301 | − return ERR_PTR(ret); | |
| 302 | − | |
| 303 | − return tt_zone; | |
| 298 | + return ERR_PTR(-EINVAL); | |
| 304 | 299 | } |
| 305 | 300 | |
| 306 | 301 | static void tt_put_tt_zone(struct tt_thermal_zone *tt_zone) |
| @@ -310,6 +305,9 @@ static void tt_put_tt_zone(struct tt_thermal_zone *tt_zone) · 3 unmodified lines | ||
| 310 | 305 | tt_zone->refcount--; |
| 311 | 306 | } |
| 312 | 307 | |
| 308 | +DEFINE_FREE(put_tt_zone, struct tt_thermal_zone *, | |
| 309 | + if (!IS_ERR_OR_NULL(_T)) tt_put_tt_zone(_T)) | |
| 310 | + | |
| 313 | 311 | static void tt_zone_add_trip_work_fn(struct work_struct *work) |
| 314 | 312 | { |
| 315 | 313 | struct tt_work *tt_work = tt_work_of_work(work); |
| @@ -332,9 +330,9 @@ static void tt_zone_add_trip_work_fn(struct work_struct *work) · 16 unmodified lines | ||
| 332 | 330 | |
| 333 | 331 | int tt_zone_add_trip(const char *arg) |
| 334 | 332 | { |
| 333 | + struct tt_thermal_zone *tt_zone __free(put_tt_zone) = NULL; | |
| 334 | + struct tt_trip *tt_trip __free(kfree) = NULL; | |
| 335 | 335 | struct tt_work *tt_work __free(kfree); |
| 336 | − struct tt_trip *tt_trip __free(kfree); | |
| 337 | − struct tt_thermal_zone *tt_zone; | |
| 338 | 336 | int id; |
| 339 | 337 | |
| 340 | 338 | tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL); |
| @@ -350,10 +348,8 @@ int tt_zone_add_trip(const char *arg) · 9 unmodified lines | ||
| 350 | 348 | return PTR_ERR(tt_zone); |
| 351 | 349 | |
| 352 | 350 | id = ida_alloc(&tt_zone->ida, GFP_KERNEL); |
| 353 | − if (id < 0) { | |
| 354 | − tt_put_tt_zone(tt_zone); | |
| 351 | + if (id < 0) | |
| 355 | 352 | return id; |
| 356 | − } | |
| 357 | 353 | |
| 358 | 354 | tt_trip->trip.type = THERMAL_TRIP_ACTIVE; |
| 359 | 355 | tt_trip->trip.temperature = THERMAL_TEMP_INVALID; |
| @@ -366,7 +362,7 @@ int tt_zone_add_trip(const char *arg) · 6 unmodified lines | ||
| 366 | 362 | tt_zone->num_trips++; |
| 367 | 363 | |
| 368 | 364 | INIT_WORK(&tt_work->work, tt_zone_add_trip_work_fn); |
| 369 | − tt_work->tt_zone = tt_zone; | |
| 365 | + tt_work->tt_zone = no_free_ptr(tt_zone); | |
| 370 | 366 | tt_work->tt_trip = no_free_ptr(tt_trip); |
| 371 | 367 | schedule_work(&(no_free_ptr(tt_work)->work)); |
| 372 | 368 | |
| @@ -391,7 +387,7 @@ static struct thermal_zone_device_ops tt_zone_ops = { · 18 unmodified lines | ||
| 391 | 387 | |
| 392 | 388 | static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone) |
| 393 | 389 | { |
| 394 | − struct thermal_trip *trips __free(kfree); | |
| 390 | + struct thermal_trip *trips __free(kfree) = NULL; | |
| 395 | 391 | struct thermal_zone_device *tz; |
| 396 | 392 | struct tt_trip *tt_trip; |
| 397 | 393 | int i; |
| @@ -425,23 +421,18 @@ static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone) · 27 unmodified lines | ||
| 425 | 421 | |
| 426 | 422 | int tt_zone_reg(const char *arg) |
| 427 | 423 | { |
| 428 | − struct tt_thermal_zone *tt_zone; | |
| 429 | − int ret; | |
| 424 | + struct tt_thermal_zone *tt_zone __free(put_tt_zone); | |
| 430 | 425 | |
| 431 | 426 | tt_zone = tt_get_tt_zone(arg); |
| 432 | 427 | if (IS_ERR(tt_zone)) |
| 433 | 428 | return PTR_ERR(tt_zone); |
| 434 | 429 | |
| 435 | − ret = tt_zone_register_tz(tt_zone); | |
| 436 | − | |
| 437 | − tt_put_tt_zone(tt_zone); | |
| 438 | − | |
| 439 | − return ret; | |
| 430 | + return tt_zone_register_tz(tt_zone); | |
| 440 | 431 | } |
| 441 | 432 | |
| 442 | 433 | int tt_zone_unreg(const char *arg) |
| 443 | 434 | { |
| 444 | − struct tt_thermal_zone *tt_zone; | |
| 435 | + struct tt_thermal_zone *tt_zone __free(put_tt_zone); | |
| 445 | 436 | |
| 446 | 437 | tt_zone = tt_get_tt_zone(arg); |
| 447 | 438 | if (IS_ERR(tt_zone)) |
| @@ -449,8 +440,6 @@ int tt_zone_unreg(const char *arg) · 1 unmodified line | ||
| 449 | 440 | |
| 450 | 441 | tt_zone_unregister_tz(tt_zone); |
| 451 | 442 | |
| 452 | − tt_put_tt_zone(tt_zone); | |
| 453 | − | |
| 454 | 443 | return 0; |
| 455 | 444 | } |
| 456 | 445 | |
| @@ -15,7 +15,6 @@ · 14 unmodified lines | ||
| 15 | 15 | #include <linux/slab.h> |
| 16 | 16 | #include <linux/kdev_t.h> |
| 17 | 17 | #include <linux/idr.h> |
| 18 | −#include <linux/list_sort.h> | |
| 19 | 18 | #include <linux/thermal.h> |
| 20 | 19 | #include <linux/reboot.h> |
| 21 | 20 | #include <linux/string.h> |
| @@ -40,6 +39,8 @@ static DEFINE_MUTEX(thermal_governor_lock); · 18 unmodified lines | ||
| 40 | 39 | |
| 41 | 40 | static struct thermal_governor *def_governor; |
| 42 | 41 | |
| 42 | +static bool thermal_pm_suspended; | |
| 43 | + | |
| 43 | 44 | /* |
| 44 | 45 | * Governor section: set of functions to handle thermal governors |
| 45 | 46 | * |
| @@ -122,7 +123,7 @@ int thermal_register_governor(struct thermal_governor *governor) · 76 unmodified lines | ||
| 122 | 123 | if (!governor) |
| 123 | 124 | return -EINVAL; |
| 124 | 125 | |
| 125 | − mutex_lock(&thermal_governor_lock); | |
| 126 | + guard(mutex)(&thermal_governor_lock); | |
| 126 | 127 | |
| 127 | 128 | err = -EBUSY; |
| 128 | 129 | if (!__find_governor(governor->name)) { |
| @@ -138,7 +139,7 @@ int thermal_register_governor(struct thermal_governor *governor) · 9 unmodified lines | ||
| 138 | 139 | def_governor = governor; |
| 139 | 140 | } |
| 140 | 141 | |
| 141 | − mutex_lock(&thermal_list_lock); | |
| 142 | + guard(mutex)(&thermal_list_lock); | |
| 142 | 143 | |
| 143 | 144 | list_for_each_entry(pos, &thermal_tz_list, node) { |
| 144 | 145 | /* |
| @@ -161,9 +162,6 @@ int thermal_register_governor(struct thermal_governor *governor) · 16 unmodified lines | ||
| 161 | 162 | } |
| 162 | 163 | } |
| 163 | 164 | |
| 164 | − mutex_unlock(&thermal_list_lock); | |
| 165 | − mutex_unlock(&thermal_governor_lock); | |
| 166 | − | |
| 167 | 165 | return err; |
| 168 | 166 | } |
| 169 | 167 | |
| @@ -174,23 +172,20 @@ void thermal_unregister_governor(struct thermal_governor *governor) · 4 unmodified lines | ||
| 174 | 172 | if (!governor) |
| 175 | 173 | return; |
| 176 | 174 | |
| 177 | − mutex_lock(&thermal_governor_lock); | |
| 175 | + guard(mutex)(&thermal_governor_lock); | |
| 178 | 176 | |
| 179 | 177 | if (!__find_governor(governor->name)) |
| 180 | − goto exit; | |
| 178 | + return; | |
| 181 | 179 | |
| 182 | − mutex_lock(&thermal_list_lock); | |
| 180 | + list_del(&governor->governor_list); | |
| 181 | + | |
| 182 | + guard(mutex)(&thermal_list_lock); | |
| 183 | 183 | |
| 184 | 184 | list_for_each_entry(pos, &thermal_tz_list, node) { |
| 185 | 185 | if (!strncasecmp(pos->governor->name, governor->name, |
| 186 | 186 | THERMAL_NAME_LENGTH)) |
| 187 | 187 | thermal_set_governor(pos, NULL); |
| 188 | 188 | } |
| 189 | − | |
| 190 | − mutex_unlock(&thermal_list_lock); | |
| 191 | − list_del(&governor->governor_list); | |
| 192 | −exit: | |
| 193 | − mutex_unlock(&thermal_governor_lock); | |
| 194 | 189 | } |
| 195 | 190 | |
| 196 | 191 | int thermal_zone_device_set_policy(struct thermal_zone_device *tz, |
| @@ -199,18 +194,12 @@ int thermal_zone_device_set_policy(struct thermal_zone_device *tz, · 2 unmodified lines | ||
| 199 | 194 | struct thermal_governor *gov; |
| 200 | 195 | int ret = -EINVAL; |
| 201 | 196 | |
| 202 | − mutex_lock(&thermal_governor_lock); | |
| 203 | − mutex_lock(&tz->lock); | |
| 197 | + guard(mutex)(&thermal_governor_lock); | |
| 198 | + guard(thermal_zone)(tz); | |
| 204 | 199 | |
| 205 | 200 | gov = __find_governor(strim(policy)); |
| 206 | − if (!gov) | |
| 207 | − goto exit; | |
| 208 | − | |
| 209 | − ret = thermal_set_governor(tz, gov); | |
| 210 | − | |
| 211 | −exit: | |
| 212 | − mutex_unlock(&tz->lock); | |
| 213 | − mutex_unlock(&thermal_governor_lock); | |
| 201 | + if (gov) | |
| 202 | + ret = thermal_set_governor(tz, gov); | |
| 214 | 203 | |
| 215 | 204 | thermal_notify_tz_gov_change(tz, policy); |
| 216 | 205 | |
| @@ -222,15 +211,13 @@ int thermal_build_list_of_policies(char *buf) · 5 unmodified lines | ||
| 222 | 211 | struct thermal_governor *pos; |
| 223 | 212 | ssize_t count = 0; |
| 224 | 213 | |
| 225 | − mutex_lock(&thermal_governor_lock); | |
| 214 | + guard(mutex)(&thermal_governor_lock); | |
| 226 | 215 | |
| 227 | 216 | list_for_each_entry(pos, &thermal_governor_list, governor_list) { |
| 228 | 217 | count += sysfs_emit_at(buf, count, "%s ", pos->name); |
| 229 | 218 | } |
| 230 | 219 | count += sysfs_emit_at(buf, count, "\n"); |
| 231 | 220 | |
| 232 | − mutex_unlock(&thermal_governor_lock); | |
| 233 | − | |
| 234 | 221 | return count; |
| 235 | 222 | } |
| 236 | 223 | |
| @@ -421,83 +408,46 @@ static void handle_critical_trips(struct thermal_zone_device *tz, · 184 unmodified lines | ||
| 421 | 408 | tz->ops.hot(tz); |
| 422 | 409 | } |
| 423 | 410 | |
| 424 | −static void handle_thermal_trip(struct thermal_zone_device *tz, | |
| 425 | − struct thermal_trip_desc *td, | |
| 426 | − struct list_head *way_up_list, | |
| 427 | − struct list_head *way_down_list) | |
| 411 | +static void move_trip_to_sorted_list(struct thermal_trip_desc *td, | |
| 412 | + struct list_head *list) | |
| 428 | 413 | { |
| 429 | − const struct thermal_trip *trip = &td->trip; | |
| 430 | − int old_threshold; | |
| 431 | − | |
| 432 | − if (trip->temperature == THERMAL_TEMP_INVALID) | |
| 433 | − return; | |
| 414 | + struct thermal_trip_desc *entry; | |
| 434 | 415 | |
| 435 | 416 | /* |
| 436 | − * If the trip temperature or hysteresis has been updated recently, | |
| 437 | − * the threshold needs to be computed again using the new values. | |
| 438 | − * However, its initial value still reflects the old ones and that | |
| 439 | − * is what needs to be compared with the previous zone temperature | |
| 440 | − * to decide which action to take. | |
| 417 | + * Delete upfront and then add to make relocation within the same list | |
| 418 | + * work. | |
| 441 | 419 | */ |
| 442 | − old_threshold = td->threshold; | |
| 443 | − td->threshold = trip->temperature; | |
| 420 | + list_del(&td->list_node); | |
| 444 | 421 | |
| 445 | − if (tz->last_temperature >= old_threshold && | |
| 446 | − tz->last_temperature != THERMAL_TEMP_INIT) { | |
| 447 | − /* | |
| 448 | − * Mitigation is under way, so it needs to stop if the zone | |
| 449 | − * temperature falls below the low temperature of the trip. | |
| 450 | − * In that case, the trip temperature becomes the new threshold. | |
| 451 | − */ | |
| 452 | − if (tz->temperature < trip->temperature - trip->hysteresis) { | |
| 453 | − list_add(&td->notify_list_node, way_down_list); | |
| 454 | − td->notify_temp = trip->temperature - trip->hysteresis; | |
| 455 | − | |
| 456 | − if (trip->type == THERMAL_TRIP_PASSIVE) { | |
| 457 | − tz->passive--; | |
| 458 | − WARN_ON(tz->passive < 0); | |
| 459 | − } | |
| 460 | − } else { | |
| 461 | − td->threshold -= trip->hysteresis; | |
| 422 | + /* Assume that the new entry is likely to be the last one. */ | |
| 423 | + list_for_each_entry_reverse(entry, list, list_node) { | |
| 424 | + if (entry->threshold <= td->threshold) { | |
| 425 | + list_add(&td->list_node, &entry->list_node); | |
| 426 | + return; | |
| 462 | 427 | } |
| 463 | − } else if (tz->temperature >= trip->temperature) { | |
| 464 | − /* | |
| 465 | − * There is no mitigation under way, so it needs to be started | |
| 466 | − * if the zone temperature exceeds the trip one. The new | |
| 467 | − * threshold is then set to the low temperature of the trip. | |
| 468 | − */ | |
| 469 | − list_add_tail(&td->notify_list_node, way_up_list); | |
| 470 | − td->notify_temp = trip->temperature; | |
| 471 | − td->threshold -= trip->hysteresis; | |
| 472 | − | |
| 473 | − if (trip->type == THERMAL_TRIP_PASSIVE) | |
| 474 | − tz->passive++; | |
| 475 | − else if (trip->type == THERMAL_TRIP_CRITICAL || | |
| 476 | − trip->type == THERMAL_TRIP_HOT) | |
| 477 | − handle_critical_trips(tz, trip); | |
| 478 | 428 | } |
| 429 | + list_add(&td->list_node, list); | |
| 479 | 430 | } |
| 480 | 431 | |
| 481 | −static void thermal_zone_device_check(struct work_struct *work) | |
| 432 | +static void move_to_trips_high(struct thermal_zone_device *tz, | |
| 433 | + struct thermal_trip_desc *td) | |
| 482 | 434 | { |
| 483 | − struct thermal_zone_device *tz = container_of(work, struct | |
| 484 | − thermal_zone_device, | |
| 485 | − poll_queue.work); | |
| 486 | − thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | |
| 435 | + td->threshold = td->trip.temperature; | |
| 436 | + move_trip_to_sorted_list(td, &tz->trips_high); | |
| 487 | 437 | } |
| 488 | 438 | |
| 489 | −static void thermal_zone_device_init(struct thermal_zone_device *tz) | |
| 439 | +static void move_to_trips_reached(struct thermal_zone_device *tz, | |
| 440 | + struct thermal_trip_desc *td) | |
| 490 | 441 | { |
| 491 | − struct thermal_instance *pos; | |
| 492 | − | |
| 493 | − INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check); | |
| 442 | + td->threshold = td->trip.temperature - td->trip.hysteresis; | |
| 443 | + move_trip_to_sorted_list(td, &tz->trips_reached); | |
| 444 | +} | |
| 494 | 445 | |
| 495 | − tz->temperature = THERMAL_TEMP_INIT; | |
| 496 | − tz->passive = 0; | |
| 497 | − tz->prev_low_trip = -INT_MAX; | |
| 498 | − tz->prev_high_trip = INT_MAX; | |
| 499 | − list_for_each_entry(pos, &tz->thermal_instances, tz_node) | |
| 500 | − pos->initialized = false; | |
| 446 | +static void move_to_trips_invalid(struct thermal_zone_device *tz, | |
| 447 | + struct thermal_trip_desc *td) | |
| 448 | +{ | |
| 449 | + td->threshold = INT_MAX; | |
| 450 | + list_move(&td->list_node, &tz->trips_invalid); | |
| 501 | 451 | } |
| 502 | 452 | |
| 503 | 453 | static void thermal_governor_trip_crossed(struct thermal_governor *governor, |
| @@ -513,41 +463,154 @@ static void thermal_governor_trip_crossed(struct thermal_governor *governor, · 9 unmodified lines | ||
| 513 | 463 | } |
| 514 | 464 | |
| 515 | 465 | static void thermal_trip_crossed(struct thermal_zone_device *tz, |
| 516 | − const struct thermal_trip *trip, | |
| 466 | + struct thermal_trip_desc *td, | |
| 517 | 467 | struct thermal_governor *governor, |
| 518 | 468 | bool crossed_up) |
| 519 | 469 | { |
| 470 | + const struct thermal_trip *trip = &td->trip; | |
| 471 | + | |
| 520 | 472 | if (crossed_up) { |
| 473 | + if (trip->type == THERMAL_TRIP_PASSIVE) | |
| 474 | + tz->passive++; | |
| 475 | + else if (trip->type == THERMAL_TRIP_CRITICAL || | |
| 476 | + trip->type == THERMAL_TRIP_HOT) | |
| 477 | + handle_critical_trips(tz, trip); | |
| 478 | + | |
| 521 | 479 | thermal_notify_tz_trip_up(tz, trip); |
| 522 | 480 | thermal_debug_tz_trip_up(tz, trip); |
| 523 | 481 | } else { |
| 482 | + if (trip->type == THERMAL_TRIP_PASSIVE) { | |
| 483 | + tz->passive--; | |
| 484 | + WARN_ON(tz->passive < 0); | |
| 485 | + } | |
| 524 | 486 | thermal_notify_tz_trip_down(tz, trip); |
| 525 | 487 | thermal_debug_tz_trip_down(tz, trip); |
| 526 | 488 | } |
| 527 | 489 | thermal_governor_trip_crossed(governor, tz, trip, crossed_up); |
| 528 | 490 | } |
| 529 | 491 | |
| 530 | −static int thermal_trip_notify_cmp(void *not_used, const struct list_head *a, | |
| 531 | − const struct list_head *b) | |
| 492 | +void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz, | |
| 493 | + struct thermal_trip *trip, int hyst) | |
| 532 | 494 | { |
| 533 | − struct thermal_trip_desc *tda = container_of(a, struct thermal_trip_desc, | |
| 534 | − notify_list_node); | |
| 535 | − struct thermal_trip_desc *tdb = container_of(b, struct thermal_trip_desc, | |
| 536 | − notify_list_node); | |
| 537 | − return tda->notify_temp - tdb->notify_temp; | |
| 495 | + struct thermal_trip_desc *td = trip_to_trip_desc(trip); | |
| 496 | + | |
| 497 | + WRITE_ONCE(trip->hysteresis, hyst); | |
| 498 | + thermal_notify_tz_trip_change(tz, trip); | |
| 499 | + /* | |
| 500 | + * If the zone temperature is above or at the trip tmperature, the trip | |
| 501 | + * is in the trips_reached list and its threshold is equal to its low | |
| 502 | + * temperature. It needs to stay in that list, but its threshold needs | |
| 503 | + * to be updated and the list ordering may need to be restored. | |
| 504 | + */ | |
| 505 | + if (tz->temperature >= td->threshold) | |
| 506 | + move_to_trips_reached(tz, td); | |
| 507 | +} | |
| 508 | + | |
| 509 | +void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, | |
| 510 | + struct thermal_trip *trip, int temp) | |
| 511 | +{ | |
| 512 | + struct thermal_trip_desc *td = trip_to_trip_desc(trip); | |
| 513 | + int old_temp = trip->temperature; | |
| 514 | + | |
| 515 | + if (old_temp == temp) | |
| 516 | + return; | |
| 517 | + | |
| 518 | + WRITE_ONCE(trip->temperature, temp); | |
| 519 | + thermal_notify_tz_trip_change(tz, trip); | |
| 520 | + | |
| 521 | + if (old_temp == THERMAL_TEMP_INVALID) { | |
| 522 | + /* | |
| 523 | + * The trip was invalid before the change, so move it to the | |
| 524 | + * trips_high list regardless of the new temperature value | |
| 525 | + * because there is no mitigation under way for it. If a | |
| 526 | + * mitigation needs to be started, the trip will be moved to the | |
| 527 | + * trips_reached list later. | |
| 528 | + */ | |
| 529 | + move_to_trips_high(tz, td); | |
| 530 | + return; | |
| 531 | + } | |
| 532 | + | |
| 533 | + if (temp == THERMAL_TEMP_INVALID) { | |
| 534 | + /* | |
| 535 | + * If the trip is in the trips_reached list, mitigation is under | |
| 536 | + * way for it and it needs to be stopped because the trip is | |
| 537 | + * effectively going away. | |
| 538 | + */ | |
| 539 | + if (tz->temperature >= td->threshold) | |
| 540 | + thermal_trip_crossed(tz, td, thermal_get_tz_governor(tz), false); | |
| 541 | + | |
| 542 | + move_to_trips_invalid(tz, td); | |
| 543 | + return; | |
| 544 | + } | |
| 545 | + | |
| 546 | + /* | |
| 547 | + * The trip stays on its current list, but its threshold needs to be | |
| 548 | + * updated due to the temperature change and the list ordering may need | |
| 549 | + * to be restored. | |
| 550 | + */ | |
| 551 | + if (tz->temperature >= td->threshold) | |
| 552 | + move_to_trips_reached(tz, td); | |
| 553 | + else | |
| 554 | + move_to_trips_high(tz, td); | |
| 555 | +} | |
| 556 | +EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp); | |
| 557 | + | |
| 558 | +static void thermal_zone_handle_trips(struct thermal_zone_device *tz, | |
| 559 | + struct thermal_governor *governor, | |
| 560 | + int *low, int *high) | |
| 561 | +{ | |
| 562 | + struct thermal_trip_desc *td, *next; | |
| 563 | + LIST_HEAD(way_down_list); | |
| 564 | + | |
| 565 | + /* Check the trips that were below or at the zone temperature. */ | |
| 566 | + list_for_each_entry_safe_reverse(td, next, &tz->trips_reached, list_node) { | |
| 567 | + if (td->threshold <= tz->temperature) | |
| 568 | + break; | |
| 569 | + | |
| 570 | + thermal_trip_crossed(tz, td, governor, false); | |
| 571 | + /* | |
| 572 | + * The current trips_high list needs to be processed before | |
| 573 | + * adding new entries to it, so put them on a temporary list. | |
| 574 | + */ | |
| 575 | + list_move(&td->list_node, &way_down_list); | |
| 576 | + } | |
| 577 | + /* Check the trips that were previously above the zone temperature. */ | |
| 578 | + list_for_each_entry_safe(td, next, &tz->trips_high, list_node) { | |
| 579 | + if (td->threshold > tz->temperature) | |
| 580 | + break; | |
| 581 | + | |
| 582 | + thermal_trip_crossed(tz, td, governor, true); | |
| 583 | + move_to_trips_reached(tz, td); | |
| 584 | + } | |
| 585 | + /* Move all of the trips from the temporary list to trips_high. */ | |
| 586 | + list_for_each_entry_safe(td, next, &way_down_list, list_node) | |
| 587 | + move_to_trips_high(tz, td); | |
| 588 | + | |
| 589 | + if (!list_empty(&tz->trips_reached)) { | |
| 590 | + td = list_last_entry(&tz->trips_reached, | |
| 591 | + struct thermal_trip_desc, list_node); | |
| 592 | + /* | |
| 593 | + * Set the "low" value below the current trip threshold in case | |
| 594 | + * the zone temperature is at that threshold and stays there, | |
| 595 | + * which would trigger a new interrupt immediately in vain. | |
| 596 | + */ | |
| 597 | + *low = td->threshold - 1; | |
| 598 | + } | |
| 599 | + if (!list_empty(&tz->trips_high)) { | |
| 600 | + td = list_first_entry(&tz->trips_high, | |
| 601 | + struct thermal_trip_desc, list_node); | |
| 602 | + *high = td->threshold; | |
| 603 | + } | |
| 538 | 604 | } |
| 539 | 605 | |
| 540 | 606 | void __thermal_zone_device_update(struct thermal_zone_device *tz, |
| 541 | 607 | enum thermal_notify_event event) |
| 542 | 608 | { |
| 543 | 609 | struct thermal_governor *governor = thermal_get_tz_governor(tz); |
| 544 | − struct thermal_trip_desc *td; | |
| 545 | − LIST_HEAD(way_down_list); | |
| 546 | − LIST_HEAD(way_up_list); | |
| 547 | 610 | int low = -INT_MAX, high = INT_MAX; |
| 548 | 611 | int temp, ret; |
| 549 | 612 | |
| 550 | − if (tz->suspended || tz->mode != THERMAL_DEVICE_ENABLED) | |
| 613 | + if (tz->state != TZ_STATE_READY || tz->mode != THERMAL_DEVICE_ENABLED) | |
| 551 | 614 | return; |
| 552 | 615 | |
| 553 | 616 | ret = __thermal_zone_get_temp(tz, &temp); |
| @@ -575,26 +638,12 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, · 21 unmodified lines | ||
| 575 | 638 | |
| 576 | 639 | tz->notify_event = event; |
| 577 | 640 | |
| 578 | − for_each_trip_desc(tz, td) { | |
| 579 | − handle_thermal_trip(tz, td, &way_up_list, &way_down_list); | |
| 641 | + thermal_zone_handle_trips(tz, governor, &low, &high); | |
| 580 | 642 | |
| 581 | − if (td->threshold <= tz->temperature && td->threshold > low) | |
| 582 | − low = td->threshold; | |
| 583 | − | |
| 584 | − if (td->threshold >= tz->temperature && td->threshold < high) | |
| 585 | − high = td->threshold; | |
| 586 | − } | |
| 643 | + thermal_thresholds_handle(tz, &low, &high); | |
| 587 | 644 | |
| 588 | 645 | thermal_zone_set_trips(tz, low, high); |
| 589 | 646 | |
| 590 | − list_sort(NULL, &way_up_list, thermal_trip_notify_cmp); | |
| 591 | − list_for_each_entry(td, &way_up_list, notify_list_node) | |
| 592 | − thermal_trip_crossed(tz, &td->trip, governor, true); | |
| 593 | − | |
| 594 | − list_sort(NULL, &way_down_list, thermal_trip_notify_cmp); | |
| 595 | − list_for_each_entry_reverse(td, &way_down_list, notify_list_node) | |
| 596 | − thermal_trip_crossed(tz, &td->trip, governor, false); | |
| 597 | − | |
| 598 | 647 | if (governor->manage) |
| 599 | 648 | governor->manage(tz); |
| 600 | 649 | |
| @@ -609,26 +658,18 @@ static int thermal_zone_device_set_mode(struct thermal_zone_device *tz, · 8 unmodified lines | ||
| 609 | 658 | { |
| 610 | 659 | int ret; |
| 611 | 660 | |
| 612 | − mutex_lock(&tz->lock); | |
| 661 | + guard(thermal_zone)(tz); | |
| 613 | 662 | |
| 614 | 663 | /* do nothing if mode isn't changing */ |
| 615 | − if (mode == tz->mode) { | |
| 616 | − mutex_unlock(&tz->lock); | |
| 617 | − | |
| 664 | + if (mode == tz->mode) | |
| 618 | 665 | return 0; |
| 619 | − } | |
| 620 | 666 | |
| 621 | 667 | ret = __thermal_zone_device_set_mode(tz, mode); |
| 622 | − if (ret) { | |
| 623 | − mutex_unlock(&tz->lock); | |
| 624 | − | |
| 668 | + if (ret) | |
| 625 | 669 | return ret; |
| 626 | − } | |
| 627 | 670 | |
| 628 | 671 | __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); |
| 629 | 672 | |
| 630 | − mutex_unlock(&tz->lock); | |
| 631 | − | |
| 632 | 673 | if (mode == THERMAL_DEVICE_ENABLED) |
| 633 | 674 | thermal_notify_tz_enable(tz); |
| 634 | 675 | else |
| @@ -657,85 +698,81 @@ static bool thermal_zone_is_present(struct thermal_zone_device *tz) · 22 unmodified lines | ||
| 657 | 698 | void thermal_zone_device_update(struct thermal_zone_device *tz, |
| 658 | 699 | enum thermal_notify_event event) |
| 659 | 700 | { |
| 660 | − mutex_lock(&tz->lock); | |
| 701 | + guard(thermal_zone)(tz); | |
| 702 | + | |
| 661 | 703 | if (thermal_zone_is_present(tz)) |
| 662 | 704 | __thermal_zone_device_update(tz, event); |
| 663 | − mutex_unlock(&tz->lock); | |
| 664 | 705 | } |
| 665 | 706 | EXPORT_SYMBOL_GPL(thermal_zone_device_update); |
| 666 | 707 | |
| 667 | −void thermal_zone_trip_down(struct thermal_zone_device *tz, | |
| 668 | − const struct thermal_trip *trip) | |
| 669 | −{ | |
| 670 | − thermal_trip_crossed(tz, trip, thermal_get_tz_governor(tz), false); | |
| 671 | −} | |
| 672 | − | |
| 673 | 708 | int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *), |
| 674 | 709 | void *data) |
| 675 | 710 | { |
| 676 | 711 | struct thermal_governor *gov; |
| 677 | − int ret = 0; | |
| 678 | 712 | |
| 679 | − mutex_lock(&thermal_governor_lock); | |
| 713 | + guard(mutex)(&thermal_governor_lock); | |
| 714 | + | |
| 680 | 715 | list_for_each_entry(gov, &thermal_governor_list, governor_list) { |
| 716 | + int ret; | |
| 717 | + | |
| 681 | 718 | ret = cb(gov, data); |
| 682 | 719 | if (ret) |
| 683 | − break; | |
| 720 | + return ret; | |
| 684 | 721 | } |
| 685 | − mutex_unlock(&thermal_governor_lock); | |
| 686 | 722 | |
| 687 | − return ret; | |
| 723 | + return 0; | |
| 688 | 724 | } |
| 689 | 725 | |
| 690 | 726 | int for_each_thermal_cooling_device(int (*cb)(struct thermal_cooling_device *, |
| 691 | 727 | void *), void *data) |
| 692 | 728 | { |
| 693 | 729 | struct thermal_cooling_device *cdev; |
| 694 | − int ret = 0; | |
| 695 | 730 | |
| 696 | − mutex_lock(&thermal_list_lock); | |
| 731 | + guard(mutex)(&thermal_list_lock); | |
| 732 | + | |
| 697 | 733 | list_for_each_entry(cdev, &thermal_cdev_list, node) { |
| 734 | + int ret; | |
| 735 | + | |
| 698 | 736 | ret = cb(cdev, data); |
| 699 | 737 | if (ret) |
| 700 | − break; | |
| 738 | + return ret; | |
| 701 | 739 | } |
| 702 | − mutex_unlock(&thermal_list_lock); | |
| 703 | 740 | |
| 704 | − return ret; | |
| 741 | + return 0; | |
| 705 | 742 | } |
| 706 | 743 | |
| 707 | 744 | int for_each_thermal_zone(int (*cb)(struct thermal_zone_device *, void *), |
| 708 | 745 | void *data) |
| 709 | 746 | { |
| 710 | 747 | struct thermal_zone_device *tz; |
| 711 | − int ret = 0; | |
| 712 | 748 | |
| 713 | − mutex_lock(&thermal_list_lock); | |
| 749 | + guard(mutex)(&thermal_list_lock); | |
| 750 | + | |
| 714 | 751 | list_for_each_entry(tz, &thermal_tz_list, node) { |
| 752 | + int ret; | |
| 753 | + | |
| 715 | 754 | ret = cb(tz, data); |
| 716 | 755 | if (ret) |
| 717 | − break; | |
| 756 | + return ret; | |
| 718 | 757 | } |
| 719 | − mutex_unlock(&thermal_list_lock); | |
| 720 | 758 | |
| 721 | − return ret; | |
| 759 | + return 0; | |
| 722 | 760 | } |
| 723 | 761 | |
| 724 | 762 | struct thermal_zone_device *thermal_zone_get_by_id(int id) |
| 725 | 763 | { |
| 726 | − struct thermal_zone_device *tz, *match = NULL; | |
| 764 | + struct thermal_zone_device *tz; | |
| 765 | + | |
| 766 | + guard(mutex)(&thermal_list_lock); | |
| 727 | 767 | |
| 728 | − mutex_lock(&thermal_list_lock); | |
| 729 | 768 | list_for_each_entry(tz, &thermal_tz_list, node) { |
| 730 | 769 | if (tz->id == id) { |
| 731 | 770 | get_device(&tz->device); |
| 732 | − match = tz; | |
| 733 | − break; | |
| 771 | + return tz; | |
| 734 | 772 | } |
| 735 | 773 | } |
| 736 | − mutex_unlock(&thermal_list_lock); | |
| 737 | 774 | |
| 738 | − return match; | |
| 775 | + return NULL; | |
| 739 | 776 | } |
| 740 | 777 | |
| 741 | 778 | /* |
| @@ -748,12 +785,32 @@ struct thermal_zone_device *thermal_zone_get_by_id(int id) · 6 unmodified lines | ||
| 748 | 785 | * binding, and unbinding. |
| 749 | 786 | */ |
| 750 | 787 | |
| 788 | +static int thermal_instance_add(struct thermal_instance *new_instance, | |
| 789 | + struct thermal_cooling_device *cdev, | |
| 790 | + struct thermal_trip_desc *td) | |
| 791 | +{ | |
| 792 | + struct thermal_instance *instance; | |
| 793 | + | |
| 794 | + list_for_each_entry(instance, &td->thermal_instances, trip_node) { | |
| 795 | + if (instance->cdev == cdev) | |
| 796 | + return -EEXIST; | |
| 797 | + } | |
| 798 | + | |
| 799 | + list_add_tail(&new_instance->trip_node, &td->thermal_instances); | |
| 800 | + | |
| 801 | + guard(cooling_dev)(cdev); | |
| 802 | + | |
| 803 | + list_add_tail(&new_instance->cdev_node, &cdev->thermal_instances); | |
| 804 | + | |
| 805 | + return 0; | |
| 806 | +} | |
| 807 | + | |
| 751 | 808 | /** |
| 752 | 809 | * thermal_bind_cdev_to_trip - bind a cooling device to a thermal zone |
| 753 | 810 | * @tz: pointer to struct thermal_zone_device |
| 754 | − * @trip: trip point the cooling devices is associated with in this zone. | |
| 811 | + * @td: descriptor of the trip point to bind @cdev to | |
| 755 | 812 | * @cdev: pointer to struct thermal_cooling_device |
| 756 | − * @cool_spec: cooling specification for @trip and @cdev | |
| 813 | + * @cool_spec: cooling specification for the trip point and @cdev | |
| 757 | 814 | * |
| 758 | 815 | * This interface function bind a thermal cooling device to the certain trip |
| 759 | 816 | * point of a thermal zone device. |
| @@ -762,12 +819,11 @@ struct thermal_zone_device *thermal_zone_get_by_id(int id) · 2 unmodified lines | ||
| 762 | 819 | * Return: 0 on success, the proper error value otherwise. |
| 763 | 820 | */ |
| 764 | 821 | static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, |
| 765 | − const struct thermal_trip *trip, | |
| 822 | + struct thermal_trip_desc *td, | |
| 766 | 823 | struct thermal_cooling_device *cdev, |
| 767 | 824 | struct cooling_spec *cool_spec) |
| 768 | 825 | { |
| 769 | 826 | struct thermal_instance *dev; |
| 770 | − struct thermal_instance *pos; | |
| 771 | 827 | bool upper_no_limit; |
| 772 | 828 | int result; |
| 773 | 829 | |
| @@ -790,7 +846,7 @@ static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, · 16 unmodified lines | ||
| 790 | 846 | return -ENOMEM; |
| 791 | 847 | |
| 792 | 848 | dev->cdev = cdev; |
| 793 | − dev->trip = trip; | |
| 849 | + dev->trip = &td->trip; | |
| 794 | 850 | dev->upper = cool_spec->upper; |
| 795 | 851 | dev->upper_no_limit = upper_no_limit; |
| 796 | 852 | dev->lower = cool_spec->lower; |
| @@ -829,24 +885,15 @@ static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, · 32 unmodified lines | ||
| 829 | 885 | if (result) |
| 830 | 886 | goto remove_trip_file; |
| 831 | 887 | |
| 832 | − mutex_lock(&cdev->lock); | |
| 833 | − list_for_each_entry(pos, &tz->thermal_instances, tz_node) | |
| 834 | − if (pos->trip == trip && pos->cdev == cdev) { | |
| 835 | − result = -EEXIST; | |
| 836 | − break; | |
| 837 | − } | |
| 838 | − if (!result) { | |
| 839 | − list_add_tail(&dev->tz_node, &tz->thermal_instances); | |
| 840 | − list_add_tail(&dev->cdev_node, &cdev->thermal_instances); | |
| 841 | − atomic_set(&tz->need_update, 1); | |
| 888 | + result = thermal_instance_add(dev, cdev, td); | |
| 889 | + if (result) | |
| 890 | + goto remove_weight_file; | |
| 842 | 891 | |
| 843 | − thermal_governor_update_tz(tz, THERMAL_TZ_BIND_CDEV); | |
| 844 | − } | |
| 845 | − mutex_unlock(&cdev->lock); | |
| 892 | + thermal_governor_update_tz(tz, THERMAL_TZ_BIND_CDEV); | |
| 846 | 893 | |
| 847 | − if (!result) | |
| 848 | − return 0; | |
| 894 | + return 0; | |
| 849 | 895 | |
| 896 | +remove_weight_file: | |
| 850 | 897 | device_remove_file(&tz->device, &dev->weight_attr); |
| 851 | 898 | remove_trip_file: |
| 852 | 899 | device_remove_file(&tz->device, &dev->attr); |
| @@ -859,10 +906,19 @@ free_mem: · 6 unmodified lines | ||
| 859 | 906 | return result; |
| 860 | 907 | } |
| 861 | 908 | |
| 909 | +static void thermal_instance_delete(struct thermal_instance *instance) | |
| 910 | +{ | |
| 911 | + list_del(&instance->trip_node); | |
| 912 | + | |
| 913 | + guard(cooling_dev)(instance->cdev); | |
| 914 | + | |
| 915 | + list_del(&instance->cdev_node); | |
| 916 | +} | |
| 917 | + | |
| 862 | 918 | /** |
| 863 | 919 | * thermal_unbind_cdev_from_trip - unbind a cooling device from a thermal zone. |
| 864 | 920 | * @tz: pointer to a struct thermal_zone_device. |
| 865 | − * @trip: trip point the cooling devices is associated with in this zone. | |
| 921 | + * @td: descriptor of the trip point to unbind @cdev from | |
| 866 | 922 | * @cdev: pointer to a struct thermal_cooling_device. |
| 867 | 923 | * |
| 868 | 924 | * This interface function unbind a thermal cooling device from the certain |
| @@ -870,28 +926,23 @@ free_mem: · 1 unmodified line | ||
| 870 | 926 | * This function is usually called in the thermal zone device .unbind callback. |
| 871 | 927 | */ |
| 872 | 928 | static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, |
| 873 | − const struct thermal_trip *trip, | |
| 929 | + struct thermal_trip_desc *td, | |
| 874 | 930 | struct thermal_cooling_device *cdev) |
| 875 | 931 | { |
| 876 | 932 | struct thermal_instance *pos, *next; |
| 877 | 933 | |
| 878 | − mutex_lock(&cdev->lock); | |
| 879 | − list_for_each_entry_safe(pos, next, &tz->thermal_instances, tz_node) { | |
| 880 | − if (pos->trip == trip && pos->cdev == cdev) { | |
| 881 | − list_del(&pos->tz_node); | |
| 882 | − list_del(&pos->cdev_node); | |
| 883 | − | |
| 884 | − thermal_governor_update_tz(tz, THERMAL_TZ_UNBIND_CDEV); | |
| 885 | − | |
| 886 | − mutex_unlock(&cdev->lock); | |
| 934 | + list_for_each_entry_safe(pos, next, &td->thermal_instances, trip_node) { | |
| 935 | + if (pos->cdev == cdev) { | |
| 936 | + thermal_instance_delete(pos); | |
| 887 | 937 | goto unbind; |
| 888 | 938 | } |
| 889 | 939 | } |
| 890 | − mutex_unlock(&cdev->lock); | |
| 891 | 940 | |
| 892 | 941 | return; |
| 893 | 942 | |
| 894 | 943 | unbind: |
| 944 | + thermal_governor_update_tz(tz, THERMAL_TZ_UNBIND_CDEV); | |
| 945 | + | |
| 895 | 946 | device_remove_file(&tz->device, &pos->weight_attr); |
| 896 | 947 | device_remove_file(&tz->device, &pos->attr); |
| 897 | 948 | sysfs_remove_link(&tz->device.kobj, pos->name); |
| @@ -924,25 +975,23 @@ static struct class *thermal_class; · 26 unmodified lines | ||
| 924 | 975 | |
| 925 | 976 | static inline |
| 926 | 977 | void print_bind_err_msg(struct thermal_zone_device *tz, |
| 927 | − const struct thermal_trip *trip, | |
| 978 | + const struct thermal_trip_desc *td, | |
| 928 | 979 | struct thermal_cooling_device *cdev, int ret) |
| 929 | 980 | { |
| 930 | 981 | dev_err(&tz->device, "binding cdev %s to trip %d failed: %d\n", |
| 931 | − cdev->type, thermal_zone_trip_id(tz, trip), ret); | |
| 982 | + cdev->type, thermal_zone_trip_id(tz, &td->trip), ret); | |
| 932 | 983 | } |
| 933 | 984 | |
| 934 | −static void thermal_zone_cdev_bind(struct thermal_zone_device *tz, | |
| 935 | − struct thermal_cooling_device *cdev) | |
| 985 | +static bool __thermal_zone_cdev_bind(struct thermal_zone_device *tz, | |
| 986 | + struct thermal_cooling_device *cdev) | |
| 936 | 987 | { |
| 937 | 988 | struct thermal_trip_desc *td; |
| 989 | + bool update_tz = false; | |
| 938 | 990 | |
| 939 | 991 | if (!tz->ops.should_bind) |
| 940 | − return; | |
| 941 | − | |
| 942 | − mutex_lock(&tz->lock); | |
| 992 | + return false; | |
| 943 | 993 | |
| 944 | 994 | for_each_trip_desc(tz, td) { |
| 945 | − struct thermal_trip *trip = &td->trip; | |
| 946 | 995 | struct cooling_spec c = { |
| 947 | 996 | .upper = THERMAL_NO_LIMIT, |
| 948 | 997 | .lower = THERMAL_NO_LIMIT, |
| @@ -950,15 +999,40 @@ static void thermal_zone_cdev_bind(struct thermal_zone_device *tz, · 1 unmodified line | ||
| 950 | 999 | }; |
| 951 | 1000 | int ret; |
| 952 | 1001 | |
| 953 | − if (!tz->ops.should_bind(tz, trip, cdev, &c)) | |
| 1002 | + if (!tz->ops.should_bind(tz, &td->trip, cdev, &c)) | |
| 954 | 1003 | continue; |
| 955 | 1004 | |
| 956 | − ret = thermal_bind_cdev_to_trip(tz, trip, cdev, &c); | |
| 957 | − if (ret) | |
| 958 | − print_bind_err_msg(tz, trip, cdev, ret); | |
| 1005 | + ret = thermal_bind_cdev_to_trip(tz, td, cdev, &c); | |
| 1006 | + if (ret) { | |
| 1007 | + print_bind_err_msg(tz, td, cdev, ret); | |
| 1008 | + continue; | |
| 1009 | + } | |
| 1010 | + | |
| 1011 | + update_tz = true; | |
| 959 | 1012 | } |
| 960 | 1013 | |
| 961 | − mutex_unlock(&tz->lock); | |
| 1014 | + return update_tz; | |
| 1015 | +} | |
| 1016 | + | |
| 1017 | +static void thermal_zone_cdev_bind(struct thermal_zone_device *tz, | |
| 1018 | + struct thermal_cooling_device *cdev) | |
| 1019 | +{ | |
| 1020 | + guard(thermal_zone)(tz); | |
| 1021 | + | |
| 1022 | + if (__thermal_zone_cdev_bind(tz, cdev)) | |
| 1023 | + __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | |
| 1024 | +} | |
| 1025 | + | |
| 1026 | +static void thermal_cooling_device_init_complete(struct thermal_cooling_device *cdev) | |
| 1027 | +{ | |
| 1028 | + struct thermal_zone_device *tz; | |
| 1029 | + | |
| 1030 | + guard(mutex)(&thermal_list_lock); | |
| 1031 | + | |
| 1032 | + list_add(&cdev->node, &thermal_cdev_list); | |
| 1033 | + | |
| 1034 | + list_for_each_entry(tz, &thermal_tz_list, node) | |
| 1035 | + thermal_zone_cdev_bind(tz, cdev); | |
| 962 | 1036 | } |
| 963 | 1037 | |
| 964 | 1038 | /** |
| @@ -983,7 +1057,6 @@ __thermal_cooling_device_register(struct device_node *np, · 18 unmodified lines | ||
| 983 | 1057 | const struct thermal_cooling_device_ops *ops) |
| 984 | 1058 | { |
| 985 | 1059 | struct thermal_cooling_device *cdev; |
| 986 | − struct thermal_zone_device *pos = NULL; | |
| 987 | 1060 | unsigned long current_state; |
| 988 | 1061 | int id, ret; |
| 989 | 1062 | |
| @@ -1050,21 +1123,7 @@ __thermal_cooling_device_register(struct device_node *np, · 60 unmodified lines | ||
| 1050 | 1123 | if (current_state <= cdev->max_state) |
| 1051 | 1124 | thermal_debug_cdev_add(cdev, current_state); |
| 1052 | 1125 | |
| 1053 | − /* Add 'this' new cdev to the global cdev list */ | |
| 1054 | − mutex_lock(&thermal_list_lock); | |
| 1055 | − | |
| 1056 | − list_add(&cdev->node, &thermal_cdev_list); | |
| 1057 | − | |
| 1058 | − /* Update binding information for 'this' new cdev */ | |
| 1059 | − list_for_each_entry(pos, &thermal_tz_list, node) | |
| 1060 | − thermal_zone_cdev_bind(pos, cdev); | |
| 1061 | − | |
| 1062 | − list_for_each_entry(pos, &thermal_tz_list, node) | |
| 1063 | − if (atomic_cmpxchg(&pos->need_update, 1, 0)) | |
| 1064 | − thermal_zone_device_update(pos, | |
| 1065 | − THERMAL_EVENT_UNSPECIFIED); | |
| 1066 | − | |
| 1067 | − mutex_unlock(&thermal_list_lock); | |
| 1126 | + thermal_cooling_device_init_complete(cdev); | |
| 1068 | 1127 | |
| 1069 | 1128 | return cdev; |
| 1070 | 1129 | |
| @@ -1207,19 +1266,19 @@ void thermal_cooling_device_update(struct thermal_cooling_device *cdev) · 136 unmodified lines | ||
| 1207 | 1266 | * Hold thermal_list_lock throughout the update to prevent the device |
| 1208 | 1267 | * from going away while being updated. |
| 1209 | 1268 | */ |
| 1210 | − mutex_lock(&thermal_list_lock); | |
| 1269 | + guard(mutex)(&thermal_list_lock); | |
| 1211 | 1270 | |
| 1212 | 1271 | if (!thermal_cooling_device_present(cdev)) |
| 1213 | − goto unlock_list; | |
| 1272 | + return; | |
| 1214 | 1273 | |
| 1215 | 1274 | /* |
| 1216 | 1275 | * Update under the cdev lock to prevent the state from being set beyond |
| 1217 | 1276 | * the new limit concurrently. |
| 1218 | 1277 | */ |
| 1219 | − mutex_lock(&cdev->lock); | |
| 1278 | + guard(cooling_dev)(cdev); | |
| 1220 | 1279 | |
| 1221 | 1280 | if (cdev->ops->get_max_state(cdev, &cdev->max_state)) |
| 1222 | − goto unlock; | |
| 1281 | + return; | |
| 1223 | 1282 | |
| 1224 | 1283 | thermal_cooling_device_stats_reinit(cdev); |
| 1225 | 1284 | |
| @@ -1246,63 +1305,59 @@ void thermal_cooling_device_update(struct thermal_cooling_device *cdev) · 20 unmodified lines | ||
| 1246 | 1305 | } |
| 1247 | 1306 | |
| 1248 | 1307 | if (cdev->ops->get_cur_state(cdev, &state) || state > cdev->max_state) |
| 1249 | − goto unlock; | |
| 1308 | + return; | |
| 1250 | 1309 | |
| 1251 | 1310 | thermal_cooling_device_stats_update(cdev, state); |
| 1252 | − | |
| 1253 | −unlock: | |
| 1254 | − mutex_unlock(&cdev->lock); | |
| 1255 | − | |
| 1256 | −unlock_list: | |
| 1257 | − mutex_unlock(&thermal_list_lock); | |
| 1258 | 1311 | } |
| 1259 | 1312 | EXPORT_SYMBOL_GPL(thermal_cooling_device_update); |
| 1260 | 1313 | |
| 1261 | −static void thermal_zone_cdev_unbind(struct thermal_zone_device *tz, | |
| 1262 | − struct thermal_cooling_device *cdev) | |
| 1314 | +static void __thermal_zone_cdev_unbind(struct thermal_zone_device *tz, | |
| 1315 | + struct thermal_cooling_device *cdev) | |
| 1263 | 1316 | { |
| 1264 | 1317 | struct thermal_trip_desc *td; |
| 1265 | 1318 | |
| 1266 | − mutex_lock(&tz->lock); | |
| 1267 | − | |
| 1268 | 1319 | for_each_trip_desc(tz, td) |
| 1269 | − thermal_unbind_cdev_from_trip(tz, &td->trip, cdev); | |
| 1270 | − | |
| 1271 | − mutex_unlock(&tz->lock); | |
| 1320 | + thermal_unbind_cdev_from_trip(tz, td, cdev); | |
| 1272 | 1321 | } |
| 1273 | 1322 | |
| 1274 | −/** | |
| 1275 | − * thermal_cooling_device_unregister - removes a thermal cooling device | |
| 1276 | − * @cdev: the thermal cooling device to remove. | |
| 1277 | − * | |
| 1278 | − * thermal_cooling_device_unregister() must be called when a registered | |
| 1279 | − * thermal cooling device is no longer needed. | |
| 1280 | − */ | |
| 1281 | −void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) | |
| 1323 | +static void thermal_zone_cdev_unbind(struct thermal_zone_device *tz, | |
| 1324 | + struct thermal_cooling_device *cdev) | |
| 1282 | 1325 | { |
| 1283 | − struct thermal_zone_device *tz; | |
| 1326 | + guard(thermal_zone)(tz); | |
| 1284 | 1327 | |
| 1285 | − if (!cdev) | |
| 1286 | − return; | |
| 1328 | + __thermal_zone_cdev_unbind(tz, cdev); | |
| 1329 | +} | |
| 1287 | 1330 | |
| 1288 | − thermal_debug_cdev_remove(cdev); | |
| 1331 | +static bool thermal_cooling_device_exit(struct thermal_cooling_device *cdev) | |
| 1332 | +{ | |
| 1333 | + struct thermal_zone_device *tz; | |
| 1289 | 1334 | |
| 1290 | − mutex_lock(&thermal_list_lock); | |
| 1335 | + guard(mutex)(&thermal_list_lock); | |
| 1291 | 1336 | |
| 1292 | − if (!thermal_cooling_device_present(cdev)) { | |
| 1293 | − mutex_unlock(&thermal_list_lock); | |
| 1294 | − return; | |
| 1295 | − } | |
| 1337 | + if (!thermal_cooling_device_present(cdev)) | |
| 1338 | + return false; | |
| 1296 | 1339 | |
| 1297 | 1340 | list_del(&cdev->node); |
| 1298 | 1341 | |
| 1299 | − /* Unbind all thermal zones associated with 'this' cdev */ | |
| 1300 | 1342 | list_for_each_entry(tz, &thermal_tz_list, node) |
| 1301 | 1343 | thermal_zone_cdev_unbind(tz, cdev); |
| 1302 | 1344 | |
| 1303 | − mutex_unlock(&thermal_list_lock); | |
| 1345 | + return true; | |
| 1346 | +} | |
| 1304 | 1347 | |
| 1305 | − device_unregister(&cdev->device); | |
| 1348 | +/** | |
| 1349 | + * thermal_cooling_device_unregister() - removes a thermal cooling device | |
| 1350 | + * @cdev: Thermal cooling device to remove. | |
| 1351 | + */ | |
| 1352 | +void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) | |
| 1353 | +{ | |
| 1354 | + if (!cdev) | |
| 1355 | + return; | |
| 1356 | + | |
| 1357 | + thermal_debug_cdev_remove(cdev); | |
| 1358 | + | |
| 1359 | + if (thermal_cooling_device_exit(cdev)) | |
| 1360 | + device_unregister(&cdev->device); | |
| 1306 | 1361 | } |
| 1307 | 1362 | EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); |
| 1308 | 1363 | |
| @@ -1314,7 +1369,7 @@ int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) · 5 unmodified lines | ||
| 1314 | 1369 | if (tz->ops.get_crit_temp) |
| 1315 | 1370 | return tz->ops.get_crit_temp(tz, temp); |
| 1316 | 1371 | |
| 1317 | − mutex_lock(&tz->lock); | |
| 1372 | + guard(thermal_zone)(tz); | |
| 1318 | 1373 | |
| 1319 | 1374 | for_each_trip_desc(tz, td) { |
| 1320 | 1375 | const struct thermal_trip *trip = &td->trip; |
| @@ -1326,12 +1381,91 @@ int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) · 5 unmodified lines | ||
| 1326 | 1381 | } |
| 1327 | 1382 | } |
| 1328 | 1383 | |
| 1329 | − mutex_unlock(&tz->lock); | |
| 1330 | − | |
| 1331 | 1384 | return ret; |
| 1332 | 1385 | } |
| 1333 | 1386 | EXPORT_SYMBOL_GPL(thermal_zone_get_crit_temp); |
| 1334 | 1387 | |
| 1388 | +static void thermal_zone_device_check(struct work_struct *work) | |
| 1389 | +{ | |
| 1390 | + struct thermal_zone_device *tz = container_of(work, struct | |
| 1391 | + thermal_zone_device, | |
| 1392 | + poll_queue.work); | |
| 1393 | + thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | |
| 1394 | +} | |
| 1395 | + | |
| 1396 | +static void thermal_zone_device_init(struct thermal_zone_device *tz) | |
| 1397 | +{ | |
| 1398 | + struct thermal_trip_desc *td, *next; | |
| 1399 | + | |
| 1400 | + INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check); | |
| 1401 | + | |
| 1402 | + tz->temperature = THERMAL_TEMP_INIT; | |
| 1403 | + tz->passive = 0; | |
| 1404 | + tz->prev_low_trip = -INT_MAX; | |
| 1405 | + tz->prev_high_trip = INT_MAX; | |
| 1406 | + for_each_trip_desc(tz, td) { | |
| 1407 | + struct thermal_instance *instance; | |
| 1408 | + | |
| 1409 | + list_for_each_entry(instance, &td->thermal_instances, trip_node) | |
| 1410 | + instance->initialized = false; | |
| 1411 | + } | |
| 1412 | + /* | |
| 1413 | + * At this point, all valid trips need to be moved to trips_high so that | |
| 1414 | + * mitigation can be started if the zone temperature is above them. | |
| 1415 | + */ | |
| 1416 | + list_for_each_entry_safe(td, next, &tz->trips_invalid, list_node) { | |
| 1417 | + if (td->trip.temperature != THERMAL_TEMP_INVALID) | |
| 1418 | + move_to_trips_high(tz, td); | |
| 1419 | + } | |
| 1420 | + /* The trips_reached list may not be empty during system resume. */ | |
| 1421 | + list_for_each_entry_safe(td, next, &tz->trips_reached, list_node) { | |
| 1422 | + if (td->trip.temperature == THERMAL_TEMP_INVALID) | |
| 1423 | + move_to_trips_invalid(tz, td); | |
| 1424 | + else | |
| 1425 | + move_to_trips_high(tz, td); | |
| 1426 | + } | |
| 1427 | +} | |
| 1428 | + | |
| 1429 | +static int thermal_zone_init_governor(struct thermal_zone_device *tz) | |
| 1430 | +{ | |
| 1431 | + struct thermal_governor *governor; | |
| 1432 | + | |
| 1433 | + guard(mutex)(&thermal_governor_lock); | |
| 1434 | + | |
| 1435 | + if (tz->tzp) | |
| 1436 | + governor = __find_governor(tz->tzp->governor_name); | |
| 1437 | + else | |
| 1438 | + governor = def_governor; | |
| 1439 | + | |
| 1440 | + return thermal_set_governor(tz, governor); | |
| 1441 | +} | |
| 1442 | + | |
| 1443 | +static void thermal_zone_init_complete(struct thermal_zone_device *tz) | |
| 1444 | +{ | |
| 1445 | + struct thermal_cooling_device *cdev; | |
| 1446 | + | |
| 1447 | + guard(mutex)(&thermal_list_lock); | |
| 1448 | + | |
| 1449 | + list_add_tail(&tz->node, &thermal_tz_list); | |
| 1450 | + | |
| 1451 | + guard(thermal_zone)(tz); | |
| 1452 | + | |
| 1453 | + /* Bind cooling devices for this zone. */ | |
| 1454 | + list_for_each_entry(cdev, &thermal_cdev_list, node) | |
| 1455 | + __thermal_zone_cdev_bind(tz, cdev); | |
| 1456 | + | |
| 1457 | + tz->state &= ~TZ_STATE_FLAG_INIT; | |
| 1458 | + /* | |
| 1459 | + * If system suspend or resume is in progress at this point, the | |
| 1460 | + * new thermal zone needs to be marked as suspended because | |
| 1461 | + * thermal_pm_notify() has run already. | |
| 1462 | + */ | |
| 1463 | + if (thermal_pm_suspended) | |
| 1464 | + tz->state |= TZ_STATE_FLAG_SUSPENDED; | |
| 1465 | + | |
| 1466 | + __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | |
| 1467 | +} | |
| 1468 | + | |
| 1335 | 1469 | /** |
| 1336 | 1470 | * thermal_zone_device_register_with_trips() - register a new thermal zone device |
| 1337 | 1471 | * @type: the thermal zone device type |
| @@ -1366,12 +1500,10 @@ thermal_zone_device_register_with_trips(const char *type, · 28 unmodified lines | ||
| 1366 | 1500 | unsigned int polling_delay) |
| 1367 | 1501 | { |
| 1368 | 1502 | const struct thermal_trip *trip = trips; |
| 1369 | − struct thermal_cooling_device *cdev; | |
| 1370 | 1503 | struct thermal_zone_device *tz; |
| 1371 | 1504 | struct thermal_trip_desc *td; |
| 1372 | 1505 | int id; |
| 1373 | 1506 | int result; |
| 1374 | − struct thermal_governor *governor; | |
| 1375 | 1507 | |
| 1376 | 1508 | if (!type || strlen(type) == 0) { |
| 1377 | 1509 | pr_err("No thermal zone type defined\n"); |
| @@ -1415,8 +1547,10 @@ thermal_zone_device_register_with_trips(const char *type, · 37 unmodified lines | ||
| 1415 | 1547 | } |
| 1416 | 1548 | } |
| 1417 | 1549 | |
| 1418 | − INIT_LIST_HEAD(&tz->thermal_instances); | |
| 1419 | 1550 | INIT_LIST_HEAD(&tz->node); |
| 1551 | + INIT_LIST_HEAD(&tz->trips_high); | |
| 1552 | + INIT_LIST_HEAD(&tz->trips_reached); | |
| 1553 | + INIT_LIST_HEAD(&tz->trips_invalid); | |
| 1420 | 1554 | ida_init(&tz->ida); |
| 1421 | 1555 | mutex_init(&tz->lock); |
| 1422 | 1556 | init_completion(&tz->removal); |
| @@ -1439,51 +1573,41 @@ thermal_zone_device_register_with_trips(const char *type, · 16 unmodified lines | ||
| 1439 | 1573 | tz->num_trips = num_trips; |
| 1440 | 1574 | for_each_trip_desc(tz, td) { |
| 1441 | 1575 | td->trip = *trip++; |
| 1576 | + INIT_LIST_HEAD(&td->thermal_instances); | |
| 1577 | + INIT_LIST_HEAD(&td->list_node); | |
| 1442 | 1578 | /* |
| 1443 | 1579 | * Mark all thresholds as invalid to start with even though |
| 1444 | 1580 | * this only matters for the trips that start as invalid and |
| 1445 | 1581 | * become valid later. |
| 1446 | 1582 | */ |
| 1447 | − td->threshold = INT_MAX; | |
| 1583 | + move_to_trips_invalid(tz, td); | |
| 1448 | 1584 | } |
| 1449 | 1585 | |
| 1450 | 1586 | tz->polling_delay_jiffies = msecs_to_jiffies(polling_delay); |
| 1451 | 1587 | tz->passive_delay_jiffies = msecs_to_jiffies(passive_delay); |
| 1452 | 1588 | tz->recheck_delay_jiffies = THERMAL_RECHECK_DELAY; |
| 1453 | 1589 | |
| 1590 | + tz->state = TZ_STATE_FLAG_INIT; | |
| 1591 | + | |
| 1454 | 1592 | /* sys I/F */ |
| 1455 | 1593 | /* Add nodes that are always present via .groups */ |
| 1456 | 1594 | result = thermal_zone_create_device_groups(tz); |
| 1457 | 1595 | if (result) |
| 1458 | 1596 | goto remove_id; |
| 1459 | 1597 | |
| 1460 | − /* A new thermal zone needs to be updated anyway. */ | |
| 1461 | − atomic_set(&tz->need_update, 1); | |
| 1462 | − | |
| 1463 | 1598 | result = dev_set_name(&tz->device, "thermal_zone%d", tz->id); |
| 1464 | 1599 | if (result) { |
| 1465 | 1600 | thermal_zone_destroy_device_groups(tz); |
| 1466 | 1601 | goto remove_id; |
| 1467 | 1602 | } |
| 1603 | + thermal_zone_device_init(tz); | |
| 1468 | 1604 | result = device_register(&tz->device); |
| 1469 | 1605 | if (result) |
| 1470 | 1606 | goto release_device; |
| 1471 | 1607 | |
| 1472 | − /* Update 'this' zone's governor information */ | |
| 1473 | − mutex_lock(&thermal_governor_lock); | |
| 1474 | − | |
| 1475 | − if (tz->tzp) | |
| 1476 | − governor = __find_governor(tz->tzp->governor_name); | |
| 1477 | − else | |
| 1478 | − governor = def_governor; | |
| 1479 | − | |
| 1480 | − result = thermal_set_governor(tz, governor); | |
| 1481 | − if (result) { | |
| 1482 | − mutex_unlock(&thermal_governor_lock); | |
| 1608 | + result = thermal_zone_init_governor(tz); | |
| 1609 | + if (result) | |
| 1483 | 1610 | goto unregister; |
| 1484 | − } | |
| 1485 | − | |
| 1486 | − mutex_unlock(&thermal_governor_lock); | |
| 1487 | 1611 | |
| 1488 | 1612 | if (!tz->tzp || !tz->tzp->no_hwmon) { |
| 1489 | 1613 | result = thermal_add_hwmon_sysfs(tz); |
| @@ -1491,22 +1615,11 @@ thermal_zone_device_register_with_trips(const char *type, · 1 unmodified line | ||
| 1491 | 1615 | goto unregister; |
| 1492 | 1616 | } |
| 1493 | 1617 | |
| 1494 | − mutex_lock(&thermal_list_lock); | |
| 1495 | − | |
| 1496 | − mutex_lock(&tz->lock); | |
| 1497 | − list_add_tail(&tz->node, &thermal_tz_list); | |
| 1498 | − mutex_unlock(&tz->lock); | |
| 1499 | − | |
| 1500 | − /* Bind cooling devices for this zone */ | |
| 1501 | − list_for_each_entry(cdev, &thermal_cdev_list, node) | |
| 1502 | − thermal_zone_cdev_bind(tz, cdev); | |
| 1503 | − | |
| 1504 | − mutex_unlock(&thermal_list_lock); | |
| 1618 | + result = thermal_thresholds_init(tz); | |
| 1619 | + if (result) | |
| 1620 | + goto remove_hwmon; | |
| 1505 | 1621 | |
| 1506 | − thermal_zone_device_init(tz); | |
| 1507 | − /* Update the new thermal zone and mark it as already updated. */ | |
| 1508 | − if (atomic_cmpxchg(&tz->need_update, 1, 0)) | |
| 1509 | − thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | |
| 1622 | + thermal_zone_init_complete(tz); | |
| 1510 | 1623 | |
| 1511 | 1624 | thermal_notify_tz_create(tz); |
| 1512 | 1625 | |
| @@ -1514,6 +1627,8 @@ thermal_zone_device_register_with_trips(const char *type, · 1 unmodified line | ||
| 1514 | 1627 | |
| 1515 | 1628 | return tz; |
| 1516 | 1629 | |
| 1630 | +remove_hwmon: | |
| 1631 | + thermal_remove_hwmon_sysfs(tz); | |
| 1517 | 1632 | unregister: |
| 1518 | 1633 | device_del(&tz->device); |
| 1519 | 1634 | release_device: |
| @@ -1563,44 +1678,46 @@ struct device *thermal_zone_device(struct thermal_zone_device *tzd) · 43 unmodified lines | ||
| 1563 | 1678 | } |
| 1564 | 1679 | EXPORT_SYMBOL_GPL(thermal_zone_device); |
| 1565 | 1680 | |
| 1681 | +static bool thermal_zone_exit(struct thermal_zone_device *tz) | |
| 1682 | +{ | |
| 1683 | + struct thermal_cooling_device *cdev; | |
| 1684 | + | |
| 1685 | + guard(mutex)(&thermal_list_lock); | |
| 1686 | + | |
| 1687 | + if (list_empty(&tz->node)) | |
| 1688 | + return false; | |
| 1689 | + | |
| 1690 | + guard(thermal_zone)(tz); | |
| 1691 | + | |
| 1692 | + tz->state |= TZ_STATE_FLAG_EXIT; | |
| 1693 | + list_del_init(&tz->node); | |
| 1694 | + | |
| 1695 | + /* Unbind all cdevs associated with this thermal zone. */ | |
| 1696 | + list_for_each_entry(cdev, &thermal_cdev_list, node) | |
| 1697 | + __thermal_zone_cdev_unbind(tz, cdev); | |
| 1698 | + | |
| 1699 | + return true; | |
| 1700 | +} | |
| 1701 | + | |
| 1566 | 1702 | /** |
| 1567 | 1703 | * thermal_zone_device_unregister - removes the registered thermal zone device |
| 1568 | 1704 | * @tz: the thermal zone device to remove |
| 1569 | 1705 | */ |
| 1570 | 1706 | void thermal_zone_device_unregister(struct thermal_zone_device *tz) |
| 1571 | 1707 | { |
| 1572 | − struct thermal_cooling_device *cdev; | |
| 1573 | − struct thermal_zone_device *pos = NULL; | |
| 1574 | − | |
| 1575 | 1708 | if (!tz) |
| 1576 | 1709 | return; |
| 1577 | 1710 | |
| 1578 | 1711 | thermal_debug_tz_remove(tz); |
| 1579 | 1712 | |
| 1580 | − mutex_lock(&thermal_list_lock); | |
| 1581 | − list_for_each_entry(pos, &thermal_tz_list, node) | |
| 1582 | − if (pos == tz) | |
| 1583 | − break; | |
| 1584 | − if (pos != tz) { | |
| 1585 | − /* thermal zone device not found */ | |
| 1586 | − mutex_unlock(&thermal_list_lock); | |
| 1713 | + if (!thermal_zone_exit(tz)) | |
| 1587 | 1714 | return; |
| 1588 | − } | |
| 1589 | − | |
| 1590 | − mutex_lock(&tz->lock); | |
| 1591 | − list_del(&tz->node); | |
| 1592 | − mutex_unlock(&tz->lock); | |
| 1593 | − | |
| 1594 | − /* Unbind all cdevs associated with 'this' thermal zone */ | |
| 1595 | − list_for_each_entry(cdev, &thermal_cdev_list, node) | |
| 1596 | − thermal_zone_cdev_unbind(tz, cdev); | |
| 1597 | − | |
| 1598 | − mutex_unlock(&thermal_list_lock); | |
| 1599 | 1715 | |
| 1600 | 1716 | cancel_delayed_work_sync(&tz->poll_queue); |
| 1601 | 1717 | |
| 1602 | 1718 | thermal_set_governor(tz, NULL); |
| 1603 | 1719 | |
| 1720 | + thermal_thresholds_exit(tz); | |
| 1604 | 1721 | thermal_remove_hwmon_sysfs(tz); |
| 1605 | 1722 | ida_free(&thermal_tz_ida, tz->id); |
| 1606 | 1723 | ida_destroy(&tz->ida); |
| @@ -1632,24 +1749,23 @@ struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name) · 25 unmodified lines | ||
| 1632 | 1749 | unsigned int found = 0; |
| 1633 | 1750 | |
| 1634 | 1751 | if (!name) |
| 1635 | − goto exit; | |
| 1752 | + return ERR_PTR(-EINVAL); | |
| 1753 | + | |
| 1754 | + guard(mutex)(&thermal_list_lock); | |
| 1636 | 1755 | |
| 1637 | − mutex_lock(&thermal_list_lock); | |
| 1638 | 1756 | list_for_each_entry(pos, &thermal_tz_list, node) |
| 1639 | 1757 | if (!strncasecmp(name, pos->type, THERMAL_NAME_LENGTH)) { |
| 1640 | 1758 | found++; |
| 1641 | 1759 | ref = pos; |
| 1642 | 1760 | } |
| 1643 | − mutex_unlock(&thermal_list_lock); | |
| 1644 | 1761 | |
| 1645 | − /* nothing has been found, thus an error code for it */ | |
| 1646 | − if (found == 0) | |
| 1647 | − ref = ERR_PTR(-ENODEV); | |
| 1648 | − else if (found > 1) | |
| 1649 | − /* Success only when an unique zone is found */ | |
| 1650 | − ref = ERR_PTR(-EEXIST); | |
| 1762 | + if (!found) | |
| 1763 | + return ERR_PTR(-ENODEV); | |
| 1764 | + | |
| 1765 | + /* Success only when one zone is found. */ | |
| 1766 | + if (found > 1) | |
| 1767 | + return ERR_PTR(-EEXIST); | |
| 1651 | 1768 | |
| 1652 | −exit: | |
| 1653 | 1769 | return ref; |
| 1654 | 1770 | } |
| 1655 | 1771 | EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name); |
| @@ -1660,9 +1776,9 @@ static void thermal_zone_device_resume(struct work_struct *work) · 4 unmodified lines | ||
| 1660 | 1776 | |
| 1661 | 1777 | tz = container_of(work, struct thermal_zone_device, poll_queue.work); |
| 1662 | 1778 | |
| 1663 | − mutex_lock(&tz->lock); | |
| 1779 | + guard(thermal_zone)(tz); | |
| 1664 | 1780 | |
| 1665 | − tz->suspended = false; | |
| 1781 | + tz->state &= ~(TZ_STATE_FLAG_SUSPENDED | TZ_STATE_FLAG_RESUMING); | |
| 1666 | 1782 | |
| 1667 | 1783 | thermal_debug_tz_resume(tz); |
| 1668 | 1784 | thermal_zone_device_init(tz); |
| @@ -1670,74 +1786,81 @@ static void thermal_zone_device_resume(struct work_struct *work) · 1 unmodified line | ||
| 1670 | 1786 | __thermal_zone_device_update(tz, THERMAL_TZ_RESUME); |
| 1671 | 1787 | |
| 1672 | 1788 | complete(&tz->resume); |
| 1673 | − tz->resuming = false; | |
| 1674 | − | |
| 1675 | − mutex_unlock(&tz->lock); | |
| 1676 | 1789 | } |
| 1677 | 1790 | |
| 1678 | −static int thermal_pm_notify(struct notifier_block *nb, | |
| 1679 | − unsigned long mode, void *_unused) | |
| 1791 | +static void thermal_zone_pm_prepare(struct thermal_zone_device *tz) | |
| 1680 | 1792 | { |
| 1681 | − struct thermal_zone_device *tz; | |
| 1793 | + guard(thermal_zone)(tz); | |
| 1682 | 1794 | |
| 1683 | − switch (mode) { | |
| 1684 | − case PM_HIBERNATION_PREPARE: | |
| 1685 | − case PM_RESTORE_PREPARE: | |
| 1686 | − case PM_SUSPEND_PREPARE: | |
| 1687 | − mutex_lock(&thermal_list_lock); | |
| 1795 | + if (tz->state & TZ_STATE_FLAG_RESUMING) { | |
| 1796 | + /* | |
| 1797 | + * thermal_zone_device_resume() queued up for this zone has not | |
| 1798 | + * acquired the lock yet, so release it to let the function run | |
| 1799 | + * and wait util it has done the work. | |
| 1800 | + */ | |
| 1801 | + scoped_guard(thermal_zone_reverse, tz) { | |
| 1802 | + wait_for_completion(&tz->resume); | |
| 1803 | + } | |
| 1804 | + } | |
| 1688 | 1805 | |
| 1689 | − list_for_each_entry(tz, &thermal_tz_list, node) { | |
| 1690 | − mutex_lock(&tz->lock); | |
| 1806 | + tz->state |= TZ_STATE_FLAG_SUSPENDED; | |
| 1807 | +} | |
| 1691 | 1808 | |
| 1692 | − if (tz->resuming) { | |
| 1693 | − /* | |
| 1694 | − * thermal_zone_device_resume() queued up for | |
| 1695 | − * this zone has not acquired the lock yet, so | |
| 1696 | − * release it to let the function run and wait | |
| 1697 | − * util it has done the work. | |
| 1698 | − */ | |
| 1699 | − mutex_unlock(&tz->lock); | |
| 1809 | +static void thermal_pm_notify_prepare(void) | |
| 1810 | +{ | |
| 1811 | + struct thermal_zone_device *tz; | |
| 1700 | 1812 | |
| 1701 | − wait_for_completion(&tz->resume); | |
| 1813 | + guard(mutex)(&thermal_list_lock); | |
| 1702 | 1814 | |
| 1703 | − mutex_lock(&tz->lock); | |
| 1704 | − } | |
| 1815 | + thermal_pm_suspended = true; | |
| 1705 | 1816 | |
| 1706 | − tz->suspended = true; | |
| 1817 | + list_for_each_entry(tz, &thermal_tz_list, node) | |
| 1818 | + thermal_zone_pm_prepare(tz); | |
| 1819 | +} | |
| 1707 | 1820 | |
| 1708 | − mutex_unlock(&tz->lock); | |
| 1709 | − } | |
| 1821 | +static void thermal_zone_pm_complete(struct thermal_zone_device *tz) | |
| 1822 | +{ | |
| 1823 | + guard(thermal_zone)(tz); | |
| 1710 | 1824 | |
| 1711 | − mutex_unlock(&thermal_list_lock); | |
| 1712 | − break; | |
| 1713 | − case PM_POST_HIBERNATION: | |
| 1714 | − case PM_POST_RESTORE: | |
| 1715 | − case PM_POST_SUSPEND: | |
| 1716 | − mutex_lock(&thermal_list_lock); | |
| 1825 | + cancel_delayed_work(&tz->poll_queue); | |
| 1717 | 1826 | |
| 1718 | − list_for_each_entry(tz, &thermal_tz_list, node) { | |
| 1719 | − mutex_lock(&tz->lock); | |
| 1827 | + reinit_completion(&tz->resume); | |
| 1828 | + tz->state |= TZ_STATE_FLAG_RESUMING; | |
| 1829 | + | |
| 1830 | + /* | |
| 1831 | + * Replace the work function with the resume one, which will restore the | |
| 1832 | + * original work function and schedule the polling work if needed. | |
| 1833 | + */ | |
| 1834 | + INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_resume); | |
| 1835 | + /* Queue up the work without a delay. */ | |
| 1836 | + mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, 0); | |
| 1837 | +} | |
| 1720 | 1838 | |
| 1721 | − cancel_delayed_work(&tz->poll_queue); | |
| 1839 | +static void thermal_pm_notify_complete(void) | |
| 1840 | +{ | |
| 1841 | + struct thermal_zone_device *tz; | |
| 1722 | 1842 | |
| 1723 | − reinit_completion(&tz->resume); | |
| 1724 | − tz->resuming = true; | |
| 1843 | + guard(mutex)(&thermal_list_lock); | |
| 1725 | 1844 | |
| 1726 | − /* | |
| 1727 | − * Replace the work function with the resume one, which | |
| 1728 | − * will restore the original work function and schedule | |
| 1729 | − * the polling work if needed. | |
| 1730 | − */ | |
| 1731 | − INIT_DELAYED_WORK(&tz->poll_queue, | |
| 1732 | − thermal_zone_device_resume); | |
| 1733 | − /* Queue up the work without a delay. */ | |
| 1734 | − mod_delayed_work(system_freezable_power_efficient_wq, | |
| 1735 | − &tz->poll_queue, 0); | |
| 1845 | + thermal_pm_suspended = false; | |
| 1736 | 1846 | |
| 1737 | − mutex_unlock(&tz->lock); | |
| 1738 | − } | |
| 1847 | + list_for_each_entry(tz, &thermal_tz_list, node) | |
| 1848 | + thermal_zone_pm_complete(tz); | |
| 1849 | +} | |
| 1739 | 1850 | |
| 1740 | − mutex_unlock(&thermal_list_lock); | |
| 1851 | +static int thermal_pm_notify(struct notifier_block *nb, | |
| 1852 | + unsigned long mode, void *_unused) | |
| 1853 | +{ | |
| 1854 | + switch (mode) { | |
| 1855 | + case PM_HIBERNATION_PREPARE: | |
| 1856 | + case PM_RESTORE_PREPARE: | |
| 1857 | + case PM_SUSPEND_PREPARE: | |
| 1858 | + thermal_pm_notify_prepare(); | |
| 1859 | + break; | |
| 1860 | + case PM_POST_HIBERNATION: | |
| 1861 | + case PM_POST_RESTORE: | |
| 1862 | + case PM_POST_SUSPEND: | |
| 1863 | + thermal_pm_notify_complete(); | |
| 1741 | 1864 | break; |
| 1742 | 1865 | default: |
| 1743 | 1866 | break; |
| @@ -9,10 +9,12 @@ · 8 unmodified lines | ||
| 9 | 9 | #ifndef __THERMAL_CORE_H__ |
| 10 | 10 | #define __THERMAL_CORE_H__ |
| 11 | 11 | |
| 12 | +#include <linux/cleanup.h> | |
| 12 | 13 | #include <linux/device.h> |
| 13 | 14 | #include <linux/thermal.h> |
| 14 | 15 | |
| 15 | 16 | #include "thermal_netlink.h" |
| 17 | +#include "thermal_thresholds.h" | |
| 16 | 18 | #include "thermal_debugfs.h" |
| 17 | 19 | |
| 18 | 20 | struct thermal_attr { |
| @@ -29,8 +31,8 @@ struct thermal_trip_attrs { · 10 unmodified lines | ||
| 29 | 31 | struct thermal_trip_desc { |
| 30 | 32 | struct thermal_trip trip; |
| 31 | 33 | struct thermal_trip_attrs trip_attrs; |
| 32 | − struct list_head notify_list_node; | |
| 33 | − int notify_temp; | |
| 34 | + struct list_head list_node; | |
| 35 | + struct list_head thermal_instances; | |
| 34 | 36 | int threshold; |
| 35 | 37 | }; |
| 36 | 38 | |
| @@ -61,6 +63,13 @@ struct thermal_governor { · 24 unmodified lines | ||
| 61 | 63 | struct list_head governor_list; |
| 62 | 64 | }; |
| 63 | 65 | |
| 66 | +#define TZ_STATE_FLAG_SUSPENDED BIT(0) | |
| 67 | +#define TZ_STATE_FLAG_RESUMING BIT(1) | |
| 68 | +#define TZ_STATE_FLAG_INIT BIT(2) | |
| 69 | +#define TZ_STATE_FLAG_EXIT BIT(3) | |
| 70 | + | |
| 71 | +#define TZ_STATE_READY 0 | |
| 72 | + | |
| 64 | 73 | /** |
| 65 | 74 | * struct thermal_zone_device - structure for a thermal zone |
| 66 | 75 | * @id: unique id number for each thermal zone |
| @@ -68,6 +77,9 @@ struct thermal_governor { · 1 unmodified line | ||
| 68 | 77 | * @device: &struct device for this thermal zone |
| 69 | 78 | * @removal: removal completion |
| 70 | 79 | * @resume: resume completion |
| 80 | + * @trips_high: trips above the current zone temperature | |
| 81 | + * @trips_reached: trips below or at the current zone temperature | |
| 82 | + * @trips_invalid: trips with invalid temperature | |
| 71 | 83 | * @mode: current mode of this thermal zone |
| 72 | 84 | * @devdata: private pointer for device private data |
| 73 | 85 | * @num_trips: number of trip points the thermal zone supports |
| @@ -88,20 +100,17 @@ struct thermal_governor { · 14 unmodified lines | ||
| 88 | 100 | trip point. |
| 89 | 101 | * @prev_high_trip: the above current temperature if you've crossed a |
| 90 | 102 | passive trip point. |
| 91 | − * @need_update: if equals 1, thermal_zone_device_update needs to be invoked. | |
| 92 | 103 | * @ops: operations this &thermal_zone_device supports |
| 93 | 104 | * @tzp: thermal zone parameters |
| 94 | 105 | * @governor: pointer to the governor for this thermal zone |
| 95 | 106 | * @governor_data: private pointer for governor data |
| 96 | − * @thermal_instances: list of &struct thermal_instance of this thermal zone | |
| 97 | 107 | * @ida: &struct ida to generate unique id for this zone's cooling |
| 98 | 108 | * devices |
| 99 | 109 | * @lock: lock to protect thermal_instances list |
| 100 | 110 | * @node: node in thermal_tz_list (in thermal_core.c) |
| 101 | 111 | * @poll_queue: delayed work for polling |
| 102 | 112 | * @notify_event: Last notification event |
| 103 | − * @suspended: thermal zone suspend indicator | |
| 104 | − * @resuming: indicates whether or not thermal zone resume is in progress | |
| 113 | + * @state: current state of the thermal zone | |
| 105 | 114 | * @trips: array of struct thermal_trip objects |
| 106 | 115 | */ |
| 107 | 116 | struct thermal_zone_device { |
| @@ -111,6 +120,9 @@ struct thermal_zone_device { · 3 unmodified lines | ||
| 111 | 120 | struct completion removal; |
| 112 | 121 | struct completion resume; |
| 113 | 122 | struct attribute_group trips_attribute_group; |
| 123 | + struct list_head trips_high; | |
| 124 | + struct list_head trips_reached; | |
| 125 | + struct list_head trips_invalid; | |
| 114 | 126 | enum thermal_device_mode mode; |
| 115 | 127 | void *devdata; |
| 116 | 128 | int num_trips; |
| @@ -123,25 +135,29 @@ struct thermal_zone_device { · 6 unmodified lines | ||
| 123 | 135 | int passive; |
| 124 | 136 | int prev_low_trip; |
| 125 | 137 | int prev_high_trip; |
| 126 | − atomic_t need_update; | |
| 127 | 138 | struct thermal_zone_device_ops ops; |
| 128 | 139 | struct thermal_zone_params *tzp; |
| 129 | 140 | struct thermal_governor *governor; |
| 130 | 141 | void *governor_data; |
| 131 | − struct list_head thermal_instances; | |
| 132 | 142 | struct ida ida; |
| 133 | 143 | struct mutex lock; |
| 134 | 144 | struct list_head node; |
| 135 | 145 | struct delayed_work poll_queue; |
| 136 | 146 | enum thermal_notify_event notify_event; |
| 137 | − bool suspended; | |
| 138 | − bool resuming; | |
| 147 | + u8 state; | |
| 139 | 148 | #ifdef CONFIG_THERMAL_DEBUGFS |
| 140 | 149 | struct thermal_debugfs *debugfs; |
| 141 | 150 | #endif |
| 151 | + struct list_head user_thresholds; | |
| 142 | 152 | struct thermal_trip_desc trips[] __counted_by(num_trips); |
| 143 | 153 | }; |
| 144 | 154 | |
| 155 | +DEFINE_GUARD(thermal_zone, struct thermal_zone_device *, mutex_lock(&_T->lock), | |
| 156 | + mutex_unlock(&_T->lock)) | |
| 157 | + | |
| 158 | +DEFINE_GUARD(thermal_zone_reverse, struct thermal_zone_device *, | |
| 159 | + mutex_unlock(&_T->lock), mutex_lock(&_T->lock)) | |
| 160 | + | |
| 145 | 161 | /* Initial thermal zone temperature. */ |
| 146 | 162 | #define THERMAL_TEMP_INIT INT_MIN |
| 147 | 163 | |
| @@ -204,6 +220,7 @@ static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev) · 56 unmodified lines | ||
| 204 | 220 | } |
| 205 | 221 | |
| 206 | 222 | void thermal_cdev_update(struct thermal_cooling_device *); |
| 223 | +void thermal_cdev_update_nocheck(struct thermal_cooling_device *cdev); | |
| 207 | 224 | void __thermal_cdev_update(struct thermal_cooling_device *cdev); |
| 208 | 225 | |
| 209 | 226 | int get_tz_trend(struct thermal_zone_device *tz, const struct thermal_trip *trip); |
| @@ -226,7 +243,7 @@ struct thermal_instance { · 16 unmodified lines | ||
| 226 | 243 | struct device_attribute attr; |
| 227 | 244 | char weight_attr_name[THERMAL_NAME_LENGTH]; |
| 228 | 245 | struct device_attribute weight_attr; |
| 229 | − struct list_head tz_node; /* node in tz->thermal_instances */ | |
| 246 | + struct list_head trip_node; /* node in trip->thermal_instances */ | |
| 230 | 247 | struct list_head cdev_node; /* node in cdev->thermal_instances */ |
| 231 | 248 | unsigned int weight; /* The weight of the cooling device */ |
| 232 | 249 | bool upper_no_limit; |
| @@ -261,8 +278,6 @@ void thermal_zone_set_trips(struct thermal_zone_device *tz, int low, int high); · 28 unmodified lines | ||
| 261 | 278 | int thermal_zone_trip_id(const struct thermal_zone_device *tz, |
| 262 | 279 | const struct thermal_trip *trip); |
| 263 | 280 | int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp); |
| 264 | −void thermal_zone_trip_down(struct thermal_zone_device *tz, | |
| 265 | − const struct thermal_trip *trip); | |
| 266 | 281 | void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz, |
| 267 | 282 | struct thermal_trip *trip, int hyst); |
| 268 | 283 | |
| @@ -516,6 +516,19 @@ void thermal_debug_cdev_add(struct thermal_cooling_device *cdev, int state) · 515 unmodified lines | ||
| 516 | 516 | cdev->debugfs = thermal_dbg; |
| 517 | 517 | } |
| 518 | 518 | |
| 519 | +static struct thermal_debugfs *thermal_debug_cdev_clear(struct thermal_cooling_device *cdev) | |
| 520 | +{ | |
| 521 | + struct thermal_debugfs *thermal_dbg; | |
| 522 | + | |
| 523 | + guard(cooling_dev)(cdev); | |
| 524 | + | |
| 525 | + thermal_dbg = cdev->debugfs; | |
| 526 | + if (thermal_dbg) | |
| 527 | + cdev->debugfs = NULL; | |
| 528 | + | |
| 529 | + return thermal_dbg; | |
| 530 | +} | |
| 531 | + | |
| 519 | 532 | /** |
| 520 | 533 | * thermal_debug_cdev_remove - Remove a cooling device debugfs entry |
| 521 | 534 | * |
| @@ -527,17 +540,9 @@ void thermal_debug_cdev_remove(struct thermal_cooling_device *cdev) · 5 unmodified lines | ||
| 527 | 540 | { |
| 528 | 541 | struct thermal_debugfs *thermal_dbg; |
| 529 | 542 | |
| 530 | − mutex_lock(&cdev->lock); | |
| 531 | − | |
| 532 | − thermal_dbg = cdev->debugfs; | |
| 533 | − if (!thermal_dbg) { | |
| 534 | − mutex_unlock(&cdev->lock); | |
| 543 | + thermal_dbg = thermal_debug_cdev_clear(cdev); | |
| 544 | + if (!thermal_dbg) | |
| 535 | 545 | return; |
| 536 | − } | |
| 537 | − | |
| 538 | − cdev->debugfs = NULL; | |
| 539 | − | |
| 540 | − mutex_unlock(&cdev->lock); | |
| 541 | 546 | |
| 542 | 547 | mutex_lock(&thermal_dbg->lock); |
| 543 | 548 | |
| @@ -885,6 +890,19 @@ void thermal_debug_tz_add(struct thermal_zone_device *tz) · 341 unmodified lines | ||
| 885 | 890 | tz->debugfs = thermal_dbg; |
| 886 | 891 | } |
| 887 | 892 | |
| 893 | +static struct thermal_debugfs *thermal_debug_tz_clear(struct thermal_zone_device *tz) | |
| 894 | +{ | |
| 895 | + struct thermal_debugfs *thermal_dbg; | |
| 896 | + | |
| 897 | + guard(thermal_zone)(tz); | |
| 898 | + | |
| 899 | + thermal_dbg = tz->debugfs; | |
| 900 | + if (thermal_dbg) | |
| 901 | + tz->debugfs = NULL; | |
| 902 | + | |
| 903 | + return thermal_dbg; | |
| 904 | +} | |
| 905 | + | |
| 888 | 906 | void thermal_debug_tz_remove(struct thermal_zone_device *tz) |
| 889 | 907 | { |
| 890 | 908 | struct thermal_debugfs *thermal_dbg; |
| @@ -892,17 +910,9 @@ void thermal_debug_tz_remove(struct thermal_zone_device *tz) · 1 unmodified line | ||
| 892 | 910 | struct tz_debugfs *tz_dbg; |
| 893 | 911 | int *trips_crossed; |
| 894 | 912 | |
| 895 | − mutex_lock(&tz->lock); | |
| 896 | − | |
| 897 | − thermal_dbg = tz->debugfs; | |
| 898 | − if (!thermal_dbg) { | |
| 899 | − mutex_unlock(&tz->lock); | |
| 913 | + thermal_dbg = thermal_debug_tz_clear(tz); | |
| 914 | + if (!thermal_dbg) | |
| 900 | 915 | return; |
| 901 | − } | |
| 902 | − | |
| 903 | − tz->debugfs = NULL; | |
| 904 | − | |
| 905 | − mutex_unlock(&tz->lock); | |
| 906 | 916 | |
| 907 | 917 | tz_dbg = &thermal_dbg->tz_dbg; |
| 908 | 918 | |
| @@ -43,10 +43,11 @@ static bool thermal_instance_present(struct thermal_zone_device *tz, · 42 unmodified lines | ||
| 43 | 43 | struct thermal_cooling_device *cdev, |
| 44 | 44 | const struct thermal_trip *trip) |
| 45 | 45 | { |
| 46 | + const struct thermal_trip_desc *td = trip_to_trip_desc(trip); | |
| 46 | 47 | struct thermal_instance *ti; |
| 47 | 48 | |
| 48 | − list_for_each_entry(ti, &tz->thermal_instances, tz_node) { | |
| 49 | − if (ti->trip == trip && ti->cdev == cdev) | |
| 49 | + list_for_each_entry(ti, &td->thermal_instances, trip_node) { | |
| 50 | + if (ti->cdev == cdev) | |
| 50 | 51 | return true; |
| 51 | 52 | } |
| 52 | 53 | |
| @@ -57,17 +58,10 @@ bool thermal_trip_is_bound_to_cdev(struct thermal_zone_device *tz, · 4 unmodified lines | ||
| 57 | 58 | const struct thermal_trip *trip, |
| 58 | 59 | struct thermal_cooling_device *cdev) |
| 59 | 60 | { |
| 60 | − bool ret; | |
| 61 | + guard(thermal_zone)(tz); | |
| 62 | + guard(cooling_dev)(cdev); | |
| 61 | 63 | |
| 62 | − mutex_lock(&tz->lock); | |
| 63 | − mutex_lock(&cdev->lock); | |
| 64 | − | |
| 65 | − ret = thermal_instance_present(tz, cdev, trip); | |
| 66 | − | |
| 67 | − mutex_unlock(&cdev->lock); | |
| 68 | − mutex_unlock(&tz->lock); | |
| 69 | − | |
| 70 | − return ret; | |
| 64 | + return thermal_instance_present(tz, cdev, trip); | |
| 71 | 65 | } |
| 72 | 66 | EXPORT_SYMBOL_GPL(thermal_trip_is_bound_to_cdev); |
| 73 | 67 | |
| @@ -137,19 +131,14 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) · 63 unmodified lines | ||
| 137 | 131 | if (IS_ERR_OR_NULL(tz)) |
| 138 | 132 | return -EINVAL; |
| 139 | 133 | |
| 140 | − mutex_lock(&tz->lock); | |
| 134 | + guard(thermal_zone)(tz); | |
| 141 | 135 | |
| 142 | − if (!tz->ops.get_temp) { | |
| 143 | − ret = -EINVAL; | |
| 144 | − goto unlock; | |
| 145 | − } | |
| 136 | + if (!tz->ops.get_temp) | |
| 137 | + return -EINVAL; | |
| 146 | 138 | |
| 147 | 139 | ret = __thermal_zone_get_temp(tz, temp); |
| 148 | 140 | if (!ret && *temp <= THERMAL_TEMP_INVALID) |
| 149 | − ret = -ENODATA; | |
| 150 | − | |
| 151 | −unlock: | |
| 152 | − mutex_unlock(&tz->lock); | |
| 141 | + return -ENODATA; | |
| 153 | 142 | |
| 154 | 143 | return ret; |
| 155 | 144 | } |
| @@ -201,12 +190,23 @@ void __thermal_cdev_update(struct thermal_cooling_device *cdev) · 45 unmodified lines | ||
| 201 | 190 | */ |
| 202 | 191 | void thermal_cdev_update(struct thermal_cooling_device *cdev) |
| 203 | 192 | { |
| 204 | − mutex_lock(&cdev->lock); | |
| 193 | + guard(cooling_dev)(cdev); | |
| 194 | + | |
| 205 | 195 | if (!cdev->updated) { |
| 206 | 196 | __thermal_cdev_update(cdev); |
| 207 | 197 | cdev->updated = true; |
| 208 | 198 | } |
| 209 | − mutex_unlock(&cdev->lock); | |
| 199 | +} | |
| 200 | + | |
| 201 | +/** | |
| 202 | + * thermal_cdev_update_nocheck() - Unconditionally update cooling device state | |
| 203 | + * @cdev: Target cooling device. | |
| 204 | + */ | |
| 205 | +void thermal_cdev_update_nocheck(struct thermal_cooling_device *cdev) | |
| 206 | +{ | |
| 207 | + guard(cooling_dev)(cdev); | |
| 208 | + | |
| 209 | + __thermal_cdev_update(cdev); | |
| 210 | 210 | } |
| 211 | 211 | |
| 212 | 212 | /** |
| @@ -78,12 +78,9 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf) · 77 unmodified lines | ||
| 78 | 78 | int temperature; |
| 79 | 79 | int ret; |
| 80 | 80 | |
| 81 | − mutex_lock(&tz->lock); | |
| 81 | + guard(thermal_zone)(tz); | |
| 82 | 82 | |
| 83 | 83 | ret = tz->ops.get_crit_temp(tz, &temperature); |
| 84 | − | |
| 85 | − mutex_unlock(&tz->lock); | |
| 86 | − | |
| 87 | 84 | if (ret) |
| 88 | 85 | return ret; |
| 89 | 86 | |
| @@ -9,6 +9,7 @@ · 8 unmodified lines | ||
| 9 | 9 | #include <linux/module.h> |
| 10 | 10 | #include <linux/notifier.h> |
| 11 | 11 | #include <linux/kernel.h> |
| 12 | +#include <net/sock.h> | |
| 12 | 13 | #include <net/genetlink.h> |
| 13 | 14 | #include <uapi/linux/thermal.h> |
| 14 | 15 | |
| @@ -49,6 +50,11 @@ static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = · 34 unmodified lines | ||
| 49 | 50 | [THERMAL_GENL_ATTR_CPU_CAPABILITY_ID] = { .type = NLA_U32 }, |
| 50 | 51 | [THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE] = { .type = NLA_U32 }, |
| 51 | 52 | [THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY] = { .type = NLA_U32 }, |
| 53 | + | |
| 54 | + /* Thresholds */ | |
| 55 | + [THERMAL_GENL_ATTR_THRESHOLD] = { .type = NLA_NESTED }, | |
| 56 | + [THERMAL_GENL_ATTR_THRESHOLD_TEMP] = { .type = NLA_U32 }, | |
| 57 | + [THERMAL_GENL_ATTR_THRESHOLD_DIRECTION] = { .type = NLA_U32 }, | |
| 52 | 58 | }; |
| 53 | 59 | |
| 54 | 60 | struct param { |
| @@ -62,6 +68,8 @@ struct param { · 7 unmodified lines | ||
| 62 | 68 | int trip_type; |
| 63 | 69 | int trip_hyst; |
| 64 | 70 | int temp; |
| 71 | + int prev_temp; | |
| 72 | + int direction; | |
| 65 | 73 | int cdev_state; |
| 66 | 74 | int cdev_max_state; |
| 67 | 75 | struct thermal_genl_cpu_caps *cpu_capabilities; |
| @@ -234,6 +242,34 @@ out_cancel_nest: · 166 unmodified lines | ||
| 234 | 242 | return -EMSGSIZE; |
| 235 | 243 | } |
| 236 | 244 | |
| 245 | +static int thermal_genl_event_threshold_add(struct param *p) | |
| 246 | +{ | |
| 247 | + if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) || | |
| 248 | + nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, p->temp) || | |
| 249 | + nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, p->direction)) | |
| 250 | + return -EMSGSIZE; | |
| 251 | + | |
| 252 | + return 0; | |
| 253 | +} | |
| 254 | + | |
| 255 | +static int thermal_genl_event_threshold_flush(struct param *p) | |
| 256 | +{ | |
| 257 | + if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id)) | |
| 258 | + return -EMSGSIZE; | |
| 259 | + | |
| 260 | + return 0; | |
| 261 | +} | |
| 262 | + | |
| 263 | +static int thermal_genl_event_threshold_up(struct param *p) | |
| 264 | +{ | |
| 265 | + if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) || | |
| 266 | + nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_PREV_TEMP, p->prev_temp) || | |
| 267 | + nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TEMP, p->temp)) | |
| 268 | + return -EMSGSIZE; | |
| 269 | + | |
| 270 | + return 0; | |
| 271 | +} | |
| 272 | + | |
| 237 | 273 | int thermal_genl_event_tz_delete(struct param *p) |
| 238 | 274 | __attribute__((alias("thermal_genl_event_tz"))); |
| 239 | 275 | |
| @@ -246,6 +282,12 @@ int thermal_genl_event_tz_disable(struct param *p) · 6 unmodified lines | ||
| 246 | 282 | int thermal_genl_event_tz_trip_down(struct param *p) |
| 247 | 283 | __attribute__((alias("thermal_genl_event_tz_trip_up"))); |
| 248 | 284 | |
| 285 | +int thermal_genl_event_threshold_delete(struct param *p) | |
| 286 | + __attribute__((alias("thermal_genl_event_threshold_add"))); | |
| 287 | + | |
| 288 | +int thermal_genl_event_threshold_down(struct param *p) | |
| 289 | + __attribute__((alias("thermal_genl_event_threshold_up"))); | |
| 290 | + | |
| 249 | 291 | static cb_t event_cb[] = { |
| 250 | 292 | [THERMAL_GENL_EVENT_TZ_CREATE] = thermal_genl_event_tz_create, |
| 251 | 293 | [THERMAL_GENL_EVENT_TZ_DELETE] = thermal_genl_event_tz_delete, |
| @@ -259,6 +301,11 @@ static cb_t event_cb[] = { · 7 unmodified lines | ||
| 259 | 301 | [THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = thermal_genl_event_cdev_state_update, |
| 260 | 302 | [THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = thermal_genl_event_gov_change, |
| 261 | 303 | [THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE] = thermal_genl_event_cpu_capability_change, |
| 304 | + [THERMAL_GENL_EVENT_THRESHOLD_ADD] = thermal_genl_event_threshold_add, | |
| 305 | + [THERMAL_GENL_EVENT_THRESHOLD_DELETE] = thermal_genl_event_threshold_delete, | |
| 306 | + [THERMAL_GENL_EVENT_THRESHOLD_FLUSH] = thermal_genl_event_threshold_flush, | |
| 307 | + [THERMAL_GENL_EVENT_THRESHOLD_DOWN] = thermal_genl_event_threshold_down, | |
| 308 | + [THERMAL_GENL_EVENT_THRESHOLD_UP] = thermal_genl_event_threshold_up, | |
| 262 | 309 | }; |
| 263 | 310 | |
| 264 | 311 | /* |
| @@ -401,6 +448,43 @@ int thermal_genl_cpu_capability_event(int count, · 136 unmodified lines | ||
| 401 | 448 | } |
| 402 | 449 | EXPORT_SYMBOL_GPL(thermal_genl_cpu_capability_event); |
| 403 | 450 | |
| 451 | +int thermal_notify_threshold_add(const struct thermal_zone_device *tz, | |
| 452 | + int temperature, int direction) | |
| 453 | +{ | |
| 454 | + struct param p = { .tz_id = tz->id, .temp = temperature, .direction = direction }; | |
| 455 | + | |
| 456 | + return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_ADD, &p); | |
| 457 | +} | |
| 458 | + | |
| 459 | +int thermal_notify_threshold_delete(const struct thermal_zone_device *tz, | |
| 460 | + int temperature, int direction) | |
| 461 | +{ | |
| 462 | + struct param p = { .tz_id = tz->id, .temp = temperature, .direction = direction }; | |
| 463 | + | |
| 464 | + return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_DELETE, &p); | |
| 465 | +} | |
| 466 | + | |
| 467 | +int thermal_notify_threshold_flush(const struct thermal_zone_device *tz) | |
| 468 | +{ | |
| 469 | + struct param p = { .tz_id = tz->id }; | |
| 470 | + | |
| 471 | + return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_FLUSH, &p); | |
| 472 | +} | |
| 473 | + | |
| 474 | +int thermal_notify_threshold_down(const struct thermal_zone_device *tz) | |
| 475 | +{ | |
| 476 | + struct param p = { .tz_id = tz->id, .temp = tz->temperature, .prev_temp = tz->last_temperature }; | |
| 477 | + | |
| 478 | + return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_DOWN, &p); | |
| 479 | +} | |
| 480 | + | |
| 481 | +int thermal_notify_threshold_up(const struct thermal_zone_device *tz) | |
| 482 | +{ | |
| 483 | + struct param p = { .tz_id = tz->id, .temp = tz->temperature, .prev_temp = tz->last_temperature }; | |
| 484 | + | |
| 485 | + return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_UP, &p); | |
| 486 | +} | |
| 487 | + | |
| 404 | 488 | /*************************** Command encoding ********************************/ |
| 405 | 489 | |
| 406 | 490 | static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz, |
| @@ -459,7 +543,7 @@ static int thermal_genl_cmd_tz_get_trip(struct param *p) · 52 unmodified lines | ||
| 459 | 543 | if (!start_trip) |
| 460 | 544 | return -EMSGSIZE; |
| 461 | 545 | |
| 462 | − mutex_lock(&tz->lock); | |
| 546 | + guard(thermal_zone)(tz); | |
| 463 | 547 | |
| 464 | 548 | for_each_trip_desc(tz, td) { |
| 465 | 549 | const struct thermal_trip *trip = &td->trip; |
| @@ -469,19 +553,12 @@ static int thermal_genl_cmd_tz_get_trip(struct param *p) · 3 unmodified lines | ||
| 469 | 553 | nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, trip->type) || |
| 470 | 554 | nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, trip->temperature) || |
| 471 | 555 | nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, trip->hysteresis)) |
| 472 | − goto out_cancel_nest; | |
| 556 | + return -EMSGSIZE; | |
| 473 | 557 | } |
| 474 | 558 | |
| 475 | − mutex_unlock(&tz->lock); | |
| 476 | − | |
| 477 | 559 | nla_nest_end(msg, start_trip); |
| 478 | 560 | |
| 479 | 561 | return 0; |
| 480 | − | |
| 481 | −out_cancel_nest: | |
| 482 | − mutex_unlock(&tz->lock); | |
| 483 | − | |
| 484 | − return -EMSGSIZE; | |
| 485 | 562 | } |
| 486 | 563 | |
| 487 | 564 | static int thermal_genl_cmd_tz_get_temp(struct param *p) |
| @@ -512,7 +589,7 @@ static int thermal_genl_cmd_tz_get_temp(struct param *p) · 24 unmodified lines | ||
| 512 | 589 | static int thermal_genl_cmd_tz_get_gov(struct param *p) |
| 513 | 590 | { |
| 514 | 591 | struct sk_buff *msg = p->msg; |
| 515 | − int id, ret = 0; | |
| 592 | + int id; | |
| 516 | 593 | |
| 517 | 594 | if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID]) |
| 518 | 595 | return -EINVAL; |
| @@ -523,16 +600,14 @@ static int thermal_genl_cmd_tz_get_gov(struct param *p) · 4 unmodified lines | ||
| 523 | 600 | if (!tz) |
| 524 | 601 | return -EINVAL; |
| 525 | 602 | |
| 526 | − mutex_lock(&tz->lock); | |
| 603 | + guard(thermal_zone)(tz); | |
| 527 | 604 | |
| 528 | 605 | if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) || |
| 529 | 606 | nla_put_string(msg, THERMAL_GENL_ATTR_TZ_GOV_NAME, |
| 530 | 607 | tz->governor->name)) |
| 531 | − ret = -EMSGSIZE; | |
| 532 | − | |
| 533 | − mutex_unlock(&tz->lock); | |
| 608 | + return -EMSGSIZE; | |
| 534 | 609 | |
| 535 | − return ret; | |
| 610 | + return 0; | |
| 536 | 611 | } |
| 537 | 612 | |
| 538 | 613 | static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device *cdev, |
| @@ -572,12 +647,128 @@ out_cancel_nest: · 33 unmodified lines | ||
| 572 | 647 | return ret; |
| 573 | 648 | } |
| 574 | 649 | |
| 650 | +static int __thermal_genl_cmd_threshold_get(struct user_threshold *threshold, void *arg) | |
| 651 | +{ | |
| 652 | + struct sk_buff *msg = arg; | |
| 653 | + | |
| 654 | + if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, threshold->temperature) || | |
| 655 | + nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, threshold->direction)) | |
| 656 | + return -1; | |
| 657 | + | |
| 658 | + return 0; | |
| 659 | +} | |
| 660 | + | |
| 661 | +static int thermal_genl_cmd_threshold_get(struct param *p) | |
| 662 | +{ | |
| 663 | + struct sk_buff *msg = p->msg; | |
| 664 | + struct nlattr *start_trip; | |
| 665 | + int id, ret; | |
| 666 | + | |
| 667 | + if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID]) | |
| 668 | + return -EINVAL; | |
| 669 | + | |
| 670 | + id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]); | |
| 671 | + | |
| 672 | + CLASS(thermal_zone_get_by_id, tz)(id); | |
| 673 | + if (!tz) | |
| 674 | + return -EINVAL; | |
| 675 | + | |
| 676 | + start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_THRESHOLD); | |
| 677 | + if (!start_trip) | |
| 678 | + return -EMSGSIZE; | |
| 679 | + | |
| 680 | + ret = thermal_thresholds_for_each(tz, __thermal_genl_cmd_threshold_get, msg); | |
| 681 | + if (ret) | |
| 682 | + return -EMSGSIZE; | |
| 683 | + | |
| 684 | + nla_nest_end(msg, start_trip); | |
| 685 | + | |
| 686 | + return 0; | |
| 687 | +} | |
| 688 | + | |
| 689 | +static int thermal_genl_cmd_threshold_add(struct param *p) | |
| 690 | +{ | |
| 691 | + int id, temp, direction; | |
| 692 | + | |
| 693 | + if (!capable(CAP_SYS_ADMIN)) | |
| 694 | + return -EPERM; | |
| 695 | + | |
| 696 | + if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID] || | |
| 697 | + !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP] || | |
| 698 | + !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]) | |
| 699 | + return -EINVAL; | |
| 700 | + | |
| 701 | + id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]); | |
| 702 | + temp = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]); | |
| 703 | + direction = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]); | |
| 704 | + | |
| 705 | + CLASS(thermal_zone_get_by_id, tz)(id); | |
| 706 | + if (!tz) | |
| 707 | + return -EINVAL; | |
| 708 | + | |
| 709 | + guard(thermal_zone)(tz); | |
| 710 | + | |
| 711 | + return thermal_thresholds_add(tz, temp, direction); | |
| 712 | +} | |
| 713 | + | |
| 714 | +static int thermal_genl_cmd_threshold_delete(struct param *p) | |
| 715 | +{ | |
| 716 | + int id, temp, direction; | |
| 717 | + | |
| 718 | + if (!capable(CAP_SYS_ADMIN)) | |
| 719 | + return -EPERM; | |
| 720 | + | |
| 721 | + if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID] || | |
| 722 | + !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP] || | |
| 723 | + !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]) | |
| 724 | + return -EINVAL; | |
| 725 | + | |
| 726 | + id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]); | |
| 727 | + temp = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]); | |
| 728 | + direction = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]); | |
| 729 | + | |
| 730 | + CLASS(thermal_zone_get_by_id, tz)(id); | |
| 731 | + if (!tz) | |
| 732 | + return -EINVAL; | |
| 733 | + | |
| 734 | + guard(thermal_zone)(tz); | |
| 735 | + | |
| 736 | + return thermal_thresholds_delete(tz, temp, direction); | |
| 737 | +} | |
| 738 | + | |
| 739 | +static int thermal_genl_cmd_threshold_flush(struct param *p) | |
| 740 | +{ | |
| 741 | + int id; | |
| 742 | + | |
| 743 | + if (!capable(CAP_SYS_ADMIN)) | |
| 744 | + return -EPERM; | |
| 745 | + | |
| 746 | + if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID]) | |
| 747 | + return -EINVAL; | |
| 748 | + | |
| 749 | + id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]); | |
| 750 | + | |
| 751 | + CLASS(thermal_zone_get_by_id, tz)(id); | |
| 752 | + if (!tz) | |
| 753 | + return -EINVAL; | |
| 754 | + | |
| 755 | + guard(thermal_zone)(tz); | |
| 756 | + | |
| 757 | + thermal_thresholds_flush(tz); | |
| 758 | + | |
| 759 | + return 0; | |
| 760 | +} | |
| 761 | + | |
| 575 | 762 | 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, | |
| 763 | + [THERMAL_GENL_CMD_TZ_GET_ID] = thermal_genl_cmd_tz_get_id, | |
| 764 | + [THERMAL_GENL_CMD_TZ_GET_TRIP] = thermal_genl_cmd_tz_get_trip, | |
| 765 | + [THERMAL_GENL_CMD_TZ_GET_TEMP] = thermal_genl_cmd_tz_get_temp, | |
| 766 | + [THERMAL_GENL_CMD_TZ_GET_GOV] = thermal_genl_cmd_tz_get_gov, | |
| 767 | + [THERMAL_GENL_CMD_CDEV_GET] = thermal_genl_cmd_cdev_get, | |
| 768 | + [THERMAL_GENL_CMD_THRESHOLD_GET] = thermal_genl_cmd_threshold_get, | |
| 769 | + [THERMAL_GENL_CMD_THRESHOLD_ADD] = thermal_genl_cmd_threshold_add, | |
| 770 | + [THERMAL_GENL_CMD_THRESHOLD_DELETE] = thermal_genl_cmd_threshold_delete, | |
| 771 | + [THERMAL_GENL_CMD_THRESHOLD_FLUSH] = thermal_genl_cmd_threshold_flush, | |
| 581 | 772 | }; |
| 582 | 773 | |
| 583 | 774 | static int thermal_genl_cmd_dumpit(struct sk_buff *skb, |
| @@ -688,6 +879,26 @@ static const struct genl_small_ops thermal_genl_ops[] = { · 104 unmodified lines | ||
| 688 | 879 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
| 689 | 880 | .dumpit = thermal_genl_cmd_dumpit, |
| 690 | 881 | }, |
| 882 | + { | |
| 883 | + .cmd = THERMAL_GENL_CMD_THRESHOLD_GET, | |
| 884 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | |
| 885 | + .doit = thermal_genl_cmd_doit, | |
| 886 | + }, | |
| 887 | + { | |
| 888 | + .cmd = THERMAL_GENL_CMD_THRESHOLD_ADD, | |
| 889 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | |
| 890 | + .doit = thermal_genl_cmd_doit, | |
| 891 | + }, | |
| 892 | + { | |
| 893 | + .cmd = THERMAL_GENL_CMD_THRESHOLD_DELETE, | |
| 894 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | |
| 895 | + .doit = thermal_genl_cmd_doit, | |
| 896 | + }, | |
| 897 | + { | |
| 898 | + .cmd = THERMAL_GENL_CMD_THRESHOLD_FLUSH, | |
| 899 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | |
| 900 | + .doit = thermal_genl_cmd_doit, | |
| 901 | + }, | |
| 691 | 902 | }; |
| 692 | 903 | |
| 693 | 904 | static struct genl_family thermal_genl_family __ro_after_init = { |
| @@ -700,7 +911,7 @@ static struct genl_family thermal_genl_family __ro_after_init = { · 6 unmodified lines | ||
| 700 | 911 | .unbind = thermal_genl_unbind, |
| 701 | 912 | .small_ops = thermal_genl_ops, |
| 702 | 913 | .n_small_ops = ARRAY_SIZE(thermal_genl_ops), |
| 703 | − .resv_start_op = THERMAL_GENL_CMD_CDEV_GET + 1, | |
| 914 | + .resv_start_op = __THERMAL_GENL_CMD_MAX, | |
| 704 | 915 | .mcgrps = thermal_genl_mcgrps, |
| 705 | 916 | .n_mcgrps = ARRAY_SIZE(thermal_genl_mcgrps), |
| 706 | 917 | }; |
| @@ -53,6 +53,13 @@ int thermal_notify_tz_gov_change(const struct thermal_zone_device *tz, · 52 unmodified lines | ||
| 53 | 53 | int thermal_genl_sampling_temp(int id, int temp); |
| 54 | 54 | int thermal_genl_cpu_capability_event(int count, |
| 55 | 55 | struct thermal_genl_cpu_caps *caps); |
| 56 | +int thermal_notify_threshold_add(const struct thermal_zone_device *tz, | |
| 57 | + int temperature, int direction); | |
| 58 | +int thermal_notify_threshold_delete(const struct thermal_zone_device *tz, | |
| 59 | + int temperature, int direction); | |
| 60 | +int thermal_notify_threshold_flush(const struct thermal_zone_device *tz); | |
| 61 | +int thermal_notify_threshold_down(const struct thermal_zone_device *tz); | |
| 62 | +int thermal_notify_threshold_up(const struct thermal_zone_device *tz); | |
| 56 | 63 | #else |
| 57 | 64 | static inline int thermal_netlink_init(void) |
| 58 | 65 | { |
| @@ -139,6 +146,33 @@ static inline int thermal_genl_cpu_capability_event(int count, struct thermal_ge · 80 unmodified lines | ||
| 139 | 146 | return 0; |
| 140 | 147 | } |
| 141 | 148 | |
| 149 | +static inline int thermal_notify_threshold_add(const struct thermal_zone_device *tz, | |
| 150 | + int temperature, int direction) | |
| 151 | +{ | |
| 152 | + return 0; | |
| 153 | +} | |
| 154 | + | |
| 155 | +static inline int thermal_notify_threshold_delete(const struct thermal_zone_device *tz, | |
| 156 | + int temperature, int direction) | |
| 157 | +{ | |
| 158 | + return 0; | |
| 159 | +} | |
| 160 | + | |
| 161 | +static inline int thermal_notify_threshold_flush(const struct thermal_zone_device *tz) | |
| 162 | +{ | |
| 163 | + return 0; | |
| 164 | +} | |
| 165 | + | |
| 166 | +static inline int thermal_notify_threshold_down(const struct thermal_zone_device *tz) | |
| 167 | +{ | |
| 168 | + return 0; | |
| 169 | +} | |
| 170 | + | |
| 171 | +static inline int thermal_notify_threshold_up(const struct thermal_zone_device *tz) | |
| 172 | +{ | |
| 173 | + return 0; | |
| 174 | +} | |
| 175 | + | |
| 142 | 176 | static inline void __init thermal_netlink_exit(void) {} |
| 143 | 177 | |
| 144 | 178 | #endif /* CONFIG_THERMAL_NETLINK */ |
| @@ -50,13 +50,13 @@ static ssize_t · 49 unmodified lines | ||
| 50 | 50 | mode_show(struct device *dev, struct device_attribute *attr, char *buf) |
| 51 | 51 | { |
| 52 | 52 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
| 53 | − int enabled; | |
| 54 | 53 | |
| 55 | − mutex_lock(&tz->lock); | |
| 56 | − enabled = tz->mode == THERMAL_DEVICE_ENABLED; | |
| 57 | − mutex_unlock(&tz->lock); | |
| 54 | + guard(thermal_zone)(tz); | |
| 58 | 55 | |
| 59 | − return sprintf(buf, "%s\n", enabled ? "enabled" : "disabled"); | |
| 56 | + if (tz->mode == THERMAL_DEVICE_ENABLED) | |
| 57 | + return sprintf(buf, "enabled\n"); | |
| 58 | + | |
| 59 | + return sprintf(buf, "disabled\n"); | |
| 60 | 60 | } |
| 61 | 61 | |
| 62 | 62 | static ssize_t |
| @@ -103,38 +103,34 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr, · 40 unmodified lines | ||
| 103 | 103 | { |
| 104 | 104 | struct thermal_trip *trip = thermal_trip_of_attr(attr, temp); |
| 105 | 105 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
| 106 | − int ret, temp; | |
| 106 | + int temp; | |
| 107 | 107 | |
| 108 | − ret = kstrtoint(buf, 10, &temp); | |
| 109 | − if (ret) | |
| 108 | + if (kstrtoint(buf, 10, &temp)) | |
| 110 | 109 | return -EINVAL; |
| 111 | 110 | |
| 112 | − mutex_lock(&tz->lock); | |
| 111 | + guard(thermal_zone)(tz); | |
| 113 | 112 | |
| 114 | 113 | if (temp == trip->temperature) |
| 115 | − goto unlock; | |
| 114 | + return count; | |
| 116 | 115 | |
| 117 | 116 | /* Arrange the condition to avoid integer overflows. */ |
| 118 | 117 | if (temp != THERMAL_TEMP_INVALID && |
| 119 | − temp <= trip->hysteresis + THERMAL_TEMP_INVALID) { | |
| 120 | − ret = -EINVAL; | |
| 121 | − goto unlock; | |
| 122 | − } | |
| 118 | + temp <= trip->hysteresis + THERMAL_TEMP_INVALID) | |
| 119 | + return -EINVAL; | |
| 123 | 120 | |
| 124 | 121 | if (tz->ops.set_trip_temp) { |
| 122 | + int ret; | |
| 123 | + | |
| 125 | 124 | ret = tz->ops.set_trip_temp(tz, trip, temp); |
| 126 | 125 | if (ret) |
| 127 | − goto unlock; | |
| 126 | + return ret; | |
| 128 | 127 | } |
| 129 | 128 | |
| 130 | 129 | thermal_zone_set_trip_temp(tz, trip, temp); |
| 131 | 130 | |
| 132 | 131 | __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); |
| 133 | 132 | |
| 134 | −unlock: | |
| 135 | − mutex_unlock(&tz->lock); | |
| 136 | − | |
| 137 | − return ret ? ret : count; | |
| 133 | + return count; | |
| 138 | 134 | } |
| 139 | 135 | |
| 140 | 136 | static ssize_t |
| @@ -152,16 +148,15 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr, · 11 unmodified lines | ||
| 152 | 148 | { |
| 153 | 149 | struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst); |
| 154 | 150 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
| 155 | − int ret, hyst; | |
| 151 | + int hyst; | |
| 156 | 152 | |
| 157 | − ret = kstrtoint(buf, 10, &hyst); | |
| 158 | − if (ret || hyst < 0) | |
| 153 | + if (kstrtoint(buf, 10, &hyst) || hyst < 0) | |
| 159 | 154 | return -EINVAL; |
| 160 | 155 | |
| 161 | − mutex_lock(&tz->lock); | |
| 156 | + guard(thermal_zone)(tz); | |
| 162 | 157 | |
| 163 | 158 | if (hyst == trip->hysteresis) |
| 164 | − goto unlock; | |
| 159 | + return count; | |
| 165 | 160 | |
| 166 | 161 | /* |
| 167 | 162 | * Allow the hysteresis to be updated when the temperature is invalid |
| @@ -171,22 +166,17 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr, · 3 unmodified lines | ||
| 171 | 166 | */ |
| 172 | 167 | if (trip->temperature == THERMAL_TEMP_INVALID) { |
| 173 | 168 | WRITE_ONCE(trip->hysteresis, hyst); |
| 174 | − goto unlock; | |
| 169 | + return count; | |
| 175 | 170 | } |
| 176 | 171 | |
| 177 | − if (trip->temperature - hyst <= THERMAL_TEMP_INVALID) { | |
| 178 | − ret = -EINVAL; | |
| 179 | − goto unlock; | |
| 180 | − } | |
| 172 | + if (trip->temperature - hyst <= THERMAL_TEMP_INVALID) | |
| 173 | + return -EINVAL; | |
| 181 | 174 | |
| 182 | 175 | thermal_zone_set_trip_hyst(tz, trip, hyst); |
| 183 | 176 | |
| 184 | 177 | __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); |
| 185 | 178 | |
| 186 | −unlock: | |
| 187 | − mutex_unlock(&tz->lock); | |
| 188 | − | |
| 189 | − return ret ? ret : count; | |
| 179 | + return count; | |
| 190 | 180 | } |
| 191 | 181 | |
| 192 | 182 | static ssize_t |
| @@ -236,25 +226,26 @@ emul_temp_store(struct device *dev, struct device_attribute *attr, · 43 unmodified lines | ||
| 236 | 226 | const char *buf, size_t count) |
| 237 | 227 | { |
| 238 | 228 | struct thermal_zone_device *tz = to_thermal_zone(dev); |
| 239 | − int ret = 0; | |
| 240 | 229 | int temperature; |
| 241 | 230 | |
| 242 | 231 | if (kstrtoint(buf, 10, &temperature)) |
| 243 | 232 | return -EINVAL; |
| 244 | 233 | |
| 245 | − mutex_lock(&tz->lock); | |
| 234 | + guard(thermal_zone)(tz); | |
| 246 | 235 | |
| 247 | − if (!tz->ops.set_emul_temp) | |
| 248 | − tz->emul_temperature = temperature; | |
| 249 | − else | |
| 250 | − ret = tz->ops.set_emul_temp(tz, temperature); | |
| 236 | + if (tz->ops.set_emul_temp) { | |
| 237 | + int ret; | |
| 251 | 238 | |
| 252 | − if (!ret) | |
| 253 | − __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | |
| 239 | + ret = tz->ops.set_emul_temp(tz, temperature); | |
| 240 | + if (ret) | |
| 241 | + return ret; | |
| 242 | + } else { | |
| 243 | + tz->emul_temperature = temperature; | |
| 244 | + } | |
| 254 | 245 | |
| 255 | − mutex_unlock(&tz->lock); | |
| 246 | + __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | |
| 256 | 247 | |
| 257 | − return ret ? ret : count; | |
| 248 | + return count; | |
| 258 | 249 | } |
| 259 | 250 | static DEVICE_ATTR_WO(emul_temp); |
| 260 | 251 | #endif |
| @@ -553,14 +544,15 @@ cur_state_store(struct device *dev, struct device_attribute *attr, · 292 unmodified lines | ||
| 553 | 544 | if (state > cdev->max_state) |
| 554 | 545 | return -EINVAL; |
| 555 | 546 | |
| 556 | − mutex_lock(&cdev->lock); | |
| 547 | + guard(cooling_dev)(cdev); | |
| 557 | 548 | |
| 558 | 549 | result = cdev->ops->set_cur_state(cdev, state); |
| 559 | − if (!result) | |
| 560 | − thermal_cooling_device_stats_update(cdev, state); | |
| 550 | + if (result) | |
| 551 | + return result; | |
| 561 | 552 | |
| 562 | − mutex_unlock(&cdev->lock); | |
| 563 | − return result ? result : count; | |
| 553 | + thermal_cooling_device_stats_update(cdev, state); | |
| 554 | + | |
| 555 | + return count; | |
| 564 | 556 | } |
| 565 | 557 | |
| 566 | 558 | static struct device_attribute |
| @@ -634,21 +626,18 @@ static ssize_t total_trans_show(struct device *dev, · 67 unmodified lines | ||
| 634 | 626 | { |
| 635 | 627 | struct thermal_cooling_device *cdev = to_cooling_device(dev); |
| 636 | 628 | struct cooling_dev_stats *stats; |
| 637 | − int ret = 0; | |
| 629 | + int ret; | |
| 638 | 630 | |
| 639 | − mutex_lock(&cdev->lock); | |
| 631 | + guard(cooling_dev)(cdev); | |
| 640 | 632 | |
| 641 | 633 | stats = cdev->stats; |
| 642 | 634 | if (!stats) |
| 643 | − goto unlock; | |
| 635 | + return 0; | |
| 644 | 636 | |
| 645 | 637 | spin_lock(&stats->lock); |
| 646 | 638 | ret = sprintf(buf, "%u\n", stats->total_trans); |
| 647 | 639 | spin_unlock(&stats->lock); |
| 648 | 640 | |
| 649 | −unlock: | |
| 650 | − mutex_unlock(&cdev->lock); | |
| 651 | − | |
| 652 | 641 | return ret; |
| 653 | 642 | } |
| 654 | 643 | |
| @@ -661,11 +650,11 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr, · 6 unmodified lines | ||
| 661 | 650 | ssize_t len = 0; |
| 662 | 651 | int i; |
| 663 | 652 | |
| 664 | − mutex_lock(&cdev->lock); | |
| 653 | + guard(cooling_dev)(cdev); | |
| 665 | 654 | |
| 666 | 655 | stats = cdev->stats; |
| 667 | 656 | if (!stats) |
| 668 | − goto unlock; | |
| 657 | + return 0; | |
| 669 | 658 | |
| 670 | 659 | spin_lock(&stats->lock); |
| 671 | 660 | |
| @@ -677,9 +666,6 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr, · 5 unmodified lines | ||
| 677 | 666 | } |
| 678 | 667 | spin_unlock(&stats->lock); |
| 679 | 668 | |
| 680 | −unlock: | |
| 681 | − mutex_unlock(&cdev->lock); | |
| 682 | − | |
| 683 | 669 | return len; |
| 684 | 670 | } |
| 685 | 671 | |
| @@ -691,11 +677,11 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf, · 5 unmodified lines | ||
| 691 | 677 | struct cooling_dev_stats *stats; |
| 692 | 678 | int i, states; |
| 693 | 679 | |
| 694 | − mutex_lock(&cdev->lock); | |
| 680 | + guard(cooling_dev)(cdev); | |
| 695 | 681 | |
| 696 | 682 | stats = cdev->stats; |
| 697 | 683 | if (!stats) |
| 698 | − goto unlock; | |
| 684 | + return count; | |
| 699 | 685 | |
| 700 | 686 | states = cdev->max_state + 1; |
| 701 | 687 | |
| @@ -711,9 +697,6 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf, · 9 unmodified lines | ||
| 711 | 697 | |
| 712 | 698 | spin_unlock(&stats->lock); |
| 713 | 699 | |
| 714 | −unlock: | |
| 715 | − mutex_unlock(&cdev->lock); | |
| 716 | − | |
| 717 | 700 | return count; |
| 718 | 701 | } |
| 719 | 702 | |
| @@ -725,13 +708,11 @@ static ssize_t trans_table_show(struct device *dev, · 5 unmodified lines | ||
| 725 | 708 | ssize_t len = 0; |
| 726 | 709 | int i, j; |
| 727 | 710 | |
| 728 | − mutex_lock(&cdev->lock); | |
| 711 | + guard(cooling_dev)(cdev); | |
| 729 | 712 | |
| 730 | 713 | stats = cdev->stats; |
| 731 | − if (!stats) { | |
| 732 | − len = -ENODATA; | |
| 733 | − goto unlock; | |
| 734 | − } | |
| 714 | + if (!stats) | |
| 715 | + return -ENODATA; | |
| 735 | 716 | |
| 736 | 717 | len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n"); |
| 737 | 718 | len += snprintf(buf + len, PAGE_SIZE - len, " : "); |
| @@ -740,10 +721,8 @@ static ssize_t trans_table_show(struct device *dev, · 2 unmodified lines | ||
| 740 | 721 | break; |
| 741 | 722 | len += snprintf(buf + len, PAGE_SIZE - len, "state%2u ", i); |
| 742 | 723 | } |
| 743 | − if (len >= PAGE_SIZE) { | |
| 744 | − len = PAGE_SIZE; | |
| 745 | − goto unlock; | |
| 746 | − } | |
| 724 | + if (len >= PAGE_SIZE) | |
| 725 | + return PAGE_SIZE; | |
| 747 | 726 | |
| 748 | 727 | len += snprintf(buf + len, PAGE_SIZE - len, "\n"); |
| 749 | 728 | |
| @@ -769,9 +748,6 @@ static ssize_t trans_table_show(struct device *dev, · 19 unmodified lines | ||
| 769 | 748 | len = -EFBIG; |
| 770 | 749 | } |
| 771 | 750 | |
| 772 | −unlock: | |
| 773 | − mutex_unlock(&cdev->lock); | |
| 774 | − | |
| 775 | 751 | return len; |
| 776 | 752 | } |
| 777 | 753 | |
| @@ -894,13 +870,11 @@ ssize_t weight_store(struct device *dev, struct device_attribute *attr, · 116 unmodified lines | ||
| 894 | 870 | instance = container_of(attr, struct thermal_instance, weight_attr); |
| 895 | 871 | |
| 896 | 872 | /* Don't race with governors using the 'weight' value */ |
| 897 | − mutex_lock(&tz->lock); | |
| 873 | + guard(thermal_zone)(tz); | |
| 898 | 874 | |
| 899 | 875 | instance->weight = weight; |
| 900 | 876 | |
| 901 | 877 | thermal_governor_update_tz(tz, THERMAL_INSTANCE_WEIGHT_CHANGED); |
| 902 | 878 | |
| 903 | − mutex_unlock(&tz->lock); | |
| 904 | − | |
| 905 | 879 | return count; |
| 906 | 880 | } |
| @@ -0,0 +1,240 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * Copyright 2024 Linaro Limited | |
| 4 | + * | |
| 5 | + * Author: Daniel Lezcano <daniel.lezcano@linaro.org> | |
| 6 | + * | |
| 7 | + * Thermal thresholds | |
| 8 | + */ | |
| 9 | +#include <linux/list.h> | |
| 10 | +#include <linux/list_sort.h> | |
| 11 | +#include <linux/slab.h> | |
| 12 | + | |
| 13 | +#include "thermal_core.h" | |
| 14 | +#include "thermal_thresholds.h" | |
| 15 | + | |
| 16 | +int thermal_thresholds_init(struct thermal_zone_device *tz) | |
| 17 | +{ | |
| 18 | + INIT_LIST_HEAD(&tz->user_thresholds); | |
| 19 | + | |
| 20 | + return 0; | |
| 21 | +} | |
| 22 | + | |
| 23 | +static void __thermal_thresholds_flush(struct thermal_zone_device *tz) | |
| 24 | +{ | |
| 25 | + struct list_head *thresholds = &tz->user_thresholds; | |
| 26 | + struct user_threshold *entry, *tmp; | |
| 27 | + | |
| 28 | + list_for_each_entry_safe(entry, tmp, thresholds, list_node) { | |
| 29 | + list_del(&entry->list_node); | |
| 30 | + kfree(entry); | |
| 31 | + } | |
| 32 | +} | |
| 33 | + | |
| 34 | +void thermal_thresholds_flush(struct thermal_zone_device *tz) | |
| 35 | +{ | |
| 36 | + lockdep_assert_held(&tz->lock); | |
| 37 | + | |
| 38 | + __thermal_thresholds_flush(tz); | |
| 39 | + | |
| 40 | + thermal_notify_threshold_flush(tz); | |
| 41 | + | |
| 42 | + __thermal_zone_device_update(tz, THERMAL_TZ_FLUSH_THRESHOLDS); | |
| 43 | +} | |
| 44 | + | |
| 45 | +void thermal_thresholds_exit(struct thermal_zone_device *tz) | |
| 46 | +{ | |
| 47 | + __thermal_thresholds_flush(tz); | |
| 48 | +} | |
| 49 | + | |
| 50 | +static int __thermal_thresholds_cmp(void *data, | |
| 51 | + const struct list_head *l1, | |
| 52 | + const struct list_head *l2) | |
| 53 | +{ | |
| 54 | + struct user_threshold *t1 = container_of(l1, struct user_threshold, list_node); | |
| 55 | + struct user_threshold *t2 = container_of(l2, struct user_threshold, list_node); | |
| 56 | + | |
| 57 | + return t1->temperature - t2->temperature; | |
| 58 | +} | |
| 59 | + | |
| 60 | +static struct user_threshold *__thermal_thresholds_find(const struct list_head *thresholds, | |
| 61 | + int temperature) | |
| 62 | +{ | |
| 63 | + struct user_threshold *t; | |
| 64 | + | |
| 65 | + list_for_each_entry(t, thresholds, list_node) | |
| 66 | + if (t->temperature == temperature) | |
| 67 | + return t; | |
| 68 | + | |
| 69 | + return NULL; | |
| 70 | +} | |
| 71 | + | |
| 72 | +static bool __thermal_threshold_is_crossed(struct user_threshold *threshold, int temperature, | |
| 73 | + int last_temperature, int direction, | |
| 74 | + int *low, int *high) | |
| 75 | +{ | |
| 76 | + | |
| 77 | + if (temperature >= threshold->temperature) { | |
| 78 | + if (threshold->temperature > *low && | |
| 79 | + THERMAL_THRESHOLD_WAY_DOWN & threshold->direction) | |
| 80 | + *low = threshold->temperature; | |
| 81 | + | |
| 82 | + if (last_temperature < threshold->temperature && | |
| 83 | + threshold->direction & direction) | |
| 84 | + return true; | |
| 85 | + } else { | |
| 86 | + if (threshold->temperature < *high && THERMAL_THRESHOLD_WAY_UP | |
| 87 | + & threshold->direction) | |
| 88 | + *high = threshold->temperature; | |
| 89 | + | |
| 90 | + if (last_temperature >= threshold->temperature && | |
| 91 | + threshold->direction & direction) | |
| 92 | + return true; | |
| 93 | + } | |
| 94 | + | |
| 95 | + return false; | |
| 96 | +} | |
| 97 | + | |
| 98 | +static bool thermal_thresholds_handle_raising(struct list_head *thresholds, int temperature, | |
| 99 | + int last_temperature, int *low, int *high) | |
| 100 | +{ | |
| 101 | + struct user_threshold *t; | |
| 102 | + | |
| 103 | + list_for_each_entry(t, thresholds, list_node) { | |
| 104 | + if (__thermal_threshold_is_crossed(t, temperature, last_temperature, | |
| 105 | + THERMAL_THRESHOLD_WAY_UP, low, high)) | |
| 106 | + return true; | |
| 107 | + } | |
| 108 | + | |
| 109 | + return false; | |
| 110 | +} | |
| 111 | + | |
| 112 | +static bool thermal_thresholds_handle_dropping(struct list_head *thresholds, int temperature, | |
| 113 | + int last_temperature, int *low, int *high) | |
| 114 | +{ | |
| 115 | + struct user_threshold *t; | |
| 116 | + | |
| 117 | + list_for_each_entry_reverse(t, thresholds, list_node) { | |
| 118 | + if (__thermal_threshold_is_crossed(t, temperature, last_temperature, | |
| 119 | + THERMAL_THRESHOLD_WAY_DOWN, low, high)) | |
| 120 | + return true; | |
| 121 | + } | |
| 122 | + | |
| 123 | + return false; | |
| 124 | +} | |
| 125 | + | |
| 126 | +void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high) | |
| 127 | +{ | |
| 128 | + struct list_head *thresholds = &tz->user_thresholds; | |
| 129 | + | |
| 130 | + int temperature = tz->temperature; | |
| 131 | + int last_temperature = tz->last_temperature; | |
| 132 | + | |
| 133 | + lockdep_assert_held(&tz->lock); | |
| 134 | + | |
| 135 | + /* | |
| 136 | + * We need a second update in order to detect a threshold being crossed | |
| 137 | + */ | |
| 138 | + if (last_temperature == THERMAL_TEMP_INVALID) | |
| 139 | + return; | |
| 140 | + | |
| 141 | + /* | |
| 142 | + * The temperature is stable, so obviously we can not have | |
| 143 | + * crossed a threshold. | |
| 144 | + */ | |
| 145 | + if (last_temperature == temperature) | |
| 146 | + return; | |
| 147 | + | |
| 148 | + /* | |
| 149 | + * Since last update the temperature: | |
| 150 | + * - increased : thresholds are crossed the way up | |
| 151 | + * - decreased : thresholds are crossed the way down | |
| 152 | + */ | |
| 153 | + if (temperature > last_temperature) { | |
| 154 | + if (thermal_thresholds_handle_raising(thresholds, temperature, | |
| 155 | + last_temperature, low, high)) | |
| 156 | + thermal_notify_threshold_up(tz); | |
| 157 | + } else { | |
| 158 | + if (thermal_thresholds_handle_dropping(thresholds, temperature, | |
| 159 | + last_temperature, low, high)) | |
| 160 | + thermal_notify_threshold_down(tz); | |
| 161 | + } | |
| 162 | +} | |
| 163 | + | |
| 164 | +int thermal_thresholds_add(struct thermal_zone_device *tz, | |
| 165 | + int temperature, int direction) | |
| 166 | +{ | |
| 167 | + struct list_head *thresholds = &tz->user_thresholds; | |
| 168 | + struct user_threshold *t; | |
| 169 | + | |
| 170 | + lockdep_assert_held(&tz->lock); | |
| 171 | + | |
| 172 | + t = __thermal_thresholds_find(thresholds, temperature); | |
| 173 | + if (t) { | |
| 174 | + if (t->direction == direction) | |
| 175 | + return -EEXIST; | |
| 176 | + | |
| 177 | + t->direction |= direction; | |
| 178 | + } else { | |
| 179 | + | |
| 180 | + t = kmalloc(sizeof(*t), GFP_KERNEL); | |
| 181 | + if (!t) | |
| 182 | + return -ENOMEM; | |
| 183 | + | |
| 184 | + INIT_LIST_HEAD(&t->list_node); | |
| 185 | + t->temperature = temperature; | |
| 186 | + t->direction = direction; | |
| 187 | + list_add(&t->list_node, thresholds); | |
| 188 | + list_sort(NULL, thresholds, __thermal_thresholds_cmp); | |
| 189 | + } | |
| 190 | + | |
| 191 | + thermal_notify_threshold_add(tz, temperature, direction); | |
| 192 | + | |
| 193 | + __thermal_zone_device_update(tz, THERMAL_TZ_ADD_THRESHOLD); | |
| 194 | + | |
| 195 | + return 0; | |
| 196 | +} | |
| 197 | + | |
| 198 | +int thermal_thresholds_delete(struct thermal_zone_device *tz, | |
| 199 | + int temperature, int direction) | |
| 200 | +{ | |
| 201 | + struct list_head *thresholds = &tz->user_thresholds; | |
| 202 | + struct user_threshold *t; | |
| 203 | + | |
| 204 | + lockdep_assert_held(&tz->lock); | |
| 205 | + | |
| 206 | + t = __thermal_thresholds_find(thresholds, temperature); | |
| 207 | + if (!t) | |
| 208 | + return -ENOENT; | |
| 209 | + | |
| 210 | + if (t->direction == direction) { | |
| 211 | + list_del(&t->list_node); | |
| 212 | + kfree(t); | |
| 213 | + } else { | |
| 214 | + t->direction &= ~direction; | |
| 215 | + } | |
| 216 | + | |
| 217 | + thermal_notify_threshold_delete(tz, temperature, direction); | |
| 218 | + | |
| 219 | + __thermal_zone_device_update(tz, THERMAL_TZ_DEL_THRESHOLD); | |
| 220 | + | |
| 221 | + return 0; | |
| 222 | +} | |
| 223 | + | |
| 224 | +int thermal_thresholds_for_each(struct thermal_zone_device *tz, | |
| 225 | + int (*cb)(struct user_threshold *, void *arg), void *arg) | |
| 226 | +{ | |
| 227 | + struct list_head *thresholds = &tz->user_thresholds; | |
| 228 | + struct user_threshold *entry; | |
| 229 | + int ret; | |
| 230 | + | |
| 231 | + guard(thermal_zone)(tz); | |
| 232 | + | |
| 233 | + list_for_each_entry(entry, thresholds, list_node) { | |
| 234 | + ret = cb(entry, arg); | |
| 235 | + if (ret) | |
| 236 | + return ret; | |
| 237 | + } | |
| 238 | + | |
| 239 | + return 0; | |
| 240 | +} | |
| @@ -0,0 +1,19 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0 */ | |
| 2 | +#ifndef __THERMAL_THRESHOLDS_H__ | |
| 3 | +#define __THERMAL_THRESHOLDS_H__ | |
| 4 | + | |
| 5 | +struct user_threshold { | |
| 6 | + struct list_head list_node; | |
| 7 | + int temperature; | |
| 8 | + int direction; | |
| 9 | +}; | |
| 10 | + | |
| 11 | +int thermal_thresholds_init(struct thermal_zone_device *tz); | |
| 12 | +void thermal_thresholds_exit(struct thermal_zone_device *tz); | |
| 13 | +void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high); | |
| 14 | +void thermal_thresholds_flush(struct thermal_zone_device *tz); | |
| 15 | +int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction); | |
| 16 | +int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction); | |
| 17 | +int thermal_thresholds_for_each(struct thermal_zone_device *tz, | |
| 18 | + int (*cb)(struct user_threshold *, void *arg), void *arg); | |
| 19 | +#endif | |
| @@ -45,13 +45,9 @@ int thermal_zone_for_each_trip(struct thermal_zone_device *tz, · 44 unmodified lines | ||
| 45 | 45 | int (*cb)(struct thermal_trip *, void *), |
| 46 | 46 | void *data) |
| 47 | 47 | { |
| 48 | − int ret; | |
| 49 | − | |
| 50 | − mutex_lock(&tz->lock); | |
| 51 | − ret = for_each_thermal_trip(tz, cb, data); | |
| 52 | − mutex_unlock(&tz->lock); | |
| 48 | + guard(thermal_zone)(tz); | |
| 53 | 49 | |
| 54 | − return ret; | |
| 50 | + return for_each_thermal_trip(tz, cb, data); | |
| 55 | 51 | } |
| 56 | 52 | EXPORT_SYMBOL_GPL(thermal_zone_for_each_trip); |
| 57 | 53 | |
| @@ -92,43 +88,3 @@ int thermal_zone_trip_id(const struct thermal_zone_device *tz, · 34 unmodified lines | ||
| 92 | 88 | */ |
| 93 | 89 | return trip_to_trip_desc(trip) - tz->trips; |
| 94 | 90 | } |
| 95 | − | |
| 96 | −void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz, | |
| 97 | − struct thermal_trip *trip, int hyst) | |
| 98 | −{ | |
| 99 | − WRITE_ONCE(trip->hysteresis, hyst); | |
| 100 | − thermal_notify_tz_trip_change(tz, trip); | |
| 101 | −} | |
| 102 | − | |
| 103 | −void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, | |
| 104 | − struct thermal_trip *trip, int temp) | |
| 105 | −{ | |
| 106 | − if (trip->temperature == temp) | |
| 107 | − return; | |
| 108 | − | |
| 109 | − WRITE_ONCE(trip->temperature, temp); | |
| 110 | − thermal_notify_tz_trip_change(tz, trip); | |
| 111 | − | |
| 112 | − if (temp == THERMAL_TEMP_INVALID) { | |
| 113 | − struct thermal_trip_desc *td = trip_to_trip_desc(trip); | |
| 114 | − | |
| 115 | − if (tz->temperature >= td->threshold) { | |
| 116 | − /* | |
| 117 | − * The trip has been crossed on the way up, so some | |
| 118 | − * adjustments are needed to compensate for the lack | |
| 119 | − * of it going forward. | |
| 120 | − */ | |
| 121 | − if (trip->type == THERMAL_TRIP_PASSIVE) { | |
| 122 | − tz->passive--; | |
| 123 | − WARN_ON_ONCE(tz->passive < 0); | |
| 124 | − } | |
| 125 | − thermal_zone_trip_down(tz, trip); | |
| 126 | − } | |
| 127 | − /* | |
| 128 | − * Invalidate the threshold to avoid triggering a spurious | |
| 129 | − * trip crossing notification when the trip becomes valid. | |
| 130 | − */ | |
| 131 | − td->threshold = INT_MAX; | |
| 132 | − } | |
| 133 | −} | |
| 134 | −EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp); | |
| @@ -56,6 +56,9 @@ enum thermal_notify_event { · 55 unmodified lines | ||
| 56 | 56 | THERMAL_TZ_UNBIND_CDEV, /* Cooling dev is unbind from the thermal zone */ |
| 57 | 57 | THERMAL_INSTANCE_WEIGHT_CHANGED, /* Thermal instance weight changed */ |
| 58 | 58 | THERMAL_TZ_RESUME, /* Thermal zone is resuming after system sleep */ |
| 59 | + THERMAL_TZ_ADD_THRESHOLD, /* Threshold added */ | |
| 60 | + THERMAL_TZ_DEL_THRESHOLD, /* Threshold deleted */ | |
| 61 | + THERMAL_TZ_FLUSH_THRESHOLDS, /* All thresholds deleted */ | |
| 59 | 62 | }; |
| 60 | 63 | |
| 61 | 64 | /** |
| @@ -137,6 +140,9 @@ struct thermal_cooling_device { · 75 unmodified lines | ||
| 137 | 140 | #endif |
| 138 | 141 | }; |
| 139 | 142 | |
| 143 | +DEFINE_GUARD(cooling_dev, struct thermal_cooling_device *, mutex_lock(&_T->lock), | |
| 144 | + mutex_unlock(&_T->lock)) | |
| 145 | + | |
| 140 | 146 | /* Structure to define Thermal Zone parameters */ |
| 141 | 147 | struct thermal_zone_params { |
| 142 | 148 | const char *governor_name; |
| @@ -3,6 +3,8 @@ · 2 unmodified lines | ||
| 3 | 3 | #define _UAPI_LINUX_THERMAL_H |
| 4 | 4 | |
| 5 | 5 | #define THERMAL_NAME_LENGTH 20 |
| 6 | +#define THERMAL_THRESHOLD_WAY_UP BIT(0) | |
| 7 | +#define THERMAL_THRESHOLD_WAY_DOWN BIT(1) | |
| 6 | 8 | |
| 7 | 9 | enum thermal_device_mode { |
| 8 | 10 | THERMAL_DEVICE_DISABLED = 0, |
| @@ -18,7 +20,7 @@ enum thermal_trip_type { · 9 unmodified lines | ||
| 18 | 20 | |
| 19 | 21 | /* Adding event notification support elements */ |
| 20 | 22 | #define THERMAL_GENL_FAMILY_NAME "thermal" |
| 21 | −#define THERMAL_GENL_VERSION 0x01 | |
| 23 | +#define THERMAL_GENL_VERSION 0x02 | |
| 22 | 24 | #define THERMAL_GENL_SAMPLING_GROUP_NAME "sampling" |
| 23 | 25 | #define THERMAL_GENL_EVENT_GROUP_NAME "event" |
| 24 | 26 | |
| @@ -28,6 +30,7 @@ enum thermal_genl_attr { · 3 unmodified lines | ||
| 28 | 30 | THERMAL_GENL_ATTR_TZ, |
| 29 | 31 | THERMAL_GENL_ATTR_TZ_ID, |
| 30 | 32 | THERMAL_GENL_ATTR_TZ_TEMP, |
| 33 | + THERMAL_GENL_ATTR_TZ_PREV_TEMP, | |
| 31 | 34 | THERMAL_GENL_ATTR_TZ_TRIP, |
| 32 | 35 | THERMAL_GENL_ATTR_TZ_TRIP_ID, |
| 33 | 36 | THERMAL_GENL_ATTR_TZ_TRIP_TYPE, |
| @@ -48,6 +51,9 @@ enum thermal_genl_attr { · 14 unmodified lines | ||
| 48 | 51 | THERMAL_GENL_ATTR_CPU_CAPABILITY_ID, |
| 49 | 52 | THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE, |
| 50 | 53 | THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY, |
| 54 | + THERMAL_GENL_ATTR_THRESHOLD, | |
| 55 | + THERMAL_GENL_ATTR_THRESHOLD_TEMP, | |
| 56 | + THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, | |
| 51 | 57 | __THERMAL_GENL_ATTR_MAX, |
| 52 | 58 | }; |
| 53 | 59 | #define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1) |
| @@ -75,6 +81,11 @@ enum thermal_genl_event { · 21 unmodified lines | ||
| 75 | 81 | THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, /* Cdev state updated */ |
| 76 | 82 | THERMAL_GENL_EVENT_TZ_GOV_CHANGE, /* Governor policy changed */ |
| 77 | 83 | THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE, /* CPU capability changed */ |
| 84 | + THERMAL_GENL_EVENT_THRESHOLD_ADD, /* A thresold has been added */ | |
| 85 | + THERMAL_GENL_EVENT_THRESHOLD_DELETE, /* A thresold has been deleted */ | |
| 86 | + THERMAL_GENL_EVENT_THRESHOLD_FLUSH, /* All thresolds have been deleted */ | |
| 87 | + THERMAL_GENL_EVENT_THRESHOLD_UP, /* A thresold has been crossed the way up */ | |
| 88 | + THERMAL_GENL_EVENT_THRESHOLD_DOWN, /* A thresold has been crossed the way down */ | |
| 78 | 89 | __THERMAL_GENL_EVENT_MAX, |
| 79 | 90 | }; |
| 80 | 91 | #define THERMAL_GENL_EVENT_MAX (__THERMAL_GENL_EVENT_MAX - 1) |
| @@ -82,12 +93,16 @@ enum thermal_genl_event { · 1 unmodified line | ||
| 82 | 93 | /* Commands supported by the thermal_genl_family */ |
| 83 | 94 | enum thermal_genl_cmd { |
| 84 | 95 | THERMAL_GENL_CMD_UNSPEC, |
| 85 | − THERMAL_GENL_CMD_TZ_GET_ID, /* List of thermal zones id */ | |
| 86 | − THERMAL_GENL_CMD_TZ_GET_TRIP, /* List of thermal trips */ | |
| 87 | − THERMAL_GENL_CMD_TZ_GET_TEMP, /* Get the thermal zone temperature */ | |
| 88 | − THERMAL_GENL_CMD_TZ_GET_GOV, /* Get the thermal zone governor */ | |
| 89 | − THERMAL_GENL_CMD_TZ_GET_MODE, /* Get the thermal zone mode */ | |
| 90 | − THERMAL_GENL_CMD_CDEV_GET, /* List of cdev id */ | |
| 96 | + THERMAL_GENL_CMD_TZ_GET_ID, /* List of thermal zones id */ | |
| 97 | + THERMAL_GENL_CMD_TZ_GET_TRIP, /* List of thermal trips */ | |
| 98 | + THERMAL_GENL_CMD_TZ_GET_TEMP, /* Get the thermal zone temperature */ | |
| 99 | + THERMAL_GENL_CMD_TZ_GET_GOV, /* Get the thermal zone governor */ | |
| 100 | + THERMAL_GENL_CMD_TZ_GET_MODE, /* Get the thermal zone mode */ | |
| 101 | + THERMAL_GENL_CMD_CDEV_GET, /* List of cdev id */ | |
| 102 | + THERMAL_GENL_CMD_THRESHOLD_GET, /* List of thresholds */ | |
| 103 | + THERMAL_GENL_CMD_THRESHOLD_ADD, /* Add a threshold */ | |
| 104 | + THERMAL_GENL_CMD_THRESHOLD_DELETE, /* Delete a threshold */ | |
| 105 | + THERMAL_GENL_CMD_THRESHOLD_FLUSH, /* Flush all the thresholds */ | |
| 91 | 106 | __THERMAL_GENL_CMD_MAX, |
| 92 | 107 | }; |
| 93 | 108 | #define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1) |
| @@ -5,6 +5,7 @@ · 4 unmodified lines | ||
| 5 | 5 | #include <stdio.h> |
| 6 | 6 | #include <stdlib.h> |
| 7 | 7 | #include <unistd.h> |
| 8 | +#include <limits.h> | |
| 8 | 9 | |
| 9 | 10 | #include <thermal.h> |
| 10 | 11 | #include "thermal_nl.h" |
| @@ -33,6 +34,11 @@ static struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = { · 22 unmodified lines | ||
| 33 | 34 | [THERMAL_GENL_ATTR_CDEV_CUR_STATE] = { .type = NLA_U32 }, |
| 34 | 35 | [THERMAL_GENL_ATTR_CDEV_MAX_STATE] = { .type = NLA_U32 }, |
| 35 | 36 | [THERMAL_GENL_ATTR_CDEV_NAME] = { .type = NLA_STRING }, |
| 37 | + | |
| 38 | + /* Thresholds */ | |
| 39 | + [THERMAL_GENL_ATTR_THRESHOLD] = { .type = NLA_NESTED }, | |
| 40 | + [THERMAL_GENL_ATTR_THRESHOLD_TEMP] = { .type = NLA_U32 }, | |
| 41 | + [THERMAL_GENL_ATTR_THRESHOLD_DIRECTION] = { .type = NLA_U32 }, | |
| 36 | 42 | }; |
| 37 | 43 | |
| 38 | 44 | static int parse_tz_get(struct genl_info *info, struct thermal_zone **tz) |
| @@ -182,6 +188,48 @@ static int parse_tz_get_gov(struct genl_info *info, struct thermal_zone *tz) · 143 unmodified lines | ||
| 182 | 188 | return THERMAL_SUCCESS; |
| 183 | 189 | } |
| 184 | 190 | |
| 191 | +static int parse_threshold_get(struct genl_info *info, struct thermal_zone *tz) | |
| 192 | +{ | |
| 193 | + struct nlattr *attr; | |
| 194 | + struct thermal_threshold *__tt = NULL; | |
| 195 | + size_t size = 0; | |
| 196 | + int rem; | |
| 197 | + | |
| 198 | + /* | |
| 199 | + * The size contains the size of the array and we want to | |
| 200 | + * access the last element, size - 1. | |
| 201 | + * | |
| 202 | + * The variable size is initialized to zero but it will be | |
| 203 | + * then incremented by the first if() statement. The message | |
| 204 | + * attributes are ordered, so the first if() statement will be | |
| 205 | + * always called before the second one. If it happens that is | |
| 206 | + * not the case, then it is a kernel bug. | |
| 207 | + */ | |
| 208 | + nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_THRESHOLD], rem) { | |
| 209 | + | |
| 210 | + if (nla_type(attr) == THERMAL_GENL_ATTR_THRESHOLD_TEMP) { | |
| 211 | + | |
| 212 | + size++; | |
| 213 | + | |
| 214 | + __tt = realloc(__tt, sizeof(*__tt) * (size + 2)); | |
| 215 | + if (!__tt) | |
| 216 | + return THERMAL_ERROR; | |
| 217 | + | |
| 218 | + __tt[size - 1].temperature = nla_get_u32(attr); | |
| 219 | + } | |
| 220 | + | |
| 221 | + if (nla_type(attr) == THERMAL_GENL_ATTR_THRESHOLD_DIRECTION) | |
| 222 | + __tt[size - 1].direction = nla_get_u32(attr); | |
| 223 | + } | |
| 224 | + | |
| 225 | + if (__tt) | |
| 226 | + __tt[size].temperature = INT_MAX; | |
| 227 | + | |
| 228 | + tz->thresholds = __tt; | |
| 229 | + | |
| 230 | + return THERMAL_SUCCESS; | |
| 231 | +} | |
| 232 | + | |
| 185 | 233 | static int handle_netlink(struct nl_cache_ops *unused, |
| 186 | 234 | struct genl_cmd *cmd, |
| 187 | 235 | struct genl_info *info, void *arg) |
| @@ -210,6 +258,10 @@ static int handle_netlink(struct nl_cache_ops *unused, · 22 unmodified lines | ||
| 210 | 258 | ret = parse_tz_get_gov(info, arg); |
| 211 | 259 | break; |
| 212 | 260 | |
| 261 | + case THERMAL_GENL_CMD_THRESHOLD_GET: | |
| 262 | + ret = parse_threshold_get(info, arg); | |
| 263 | + break; | |
| 264 | + | |
| 213 | 265 | default: |
| 214 | 266 | return THERMAL_ERROR; |
| 215 | 267 | } |
| @@ -253,6 +305,34 @@ static struct genl_cmd thermal_cmds[] = { · 37 unmodified lines | ||
| 253 | 305 | .c_maxattr = THERMAL_GENL_ATTR_MAX, |
| 254 | 306 | .c_attr_policy = thermal_genl_policy, |
| 255 | 307 | }, |
| 308 | + { | |
| 309 | + .c_id = THERMAL_GENL_CMD_THRESHOLD_GET, | |
| 310 | + .c_name = (char *)"Get thresholds list", | |
| 311 | + .c_msg_parser = handle_netlink, | |
| 312 | + .c_maxattr = THERMAL_GENL_ATTR_MAX, | |
| 313 | + .c_attr_policy = thermal_genl_policy, | |
| 314 | + }, | |
| 315 | + { | |
| 316 | + .c_id = THERMAL_GENL_CMD_THRESHOLD_ADD, | |
| 317 | + .c_name = (char *)"Add a threshold", | |
| 318 | + .c_msg_parser = handle_netlink, | |
| 319 | + .c_maxattr = THERMAL_GENL_ATTR_MAX, | |
| 320 | + .c_attr_policy = thermal_genl_policy, | |
| 321 | + }, | |
| 322 | + { | |
| 323 | + .c_id = THERMAL_GENL_CMD_THRESHOLD_DELETE, | |
| 324 | + .c_name = (char *)"Delete a threshold", | |
| 325 | + .c_msg_parser = handle_netlink, | |
| 326 | + .c_maxattr = THERMAL_GENL_ATTR_MAX, | |
| 327 | + .c_attr_policy = thermal_genl_policy, | |
| 328 | + }, | |
| 329 | + { | |
| 330 | + .c_id = THERMAL_GENL_CMD_THRESHOLD_FLUSH, | |
| 331 | + .c_name = (char *)"Flush the thresholds", | |
| 332 | + .c_msg_parser = handle_netlink, | |
| 333 | + .c_maxattr = THERMAL_GENL_ATTR_MAX, | |
| 334 | + .c_attr_policy = thermal_genl_policy, | |
| 335 | + }, | |
| 256 | 336 | }; |
| 257 | 337 | |
| 258 | 338 | static struct genl_ops thermal_cmd_ops = { |
| @@ -261,9 +341,41 @@ static struct genl_ops thermal_cmd_ops = { · 2 unmodified lines | ||
| 261 | 341 | .o_ncmds = ARRAY_SIZE(thermal_cmds), |
| 262 | 342 | }; |
| 263 | 343 | |
| 264 | −static thermal_error_t thermal_genl_auto(struct thermal_handler *th, int id, int cmd, | |
| 265 | − int flags, void *arg) | |
| 344 | +struct cmd_param { | |
| 345 | + int tz_id; | |
| 346 | + int temp; | |
| 347 | + int direction; | |
| 348 | +}; | |
| 349 | + | |
| 350 | +typedef int (*cmd_cb_t)(struct nl_msg *, struct cmd_param *); | |
| 351 | + | |
| 352 | +static int thermal_genl_tz_id_encode(struct nl_msg *msg, struct cmd_param *p) | |
| 266 | 353 | { |
| 354 | + if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id)) | |
| 355 | + return -1; | |
| 356 | + | |
| 357 | + return 0; | |
| 358 | +} | |
| 359 | + | |
| 360 | +static int thermal_genl_threshold_encode(struct nl_msg *msg, struct cmd_param *p) | |
| 361 | +{ | |
| 362 | + if (thermal_genl_tz_id_encode(msg, p)) | |
| 363 | + return -1; | |
| 364 | + | |
| 365 | + if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, p->temp)) | |
| 366 | + return -1; | |
| 367 | + | |
| 368 | + if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, p->direction)) | |
| 369 | + return -1; | |
| 370 | + | |
| 371 | + return 0; | |
| 372 | +} | |
| 373 | + | |
| 374 | +static thermal_error_t thermal_genl_auto(struct thermal_handler *th, cmd_cb_t cmd_cb, | |
| 375 | + struct cmd_param *param, | |
| 376 | + int cmd, int flags, void *arg) | |
| 377 | +{ | |
| 378 | + thermal_error_t ret = THERMAL_ERROR; | |
| 267 | 379 | struct nl_msg *msg; |
| 268 | 380 | void *hdr; |
| 269 | 381 | |
| @@ -274,45 +386,95 @@ static thermal_error_t thermal_genl_auto(struct thermal_handler *th, int id, int · 4 unmodified lines | ||
| 274 | 386 | hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, thermal_cmd_ops.o_id, |
| 275 | 387 | 0, flags, cmd, THERMAL_GENL_VERSION); |
| 276 | 388 | if (!hdr) |
| 277 | − return THERMAL_ERROR; | |
| 389 | + goto out; | |
| 278 | 390 | |
| 279 | − if (id >= 0 && nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id)) | |
| 280 | − return THERMAL_ERROR; | |
| 391 | + if (cmd_cb && cmd_cb(msg, param)) | |
| 392 | + goto out; | |
| 281 | 393 | |
| 282 | 394 | if (nl_send_msg(th->sk_cmd, th->cb_cmd, msg, genl_handle_msg, arg)) |
| 283 | − return THERMAL_ERROR; | |
| 395 | + goto out; | |
| 284 | 396 | |
| 397 | + ret = THERMAL_SUCCESS; | |
| 398 | +out: | |
| 285 | 399 | nlmsg_free(msg); |
| 286 | 400 | |
| 287 | − return THERMAL_SUCCESS; | |
| 401 | + return ret; | |
| 288 | 402 | } |
| 289 | 403 | |
| 290 | 404 | thermal_error_t thermal_cmd_get_tz(struct thermal_handler *th, struct thermal_zone **tz) |
| 291 | 405 | { |
| 292 | − return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_TZ_GET_ID, | |
| 406 | + return thermal_genl_auto(th, NULL, NULL, THERMAL_GENL_CMD_TZ_GET_ID, | |
| 293 | 407 | NLM_F_DUMP | NLM_F_ACK, tz); |
| 294 | 408 | } |
| 295 | 409 | |
| 296 | 410 | thermal_error_t thermal_cmd_get_cdev(struct thermal_handler *th, struct thermal_cdev **tc) |
| 297 | 411 | { |
| 298 | − return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_CDEV_GET, | |
| 412 | + return thermal_genl_auto(th, NULL, NULL, THERMAL_GENL_CMD_CDEV_GET, | |
| 299 | 413 | NLM_F_DUMP | NLM_F_ACK, tc); |
| 300 | 414 | } |
| 301 | 415 | |
| 302 | 416 | thermal_error_t thermal_cmd_get_trip(struct thermal_handler *th, struct thermal_zone *tz) |
| 303 | 417 | { |
| 304 | − return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_TRIP, | |
| 305 | − 0, tz); | |
| 418 | + struct cmd_param p = { .tz_id = tz->id }; | |
| 419 | + | |
| 420 | + return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p, | |
| 421 | + THERMAL_GENL_CMD_TZ_GET_TRIP, 0, tz); | |
| 306 | 422 | } |
| 307 | 423 | |
| 308 | 424 | thermal_error_t thermal_cmd_get_governor(struct thermal_handler *th, struct thermal_zone *tz) |
| 309 | 425 | { |
| 310 | − return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_GOV, 0, tz); | |
| 426 | + struct cmd_param p = { .tz_id = tz->id }; | |
| 427 | + | |
| 428 | + return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p, | |
| 429 | + THERMAL_GENL_CMD_TZ_GET_GOV, 0, tz); | |
| 311 | 430 | } |
| 312 | 431 | |
| 313 | 432 | thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th, struct thermal_zone *tz) |
| 314 | 433 | { |
| 315 | − return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz); | |
| 434 | + struct cmd_param p = { .tz_id = tz->id }; | |
| 435 | + | |
| 436 | + return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p, | |
| 437 | + THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz); | |
| 438 | +} | |
| 439 | + | |
| 440 | +thermal_error_t thermal_cmd_threshold_get(struct thermal_handler *th, | |
| 441 | + struct thermal_zone *tz) | |
| 442 | +{ | |
| 443 | + struct cmd_param p = { .tz_id = tz->id }; | |
| 444 | + | |
| 445 | + return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p, | |
| 446 | + THERMAL_GENL_CMD_THRESHOLD_GET, 0, tz); | |
| 447 | +} | |
| 448 | + | |
| 449 | +thermal_error_t thermal_cmd_threshold_add(struct thermal_handler *th, | |
| 450 | + struct thermal_zone *tz, | |
| 451 | + int temperature, | |
| 452 | + int direction) | |
| 453 | +{ | |
| 454 | + struct cmd_param p = { .tz_id = tz->id, .temp = temperature, .direction = direction }; | |
| 455 | + | |
| 456 | + return thermal_genl_auto(th, thermal_genl_threshold_encode, &p, | |
| 457 | + THERMAL_GENL_CMD_THRESHOLD_ADD, 0, tz); | |
| 458 | +} | |
| 459 | + | |
| 460 | +thermal_error_t thermal_cmd_threshold_delete(struct thermal_handler *th, | |
| 461 | + struct thermal_zone *tz, | |
| 462 | + int temperature, | |
| 463 | + int direction) | |
| 464 | +{ | |
| 465 | + struct cmd_param p = { .tz_id = tz->id, .temp = temperature, .direction = direction }; | |
| 466 | + | |
| 467 | + return thermal_genl_auto(th, thermal_genl_threshold_encode, &p, | |
| 468 | + THERMAL_GENL_CMD_THRESHOLD_DELETE, 0, tz); | |
| 469 | +} | |
| 470 | + | |
| 471 | +thermal_error_t thermal_cmd_threshold_flush(struct thermal_handler *th, | |
| 472 | + struct thermal_zone *tz) | |
| 473 | +{ | |
| 474 | + struct cmd_param p = { .tz_id = tz->id }; | |
| 475 | + | |
| 476 | + return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p, | |
| 477 | + THERMAL_GENL_CMD_THRESHOLD_FLUSH, 0, tz); | |
| 316 | 478 | } |
| 317 | 479 | |
| 318 | 480 | thermal_error_t thermal_cmd_exit(struct thermal_handler *th) |
| @@ -94,6 +94,30 @@ static int handle_thermal_event(struct nl_msg *n, void *arg) · 93 unmodified lines | ||
| 94 | 94 | case THERMAL_GENL_EVENT_TZ_GOV_CHANGE: |
| 95 | 95 | return ops->gov_change(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), |
| 96 | 96 | nla_get_string(attrs[THERMAL_GENL_ATTR_GOV_NAME]), arg); |
| 97 | + | |
| 98 | + case THERMAL_GENL_EVENT_THRESHOLD_ADD: | |
| 99 | + return ops->threshold_add(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), | |
| 100 | + nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]), | |
| 101 | + nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]), arg); | |
| 102 | + | |
| 103 | + case THERMAL_GENL_EVENT_THRESHOLD_DELETE: | |
| 104 | + return ops->threshold_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), | |
| 105 | + nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]), | |
| 106 | + nla_get_u32(attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]), arg); | |
| 107 | + | |
| 108 | + case THERMAL_GENL_EVENT_THRESHOLD_FLUSH: | |
| 109 | + return ops->threshold_flush(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg); | |
| 110 | + | |
| 111 | + case THERMAL_GENL_EVENT_THRESHOLD_UP: | |
| 112 | + return ops->threshold_up(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), | |
| 113 | + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]), | |
| 114 | + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_PREV_TEMP]), arg); | |
| 115 | + | |
| 116 | + case THERMAL_GENL_EVENT_THRESHOLD_DOWN: | |
| 117 | + return ops->threshold_down(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), | |
| 118 | + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]), | |
| 119 | + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_PREV_TEMP]), arg); | |
| 120 | + | |
| 97 | 121 | default: |
| 98 | 122 | return -1; |
| 99 | 123 | } |
| @@ -101,19 +125,24 @@ static int handle_thermal_event(struct nl_msg *n, void *arg) · 1 unmodified line | ||
| 101 | 125 | |
| 102 | 126 | static void thermal_events_ops_init(struct thermal_events_ops *ops) |
| 103 | 127 | { |
| 104 | − enabled_ops[THERMAL_GENL_EVENT_TZ_CREATE] = !!ops->tz_create; | |
| 105 | − enabled_ops[THERMAL_GENL_EVENT_TZ_DELETE] = !!ops->tz_delete; | |
| 106 | − enabled_ops[THERMAL_GENL_EVENT_TZ_DISABLE] = !!ops->tz_disable; | |
| 107 | − enabled_ops[THERMAL_GENL_EVENT_TZ_ENABLE] = !!ops->tz_enable; | |
| 108 | − enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_UP] = !!ops->trip_high; | |
| 109 | − enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = !!ops->trip_low; | |
| 110 | − enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = !!ops->trip_change; | |
| 111 | − enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_ADD] = !!ops->trip_add; | |
| 112 | − enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = !!ops->trip_delete; | |
| 113 | − enabled_ops[THERMAL_GENL_EVENT_CDEV_ADD] = !!ops->cdev_add; | |
| 114 | − enabled_ops[THERMAL_GENL_EVENT_CDEV_DELETE] = !!ops->cdev_delete; | |
| 115 | − enabled_ops[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = !!ops->cdev_update; | |
| 116 | − enabled_ops[THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = !!ops->gov_change; | |
| 128 | + enabled_ops[THERMAL_GENL_EVENT_TZ_CREATE] = !!ops->tz_create; | |
| 129 | + enabled_ops[THERMAL_GENL_EVENT_TZ_DELETE] = !!ops->tz_delete; | |
| 130 | + enabled_ops[THERMAL_GENL_EVENT_TZ_DISABLE] = !!ops->tz_disable; | |
| 131 | + enabled_ops[THERMAL_GENL_EVENT_TZ_ENABLE] = !!ops->tz_enable; | |
| 132 | + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_UP] = !!ops->trip_high; | |
| 133 | + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = !!ops->trip_low; | |
| 134 | + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = !!ops->trip_change; | |
| 135 | + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_ADD] = !!ops->trip_add; | |
| 136 | + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = !!ops->trip_delete; | |
| 137 | + enabled_ops[THERMAL_GENL_EVENT_CDEV_ADD] = !!ops->cdev_add; | |
| 138 | + enabled_ops[THERMAL_GENL_EVENT_CDEV_DELETE] = !!ops->cdev_delete; | |
| 139 | + enabled_ops[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = !!ops->cdev_update; | |
| 140 | + enabled_ops[THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = !!ops->gov_change; | |
| 141 | + enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_ADD] = !!ops->threshold_add; | |
| 142 | + enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_DELETE] = !!ops->threshold_delete; | |
| 143 | + enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_FLUSH] = !!ops->threshold_flush; | |
| 144 | + enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_UP] = !!ops->threshold_up; | |
| 145 | + enabled_ops[THERMAL_GENL_EVENT_THRESHOLD_DOWN] = !!ops->threshold_down; | |
| 117 | 146 | } |
| 118 | 147 | |
| 119 | 148 | thermal_error_t thermal_events_handle(struct thermal_handler *th, void *arg) |
| @@ -4,11 +4,20 @@ · 3 unmodified lines | ||
| 4 | 4 | #define __LIBTHERMAL_H |
| 5 | 5 | |
| 6 | 6 | #include <linux/thermal.h> |
| 7 | +#include <sys/types.h> | |
| 7 | 8 | |
| 8 | 9 | #ifndef LIBTHERMAL_API |
| 9 | 10 | #define LIBTHERMAL_API __attribute__((visibility("default"))) |
| 10 | 11 | #endif |
| 11 | 12 | |
| 13 | +#ifndef THERMAL_THRESHOLD_WAY_UP | |
| 14 | +#define THERMAL_THRESHOLD_WAY_UP 0x1 | |
| 15 | +#endif | |
| 16 | + | |
| 17 | +#ifndef THERMAL_THRESHOLD_WAY_DOWN | |
| 18 | +#define THERMAL_THRESHOLD_WAY_DOWN 0x2 | |
| 19 | +#endif | |
| 20 | + | |
| 12 | 21 | #ifdef __cplusplus |
| 13 | 22 | extern "C" { |
| 14 | 23 | #endif |
| @@ -31,6 +40,11 @@ struct thermal_events_ops { · 16 unmodified lines | ||
| 31 | 40 | int (*cdev_delete)(int cdev_id, void *arg); |
| 32 | 41 | int (*cdev_update)(int cdev_id, int cur_state, void *arg); |
| 33 | 42 | int (*gov_change)(int tz_id, const char *gov_name, void *arg); |
| 43 | + int (*threshold_add)(int tz_id, int temperature, int direction, void *arg); | |
| 44 | + int (*threshold_delete)(int tz_id, int temperature, int direction, void *arg); | |
| 45 | + int (*threshold_flush)(int tz_id, void *arg); | |
| 46 | + int (*threshold_up)(int tz_id, int temp, int prev_temp, void *arg); | |
| 47 | + int (*threshold_down)(int tz_id, int temp, int prev_temp, void *arg); | |
| 34 | 48 | }; |
| 35 | 49 | |
| 36 | 50 | struct thermal_ops { |
| @@ -45,12 +59,18 @@ struct thermal_trip { · 8 unmodified lines | ||
| 45 | 59 | int hyst; |
| 46 | 60 | }; |
| 47 | 61 | |
| 62 | +struct thermal_threshold { | |
| 63 | + int temperature; | |
| 64 | + int direction; | |
| 65 | +}; | |
| 66 | + | |
| 48 | 67 | struct thermal_zone { |
| 49 | 68 | int id; |
| 50 | 69 | int temp; |
| 51 | 70 | char name[THERMAL_NAME_LENGTH]; |
| 52 | 71 | char governor[THERMAL_NAME_LENGTH]; |
| 53 | 72 | struct thermal_trip *trip; |
| 73 | + struct thermal_threshold *thresholds; | |
| 54 | 74 | }; |
| 55 | 75 | |
| 56 | 76 | struct thermal_cdev { |
| @@ -74,12 +94,16 @@ typedef int (*cb_tt_t)(struct thermal_trip *, void *); · 17 unmodified lines | ||
| 74 | 94 | |
| 75 | 95 | typedef int (*cb_tc_t)(struct thermal_cdev *, void *); |
| 76 | 96 | |
| 97 | +typedef int (*cb_th_t)(struct thermal_threshold *, void *); | |
| 98 | + | |
| 77 | 99 | LIBTHERMAL_API int for_each_thermal_zone(struct thermal_zone *tz, cb_tz_t cb, void *arg); |
| 78 | 100 | |
| 79 | 101 | LIBTHERMAL_API int for_each_thermal_trip(struct thermal_trip *tt, cb_tt_t cb, void *arg); |
| 80 | 102 | |
| 81 | 103 | LIBTHERMAL_API int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg); |
| 82 | 104 | |
| 105 | +LIBTHERMAL_API int for_each_thermal_threshold(struct thermal_threshold *th, cb_th_t cb, void *arg); | |
| 106 | + | |
| 83 | 107 | LIBTHERMAL_API struct thermal_zone *thermal_zone_find_by_name(struct thermal_zone *tz, |
| 84 | 108 | const char *name); |
| 85 | 109 | |
| @@ -124,6 +148,22 @@ LIBTHERMAL_API thermal_error_t thermal_cmd_get_governor(struct thermal_handler * · 38 unmodified lines | ||
| 124 | 148 | LIBTHERMAL_API thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th, |
| 125 | 149 | struct thermal_zone *tz); |
| 126 | 150 | |
| 151 | +LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_get(struct thermal_handler *th, | |
| 152 | + struct thermal_zone *tz); | |
| 153 | + | |
| 154 | +LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_add(struct thermal_handler *th, | |
| 155 | + struct thermal_zone *tz, | |
| 156 | + int temperature, | |
| 157 | + int direction); | |
| 158 | + | |
| 159 | +LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_delete(struct thermal_handler *th, | |
| 160 | + struct thermal_zone *tz, | |
| 161 | + int temperature, | |
| 162 | + int direction); | |
| 163 | + | |
| 164 | +LIBTHERMAL_API thermal_error_t thermal_cmd_threshold_flush(struct thermal_handler *th, | |
| 165 | + struct thermal_zone *tz); | |
| 166 | + | |
| 127 | 167 | /* |
| 128 | 168 | * Netlink thermal samples |
| 129 | 169 | */ |
| @@ -4,6 +4,7 @@ LIBTHERMAL_0.0.1 { · 3 unmodified lines | ||
| 4 | 4 | for_each_thermal_zone; |
| 5 | 5 | for_each_thermal_trip; |
| 6 | 6 | for_each_thermal_cdev; |
| 7 | + for_each_thermal_threshold; | |
| 7 | 8 | thermal_zone_find_by_name; |
| 8 | 9 | thermal_zone_find_by_id; |
| 9 | 10 | thermal_zone_discover; |
| @@ -17,6 +18,10 @@ LIBTHERMAL_0.0.1 { · 7 unmodified lines | ||
| 17 | 18 | thermal_cmd_get_trip; |
| 18 | 19 | thermal_cmd_get_governor; |
| 19 | 20 | thermal_cmd_get_temp; |
| 21 | + thermal_cmd_threshold_get; | |
| 22 | + thermal_cmd_threshold_add; | |
| 23 | + thermal_cmd_threshold_delete; | |
| 24 | + thermal_cmd_threshold_flush; | |
| 20 | 25 | thermal_sampling_init; |
| 21 | 26 | thermal_sampling_handle; |
| 22 | 27 | thermal_sampling_fd; |
| @@ -1,10 +1,24 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: LGPL-2.1+ |
| 2 | 2 | // Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> |
| 3 | 3 | #include <stdio.h> |
| 4 | +#include <limits.h> | |
| 4 | 5 | #include <thermal.h> |
| 5 | 6 | |
| 6 | 7 | #include "thermal_nl.h" |
| 7 | 8 | |
| 9 | +int for_each_thermal_threshold(struct thermal_threshold *th, cb_th_t cb, void *arg) | |
| 10 | +{ | |
| 11 | + int i, ret = 0; | |
| 12 | + | |
| 13 | + if (!th) | |
| 14 | + return 0; | |
| 15 | + | |
| 16 | + for (i = 0; th[i].temperature != INT_MAX; i++) | |
| 17 | + ret |= cb(&th[i], arg); | |
| 18 | + | |
| 19 | + return ret; | |
| 20 | +} | |
| 21 | + | |
| 8 | 22 | int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg) |
| 9 | 23 | { |
| 10 | 24 | int i, ret = 0; |
| @@ -80,6 +94,9 @@ static int __thermal_zone_discover(struct thermal_zone *tz, void *th) · 69 unmodified lines | ||
| 80 | 94 | if (thermal_cmd_get_trip(th, tz) < 0) |
| 81 | 95 | return -1; |
| 82 | 96 | |
| 97 | + if (thermal_cmd_threshold_get(th, tz)) | |
| 98 | + return -1; | |
| 99 | + | |
| 83 | 100 | if (thermal_cmd_get_governor(th, tz)) |
| 84 | 101 | return -1; |
| 85 | 102 | |
| @@ -3,7 +3,7 @@ · 2 unmodified lines | ||
| 3 | 3 | |
| 4 | 4 | LIBTHERMAL_TOOLS_VERSION = 0 |
| 5 | 5 | LIBTHERMAL_TOOLS_PATCHLEVEL = 0 |
| 6 | −LIBTHERMAL_TOOLS_EXTRAVERSION = 1 | |
| 6 | +LIBTHERMAL_TOOLS_EXTRAVERSION = 2 | |
| 7 | 7 | |
| 8 | 8 | MAKEFLAGS += --no-print-directory |
| 9 | 9 | |
| @@ -38,6 +38,14 @@ struct thermal_data { · 37 unmodified lines | ||
| 38 | 38 | struct thermal_handler *th; |
| 39 | 39 | }; |
| 40 | 40 | |
| 41 | +static int show_threshold(struct thermal_threshold *th, __maybe_unused void *arg) | |
| 42 | +{ | |
| 43 | + INFO("threshold temp=%d, direction=%d\n", | |
| 44 | + th->temperature, th->direction); | |
| 45 | + | |
| 46 | + return 0; | |
| 47 | +} | |
| 48 | + | |
| 41 | 49 | static int show_trip(struct thermal_trip *tt, __maybe_unused void *arg) |
| 42 | 50 | { |
| 43 | 51 | INFO("trip id=%d, type=%d, temp=%d, hyst=%d\n", |
| @@ -70,6 +78,8 @@ static int show_tz(struct thermal_zone *tz, __maybe_unused void *arg) · 26 unmodified lines | ||
| 70 | 78 | |
| 71 | 79 | for_each_thermal_trip(tz->trip, show_trip, NULL); |
| 72 | 80 | |
| 81 | + for_each_thermal_threshold(tz->thresholds, show_threshold, NULL); | |
| 82 | + | |
| 73 | 83 | show_temp(tz, arg); |
| 74 | 84 | |
| 75 | 85 | show_governor(tz, arg); |
| @@ -77,6 +87,30 @@ static int show_tz(struct thermal_zone *tz, __maybe_unused void *arg) · 1 unmodified line | ||
| 77 | 87 | return 0; |
| 78 | 88 | } |
| 79 | 89 | |
| 90 | +static int set_threshold(struct thermal_zone *tz, __maybe_unused void *arg) | |
| 91 | +{ | |
| 92 | + struct thermal_handler *th = arg; | |
| 93 | + int thresholds[] = { 43000, 65000, 49000, 55000, 57000 }; | |
| 94 | + size_t i; | |
| 95 | + | |
| 96 | + INFO("Setting threshold for thermal zone '%s', id=%d\n", tz->name, tz->id); | |
| 97 | + | |
| 98 | + if (thermal_cmd_threshold_flush(th, tz)) { | |
| 99 | + ERROR("Failed to flush all previous thresholds\n"); | |
| 100 | + return -1; | |
| 101 | + } | |
| 102 | + | |
| 103 | + for (i = 0; i < sizeof(thresholds) / sizeof(thresholds[0]); i++) | |
| 104 | + if (thermal_cmd_threshold_add(th, tz, thresholds[i], | |
| 105 | + THERMAL_THRESHOLD_WAY_UP | | |
| 106 | + THERMAL_THRESHOLD_WAY_DOWN)) { | |
| 107 | + ERROR("Failed to set threshold\n"); | |
| 108 | + return -1; | |
| 109 | + } | |
| 110 | + | |
| 111 | + return 0; | |
| 112 | +} | |
| 113 | + | |
| 80 | 114 | static int tz_create(const char *name, int tz_id, __maybe_unused void *arg) |
| 81 | 115 | { |
| 82 | 116 | INFO("Thermal zone '%s'/%d created\n", name, tz_id); |
| @@ -197,20 +231,62 @@ static int gov_change(int tz_id, const char *name, __maybe_unused void *arg) · 114 unmodified lines | ||
| 197 | 231 | return 0; |
| 198 | 232 | } |
| 199 | 233 | |
| 234 | +static int threshold_add(int tz_id, int temp, int direction, __maybe_unused void *arg) | |
| 235 | +{ | |
| 236 | + INFO("Threshold added tz_id=%d: temp=%d, direction=%d\n", tz_id, temp, direction); | |
| 237 | + | |
| 238 | + return 0; | |
| 239 | +} | |
| 240 | + | |
| 241 | +static int threshold_delete(int tz_id, int temp, int direction, __maybe_unused void *arg) | |
| 242 | +{ | |
| 243 | + INFO("Threshold deleted tz_id=%d: temp=%d, direction=%d\n", tz_id, temp, direction); | |
| 244 | + | |
| 245 | + return 0; | |
| 246 | +} | |
| 247 | + | |
| 248 | +static int threshold_flush(int tz_id, __maybe_unused void *arg) | |
| 249 | +{ | |
| 250 | + INFO("Thresholds flushed tz_id=%d\n", tz_id); | |
| 251 | + | |
| 252 | + return 0; | |
| 253 | +} | |
| 254 | + | |
| 255 | +static int threshold_up(int tz_id, int temp, int prev_temp, __maybe_unused void *arg) | |
| 256 | +{ | |
| 257 | + INFO("Threshold crossed way up tz_id=%d: temp=%d, prev_temp=%d\n", | |
| 258 | + tz_id, temp, prev_temp); | |
| 259 | + | |
| 260 | + return 0; | |
| 261 | +} | |
| 262 | + | |
| 263 | +static int threshold_down(int tz_id, int temp, int prev_temp, __maybe_unused void *arg) | |
| 264 | +{ | |
| 265 | + INFO("Threshold crossed way down tz_id=%d: temp=%d, prev_temp=%d\n", | |
| 266 | + tz_id, temp, prev_temp); | |
| 267 | + | |
| 268 | + return 0; | |
| 269 | +} | |
| 270 | + | |
| 200 | 271 | static struct thermal_ops ops = { |
| 201 | − .events.tz_create = tz_create, | |
| 202 | − .events.tz_delete = tz_delete, | |
| 203 | − .events.tz_disable = tz_disable, | |
| 204 | − .events.tz_enable = tz_enable, | |
| 205 | − .events.trip_high = trip_high, | |
| 206 | − .events.trip_low = trip_low, | |
| 207 | − .events.trip_add = trip_add, | |
| 208 | − .events.trip_delete = trip_delete, | |
| 209 | − .events.trip_change = trip_change, | |
| 210 | − .events.cdev_add = cdev_add, | |
| 211 | − .events.cdev_delete = cdev_delete, | |
| 212 | − .events.cdev_update = cdev_update, | |
| 213 | − .events.gov_change = gov_change | |
| 272 | + .events.tz_create = tz_create, | |
| 273 | + .events.tz_delete = tz_delete, | |
| 274 | + .events.tz_disable = tz_disable, | |
| 275 | + .events.tz_enable = tz_enable, | |
| 276 | + .events.trip_high = trip_high, | |
| 277 | + .events.trip_low = trip_low, | |
| 278 | + .events.trip_add = trip_add, | |
| 279 | + .events.trip_delete = trip_delete, | |
| 280 | + .events.trip_change = trip_change, | |
| 281 | + .events.cdev_add = cdev_add, | |
| 282 | + .events.cdev_delete = cdev_delete, | |
| 283 | + .events.cdev_update = cdev_update, | |
| 284 | + .events.gov_change = gov_change, | |
| 285 | + .events.threshold_add = threshold_add, | |
| 286 | + .events.threshold_delete = threshold_delete, | |
| 287 | + .events.threshold_flush = threshold_flush, | |
| 288 | + .events.threshold_up = threshold_up, | |
| 289 | + .events.threshold_down = threshold_down, | |
| 214 | 290 | }; |
| 215 | 291 | |
| 216 | 292 | static int thermal_event(__maybe_unused int fd, __maybe_unused void *arg) |
| @@ -280,6 +356,7 @@ enum { · 63 unmodified lines | ||
| 280 | 356 | THERMAL_ENGINE_DAEMON_ERROR, |
| 281 | 357 | THERMAL_ENGINE_LOG_ERROR, |
| 282 | 358 | THERMAL_ENGINE_THERMAL_ERROR, |
| 359 | + THERMAL_ENGINE_THRESHOLD_ERROR, | |
| 283 | 360 | THERMAL_ENGINE_MAINLOOP_ERROR, |
| 284 | 361 | }; |
| 285 | 362 | |
| @@ -318,6 +395,8 @@ int main(int argc, char *argv[]) · 32 unmodified lines | ||
| 318 | 395 | return THERMAL_ENGINE_THERMAL_ERROR; |
| 319 | 396 | } |
| 320 | 397 | |
| 398 | + for_each_thermal_zone(td.tz, set_threshold, td.th); | |
| 399 | + | |
| 321 | 400 | for_each_thermal_zone(td.tz, show_tz, td.th); |
| 322 | 401 | |
| 323 | 402 | if (mainloop_init()) { |