Commit | Line | Data |
---|---|---|
6fa3eb70 S |
1 | /* |
2 | * kernel/power/tuxonice_storage.c | |
3 | * | |
4 | * Copyright (C) 2005-2010 Nigel Cunningham (nigel at tuxonice net) | |
5 | * | |
6 | * This file is released under the GPLv2. | |
7 | * | |
8 | * Routines for talking to a userspace program that manages storage. | |
9 | * | |
10 | * The kernel side: | |
11 | * - starts the userspace program; | |
12 | * - sends messages telling it when to open and close the connection; | |
13 | * - tells it when to quit; | |
14 | * | |
15 | * The user space side: | |
16 | * - passes messages regarding status; | |
17 | * | |
18 | */ | |
19 | ||
20 | #include <linux/module.h> | |
21 | #include <linux/suspend.h> | |
22 | #include <linux/freezer.h> | |
23 | ||
24 | #include "tuxonice_sysfs.h" | |
25 | #include "tuxonice_modules.h" | |
26 | #include "tuxonice_netlink.h" | |
27 | #include "tuxonice_storage.h" | |
28 | #include "tuxonice_ui.h" | |
29 | ||
30 | static struct user_helper_data usm_helper_data; | |
31 | static struct toi_module_ops usm_ops; | |
32 | static int message_received, usm_prepare_count; | |
33 | static int storage_manager_last_action, storage_manager_action; | |
34 | ||
35 | static int usm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |
36 | { | |
37 | int type; | |
38 | int *data; | |
39 | ||
40 | type = nlh->nlmsg_type; | |
41 | ||
42 | /* A control message: ignore them */ | |
43 | if (type < NETLINK_MSG_BASE) | |
44 | return 0; | |
45 | ||
46 | /* Unknown message: reply with EINVAL */ | |
47 | if (type >= USM_MSG_MAX) | |
48 | return -EINVAL; | |
49 | ||
50 | /* All operations require privileges, even GET */ | |
51 | if (!capable(CAP_NET_ADMIN)) | |
52 | return -EPERM; | |
53 | ||
54 | /* Only allow one task to receive NOFREEZE privileges */ | |
55 | if (type == NETLINK_MSG_NOFREEZE_ME && usm_helper_data.pid != -1) | |
56 | return -EBUSY; | |
57 | ||
58 | data = (int *)NLMSG_DATA(nlh); | |
59 | ||
60 | switch (type) { | |
61 | case USM_MSG_SUCCESS: | |
62 | case USM_MSG_FAILED: | |
63 | message_received = type; | |
64 | complete(&usm_helper_data.wait_for_process); | |
65 | break; | |
66 | default: | |
67 | printk(KERN_INFO "Storage manager doesn't recognise " "message %d.\n", type); | |
68 | } | |
69 | ||
70 | return 1; | |
71 | } | |
72 | ||
73 | #ifdef CONFIG_NET | |
74 | static int activations; | |
75 | ||
76 | int toi_activate_storage(int force) | |
77 | { | |
78 | int tries = 1; | |
79 | ||
80 | if (usm_helper_data.pid == -1 || !usm_ops.enabled) | |
81 | return 0; | |
82 | ||
83 | message_received = 0; | |
84 | activations++; | |
85 | ||
86 | if (activations > 1 && !force) | |
87 | return 0; | |
88 | ||
89 | while ((!message_received || message_received == USM_MSG_FAILED) && tries < 2) { | |
90 | toi_prepare_status(DONT_CLEAR_BAR, "Activate storage attempt " "%d.\n", tries); | |
91 | ||
92 | init_completion(&usm_helper_data.wait_for_process); | |
93 | ||
94 | toi_send_netlink_message(&usm_helper_data, USM_MSG_CONNECT, NULL, 0); | |
95 | ||
96 | /* Wait 2 seconds for the userspace process to make contact */ | |
97 | wait_for_completion_timeout(&usm_helper_data.wait_for_process, 2 * HZ); | |
98 | ||
99 | tries++; | |
100 | } | |
101 | ||
102 | return 0; | |
103 | } | |
104 | ||
105 | int toi_deactivate_storage(int force) | |
106 | { | |
107 | if (usm_helper_data.pid == -1 || !usm_ops.enabled) | |
108 | return 0; | |
109 | ||
110 | message_received = 0; | |
111 | activations--; | |
112 | ||
113 | if (activations && !force) | |
114 | return 0; | |
115 | ||
116 | init_completion(&usm_helper_data.wait_for_process); | |
117 | ||
118 | toi_send_netlink_message(&usm_helper_data, USM_MSG_DISCONNECT, NULL, 0); | |
119 | ||
120 | wait_for_completion_timeout(&usm_helper_data.wait_for_process, 2 * HZ); | |
121 | ||
122 | if (!message_received || message_received == USM_MSG_FAILED) { | |
123 | printk(KERN_INFO "Returning failure disconnecting storage.\n"); | |
124 | return 1; | |
125 | } | |
126 | ||
127 | return 0; | |
128 | } | |
129 | #endif | |
130 | ||
131 | static void storage_manager_simulate(void) | |
132 | { | |
133 | printk(KERN_INFO "--- Storage manager simulate ---\n"); | |
134 | toi_prepare_usm(); | |
135 | schedule(); | |
136 | printk(KERN_INFO "--- Activate storage 1 ---\n"); | |
137 | toi_activate_storage(1); | |
138 | schedule(); | |
139 | printk(KERN_INFO "--- Deactivate storage 1 ---\n"); | |
140 | toi_deactivate_storage(1); | |
141 | schedule(); | |
142 | printk(KERN_INFO "--- Cleanup usm ---\n"); | |
143 | toi_cleanup_usm(); | |
144 | schedule(); | |
145 | printk(KERN_INFO "--- Storage manager simulate ends ---\n"); | |
146 | } | |
147 | ||
148 | static int usm_storage_needed(void) | |
149 | { | |
150 | return sizeof(int) + strlen(usm_helper_data.program) + 1; | |
151 | } | |
152 | ||
153 | static int usm_save_config_info(char *buf) | |
154 | { | |
155 | int len = strlen(usm_helper_data.program); | |
156 | memcpy(buf, usm_helper_data.program, len + 1); | |
157 | return sizeof(int) + len + 1; | |
158 | } | |
159 | ||
160 | static void usm_load_config_info(char *buf, int size) | |
161 | { | |
162 | /* Don't load the saved path if one has already been set */ | |
163 | if (usm_helper_data.program[0]) | |
164 | return; | |
165 | ||
166 | memcpy(usm_helper_data.program, buf + sizeof(int), *((int *)buf)); | |
167 | } | |
168 | ||
169 | static int usm_memory_needed(void) | |
170 | { | |
171 | /* ball park figure of 32 pages */ | |
172 | return 32 * PAGE_SIZE; | |
173 | } | |
174 | ||
175 | /* toi_prepare_usm | |
176 | */ | |
177 | int toi_prepare_usm(void) | |
178 | { | |
179 | usm_prepare_count++; | |
180 | ||
181 | if (usm_prepare_count > 1 || !usm_ops.enabled) | |
182 | return 0; | |
183 | ||
184 | usm_helper_data.pid = -1; | |
185 | ||
186 | if (!*usm_helper_data.program) | |
187 | return 0; | |
188 | ||
189 | toi_netlink_setup(&usm_helper_data); | |
190 | ||
191 | if (usm_helper_data.pid == -1) | |
192 | printk(KERN_INFO "TuxOnIce Storage Manager wanted, but couldn't" " start it.\n"); | |
193 | ||
194 | toi_activate_storage(0); | |
195 | ||
196 | return usm_helper_data.pid != -1; | |
197 | } | |
198 | ||
199 | void toi_cleanup_usm(void) | |
200 | { | |
201 | usm_prepare_count--; | |
202 | ||
203 | if (usm_helper_data.pid > -1 && !usm_prepare_count) { | |
204 | toi_deactivate_storage(0); | |
205 | toi_netlink_close(&usm_helper_data); | |
206 | } | |
207 | } | |
208 | ||
209 | static void storage_manager_activate(void) | |
210 | { | |
211 | if (storage_manager_action == storage_manager_last_action) | |
212 | return; | |
213 | ||
214 | if (storage_manager_action) | |
215 | toi_prepare_usm(); | |
216 | else | |
217 | toi_cleanup_usm(); | |
218 | ||
219 | storage_manager_last_action = storage_manager_action; | |
220 | } | |
221 | ||
222 | /* | |
223 | * User interface specific /sys/power/tuxonice entries. | |
224 | */ | |
225 | ||
226 | static struct toi_sysfs_data sysfs_params[] = { | |
227 | SYSFS_NONE("simulate_atomic_copy", storage_manager_simulate), | |
228 | SYSFS_INT("enabled", SYSFS_RW, &usm_ops.enabled, 0, 1, 0, NULL), | |
229 | SYSFS_STRING("program", SYSFS_RW, usm_helper_data.program, 254, 0, | |
230 | NULL), | |
231 | SYSFS_INT("activate_storage", SYSFS_RW, &storage_manager_action, 0, 1, | |
232 | 0, storage_manager_activate) | |
233 | }; | |
234 | ||
235 | static struct toi_module_ops usm_ops = { | |
236 | .type = MISC_MODULE, | |
237 | .name = "usm", | |
238 | .directory = "storage_manager", | |
239 | .module = THIS_MODULE, | |
240 | .storage_needed = usm_storage_needed, | |
241 | .save_config_info = usm_save_config_info, | |
242 | .load_config_info = usm_load_config_info, | |
243 | .memory_needed = usm_memory_needed, | |
244 | ||
245 | .sysfs_data = sysfs_params, | |
246 | .num_sysfs_entries = sizeof(sysfs_params) / sizeof(struct toi_sysfs_data), | |
247 | }; | |
248 | ||
249 | /* toi_usm_sysfs_init | |
250 | * Description: Boot time initialisation for user interface. | |
251 | */ | |
252 | int toi_usm_init(void) | |
253 | { | |
254 | usm_helper_data.nl = NULL; | |
255 | usm_helper_data.program[0] = '\0'; | |
256 | usm_helper_data.pid = -1; | |
257 | usm_helper_data.skb_size = 0; | |
258 | usm_helper_data.pool_limit = 6; | |
259 | usm_helper_data.netlink_id = NETLINK_TOI_USM; | |
260 | usm_helper_data.name = "userspace storage manager"; | |
261 | usm_helper_data.rcv_msg = usm_user_rcv_msg; | |
262 | usm_helper_data.interface_version = 2; | |
263 | usm_helper_data.must_init = 0; | |
264 | init_completion(&usm_helper_data.wait_for_process); | |
265 | ||
266 | return toi_register_module(&usm_ops); | |
267 | } | |
268 | ||
269 | void toi_usm_exit(void) | |
270 | { | |
271 | toi_netlink_close_complete(&usm_helper_data); | |
272 | toi_unregister_module(&usm_ops); | |
273 | } |