diff --git a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c index dc519a665c18..4b4a4d63e61f 100644 --- a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c +++ b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c @@ -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; diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c index 40d664a66cdc..fbc7f0cd83d7 100644 --- a/drivers/thermal/intel/intel_hfi.c +++ b/drivers/thermal/intel/intel_hfi.c @@ -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]; diff --git a/drivers/thermal/thermal_netlink.c b/drivers/thermal/thermal_netlink.c index 0ef6368da766..97157c453630 100644 --- a/drivers/thermal/thermal_netlink.c +++ b/drivers/thermal/thermal_netlink.c @@ -7,17 +7,13 @@ * Generic netlink for thermal management framework */ #include +#include #include #include #include #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; @@ -595,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; @@ -627,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; @@ -647,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, @@ -675,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, @@ -688,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); } diff --git a/drivers/thermal/thermal_netlink.h b/drivers/thermal/thermal_netlink.h index 93a927e144d5..e01221e8816b 100644 --- a/drivers/thermal/thermal_netlink.h +++ b/drivers/thermal/thermal_netlink.h @@ -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;