2 * kernel/power/tuxonice_netlink.c
4 * Copyright (C) 2004-2010 Nigel Cunningham (nigel at tuxonice net)
6 * This file is released under the GPLv2.
8 * Functions for communicating with a userspace helper via netlink.
12 #include <linux/suspend.h>
13 #include <linux/sched.h>
14 #include "tuxonice_netlink.h"
16 #include "tuxonice_modules.h"
17 #include "tuxonice_alloc.h"
18 #include "tuxonice_builtin.h"
20 static struct user_helper_data
*uhd_list
;
23 * Refill our pool of SKBs for use in emergencies (eg, when eating memory and
24 * none can be allocated).
26 static void toi_fill_skb_pool(struct user_helper_data
*uhd
)
28 while (uhd
->pool_level
< uhd
->pool_limit
) {
29 struct sk_buff
*new_skb
= alloc_skb(NLMSG_SPACE(uhd
->skb_size
), TOI_ATOMIC_GFP
);
34 new_skb
->next
= uhd
->emerg_skbs
;
35 uhd
->emerg_skbs
= new_skb
;
41 * Try to allocate a single skb. If we can't get one, try to use one from
44 static struct sk_buff
*toi_get_skb(struct user_helper_data
*uhd
)
46 struct sk_buff
*skb
= alloc_skb(NLMSG_SPACE(uhd
->skb_size
), TOI_ATOMIC_GFP
);
51 skb
= uhd
->emerg_skbs
;
54 uhd
->emerg_skbs
= skb
->next
;
61 void toi_send_netlink_message(struct user_helper_data
*uhd
, int type
, void *params
, size_t len
)
66 struct task_struct
*t
;
72 printk(KERN_ERR
"toi_send_netlink_message: Send " "message type %d.\n", type
);
74 skb
= toi_get_skb(uhd
);
76 printk(KERN_INFO
"toi_netlink: Can't allocate skb!\n");
80 nlh
= nlmsg_put(skb
, 0, uhd
->sock_seq
, type
, len
, 0);
83 dest
= NLMSG_DATA(nlh
);
84 if (params
&& len
> 0)
85 memcpy(dest
, params
, len
);
87 netlink_unicast(uhd
->nl
, skb
, uhd
->pid
, 0);
89 toi_read_lock_tasklist();
90 t
= find_task_by_pid_ns(uhd
->pid
, &init_pid_ns
);
92 toi_read_unlock_tasklist();
94 printk(KERN_INFO
"Hmm. Can't find the userspace task" " %d.\n", uhd
->pid
);
98 toi_read_unlock_tasklist();
102 EXPORT_SYMBOL_GPL(toi_send_netlink_message
);
104 static void send_whether_debugging(struct user_helper_data
*uhd
)
106 static u8 is_debugging
= 1;
108 toi_send_netlink_message(uhd
, NETLINK_MSG_IS_DEBUGGING
, &is_debugging
, sizeof(u8
));
112 * Set the PF_NOFREEZE flag on the given process to ensure it can run whilst we
115 static int nl_set_nofreeze(struct user_helper_data
*uhd
, __u32 pid
)
117 struct task_struct
*t
;
120 printk(KERN_ERR
"nl_set_nofreeze for pid %d.\n", pid
);
122 toi_read_lock_tasklist();
123 t
= find_task_by_pid_ns(pid
, &init_pid_ns
);
125 toi_read_unlock_tasklist();
126 printk(KERN_INFO
"Strange. Can't find the userspace task %d.\n", pid
);
130 t
->flags
|= PF_NOFREEZE
;
132 toi_read_unlock_tasklist();
135 toi_send_netlink_message(uhd
, NETLINK_MSG_NOFREEZE_ACK
, NULL
, 0);
141 * Called when the userspace process has informed us that it's ready to roll.
143 static int nl_ready(struct user_helper_data
*uhd
, u32 version
)
145 if (version
!= uhd
->interface_version
) {
146 printk(KERN_INFO
"%s userspace process using invalid interface"
147 " version (%d - kernel wants %d). Trying to "
148 "continue without it.\n", uhd
->name
, version
, uhd
->interface_version
);
154 complete(&uhd
->wait_for_process
);
159 void toi_netlink_close_complete(struct user_helper_data
*uhd
)
162 netlink_kernel_release(uhd
->nl
);
166 while (uhd
->emerg_skbs
) {
167 struct sk_buff
*next
= uhd
->emerg_skbs
->next
;
168 kfree_skb(uhd
->emerg_skbs
);
169 uhd
->emerg_skbs
= next
;
174 EXPORT_SYMBOL_GPL(toi_netlink_close_complete
);
176 static int toi_nl_gen_rcv_msg(struct user_helper_data
*uhd
,
177 struct sk_buff
*skb
, struct nlmsghdr
*nlh
)
179 int type
= nlh
->nlmsg_type
;
184 printk(KERN_ERR
"toi_user_rcv_skb: Received message %d.\n", type
);
186 /* Let the more specific handler go first. It returns
187 * 1 for valid messages that it doesn't know. */
188 err
= uhd
->rcv_msg(skb
, nlh
);
192 /* Only allow one task to receive NOFREEZE privileges */
193 if (type
== NETLINK_MSG_NOFREEZE_ME
&& uhd
->pid
!= -1) {
194 printk(KERN_INFO
"Received extra nofreeze me requests.\n");
198 data
= NLMSG_DATA(nlh
);
201 case NETLINK_MSG_NOFREEZE_ME
:
202 return nl_set_nofreeze(uhd
, nlh
->nlmsg_pid
);
203 case NETLINK_MSG_GET_DEBUGGING
:
204 send_whether_debugging(uhd
);
206 case NETLINK_MSG_READY
:
207 if (nlh
->nlmsg_len
!= NLMSG_LENGTH(sizeof(u32
))) {
208 printk(KERN_INFO
"Invalid ready mesage.\n");
213 return nl_ready(uhd
, (u32
) *data
);
214 case NETLINK_MSG_CLEANUP
:
215 toi_netlink_close_complete(uhd
);
222 static void toi_user_rcv_skb(struct sk_buff
*skb
)
225 struct nlmsghdr
*nlh
;
226 struct user_helper_data
*uhd
= uhd_list
;
228 while (uhd
&& uhd
->netlink_id
!= skb
->sk
->sk_protocol
)
234 while (skb
->len
>= NLMSG_SPACE(0)) {
237 nlh
= (struct nlmsghdr
*)skb
->data
;
238 if (nlh
->nlmsg_len
< sizeof(*nlh
) || skb
->len
< nlh
->nlmsg_len
)
241 rlen
= NLMSG_ALIGN(nlh
->nlmsg_len
);
245 err
= toi_nl_gen_rcv_msg(uhd
, skb
, nlh
);
247 netlink_ack(skb
, nlh
, err
);
248 else if (nlh
->nlmsg_flags
& NLM_F_ACK
)
249 netlink_ack(skb
, nlh
, 0);
254 static int netlink_prepare(struct user_helper_data
*uhd
)
256 struct netlink_kernel_cfg cfg
= {
258 .input
= toi_user_rcv_skb
,
261 uhd
->next
= uhd_list
;
264 uhd
->sock_seq
= 0x42c0ffee;
265 uhd
->nl
= netlink_kernel_create(&init_net
, uhd
->netlink_id
, &cfg
);
267 printk(KERN_INFO
"Failed to allocate netlink socket for %s.\n", uhd
->name
);
271 toi_fill_skb_pool(uhd
);
276 void toi_netlink_close(struct user_helper_data
*uhd
)
278 struct task_struct
*t
;
280 toi_read_lock_tasklist();
281 t
= find_task_by_pid_ns(uhd
->pid
, &init_pid_ns
);
283 t
->flags
&= ~PF_NOFREEZE
;
284 toi_read_unlock_tasklist();
286 toi_send_netlink_message(uhd
, NETLINK_MSG_CLEANUP
, NULL
, 0);
288 EXPORT_SYMBOL_GPL(toi_netlink_close
);
290 int toi_netlink_setup(struct user_helper_data
*uhd
)
292 /* In case userui didn't cleanup properly on us */
293 toi_netlink_close_complete(uhd
);
295 if (netlink_prepare(uhd
) < 0) {
296 printk(KERN_INFO
"Netlink prepare failed.\n");
300 if (toi_launch_userspace_program(uhd
->program
, uhd
->netlink_id
,
301 UMH_WAIT_EXEC
, uhd
->debug
) < 0) {
302 printk(KERN_INFO
"Launch userspace program failed.\n");
303 toi_netlink_close_complete(uhd
);
307 /* Wait 2 seconds for the userspace process to make contact */
308 wait_for_completion_timeout(&uhd
->wait_for_process
, 2 * HZ
);
310 if (uhd
->pid
== -1) {
311 printk(KERN_INFO
"%s: Failed to contact userspace process.\n", uhd
->name
);
312 toi_netlink_close_complete(uhd
);
318 EXPORT_SYMBOL_GPL(toi_netlink_setup
);