Commit 5c897a9a authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge back earlier thermal control material for v6.10.

parents b552f63c 0dbf6087
......@@ -17,10 +17,13 @@
static int get_trip_level(struct thermal_zone_device *tz)
{
const struct thermal_trip *trip, *level_trip = NULL;
const struct thermal_trip *level_trip = NULL;
const struct thermal_trip_desc *td;
int trip_level = -1;
for_each_trip(tz, trip) {
for_each_trip_desc(tz, td) {
const struct thermal_trip *trip = &td->trip;
if (trip->temperature >= tz->temperature)
continue;
......
......@@ -496,9 +496,11 @@ static void get_governor_trips(struct thermal_zone_device *tz,
const struct thermal_trip *first_passive = NULL;
const struct thermal_trip *last_passive = NULL;
const struct thermal_trip *last_active = NULL;
const struct thermal_trip *trip;
const struct thermal_trip_desc *td;
for_each_trip_desc(tz, td) {
const struct thermal_trip *trip = &td->trip;
for_each_trip(tz, trip) {
switch (trip->type) {
case THERMAL_TRIP_PASSIVE:
if (!first_passive) {
......
......@@ -32,7 +32,6 @@ static unsigned long get_target_state(struct thermal_instance *instance,
{
struct thermal_cooling_device *cdev = instance->cdev;
unsigned long cur_state;
unsigned long next_target;
/*
* We keep this instance the way it is by default.
......@@ -40,32 +39,26 @@ static unsigned long get_target_state(struct thermal_instance *instance,
* cdev in use to determine the next_target.
*/
cdev->ops->get_cur_state(cdev, &cur_state);
next_target = instance->target;
dev_dbg(&cdev->device, "cur_state=%ld\n", cur_state);
if (!instance->initialized) {
if (throttle) {
next_target = clamp((cur_state + 1), instance->lower, instance->upper);
} else {
next_target = THERMAL_NO_TARGET;
}
if (throttle)
return clamp(cur_state + 1, instance->lower, instance->upper);
return next_target;
return THERMAL_NO_TARGET;
}
if (throttle) {
if (trend == THERMAL_TREND_RAISING)
next_target = clamp((cur_state + 1), instance->lower, instance->upper);
} else {
if (trend == THERMAL_TREND_DROPPING) {
if (cur_state <= instance->lower)
next_target = THERMAL_NO_TARGET;
else
next_target = clamp((cur_state - 1), instance->lower, instance->upper);
}
return clamp(cur_state + 1, instance->lower, instance->upper);
} else if (trend == THERMAL_TREND_DROPPING) {
if (cur_state <= instance->lower)
return THERMAL_NO_TARGET;
return clamp(cur_state - 1, instance->lower, instance->upper);
}
return next_target;
return instance->target;
}
static void thermal_zone_trip_update(struct thermal_zone_device *tz,
......@@ -99,15 +92,13 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz,
if (instance->initialized && old_target == instance->target)
continue;
if (old_target == THERMAL_NO_TARGET &&
instance->target != THERMAL_NO_TARGET) {
/* Activate a passive thermal instance */
if (trip->type == THERMAL_TRIP_PASSIVE)
if (trip->type == THERMAL_TRIP_PASSIVE) {
/* If needed, update the status of passive polling. */
if (old_target == THERMAL_NO_TARGET &&
instance->target != THERMAL_NO_TARGET)
tz->passive++;
} else if (old_target != THERMAL_NO_TARGET &&
instance->target == THERMAL_NO_TARGET) {
/* Deactivate a passive thermal instance */
if (trip->type == THERMAL_TRIP_PASSIVE)
else if (old_target != THERMAL_NO_TARGET &&
instance->target == THERMAL_NO_TARGET)
tz->passive--;
}
......
......@@ -309,7 +309,7 @@ static int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **ps
if (knob->type == ACPI_TYPE_STRING) {
memset(&psvt->limit, 0, sizeof(u64));
strncpy(psvt->limit.string, psvt_ptr->limit.str_ptr, knob->string.length);
strscpy(psvt->limit.string, psvt_ptr->limit.str_ptr, ACPI_LIMIT_STR_MAX_LEN);
} else {
psvt->limit.integer = psvt_ptr->limit.integer;
}
......@@ -468,7 +468,7 @@ static int fill_psvt(char __user *ubuf)
psvt_user[i].unlimit_coeff = psvts[i].unlimit_coeff;
psvt_user[i].control_knob_type = psvts[i].control_knob_type;
if (psvt_user[i].control_knob_type == ACPI_TYPE_STRING)
strncpy(psvt_user[i].limit.string, psvts[i].limit.string,
strscpy(psvt_user[i].limit.string, psvts[i].limit.string,
ACPI_LIMIT_STR_MAX_LEN);
else
psvt_user[i].limit.integer = psvts[i].limit.integer;
......
......@@ -159,6 +159,7 @@ struct hfi_cpu_info {
static DEFINE_PER_CPU(struct hfi_cpu_info, hfi_cpu_info) = { .index = -1 };
static int max_hfi_instances;
static int hfi_clients_nr;
static struct hfi_instance *hfi_instances;
static struct hfi_features hfi_features;
......@@ -477,8 +478,11 @@ void intel_hfi_online(unsigned int cpu)
enable:
cpumask_set_cpu(cpu, hfi_instance->cpus);
/* Enable this HFI instance if this is its first online CPU. */
if (cpumask_weight(hfi_instance->cpus) == 1) {
/*
* Enable this HFI instance if this is its first online CPU and
* there are user-space clients of thermal events.
*/
if (cpumask_weight(hfi_instance->cpus) == 1 && hfi_clients_nr > 0) {
hfi_set_hw_table(hfi_instance);
hfi_enable();
}
......@@ -573,18 +577,33 @@ static __init int hfi_parse_features(void)
return 0;
}
static void hfi_do_enable(void)
/*
* If concurrency is not prevented by other means, the HFI enable/disable
* routines must be called under hfi_instance_lock."
*/
static void hfi_enable_instance(void *ptr)
{
hfi_set_hw_table(ptr);
hfi_enable();
}
static void hfi_disable_instance(void *ptr)
{
hfi_disable();
}
static void hfi_syscore_resume(void)
{
/* This code runs only on the boot CPU. */
struct hfi_cpu_info *info = &per_cpu(hfi_cpu_info, 0);
struct hfi_instance *hfi_instance = info->hfi_instance;
/* No locking needed. There is no concurrency with CPU online. */
hfi_set_hw_table(hfi_instance);
hfi_enable();
if (hfi_clients_nr > 0)
hfi_enable_instance(hfi_instance);
}
static int hfi_do_disable(void)
static int hfi_syscore_suspend(void)
{
/* No locking needed. There is no concurrency with CPU offline. */
hfi_disable();
......@@ -593,8 +612,58 @@ static int hfi_do_disable(void)
}
static struct syscore_ops hfi_pm_ops = {
.resume = hfi_do_enable,
.suspend = hfi_do_disable,
.resume = hfi_syscore_resume,
.suspend = hfi_syscore_suspend,
};
static int hfi_thermal_notify(struct notifier_block *nb, unsigned long state,
void *_notify)
{
struct thermal_genl_notify *notify = _notify;
struct hfi_instance *hfi_instance;
smp_call_func_t func = NULL;
unsigned int cpu;
int i;
if (notify->mcgrp != THERMAL_GENL_EVENT_GROUP)
return NOTIFY_DONE;
if (state != THERMAL_NOTIFY_BIND && state != THERMAL_NOTIFY_UNBIND)
return NOTIFY_DONE;
mutex_lock(&hfi_instance_lock);
switch (state) {
case THERMAL_NOTIFY_BIND:
if (++hfi_clients_nr == 1)
func = hfi_enable_instance;
break;
case THERMAL_NOTIFY_UNBIND:
if (--hfi_clients_nr == 0)
func = hfi_disable_instance;
break;
}
if (!func)
goto out;
for (i = 0; i < max_hfi_instances; i++) {
hfi_instance = &hfi_instances[i];
if (cpumask_empty(hfi_instance->cpus))
continue;
cpu = cpumask_any(hfi_instance->cpus);
smp_call_function_single(cpu, func, hfi_instance, true);
}
out:
mutex_unlock(&hfi_instance_lock);
return NOTIFY_OK;
}
static struct notifier_block hfi_thermal_nb = {
.notifier_call = hfi_thermal_notify,
};
void __init intel_hfi_init(void)
......@@ -628,10 +697,22 @@ void __init intel_hfi_init(void)
if (!hfi_updates_wq)
goto err_nomem;
/*
* Both thermal core and Intel HFI can not be build as modules.
* As kernel build-in drivers they are initialized before user-space
* starts, hence we can not miss BIND/UNBIND events when applications
* add/remove thermal multicast group to/from a netlink socket.
*/
if (thermal_genl_register_notifier(&hfi_thermal_nb))
goto err_nl_notif;
register_syscore_ops(&hfi_pm_ops);
return;
err_nl_notif:
destroy_workqueue(hfi_updates_wq);
err_nomem:
for (j = 0; j < i; ++j) {
hfi_instance = &hfi_instances[j];
......
......@@ -15,6 +15,7 @@
#include <linux/slab.h>
#include <linux/kdev_t.h>
#include <linux/idr.h>
#include <linux/list_sort.h>
#include <linux/thermal.h>
#include <linux/reboot.h>
#include <linux/string.h>
......@@ -361,49 +362,48 @@ static void handle_critical_trips(struct thermal_zone_device *tz,
}
static void handle_thermal_trip(struct thermal_zone_device *tz,
struct thermal_trip *trip)
struct thermal_trip_desc *td,
struct list_head *way_up_list,
struct list_head *way_down_list)
{
const struct thermal_trip *trip = &td->trip;
int old_threshold;
if (trip->temperature == THERMAL_TEMP_INVALID)
return;
if (tz->last_temperature == THERMAL_TEMP_INVALID) {
/* Initialization. */
trip->threshold = trip->temperature;
if (tz->temperature >= trip->threshold)
trip->threshold -= trip->hysteresis;
} else if (tz->last_temperature < trip->threshold) {
/*
* If the trip temperature or hysteresis has been updated recently,
* the threshold needs to be computed again using the new values.
* However, its initial value still reflects the old ones and that
* is what needs to be compared with the previous zone temperature
* to decide which action to take.
*/
old_threshold = td->threshold;
td->threshold = trip->temperature;
if (tz->last_temperature >= old_threshold &&
tz->last_temperature != THERMAL_TEMP_INVALID) {
/*
* The trip threshold is equal to the trip temperature, unless
* the latter has changed in the meantime. In either case,
* the trip is crossed if the current zone temperature is at
* least equal to its temperature, but otherwise ensure that
* the threshold and the trip temperature will be equal.
* Mitigation is under way, so it needs to stop if the zone
* temperature falls below the low temperature of the trip.
* In that case, the trip temperature becomes the new threshold.
*/
if (tz->temperature >= trip->temperature) {
thermal_notify_tz_trip_up(tz, trip);
thermal_debug_tz_trip_up(tz, trip);
trip->threshold = trip->temperature - trip->hysteresis;
if (tz->temperature < trip->temperature - trip->hysteresis) {
list_add(&td->notify_list_node, way_down_list);
td->notify_temp = trip->temperature - trip->hysteresis;
} else {
trip->threshold = trip->temperature;
td->threshold -= trip->hysteresis;
}
} else {
} else if (tz->temperature >= trip->temperature) {
/*
* The previous zone temperature was above or equal to the trip
* threshold, which would be equal to the "low temperature" of
* the trip (its temperature minus its hysteresis), unless the
* trip temperature or hysteresis had changed. In either case,
* the trip is crossed if the current zone temperature is below
* the low temperature of the trip, but otherwise ensure that
* the trip threshold will be equal to the low temperature of
* the trip.
* There is no mitigation under way, so it needs to be started
* if the zone temperature exceeds the trip one. The new
* threshold is then set to the low temperature of the trip.
*/
if (tz->temperature < trip->temperature - trip->hysteresis) {
thermal_notify_tz_trip_down(tz, trip);
thermal_debug_tz_trip_down(tz, trip);
trip->threshold = trip->temperature;
} else {
trip->threshold = trip->temperature - trip->hysteresis;
}
list_add_tail(&td->notify_list_node, way_up_list);
td->notify_temp = trip->temperature;
td->threshold -= trip->hysteresis;
}
if (trip->type == THERMAL_TRIP_CRITICAL || trip->type == THERMAL_TRIP_HOT)
......@@ -455,10 +455,24 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz)
pos->initialized = false;
}
static int thermal_trip_notify_cmp(void *ascending, const struct list_head *a,
const struct list_head *b)
{
struct thermal_trip_desc *tda = container_of(a, struct thermal_trip_desc,
notify_list_node);
struct thermal_trip_desc *tdb = container_of(b, struct thermal_trip_desc,
notify_list_node);
int ret = tdb->notify_temp - tda->notify_temp;
return ascending ? ret : -ret;
}
void __thermal_zone_device_update(struct thermal_zone_device *tz,
enum thermal_notify_event event)
{
struct thermal_trip *trip;
struct thermal_trip_desc *td;
LIST_HEAD(way_down_list);
LIST_HEAD(way_up_list);
if (tz->suspended)
return;
......@@ -472,8 +486,20 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz,
tz->notify_event = event;
for_each_trip(tz, trip)
handle_thermal_trip(tz, trip);
for_each_trip_desc(tz, td)
handle_thermal_trip(tz, td, &way_up_list, &way_down_list);
list_sort(&way_up_list, &way_up_list, thermal_trip_notify_cmp);
list_for_each_entry(td, &way_up_list, notify_list_node) {
thermal_notify_tz_trip_up(tz, &td->trip);
thermal_debug_tz_trip_up(tz, &td->trip);
}
list_sort(NULL, &way_down_list, thermal_trip_notify_cmp);
list_for_each_entry(td, &way_down_list, notify_list_node) {
thermal_notify_tz_trip_down(tz, &td->trip);
thermal_debug_tz_trip_down(tz, &td->trip);
}
monitor_thermal_zone(tz);
}
......@@ -766,7 +792,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
if (trip_index < 0 || trip_index >= tz->num_trips)
return -EINVAL;
return thermal_bind_cdev_to_trip(tz, &tz->trips[trip_index], cdev,
return thermal_bind_cdev_to_trip(tz, &tz->trips[trip_index].trip, cdev,
upper, lower, weight);
}
EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device);
......@@ -825,7 +851,7 @@ int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
if (trip_index < 0 || trip_index >= tz->num_trips)
return -EINVAL;
return thermal_unbind_cdev_from_trip(tz, &tz->trips[trip_index], cdev);
return thermal_unbind_cdev_from_trip(tz, &tz->trips[trip_index].trip, cdev);
}
EXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device);
......@@ -1221,16 +1247,19 @@ static void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms
int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp)
{
int i, ret = -EINVAL;
const struct thermal_trip_desc *td;
int ret = -EINVAL;
if (tz->ops.get_crit_temp)
return tz->ops.get_crit_temp(tz, temp);
mutex_lock(&tz->lock);
for (i = 0; i < tz->num_trips; i++) {
if (tz->trips[i].type == THERMAL_TRIP_CRITICAL) {
*temp = tz->trips[i].temperature;
for_each_trip_desc(tz, td) {
const struct thermal_trip *trip = &td->trip;
if (trip->type == THERMAL_TRIP_CRITICAL) {
*temp = trip->temperature;
ret = 0;
break;
}
......@@ -1274,7 +1303,9 @@ thermal_zone_device_register_with_trips(const char *type,
const struct thermal_zone_params *tzp,
int passive_delay, int polling_delay)
{
const struct thermal_trip *trip = trips;
struct thermal_zone_device *tz;
struct thermal_trip_desc *td;
int id;
int result;
struct thermal_governor *governor;
......@@ -1339,7 +1370,8 @@ thermal_zone_device_register_with_trips(const char *type,
tz->device.class = thermal_class;
tz->devdata = devdata;
tz->num_trips = num_trips;
memcpy(tz->trips, trips, num_trips * sizeof(*trips));
for_each_trip_desc(tz, td)
td->trip = *trip++;
thermal_set_delay_jiffies(&tz->passive_delay_jiffies, passive_delay);
thermal_set_delay_jiffies(&tz->polling_delay_jiffies, polling_delay);
......
......@@ -15,6 +15,118 @@
#include "thermal_netlink.h"
#include "thermal_debugfs.h"
struct thermal_trip_desc {
struct thermal_trip trip;
struct list_head notify_list_node;
int notify_temp;
int threshold;
};
/**
* struct thermal_governor - structure that holds thermal governor information
* @name: name of the governor
* @bind_to_tz: callback called when binding to a thermal zone. If it
* returns 0, the governor is bound to the thermal zone,
* otherwise it fails.
* @unbind_from_tz: callback called when a governor is unbound from a
* thermal zone.
* @throttle: callback called for every trip point even if temperature is
* below the trip point temperature
* @update_tz: callback called when thermal zone internals have changed, e.g.
* thermal cooling instance was added/removed
* @governor_list: node in thermal_governor_list (in thermal_core.c)
*/
struct thermal_governor {
const char *name;
int (*bind_to_tz)(struct thermal_zone_device *tz);
void (*unbind_from_tz)(struct thermal_zone_device *tz);
int (*throttle)(struct thermal_zone_device *tz,
const struct thermal_trip *trip);
void (*update_tz)(struct thermal_zone_device *tz,
enum thermal_notify_event reason);
struct list_head governor_list;
};
/**
* struct thermal_zone_device - structure for a thermal zone
* @id: unique id number for each thermal zone
* @type: the thermal zone device type
* @device: &struct device for this thermal zone
* @removal: removal completion
* @trip_temp_attrs: attributes for trip points for sysfs: trip temperature
* @trip_type_attrs: attributes for trip points for sysfs: trip type
* @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis
* @mode: current mode of this thermal zone
* @devdata: private pointer for device private data
* @num_trips: number of trip points the thermal zone supports
* @passive_delay_jiffies: number of jiffies to wait between polls when
* performing passive cooling.
* @polling_delay_jiffies: number of jiffies to wait between polls when
* checking whether trip points have been crossed (0 for
* interrupt driven systems)
* @temperature: current temperature. This is only for core code,
* drivers should use thermal_zone_get_temp() to get the
* current temperature
* @last_temperature: previous temperature read
* @emul_temperature: emulated temperature when using CONFIG_THERMAL_EMULATION
* @passive: 1 if you've crossed a passive trip point, 0 otherwise.
* @prev_low_trip: the low current temperature if you've crossed a passive
trip point.
* @prev_high_trip: the above current temperature if you've crossed a
passive trip point.
* @need_update: if equals 1, thermal_zone_device_update needs to be invoked.
* @ops: operations this &thermal_zone_device supports
* @tzp: thermal zone parameters
* @governor: pointer to the governor for this thermal zone
* @governor_data: private pointer for governor data
* @thermal_instances: list of &struct thermal_instance of this thermal zone
* @ida: &struct ida to generate unique id for this zone's cooling
* devices
* @lock: lock to protect thermal_instances list
* @node: node in thermal_tz_list (in thermal_core.c)
* @poll_queue: delayed work for polling
* @notify_event: Last notification event
* @suspended: thermal zone suspend indicator
* @trips: array of struct thermal_trip objects
*/
struct thermal_zone_device {
int id;
char type[THERMAL_NAME_LENGTH];
struct device device;
struct completion removal;
struct attribute_group trips_attribute_group;
struct thermal_attr *trip_temp_attrs;
struct thermal_attr *trip_type_attrs;
struct thermal_attr *trip_hyst_attrs;
enum thermal_device_mode mode;
void *devdata;
int num_trips;
unsigned long passive_delay_jiffies;
unsigned long polling_delay_jiffies;
int temperature;
int last_temperature;
int emul_temperature;
int passive;
int prev_low_trip;
int prev_high_trip;
atomic_t need_update;
struct thermal_zone_device_ops ops;
struct thermal_zone_params *tzp;
struct thermal_governor *governor;
void *governor_data;
struct list_head thermal_instances;
struct ida ida;
struct mutex lock;
struct list_head node;
struct delayed_work poll_queue;
enum thermal_notify_event notify_event;
bool suspended;
#ifdef CONFIG_THERMAL_DEBUGFS
struct thermal_debugfs *debugfs;
#endif
struct thermal_trip_desc trips[] __counted_by(num_trips);
};
/* Default Thermal Governor */
#if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE)
#define DEFAULT_THERMAL_GOVERNOR "step_wise"
......@@ -120,8 +232,11 @@ void thermal_governor_update_tz(struct thermal_zone_device *tz,
enum thermal_notify_event reason);
/* Helpers */
#define for_each_trip(__tz, __trip) \
for (__trip = __tz->trips; __trip - __tz->trips < __tz->num_trips; __trip++)
#define for_each_trip_desc(__tz, __td) \
for (__td = __tz->trips; __td - __tz->trips < __tz->num_trips; __td++)
#define trip_to_trip_desc(__trip) \
container_of(__trip, struct thermal_trip_desc, trip)
void __thermal_zone_set_trips(struct thermal_zone_device *tz);
int thermal_zone_trip_id(const struct thermal_zone_device *tz,
......
......@@ -745,7 +745,7 @@ static void tze_seq_stop(struct seq_file *s, void *v)
static int tze_seq_show(struct seq_file *s, void *v)
{
struct thermal_zone_device *tz = s->private;
struct thermal_trip *trip;
struct thermal_trip_desc *td;
struct tz_episode *tze;
const char *type;
int trip_id;
......@@ -758,7 +758,9 @@ static int tze_seq_show(struct seq_file *s, void *v)
seq_printf(s, "| trip | type | temp(°mC) | hyst(°mC) | duration | avg(°mC) | min(°mC) | max(°mC) |\n");
for_each_trip(tz, trip) {
for_each_trip_desc(tz, td) {
const struct thermal_trip *trip = &td->trip;
/*
* There is no possible mitigation happening at the
* critical trip point, so the stats will be always
......
......@@ -50,7 +50,7 @@ get_thermal_instance(struct thermal_zone_device *tz,
mutex_lock(&tz->lock);
mutex_lock(&cdev->lock);
trip = &tz->trips[trip_index];
trip = &tz->trips[trip_index].trip;
list_for_each_entry(pos, &tz->thermal_instances, tz_node) {
if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
......@@ -82,7 +82,7 @@ EXPORT_SYMBOL(get_thermal_instance);
*/
int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
{
const struct thermal_trip *trip;
const struct thermal_trip_desc *td;
int crit_temp = INT_MAX;
int ret = -EINVAL;
......@@ -91,7 +91,9 @@ int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
ret = tz->ops.get_temp(tz, temp);
if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) {
for_each_trip(tz, trip) {
for_each_trip_desc(tz, td) {
const struct thermal_trip *trip = &td->trip;
if (trip->type == THERMAL_TRIP_CRITICAL) {
crit_temp = trip->temperature;
break;
......
......@@ -7,17 +7,13 @@
* Generic netlink for thermal management framework
*/
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/kernel.h>
#include <net/genetlink.h>
#include <uapi/linux/thermal.h>
#include "thermal_core.h"
enum thermal_genl_multicast_groups {
THERMAL_GENL_SAMPLING_GROUP = 0,
THERMAL_GENL_EVENT_GROUP = 1,
};
static const struct genl_multicast_group thermal_genl_mcgrps[] = {
[THERMAL_GENL_SAMPLING_GROUP] = { .name = THERMAL_GENL_SAMPLING_GROUP_NAME, },
[THERMAL_GENL_EVENT_GROUP] = { .name = THERMAL_GENL_EVENT_GROUP_NAME, },
......@@ -74,11 +70,12 @@ struct param {
typedef int (*cb_t)(struct param *);
static struct genl_family thermal_gnl_family;
static struct genl_family thermal_genl_family;
static BLOCKING_NOTIFIER_HEAD(thermal_genl_chain);
static int thermal_group_has_listeners(enum thermal_genl_multicast_groups group)
{
return genl_has_listeners(&thermal_gnl_family, &init_net, group);
return genl_has_listeners(&thermal_genl_family, &init_net, group);
}
/************************** Sampling encoding *******************************/
......@@ -95,7 +92,7 @@ int thermal_genl_sampling_temp(int id, int temp)
if (!skb)
return -ENOMEM;
hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0,
hdr = genlmsg_put(skb, 0, 0, &thermal_genl_family, 0,
THERMAL_GENL_SAMPLING_TEMP);
if (!hdr)
goto out_free;
......@@ -108,7 +105,7 @@ int thermal_genl_sampling_temp(int id, int temp)
genlmsg_end(skb, hdr);
genlmsg_multicast(&thermal_gnl_family, skb, 0, THERMAL_GENL_SAMPLING_GROUP, GFP_KERNEL);
genlmsg_multicast(&thermal_genl_family, skb, 0, THERMAL_GENL_SAMPLING_GROUP, GFP_KERNEL);
return 0;
out_cancel:
......@@ -282,7 +279,7 @@ static int thermal_genl_send_event(enum thermal_genl_event event,
return -ENOMEM;
p->msg = msg;
hdr = genlmsg_put(msg, 0, 0, &thermal_gnl_family, 0, event);
hdr = genlmsg_put(msg, 0, 0, &thermal_genl_family, 0, event);
if (!hdr)
goto out_free_msg;
......@@ -292,7 +289,7 @@ static int thermal_genl_send_event(enum thermal_genl_event event,
genlmsg_end(msg, hdr);
genlmsg_multicast(&thermal_gnl_family, msg, 0, THERMAL_GENL_EVENT_GROUP, GFP_KERNEL);
genlmsg_multicast(&thermal_genl_family, msg, 0, THERMAL_GENL_EVENT_GROUP, GFP_KERNEL);
return 0;
......@@ -445,7 +442,7 @@ static int thermal_genl_cmd_tz_get_id(struct param *p)
static int thermal_genl_cmd_tz_get_trip(struct param *p)
{
struct sk_buff *msg = p->msg;
const struct thermal_trip *trip;
const struct thermal_trip_desc *td;
struct thermal_zone_device *tz;
struct nlattr *start_trip;
int id;
......@@ -465,7 +462,9 @@ static int thermal_genl_cmd_tz_get_trip(struct param *p)
mutex_lock(&tz->lock);
for_each_trip(tz, trip) {
for_each_trip_desc(tz, td) {
const struct thermal_trip *trip = &td->trip;
if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID,
thermal_zone_trip_id(tz, trip)) ||
nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, trip->type) ||
......@@ -593,7 +592,7 @@ static int thermal_genl_cmd_dumpit(struct sk_buff *skb,
int ret;
void *hdr;
hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0, cmd);
hdr = genlmsg_put(skb, 0, 0, &thermal_genl_family, 0, cmd);
if (!hdr)
return -EMSGSIZE;
......@@ -625,7 +624,7 @@ static int thermal_genl_cmd_doit(struct sk_buff *skb,
return -ENOMEM;
p.msg = msg;
hdr = genlmsg_put_reply(msg, info, &thermal_gnl_family, 0, cmd);
hdr = genlmsg_put_reply(msg, info, &thermal_genl_family, 0, cmd);
if (!hdr)
goto out_free_msg;
......@@ -645,6 +644,27 @@ static int thermal_genl_cmd_doit(struct sk_buff *skb,
return ret;
}
static int thermal_genl_bind(int mcgrp)
{
struct thermal_genl_notify n = { .mcgrp = mcgrp };
if (WARN_ON_ONCE(mcgrp > THERMAL_GENL_MAX_GROUP))
return -EINVAL;
blocking_notifier_call_chain(&thermal_genl_chain, THERMAL_NOTIFY_BIND, &n);
return 0;
}
static void thermal_genl_unbind(int mcgrp)
{
struct thermal_genl_notify n = { .mcgrp = mcgrp };
if (WARN_ON_ONCE(mcgrp > THERMAL_GENL_MAX_GROUP))
return;
blocking_notifier_call_chain(&thermal_genl_chain, THERMAL_NOTIFY_UNBIND, &n);
}
static const struct genl_small_ops thermal_genl_ops[] = {
{
.cmd = THERMAL_GENL_CMD_TZ_GET_ID,
......@@ -673,12 +693,14 @@ static const struct genl_small_ops thermal_genl_ops[] = {
},
};
static struct genl_family thermal_gnl_family __ro_after_init = {
static struct genl_family thermal_genl_family __ro_after_init = {
.hdrsize = 0,
.name = THERMAL_GENL_FAMILY_NAME,
.version = THERMAL_GENL_VERSION,
.maxattr = THERMAL_GENL_ATTR_MAX,
.policy = thermal_genl_policy,
.bind = thermal_genl_bind,
.unbind = thermal_genl_unbind,
.small_ops = thermal_genl_ops,
.n_small_ops = ARRAY_SIZE(thermal_genl_ops),
.resv_start_op = THERMAL_GENL_CMD_CDEV_GET + 1,
......@@ -686,12 +708,22 @@ static struct genl_family thermal_gnl_family __ro_after_init = {
.n_mcgrps = ARRAY_SIZE(thermal_genl_mcgrps),
};
int thermal_genl_register_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&thermal_genl_chain, nb);
}
int thermal_genl_unregister_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&thermal_genl_chain, nb);
}
int __init thermal_netlink_init(void)
{
return genl_register_family(&thermal_gnl_family);
return genl_register_family(&thermal_genl_family);
}
void __init thermal_netlink_exit(void)
{
genl_unregister_family(&thermal_gnl_family);
genl_unregister_family(&thermal_genl_family);
}
......@@ -10,6 +10,19 @@ struct thermal_genl_cpu_caps {
int efficiency;
};
enum thermal_genl_multicast_groups {
THERMAL_GENL_SAMPLING_GROUP = 0,
THERMAL_GENL_EVENT_GROUP = 1,
THERMAL_GENL_MAX_GROUP = THERMAL_GENL_EVENT_GROUP,
};
#define THERMAL_NOTIFY_BIND 0
#define THERMAL_NOTIFY_UNBIND 1
struct thermal_genl_notify {
int mcgrp;
};
struct thermal_zone_device;
struct thermal_trip;
struct thermal_cooling_device;
......@@ -18,6 +31,9 @@ struct thermal_cooling_device;
#ifdef CONFIG_THERMAL_NETLINK
int __init thermal_netlink_init(void);
void __init thermal_netlink_exit(void);
int thermal_genl_register_notifier(struct notifier_block *nb);
int thermal_genl_unregister_notifier(struct notifier_block *nb);
int thermal_notify_tz_create(const struct thermal_zone_device *tz);
int thermal_notify_tz_delete(const struct thermal_zone_device *tz);
int thermal_notify_tz_enable(const struct thermal_zone_device *tz);
......@@ -48,6 +64,16 @@ static inline int thermal_notify_tz_create(const struct thermal_zone_device *tz)
return 0;
}
static inline int thermal_genl_register_notifier(struct notifier_block *nb)
{
return 0;
}
static inline int thermal_genl_unregister_notifier(struct notifier_block *nb)
{
return 0;
}
static inline int thermal_notify_tz_delete(const struct thermal_zone_device *tz)
{
return 0;
......
......@@ -88,7 +88,7 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
if (sscanf(attr->attr.name, "trip_point_%d_type", &trip_id) != 1)
return -EINVAL;
switch (tz->trips[trip_id].type) {
switch (tz->trips[trip_id].trip.type) {
case THERMAL_TRIP_CRITICAL:
return sprintf(buf, "critical\n");
case THERMAL_TRIP_HOT:
......@@ -120,7 +120,7 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
mutex_lock(&tz->lock);
trip = &tz->trips[trip_id];
trip = &tz->trips[trip_id].trip;
if (temp != trip->temperature) {
if (tz->ops.set_trip_temp) {
......@@ -150,7 +150,7 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1)
return -EINVAL;
return sprintf(buf, "%d\n", tz->trips[trip_id].temperature);
return sprintf(buf, "%d\n", tz->trips[trip_id].trip.temperature);
}
static ssize_t
......@@ -171,7 +171,7 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
mutex_lock(&tz->lock);
trip = &tz->trips[trip_id];
trip = &tz->trips[trip_id].trip;
if (hyst != trip->hysteresis) {
trip->hysteresis = hyst;
......@@ -194,7 +194,7 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1)
return -EINVAL;
return sprintf(buf, "%d\n", tz->trips[trip_id].hysteresis);
return sprintf(buf, "%d\n", tz->trips[trip_id].trip.hysteresis);
}
static ssize_t
......@@ -393,7 +393,7 @@ static const struct attribute_group *thermal_zone_attribute_groups[] = {
*/
static int create_trip_attrs(struct thermal_zone_device *tz)
{
const struct thermal_trip *trip;
const struct thermal_trip_desc *td;
struct attribute **attrs;
/* This function works only for zones with at least one trip */
......@@ -429,8 +429,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz)
return -ENOMEM;
}
for_each_trip(tz, trip) {
int indx = thermal_zone_trip_id(tz, trip);
for_each_trip_desc(tz, td) {
int indx = thermal_zone_trip_id(tz, &td->trip);
/* create trip type attribute */
snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH,
......@@ -452,7 +452,7 @@ static int create_trip_attrs(struct thermal_zone_device *tz)
tz->trip_temp_attrs[indx].name;
tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO;
tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show;
if (trip->flags & THERMAL_TRIP_FLAG_RW_TEMP) {
if (td->trip.flags & THERMAL_TRIP_FLAG_RW_TEMP) {
tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR;
tz->trip_temp_attrs[indx].attr.store =
trip_point_temp_store;
......@@ -467,7 +467,7 @@ static int create_trip_attrs(struct thermal_zone_device *tz)
tz->trip_hyst_attrs[indx].name;
tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO;
tz->trip_hyst_attrs[indx].attr.show = trip_point_hyst_show;
if (trip->flags & THERMAL_TRIP_FLAG_RW_HYST) {
if (td->trip.flags & THERMAL_TRIP_FLAG_RW_HYST) {
tz->trip_hyst_attrs[indx].attr.attr.mode |= S_IWUSR;
tz->trip_hyst_attrs[indx].attr.store =
trip_point_hyst_store;
......
......@@ -9,6 +9,8 @@
#include <linux/thermal.h>
#include <linux/tracepoint.h>
#include "thermal_core.h"
TRACE_DEFINE_ENUM(THERMAL_TRIP_CRITICAL);
TRACE_DEFINE_ENUM(THERMAL_TRIP_HOT);
TRACE_DEFINE_ENUM(THERMAL_TRIP_PASSIVE);
......
......@@ -7,6 +7,8 @@
#include <linux/tracepoint.h>
#include "thermal_core.h"
TRACE_EVENT(thermal_power_allocator,
TP_PROTO(struct thermal_zone_device *tz, u32 total_req_power,
u32 total_granted_power, int num_actors, u32 power_range,
......
......@@ -13,11 +13,11 @@ int for_each_thermal_trip(struct thermal_zone_device *tz,
int (*cb)(struct thermal_trip *, void *),
void *data)
{
struct thermal_trip *trip;
struct thermal_trip_desc *td;
int ret;
for_each_trip(tz, trip) {
ret = cb(trip, data);
for_each_trip_desc(tz, td) {
ret = cb(&td->trip, data);
if (ret)
return ret;
}
......@@ -63,7 +63,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips);
*/
void __thermal_zone_set_trips(struct thermal_zone_device *tz)
{
const struct thermal_trip *trip;
const struct thermal_trip_desc *td;
int low = -INT_MAX, high = INT_MAX;
int ret;
......@@ -72,7 +72,8 @@ void __thermal_zone_set_trips(struct thermal_zone_device *tz)
if (!tz->ops.set_trips)
return;
for_each_trip(tz, trip) {
for_each_trip_desc(tz, td) {
const struct thermal_trip *trip = &td->trip;
int trip_low;
trip_low = trip->temperature - trip->hysteresis;
......@@ -110,7 +111,7 @@ int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
if (!tz || trip_id < 0 || trip_id >= tz->num_trips || !trip)
return -EINVAL;
*trip = tz->trips[trip_id];
*trip = tz->trips[trip_id].trip;
return 0;
}
EXPORT_SYMBOL_GPL(__thermal_zone_get_trip);
......@@ -135,7 +136,7 @@ int thermal_zone_trip_id(const struct thermal_zone_device *tz,
* Assume the trip to be located within the bounds of the thermal
* zone's trips[] table.
*/
return trip - tz->trips;
return trip_to_trip_desc(trip) - tz->trips;
}
void thermal_zone_trip_updated(struct thermal_zone_device *tz,
const struct thermal_trip *trip)
......
......@@ -61,7 +61,6 @@ enum thermal_notify_event {
* struct thermal_trip - representation of a point in temperature domain
* @temperature: temperature value in miliCelsius
* @hysteresis: relative hysteresis in miliCelsius
* @threshold: trip crossing notification threshold miliCelsius
* @type: trip point type
* @priv: pointer to driver data associated with this trip
* @flags: flags representing binary properties of the trip
......@@ -69,7 +68,6 @@ enum thermal_notify_event {
struct thermal_trip {
int temperature;
int hysteresis;
int threshold;
enum thermal_trip_type type;
u8 flags;
void *priv;
......@@ -81,6 +79,8 @@ struct thermal_trip {
#define THERMAL_TRIP_FLAG_RW (THERMAL_TRIP_FLAG_RW_TEMP | \
THERMAL_TRIP_FLAG_RW_HYST)
struct thermal_zone_device;
struct thermal_zone_device_ops {
int (*bind) (struct thermal_zone_device *,
struct thermal_cooling_device *);
......@@ -126,111 +126,6 @@ struct thermal_cooling_device {
#endif
};
/**
* struct thermal_zone_device - structure for a thermal zone
* @id: unique id number for each thermal zone
* @type: the thermal zone device type
* @device: &struct device for this thermal zone
* @removal: removal completion
* @trip_temp_attrs: attributes for trip points for sysfs: trip temperature
* @trip_type_attrs: attributes for trip points for sysfs: trip type
* @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis
* @mode: current mode of this thermal zone
* @devdata: private pointer for device private data
* @num_trips: number of trip points the thermal zone supports
* @passive_delay_jiffies: number of jiffies to wait between polls when
* performing passive cooling.
* @polling_delay_jiffies: number of jiffies to wait between polls when
* checking whether trip points have been crossed (0 for
* interrupt driven systems)
* @temperature: current temperature. This is only for core code,
* drivers should use thermal_zone_get_temp() to get the
* current temperature
* @last_temperature: previous temperature read
* @emul_temperature: emulated temperature when using CONFIG_THERMAL_EMULATION
* @passive: 1 if you've crossed a passive trip point, 0 otherwise.
* @prev_low_trip: the low current temperature if you've crossed a passive
trip point.
* @prev_high_trip: the above current temperature if you've crossed a
passive trip point.
* @need_update: if equals 1, thermal_zone_device_update needs to be invoked.
* @ops: operations this &thermal_zone_device supports
* @tzp: thermal zone parameters
* @governor: pointer to the governor for this thermal zone
* @governor_data: private pointer for governor data
* @thermal_instances: list of &struct thermal_instance of this thermal zone
* @ida: &struct ida to generate unique id for this zone's cooling
* devices
* @lock: lock to protect thermal_instances list
* @node: node in thermal_tz_list (in thermal_core.c)
* @poll_queue: delayed work for polling
* @notify_event: Last notification event
* @suspended: thermal zone suspend indicator
* @trips: array of struct thermal_trip objects
*/
struct thermal_zone_device {
int id;
char type[THERMAL_NAME_LENGTH];
struct device device;
struct completion removal;
struct attribute_group trips_attribute_group;
struct thermal_attr *trip_temp_attrs;
struct thermal_attr *trip_type_attrs;
struct thermal_attr *trip_hyst_attrs;
enum thermal_device_mode mode;
void *devdata;
int num_trips;
unsigned long passive_delay_jiffies;
unsigned long polling_delay_jiffies;
int temperature;
int last_temperature;
int emul_temperature;
int passive;
int prev_low_trip;
int prev_high_trip;
atomic_t need_update;
struct thermal_zone_device_ops ops;
struct thermal_zone_params *tzp;
struct thermal_governor *governor;
void *governor_data;
struct list_head thermal_instances;
struct ida ida;
struct mutex lock;
struct list_head node;
struct delayed_work poll_queue;
enum thermal_notify_event notify_event;
bool suspended;
#ifdef CONFIG_THERMAL_DEBUGFS
struct thermal_debugfs *debugfs;
#endif
struct thermal_trip trips[] __counted_by(num_trips);
};
/**
* struct thermal_governor - structure that holds thermal governor information
* @name: name of the governor
* @bind_to_tz: callback called when binding to a thermal zone. If it
* returns 0, the governor is bound to the thermal zone,
* otherwise it fails.
* @unbind_from_tz: callback called when a governor is unbound from a
* thermal zone.
* @throttle: callback called for every trip point even if temperature is
* below the trip point temperature
* @update_tz: callback called when thermal zone internals have changed, e.g.
* thermal cooling instance was added/removed
* @governor_list: node in thermal_governor_list (in thermal_core.c)
*/
struct thermal_governor {
const char *name;
int (*bind_to_tz)(struct thermal_zone_device *tz);
void (*unbind_from_tz)(struct thermal_zone_device *tz);
int (*throttle)(struct thermal_zone_device *tz,
const struct thermal_trip *trip);
void (*update_tz)(struct thermal_zone_device *tz,
enum thermal_notify_event reason);
struct list_head governor_list;
};
/* Structure to define Thermal Zone parameters */
struct thermal_zone_params {
const char *governor_name;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment