Merge tag 'v3.10.85' into update
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / kernel / power / tuxonice_storage.c
CommitLineData
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
30static struct user_helper_data usm_helper_data;
31static struct toi_module_ops usm_ops;
32static int message_received, usm_prepare_count;
33static int storage_manager_last_action, storage_manager_action;
34
35static 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
74static int activations;
75
76int 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
105int 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
131static 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
148static int usm_storage_needed(void)
149{
150 return sizeof(int) + strlen(usm_helper_data.program) + 1;
151}
152
153static 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
160static 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
169static 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 */
177int 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
199void 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
209static 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
226static 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
235static 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 */
252int 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
269void toi_usm_exit(void)
270{
271 toi_netlink_close_complete(&usm_helper_data);
272 toi_unregister_module(&usm_ops);
273}