import PULS_20160108
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / kernel / power / tuxonice_storage.c
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 }