diff mbox series

[v4,3/6] thermal/netlink: Add the commands and the events for the thresholds

Message ID 20240923100005.2532430-4-daniel.lezcano@linaro.org (mailing list archive)
State Changes Requested, archived
Headers show
Series Add thermal user thresholds support | expand

Commit Message

Daniel Lezcano Sept. 23, 2024, 9:59 a.m. UTC
The thresholds exist but there is no notification neither action code
related to them yet.

These changes implement the netlink for the notifications when the
thresholds are crossed, added, deleted or flushed as well as the
commands which allows to get the list of the thresholds, flush them,
add and delete.

As different processes in userspace can interact with the thresholds,
the process id responsible of the action (add, delete or flush) will
be added in the notification. This way a thermal engine is able to
detect if another process is interfering with the thresholds. A
process id of zero is the kernel as it is by convention usually.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 drivers/thermal/thermal_netlink.c    | 239 ++++++++++++++++++++++++++-
 drivers/thermal/thermal_netlink.h    |  34 ++++
 drivers/thermal/thermal_thresholds.c |  38 +++--
 drivers/thermal/thermal_thresholds.h |   6 +-
 include/uapi/linux/thermal.h         |  28 +++-
 5 files changed, 313 insertions(+), 32 deletions(-)

Comments

Rafael J. Wysocki Oct. 2, 2024, 12:53 p.m. UTC | #1
On Mon, Sep 23, 2024 at 12:00 PM Daniel Lezcano
<daniel.lezcano@linaro.org> wrote:
>
> The thresholds exist but there is no notification neither action code
> related to them yet.
>
> These changes implement the netlink for the notifications when the
> thresholds are crossed, added, deleted or flushed as well as the
> commands which allows to get the list of the thresholds, flush them,
> add and delete.

The last three commands need special privileges I would think because
they essentially cause the kernel to either allocate or free memory
and they may interfere with what the other processes in user space do.
For instance, one process may flush the thresholds for a given thermal
zone while another process is using them.

What controls the level of privilege required to use these commands?

> As different processes in userspace can interact with the thresholds,
> the process id responsible of the action (add, delete or flush) will
> be added in the notification.

This may leak PIDs between containers which has been pointed out as an
issue (for example, see
https://lore.kernel.org/linux-pm/20240704-umsatz-drollig-38db6b84da7b@brauner/).

OTOH, the thermal engine should not need the extra information because
it knows which thresholds were added by it, so any other thresholds
would be added by someone else, wouldn't they?

> This way a thermal engine is able to
> detect if another process is interfering with the thresholds. A
> process id of zero is the kernel as it is by convention usually.
>
> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
> ---
>  drivers/thermal/thermal_netlink.c    | 239 ++++++++++++++++++++++++++-
>  drivers/thermal/thermal_netlink.h    |  34 ++++
>  drivers/thermal/thermal_thresholds.c |  38 +++--
>  drivers/thermal/thermal_thresholds.h |   6 +-
>  include/uapi/linux/thermal.h         |  28 +++-
>  5 files changed, 313 insertions(+), 32 deletions(-)
>
> diff --git a/drivers/thermal/thermal_netlink.c b/drivers/thermal/thermal_netlink.c
> index 97157c453630..8d92ebeb72fc 100644
> --- a/drivers/thermal/thermal_netlink.c
> +++ b/drivers/thermal/thermal_netlink.c
> @@ -9,6 +9,7 @@
>  #include <linux/module.h>
>  #include <linux/notifier.h>
>  #include <linux/kernel.h>
> +#include <net/sock.h>
>  #include <net/genetlink.h>
>  #include <uapi/linux/thermal.h>
>
> @@ -49,12 +50,19 @@ static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] =
>         [THERMAL_GENL_ATTR_CPU_CAPABILITY_ID]           = { .type = NLA_U32 },
>         [THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE]  = { .type = NLA_U32 },
>         [THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY]   = { .type = NLA_U32 },
> +
> +       /* Thresholds */
> +       [THERMAL_GENL_ATTR_THRESHOLD]           = { .type = NLA_NESTED },
> +       [THERMAL_GENL_ATTR_THRESHOLD_TEMP]      = { .type = NLA_U32 },
> +       [THERMAL_GENL_ATTR_THRESHOLD_WAY]       = { .type = NLA_U32 },

It would be better to call this THERMAL_GENL_ATTR_THRESHOLD_DIR IMV.

