tuxonice_prune.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. /*
  2. * kernel/power/tuxonice_prune.c
  3. *
  4. * Copyright (C) 2012 Nigel Cunningham (nigel at tuxonice net)
  5. *
  6. * This file is released under the GPLv2.
  7. *
  8. * This file implements a TuxOnIce module that seeks to prune the
  9. * amount of data written to disk. It builds a table of hashes
  10. * of the uncompressed data, and writes the pfn of the previous page
  11. * with the same contents instead of repeating the data when a match
  12. * is found.
  13. */
  14. #include <linux/suspend.h>
  15. #include <linux/highmem.h>
  16. #include <linux/vmalloc.h>
  17. #include <linux/crypto.h>
  18. #include <linux/scatterlist.h>
  19. #include <crypto/hash.h>
  20. #include "tuxonice_builtin.h"
  21. #include "tuxonice.h"
  22. #include "tuxonice_modules.h"
  23. #include "tuxonice_sysfs.h"
  24. #include "tuxonice_io.h"
  25. #include "tuxonice_ui.h"
  26. #include "tuxonice_alloc.h"
  27. /*
  28. * We never write a page bigger than PAGE_SIZE, so use a large number
  29. * to indicate that data is a PFN.
  30. */
  31. #define PRUNE_DATA_IS_PFN (PAGE_SIZE + 100)
  32. static unsigned long toi_pruned_pages;
  33. static struct toi_module_ops toi_prune_ops;
  34. static struct toi_module_ops *next_driver;
  35. static char toi_prune_hash_algo_name[32] = "sha1";
  36. static DEFINE_MUTEX(stats_lock);
  37. struct toi_cpu_context {
  38. struct shash_desc desc;
  39. char *digest;
  40. };
  41. #define OUT_BUF_SIZE (2 * PAGE_SIZE)
  42. static DEFINE_PER_CPU(struct toi_cpu_context, contexts);
  43. /*
  44. * toi_crypto_prepare
  45. *
  46. * Prepare to do some work by allocating buffers and transforms.
  47. */
  48. static int toi_prune_crypto_prepare(void)
  49. {
  50. int cpu, ret, digestsize;
  51. if (!*toi_prune_hash_algo_name) {
  52. pr_warn("TuxOnIce: Pruning enabled but no " "hash algorithm set.\n");
  53. return 1;
  54. }
  55. for_each_online_cpu(cpu) {
  56. struct toi_cpu_context *this = &per_cpu(contexts, cpu);
  57. this->desc.tfm = crypto_alloc_shash(toi_prune_hash_algo_name, 0, 0);
  58. if (IS_ERR(this->desc.tfm)) {
  59. pr_warn("TuxOnIce: Failed to allocate the %s prune hash algorithm.\n",
  60. toi_prune_hash_algo_name);
  61. this->desc.tfm = NULL;
  62. return 1;
  63. }
  64. if (!digestsize)
  65. digestsize = crypto_shash_digestsize(this->desc.tfm);
  66. this->digest = kmalloc(digestsize, GFP_KERNEL);
  67. if (!this->digest) {
  68. crypto_free_shash(this->desc.tfm);
  69. this->desc.tfm = NULL;
  70. }
  71. this->desc.flags = 0;
  72. ret = crypto_shash_init(&this->desc);
  73. if (ret < 0) {
  74. pr_warn("TuxOnIce: Failed to initialise the %s prune hash algorithm.\n",
  75. toi_prune_hash_algo_name);
  76. kfree(this->digest);
  77. this->digest = NULL;
  78. crypto_free_shash(this->desc.tfm);
  79. this->desc.tfm = NULL;
  80. return 1;
  81. }
  82. }
  83. return 0;
  84. }
  85. static int toi_prune_rw_cleanup(int writing)
  86. {
  87. int cpu;
  88. for_each_online_cpu(cpu) {
  89. struct toi_cpu_context *this = &per_cpu(contexts, cpu);
  90. if (this->desc.tfm) {
  91. crypto_free_shash(this->desc.tfm);
  92. this->desc.tfm = NULL;
  93. }
  94. kfree(this->digest);
  95. this->digest = NULL;
  96. }
  97. return 0;
  98. }
  99. /*
  100. * toi_prune_init
  101. */
  102. static int toi_prune_init(int toi_or_resume)
  103. {
  104. if (!toi_or_resume)
  105. return 0;
  106. toi_pruned_pages = 0;
  107. next_driver = toi_get_next_filter(&toi_prune_ops);
  108. return next_driver ? 0 : -ECHILD;
  109. }
  110. /*
  111. * toi_prune_rw_init()
  112. */
  113. static int toi_prune_rw_init(int rw, int stream_number)
  114. {
  115. if (toi_prune_crypto_prepare()) {
  116. pr_err("Failed to initialise prune " "algorithm.\n");
  117. if (rw == READ) {
  118. pr_warn("Unable to read the image.\n");
  119. return -ENODEV;
  120. }
  121. pr_warn("Continuing without " "pruning the image.\n");
  122. toi_prune_ops.enabled = 0;
  123. }
  124. return 0;
  125. }
  126. /*
  127. * toi_prune_write_page()
  128. *
  129. * Compress a page of data, buffering output and passing on filled
  130. * pages to the next module in the pipeline.
  131. *
  132. * Buffer_page: Pointer to a buffer of size PAGE_SIZE, containing
  133. * data to be checked.
  134. *
  135. * Returns: 0 on success. Otherwise the error is that returned by later
  136. * modules, -ECHILD if we have a broken pipeline or -EIO if
  137. * zlib errs.
  138. */
  139. static int toi_prune_write_page(unsigned long index, int buf_type,
  140. void *buffer_page, unsigned int buf_size)
  141. {
  142. int ret = 0, cpu = smp_processor_id(), write_data = 1;
  143. struct toi_cpu_context *ctx = &per_cpu(contexts, cpu);
  144. u8 *output_buffer = buffer_page;
  145. int output_len = buf_size;
  146. int out_buf_type = buf_type;
  147. void *buffer_start;
  148. u32 buf[4];
  149. if (ctx->desc.tfm) {
  150. buffer_start = TOI_MAP(buf_type, buffer_page);
  151. ctx->len = OUT_BUF_SIZE;
  152. ret = crypto_shash_digest(&ctx->desc, buffer_start, buf_size, &ctx->digest);
  153. if (ret) {
  154. pr_warn("TuxOnIce: Failed to calculate digest (%d).\n", ret);
  155. } else {
  156. mutex_lock(&stats_lock);
  157. toi_pruned_pages++;
  158. mutex_unlock(&stats_lock);
  159. }
  160. TOI_UNMAP(buf_type, buffer_page);
  161. }
  162. if (write_data)
  163. ret = next_driver->write_page(index, out_buf_type, output_buffer, output_len);
  164. else
  165. ret = next_driver->write_page(index, out_buf_type, output_buffer, output_len);
  166. return ret;
  167. }
  168. /*
  169. * toi_prune_read_page()
  170. * @buffer_page: struct page *. Pointer to a buffer of size PAGE_SIZE.
  171. *
  172. * Retrieve data from later modules or from a previously loaded page and
  173. * fill the input buffer.
  174. * Zero if successful. Error condition from me or from downstream on failure.
  175. */
  176. static int toi_prune_read_page(unsigned long *index, int buf_type,
  177. void *buffer_page, unsigned int *buf_size)
  178. {
  179. int ret, cpu = smp_processor_id();
  180. unsigned int len;
  181. char *buffer_start;
  182. struct toi_cpu_context *ctx = &per_cpu(contexts, cpu);
  183. if (!ctx->desc.tfm)
  184. return next_driver->read_page(index, TOI_PAGE, buffer_page, buf_size);
  185. /*
  186. * All our reads must be synchronous - we can't handle
  187. * data that hasn't been read yet.
  188. */
  189. ret = next_driver->read_page(index, buf_type, buffer_page, &len);
  190. if (len == PRUNE_DATA_IS_PFN)
  191. buffer_start = kmap(buffer_page);
  192. return ret;
  193. }
  194. /*
  195. * toi_prune_print_debug_stats
  196. * @buffer: Pointer to a buffer into which the debug info will be printed.
  197. * @size: Size of the buffer.
  198. *
  199. * Print information to be recorded for debugging purposes into a buffer.
  200. * Returns: Number of characters written to the buffer.
  201. */
  202. static int toi_prune_print_debug_stats(char *buffer, int size)
  203. {
  204. int len;
  205. /* Output the number of pages pruned. */
  206. if (*toi_prune_hash_algo_name)
  207. len = scnprintf(buffer, size, "- Compressor is '%s'.\n", toi_prune_hash_algo_name);
  208. else
  209. len = scnprintf(buffer, size, "- Compressor is not set.\n");
  210. if (toi_pruned_pages)
  211. len += scnprintf(buffer + len, size - len, " Pruned %lu pages).\n", toi_pruned_pages);
  212. return len;
  213. }
  214. /*
  215. * toi_prune_memory_needed
  216. *
  217. * Tell the caller how much memory we need to operate during hibernate/resume.
  218. * Returns: Unsigned long. Maximum number of bytes of memory required for
  219. * operation.
  220. */
  221. static int toi_prune_memory_needed(void)
  222. {
  223. return 2 * PAGE_SIZE;
  224. }
  225. static int toi_prune_storage_needed(void)
  226. {
  227. return 2 * sizeof(unsigned long) + 2 * sizeof(int) + strlen(toi_prune_hash_algo_name) + 1;
  228. }
  229. /*
  230. * toi_prune_save_config_info
  231. * @buffer: Pointer to a buffer of size PAGE_SIZE.
  232. *
  233. * Save informaton needed when reloading the image at resume time.
  234. * Returns: Number of bytes used for saving our data.
  235. */
  236. static int toi_prune_save_config_info(char *buffer)
  237. {
  238. int len = strlen(toi_prune_hash_algo_name) + 1, offset = 0;
  239. *((unsigned long *)buffer) = toi_pruned_pages;
  240. offset += sizeof(unsigned long);
  241. *((int *)(buffer + offset)) = len;
  242. offset += sizeof(int);
  243. strncpy(buffer + offset, toi_prune_hash_algo_name, len);
  244. return offset + len;
  245. }
  246. /* toi_prune_load_config_info
  247. * @buffer: Pointer to the start of the data.
  248. * @size: Number of bytes that were saved.
  249. *
  250. * Description: Reload information needed for passing back to the
  251. * resumed kernel.
  252. */
  253. static void toi_prune_load_config_info(char *buffer, int size)
  254. {
  255. int len, offset = 0;
  256. toi_pruned_pages = *((unsigned long *)buffer);
  257. offset += sizeof(unsigned long);
  258. len = *((int *)(buffer + offset));
  259. offset += sizeof(int);
  260. strncpy(toi_prune_hash_algo_name, buffer + offset, len);
  261. }
  262. static void toi_prune_pre_atomic_restore(struct toi_boot_kernel_data *bkd)
  263. {
  264. bkd->pruned_pages = toi_pruned_pages;
  265. }
  266. static void toi_prune_post_atomic_restore(struct toi_boot_kernel_data *bkd)
  267. {
  268. toi_pruned_pages = bkd->pruned_pages;
  269. }
  270. /*
  271. * toi_expected_ratio
  272. *
  273. * Description: Returns the expected ratio between data passed into this module
  274. * and the amount of data output when writing.
  275. * Returns: 100 - we have no idea how many pages will be pruned.
  276. */
  277. static int toi_prune_expected_ratio(void)
  278. {
  279. return 100;
  280. }
  281. /*
  282. * data for our sysfs entries.
  283. */
  284. static struct toi_sysfs_data sysfs_params[] = {
  285. SYSFS_INT("enabled", SYSFS_RW, &toi_prune_ops.enabled, 0, 1, 0,
  286. NULL),
  287. SYSFS_STRING("algorithm", SYSFS_RW, toi_prune_hash_algo_name, 31, 0, NULL),
  288. };
  289. /*
  290. * Ops structure.
  291. */
  292. static struct toi_module_ops toi_prune_ops = {
  293. .type = FILTER_MODULE,
  294. .name = "prune",
  295. .directory = "prune",
  296. .module = THIS_MODULE,
  297. .initialise = toi_prune_init,
  298. .memory_needed = toi_prune_memory_needed,
  299. .print_debug_info = toi_prune_print_debug_stats,
  300. .save_config_info = toi_prune_save_config_info,
  301. .load_config_info = toi_prune_load_config_info,
  302. .storage_needed = toi_prune_storage_needed,
  303. .expected_compression = toi_prune_expected_ratio,
  304. .pre_atomic_restore = toi_prune_pre_atomic_restore,
  305. .post_atomic_restore = toi_prune_post_atomic_restore,
  306. .rw_init = toi_prune_rw_init,
  307. .rw_cleanup = toi_prune_rw_cleanup,
  308. .write_page = toi_prune_write_page,
  309. .read_page = toi_prune_read_page,
  310. .sysfs_data = sysfs_params,
  311. .num_sysfs_entries = sizeof(sysfs_params) / sizeof(struct toi_sysfs_data),
  312. };
  313. /* ---- Registration ---- */
  314. static __init int toi_prune_load(void)
  315. {
  316. return toi_register_module(&toi_prune_ops);
  317. }
  318. #ifdef MODULE
  319. static __exit void toi_prune_unload(void)
  320. {
  321. toi_unregister_module(&toi_prune_ops);
  322. }
  323. module_init(toi_prune_load);
  324. module_exit(toi_prune_unload);
  325. MODULE_LICENSE("GPL");
  326. MODULE_AUTHOR("Nigel Cunningham");
  327. MODULE_DESCRIPTION("Image Pruning Support for TuxOnIce");
  328. #else
  329. late_initcall(toi_prune_load);
  330. #endif