| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- /*
- * kernel/power/tuxonice_storage.c
- *
- * Copyright (C) 2005-2014 Nigel Cunningham (nigel at tuxonice net)
- *
- * This file is released under the GPLv2.
- *
- * Routines for talking to a userspace program that manages storage.
- *
- * The kernel side:
- * - starts the userspace program;
- * - sends messages telling it when to open and close the connection;
- * - tells it when to quit;
- *
- * The user space side:
- * - passes messages regarding status;
- *
- */
- #include <linux/module.h>
- #include <linux/suspend.h>
- #include <linux/freezer.h>
- #include "tuxonice_sysfs.h"
- #include "tuxonice_modules.h"
- #include "tuxonice_netlink.h"
- #include "tuxonice_storage.h"
- #include "tuxonice_ui.h"
- static struct user_helper_data usm_helper_data;
- static struct toi_module_ops usm_ops;
- static int message_received, usm_prepare_count;
- static int storage_manager_last_action, storage_manager_action;
- static int usm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
- {
- int type;
- int *data;
- type = nlh->nlmsg_type;
- /* A control message: ignore them */
- if (type < NETLINK_MSG_BASE)
- return 0;
- /* Unknown message: reply with EINVAL */
- if (type >= USM_MSG_MAX)
- return -EINVAL;
- /* All operations require privileges, even GET */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
- /* Only allow one task to receive NOFREEZE privileges */
- if (type == NETLINK_MSG_NOFREEZE_ME && usm_helper_data.pid != -1)
- return -EBUSY;
- data = (int *)NLMSG_DATA(nlh);
- switch (type) {
- case USM_MSG_SUCCESS:
- case USM_MSG_FAILED:
- message_received = type;
- complete(&usm_helper_data.wait_for_process);
- break;
- default:
- pr_warn("Storage manager doesn't recognise " "message %d.\n", type);
- }
- return 1;
- }
- #ifdef CONFIG_NET
- static int activations;
- int toi_activate_storage(int force)
- {
- int tries = 1;
- if (usm_helper_data.pid == -1 || !usm_ops.enabled)
- return 0;
- message_received = 0;
- activations++;
- if (activations > 1 && !force)
- return 0;
- while ((!message_received || message_received == USM_MSG_FAILED) && tries < 2) {
- toi_prepare_status(DONT_CLEAR_BAR, "Activate storage attempt " "%d.\n", tries);
- init_completion(&usm_helper_data.wait_for_process);
- toi_send_netlink_message(&usm_helper_data, USM_MSG_CONNECT, NULL, 0);
- /* Wait 2 seconds for the userspace process to make contact */
- wait_for_completion_timeout(&usm_helper_data.wait_for_process, 2 * HZ);
- tries++;
- }
- return 0;
- }
- int toi_deactivate_storage(int force)
- {
- if (usm_helper_data.pid == -1 || !usm_ops.enabled)
- return 0;
- message_received = 0;
- activations--;
- if (activations && !force)
- return 0;
- init_completion(&usm_helper_data.wait_for_process);
- toi_send_netlink_message(&usm_helper_data, USM_MSG_DISCONNECT, NULL, 0);
- wait_for_completion_timeout(&usm_helper_data.wait_for_process, 2 * HZ);
- if (!message_received || message_received == USM_MSG_FAILED) {
- pr_warn("Returning failure disconnecting storage.\n");
- return 1;
- }
- return 0;
- }
- #endif
- static void storage_manager_simulate(void)
- {
- pr_warn("--- Storage manager simulate ---\n");
- toi_prepare_usm();
- schedule();
- pr_warn("--- Activate storage 1 ---\n");
- toi_activate_storage(1);
- schedule();
- pr_warn("--- Deactivate storage 1 ---\n");
- toi_deactivate_storage(1);
- schedule();
- pr_warn("--- Cleanup usm ---\n");
- toi_cleanup_usm();
- schedule();
- pr_warn("--- Storage manager simulate ends ---\n");
- }
- static int usm_storage_needed(void)
- {
- return sizeof(int) + strlen(usm_helper_data.program) + 1;
- }
- static int usm_save_config_info(char *buf)
- {
- int len = strlen(usm_helper_data.program);
- memcpy(buf, usm_helper_data.program, len + 1);
- return sizeof(int) + len + 1;
- }
- static void usm_load_config_info(char *buf, int size)
- {
- /* Don't load the saved path if one has already been set */
- if (usm_helper_data.program[0])
- return;
- memcpy(usm_helper_data.program, buf + sizeof(int), *((int *)buf));
- }
- static int usm_memory_needed(void)
- {
- /* ball park figure of 32 pages */
- return 32 * PAGE_SIZE;
- }
- /* toi_prepare_usm
- */
- int toi_prepare_usm(void)
- {
- usm_prepare_count++;
- if (usm_prepare_count > 1 || !usm_ops.enabled)
- return 0;
- usm_helper_data.pid = -1;
- if (!*usm_helper_data.program)
- return 0;
- toi_netlink_setup(&usm_helper_data);
- if (usm_helper_data.pid == -1)
- pr_warn("TuxOnIce Storage Manager wanted, but couldn't" " start it.\n");
- toi_activate_storage(0);
- return usm_helper_data.pid != -1;
- }
- void toi_cleanup_usm(void)
- {
- usm_prepare_count--;
- if (usm_helper_data.pid > -1 && !usm_prepare_count) {
- toi_deactivate_storage(0);
- toi_netlink_close(&usm_helper_data);
- }
- }
- static void storage_manager_activate(void)
- {
- if (storage_manager_action == storage_manager_last_action)
- return;
- if (storage_manager_action)
- toi_prepare_usm();
- else
- toi_cleanup_usm();
- storage_manager_last_action = storage_manager_action;
- }
- /*
- * User interface specific /sys/power/tuxonice entries.
- */
- static struct toi_sysfs_data sysfs_params[] = {
- SYSFS_NONE("simulate_atomic_copy", storage_manager_simulate),
- SYSFS_INT("enabled", SYSFS_RW, &usm_ops.enabled, 0, 1, 0, NULL),
- SYSFS_STRING("program", SYSFS_RW, usm_helper_data.program, 254, 0,
- NULL),
- SYSFS_INT("activate_storage", SYSFS_RW, &storage_manager_action, 0, 1,
- 0, storage_manager_activate)
- };
- static struct toi_module_ops usm_ops = {
- .type = MISC_MODULE,
- .name = "usm",
- .directory = "storage_manager",
- .module = THIS_MODULE,
- .storage_needed = usm_storage_needed,
- .save_config_info = usm_save_config_info,
- .load_config_info = usm_load_config_info,
- .memory_needed = usm_memory_needed,
- .sysfs_data = sysfs_params,
- .num_sysfs_entries = sizeof(sysfs_params) / sizeof(struct toi_sysfs_data),
- };
- /* toi_usm_sysfs_init
- * Description: Boot time initialisation for user interface.
- */
- int toi_usm_init(void)
- {
- usm_helper_data.nl = NULL;
- usm_helper_data.program[0] = '\0';
- usm_helper_data.pid = -1;
- usm_helper_data.skb_size = 0;
- usm_helper_data.pool_limit = 6;
- usm_helper_data.netlink_id = NETLINK_TOI_USM;
- usm_helper_data.name = "userspace storage manager";
- usm_helper_data.rcv_msg = usm_user_rcv_msg;
- usm_helper_data.interface_version = 2;
- usm_helper_data.must_init = 0;
- init_completion(&usm_helper_data.wait_for_process);
- return toi_register_module(&usm_ops);
- }
- void toi_usm_exit(void)
- {
- toi_netlink_close_complete(&usm_helper_data);
- toi_unregister_module(&usm_ops);
- }
|