> +       [THERMAL_GENL_ATTR_THRESHOLD_PID]       = { .type = NLA_U32 },
>  };
>
>  struct param {
>         struct nlattr **attrs;
>         struct sk_buff *msg;
>         const char *name;
> +       pid_t pid;

I'd rather not add it as mentioned above.

>         int tz_id;
>         int cdev_id;
>         int trip_id;
> @@ -62,6 +70,8 @@ struct param {
>         int trip_type;
>         int trip_hyst;
>         int temp;
> +       int last_temp;

Or prev_temp?  It should be less ambiguous than last_temp.

> +       int direction;
>         int cdev_state;
>         int cdev_max_state;
>         struct thermal_genl_cpu_caps *cpu_capabilities;
> @@ -234,6 +244,36 @@ static int thermal_genl_event_cpu_capability_change(struct param *p)
>         return -EMSGSIZE;
>  }
>
> +static int thermal_genl_event_threshold_add(struct param *p)
> +{
> +       if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
> +           nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, p->temp) ||
> +           nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_WAY, p->direction) ||
> +           nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_PID, p->pid))
> +               return -EMSGSIZE;
> +
> +       return 0;
> +}
> +
> +static int thermal_genl_event_threshold_flush(struct param *p)
> +{
> +       if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id),
> +           nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_PID, p->pid))
> +               return -EMSGSIZE;
> +
> +       return 0;
> +}
> +
> +static int thermal_genl_event_threshold_up(struct param *p)
> +{
> +       if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
> +           nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_LAST_TEMP, p->last_temp) ||
> +           nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TEMP, p->temp))
> +               return -EMSGSIZE;
> +
> +       return 0;
> +}
> +
>  int thermal_genl_event_tz_delete(struct param *p)
>         __attribute__((alias("thermal_genl_event_tz")));
>
> @@ -246,6 +286,12 @@ int thermal_genl_event_tz_disable(struct param *p)
>  int thermal_genl_event_tz_trip_down(struct param *p)
>         __attribute__((alias("thermal_genl_event_tz_trip_up")));
>
> +int thermal_genl_event_threshold_delete(struct param *p)
> +       __attribute__((alias("thermal_genl_event_threshold_add")));
> +
> +int thermal_genl_event_threshold_down(struct param *p)
> +       __attribute__((alias("thermal_genl_event_threshold_up")));
> +
>  static cb_t event_cb[] = {
>         [THERMAL_GENL_EVENT_TZ_CREATE]          = thermal_genl_event_tz_create,
>         [THERMAL_GENL_EVENT_TZ_DELETE]          = thermal_genl_event_tz_delete,
> @@ -259,6 +305,11 @@ static cb_t event_cb[] = {
>         [THERMAL_GENL_EVENT_CDEV_STATE_UPDATE]  = thermal_genl_event_cdev_state_update,
>         [THERMAL_GENL_EVENT_TZ_GOV_CHANGE]      = thermal_genl_event_gov_change,
>         [THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE] = thermal_genl_event_cpu_capability_change,
> +       [THERMAL_GENL_EVENT_THRESHOLD_ADD]      = thermal_genl_event_threshold_add,
> +       [THERMAL_GENL_EVENT_THRESHOLD_DELETE]   = thermal_genl_event_threshold_delete,
> +       [THERMAL_GENL_EVENT_THRESHOLD_FLUSH]    = thermal_genl_event_threshold_flush,
> +       [THERMAL_GENL_EVENT_THRESHOLD_DOWN]     = thermal_genl_event_threshold_down,
> +       [THERMAL_GENL_EVENT_THRESHOLD_UP]       = thermal_genl_event_threshold_up,
>  };
>
>  /*
> @@ -401,6 +452,43 @@ int thermal_genl_cpu_capability_event(int count,
>  }
>  EXPORT_SYMBOL_GPL(thermal_genl_cpu_capability_event);
>
> +int thermal_notify_threshold_add(const struct thermal_zone_device *tz,
> +                                int temperature, int direction, int pid)
> +{
> +       struct param p = { .tz_id = tz->id, .temp = temperature, .direction = direction, .pid = pid };
> +
> +       return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_ADD, &p);
> +}
> +
> +int thermal_notify_threshold_delete(const struct thermal_zone_device *tz,
> +                                   int temperature, int direction, int pid)
> +{
> +       struct param p = { .tz_id = tz->id, .temp = temperature, .direction = direction, .pid = pid };
> +
> +       return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_DELETE, &p);
> +}
> +
> +int thermal_notify_threshold_flush(const struct thermal_zone_device *tz, int pid)
> +{
> +       struct param p = { .tz_id = tz->id, .pid = pid };
> +
> +       return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_FLUSH, &p);
> +}
> +
> +int thermal_notify_threshold_down(const struct thermal_zone_device *tz)
> +{
> +       struct param p = { .tz_id = tz->id, .temp = tz->temperature, .last_temp = tz->last_temperature };
> +
> +       return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_DOWN, &p);
> +}
> +
> +int thermal_notify_threshold_up(const struct thermal_zone_device *tz)
> +{
> +       struct param p = { .tz_id = tz->id, .temp = tz->temperature, .last_temp = tz->last_temperature };
> +
> +       return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_UP, &p);
> +}
> +
>  /*************************** Command encoding ********************************/
>
>  static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz,
> @@ -575,12 +663,130 @@ static int thermal_genl_cmd_cdev_get(struct param *p)
>         return ret;
>  }
>
> +static int __thermal_genl_cmd_threshold_get(struct user_threshold *threshold, void *arg)
> +{
> +       struct sk_buff *msg = arg;
> +
> +       if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, threshold->temperature) ||
> +           nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_WAY, threshold->direction))
> +               return -1;
> +
> +       return 0;
> +}
> +
> +static int thermal_genl_cmd_threshold_get(struct param *p)
> +{
> +       struct thermal_zone_device *tz;
> +       struct sk_buff *msg = p->msg;
> +       struct nlattr *start_trip;
> +       int id, ret;
> +
> +       if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
> +               return -EINVAL;
> +
> +       id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
> +
> +       tz = thermal_zone_get_by_id(id);
> +       if (!tz)
> +               return -EINVAL;

What prevents the thermal zone from going away right here?

It looks like thermal_zone_get_by_id() should do a get_device() on the
thermal zone object it is about to return and then the caller should
do a put_device() on it.

Granted, this problem is present already in the thermal netlink code,
so it needs to be fixed (I'm going to send a patch to address it) and
this patch will need to be adjusted.

> +
> +       start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_THRESHOLD);
> +       if (!start_trip)
> +               return -EMSGSIZE;
> +
> +       mutex_lock(&tz->lock);
> +       ret = thermal_thresholds_for_each(tz, __thermal_genl_cmd_threshold_get, msg);
> +       mutex_unlock(&tz->lock);

I think that the locking can be moved to
thermal_thresholds_for_each().  At least there are no other callers of
it AFAICS.

> +
> +       if (ret)
> +               return -EMSGSIZE;
> +
> +       nla_nest_end(msg, start_trip);
> +
> +       return 0;
> +}
> +
> +static int thermal_genl_cmd_threshold_add(struct param *p)
> +{
> +       struct thermal_zone_device *tz;
> +       int id, temp, direction, ret = 0;
> +
> +       if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID] ||
> +           !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP] ||
> +           !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_WAY])
> +               return -EINVAL;
> +
> +       id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
> +       temp = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]);
> +       direction = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_WAY]);
> +
> +       tz = thermal_zone_get_by_id(id);
> +       if (!tz)
> +               return -EINVAL;
> +
> +       mutex_lock(&tz->lock);
> +       ret = thermal_thresholds_add(tz, temp, direction, p->pid);
> +       mutex_unlock(&tz->lock);
> +
> +       return ret;
> +}
> +
> +static int thermal_genl_cmd_threshold_delete(struct param *p)
> +{
> +       struct thermal_zone_device *tz;
> +       int id, temp, direction, ret = 0;
> +
> +       if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID] ||
> +           !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP] ||
> +           !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_WAY])
> +               return -EINVAL;
> +
> +       id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
> +       temp = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]);
> +       direction = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_WAY]);
> +
> +       tz = thermal_zone_get_by_id(id);
> +       if (!tz)
> +               return -EINVAL;
> +
> +       mutex_lock(&tz->lock);
> +       ret = thermal_thresholds_delete(tz, temp, direction, p->pid);
> +       mutex_unlock(&tz->lock);
> +
> +       return ret;
> +}
> +
> +static int thermal_genl_cmd_threshold_flush(struct param *p)
> +{
> +       struct thermal_zone_device *tz;
> +       int id;
> +
> +       if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
> +               return -EINVAL;
> +
> +       id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
> +
> +       tz = thermal_zone_get_by_id(id);
> +       if (!tz)
> +               return -EINVAL;
> +
> +       mutex_lock(&tz->lock);
> +       thermal_thresholds_flush(tz, p->pid);
> +       mutex_unlock(&tz->lock);
> +
> +       return 0;
> +}
> +
>  static cb_t cmd_cb[] = {
> -       [THERMAL_GENL_CMD_TZ_GET_ID]    = thermal_genl_cmd_tz_get_id,
> -       [THERMAL_GENL_CMD_TZ_GET_TRIP]  = thermal_genl_cmd_tz_get_trip,
> -       [THERMAL_GENL_CMD_TZ_GET_TEMP]  = thermal_genl_cmd_tz_get_temp,
> -       [THERMAL_GENL_CMD_TZ_GET_GOV]   = thermal_genl_cmd_tz_get_gov,
> -       [THERMAL_GENL_CMD_CDEV_GET]     = thermal_genl_cmd_cdev_get,
> +       [THERMAL_GENL_CMD_TZ_GET_ID]            = thermal_genl_cmd_tz_get_id,
> +       [THERMAL_GENL_CMD_TZ_GET_TRIP]          = thermal_genl_cmd_tz_get_trip,
> +       [THERMAL_GENL_CMD_TZ_GET_TEMP]          = thermal_genl_cmd_tz_get_temp,
> +       [THERMAL_GENL_CMD_TZ_GET_GOV]           = thermal_genl_cmd_tz_get_gov,
> +       [THERMAL_GENL_CMD_CDEV_GET]             = thermal_genl_cmd_cdev_get,
> +       [THERMAL_GENL_CMD_THRESHOLD_GET]        = thermal_genl_cmd_threshold_get,
> +       [THERMAL_GENL_CMD_THRESHOLD_ADD]        = thermal_genl_cmd_threshold_add,
> +       [THERMAL_GENL_CMD_THRESHOLD_DELETE]     = thermal_genl_cmd_threshold_delete,
> +       [THERMAL_GENL_CMD_THRESHOLD_FLUSH]      = thermal_genl_cmd_threshold_flush,
>  };
>
>  static int thermal_genl_cmd_dumpit(struct sk_buff *skb,
> @@ -623,6 +829,7 @@ static int thermal_genl_cmd_doit(struct sk_buff *skb,
>         if (!msg)
>                 return -ENOMEM;
>         p.msg = msg;
> +       p.pid = task_tgid_vnr(current);
>
>         hdr = genlmsg_put_reply(msg, info, &thermal_genl_family, 0, cmd);
>         if (!hdr)
> @@ -691,6 +898,26 @@ static const struct genl_small_ops thermal_genl_ops[] = {
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .dumpit = thermal_genl_cmd_dumpit,
>         },
> +       {
> +               .cmd = THERMAL_GENL_CMD_THRESHOLD_GET,
> +               .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
> +               .doit = thermal_genl_cmd_doit,
> +       },
> +       {
> +               .cmd = THERMAL_GENL_CMD_THRESHOLD_ADD,
> +               .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
> +               .doit = thermal_genl_cmd_doit,
> +       },
> +       {
> +               .cmd = THERMAL_GENL_CMD_THRESHOLD_DELETE,
> +               .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
> +               .doit = thermal_genl_cmd_doit,
> +       },
> +       {
> +               .cmd = THERMAL_GENL_CMD_THRESHOLD_FLUSH,
> +               .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
> +               .doit = thermal_genl_cmd_doit,
> +       },
>  };
>
>  static struct genl_family thermal_genl_family __ro_after_init = {
> @@ -703,7 +930,7 @@ static struct genl_family thermal_genl_family __ro_after_init = {
>         .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,
> +       .resv_start_op  = __THERMAL_GENL_CMD_MAX,
>         .mcgrps         = thermal_genl_mcgrps,
>         .n_mcgrps       = ARRAY_SIZE(thermal_genl_mcgrps),
>  };
> diff --git a/drivers/thermal/thermal_netlink.h b/drivers/thermal/thermal_netlink.h
> index e01221e8816b..3f85c636029c 100644
> --- a/drivers/thermal/thermal_netlink.h
> +++ b/drivers/thermal/thermal_netlink.h
> @@ -53,6 +53,13 @@ int thermal_notify_tz_gov_change(const struct thermal_zone_device *tz,
>  int thermal_genl_sampling_temp(int id, int temp);
>  int thermal_genl_cpu_capability_event(int count,
>                                       struct thermal_genl_cpu_caps *caps);
> +int thermal_notify_threshold_add(const struct thermal_zone_device *tz,
> +                                int temperature, int direction, pid_t pid);
> +int thermal_notify_threshold_delete(const struct thermal_zone_device *tz,
> +                                   int temperature, int direction, pid_t pid);
> +int thermal_notify_threshold_flush(const struct thermal_zone_device *tz, pid_t pid);
> +int thermal_notify_threshold_down(const struct thermal_zone_device *tz);
> +int thermal_notify_threshold_up(const struct thermal_zone_device *tz);
>  #else
>  static inline int thermal_netlink_init(void)
>  {
> @@ -139,6 +146,33 @@ static inline int thermal_genl_cpu_capability_event(int count, struct thermal_ge
>         return 0;
>  }
>
> +static inline int thermal_notify_threshold_add(const struct thermal_zone_device *tz,
> +                                              int temperature, int direction, pid_t pid)
> +{
> +       return 0;
> +}
> +
> +static inline int thermal_notify_threshold_delete(const struct thermal_zone_device *tz,
> +                                                 int temperature, int direction, pid_t pid)
> +{
> +       return 0;
> +}
> +
> +static inline int thermal_notify_threshold_flush(const struct thermal_zone_device *tz, pid_t pid)
> +{
> +       return 0;
> +}
> +
> +static inline int thermal_notify_threshold_down(const struct thermal_zone_device *tz)
> +{
> +       return 0;
> +}
> +
> +static inline int thermal_notify_threshold_up(const struct thermal_zone_device *tz)
> +{
> +       return 0;
> +}
> +
>  static inline void __init thermal_netlink_exit(void) {}
>
>  #endif /* CONFIG_THERMAL_NETLINK */
> diff --git a/drivers/thermal/thermal_thresholds.c b/drivers/thermal/thermal_thresholds.c
> index f33b6d5474d8..6d9a0cf8031f 100644
> --- a/drivers/thermal/thermal_thresholds.c
> +++ b/drivers/thermal/thermal_thresholds.c
> @@ -20,7 +20,7 @@ int thermal_thresholds_init(struct thermal_zone_device *tz)
>         return 0;
>  }
>
> -void thermal_thresholds_flush(struct thermal_zone_device *tz)
> +void thermal_thresholds_flush(struct thermal_zone_device *tz, pid_t pid)
>  {
>         struct list_head *thresholds = &tz->user_thresholds;
>         struct user_threshold *entry, *tmp;
> @@ -32,12 +32,14 @@ void thermal_thresholds_flush(struct thermal_zone_device *tz)
>                 kfree(entry);
>         }
>
> +       thermal_notify_threshold_flush(tz, pid);
> +
>         __thermal_zone_device_update(tz, THERMAL_TZ_FLUSH_THRESHOLDS);
>  }
>
>  void thermal_thresholds_exit(struct thermal_zone_device *tz)
>  {
> -       thermal_thresholds_flush(tz);
> +       thermal_thresholds_flush(tz, 0);
>  }
>
>  static int __thermal_thresholds_cmp(void *data,
> @@ -122,7 +124,6 @@ void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *hi
>
>         int temperature = tz->temperature;
>         int last_temperature = tz->last_temperature;
> -       bool notify;
>
>         lockdep_assert_held(&tz->lock);
>
> @@ -144,19 +145,19 @@ void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *hi
>          * - increased : thresholds are crossed the way up
>          * - decreased : thresholds are crossed the way down
>          */
> -       if (temperature > last_temperature)
> -               notify = thermal_thresholds_handle_raising(thresholds, temperature,
> -                                                          last_temperature, low, high);
> -       else
> -               notify = thermal_thresholds_handle_dropping(thresholds, temperature,
> -                                                           last_temperature, low, high);
> -
> -       if (notify)
> -               pr_debug("A threshold has been crossed the way %s, with a temperature=%d, last_temperature=%d\n",
> -                        temperature > last_temperature ? "up" : "down", temperature, last_temperature);
> +       if (temperature > last_temperature) {
> +               if (thermal_thresholds_handle_raising(thresholds, temperature,
> +                                                     last_temperature, low, high))
> +                       thermal_notify_threshold_up(tz);
> +       } else {
> +               if (thermal_thresholds_handle_dropping(thresholds, temperature,
> +                                                      last_temperature, low, high))
> +                       thermal_notify_threshold_down(tz);
> +       }
>  }
>
> -int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction)
> +int thermal_thresholds_add(struct thermal_zone_device *tz,
> +                          int temperature, int direction, pid_t pid)
>  {
>         struct list_head *thresholds = &tz->user_thresholds;
>         struct user_threshold *t;
> @@ -182,12 +183,15 @@ int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int
>                 list_sort(NULL, thresholds, __thermal_thresholds_cmp);
>         }
>
> +       thermal_notify_threshold_add(tz, temperature, direction, pid);
> +
>         __thermal_zone_device_update(tz, THERMAL_TZ_ADD_THRESHOLD);
>
>         return 0;
>  }
>
> -int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction)
> +int thermal_thresholds_delete(struct thermal_zone_device *tz,
> +                             int temperature, int direction, pid_t pid)
>  {
>         struct list_head *thresholds = &tz->user_thresholds;
>         struct user_threshold *t;
> @@ -205,8 +209,10 @@ int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, i
>                 t->direction &= ~direction;
>         }
>
> -       __thermal_zone_device_update(tz, THERMAL_TZ_DEL_THRESHOLD);
> +       thermal_notify_threshold_delete(tz, temperature, direction, pid);
>
> +       __thermal_zone_device_update(tz, THERMAL_TZ_DEL_THRESHOLD);
> +
>         return 0;
>  }
>
> diff --git a/drivers/thermal/thermal_thresholds.h b/drivers/thermal/thermal_thresholds.h
> index 232f4e8089af..f97ffb715d2e 100644
> --- a/drivers/thermal/thermal_thresholds.h
> +++ b/drivers/thermal/thermal_thresholds.h
> @@ -10,10 +10,10 @@ struct user_threshold {
>
>  int thermal_thresholds_init(struct thermal_zone_device *tz);
>  void thermal_thresholds_exit(struct thermal_zone_device *tz);
> -void thermal_thresholds_flush(struct thermal_zone_device *tz);
>  void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high);
> -int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction);
> -int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction);
> +void thermal_thresholds_flush(struct thermal_zone_device *tz, pid_t pid);
> +int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction, pid_t pid);
> +int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction, pid_t pid);
>  int thermal_thresholds_for_each(struct thermal_zone_device *tz,
>                                 int (*cb)(struct user_threshold *, void *arg), void *arg);
>  #endif
> diff --git a/include/uapi/linux/thermal.h b/include/uapi/linux/thermal.h
> index 3e7c1c2e71a7..bcbaf62a1727 100644
> --- a/include/uapi/linux/thermal.h
> +++ b/include/uapi/linux/thermal.h
> @@ -20,7 +20,7 @@ enum thermal_trip_type {
>
>  /* Adding event notification support elements */
>  #define THERMAL_GENL_FAMILY_NAME               "thermal"
> -#define THERMAL_GENL_VERSION                   0x01
> +#define THERMAL_GENL_VERSION                   0x02
>  #define THERMAL_GENL_SAMPLING_GROUP_NAME       "sampling"
>  #define THERMAL_GENL_EVENT_GROUP_NAME          "event"
>
> @@ -30,6 +30,7 @@ enum thermal_genl_attr {
>         THERMAL_GENL_ATTR_TZ,
>         THERMAL_GENL_ATTR_TZ_ID,
>         THERMAL_GENL_ATTR_TZ_TEMP,
> +       THERMAL_GENL_ATTR_TZ_LAST_TEMP,
>         THERMAL_GENL_ATTR_TZ_TRIP,
>         THERMAL_GENL_ATTR_TZ_TRIP_ID,
>         THERMAL_GENL_ATTR_TZ_TRIP_TYPE,
> @@ -50,6 +51,10 @@ enum thermal_genl_attr {
>         THERMAL_GENL_ATTR_CPU_CAPABILITY_ID,
>         THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE,
>         THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY,
> +       THERMAL_GENL_ATTR_THRESHOLD,
> +       THERMAL_GENL_ATTR_THRESHOLD_TEMP,
> +       THERMAL_GENL_ATTR_THRESHOLD_WAY,
> +       THERMAL_GENL_ATTR_THRESHOLD_PID,
>         __THERMAL_GENL_ATTR_MAX,
>  };
>  #define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1)
> @@ -77,6 +82,11 @@ enum thermal_genl_event {
>         THERMAL_GENL_EVENT_CDEV_STATE_UPDATE,   /* Cdev state updated */
>         THERMAL_GENL_EVENT_TZ_GOV_CHANGE,       /* Governor policy changed  */
>         THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE,       /* CPU capability changed */
> +       THERMAL_GENL_EVENT_THRESHOLD_ADD,       /* A thresold has been added */
> +       THERMAL_GENL_EVENT_THRESHOLD_DELETE,    /* A thresold has been deleted */
> +       THERMAL_GENL_EVENT_THRESHOLD_FLUSH,     /* All thresolds have been deleted */
> +       THERMAL_GENL_EVENT_THRESHOLD_UP,        /* A thresold has been crossed the way up */
> +       THERMAL_GENL_EVENT_THRESHOLD_DOWN,      /* A thresold has been crossed the way down */
>         __THERMAL_GENL_EVENT_MAX,
>  };
>  #define THERMAL_GENL_EVENT_MAX (__THERMAL_GENL_EVENT_MAX - 1)
> @@ -84,12 +94,16 @@ enum thermal_genl_event {
>  /* Commands supported by the thermal_genl_family */
>  enum thermal_genl_cmd {
>         THERMAL_GENL_CMD_UNSPEC,
> -       THERMAL_GENL_CMD_TZ_GET_ID,     /* List of thermal zones id */
> -       THERMAL_GENL_CMD_TZ_GET_TRIP,   /* List of thermal trips */
> -       THERMAL_GENL_CMD_TZ_GET_TEMP,   /* Get the thermal zone temperature */
> -       THERMAL_GENL_CMD_TZ_GET_GOV,    /* Get the thermal zone governor */
> -       THERMAL_GENL_CMD_TZ_GET_MODE,   /* Get the thermal zone mode */
> -       THERMAL_GENL_CMD_CDEV_GET,      /* List of cdev id */
> +       THERMAL_GENL_CMD_TZ_GET_ID,             /* List of thermal zones id */
> +       THERMAL_GENL_CMD_TZ_GET_TRIP,           /* List of thermal trips */
> +       THERMAL_GENL_CMD_TZ_GET_TEMP,           /* Get the thermal zone temperature */
> +       THERMAL_GENL_CMD_TZ_GET_GOV,            /* Get the thermal zone governor */
> +       THERMAL_GENL_CMD_TZ_GET_MODE,           /* Get the thermal zone mode */
> +       THERMAL_GENL_CMD_CDEV_GET,              /* List of cdev id */
> +       THERMAL_GENL_CMD_THRESHOLD_GET,         /* List of thresholds */
> +       THERMAL_GENL_CMD_THRESHOLD_ADD,         /* Add a threshold */
> +       THERMAL_GENL_CMD_THRESHOLD_DELETE,      /* Delete a threshold */
> +       THERMAL_GENL_CMD_THRESHOLD_FLUSH,       /* Flush all the thresholds */
>         __THERMAL_GENL_CMD_MAX,
>  };
>  #define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1)
> --
> 2.43.0
>
>
Daniel Lezcano Oct. 12, 2024, 5:36 p.m. UTC | #2
On 02/10/2024 14:53, Rafael J. Wysocki wrote:
> On Mon, Sep 23, 2024 at 12:00 PM Daniel Lezcano
> <daniel.lezcano@linaro.org> wrote:
>>
>> The thresholds exist but there is no notification neither action code
>> related to them yet.
>>
>> These changes implement the netlink for the notifications when the
>> thresholds are crossed, added, deleted or flushed as well as the
>> commands which allows to get the list of the thresholds, flush them,
>> add and delete.
> 
> The last three commands need special privileges I would think because
> they essentially cause the kernel to either allocate or free memory
> and they may interfere with what the other processes in user space do.
> For instance, one process may flush the thresholds for a given thermal
> zone while another process is using them.
> 
> What controls the level of privilege required to use these commands?

I would say we can start with CAP_SYS_ADMIN and then if needed we can 
introduce later a new capability CAP_SYS_THERMAL ?

>> As different processes in userspace can interact with the thresholds,
>> the process id responsible of the action (add, delete or flush) will
>> be added in the notification.
> 
> This may leak PIDs between containers which has been pointed out as an
> issue (for example, see
> https://lore.kernel.org/linux-pm/20240704-umsatz-drollig-38db6b84da7b@brauner/).

> OTOH, the thermal engine should not need the extra information because
> it knows which thresholds were added by it, so any other thresholds
> would be added by someone else, wouldn't they?

Ok I will remove this information.

>> This way a thermal engine is able to
>> detect if another process is interfering with the thresholds. A
>> process id of zero is the kernel as it is by convention usually.
>>
>> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
>> ---
>>   drivers/thermal/thermal_netlink.c    | 239 ++++++++++++++++++++++++++-
>>   drivers/thermal/thermal_netlink.h    |  34 ++++
>>   drivers/thermal/thermal_thresholds.c |  38 +++--
>>   drivers/thermal/thermal_thresholds.h |   6 +-
>>   include/uapi/linux/thermal.h         |  28 +++-
>>   5 files changed, 313 insertions(+), 32 deletions(-)
>>
>> diff --git a/drivers/thermal/thermal_netlink.c b/drivers/thermal/thermal_netlink.c
>> index 97157c453630..8d92ebeb72fc 100644
>> --- a/drivers/thermal/thermal_netlink.c
>> +++ b/drivers/thermal/thermal_netlink.c
>> @@ -9,6 +9,7 @@
>>   #include <linux/module.h>
>>   #include <linux/notifier.h>
>>   #include <linux/kernel.h>
>> +#include <net/sock.h>
>>   #include <net/genetlink.h>
>>   #include <uapi/linux/thermal.h>
>>
>> @@ -49,12 +50,19 @@ static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] =
>>          [THERMAL_GENL_ATTR_CPU_CAPABILITY_ID]           = { .type = NLA_U32 },
>>          [THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE]  = { .type = NLA_U32 },
>>          [THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY]   = { .type = NLA_U32 },
>> +
>> +       /* Thresholds */
>> +       [THERMAL_GENL_ATTR_THRESHOLD]           = { .type = NLA_NESTED },
>> +       [THERMAL_GENL_ATTR_THRESHOLD_TEMP]      = { .type = NLA_U32 },
>> +       [THERMAL_GENL_ATTR_THRESHOLD_WAY]       = { .type = NLA_U32 },
> 
> It would be better to call this THERMAL_GENL_ATTR_THRESHOLD_DIR IMV.

Sure

>> +       [THERMAL_GENL_ATTR_THRESHOLD_PID]       = { .type = NLA_U32 },
>>   };
>>
>>   struct param {
>>          struct nlattr **attrs;
>>          struct sk_buff *msg;
>>          const char *name;
>> +       pid_t pid;
> 
> I'd rather not add it as mentioned above.

ok, no pid info

>>          int tz_id;
>>          int cdev_id;
>>          int trip_id;
>> @@ -62,6 +70,8 @@ struct param {
>>          int trip_type;
>>          int trip_hyst;
>>          int temp;
>> +       int last_temp;
> 
> Or prev_temp?  It should be less ambiguous than last_temp.

Makes sens

>> +       int direction;
>>          int cdev_state;
>>          int cdev_max_state;
>>          struct thermal_genl_cpu_caps *cpu_capabilities;
>> @@ -234,6 +244,36 @@ static int thermal_genl_event_cpu_capability_change(struct param *p)
>>          return -EMSGSIZE;
>>   }

[ ... ]

>> +static int thermal_genl_cmd_threshold_get(struct param *p)
>> +{
>> +       struct thermal_zone_device *tz;
>> +       struct sk_buff *msg = p->msg;
>> +       struct nlattr *start_trip;
>> +       int id, ret;
>> +
>> +       if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
>> +               return -EINVAL;
>> +
>> +       id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
>> +
>> +       tz = thermal_zone_get_by_id(id);
>> +       if (!tz)
>> +               return -EINVAL;
> 
> What prevents the thermal zone from going away right here?
> 
> It looks like thermal_zone_get_by_id() should do a get_device() on the
> thermal zone object it is about to return and then the caller should
> do a put_device() on it.
> 
> Granted, this problem is present already in the thermal netlink code,
> so it needs to be fixed (I'm going to send a patch to address it) and
> this patch will need to be adjusted.

Ok, I'll have a look at the recent patches

>> +
>> +       start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_THRESHOLD);
>> +       if (!start_trip)
>> +               return -EMSGSIZE;
>> +
>> +       mutex_lock(&tz->lock);
>> +       ret = thermal_thresholds_for_each(tz, __thermal_genl_cmd_threshold_get, msg);
>> +       mutex_unlock(&tz->lock);
> 
> I think that the locking can be moved to
> thermal_thresholds_for_each().  At least there are no other callers of
> it AFAICS.
> 

Ok

Thanks for the review

   -- Daniel
diff mbox series

Patch

diff --git a/drivers/thermal/thermal_netlink.c b/drivers/thermal/thermal_netlink.c
index 97157c453630..8d92ebeb72fc 100644
--- a/drivers/thermal/thermal_netlink.c
+++ b/drivers/thermal/thermal_netlink.c
@@ -9,6 +9,7 @@ 
 #include <linux/module.h>
 #include <linux/notifier.h>
 #include <linux/kernel.h>
+#include <net/sock.h>
 #include <net/genetlink.h>
 #include <uapi/linux/thermal.h>
 
@@ -49,12 +50,19 @@  static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] =
 	[THERMAL_GENL_ATTR_CPU_CAPABILITY_ID]		= { .type = NLA_U32 },
 	[THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE]	= { .type = NLA_U32 },
 	[THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY]	= { .type = NLA_U32 },
+
+	/* Thresholds */
+	[THERMAL_GENL_ATTR_THRESHOLD]		= { .type = NLA_NESTED },
+	[THERMAL_GENL_ATTR_THRESHOLD_TEMP]	= { .type = NLA_U32 },
+	[THERMAL_GENL_ATTR_THRESHOLD_WAY]	= { .type = NLA_U32 },
+	[THERMAL_GENL_ATTR_THRESHOLD_PID]	= { .type = NLA_U32 },
 };
 
 struct param {
 	struct nlattr **attrs;
 	struct sk_buff *msg;
 	const char *name;
+	pid_t pid;
 	int tz_id;
 	int cdev_id;
 	int trip_id;
@@ -62,6 +70,8 @@  struct param {
 	int trip_type;
 	int trip_hyst;
 	int temp;
+	int last_temp;
+	int direction;
 	int cdev_state;
 	int cdev_max_state;
 	struct thermal_genl_cpu_caps *cpu_capabilities;
@@ -234,6 +244,36 @@  static int thermal_genl_event_cpu_capability_change(struct param *p)
 	return -EMSGSIZE;
 }
 
+static int thermal_genl_event_threshold_add(struct param *p)
+{
+	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
+	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, p->temp) ||
+	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_WAY, p->direction) ||
+	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_PID, p->pid))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int thermal_genl_event_threshold_flush(struct param *p)
+{
+	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id),
+	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_PID, p->pid))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int thermal_genl_event_threshold_up(struct param *p)
+{
+	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
+	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_LAST_TEMP, p->last_temp) ||
+	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TEMP, p->temp))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
 int thermal_genl_event_tz_delete(struct param *p)
 	__attribute__((alias("thermal_genl_event_tz")));
 
@@ -246,6 +286,12 @@  int thermal_genl_event_tz_disable(struct param *p)
 int thermal_genl_event_tz_trip_down(struct param *p)
 	__attribute__((alias("thermal_genl_event_tz_trip_up")));
 
+int thermal_genl_event_threshold_delete(struct param *p)
+	__attribute__((alias("thermal_genl_event_threshold_add")));
+
+int thermal_genl_event_threshold_down(struct param *p)
+	__attribute__((alias("thermal_genl_event_threshold_up")));
+
 static cb_t event_cb[] = {
 	[THERMAL_GENL_EVENT_TZ_CREATE]		= thermal_genl_event_tz_create,
 	[THERMAL_GENL_EVENT_TZ_DELETE]		= thermal_genl_event_tz_delete,
@@ -259,6 +305,11 @@  static cb_t event_cb[] = {
 	[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE]	= thermal_genl_event_cdev_state_update,
 	[THERMAL_GENL_EVENT_TZ_GOV_CHANGE]	= thermal_genl_event_gov_change,
 	[THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE] = thermal_genl_event_cpu_capability_change,
+	[THERMAL_GENL_EVENT_THRESHOLD_ADD]	= thermal_genl_event_threshold_add,
+	[THERMAL_GENL_EVENT_THRESHOLD_DELETE]	= thermal_genl_event_threshold_delete,
+	[THERMAL_GENL_EVENT_THRESHOLD_FLUSH]	= thermal_genl_event_threshold_flush,
+	[THERMAL_GENL_EVENT_THRESHOLD_DOWN]	= thermal_genl_event_threshold_down,
+	[THERMAL_GENL_EVENT_THRESHOLD_UP]	= thermal_genl_event_threshold_up,
 };
 
 /*
@@ -401,6 +452,43 @@  int thermal_genl_cpu_capability_event(int count,
 }
 EXPORT_SYMBOL_GPL(thermal_genl_cpu_capability_event);
 
+int thermal_notify_threshold_add(const struct thermal_zone_device *tz,
+				 int temperature, int direction, int pid)
+{
+	struct param p = { .tz_id = tz->id, .temp = temperature, .direction = direction, .pid = pid };
+
+	return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_ADD, &p);
+}
+
+int thermal_notify_threshold_delete(const struct thermal_zone_device *tz,
+				    int temperature, int direction, int pid)
+{
+	struct param p = { .tz_id = tz->id, .temp = temperature, .direction = direction, .pid = pid };
+
+	return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_DELETE, &p);
+}
+
+int thermal_notify_threshold_flush(const struct thermal_zone_device *tz, int pid)
+{
+	struct param p = { .tz_id = tz->id, .pid = pid };
+
+	return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_FLUSH, &p);
+}
+
+int thermal_notify_threshold_down(const struct thermal_zone_device *tz)
+{
+	struct param p = { .tz_id = tz->id, .temp = tz->temperature, .last_temp = tz->last_temperature };
+
+	return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_DOWN, &p);
+}
+
+int thermal_notify_threshold_up(const struct thermal_zone_device *tz)
+{
+	struct param p = { .tz_id = tz->id, .temp = tz->temperature, .last_temp = tz->last_temperature };
+
+	return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_UP, &p);
+}
+
 /*************************** Command encoding ********************************/
 
 static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz,
@@ -575,12 +663,130 @@  static int thermal_genl_cmd_cdev_get(struct param *p)
 	return ret;
 }
 
