swsusp.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /*
  2. * linux/kernel/power/swsusp.c
  3. *
  4. * This file provides code to write suspend image to swap and read it back.
  5. *
  6. * Copyright (C) 1998-2001 Gabor Kuti <seasons@fornax.hu>
  7. * Copyright (C) 1998,2001-2005 Pavel Machek <pavel@suse.cz>
  8. *
  9. * This file is released under the GPLv2.
  10. *
  11. * I'd like to thank the following people for their work:
  12. *
  13. * Pavel Machek <pavel@ucw.cz>:
  14. * Modifications, defectiveness pointing, being with me at the very beginning,
  15. * suspend to swap space, stop all tasks. Port to 2.4.18-ac and 2.5.17.
  16. *
  17. * Steve Doddi <dirk@loth.demon.co.uk>:
  18. * Support the possibility of hardware state restoring.
  19. *
  20. * Raph <grey.havens@earthling.net>:
  21. * Support for preserving states of network devices and virtual console
  22. * (including X and svgatextmode)
  23. *
  24. * Kurt Garloff <garloff@suse.de>:
  25. * Straightened the critical function in order to prevent compilers from
  26. * playing tricks with local variables.
  27. *
  28. * Andreas Mohr <a.mohr@mailto.de>
  29. *
  30. * Alex Badea <vampire@go.ro>:
  31. * Fixed runaway init
  32. *
  33. * Rafael J. Wysocki <rjw@sisk.pl>
  34. * Reworked the freeing of memory and the handling of swap
  35. *
  36. * More state savers are welcome. Especially for the scsi layer...
  37. *
  38. * For TODOs,FIXMEs also look in Documentation/power/swsusp.txt
  39. */
  40. #include <linux/mm.h>
  41. #include <linux/suspend.h>
  42. #include <linux/spinlock.h>
  43. #include <linux/kernel.h>
  44. #include <linux/major.h>
  45. #include <linux/swap.h>
  46. #include <linux/pm.h>
  47. #include <linux/swapops.h>
  48. #include <linux/bootmem.h>
  49. #include <linux/syscalls.h>
  50. #include <linux/highmem.h>
  51. #include <linux/time.h>
  52. #include <linux/rbtree.h>
  53. #include <linux/io.h>
  54. #include "power.h"
  55. int in_suspend __nosavedata = 0;
  56. /**
  57. * The following functions are used for tracing the allocated
  58. * swap pages, so that they can be freed in case of an error.
  59. */
  60. struct swsusp_extent {
  61. struct rb_node node;
  62. unsigned long start;
  63. unsigned long end;
  64. };
  65. static struct rb_root swsusp_extents = RB_ROOT;
  66. static int swsusp_extents_insert(unsigned long swap_offset)
  67. {
  68. struct rb_node **new = &(swsusp_extents.rb_node);
  69. struct rb_node *parent = NULL;
  70. struct swsusp_extent *ext;
  71. /* Figure out where to put the new node */
  72. while (*new) {
  73. ext = container_of(*new, struct swsusp_extent, node);
  74. parent = *new;
  75. if (swap_offset < ext->start) {
  76. /* Try to merge */
  77. if (swap_offset == ext->start - 1) {
  78. ext->start--;
  79. return 0;
  80. }
  81. new = &((*new)->rb_left);
  82. } else if (swap_offset > ext->end) {
  83. /* Try to merge */
  84. if (swap_offset == ext->end + 1) {
  85. ext->end++;
  86. return 0;
  87. }
  88. new = &((*new)->rb_right);
  89. } else {
  90. /* It already is in the tree */
  91. return -EINVAL;
  92. }
  93. }
  94. /* Add the new node and rebalance the tree. */
  95. ext = kzalloc(sizeof(struct swsusp_extent), GFP_KERNEL);
  96. if (!ext)
  97. return -ENOMEM;
  98. ext->start = swap_offset;
  99. ext->end = swap_offset;
  100. rb_link_node(&ext->node, parent, new);
  101. rb_insert_color(&ext->node, &swsusp_extents);
  102. return 0;
  103. }
  104. /**
  105. * alloc_swapdev_block - allocate a swap page and register that it has
  106. * been allocated, so that it can be freed in case of an error.
  107. */
  108. sector_t alloc_swapdev_block(int swap)
  109. {
  110. unsigned long offset;
  111. offset = swp_offset(get_swap_page_of_type(swap));
  112. if (offset) {
  113. if (swsusp_extents_insert(offset))
  114. swap_free(swp_entry(swap, offset));
  115. else
  116. return swapdev_block(swap, offset);
  117. }
  118. return 0;
  119. }
  120. /**
  121. * free_all_swap_pages - free swap pages allocated for saving image data.
  122. * It also frees the extents used to register which swap entres had been
  123. * allocated.
  124. */
  125. void free_all_swap_pages(int swap)
  126. {
  127. struct rb_node *node;
  128. while ((node = swsusp_extents.rb_node)) {
  129. struct swsusp_extent *ext;
  130. unsigned long offset;
  131. ext = container_of(node, struct swsusp_extent, node);
  132. rb_erase(node, &swsusp_extents);
  133. for (offset = ext->start; offset <= ext->end; offset++)
  134. swap_free(swp_entry(swap, offset));
  135. kfree(ext);
  136. }
  137. }
  138. int swsusp_swap_in_use(void)
  139. {
  140. return (swsusp_extents.rb_node != NULL);
  141. }
  142. /**
  143. * swsusp_show_speed - print the time elapsed between two events represented by
  144. * @start and @stop
  145. *
  146. * @nr_pages - number of pages processed between @start and @stop
  147. * @msg - introductory message to print
  148. */
  149. void swsusp_show_speed(struct timeval *start, struct timeval *stop,
  150. unsigned nr_pages, char *msg)
  151. {
  152. s64 elapsed_centisecs64;
  153. int centisecs;
  154. int k;
  155. int kps;
  156. elapsed_centisecs64 = timeval_to_ns(stop) - timeval_to_ns(start);
  157. do_div(elapsed_centisecs64, NSEC_PER_SEC / 100);
  158. centisecs = elapsed_centisecs64;
  159. if (centisecs == 0)
  160. centisecs = 1; /* avoid div-by-zero */
  161. k = nr_pages * (PAGE_SIZE / 1024);
  162. kps = (k * 100) / centisecs;
  163. printk(KERN_INFO "PM: %s %d kbytes in %d.%02d seconds (%d.%02d MB/s)\n",
  164. msg, k,
  165. centisecs / 100, centisecs % 100,
  166. kps / 1000, (kps % 1000) / 10);
  167. }