Commit | Line | Data |
---|---|---|
6fa3eb70 S |
1 | /* |
2 | * kernel/power/tuxonice_modules.c | |
3 | * | |
4 | * Copyright (C) 2004-2010 Nigel Cunningham (nigel at tuxonice net) | |
5 | * | |
6 | */ | |
7 | ||
8 | #include <linux/suspend.h> | |
9 | #include "tuxonice.h" | |
10 | #include "tuxonice_modules.h" | |
11 | #include "tuxonice_sysfs.h" | |
12 | #include "tuxonice_ui.h" | |
13 | ||
14 | LIST_HEAD(toi_filters); | |
15 | LIST_HEAD(toiAllocators); | |
16 | ||
17 | LIST_HEAD(toi_modules); | |
18 | EXPORT_SYMBOL_GPL(toi_modules); | |
19 | ||
20 | struct toi_module_ops *toiActiveAllocator; | |
21 | EXPORT_SYMBOL_GPL(toiActiveAllocator); | |
22 | ||
23 | static int toi_num_filters; | |
24 | int toiNumAllocators, toi_num_modules; | |
25 | ||
26 | /* | |
27 | * toi_header_storage_for_modules | |
28 | * | |
29 | * Returns the amount of space needed to store configuration | |
30 | * data needed by the modules prior to copying back the original | |
31 | * kernel. We can exclude data for pageset2 because it will be | |
32 | * available anyway once the kernel is copied back. | |
33 | */ | |
34 | long toi_header_storage_for_modules(void) | |
35 | { | |
36 | struct toi_module_ops *this_module; | |
37 | int bytes = 0; | |
38 | ||
39 | list_for_each_entry(this_module, &toi_modules, module_list) { | |
40 | if (!this_module->enabled || | |
41 | (this_module->type == WRITER_MODULE && toiActiveAllocator != this_module)) | |
42 | continue; | |
43 | if (this_module->storage_needed) { | |
44 | int this = this_module->storage_needed() + | |
45 | sizeof(struct toi_module_header) + sizeof(int); | |
46 | this_module->header_requested = this; | |
47 | bytes += this; | |
48 | } | |
49 | } | |
50 | ||
51 | /* One more for the empty terminator */ | |
52 | return bytes + sizeof(struct toi_module_header); | |
53 | } | |
54 | ||
55 | void print_toi_header_storage_for_modules(void) | |
56 | { | |
57 | struct toi_module_ops *this_module; | |
58 | int bytes = 0; | |
59 | ||
60 | printk(KERN_DEBUG "Header storage:\n"); | |
61 | list_for_each_entry(this_module, &toi_modules, module_list) { | |
62 | if (!this_module->enabled || | |
63 | (this_module->type == WRITER_MODULE && toiActiveAllocator != this_module)) | |
64 | continue; | |
65 | if (this_module->storage_needed) { | |
66 | int this = this_module->storage_needed() + | |
67 | sizeof(struct toi_module_header) + sizeof(int); | |
68 | this_module->header_requested = this; | |
69 | bytes += this; | |
70 | printk(KERN_DEBUG "+ %16s : %-4d/%d.\n", | |
71 | this_module->name, this_module->header_used, this); | |
72 | } | |
73 | } | |
74 | ||
75 | printk(KERN_DEBUG "+ empty terminator : %zu.\n", sizeof(struct toi_module_header)); | |
76 | printk(KERN_DEBUG " ====\n"); | |
77 | printk(KERN_DEBUG " %zu\n", bytes + sizeof(struct toi_module_header)); | |
78 | } | |
79 | EXPORT_SYMBOL_GPL(print_toi_header_storage_for_modules); | |
80 | ||
81 | /* | |
82 | * toi_memory_for_modules | |
83 | * | |
84 | * Returns the amount of memory requested by modules for | |
85 | * doing their work during the cycle. | |
86 | */ | |
87 | ||
88 | long toi_memory_for_modules(int print_parts) | |
89 | { | |
90 | long bytes = 0, result; | |
91 | struct toi_module_ops *this_module; | |
92 | ||
93 | if (print_parts) | |
94 | printk(KERN_INFO "Memory for modules:\n===================\n"); | |
95 | list_for_each_entry(this_module, &toi_modules, module_list) { | |
96 | int this; | |
97 | if (!this_module->enabled) | |
98 | continue; | |
99 | if (this_module->memory_needed) { | |
100 | this = this_module->memory_needed(); | |
101 | if (print_parts) | |
102 | printk(KERN_INFO "%10d bytes (%5ld pages) for " | |
103 | "module '%s'.\n", this, | |
104 | DIV_ROUND_UP(this, PAGE_SIZE), this_module->name); | |
105 | bytes += this; | |
106 | } | |
107 | } | |
108 | ||
109 | result = DIV_ROUND_UP(bytes, PAGE_SIZE); | |
110 | if (print_parts) | |
111 | printk(KERN_INFO " => %ld bytes, %ld pages.\n", bytes, result); | |
112 | ||
113 | return result; | |
114 | } | |
115 | ||
116 | /* | |
117 | * toi_expected_compression_ratio | |
118 | * | |
119 | * Returns the compression ratio expected when saving the image. | |
120 | */ | |
121 | ||
122 | int toi_expected_compression_ratio(void) | |
123 | { | |
124 | int ratio = 100; | |
125 | struct toi_module_ops *this_module; | |
126 | ||
127 | list_for_each_entry(this_module, &toi_modules, module_list) { | |
128 | if (!this_module->enabled) | |
129 | continue; | |
130 | if (this_module->expected_compression) | |
131 | ratio = ratio * this_module->expected_compression() | |
132 | / 100; | |
133 | } | |
134 | ||
135 | return ratio; | |
136 | } | |
137 | ||
138 | #ifdef CONFIG_TOI_ENHANCE | |
139 | /* | |
140 | * toi_actual_compression_ratio | |
141 | * | |
142 | * Returns the actual compression ratio when saving the image. | |
143 | */ | |
144 | int toi_actual_compression_ratio(void) | |
145 | { | |
146 | int ratio = 0; | |
147 | struct toi_module_ops *this_module; | |
148 | ||
149 | list_for_each_entry(this_module, &toi_modules, module_list) { | |
150 | if (!this_module->enabled) | |
151 | continue; | |
152 | if (this_module->actual_compression) | |
153 | ratio = this_module->actual_compression(); | |
154 | } | |
155 | ||
156 | return ratio; | |
157 | } | |
158 | #endif /* CONFIG_TOI_ENHANCE */ | |
159 | ||
160 | ||
161 | /* toi_find_module_given_dir | |
162 | * Functionality : Return a module (if found), given a pointer | |
163 | * to its directory name | |
164 | */ | |
165 | ||
166 | static struct toi_module_ops *toi_find_module_given_dir(char *name) | |
167 | { | |
168 | struct toi_module_ops *this_module, *found_module = NULL; | |
169 | ||
170 | list_for_each_entry(this_module, &toi_modules, module_list) { | |
171 | if (!strcmp(name, this_module->directory)) { | |
172 | found_module = this_module; | |
173 | break; | |
174 | } | |
175 | } | |
176 | ||
177 | return found_module; | |
178 | } | |
179 | ||
180 | /* toi_find_module_given_name | |
181 | * Functionality : Return a module (if found), given a pointer | |
182 | * to its name | |
183 | */ | |
184 | ||
185 | struct toi_module_ops *toi_find_module_given_name(char *name) | |
186 | { | |
187 | struct toi_module_ops *this_module, *found_module = NULL; | |
188 | ||
189 | list_for_each_entry(this_module, &toi_modules, module_list) { | |
190 | if (!strcmp(name, this_module->name)) { | |
191 | found_module = this_module; | |
192 | break; | |
193 | } | |
194 | } | |
195 | ||
196 | return found_module; | |
197 | } | |
198 | ||
199 | /* | |
200 | * toi_print_module_debug_info | |
201 | * Functionality : Get debugging info from modules into a buffer. | |
202 | */ | |
203 | int toi_print_module_debug_info(char *buffer, int buffer_size) | |
204 | { | |
205 | struct toi_module_ops *this_module; | |
206 | int len = 0; | |
207 | ||
208 | list_for_each_entry(this_module, &toi_modules, module_list) { | |
209 | if (!this_module->enabled) | |
210 | continue; | |
211 | if (this_module->print_debug_info) { | |
212 | int result; | |
213 | result = this_module->print_debug_info(buffer + len, buffer_size - len); | |
214 | len += result; | |
215 | } | |
216 | } | |
217 | ||
218 | /* Ensure null terminated */ | |
219 | buffer[buffer_size] = 0; | |
220 | ||
221 | return len; | |
222 | } | |
223 | ||
224 | /* | |
225 | * toi_register_module | |
226 | * | |
227 | * Register a module. | |
228 | */ | |
229 | int toi_register_module(struct toi_module_ops *module) | |
230 | { | |
231 | int i; | |
232 | struct kobject *kobj; | |
233 | ||
234 | module->enabled = 1; | |
235 | ||
236 | if (toi_find_module_given_name(module->name)) { | |
237 | printk(KERN_INFO "TuxOnIce: Trying to load module %s," | |
238 | " which is already registered.\n", module->name); | |
239 | return -EBUSY; | |
240 | } | |
241 | ||
242 | switch (module->type) { | |
243 | case FILTER_MODULE: | |
244 | list_add_tail(&module->type_list, &toi_filters); | |
245 | toi_num_filters++; | |
246 | break; | |
247 | case WRITER_MODULE: | |
248 | list_add_tail(&module->type_list, &toiAllocators); | |
249 | toiNumAllocators++; | |
250 | break; | |
251 | case MISC_MODULE: | |
252 | case MISC_HIDDEN_MODULE: | |
253 | case BIO_ALLOCATOR_MODULE: | |
254 | break; | |
255 | default: | |
256 | printk(KERN_ERR "Hmmm. Module '%s' has an invalid type." | |
257 | " It has been ignored.\n", module->name); | |
258 | return -EINVAL; | |
259 | } | |
260 | list_add_tail(&module->module_list, &toi_modules); | |
261 | toi_num_modules++; | |
262 | ||
263 | if ((!module->directory && !module->shared_directory) || | |
264 | !module->sysfs_data || !module->num_sysfs_entries) | |
265 | return 0; | |
266 | ||
267 | /* | |
268 | * Modules may share a directory, but those with shared_dir | |
269 | * set must be loaded (via symbol dependencies) after parents | |
270 | * and unloaded beforehand. | |
271 | */ | |
272 | if (module->shared_directory) { | |
273 | struct toi_module_ops *shared = toi_find_module_given_dir(module->shared_directory); | |
274 | if (!shared) { | |
275 | printk(KERN_ERR "TuxOnIce: Module %s wants to share " | |
276 | "%s's directory but %s isn't loaded.\n", | |
277 | module->name, module->shared_directory, module->shared_directory); | |
278 | toi_unregister_module(module); | |
279 | return -ENODEV; | |
280 | } | |
281 | kobj = shared->dir_kobj; | |
282 | } else { | |
283 | if (!strncmp(module->directory, "[ROOT]", 6)) | |
284 | kobj = tuxonice_kobj; | |
285 | else | |
286 | kobj = make_toi_sysdir(module->directory); | |
287 | } | |
288 | module->dir_kobj = kobj; | |
289 | for (i = 0; i < module->num_sysfs_entries; i++) { | |
290 | int result = toi_register_sysfs_file(kobj, | |
291 | &module->sysfs_data[i]); | |
292 | if (result) | |
293 | return result; | |
294 | } | |
295 | return 0; | |
296 | } | |
297 | EXPORT_SYMBOL_GPL(toi_register_module); | |
298 | ||
299 | /* | |
300 | * toi_unregister_module | |
301 | * | |
302 | * Remove a module. | |
303 | */ | |
304 | void toi_unregister_module(struct toi_module_ops *module) | |
305 | { | |
306 | int i; | |
307 | ||
308 | if (module->dir_kobj) | |
309 | for (i = 0; i < module->num_sysfs_entries; i++) | |
310 | toi_unregister_sysfs_file(module->dir_kobj, &module->sysfs_data[i]); | |
311 | ||
312 | if (!module->shared_directory && module->directory && | |
313 | strncmp(module->directory, "[ROOT]", 6)) | |
314 | remove_toi_sysdir(module->dir_kobj); | |
315 | ||
316 | switch (module->type) { | |
317 | case FILTER_MODULE: | |
318 | list_del(&module->type_list); | |
319 | toi_num_filters--; | |
320 | break; | |
321 | case WRITER_MODULE: | |
322 | list_del(&module->type_list); | |
323 | toiNumAllocators--; | |
324 | if (toiActiveAllocator == module) { | |
325 | toiActiveAllocator = NULL; | |
326 | clear_toi_state(TOI_CAN_RESUME); | |
327 | clear_toi_state(TOI_CAN_HIBERNATE); | |
328 | } | |
329 | break; | |
330 | case MISC_MODULE: | |
331 | case MISC_HIDDEN_MODULE: | |
332 | case BIO_ALLOCATOR_MODULE: | |
333 | break; | |
334 | default: | |
335 | printk(KERN_ERR "Module '%s' has an invalid type." | |
336 | " It has been ignored.\n", module->name); | |
337 | return; | |
338 | } | |
339 | list_del(&module->module_list); | |
340 | toi_num_modules--; | |
341 | } | |
342 | EXPORT_SYMBOL_GPL(toi_unregister_module); | |
343 | ||
344 | /* | |
345 | * toi_move_module_tail | |
346 | * | |
347 | * Rearrange modules when reloading the config. | |
348 | */ | |
349 | void toi_move_module_tail(struct toi_module_ops *module) | |
350 | { | |
351 | switch (module->type) { | |
352 | case FILTER_MODULE: | |
353 | if (toi_num_filters > 1) | |
354 | list_move_tail(&module->type_list, &toi_filters); | |
355 | break; | |
356 | case WRITER_MODULE: | |
357 | if (toiNumAllocators > 1) | |
358 | list_move_tail(&module->type_list, &toiAllocators); | |
359 | break; | |
360 | case MISC_MODULE: | |
361 | case MISC_HIDDEN_MODULE: | |
362 | case BIO_ALLOCATOR_MODULE: | |
363 | break; | |
364 | default: | |
365 | printk(KERN_ERR "Module '%s' has an invalid type." | |
366 | " It has been ignored.\n", module->name); | |
367 | return; | |
368 | } | |
369 | if ((toi_num_filters + toiNumAllocators) > 1) | |
370 | list_move_tail(&module->module_list, &toi_modules); | |
371 | } | |
372 | ||
373 | /* | |
374 | * toi_initialise_modules | |
375 | * | |
376 | * Get ready to do some work! | |
377 | */ | |
378 | int toi_initialise_modules(int starting_cycle, int early) | |
379 | { | |
380 | struct toi_module_ops *this_module; | |
381 | int result; | |
382 | ||
383 | list_for_each_entry(this_module, &toi_modules, module_list) { | |
384 | this_module->header_requested = 0; | |
385 | this_module->header_used = 0; | |
386 | if (!this_module->enabled) | |
387 | continue; | |
388 | if (this_module->early != early) | |
389 | continue; | |
390 | if (this_module->initialise) { | |
391 | result = this_module->initialise(starting_cycle); | |
392 | if (result) { | |
393 | toi_cleanup_modules(starting_cycle); | |
394 | return result; | |
395 | } | |
396 | this_module->initialised = 1; | |
397 | } | |
398 | } | |
399 | ||
400 | return 0; | |
401 | } | |
402 | ||
403 | /* | |
404 | * toi_cleanup_modules | |
405 | * | |
406 | * Tell modules the work is done. | |
407 | */ | |
408 | void toi_cleanup_modules(int finishing_cycle) | |
409 | { | |
410 | struct toi_module_ops *this_module; | |
411 | ||
412 | list_for_each_entry(this_module, &toi_modules, module_list) { | |
413 | if (!this_module->enabled || !this_module->initialised) | |
414 | continue; | |
415 | if (this_module->cleanup) | |
416 | this_module->cleanup(finishing_cycle); | |
417 | this_module->initialised = 0; | |
418 | } | |
419 | } | |
420 | ||
421 | /* | |
422 | * toi_pre_atomic_restore_modules | |
423 | * | |
424 | * Get ready to do some work! | |
425 | */ | |
426 | void toi_pre_atomic_restore_modules(struct toi_boot_kernel_data *bkd) | |
427 | { | |
428 | struct toi_module_ops *this_module; | |
429 | ||
430 | list_for_each_entry(this_module, &toi_modules, module_list) { | |
431 | if (this_module->enabled && this_module->pre_atomic_restore) | |
432 | this_module->pre_atomic_restore(bkd); | |
433 | } | |
434 | } | |
435 | ||
436 | /* | |
437 | * toi_post_atomic_restore_modules | |
438 | * | |
439 | * Get ready to do some work! | |
440 | */ | |
441 | void toi_post_atomic_restore_modules(struct toi_boot_kernel_data *bkd) | |
442 | { | |
443 | struct toi_module_ops *this_module; | |
444 | ||
445 | list_for_each_entry(this_module, &toi_modules, module_list) { | |
446 | if (this_module->enabled && this_module->post_atomic_restore) | |
447 | this_module->post_atomic_restore(bkd); | |
448 | } | |
449 | } | |
450 | ||
451 | /* | |
452 | * toi_get_next_filter | |
453 | * | |
454 | * Get the next filter in the pipeline. | |
455 | */ | |
456 | struct toi_module_ops *toi_get_next_filter(struct toi_module_ops *filter_sought) | |
457 | { | |
458 | struct toi_module_ops *last_filter = NULL, *this_filter = NULL; | |
459 | ||
460 | list_for_each_entry(this_filter, &toi_filters, type_list) { | |
461 | if (!this_filter->enabled) | |
462 | continue; | |
463 | if ((last_filter == filter_sought) || (!filter_sought)) | |
464 | return this_filter; | |
465 | last_filter = this_filter; | |
466 | } | |
467 | ||
468 | return toiActiveAllocator; | |
469 | } | |
470 | EXPORT_SYMBOL_GPL(toi_get_next_filter); | |
471 | ||
472 | /** | |
473 | * toi_show_modules: Printk what support is loaded. | |
474 | */ | |
475 | void toi_print_modules(void) | |
476 | { | |
477 | struct toi_module_ops *this_module; | |
478 | int prev = 0; | |
479 | ||
480 | printk(KERN_INFO "TuxOnIce " TOI_CORE_VERSION ", with support for"); | |
481 | ||
482 | list_for_each_entry(this_module, &toi_modules, module_list) { | |
483 | if (this_module->type == MISC_HIDDEN_MODULE) | |
484 | continue; | |
485 | printk("%s %s%s%s", prev ? "," : "", | |
486 | this_module->enabled ? "" : "[", | |
487 | this_module->name, this_module->enabled ? "" : "]"); | |
488 | prev = 1; | |
489 | } | |
490 | ||
491 | printk(".\n"); | |
492 | } | |
493 | ||
494 | /* toi_get_modules | |
495 | * | |
496 | * Take a reference to modules so they can't go away under us. | |
497 | */ | |
498 | ||
499 | int toi_get_modules(void) | |
500 | { | |
501 | struct toi_module_ops *this_module; | |
502 | ||
503 | list_for_each_entry(this_module, &toi_modules, module_list) { | |
504 | struct toi_module_ops *this_module2; | |
505 | ||
506 | if (try_module_get(this_module->module)) | |
507 | continue; | |
508 | ||
509 | /* Failed! Reverse gets and return error */ | |
510 | list_for_each_entry(this_module2, &toi_modules, module_list) { | |
511 | if (this_module == this_module2) | |
512 | return -EINVAL; | |
513 | module_put(this_module2->module); | |
514 | } | |
515 | } | |
516 | return 0; | |
517 | } | |
518 | ||
519 | /* toi_put_modules | |
520 | * | |
521 | * Release our references to modules we used. | |
522 | */ | |
523 | ||
524 | void toi_put_modules(void) | |
525 | { | |
526 | struct toi_module_ops *this_module; | |
527 | ||
528 | list_for_each_entry(this_module, &toi_modules, module_list) | |
529 | module_put(this_module->module); | |
530 | } |