+static int __thermal_genl_cmd_threshold_get(struct user_threshold *threshold, void *arg)
+{
+	struct sk_buff *msg = arg;
+
+	if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, threshold->temperature) ||
+	    nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_WAY, threshold->direction))
+		return -1;
+
+	return 0;
+}
+
+static int thermal_genl_cmd_threshold_get(struct param *p)
+{
+	struct thermal_zone_device *tz;
+	struct sk_buff *msg = p->msg;
+	struct nlattr *start_trip;
+	int id, ret;
+
+	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
+		return -EINVAL;
+
+	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
+
+	tz = thermal_zone_get_by_id(id);
+	if (!tz)
+		return -EINVAL;
+
+	start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_THRESHOLD);
+	if (!start_trip)
+		return -EMSGSIZE;
+
+	mutex_lock(&tz->lock);
+	ret = thermal_thresholds_for_each(tz, __thermal_genl_cmd_threshold_get, msg);
+	mutex_unlock(&tz->lock);
+
+	if (ret)
+		return -EMSGSIZE;	
+
+	nla_nest_end(msg, start_trip);
+
+	return 0;
+}
+
+static int thermal_genl_cmd_threshold_add(struct param *p)
+{
+	struct thermal_zone_device *tz;
+	int id, temp, direction, ret = 0;
+
+	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID] ||
+	    !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP] ||
+	    !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_WAY])
+		return -EINVAL;
+
+	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
+	temp = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]);
+	direction = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_WAY]);
+
+	tz = thermal_zone_get_by_id(id);
+	if (!tz)
+		return -EINVAL;
+
+	mutex_lock(&tz->lock);
+	ret = thermal_thresholds_add(tz, temp, direction, p->pid);
+	mutex_unlock(&tz->lock);
+
+	return ret;
+}
+
+static int thermal_genl_cmd_threshold_delete(struct param *p)
+{
+	struct thermal_zone_device *tz;
+	int id, temp, direction, ret = 0;
+
+	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID] ||
+	    !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP] ||
+	    !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_WAY])
+		return -EINVAL;
+
+	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
+	temp = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]);
+	direction = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_WAY]);
+
+	tz = thermal_zone_get_by_id(id);
+	if (!tz)
+		return -EINVAL;
+
+	mutex_lock(&tz->lock);
+	ret = thermal_thresholds_delete(tz, temp, direction, p->pid);
+	mutex_unlock(&tz->lock);
+
+	return ret;
+}
+
+static int thermal_genl_cmd_threshold_flush(struct param *p)
+{
+	struct thermal_zone_device *tz;
+	int id;
+
+	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
+		return -EINVAL;
+
+	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
+
+	tz = thermal_zone_get_by_id(id);
+	if (!tz)
+		return -EINVAL;
+
+	mutex_lock(&tz->lock);
+	thermal_thresholds_flush(tz, p->pid);
+	mutex_unlock(&tz->lock);
+
+	return 0;
+}
+
 static cb_t cmd_cb[] = {
-	[THERMAL_GENL_CMD_TZ_GET_ID]	= thermal_genl_cmd_tz_get_id,
-	[THERMAL_GENL_CMD_TZ_GET_TRIP]	= thermal_genl_cmd_tz_get_trip,
-	[THERMAL_GENL_CMD_TZ_GET_TEMP]	= thermal_genl_cmd_tz_get_temp,
-	[THERMAL_GENL_CMD_TZ_GET_GOV]	= thermal_genl_cmd_tz_get_gov,
-	[THERMAL_GENL_CMD_CDEV_GET]	= thermal_genl_cmd_cdev_get,
+	[THERMAL_GENL_CMD_TZ_GET_ID]		= thermal_genl_cmd_tz_get_id,
+	[THERMAL_GENL_CMD_TZ_GET_TRIP]		= thermal_genl_cmd_tz_get_trip,
+	[THERMAL_GENL_CMD_TZ_GET_TEMP]		= thermal_genl_cmd_tz_get_temp,
+	[THERMAL_GENL_CMD_TZ_GET_GOV]		= thermal_genl_cmd_tz_get_gov,
+	[THERMAL_GENL_CMD_CDEV_GET]		= thermal_genl_cmd_cdev_get,
+	[THERMAL_GENL_CMD_THRESHOLD_GET]	= thermal_genl_cmd_threshold_get,
+	[THERMAL_GENL_CMD_THRESHOLD_ADD]	= thermal_genl_cmd_threshold_add,
+	[THERMAL_GENL_CMD_THRESHOLD_DELETE]	= thermal_genl_cmd_threshold_delete,
+	[THERMAL_GENL_CMD_THRESHOLD_FLUSH]	= thermal_genl_cmd_threshold_flush,
 };
 
 static int thermal_genl_cmd_dumpit(struct sk_buff *skb,
@@ -623,6 +829,7 @@  static int thermal_genl_cmd_doit(struct sk_buff *skb,
 	if (!msg)
 		return -ENOMEM;
 	p.msg = msg;
+	p.pid = task_tgid_vnr(current);
 
 	hdr = genlmsg_put_reply(msg, info, &thermal_genl_family, 0, cmd);
 	if (!hdr)
@@ -691,6 +898,26 @@  static const struct genl_small_ops thermal_genl_ops[] = {
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.dumpit = thermal_genl_cmd_dumpit,
 	},
+	{
+		.cmd = THERMAL_GENL_CMD_THRESHOLD_GET,
+		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+		.doit = thermal_genl_cmd_doit,
+	},
+	{
+		.cmd = THERMAL_GENL_CMD_THRESHOLD_ADD,
+		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+		.doit = thermal_genl_cmd_doit,
+	},
+	{
+		.cmd = THERMAL_GENL_CMD_THRESHOLD_DELETE,
+		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+		.doit = thermal_genl_cmd_doit,
+	},
+	{
+		.cmd = THERMAL_GENL_CMD_THRESHOLD_FLUSH,
+		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+		.doit = thermal_genl_cmd_doit,
+	},
 };
 
 static struct genl_family thermal_genl_family __ro_after_init = {
@@ -703,7 +930,7 @@  static struct genl_family thermal_genl_family __ro_after_init = {
 	.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,
+	.resv_start_op	= __THERMAL_GENL_CMD_MAX,
 	.mcgrps		= thermal_genl_mcgrps,
 	.n_mcgrps	= ARRAY_SIZE(thermal_genl_mcgrps),
 };
diff --git a/drivers/thermal/thermal_netlink.h b/drivers/thermal/thermal_netlink.h
index e01221e8816b..3f85c636029c 100644
--- a/drivers/thermal/thermal_netlink.h
+++ b/drivers/thermal/thermal_netlink.h
@@ -53,6 +53,13 @@  int thermal_notify_tz_gov_change(const struct thermal_zone_device *tz,
 int thermal_genl_sampling_temp(int id, int temp);
 int thermal_genl_cpu_capability_event(int count,
 				      struct thermal_genl_cpu_caps *caps);
+int thermal_notify_threshold_add(const struct thermal_zone_device *tz,
+				 int temperature, int direction, pid_t pid);
+int thermal_notify_threshold_delete(const struct thermal_zone_device *tz,
+				    int temperature, int direction, pid_t pid);
+int thermal_notify_threshold_flush(const struct thermal_zone_device *tz, pid_t pid);
+int thermal_notify_threshold_down(const struct thermal_zone_device *tz);
+int thermal_notify_threshold_up(const struct thermal_zone_device *tz);
 #else
 static inline int thermal_netlink_init(void)
 {
@@ -139,6 +146,33 @@  static inline int thermal_genl_cpu_capability_event(int count, struct thermal_ge
 	return 0;
 }
 
+static inline int thermal_notify_threshold_add(const struct thermal_zone_device *tz,
+					       int temperature, int direction, pid_t pid)
+{
+	return 0;
+}
+
+static inline int thermal_notify_threshold_delete(const struct thermal_zone_device *tz,
+						  int temperature, int direction, pid_t pid)
+{
+	return 0;
+}
+
+static inline int thermal_notify_threshold_flush(const struct thermal_zone_device *tz, pid_t pid)
+{
+	return 0;
+}
+
+static inline int thermal_notify_threshold_down(const struct thermal_zone_device *tz)
+{
+	return 0;
+}
+
+static inline int thermal_notify_threshold_up(const struct thermal_zone_device *tz)
+{
+	return 0;
+}
+
 static inline void __init thermal_netlink_exit(void) {}
 
 #endif /* CONFIG_THERMAL_NETLINK */
diff --git a/drivers/thermal/thermal_thresholds.c b/drivers/thermal/thermal_thresholds.c
index f33b6d5474d8..6d9a0cf8031f 100644
--- a/drivers/thermal/thermal_thresholds.c
+++ b/drivers/thermal/thermal_thresholds.c
@@ -20,7 +20,7 @@  int thermal_thresholds_init(struct thermal_zone_device *tz)
 	return 0;
 }
 
-void thermal_thresholds_flush(struct thermal_zone_device *tz)
+void thermal_thresholds_flush(struct thermal_zone_device *tz, pid_t pid)
 {
 	struct list_head *thresholds = &tz->user_thresholds;
 	struct user_threshold *entry, *tmp;
@@ -32,12 +32,14 @@  void thermal_thresholds_flush(struct thermal_zone_device *tz)
 		kfree(entry);
 	}
 
+	thermal_notify_threshold_flush(tz, pid);
+
 	__thermal_zone_device_update(tz, THERMAL_TZ_FLUSH_THRESHOLDS);
 }
 
 void thermal_thresholds_exit(struct thermal_zone_device *tz)
 {
-	thermal_thresholds_flush(tz);
+	thermal_thresholds_flush(tz, 0);
 }
 
 static int __thermal_thresholds_cmp(void *data,
@@ -122,7 +124,6 @@  void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *hi
 
 	int temperature = tz->temperature;
 	int last_temperature = tz->last_temperature;
-	bool notify;
 
 	lockdep_assert_held(&tz->lock);
 
@@ -144,19 +145,19 @@  void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *hi
 	 * - increased : thresholds are crossed the way up
 	 * - decreased : thresholds are crossed the way down
 	 */
-	if (temperature > last_temperature)
-		notify = thermal_thresholds_handle_raising(thresholds, temperature,
-							   last_temperature, low, high);
-	else
-		notify = thermal_thresholds_handle_dropping(thresholds, temperature,
-							    last_temperature, low, high);
-
-	if (notify)
-		pr_debug("A threshold has been crossed the way %s, with a temperature=%d, last_temperature=%d\n",
-			 temperature > last_temperature ? "up" : "down", temperature, last_temperature);
+	if (temperature > last_temperature) {
+		if (thermal_thresholds_handle_raising(thresholds, temperature,
+						      last_temperature, low, high))
+			thermal_notify_threshold_up(tz);
+	} else {
+		if (thermal_thresholds_handle_dropping(thresholds, temperature,
+						       last_temperature, low, high))
+			thermal_notify_threshold_down(tz);
+	}
 }
 
-int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction)
+int thermal_thresholds_add(struct thermal_zone_device *tz,
+			   int temperature, int direction, pid_t pid)
 {
 	struct list_head *thresholds = &tz->user_thresholds;
 	struct user_threshold *t;
@@ -182,12 +183,15 @@  int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int
 		list_sort(NULL, thresholds, __thermal_thresholds_cmp);
 	}
 
+	thermal_notify_threshold_add(tz, temperature, direction, pid);
+
 	__thermal_zone_device_update(tz, THERMAL_TZ_ADD_THRESHOLD);
 
 	return 0;
 }
 
