Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright 2020 Linaro Limited
0004  *
0005  * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
0006  *
0007  * Generic netlink for thermal management framework
0008  */
0009 #include <linux/module.h>
0010 #include <linux/kernel.h>
0011 #include <net/genetlink.h>
0012 #include <uapi/linux/thermal.h>
0013 
0014 #include "thermal_core.h"
0015 
0016 static const struct genl_multicast_group thermal_genl_mcgrps[] = {
0017     { .name = THERMAL_GENL_SAMPLING_GROUP_NAME, },
0018     { .name = THERMAL_GENL_EVENT_GROUP_NAME,  },
0019 };
0020 
0021 static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = {
0022     /* Thermal zone */
0023     [THERMAL_GENL_ATTR_TZ]          = { .type = NLA_NESTED },
0024     [THERMAL_GENL_ATTR_TZ_ID]       = { .type = NLA_U32 },
0025     [THERMAL_GENL_ATTR_TZ_TEMP]     = { .type = NLA_U32 },
0026     [THERMAL_GENL_ATTR_TZ_TRIP]     = { .type = NLA_NESTED },
0027     [THERMAL_GENL_ATTR_TZ_TRIP_ID]      = { .type = NLA_U32 },
0028     [THERMAL_GENL_ATTR_TZ_TRIP_TEMP]    = { .type = NLA_U32 },
0029     [THERMAL_GENL_ATTR_TZ_TRIP_TYPE]    = { .type = NLA_U32 },
0030     [THERMAL_GENL_ATTR_TZ_TRIP_HYST]    = { .type = NLA_U32 },
0031     [THERMAL_GENL_ATTR_TZ_MODE]     = { .type = NLA_U32 },
0032     [THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT]  = { .type = NLA_U32 },
0033     [THERMAL_GENL_ATTR_TZ_NAME]     = { .type = NLA_STRING,
0034                             .len = THERMAL_NAME_LENGTH },
0035     /* Governor(s) */
0036     [THERMAL_GENL_ATTR_TZ_GOV]      = { .type = NLA_NESTED },
0037     [THERMAL_GENL_ATTR_TZ_GOV_NAME]     = { .type = NLA_STRING,
0038                             .len = THERMAL_NAME_LENGTH },
0039     /* Cooling devices */
0040     [THERMAL_GENL_ATTR_CDEV]        = { .type = NLA_NESTED },
0041     [THERMAL_GENL_ATTR_CDEV_ID]     = { .type = NLA_U32 },
0042     [THERMAL_GENL_ATTR_CDEV_CUR_STATE]  = { .type = NLA_U32 },
0043     [THERMAL_GENL_ATTR_CDEV_MAX_STATE]  = { .type = NLA_U32 },
0044     [THERMAL_GENL_ATTR_CDEV_NAME]       = { .type = NLA_STRING,
0045                             .len = THERMAL_NAME_LENGTH },
0046     /* CPU capabilities */
0047     [THERMAL_GENL_ATTR_CPU_CAPABILITY]      = { .type = NLA_NESTED },
0048     [THERMAL_GENL_ATTR_CPU_CAPABILITY_ID]       = { .type = NLA_U32 },
0049     [THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE]  = { .type = NLA_U32 },
0050     [THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY]   = { .type = NLA_U32 },
0051 };
0052 
0053 struct param {
0054     struct nlattr **attrs;
0055     struct sk_buff *msg;
0056     const char *name;
0057     int tz_id;
0058     int cdev_id;
0059     int trip_id;
0060     int trip_temp;
0061     int trip_type;
0062     int trip_hyst;
0063     int temp;
0064     int cdev_state;
0065     int cdev_max_state;
0066     struct thermal_genl_cpu_caps *cpu_capabilities;
0067     int cpu_capabilities_count;
0068 };
0069 
0070 typedef int (*cb_t)(struct param *);
0071 
0072 static struct genl_family thermal_gnl_family;
0073 
0074 /************************** Sampling encoding *******************************/
0075 
0076 int thermal_genl_sampling_temp(int id, int temp)
0077 {
0078     struct sk_buff *skb;
0079     void *hdr;
0080 
0081     skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
0082     if (!skb)
0083         return -ENOMEM;
0084 
0085     hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0,
0086               THERMAL_GENL_SAMPLING_TEMP);
0087     if (!hdr)
0088         goto out_free;
0089 
0090     if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_ID, id))
0091         goto out_cancel;
0092 
0093     if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_TEMP, temp))
0094         goto out_cancel;
0095 
0096     genlmsg_end(skb, hdr);
0097 
0098     genlmsg_multicast(&thermal_gnl_family, skb, 0, 0, GFP_KERNEL);
0099 
0100     return 0;
0101 out_cancel:
0102     genlmsg_cancel(skb, hdr);
0103 out_free:
0104     nlmsg_free(skb);
0105 
0106     return -EMSGSIZE;
0107 }
0108 
0109 /**************************** Event encoding *********************************/
0110 
0111 static int thermal_genl_event_tz_create(struct param *p)
0112 {
0113     if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
0114         nla_put_string(p->msg, THERMAL_GENL_ATTR_TZ_NAME, p->name))
0115         return -EMSGSIZE;
0116 
0117     return 0;
0118 }
0119 
0120 static int thermal_genl_event_tz(struct param *p)
0121 {
0122     if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id))
0123         return -EMSGSIZE;
0124 
0125     return 0;
0126 }
0127 
0128 static int thermal_genl_event_tz_trip_up(struct param *p)
0129 {
0130     if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
0131         nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) ||
0132         nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TEMP, p->temp))
0133         return -EMSGSIZE;
0134 
0135     return 0;
0136 }
0137 
0138 static int thermal_genl_event_tz_trip_add(struct param *p)
0139 {
0140     if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
0141         nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) ||
0142         nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, p->trip_type) ||
0143         nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, p->trip_temp) ||
0144         nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, p->trip_hyst))
0145         return -EMSGSIZE;
0146 
0147     return 0;
0148 }
0149 
0150 static int thermal_genl_event_tz_trip_delete(struct param *p)
0151 {
0152     if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
0153         nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id))
0154         return -EMSGSIZE;
0155 
0156     return 0;
0157 }
0158 
0159 static int thermal_genl_event_cdev_add(struct param *p)
0160 {
0161     if (nla_put_string(p->msg, THERMAL_GENL_ATTR_CDEV_NAME,
0162                p->name) ||
0163         nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
0164             p->cdev_id) ||
0165         nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_MAX_STATE,
0166             p->cdev_max_state))
0167         return -EMSGSIZE;
0168 
0169     return 0;
0170 }
0171 
0172 static int thermal_genl_event_cdev_delete(struct param *p)
0173 {
0174     if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID, p->cdev_id))
0175         return -EMSGSIZE;
0176 
0177     return 0;
0178 }
0179 
0180 static int thermal_genl_event_cdev_state_update(struct param *p)
0181 {
0182     if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
0183             p->cdev_id) ||
0184         nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_CUR_STATE,
0185             p->cdev_state))
0186         return -EMSGSIZE;
0187 
0188     return 0;
0189 }
0190 
0191 static int thermal_genl_event_gov_change(struct param *p)
0192 {
0193     if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
0194         nla_put_string(p->msg, THERMAL_GENL_ATTR_GOV_NAME, p->name))
0195         return -EMSGSIZE;
0196 
0197     return 0;
0198 }
0199 
0200 static int thermal_genl_event_cpu_capability_change(struct param *p)
0201 {
0202     struct thermal_genl_cpu_caps *cpu_cap = p->cpu_capabilities;
0203     struct sk_buff *msg = p->msg;
0204     struct nlattr *start_cap;
0205     int i;
0206 
0207     start_cap = nla_nest_start(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY);
0208     if (!start_cap)
0209         return -EMSGSIZE;
0210 
0211     for (i = 0; i < p->cpu_capabilities_count; ++i) {
0212         if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_ID,
0213                 cpu_cap->cpu))
0214             goto out_cancel_nest;
0215 
0216         if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE,
0217                 cpu_cap->performance))
0218             goto out_cancel_nest;
0219 
0220         if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY,
0221                 cpu_cap->efficiency))
0222             goto out_cancel_nest;
0223 
0224         ++cpu_cap;
0225     }
0226 
0227     nla_nest_end(msg, start_cap);
0228 
0229     return 0;
0230 out_cancel_nest:
0231     nla_nest_cancel(msg, start_cap);
0232 
0233     return -EMSGSIZE;
0234 }
0235 
0236 int thermal_genl_event_tz_delete(struct param *p)
0237     __attribute__((alias("thermal_genl_event_tz")));
0238 
0239 int thermal_genl_event_tz_enable(struct param *p)
0240     __attribute__((alias("thermal_genl_event_tz")));
0241 
0242 int thermal_genl_event_tz_disable(struct param *p)
0243     __attribute__((alias("thermal_genl_event_tz")));
0244 
0245 int thermal_genl_event_tz_trip_down(struct param *p)
0246     __attribute__((alias("thermal_genl_event_tz_trip_up")));
0247 
0248 int thermal_genl_event_tz_trip_change(struct param *p)
0249     __attribute__((alias("thermal_genl_event_tz_trip_add")));
0250 
0251 static cb_t event_cb[] = {
0252     [THERMAL_GENL_EVENT_TZ_CREATE]      = thermal_genl_event_tz_create,
0253     [THERMAL_GENL_EVENT_TZ_DELETE]      = thermal_genl_event_tz_delete,
0254     [THERMAL_GENL_EVENT_TZ_ENABLE]      = thermal_genl_event_tz_enable,
0255     [THERMAL_GENL_EVENT_TZ_DISABLE]     = thermal_genl_event_tz_disable,
0256     [THERMAL_GENL_EVENT_TZ_TRIP_UP]     = thermal_genl_event_tz_trip_up,
0257     [THERMAL_GENL_EVENT_TZ_TRIP_DOWN]   = thermal_genl_event_tz_trip_down,
0258     [THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = thermal_genl_event_tz_trip_change,
0259     [THERMAL_GENL_EVENT_TZ_TRIP_ADD]    = thermal_genl_event_tz_trip_add,
0260     [THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = thermal_genl_event_tz_trip_delete,
0261     [THERMAL_GENL_EVENT_CDEV_ADD]       = thermal_genl_event_cdev_add,
0262     [THERMAL_GENL_EVENT_CDEV_DELETE]    = thermal_genl_event_cdev_delete,
0263     [THERMAL_GENL_EVENT_CDEV_STATE_UPDATE]  = thermal_genl_event_cdev_state_update,
0264     [THERMAL_GENL_EVENT_TZ_GOV_CHANGE]  = thermal_genl_event_gov_change,
0265     [THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE] = thermal_genl_event_cpu_capability_change,
0266 };
0267 
0268 /*
0269  * Generic netlink event encoding
0270  */
0271 static int thermal_genl_send_event(enum thermal_genl_event event,
0272                    struct param *p)
0273 {
0274     struct sk_buff *msg;
0275     int ret = -EMSGSIZE;
0276     void *hdr;
0277 
0278     msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
0279     if (!msg)
0280         return -ENOMEM;
0281     p->msg = msg;
0282 
0283     hdr = genlmsg_put(msg, 0, 0, &thermal_gnl_family, 0, event);
0284     if (!hdr)
0285         goto out_free_msg;
0286 
0287     ret = event_cb[event](p);
0288     if (ret)
0289         goto out_cancel_msg;
0290 
0291     genlmsg_end(msg, hdr);
0292 
0293     genlmsg_multicast(&thermal_gnl_family, msg, 0, 1, GFP_KERNEL);
0294 
0295     return 0;
0296 
0297 out_cancel_msg:
0298     genlmsg_cancel(msg, hdr);
0299 out_free_msg:
0300     nlmsg_free(msg);
0301 
0302     return ret;
0303 }
0304 
0305 int thermal_notify_tz_create(int tz_id, const char *name)
0306 {
0307     struct param p = { .tz_id = tz_id, .name = name };
0308 
0309     return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_CREATE, &p);
0310 }
0311 
0312 int thermal_notify_tz_delete(int tz_id)
0313 {
0314     struct param p = { .tz_id = tz_id };
0315 
0316     return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DELETE, &p);
0317 }
0318 
0319 int thermal_notify_tz_enable(int tz_id)
0320 {
0321     struct param p = { .tz_id = tz_id };
0322 
0323     return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_ENABLE, &p);
0324 }
0325 
0326 int thermal_notify_tz_disable(int tz_id)
0327 {
0328     struct param p = { .tz_id = tz_id };
0329 
0330     return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DISABLE, &p);
0331 }
0332 
0333 int thermal_notify_tz_trip_down(int tz_id, int trip_id, int temp)
0334 {
0335     struct param p = { .tz_id = tz_id, .trip_id = trip_id, .temp = temp };
0336 
0337     return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DOWN, &p);
0338 }
0339 
0340 int thermal_notify_tz_trip_up(int tz_id, int trip_id, int temp)
0341 {
0342     struct param p = { .tz_id = tz_id, .trip_id = trip_id, .temp = temp };
0343 
0344     return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_UP, &p);
0345 }
0346 
0347 int thermal_notify_tz_trip_add(int tz_id, int trip_id, int trip_type,
0348                    int trip_temp, int trip_hyst)
0349 {
0350     struct param p = { .tz_id = tz_id, .trip_id = trip_id,
0351                .trip_type = trip_type, .trip_temp = trip_temp,
0352                .trip_hyst = trip_hyst };
0353 
0354     return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_ADD, &p);
0355 }
0356 
0357 int thermal_notify_tz_trip_delete(int tz_id, int trip_id)
0358 {
0359     struct param p = { .tz_id = tz_id, .trip_id = trip_id };
0360 
0361     return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DELETE, &p);
0362 }
0363 
0364 int thermal_notify_tz_trip_change(int tz_id, int trip_id, int trip_type,
0365                   int trip_temp, int trip_hyst)
0366 {
0367     struct param p = { .tz_id = tz_id, .trip_id = trip_id,
0368                .trip_type = trip_type, .trip_temp = trip_temp,
0369                .trip_hyst = trip_hyst };
0370 
0371     return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_CHANGE, &p);
0372 }
0373 
0374 int thermal_notify_cdev_state_update(int cdev_id, int cdev_state)
0375 {
0376     struct param p = { .cdev_id = cdev_id, .cdev_state = cdev_state };
0377 
0378     return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, &p);
0379 }
0380 
0381 int thermal_notify_cdev_add(int cdev_id, const char *name, int cdev_max_state)
0382 {
0383     struct param p = { .cdev_id = cdev_id, .name = name,
0384                .cdev_max_state = cdev_max_state };
0385 
0386     return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_ADD, &p);
0387 }
0388 
0389 int thermal_notify_cdev_delete(int cdev_id)
0390 {
0391     struct param p = { .cdev_id = cdev_id };
0392 
0393     return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_DELETE, &p);
0394 }
0395 
0396 int thermal_notify_tz_gov_change(int tz_id, const char *name)
0397 {
0398     struct param p = { .tz_id = tz_id, .name = name };
0399 
0400     return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_GOV_CHANGE, &p);
0401 }
0402 
0403 int thermal_genl_cpu_capability_event(int count,
0404                       struct thermal_genl_cpu_caps *caps)
0405 {
0406     struct param p = { .cpu_capabilities_count = count, .cpu_capabilities = caps };
0407 
0408     return thermal_genl_send_event(THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE, &p);
0409 }
0410 EXPORT_SYMBOL_GPL(thermal_genl_cpu_capability_event);
0411 
0412 /*************************** Command encoding ********************************/
0413 
0414 static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz,
0415                     void *data)
0416 {
0417     struct sk_buff *msg = data;
0418 
0419     if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, tz->id) ||
0420         nla_put_string(msg, THERMAL_GENL_ATTR_TZ_NAME, tz->type))
0421         return -EMSGSIZE;
0422 
0423     return 0;
0424 }
0425 
0426 static int thermal_genl_cmd_tz_get_id(struct param *p)
0427 {
0428     struct sk_buff *msg = p->msg;
0429     struct nlattr *start_tz;
0430     int ret;
0431 
0432     start_tz = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ);
0433     if (!start_tz)
0434         return -EMSGSIZE;
0435 
0436     ret = for_each_thermal_zone(__thermal_genl_cmd_tz_get_id, msg);
0437     if (ret)
0438         goto out_cancel_nest;
0439 
0440     nla_nest_end(msg, start_tz);
0441 
0442     return 0;
0443 
0444 out_cancel_nest:
0445     nla_nest_cancel(msg, start_tz);
0446 
0447     return ret;
0448 }
0449 
0450 static int thermal_genl_cmd_tz_get_trip(struct param *p)
0451 {
0452     struct sk_buff *msg = p->msg;
0453     struct thermal_zone_device *tz;
0454     struct nlattr *start_trip;
0455     int i, id;
0456 
0457     if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
0458         return -EINVAL;
0459 
0460     id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
0461 
0462     tz = thermal_zone_get_by_id(id);
0463     if (!tz)
0464         return -EINVAL;
0465 
0466     start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ_TRIP);
0467     if (!start_trip)
0468         return -EMSGSIZE;
0469 
0470     mutex_lock(&tz->lock);
0471 
0472     for (i = 0; i < tz->num_trips; i++) {
0473 
0474         enum thermal_trip_type type;
0475         int temp, hyst = 0;
0476 
0477         tz->ops->get_trip_type(tz, i, &type);
0478         tz->ops->get_trip_temp(tz, i, &temp);
0479         if (tz->ops->get_trip_hyst)
0480             tz->ops->get_trip_hyst(tz, i, &hyst);
0481 
0482         if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, i) ||
0483             nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, type) ||
0484             nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, temp) ||
0485             nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, hyst))
0486             goto out_cancel_nest;
0487     }
0488 
0489     mutex_unlock(&tz->lock);
0490 
0491     nla_nest_end(msg, start_trip);
0492 
0493     return 0;
0494 
0495 out_cancel_nest:
0496     mutex_unlock(&tz->lock);
0497 
0498     return -EMSGSIZE;
0499 }
0500 
0501 static int thermal_genl_cmd_tz_get_temp(struct param *p)
0502 {
0503     struct sk_buff *msg = p->msg;
0504     struct thermal_zone_device *tz;
0505     int temp, ret, id;
0506 
0507     if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
0508         return -EINVAL;
0509 
0510     id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
0511 
0512     tz = thermal_zone_get_by_id(id);
0513     if (!tz)
0514         return -EINVAL;
0515 
0516     ret = thermal_zone_get_temp(tz, &temp);
0517     if (ret)
0518         return ret;
0519 
0520     if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
0521         nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TEMP, temp))
0522         return -EMSGSIZE;
0523 
0524     return 0;
0525 }
0526 
0527 static int thermal_genl_cmd_tz_get_gov(struct param *p)
0528 {
0529     struct sk_buff *msg = p->msg;
0530     struct thermal_zone_device *tz;
0531     int id, ret = 0;
0532 
0533     if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
0534         return -EINVAL;
0535 
0536     id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
0537 
0538     tz = thermal_zone_get_by_id(id);
0539     if (!tz)
0540         return -EINVAL;
0541 
0542     mutex_lock(&tz->lock);
0543 
0544     if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
0545         nla_put_string(msg, THERMAL_GENL_ATTR_TZ_GOV_NAME,
0546                tz->governor->name))
0547         ret = -EMSGSIZE;
0548 
0549     mutex_unlock(&tz->lock);
0550 
0551     return ret;
0552 }
0553 
0554 static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device *cdev,
0555                        void *data)
0556 {
0557     struct sk_buff *msg = data;
0558 
0559     if (nla_put_u32(msg, THERMAL_GENL_ATTR_CDEV_ID, cdev->id))
0560         return -EMSGSIZE;
0561 
0562     if (nla_put_string(msg, THERMAL_GENL_ATTR_CDEV_NAME, cdev->type))
0563         return -EMSGSIZE;
0564 
0565     return 0;
0566 }
0567 
0568 static int thermal_genl_cmd_cdev_get(struct param *p)
0569 {
0570     struct sk_buff *msg = p->msg;
0571     struct nlattr *start_cdev;
0572     int ret;
0573 
0574     start_cdev = nla_nest_start(msg, THERMAL_GENL_ATTR_CDEV);
0575     if (!start_cdev)
0576         return -EMSGSIZE;
0577 
0578     ret = for_each_thermal_cooling_device(__thermal_genl_cmd_cdev_get, msg);
0579     if (ret)
0580         goto out_cancel_nest;
0581 
0582     nla_nest_end(msg, start_cdev);
0583 
0584     return 0;
0585 out_cancel_nest:
0586     nla_nest_cancel(msg, start_cdev);
0587 
0588     return ret;
0589 }
0590 
0591 static cb_t cmd_cb[] = {
0592     [THERMAL_GENL_CMD_TZ_GET_ID]    = thermal_genl_cmd_tz_get_id,
0593     [THERMAL_GENL_CMD_TZ_GET_TRIP]  = thermal_genl_cmd_tz_get_trip,
0594     [THERMAL_GENL_CMD_TZ_GET_TEMP]  = thermal_genl_cmd_tz_get_temp,
0595     [THERMAL_GENL_CMD_TZ_GET_GOV]   = thermal_genl_cmd_tz_get_gov,
0596     [THERMAL_GENL_CMD_CDEV_GET] = thermal_genl_cmd_cdev_get,
0597 };
0598 
0599 static int thermal_genl_cmd_dumpit(struct sk_buff *skb,
0600                    struct netlink_callback *cb)
0601 {
0602     struct param p = { .msg = skb };
0603     const struct genl_dumpit_info *info = genl_dumpit_info(cb);
0604     int cmd = info->op.cmd;
0605     int ret;
0606     void *hdr;
0607 
0608     hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0, cmd);
0609     if (!hdr)
0610         return -EMSGSIZE;
0611 
0612     ret = cmd_cb[cmd](&p);
0613     if (ret)
0614         goto out_cancel_msg;
0615 
0616     genlmsg_end(skb, hdr);
0617 
0618     return 0;
0619 
0620 out_cancel_msg:
0621     genlmsg_cancel(skb, hdr);
0622 
0623     return ret;
0624 }
0625 
0626 static int thermal_genl_cmd_doit(struct sk_buff *skb,
0627                  struct genl_info *info)
0628 {
0629     struct param p = { .attrs = info->attrs };
0630     struct sk_buff *msg;
0631     void *hdr;
0632     int cmd = info->genlhdr->cmd;
0633     int ret = -EMSGSIZE;
0634 
0635     msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
0636     if (!msg)
0637         return -ENOMEM;
0638     p.msg = msg;
0639 
0640     hdr = genlmsg_put_reply(msg, info, &thermal_gnl_family, 0, cmd);
0641     if (!hdr)
0642         goto out_free_msg;
0643 
0644     ret = cmd_cb[cmd](&p);
0645     if (ret)
0646         goto out_cancel_msg;
0647 
0648     genlmsg_end(msg, hdr);
0649 
0650     return genlmsg_reply(msg, info);
0651 
0652 out_cancel_msg:
0653     genlmsg_cancel(msg, hdr);
0654 out_free_msg:
0655     nlmsg_free(msg);
0656 
0657     return ret;
0658 }
0659 
0660 static const struct genl_small_ops thermal_genl_ops[] = {
0661     {
0662         .cmd = THERMAL_GENL_CMD_TZ_GET_ID,
0663         .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0664         .dumpit = thermal_genl_cmd_dumpit,
0665     },
0666     {
0667         .cmd = THERMAL_GENL_CMD_TZ_GET_TRIP,
0668         .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0669         .doit = thermal_genl_cmd_doit,
0670     },
0671     {
0672         .cmd = THERMAL_GENL_CMD_TZ_GET_TEMP,
0673         .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0674         .doit = thermal_genl_cmd_doit,
0675     },
0676     {
0677         .cmd = THERMAL_GENL_CMD_TZ_GET_GOV,
0678         .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0679         .doit = thermal_genl_cmd_doit,
0680     },
0681     {
0682         .cmd = THERMAL_GENL_CMD_CDEV_GET,
0683         .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0684         .dumpit = thermal_genl_cmd_dumpit,
0685     },
0686 };
0687 
0688 static struct genl_family thermal_gnl_family __ro_after_init = {
0689     .hdrsize    = 0,
0690     .name       = THERMAL_GENL_FAMILY_NAME,
0691     .version    = THERMAL_GENL_VERSION,
0692     .maxattr    = THERMAL_GENL_ATTR_MAX,
0693     .policy     = thermal_genl_policy,
0694     .small_ops  = thermal_genl_ops,
0695     .n_small_ops    = ARRAY_SIZE(thermal_genl_ops),
0696     .mcgrps     = thermal_genl_mcgrps,
0697     .n_mcgrps   = ARRAY_SIZE(thermal_genl_mcgrps),
0698 };
0699 
0700 int __init thermal_netlink_init(void)
0701 {
0702     return genl_register_family(&thermal_gnl_family);
0703 }