Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

thermal: core: Introduce thermal_cooling_device_update()

Introduce a core thermal API function, thermal_cooling_device_update(),
for updating the max_state value for a cooling device and rearranging
its statistics in sysfs after a possible change of its ->get_max_state()
callback return value.

That callback is now invoked only once, during cooling device
registration, to populate the max_state field in the cooling device
object, so if its return value changes, it needs to be invoked again
and the new return value needs to be stored as max_state. Moreover,
the statistics presented in sysfs need to be rearranged in general,
because there may not be enough room in them to store data for all
of the possible states (in the case when max_state grows).

The new function takes care of that (and some other minor things
related to it), but some extra locking and lockdep annotations are
added in several places too to protect against crashes in the cases
when the statistics are not present or when a stale max_state value
might be used by sysfs attributes.

Note that the actual user of the new function will be added separately.

Link: https://lore.kernel.org/linux-pm/53ec1f06f61c984100868926f282647e57ecfb2d.camel@intel.com/
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Tested-by: Zhang Rui <rui.zhang@intel.com>
Reviewed-by: Zhang Rui <rui.zhang@intel.com>

+150 -10
+82 -1
drivers/thermal/thermal_core.c
··· 613 613 struct thermal_instance *pos; 614 614 struct thermal_zone_device *pos1; 615 615 struct thermal_cooling_device *pos2; 616 + bool upper_no_limit; 616 617 int result; 617 618 618 619 if (trip >= tz->num_trips || trip < 0) ··· 633 632 634 633 /* lower default 0, upper default max_state */ 635 634 lower = lower == THERMAL_NO_LIMIT ? 0 : lower; 636 - upper = upper == THERMAL_NO_LIMIT ? cdev->max_state : upper; 635 + 636 + if (upper == THERMAL_NO_LIMIT) { 637 + upper = cdev->max_state; 638 + upper_no_limit = true; 639 + } else { 640 + upper_no_limit = false; 641 + } 637 642 638 643 if (lower > upper || upper > cdev->max_state) 639 644 return -EINVAL; ··· 651 644 dev->cdev = cdev; 652 645 dev->trip = trip; 653 646 dev->upper = upper; 647 + dev->upper_no_limit = upper_no_limit; 654 648 dev->lower = lower; 655 649 dev->target = THERMAL_NO_TARGET; 656 650 dev->weight = weight; ··· 1064 1056 1065 1057 return false; 1066 1058 } 1059 + 1060 + /** 1061 + * thermal_cooling_device_update - Update a cooling device object 1062 + * @cdev: Target cooling device. 1063 + * 1064 + * Update @cdev to reflect a change of the underlying hardware or platform. 1065 + * 1066 + * Must be called when the maximum cooling state of @cdev becomes invalid and so 1067 + * its .get_max_state() callback needs to be run to produce the new maximum 1068 + * cooling state value. 1069 + */ 1070 + void thermal_cooling_device_update(struct thermal_cooling_device *cdev) 1071 + { 1072 + struct thermal_instance *ti; 1073 + unsigned long state; 1074 + 1075 + if (IS_ERR_OR_NULL(cdev)) 1076 + return; 1077 + 1078 + /* 1079 + * Hold thermal_list_lock throughout the update to prevent the device 1080 + * from going away while being updated. 1081 + */ 1082 + mutex_lock(&thermal_list_lock); 1083 + 1084 + if (!thermal_cooling_device_present(cdev)) 1085 + goto unlock_list; 1086 + 1087 + /* 1088 + * Update under the cdev lock to prevent the state from being set beyond 1089 + * the new limit concurrently. 1090 + */ 1091 + mutex_lock(&cdev->lock); 1092 + 1093 + if (cdev->ops->get_max_state(cdev, &cdev->max_state)) 1094 + goto unlock; 1095 + 1096 + thermal_cooling_device_stats_reinit(cdev); 1097 + 1098 + list_for_each_entry(ti, &cdev->thermal_instances, cdev_node) { 1099 + if (ti->upper == cdev->max_state) 1100 + continue; 1101 + 1102 + if (ti->upper < cdev->max_state) { 1103 + if (ti->upper_no_limit) 1104 + ti->upper = cdev->max_state; 1105 + 1106 + continue; 1107 + } 1108 + 1109 + ti->upper = cdev->max_state; 1110 + if (ti->lower > ti->upper) 1111 + ti->lower = ti->upper; 1112 + 1113 + if (ti->target == THERMAL_NO_TARGET) 1114 + continue; 1115 + 1116 + if (ti->target > ti->upper) 1117 + ti->target = ti->upper; 1118 + } 1119 + 1120 + if (cdev->ops->get_cur_state(cdev, &state) || state > cdev->max_state) 1121 + goto unlock; 1122 + 1123 + thermal_cooling_device_stats_update(cdev, state); 1124 + 1125 + unlock: 1126 + mutex_unlock(&cdev->lock); 1127 + 1128 + unlock_list: 1129 + mutex_unlock(&thermal_list_lock); 1130 + } 1131 + EXPORT_SYMBOL_GPL(thermal_cooling_device_update); 1067 1132 1068 1133 static void __unbind(struct thermal_zone_device *tz, int mask, 1069 1134 struct thermal_cooling_device *cdev)
+2
drivers/thermal/thermal_core.h
··· 101 101 struct list_head tz_node; /* node in tz->thermal_instances */ 102 102 struct list_head cdev_node; /* node in cdev->thermal_instances */ 103 103 unsigned int weight; /* The weight of the cooling device */ 104 + bool upper_no_limit; 104 105 }; 105 106 106 107 #define to_thermal_zone(_dev) \ ··· 128 127 void thermal_zone_destroy_device_groups(struct thermal_zone_device *); 129 128 void thermal_cooling_device_setup_sysfs(struct thermal_cooling_device *); 130 129 void thermal_cooling_device_destroy_sysfs(struct thermal_cooling_device *cdev); 130 + void thermal_cooling_device_stats_reinit(struct thermal_cooling_device *cdev); 131 131 /* used only at binding time */ 132 132 ssize_t trip_point_show(struct device *, struct device_attribute *, char *); 133 133 ssize_t weight_show(struct device *, struct device_attribute *, char *);
+65 -9
drivers/thermal/thermal_sysfs.c
··· 685 685 { 686 686 struct cooling_dev_stats *stats = cdev->stats; 687 687 688 + lockdep_assert_held(&cdev->lock); 689 + 688 690 if (!stats) 689 691 return; 690 692 ··· 708 706 struct device_attribute *attr, char *buf) 709 707 { 710 708 struct thermal_cooling_device *cdev = to_cooling_device(dev); 711 - struct cooling_dev_stats *stats = cdev->stats; 712 - int ret; 709 + struct cooling_dev_stats *stats; 710 + int ret = 0; 711 + 712 + mutex_lock(&cdev->lock); 713 + 714 + stats = cdev->stats; 715 + if (!stats) 716 + goto unlock; 713 717 714 718 spin_lock(&stats->lock); 715 719 ret = sprintf(buf, "%u\n", stats->total_trans); 716 720 spin_unlock(&stats->lock); 721 + 722 + unlock: 723 + mutex_unlock(&cdev->lock); 717 724 718 725 return ret; 719 726 } ··· 732 721 char *buf) 733 722 { 734 723 struct thermal_cooling_device *cdev = to_cooling_device(dev); 735 - struct cooling_dev_stats *stats = cdev->stats; 724 + struct cooling_dev_stats *stats; 736 725 ssize_t len = 0; 737 726 int i; 738 727 728 + mutex_lock(&cdev->lock); 729 + 730 + stats = cdev->stats; 731 + if (!stats) 732 + goto unlock; 733 + 739 734 spin_lock(&stats->lock); 735 + 740 736 update_time_in_state(stats); 741 737 742 738 for (i = 0; i <= cdev->max_state; i++) { ··· 751 733 ktime_to_ms(stats->time_in_state[i])); 752 734 } 753 735 spin_unlock(&stats->lock); 736 + 737 + unlock: 738 + mutex_unlock(&cdev->lock); 754 739 755 740 return len; 756 741 } ··· 763 742 size_t count) 764 743 { 765 744 struct thermal_cooling_device *cdev = to_cooling_device(dev); 766 - struct cooling_dev_stats *stats = cdev->stats; 767 - int i, states = cdev->max_state + 1; 745 + struct cooling_dev_stats *stats; 746 + int i, states; 747 + 748 + mutex_lock(&cdev->lock); 749 + 750 + stats = cdev->stats; 751 + if (!stats) 752 + goto unlock; 753 + 754 + states = cdev->max_state + 1; 768 755 769 756 spin_lock(&stats->lock); 770 757 ··· 786 757 787 758 spin_unlock(&stats->lock); 788 759 760 + unlock: 761 + mutex_unlock(&cdev->lock); 762 + 789 763 return count; 790 764 } 791 765 ··· 796 764 struct device_attribute *attr, char *buf) 797 765 { 798 766 struct thermal_cooling_device *cdev = to_cooling_device(dev); 799 - struct cooling_dev_stats *stats = cdev->stats; 767 + struct cooling_dev_stats *stats; 800 768 ssize_t len = 0; 801 769 int i, j; 770 + 771 + mutex_lock(&cdev->lock); 772 + 773 + stats = cdev->stats; 774 + if (!stats) { 775 + len = -ENODATA; 776 + goto unlock; 777 + } 802 778 803 779 len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n"); 804 780 len += snprintf(buf + len, PAGE_SIZE - len, " : "); ··· 815 775 break; 816 776 len += snprintf(buf + len, PAGE_SIZE - len, "state%2u ", i); 817 777 } 818 - if (len >= PAGE_SIZE) 819 - return PAGE_SIZE; 778 + if (len >= PAGE_SIZE) { 779 + len = PAGE_SIZE; 780 + goto unlock; 781 + } 820 782 821 783 len += snprintf(buf + len, PAGE_SIZE - len, "\n"); 822 784 ··· 841 799 842 800 if (len >= PAGE_SIZE) { 843 801 pr_warn_once("Thermal transition table exceeds PAGE_SIZE. Disabling\n"); 844 - return -EFBIG; 802 + len = -EFBIG; 845 803 } 804 + 805 + unlock: 806 + mutex_unlock(&cdev->lock); 807 + 846 808 return len; 847 809 } 848 810 ··· 876 830 unsigned long states = cdev->max_state + 1; 877 831 int var; 878 832 833 + lockdep_assert_held(&cdev->lock); 834 + 879 835 var = sizeof(*stats); 880 836 var += sizeof(*stats->time_in_state) * states; 881 837 var += sizeof(*stats->trans_table) * states * states; ··· 903 855 904 856 static void cooling_device_stats_destroy(struct thermal_cooling_device *cdev) 905 857 { 858 + lockdep_assert_held(&cdev->lock); 859 + 906 860 kfree(cdev->stats); 907 861 cdev->stats = NULL; 908 862 } ··· 927 877 void thermal_cooling_device_destroy_sysfs(struct thermal_cooling_device *cdev) 928 878 { 929 879 cooling_device_stats_destroy(cdev); 880 + } 881 + 882 + void thermal_cooling_device_stats_reinit(struct thermal_cooling_device *cdev) 883 + { 884 + cooling_device_stats_destroy(cdev); 885 + cooling_device_stats_setup(cdev); 930 886 } 931 887 932 888 /* these helper will be used only at the time of bindig */
+1
include/linux/thermal.h
··· 384 384 struct device_node *np, 385 385 char *type, void *devdata, 386 386 const struct thermal_cooling_device_ops *ops); 387 + void thermal_cooling_device_update(struct thermal_cooling_device *); 387 388 void thermal_cooling_device_unregister(struct thermal_cooling_device *); 388 389 struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name); 389 390 int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);