-int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction)
+int thermal_thresholds_delete(struct thermal_zone_device *tz,
+			      int temperature, int direction, pid_t pid)
 {
 	struct list_head *thresholds = &tz->user_thresholds;
 	struct user_threshold *t;
@@ -205,8 +209,10 @@  int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, i
 		t->direction &= ~direction;
 	}
 
-	__thermal_zone_device_update(tz, THERMAL_TZ_DEL_THRESHOLD);
+	thermal_notify_threshold_delete(tz, temperature, direction, pid);
 
+	__thermal_zone_device_update(tz, THERMAL_TZ_DEL_THRESHOLD);
+	
 	return 0;
 }
 
diff --git a/drivers/thermal/thermal_thresholds.h b/drivers/thermal/thermal_thresholds.h
index 232f4e8089af..f97ffb715d2e 100644
--- a/drivers/thermal/thermal_thresholds.h
+++ b/drivers/thermal/thermal_thresholds.h
@@ -10,10 +10,10 @@  struct user_threshold {
 
 int thermal_thresholds_init(struct thermal_zone_device *tz);
 void thermal_thresholds_exit(struct thermal_zone_device *tz);
-void thermal_thresholds_flush(struct thermal_zone_device *tz);
 void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high);
-int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction);
-int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction);
+void thermal_thresholds_flush(struct thermal_zone_device *tz, pid_t pid);
+int thermal_thresholds_add(struct thermal_zone_device *tz, int temperature, int direction, pid_t pid);
+int thermal_thresholds_delete(struct thermal_zone_device *tz, int temperature, int direction, pid_t pid);
 int thermal_thresholds_for_each(struct thermal_zone_device *tz,
 				int (*cb)(struct user_threshold *, void *arg), void *arg);
 #endif
