tuxonice_netlink.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. /*
  2. * kernel/power/tuxonice_netlink.c
  3. *
  4. * Copyright (C) 2004-2014 Nigel Cunningham (nigel at tuxonice net)
  5. *
  6. * This file is released under the GPLv2.
  7. *
  8. * Functions for communicating with a userspace helper via netlink.
  9. */
  10. #include <linux/suspend.h>
  11. #include <linux/sched.h>
  12. #include "tuxonice_netlink.h"
  13. #include "tuxonice.h"
  14. #include "tuxonice_modules.h"
  15. #include "tuxonice_alloc.h"
  16. #include "tuxonice_builtin.h"
  17. static struct user_helper_data *uhd_list;
  18. /*
  19. * Refill our pool of SKBs for use in emergencies (eg, when eating memory and
  20. * none can be allocated).
  21. */
  22. static void toi_fill_skb_pool(struct user_helper_data *uhd)
  23. {
  24. while (uhd->pool_level < uhd->pool_limit) {
  25. struct sk_buff *new_skb = alloc_skb(NLMSG_SPACE(uhd->skb_size), TOI_ATOMIC_GFP);
  26. if (!new_skb)
  27. break;
  28. new_skb->next = uhd->emerg_skbs;
  29. uhd->emerg_skbs = new_skb;
  30. uhd->pool_level++;
  31. }
  32. }
  33. /*
  34. * Try to allocate a single skb. If we can't get one, try to use one from
  35. * our pool.
  36. */
  37. static struct sk_buff *toi_get_skb(struct user_helper_data *uhd)
  38. {
  39. struct sk_buff *skb = alloc_skb(NLMSG_SPACE(uhd->skb_size), TOI_ATOMIC_GFP);
  40. if (skb)
  41. return skb;
  42. skb = uhd->emerg_skbs;
  43. if (skb) {
  44. uhd->pool_level--;
  45. uhd->emerg_skbs = skb->next;
  46. skb->next = NULL;
  47. }
  48. return skb;
  49. }
  50. void toi_send_netlink_message(struct user_helper_data *uhd, int type, void *params, size_t len)
  51. {
  52. struct sk_buff *skb;
  53. struct nlmsghdr *nlh;
  54. void *dest;
  55. struct task_struct *t;
  56. if (uhd->pid == -1)
  57. return;
  58. if (uhd->debug)
  59. pr_err("toi_send_netlink_message: Send " "message type %d.\n", type);
  60. skb = toi_get_skb(uhd);
  61. if (!skb) {
  62. pr_warn("toi_netlink: Can't allocate skb!\n");
  63. return;
  64. }
  65. nlh = nlmsg_put(skb, 0, uhd->sock_seq, type, len, 0);
  66. uhd->sock_seq++;
  67. dest = NLMSG_DATA(nlh);
  68. if (params && len > 0)
  69. memcpy(dest, params, len);
  70. netlink_unicast(uhd->nl, skb, uhd->pid, 0);
  71. toi_read_lock_tasklist();
  72. t = find_task_by_pid_ns(uhd->pid, &init_pid_ns);
  73. if (!t) {
  74. toi_read_unlock_tasklist();
  75. if (uhd->pid > -1)
  76. pr_warn("Hmm. Can't find the userspace task" " %d.\n", uhd->pid);
  77. return;
  78. }
  79. wake_up_process(t);
  80. toi_read_unlock_tasklist();
  81. cond_resched();
  82. }
  83. EXPORT_SYMBOL_GPL(toi_send_netlink_message);
  84. static void send_whether_debugging(struct user_helper_data *uhd)
  85. {
  86. static u8 is_debugging = 1;
  87. toi_send_netlink_message(uhd, NETLINK_MSG_IS_DEBUGGING, &is_debugging, sizeof(u8));
  88. }
  89. /*
  90. * Set the PF_NOFREEZE flag on the given process to ensure it can run whilst we
  91. * are hibernating.
  92. */
  93. static int nl_set_nofreeze(struct user_helper_data *uhd, __u32 pid)
  94. {
  95. struct task_struct *t;
  96. if (uhd->debug)
  97. pr_err("nl_set_nofreeze for pid %d.\n", pid);
  98. toi_read_lock_tasklist();
  99. t = find_task_by_pid_ns(pid, &init_pid_ns);
  100. if (!t) {
  101. toi_read_unlock_tasklist();
  102. pr_warn("Strange. Can't find the userspace task %d.\n", pid);
  103. return -EINVAL;
  104. }
  105. t->flags |= PF_NOFREEZE;
  106. toi_read_unlock_tasklist();
  107. uhd->pid = pid;
  108. toi_send_netlink_message(uhd, NETLINK_MSG_NOFREEZE_ACK, NULL, 0);
  109. return 0;
  110. }
  111. /*
  112. * Called when the userspace process has informed us that it's ready to roll.
  113. */
  114. static int nl_ready(struct user_helper_data *uhd, u32 version)
  115. {
  116. if (version != uhd->interface_version) {
  117. pr_warn("%s using interface X:v.%d (O:v.%d). Trying to continue without it.",
  118. uhd->name, version, uhd->interface_version);
  119. if (uhd->not_ready)
  120. uhd->not_ready();
  121. return -EINVAL;
  122. }
  123. complete(&uhd->wait_for_process);
  124. return 0;
  125. }
  126. void toi_netlink_close_complete(struct user_helper_data *uhd)
  127. {
  128. if (uhd->nl) {
  129. netlink_kernel_release(uhd->nl);
  130. uhd->nl = NULL;
  131. }
  132. while (uhd->emerg_skbs) {
  133. struct sk_buff *next = uhd->emerg_skbs->next;
  134. kfree_skb(uhd->emerg_skbs);
  135. uhd->emerg_skbs = next;
  136. }
  137. uhd->pid = -1;
  138. }
  139. EXPORT_SYMBOL_GPL(toi_netlink_close_complete);
  140. static int toi_nl_gen_rcv_msg(struct user_helper_data *uhd,
  141. struct sk_buff *skb, struct nlmsghdr *nlh)
  142. {
  143. int type = nlh->nlmsg_type;
  144. int *data;
  145. int err;
  146. if (uhd->debug)
  147. pr_err("toi_user_rcv_skb: Received message %d.\n", type);
  148. /* Let the more specific handler go first. It returns
  149. * 1 for valid messages that it doesn't know. */
  150. err = uhd->rcv_msg(skb, nlh);
  151. if (err != 1)
  152. return err;
  153. /* Only allow one task to receive NOFREEZE privileges */
  154. if (type == NETLINK_MSG_NOFREEZE_ME && uhd->pid != -1) {
  155. pr_warn("Received extra nofreeze me requests.\n");
  156. return -EBUSY;
  157. }
  158. data = NLMSG_DATA(nlh);
  159. switch (type) {
  160. case NETLINK_MSG_NOFREEZE_ME:
  161. return nl_set_nofreeze(uhd, nlh->nlmsg_pid);
  162. case NETLINK_MSG_GET_DEBUGGING:
  163. send_whether_debugging(uhd);
  164. return 0;
  165. case NETLINK_MSG_READY:
  166. if (nlh->nlmsg_len != NLMSG_LENGTH(sizeof(u32))) {
  167. pr_warn("Invalid ready message.\n");
  168. if (uhd->not_ready)
  169. uhd->not_ready();
  170. return -EINVAL;
  171. }
  172. return nl_ready(uhd, (u32) *data);
  173. case NETLINK_MSG_CLEANUP:
  174. toi_netlink_close_complete(uhd);
  175. return 0;
  176. }
  177. return -EINVAL;
  178. }
  179. static void toi_user_rcv_skb(struct sk_buff *skb)
  180. {
  181. int err;
  182. struct nlmsghdr *nlh;
  183. struct user_helper_data *uhd = uhd_list;
  184. while (uhd && uhd->netlink_id != skb->sk->sk_protocol)
  185. uhd = uhd->next;
  186. if (!uhd)
  187. return;
  188. while (skb->len >= NLMSG_SPACE(0)) {
  189. u32 rlen;
  190. nlh = (struct nlmsghdr *)skb->data;
  191. if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
  192. return;
  193. rlen = NLMSG_ALIGN(nlh->nlmsg_len);
  194. if (rlen > skb->len)
  195. rlen = skb->len;
  196. err = toi_nl_gen_rcv_msg(uhd, skb, nlh);
  197. if (err)
  198. netlink_ack(skb, nlh, err);
  199. else if (nlh->nlmsg_flags & NLM_F_ACK)
  200. netlink_ack(skb, nlh, 0);
  201. skb_pull(skb, rlen);
  202. }
  203. }
  204. static int netlink_prepare(struct user_helper_data *uhd)
  205. {
  206. struct netlink_kernel_cfg cfg = {
  207. .groups = 0,
  208. .input = toi_user_rcv_skb,
  209. };
  210. uhd->next = uhd_list;
  211. uhd_list = uhd;
  212. uhd->sock_seq = 0x42c0ffee;
  213. uhd->nl = netlink_kernel_create(&init_net, uhd->netlink_id, &cfg);
  214. if (!uhd->nl) {
  215. pr_warn("Failed to allocate netlink socket for %s.\n", uhd->name);
  216. return -ENOMEM;
  217. }
  218. toi_fill_skb_pool(uhd);
  219. return 0;
  220. }
  221. void toi_netlink_close(struct user_helper_data *uhd)
  222. {
  223. struct task_struct *t;
  224. toi_read_lock_tasklist();
  225. t = find_task_by_pid_ns(uhd->pid, &init_pid_ns);
  226. if (t)
  227. t->flags &= ~PF_NOFREEZE;
  228. toi_read_unlock_tasklist();
  229. toi_send_netlink_message(uhd, NETLINK_MSG_CLEANUP, NULL, 0);
  230. }
  231. EXPORT_SYMBOL_GPL(toi_netlink_close);
  232. int toi_netlink_setup(struct user_helper_data *uhd)
  233. {
  234. /* In case userui didn't cleanup properly on us */
  235. toi_netlink_close_complete(uhd);
  236. if (netlink_prepare(uhd) < 0) {
  237. pr_warn("Netlink prepare failed.\n");
  238. return 1;
  239. }
  240. if (toi_launch_userspace_program(uhd->program, uhd->netlink_id,
  241. UMH_WAIT_EXEC, uhd->debug) < 0) {
  242. pr_warn("Launch userspace program failed.\n");
  243. toi_netlink_close_complete(uhd);
  244. return 1;
  245. }
  246. /* Wait 2 seconds for the userspace process to make contact */
  247. wait_for_completion_timeout(&uhd->wait_for_process, 2 * HZ);
  248. if (uhd->pid == -1) {
  249. pr_warn("%s: Failed to contact userspace process.\n", uhd->name);
  250. toi_netlink_close_complete(uhd);
  251. return 1;
  252. }
  253. return 0;
  254. }
  255. EXPORT_SYMBOL_GPL(toi_netlink_setup);