tuxonice_checksum.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. /*
  2. * kernel/power/tuxonice_checksum.c
  3. *
  4. * Copyright (C) 2006-2014 Nigel Cunningham (nigel at tuxonice net)
  5. *
  6. * This file is released under the GPLv2.
  7. *
  8. * This file contains data checksum routines for TuxOnIce,
  9. * using cryptoapi. They are used to locate any modifications
  10. * made to pageset 2 while we're saving it.
  11. */
  12. #include <linux/suspend.h>
  13. #include <linux/highmem.h>
  14. #include <linux/vmalloc.h>
  15. #include <linux/crypto.h>
  16. #include <linux/scatterlist.h>
  17. #include "tuxonice.h"
  18. #include "tuxonice_modules.h"
  19. #include "tuxonice_sysfs.h"
  20. #include "tuxonice_io.h"
  21. #include "tuxonice_pageflags.h"
  22. #include "tuxonice_checksum.h"
  23. #include "tuxonice_pagedir.h"
  24. #include "tuxonice_alloc.h"
  25. #include "tuxonice_ui.h"
  26. static struct toi_module_ops toi_checksum_ops;
  27. /* Constant at the mo, but I might allow tuning later */
  28. static char toi_checksum_name[32] = "md4";
  29. /* Bytes per checksum */
  30. #define CHECKSUM_SIZE (16)
  31. #define CHECKSUMS_PER_PAGE ((PAGE_SIZE - sizeof(void *)) / CHECKSUM_SIZE)
  32. struct toi_cpu_context {
  33. struct crypto_hash *transform;
  34. struct hash_desc desc;
  35. struct scatterlist sg[2];
  36. char *buf;
  37. };
  38. static DEFINE_PER_CPU(struct toi_cpu_context, contexts);
  39. static int pages_allocated;
  40. static unsigned long page_list;
  41. static int toi_num_resaved;
  42. static unsigned long this_checksum, next_page;
  43. static int checksum_count;
  44. static inline int checksum_pages_needed(void)
  45. {
  46. return DIV_ROUND_UP(pagedir2.size, CHECKSUMS_PER_PAGE);
  47. }
  48. /* ---- Local buffer management ---- */
  49. /*
  50. * toi_checksum_cleanup
  51. *
  52. * Frees memory allocated for our labours.
  53. */
  54. static void toi_checksum_cleanup(int ending_cycle)
  55. {
  56. int cpu;
  57. if (ending_cycle) {
  58. for_each_online_cpu(cpu) {
  59. struct toi_cpu_context *this = &per_cpu(contexts, cpu);
  60. if (this->transform) {
  61. crypto_free_hash(this->transform);
  62. this->transform = NULL;
  63. this->desc.tfm = NULL;
  64. }
  65. if (this->buf) {
  66. toi_free_page(27, (unsigned long)this->buf);
  67. this->buf = NULL;
  68. }
  69. }
  70. }
  71. }
  72. /*
  73. * toi_crypto_initialise
  74. *
  75. * Prepare to do some work by allocating buffers and transforms.
  76. * Returns: Int: Zero. Even if we can't set up checksum, we still
  77. * seek to hibernate.
  78. */
  79. static int toi_checksum_initialise(int starting_cycle)
  80. {
  81. int cpu;
  82. if (!(starting_cycle & SYSFS_HIBERNATE) || !toi_checksum_ops.enabled)
  83. return 0;
  84. if (!*toi_checksum_name) {
  85. pr_warn("TuxOnIce: No checksum algorithm name set.\n");
  86. return 1;
  87. }
  88. for_each_online_cpu(cpu) {
  89. struct toi_cpu_context *this = &per_cpu(contexts, cpu);
  90. struct page *page;
  91. this->transform = crypto_alloc_hash(toi_checksum_name, 0, 0);
  92. if (IS_ERR(this->transform)) {
  93. pr_warn("TuxOnIce: Failed to initialise the %s checksum algorithm: %ld.\n",
  94. toi_checksum_name, (long)this->transform);
  95. this->transform = NULL;
  96. return 1;
  97. }
  98. this->desc.tfm = this->transform;
  99. this->desc.flags = 0;
  100. page = toi_alloc_page(27, GFP_KERNEL);
  101. if (!page)
  102. return 1;
  103. this->buf = page_address(page);
  104. sg_init_one(&this->sg[0], this->buf, PAGE_SIZE);
  105. }
  106. return 0;
  107. }
  108. /*
  109. * toi_checksum_print_debug_stats
  110. * @buffer: Pointer to a buffer into which the debug info will be printed.
  111. * @size: Size of the buffer.
  112. *
  113. * Print information to be recorded for debugging purposes into a buffer.
  114. * Returns: Number of characters written to the buffer.
  115. */
  116. static int toi_checksum_print_debug_stats(char *buffer, int size)
  117. {
  118. int len;
  119. if (!toi_checksum_ops.enabled)
  120. return scnprintf(buffer, size, "- Checksumming disabled.\n");
  121. len = scnprintf(buffer, size, "- Checksum method is '%s'.\n", toi_checksum_name);
  122. len += scnprintf(buffer + len, size - len,
  123. " %d pages resaved in atomic copy.\n", toi_num_resaved);
  124. return len;
  125. }
  126. static int toi_checksum_memory_needed(void)
  127. {
  128. return toi_checksum_ops.enabled ? checksum_pages_needed() << PAGE_SHIFT : 0;
  129. }
  130. static int toi_checksum_storage_needed(void)
  131. {
  132. if (toi_checksum_ops.enabled)
  133. return strlen(toi_checksum_name) + sizeof(int) + 1;
  134. else
  135. return 0;
  136. }
  137. /*
  138. * toi_checksum_save_config_info
  139. * @buffer: Pointer to a buffer of size PAGE_SIZE.
  140. *
  141. * Save informaton needed when reloading the image at resume time.
  142. * Returns: Number of bytes used for saving our data.
  143. */
  144. static int toi_checksum_save_config_info(char *buffer)
  145. {
  146. int namelen = strlen(toi_checksum_name) + 1;
  147. int total_len;
  148. *((unsigned int *)buffer) = namelen;
  149. strncpy(buffer + sizeof(unsigned int), toi_checksum_name, namelen);
  150. total_len = sizeof(unsigned int) + namelen;
  151. return total_len;
  152. }
  153. /* toi_checksum_load_config_info
  154. * @buffer: Pointer to the start of the data.
  155. * @size: Number of bytes that were saved.
  156. *
  157. * Description: Reload information needed for dechecksuming the image at
  158. * resume time.
  159. */
  160. static void toi_checksum_load_config_info(char *buffer, int size)
  161. {
  162. int namelen;
  163. namelen = *((unsigned int *)(buffer));
  164. strncpy(toi_checksum_name, buffer + sizeof(unsigned int), namelen);
  165. }
  166. /*
  167. * Free Checksum Memory
  168. */
  169. void free_checksum_pages(void)
  170. {
  171. while (pages_allocated) {
  172. unsigned long next = *((unsigned long *)page_list);
  173. ClearPageNosave(virt_to_page(page_list));
  174. toi_free_page(15, (unsigned long)page_list);
  175. page_list = next;
  176. pages_allocated--;
  177. }
  178. }
  179. /*
  180. * Allocate Checksum Memory
  181. */
  182. int allocate_checksum_pages(void)
  183. {
  184. int pages_needed = checksum_pages_needed();
  185. if (!toi_checksum_ops.enabled)
  186. return 0;
  187. while (pages_allocated < pages_needed) {
  188. unsigned long *new_page = (unsigned long *)toi_get_zeroed_page(15, TOI_ATOMIC_GFP);
  189. if (!new_page) {
  190. pr_err("Unable to allocate checksum pages.\n");
  191. return -ENOMEM;
  192. }
  193. SetPageNosave(virt_to_page(new_page));
  194. (*new_page) = page_list;
  195. page_list = (unsigned long)new_page;
  196. pages_allocated++;
  197. }
  198. next_page = (unsigned long)page_list;
  199. checksum_count = 0;
  200. return 0;
  201. }
  202. char *tuxonice_get_next_checksum(void)
  203. {
  204. if (!toi_checksum_ops.enabled)
  205. return NULL;
  206. if (checksum_count % CHECKSUMS_PER_PAGE)
  207. this_checksum += CHECKSUM_SIZE;
  208. else {
  209. this_checksum = next_page + sizeof(void *);
  210. next_page = *((unsigned long *)next_page);
  211. }
  212. checksum_count++;
  213. return (char *)this_checksum;
  214. }
  215. int tuxonice_calc_checksum(struct page *page, char *checksum_locn)
  216. {
  217. char *pa;
  218. int result, cpu = smp_processor_id();
  219. struct toi_cpu_context *ctx = &per_cpu(contexts, cpu);
  220. if (!toi_checksum_ops.enabled)
  221. return 0;
  222. pa = kmap(page);
  223. memcpy(ctx->buf, pa, PAGE_SIZE);
  224. kunmap(page);
  225. result = crypto_hash_digest(&ctx->desc, ctx->sg, PAGE_SIZE, checksum_locn);
  226. if (result)
  227. pr_err("TuxOnIce checksumming: crypto_hash_digest returned %d.\n", result);
  228. return result;
  229. }
  230. /*
  231. * Calculate checksums
  232. */
  233. void check_checksums(void)
  234. {
  235. int index = 0, cpu = smp_processor_id();
  236. char current_checksum[CHECKSUM_SIZE];
  237. struct toi_cpu_context *ctx = &per_cpu(contexts, cpu);
  238. unsigned long pfn;
  239. if (!toi_checksum_ops.enabled) {
  240. toi_message(TOI_IO, TOI_VERBOSE, 0, "Checksumming disabled.");
  241. return;
  242. }
  243. next_page = (unsigned long)page_list;
  244. toi_num_resaved = 0;
  245. this_checksum = 0;
  246. toi_trace_index++;
  247. toi_message(TOI_IO, TOI_VERBOSE, 0, "Verifying checksums.");
  248. memory_bm_position_reset(pageset2_map);
  249. for (pfn = memory_bm_next_pfn(pageset2_map, 0); pfn != BM_END_OF_MAP;
  250. pfn = memory_bm_next_pfn(pageset2_map, 0)) {
  251. int ret, resave_needed = false;
  252. char *pa;
  253. struct page *page = pfn_to_page(pfn);
  254. if (index < checksum_count) {
  255. if (index % CHECKSUMS_PER_PAGE) {
  256. this_checksum += CHECKSUM_SIZE;
  257. } else {
  258. this_checksum = next_page + sizeof(void *);
  259. next_page = *((unsigned long *)next_page);
  260. }
  261. /* Done when IRQs disabled so must be atomic */
  262. pa = kmap_atomic(page);
  263. memcpy(ctx->buf, pa, PAGE_SIZE);
  264. kunmap_atomic(pa);
  265. ret = crypto_hash_digest(&ctx->desc, ctx->sg, PAGE_SIZE, current_checksum);
  266. if (ret) {
  267. pr_warn("Digest failed. Returned %d.\n", ret);
  268. return;
  269. }
  270. resave_needed = memcmp(current_checksum, (char *)this_checksum,
  271. CHECKSUM_SIZE);
  272. } else {
  273. resave_needed = true;
  274. }
  275. if (resave_needed) {
  276. TOI_TRACE_DEBUG(pfn, "_Resaving %d", resave_needed);
  277. SetPageResave(pfn_to_page(pfn));
  278. toi_num_resaved++;
  279. if (test_action_state(TOI_ABORT_ON_RESAVE_NEEDED))
  280. set_abort_result(TOI_RESAVE_NEEDED);
  281. }
  282. index++;
  283. }
  284. toi_message(TOI_IO, TOI_VERBOSE, 0, "Checksum verification complete.");
  285. }
  286. static struct toi_sysfs_data sysfs_params[] = {
  287. SYSFS_INT("enabled", SYSFS_RW, &toi_checksum_ops.enabled, 0, 1, 0,
  288. NULL),
  289. SYSFS_BIT("abort_if_resave_needed", SYSFS_RW, &toi_bkd.toi_action,
  290. TOI_ABORT_ON_RESAVE_NEEDED, 0)
  291. };
  292. /*
  293. * Ops structure.
  294. */
  295. static struct toi_module_ops toi_checksum_ops = {
  296. .type = MISC_MODULE,
  297. .name = "checksumming",
  298. .directory = "checksum",
  299. .module = THIS_MODULE,
  300. .initialise = toi_checksum_initialise,
  301. .cleanup = toi_checksum_cleanup,
  302. .print_debug_info = toi_checksum_print_debug_stats,
  303. .save_config_info = toi_checksum_save_config_info,
  304. .load_config_info = toi_checksum_load_config_info,
  305. .memory_needed = toi_checksum_memory_needed,
  306. .storage_needed = toi_checksum_storage_needed,
  307. .sysfs_data = sysfs_params,
  308. .num_sysfs_entries = sizeof(sysfs_params) / sizeof(struct toi_sysfs_data),
  309. };
  310. /* ---- Registration ---- */
  311. int toi_checksum_init(void)
  312. {
  313. int result = toi_register_module(&toi_checksum_ops);
  314. return result;
  315. }
  316. void toi_checksum_exit(void)
  317. {
  318. toi_unregister_module(&toi_checksum_ops);
  319. }