diff --git a/include/uapi/linux/thermal.h b/include/uapi/linux/thermal.h
index 3e7c1c2e71a7..bcbaf62a1727 100644
--- a/include/uapi/linux/thermal.h
+++ b/include/uapi/linux/thermal.h
@@ -20,7 +20,7 @@  enum thermal_trip_type {
 
 /* Adding event notification support elements */
 #define THERMAL_GENL_FAMILY_NAME		"thermal"
-#define THERMAL_GENL_VERSION			0x01
+#define THERMAL_GENL_VERSION			0x02
 #define THERMAL_GENL_SAMPLING_GROUP_NAME	"sampling"
 #define THERMAL_GENL_EVENT_GROUP_NAME		"event"
 
@@ -30,6 +30,7 @@  enum thermal_genl_attr {
 	THERMAL_GENL_ATTR_TZ,
 	THERMAL_GENL_ATTR_TZ_ID,
 	THERMAL_GENL_ATTR_TZ_TEMP,
+	THERMAL_GENL_ATTR_TZ_LAST_TEMP,
 	THERMAL_GENL_ATTR_TZ_TRIP,
 	THERMAL_GENL_ATTR_TZ_TRIP_ID,
 	THERMAL_GENL_ATTR_TZ_TRIP_TYPE,
@@ -50,6 +51,10 @@  enum thermal_genl_attr {
 	THERMAL_GENL_ATTR_CPU_CAPABILITY_ID,
 	THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE,
 	THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY,
+	THERMAL_GENL_ATTR_THRESHOLD,
+	THERMAL_GENL_ATTR_THRESHOLD_TEMP,
+	THERMAL_GENL_ATTR_THRESHOLD_WAY,
+	THERMAL_GENL_ATTR_THRESHOLD_PID,
 	__THERMAL_GENL_ATTR_MAX,
 };
 #define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1)
@@ -77,6 +82,11 @@  enum thermal_genl_event {
 	THERMAL_GENL_EVENT_CDEV_STATE_UPDATE,	/* Cdev state updated */
 	THERMAL_GENL_EVENT_TZ_GOV_CHANGE,	/* Governor policy changed  */
 	THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE,	/* CPU capability changed */
+	THERMAL_GENL_EVENT_THRESHOLD_ADD,	/* A thresold has been added */
+	THERMAL_GENL_EVENT_THRESHOLD_DELETE,	/* A thresold has been deleted */
+	THERMAL_GENL_EVENT_THRESHOLD_FLUSH,	/* All thresolds have been deleted */
+	THERMAL_GENL_EVENT_THRESHOLD_UP,	/* A thresold has been crossed the way up */
+	THERMAL_GENL_EVENT_THRESHOLD_DOWN,	/* A thresold has been crossed the way down */
 	__THERMAL_GENL_EVENT_MAX,
 };
 #define THERMAL_GENL_EVENT_MAX (__THERMAL_GENL_EVENT_MAX - 1)
@@ -84,12 +94,16 @@  enum thermal_genl_event {
 /* Commands supported by the thermal_genl_family */
 enum thermal_genl_cmd {
 	THERMAL_GENL_CMD_UNSPEC,
-	THERMAL_GENL_CMD_TZ_GET_ID,	/* List of thermal zones id */
-	THERMAL_GENL_CMD_TZ_GET_TRIP,	/* List of thermal trips */
-	THERMAL_GENL_CMD_TZ_GET_TEMP,	/* Get the thermal zone temperature */
-	THERMAL_GENL_CMD_TZ_GET_GOV,	/* Get the thermal zone governor */
-	THERMAL_GENL_CMD_TZ_GET_MODE,	/* Get the thermal zone mode */
-	THERMAL_GENL_CMD_CDEV_GET,	/* List of cdev id */
+	THERMAL_GENL_CMD_TZ_GET_ID,		/* List of thermal zones id */
+	THERMAL_GENL_CMD_TZ_GET_TRIP,		/* List of thermal trips */
+	THERMAL_GENL_CMD_TZ_GET_TEMP,		/* Get the thermal zone temperature */
+	THERMAL_GENL_CMD_TZ_GET_GOV,		/* Get the thermal zone governor */
+	THERMAL_GENL_CMD_TZ_GET_MODE,		/* Get the thermal zone mode */
+	THERMAL_GENL_CMD_CDEV_GET,		/* List of cdev id */
+	THERMAL_GENL_CMD_THRESHOLD_GET,		/* List of thresholds */
+	THERMAL_GENL_CMD_THRESHOLD_ADD,		/* Add a threshold */
+	THERMAL_GENL_CMD_THRESHOLD_DELETE,	/* Delete a threshold */
+	THERMAL_GENL_CMD_THRESHOLD_FLUSH,	/* Flush all the thresholds */
 	__THERMAL_GENL_CMD_MAX,
 };
 #define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1)