thermal_netlink.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright 2020 Linaro Limited
  4. *
  5. * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
  6. *
  7. * Generic netlink for thermal management framework
  8. */
  9. #include <linux/module.h>
  10. #include <linux/kernel.h>
  11. #include <net/genetlink.h>
  12. #include <trace/hooks/thermal.h>
  13. #include <uapi/linux/thermal.h>
  14. #include "thermal_core.h"
  15. static const struct genl_multicast_group thermal_genl_mcgrps[] = {
  16. { .name = THERMAL_GENL_SAMPLING_GROUP_NAME, },
  17. { .name = THERMAL_GENL_EVENT_GROUP_NAME, },
  18. };
  19. static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = {
  20. /* Thermal zone */
  21. [THERMAL_GENL_ATTR_TZ] = { .type = NLA_NESTED },
  22. [THERMAL_GENL_ATTR_TZ_ID] = { .type = NLA_U32 },
  23. [THERMAL_GENL_ATTR_TZ_TEMP] = { .type = NLA_U32 },
  24. [THERMAL_GENL_ATTR_TZ_TRIP] = { .type = NLA_NESTED },
  25. [THERMAL_GENL_ATTR_TZ_TRIP_ID] = { .type = NLA_U32 },
  26. [THERMAL_GENL_ATTR_TZ_TRIP_TEMP] = { .type = NLA_U32 },
  27. [THERMAL_GENL_ATTR_TZ_TRIP_TYPE] = { .type = NLA_U32 },
  28. [THERMAL_GENL_ATTR_TZ_TRIP_HYST] = { .type = NLA_U32 },
  29. [THERMAL_GENL_ATTR_TZ_MODE] = { .type = NLA_U32 },
  30. [THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT] = { .type = NLA_U32 },
  31. [THERMAL_GENL_ATTR_TZ_NAME] = { .type = NLA_STRING,
  32. .len = THERMAL_NAME_LENGTH },
  33. /* Governor(s) */
  34. [THERMAL_GENL_ATTR_TZ_GOV] = { .type = NLA_NESTED },
  35. [THERMAL_GENL_ATTR_TZ_GOV_NAME] = { .type = NLA_STRING,
  36. .len = THERMAL_NAME_LENGTH },
  37. /* Cooling devices */
  38. [THERMAL_GENL_ATTR_CDEV] = { .type = NLA_NESTED },
  39. [THERMAL_GENL_ATTR_CDEV_ID] = { .type = NLA_U32 },
  40. [THERMAL_GENL_ATTR_CDEV_CUR_STATE] = { .type = NLA_U32 },
  41. [THERMAL_GENL_ATTR_CDEV_MAX_STATE] = { .type = NLA_U32 },
  42. [THERMAL_GENL_ATTR_CDEV_NAME] = { .type = NLA_STRING,
  43. .len = THERMAL_NAME_LENGTH },
  44. };
  45. struct param {
  46. struct nlattr **attrs;
  47. struct sk_buff *msg;
  48. const char *name;
  49. int tz_id;
  50. int cdev_id;
  51. int trip_id;
  52. int trip_temp;
  53. int trip_type;
  54. int trip_hyst;
  55. int temp;
  56. int cdev_state;
  57. int cdev_max_state;
  58. };
  59. typedef int (*cb_t)(struct param *);
  60. static struct genl_family thermal_gnl_family;
  61. /************************** Sampling encoding *******************************/
  62. int thermal_genl_sampling_temp(int id, int temp)
  63. {
  64. struct sk_buff *skb;
  65. void *hdr;
  66. skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
  67. if (!skb)
  68. return -ENOMEM;
  69. hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0,
  70. THERMAL_GENL_SAMPLING_TEMP);
  71. if (!hdr)
  72. goto out_free;
  73. if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_ID, id))
  74. goto out_cancel;
  75. if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_TEMP, temp))
  76. goto out_cancel;
  77. genlmsg_end(skb, hdr);
  78. genlmsg_multicast(&thermal_gnl_family, skb, 0, 0, GFP_KERNEL);
  79. return 0;
  80. out_cancel:
  81. genlmsg_cancel(skb, hdr);
  82. out_free:
  83. nlmsg_free(skb);
  84. return -EMSGSIZE;
  85. }
  86. /**************************** Event encoding *********************************/
  87. static int thermal_genl_event_tz_create(struct param *p)
  88. {
  89. if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
  90. nla_put_string(p->msg, THERMAL_GENL_ATTR_TZ_NAME, p->name))
  91. return -EMSGSIZE;
  92. return 0;
  93. }
  94. static int thermal_genl_event_tz(struct param *p)
  95. {
  96. if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id))
  97. return -EMSGSIZE;
  98. return 0;
  99. }
  100. static int thermal_genl_event_tz_trip_up(struct param *p)
  101. {
  102. if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
  103. nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id))
  104. return -EMSGSIZE;
  105. return 0;
  106. }
  107. static int thermal_genl_event_tz_trip_add(struct param *p)
  108. {
  109. if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
  110. nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) ||
  111. nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, p->trip_type) ||
  112. nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, p->trip_temp) ||
  113. nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, p->trip_hyst))
  114. return -EMSGSIZE;
  115. return 0;
  116. }
  117. static int thermal_genl_event_tz_trip_delete(struct param *p)
  118. {
  119. if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
  120. nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id))
  121. return -EMSGSIZE;
  122. return 0;
  123. }
  124. static int thermal_genl_event_cdev_add(struct param *p)
  125. {
  126. if (nla_put_string(p->msg, THERMAL_GENL_ATTR_CDEV_NAME,
  127. p->name) ||
  128. nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
  129. p->cdev_id) ||
  130. nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_MAX_STATE,
  131. p->cdev_max_state))
  132. return -EMSGSIZE;
  133. return 0;
  134. }
  135. static int thermal_genl_event_cdev_delete(struct param *p)
  136. {
  137. if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID, p->cdev_id))
  138. return -EMSGSIZE;
  139. return 0;
  140. }
  141. static int thermal_genl_event_cdev_state_update(struct param *p)
  142. {
  143. if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
  144. p->cdev_id) ||
  145. nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_CUR_STATE,
  146. p->cdev_state))
  147. return -EMSGSIZE;
  148. return 0;
  149. }
  150. static int thermal_genl_event_gov_change(struct param *p)
  151. {
  152. if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
  153. nla_put_string(p->msg, THERMAL_GENL_ATTR_GOV_NAME, p->name))
  154. return -EMSGSIZE;
  155. return 0;
  156. }
  157. int thermal_genl_event_tz_delete(struct param *p)
  158. __attribute__((alias("thermal_genl_event_tz")));
  159. int thermal_genl_event_tz_enable(struct param *p)
  160. __attribute__((alias("thermal_genl_event_tz")));
  161. int thermal_genl_event_tz_disable(struct param *p)
  162. __attribute__((alias("thermal_genl_event_tz")));
  163. int thermal_genl_event_tz_trip_down(struct param *p)
  164. __attribute__((alias("thermal_genl_event_tz_trip_up")));
  165. int thermal_genl_event_tz_trip_change(struct param *p)
  166. __attribute__((alias("thermal_genl_event_tz_trip_add")));
  167. static cb_t event_cb[] = {
  168. [THERMAL_GENL_EVENT_TZ_CREATE] = thermal_genl_event_tz_create,
  169. [THERMAL_GENL_EVENT_TZ_DELETE] = thermal_genl_event_tz_delete,
  170. [THERMAL_GENL_EVENT_TZ_ENABLE] = thermal_genl_event_tz_enable,
  171. [THERMAL_GENL_EVENT_TZ_DISABLE] = thermal_genl_event_tz_disable,
  172. [THERMAL_GENL_EVENT_TZ_TRIP_UP] = thermal_genl_event_tz_trip_up,
  173. [THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = thermal_genl_event_tz_trip_down,
  174. [THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = thermal_genl_event_tz_trip_change,
  175. [THERMAL_GENL_EVENT_TZ_TRIP_ADD] = thermal_genl_event_tz_trip_add,
  176. [THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = thermal_genl_event_tz_trip_delete,
  177. [THERMAL_GENL_EVENT_CDEV_ADD] = thermal_genl_event_cdev_add,
  178. [THERMAL_GENL_EVENT_CDEV_DELETE] = thermal_genl_event_cdev_delete,
  179. [THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = thermal_genl_event_cdev_state_update,
  180. [THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = thermal_genl_event_gov_change,
  181. };
  182. /*
  183. * Generic netlink event encoding
  184. */
  185. static int thermal_genl_send_event(enum thermal_genl_event event,
  186. struct param *p)
  187. {
  188. struct sk_buff *msg;
  189. int ret = -EMSGSIZE;
  190. void *hdr;
  191. int enable_thermal_genl = 1;
  192. trace_android_vh_enable_thermal_genl_check(event, p->tz_id, &enable_thermal_genl);
  193. if (!enable_thermal_genl)
  194. return 0;
  195. msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
  196. if (!msg)
  197. return -ENOMEM;
  198. p->msg = msg;
  199. hdr = genlmsg_put(msg, 0, 0, &thermal_gnl_family, 0, event);
  200. if (!hdr)
  201. goto out_free_msg;
  202. ret = event_cb[event](p);
  203. if (ret)
  204. goto out_cancel_msg;
  205. genlmsg_end(msg, hdr);
  206. genlmsg_multicast(&thermal_gnl_family, msg, 0, 1, GFP_KERNEL);
  207. return 0;
  208. out_cancel_msg:
  209. genlmsg_cancel(msg, hdr);
  210. out_free_msg:
  211. nlmsg_free(msg);
  212. return ret;
  213. }
  214. int thermal_notify_tz_create(int tz_id, const char *name)
  215. {
  216. struct param p = { .tz_id = tz_id, .name = name };
  217. return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_CREATE, &p);
  218. }
  219. int thermal_notify_tz_delete(int tz_id)
  220. {
  221. struct param p = { .tz_id = tz_id };
  222. return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DELETE, &p);
  223. }
  224. int thermal_notify_tz_enable(int tz_id)
  225. {
  226. struct param p = { .tz_id = tz_id };
  227. return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_ENABLE, &p);
  228. }
  229. int thermal_notify_tz_disable(int tz_id)
  230. {
  231. struct param p = { .tz_id = tz_id };
  232. return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DISABLE, &p);
  233. }
  234. int thermal_notify_tz_trip_down(int tz_id, int trip_id)
  235. {
  236. struct param p = { .tz_id = tz_id, .trip_id = trip_id };
  237. return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DOWN, &p);
  238. }
  239. int thermal_notify_tz_trip_up(int tz_id, int trip_id)
  240. {
  241. struct param p = { .tz_id = tz_id, .trip_id = trip_id };
  242. return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_UP, &p);
  243. }
  244. int thermal_notify_tz_trip_add(int tz_id, int trip_id, int trip_type,
  245. int trip_temp, int trip_hyst)
  246. {
  247. struct param p = { .tz_id = tz_id, .trip_id = trip_id,
  248. .trip_type = trip_type, .trip_temp = trip_temp,
  249. .trip_hyst = trip_hyst };
  250. return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_ADD, &p);
  251. }
  252. int thermal_notify_tz_trip_delete(int tz_id, int trip_id)
  253. {
  254. struct param p = { .tz_id = tz_id, .trip_id = trip_id };
  255. return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DELETE, &p);
  256. }
  257. int thermal_notify_tz_trip_change(int tz_id, int trip_id, int trip_type,
  258. int trip_temp, int trip_hyst)
  259. {
  260. struct param p = { .tz_id = tz_id, .trip_id = trip_id,
  261. .trip_type = trip_type, .trip_temp = trip_temp,
  262. .trip_hyst = trip_hyst };
  263. return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_CHANGE, &p);
  264. }
  265. int thermal_notify_cdev_state_update(int cdev_id, int cdev_state)
  266. {
  267. struct param p = { .cdev_id = cdev_id, .cdev_state = cdev_state };
  268. return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, &p);
  269. }
  270. int thermal_notify_cdev_add(int cdev_id, const char *name, int cdev_max_state)
  271. {
  272. struct param p = { .cdev_id = cdev_id, .name = name,
  273. .cdev_max_state = cdev_max_state };
  274. return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_ADD, &p);
  275. }
  276. int thermal_notify_cdev_delete(int cdev_id)
  277. {
  278. struct param p = { .cdev_id = cdev_id };
  279. return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_DELETE, &p);
  280. }
  281. int thermal_notify_tz_gov_change(int tz_id, const char *name)
  282. {
  283. struct param p = { .tz_id = tz_id, .name = name };
  284. return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_GOV_CHANGE, &p);
  285. }
  286. /*************************** Command encoding ********************************/
  287. static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz,
  288. void *data)
  289. {
  290. struct sk_buff *msg = data;
  291. if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, tz->id) ||
  292. nla_put_string(msg, THERMAL_GENL_ATTR_TZ_NAME, tz->type))
  293. return -EMSGSIZE;
  294. return 0;
  295. }
  296. static int thermal_genl_cmd_tz_get_id(struct param *p)
  297. {
  298. struct sk_buff *msg = p->msg;
  299. struct nlattr *start_tz;
  300. int ret;
  301. start_tz = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ);
  302. if (!start_tz)
  303. return -EMSGSIZE;
  304. ret = for_each_thermal_zone(__thermal_genl_cmd_tz_get_id, msg);
  305. if (ret)
  306. goto out_cancel_nest;
  307. nla_nest_end(msg, start_tz);
  308. return 0;
  309. out_cancel_nest:
  310. nla_nest_cancel(msg, start_tz);
  311. return ret;
  312. }
  313. static int thermal_genl_cmd_tz_get_trip(struct param *p)
  314. {
  315. struct sk_buff *msg = p->msg;
  316. struct thermal_zone_device *tz;
  317. struct nlattr *start_trip;
  318. int i, id;
  319. if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
  320. return -EINVAL;
  321. id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
  322. tz = thermal_zone_get_by_id(id);
  323. if (!tz)
  324. return -EINVAL;
  325. start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ_TRIP);
  326. if (!start_trip)
  327. return -EMSGSIZE;
  328. mutex_lock(&tz->lock);
  329. for (i = 0; i < tz->trips; i++) {
  330. enum thermal_trip_type type;
  331. int temp, hyst = 0;
  332. tz->ops->get_trip_type(tz, i, &type);
  333. tz->ops->get_trip_temp(tz, i, &temp);
  334. if (tz->ops->get_trip_hyst)
  335. tz->ops->get_trip_hyst(tz, i, &hyst);
  336. if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, i) ||
  337. nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, type) ||
  338. nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, temp) ||
  339. nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, hyst))
  340. goto out_cancel_nest;
  341. }
  342. mutex_unlock(&tz->lock);
  343. nla_nest_end(msg, start_trip);
  344. return 0;
  345. out_cancel_nest:
  346. mutex_unlock(&tz->lock);
  347. return -EMSGSIZE;
  348. }
  349. static int thermal_genl_cmd_tz_get_temp(struct param *p)
  350. {
  351. struct sk_buff *msg = p->msg;
  352. struct thermal_zone_device *tz;
  353. int temp, ret, id;
  354. if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
  355. return -EINVAL;
  356. id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
  357. tz = thermal_zone_get_by_id(id);
  358. if (!tz)
  359. return -EINVAL;
  360. ret = thermal_zone_get_temp(tz, &temp);
  361. if (ret)
  362. return ret;
  363. if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
  364. nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TEMP, temp))
  365. return -EMSGSIZE;
  366. return 0;
  367. }
  368. static int thermal_genl_cmd_tz_get_gov(struct param *p)
  369. {
  370. struct sk_buff *msg = p->msg;
  371. struct thermal_zone_device *tz;
  372. int id, ret = 0;
  373. if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
  374. return -EINVAL;
  375. id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
  376. tz = thermal_zone_get_by_id(id);
  377. if (!tz)
  378. return -EINVAL;
  379. mutex_lock(&tz->lock);
  380. if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
  381. nla_put_string(msg, THERMAL_GENL_ATTR_TZ_GOV_NAME,
  382. tz->governor->name))
  383. ret = -EMSGSIZE;
  384. mutex_unlock(&tz->lock);
  385. return ret;
  386. }
  387. static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device *cdev,
  388. void *data)
  389. {
  390. struct sk_buff *msg = data;
  391. if (nla_put_u32(msg, THERMAL_GENL_ATTR_CDEV_ID, cdev->id))
  392. return -EMSGSIZE;
  393. if (nla_put_string(msg, THERMAL_GENL_ATTR_CDEV_NAME, cdev->type))
  394. return -EMSGSIZE;
  395. return 0;
  396. }
  397. static int thermal_genl_cmd_cdev_get(struct param *p)
  398. {
  399. struct sk_buff *msg = p->msg;
  400. struct nlattr *start_cdev;
  401. int ret;
  402. start_cdev = nla_nest_start(msg, THERMAL_GENL_ATTR_CDEV);
  403. if (!start_cdev)
  404. return -EMSGSIZE;
  405. ret = for_each_thermal_cooling_device(__thermal_genl_cmd_cdev_get, msg);
  406. if (ret)
  407. goto out_cancel_nest;
  408. nla_nest_end(msg, start_cdev);
  409. return 0;
  410. out_cancel_nest:
  411. nla_nest_cancel(msg, start_cdev);
  412. return ret;
  413. }
  414. static cb_t cmd_cb[] = {
  415. [THERMAL_GENL_CMD_TZ_GET_ID] = thermal_genl_cmd_tz_get_id,
  416. [THERMAL_GENL_CMD_TZ_GET_TRIP] = thermal_genl_cmd_tz_get_trip,
  417. [THERMAL_GENL_CMD_TZ_GET_TEMP] = thermal_genl_cmd_tz_get_temp,
  418. [THERMAL_GENL_CMD_TZ_GET_GOV] = thermal_genl_cmd_tz_get_gov,
  419. [THERMAL_GENL_CMD_CDEV_GET] = thermal_genl_cmd_cdev_get,
  420. };
  421. static int thermal_genl_cmd_dumpit(struct sk_buff *skb,
  422. struct netlink_callback *cb)
  423. {
  424. struct param p = { .msg = skb };
  425. const struct genl_dumpit_info *info = genl_dumpit_info(cb);
  426. int cmd = info->op.cmd;
  427. int ret;
  428. void *hdr;
  429. hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0, cmd);
  430. if (!hdr)
  431. return -EMSGSIZE;
  432. ret = cmd_cb[cmd](&p);
  433. if (ret)
  434. goto out_cancel_msg;
  435. genlmsg_end(skb, hdr);
  436. return 0;
  437. out_cancel_msg:
  438. genlmsg_cancel(skb, hdr);
  439. return ret;
  440. }
  441. static int thermal_genl_cmd_doit(struct sk_buff *skb,
  442. struct genl_info *info)
  443. {
  444. struct param p = { .attrs = info->attrs };
  445. struct sk_buff *msg;
  446. void *hdr;
  447. int cmd = info->genlhdr->cmd;
  448. int ret = -EMSGSIZE;
  449. msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
  450. if (!msg)
  451. return -ENOMEM;
  452. p.msg = msg;
  453. hdr = genlmsg_put_reply(msg, info, &thermal_gnl_family, 0, cmd);
  454. if (!hdr)
  455. goto out_free_msg;
  456. ret = cmd_cb[cmd](&p);
  457. if (ret)
  458. goto out_cancel_msg;
  459. genlmsg_end(msg, hdr);
  460. return genlmsg_reply(msg, info);
  461. out_cancel_msg:
  462. genlmsg_cancel(msg, hdr);
  463. out_free_msg:
  464. nlmsg_free(msg);
  465. return ret;
  466. }
  467. static const struct genl_small_ops thermal_genl_ops[] = {
  468. {
  469. .cmd = THERMAL_GENL_CMD_TZ_GET_ID,
  470. .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
  471. .dumpit = thermal_genl_cmd_dumpit,
  472. },
  473. {
  474. .cmd = THERMAL_GENL_CMD_TZ_GET_TRIP,
  475. .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
  476. .doit = thermal_genl_cmd_doit,
  477. },
  478. {
  479. .cmd = THERMAL_GENL_CMD_TZ_GET_TEMP,
  480. .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
  481. .doit = thermal_genl_cmd_doit,
  482. },
  483. {
  484. .cmd = THERMAL_GENL_CMD_TZ_GET_GOV,
  485. .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
  486. .doit = thermal_genl_cmd_doit,
  487. },
  488. {
  489. .cmd = THERMAL_GENL_CMD_CDEV_GET,
  490. .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
  491. .dumpit = thermal_genl_cmd_dumpit,
  492. },
  493. };
  494. static struct genl_family thermal_gnl_family __ro_after_init = {
  495. .hdrsize = 0,
  496. .name = THERMAL_GENL_FAMILY_NAME,
  497. .version = THERMAL_GENL_VERSION,
  498. .maxattr = THERMAL_GENL_ATTR_MAX,
  499. .policy = thermal_genl_policy,
  500. .small_ops = thermal_genl_ops,
  501. .n_small_ops = ARRAY_SIZE(thermal_genl_ops),
  502. .mcgrps = thermal_genl_mcgrps,
  503. .n_mcgrps = ARRAY_SIZE(thermal_genl_mcgrps),
  504. };
  505. int __init thermal_netlink_init(void)
  506. {
  507. return genl_register_family(&thermal_gnl_family);
  508. }