tuxonice_file.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. /*
  2. * kernel/power/tuxonice_file.c
  3. *
  4. * Copyright (C) 2005-2014 Nigel Cunningham (nigel at tuxonice net)
  5. *
  6. * Distributed under GPLv2.
  7. *
  8. * This file encapsulates functions for usage of a simple file as a
  9. * backing store. It is based upon the swapallocator, and shares the
  10. * same basic working. Here, though, we have nothing to do with
  11. * swapspace, and only one device to worry about.
  12. *
  13. * The user can just
  14. *
  15. * echo TuxOnIce > /path/to/my_file
  16. *
  17. * dd if=/dev/zero bs=1M count=<file_size_desired> >> /path/to/my_file
  18. *
  19. * and
  20. *
  21. * echo /path/to/my_file > /sys/power/tuxonice/file/target
  22. *
  23. * then put what they find in /sys/power/tuxonice/resume
  24. * as their resume= parameter in lilo.conf (and rerun lilo if using it).
  25. *
  26. * Having done this, they're ready to hibernate and resume.
  27. *
  28. * TODO:
  29. * - File resizing.
  30. */
  31. #include <linux/blkdev.h>
  32. #include <linux/mount.h>
  33. #include <linux/fs.h>
  34. #include <linux/fs_uuid.h>
  35. #include "tuxonice.h"
  36. #include "tuxonice_modules.h"
  37. #include "tuxonice_bio.h"
  38. #include "tuxonice_alloc.h"
  39. #include "tuxonice_builtin.h"
  40. #include "tuxonice_sysfs.h"
  41. #include "tuxonice_ui.h"
  42. #include "tuxonice_io.h"
  43. #define target_is_normal_file() (S_ISREG(target_inode->i_mode))
  44. static struct toi_module_ops toi_fileops;
  45. static struct file *target_file;
  46. static struct block_device *toi_file_target_bdev;
  47. static unsigned long pages_available, pages_allocated;
  48. static char toi_file_target[256];
  49. static struct inode *target_inode;
  50. static int file_target_priority;
  51. static int used_devt;
  52. static int target_claim;
  53. static dev_t toi_file_dev_t;
  54. static int sig_page_index;
  55. /* For test_toi_file_target */
  56. static struct toi_bdev_info *file_chain;
  57. static int has_contiguous_blocks(struct toi_bdev_info *dev_info, int page_num)
  58. {
  59. int j;
  60. sector_t last = 0;
  61. for (j = 0; j < dev_info->blocks_per_page; j++) {
  62. sector_t this = bmap(target_inode,
  63. page_num * dev_info->blocks_per_page + j);
  64. if (!this || (last && (last + 1) != this))
  65. break;
  66. last = this;
  67. }
  68. return j == dev_info->blocks_per_page;
  69. }
  70. static unsigned long get_usable_pages(struct toi_bdev_info *dev_info)
  71. {
  72. unsigned long result = 0;
  73. struct block_device *bdev = dev_info->bdev;
  74. int i;
  75. switch (target_inode->i_mode & S_IFMT) {
  76. case S_IFSOCK:
  77. case S_IFCHR:
  78. case S_IFIFO: /* Socket, Char, Fifo */
  79. return -1;
  80. case S_IFREG: /* Regular file: current size - holes + free
  81. space on part */
  82. for (i = 0; i < (target_inode->i_size >> PAGE_SHIFT); i++) {
  83. if (has_contiguous_blocks(dev_info, i))
  84. result++;
  85. }
  86. break;
  87. case S_IFBLK: /* Block device */
  88. if (!bdev->bd_disk) {
  89. toi_message(TOI_IO, TOI_VERBOSE, 0, "bdev->bd_disk null.");
  90. return 0;
  91. }
  92. result = (bdev->bd_part ?
  93. bdev->bd_part->nr_sects :
  94. get_capacity(bdev->bd_disk)) >> (PAGE_SHIFT - 9);
  95. }
  96. return result;
  97. }
  98. static int toi_file_register_storage(void)
  99. {
  100. struct toi_bdev_info *devinfo;
  101. int result = 0;
  102. struct fs_info *fs_info;
  103. toi_message(TOI_IO, TOI_VERBOSE, 0, "toi_file_register_storage.");
  104. if (!strlen(toi_file_target)) {
  105. toi_message(TOI_IO, TOI_VERBOSE, 0, "Register file storage: No target filename set.");
  106. return 0;
  107. }
  108. target_file = filp_open(toi_file_target, O_RDONLY | O_LARGEFILE, 0);
  109. toi_message(TOI_IO, TOI_VERBOSE, 0, "filp_open %s returned %p.",
  110. toi_file_target, target_file);
  111. if (IS_ERR(target_file) || !target_file) {
  112. target_file = NULL;
  113. toi_file_dev_t = name_to_dev_t(toi_file_target);
  114. if (!toi_file_dev_t) {
  115. struct kstat stat;
  116. int error = vfs_stat(toi_file_target, &stat);
  117. pr_warn("Open file %s returned %p and name_to_devt failed.\n", toi_file_target, target_file);
  118. if (error) {
  119. pr_warn("Stating the file also failed. Nothing more we can do.");
  120. return 0;
  121. toi_file_dev_t = stat.rdev;
  122. }
  123. toi_file_target_bdev = toi_open_by_devnum(toi_file_dev_t);
  124. if (IS_ERR(toi_file_target_bdev)) {
  125. pr_warn("Got a dev_num (%lx) but failed to open it.\n", (unsigned long)toi_file_dev_t);
  126. toi_file_target_bdev = NULL;
  127. return 0;
  128. }
  129. used_devt = 1;
  130. target_inode = toi_file_target_bdev->bd_inode;
  131. } else
  132. target_inode = target_file->f_mapping->host;
  133. toi_message(TOI_IO, TOI_VERBOSE, 0, "Succeeded in opening the target.");
  134. if (S_ISLNK(target_inode->i_mode) || S_ISDIR(target_inode->i_mode) ||
  135. S_ISSOCK(target_inode->i_mode) || S_ISFIFO(target_inode->i_mode)) {
  136. pr_warn("File support works with regular files, character files and block devices.");
  137. /* Cleanup routine will undo the above */
  138. return 0;
  139. }
  140. if (!used_devt) {
  141. if (S_ISBLK(target_inode->i_mode)) {
  142. toi_file_target_bdev = I_BDEV(target_inode);
  143. if (!blkdev_get(toi_file_target_bdev, FMODE_WRITE | FMODE_READ, NULL))
  144. target_claim = 1;
  145. } else
  146. toi_file_target_bdev = target_inode->i_sb->s_bdev;
  147. if (!toi_file_target_bdev) {
  148. pr_warn("%s is not a valid file allocator target.", toi_file_target);
  149. return 0;
  150. }
  151. toi_file_dev_t = toi_file_target_bdev->bd_dev;
  152. }
  153. devinfo = toi_kzalloc(39, sizeof(struct toi_bdev_info), GFP_ATOMIC);
  154. if (!devinfo) {
  155. pr_warn("Failed to allocate a toi_bdev_info struct for the file allocator.\n");
  156. return -ENOMEM;
  157. }
  158. devinfo->bdev = toi_file_target_bdev;
  159. devinfo->allocator = &toi_fileops;
  160. devinfo->allocator_index = 0;
  161. fs_info = fs_info_from_block_dev(toi_file_target_bdev);
  162. if (fs_info && !IS_ERR(fs_info)) {
  163. memcpy(devinfo->uuid, &fs_info->uuid, 16);
  164. free_fs_info(fs_info);
  165. } else
  166. result = (int)PTR_ERR(fs_info);
  167. /* Unlike swap code, only complain if fs_info_from_block_dev returned
  168. * -ENOMEM. The 'file' might be a full partition, so might validly not
  169. * have an identifiable type, UUID etc.
  170. */
  171. if (result)
  172. pr_debug("Failed to get fs_info for file device (%d).\n", result);
  173. devinfo->dev_t = toi_file_dev_t;
  174. devinfo->prio = file_target_priority;
  175. devinfo->bmap_shift = target_inode->i_blkbits - 9;
  176. devinfo->blocks_per_page = (1 << (PAGE_SHIFT - target_inode->i_blkbits));
  177. sprintf(devinfo->name, "file %s", toi_file_target);
  178. file_chain = devinfo;
  179. toi_message(TOI_IO, TOI_VERBOSE, 0, "Dev_t is %lx. Prio is %d. Bmap shift is %d. Blocks per page %d.",
  180. devinfo->dev_t, devinfo->prio, devinfo->bmap_shift, devinfo->blocks_per_page);
  181. /* Keep one aside for the signature */
  182. pages_available = get_usable_pages(devinfo) - 1;
  183. toi_message(TOI_IO, TOI_VERBOSE, 0, "Registering file storage, %lu pages.", pages_available);
  184. toi_bio_ops.register_storage(devinfo);
  185. return 0;
  186. }
  187. static unsigned long toi_file_storage_available(void)
  188. {
  189. return pages_available;
  190. }
  191. static int toi_file_allocate_storage(struct toi_bdev_info *chain, unsigned long request)
  192. {
  193. unsigned long available = pages_available - pages_allocated;
  194. unsigned long to_add = min(available, request);
  195. toi_message(TOI_IO, TOI_VERBOSE, 0, "Pages available: %lu. Allocated: %lu. Allocating: %lu from file",
  196. pages_available, pages_allocated, to_add);
  197. pages_allocated += to_add;
  198. return to_add;
  199. }
  200. /**
  201. * __populate_block_list - add an extent to the chain
  202. * @min: Start of the extent (first physical block = sector)
  203. * @max: End of the extent (last physical block = sector)
  204. *
  205. * If TOI_TEST_BIO is set, print a debug message, outputting the min and max
  206. * fs block numbers.
  207. **/
  208. static int __populate_block_list(struct toi_bdev_info *chain, int min, int max)
  209. {
  210. if (test_action_state(TOI_TEST_BIO))
  211. toi_message(TOI_IO, TOI_VERBOSE, 0, "Adding extent %d-%d.",
  212. min << chain->bmap_shift, ((max + 1) << chain->bmap_shift) - 1);
  213. return toi_add_to_extent_chain(&chain->blocks, min, max);
  214. }
  215. static int get_main_pool_phys_params(struct toi_bdev_info *chain)
  216. {
  217. int i, extent_min = -1, extent_max = -1, result = 0, have_sig_page = 0;
  218. unsigned long pages_mapped = 0;
  219. toi_message(TOI_IO, TOI_VERBOSE, 0, "Getting file allocator blocks.");
  220. if (chain->blocks.first)
  221. toi_put_extent_chain(&chain->blocks);
  222. if (!target_is_normal_file()) {
  223. result = (pages_available > 0) ?
  224. __populate_block_list(chain, chain->blocks_per_page,
  225. (pages_allocated + 1) * chain->blocks_per_page - 1) : 0;
  226. return result;
  227. }
  228. /*
  229. * FIXME: We are assuming the first page is contiguous. Is that
  230. * assumption always right?
  231. */
  232. for (i = 0; i < (target_inode->i_size >> PAGE_SHIFT); i++) {
  233. sector_t new_sector;
  234. if (!has_contiguous_blocks(chain, i))
  235. continue;
  236. if (!have_sig_page) {
  237. have_sig_page = 1;
  238. sig_page_index = i;
  239. continue;
  240. }
  241. pages_mapped++;
  242. /* Ignore first page - it has the header */
  243. if (pages_mapped == 1)
  244. continue;
  245. new_sector = bmap(target_inode, (i * chain->blocks_per_page));
  246. /*
  247. * I'd love to be able to fill in holes and resize
  248. * files, but not yet...
  249. */
  250. if (new_sector == extent_max + 1)
  251. extent_max += chain->blocks_per_page;
  252. else {
  253. if (extent_min > -1) {
  254. result = __populate_block_list(chain, extent_min, extent_max);
  255. if (result)
  256. return result;
  257. }
  258. extent_min = new_sector;
  259. extent_max = extent_min + chain->blocks_per_page - 1;
  260. }
  261. if (pages_mapped == pages_allocated)
  262. break;
  263. }
  264. if (extent_min > -1) {
  265. result = __populate_block_list(chain, extent_min, extent_max);
  266. if (result)
  267. return result;
  268. }
  269. return 0;
  270. }
  271. static void toi_file_free_storage(struct toi_bdev_info *chain)
  272. {
  273. pages_allocated = 0;
  274. file_chain = NULL;
  275. }
  276. /**
  277. * toi_file_print_debug_stats - print debug info
  278. * @buffer: Buffer to data to populate
  279. * @size: Size of the buffer
  280. **/
  281. static int toi_file_print_debug_stats(char *buffer, int size)
  282. {
  283. int len = scnprintf(buffer, size, "- File Allocator active.\n");
  284. len += scnprintf(buffer + len, size - len, " Storage available for image: %lu pages.\n", pages_available);
  285. return len;
  286. }
  287. static void toi_file_cleanup(int finishing_cycle)
  288. {
  289. if (toi_file_target_bdev) {
  290. if (target_claim) {
  291. blkdev_put(toi_file_target_bdev, FMODE_WRITE | FMODE_READ);
  292. target_claim = 0;
  293. }
  294. if (used_devt) {
  295. blkdev_put(toi_file_target_bdev, FMODE_READ | FMODE_NDELAY);
  296. used_devt = 0;
  297. }
  298. toi_file_target_bdev = NULL;
  299. target_inode = NULL;
  300. }
  301. if (target_file) {
  302. filp_close(target_file, NULL);
  303. target_file = NULL;
  304. }
  305. pages_available = 0;
  306. }
  307. /**
  308. * test_toi_file_target - sysfs callback for /sys/power/tuxonince/file/target
  309. *
  310. * Test whether the target file is valid for hibernating.
  311. **/
  312. static void test_toi_file_target(void)
  313. {
  314. int result = toi_file_register_storage();
  315. sector_t sector;
  316. char buf[50];
  317. struct fs_info *fs_info;
  318. if (result || !file_chain)
  319. return;
  320. /* This doesn't mean we're in business. Is any storage available? */
  321. if (!pages_available)
  322. goto out;
  323. toi_file_allocate_storage(file_chain, 1);
  324. result = get_main_pool_phys_params(file_chain);
  325. if (result)
  326. goto out;
  327. sector = bmap(target_inode, sig_page_index *
  328. file_chain->blocks_per_page) << file_chain->bmap_shift;
  329. /* Use the uuid, or the dev_t if that fails */
  330. fs_info = fs_info_from_block_dev(toi_file_target_bdev);
  331. if (!fs_info || IS_ERR(fs_info)) {
  332. bdevname(toi_file_target_bdev, buf);
  333. sprintf(resume_file, "/dev/%s:%llu", buf, (unsigned long long)sector);
  334. } else {
  335. int i;
  336. hex_dump_to_buffer(fs_info->uuid, 16, 32, 1, buf, 50, 0);
  337. /* Remove the spaces */
  338. for (i = 1; i < 16; i++) {
  339. buf[2 * i] = buf[3 * i];
  340. buf[2 * i + 1] = buf[3 * i + 1];
  341. }
  342. buf[32] = 0;
  343. sprintf(resume_file, "UUID=%s:0x%llx", buf, (unsigned long long)sector);
  344. free_fs_info(fs_info);
  345. }
  346. toi_attempt_to_parse_resume_device(0);
  347. out:
  348. toi_file_free_storage(file_chain);
  349. toi_bio_ops.free_storage();
  350. }
  351. static struct toi_sysfs_data sysfs_params[] = {
  352. SYSFS_STRING("target", SYSFS_RW, toi_file_target, 256,
  353. SYSFS_NEEDS_SM_FOR_WRITE, test_toi_file_target),
  354. SYSFS_INT("enabled", SYSFS_RW, &toi_fileops.enabled, 0, 1, 0, NULL),
  355. SYSFS_INT("priority", SYSFS_RW, &file_target_priority, -4095,
  356. 4096, 0, NULL),
  357. };
  358. static struct toi_bio_allocator_ops toi_bio_fileops = {
  359. .register_storage = toi_file_register_storage,
  360. .storage_available = toi_file_storage_available,
  361. .allocate_storage = toi_file_allocate_storage,
  362. .bmap = get_main_pool_phys_params,
  363. .free_storage = toi_file_free_storage,
  364. };
  365. static struct toi_module_ops toi_fileops = {
  366. .type = BIO_ALLOCATOR_MODULE,
  367. .name = "file storage",
  368. .directory = "file",
  369. .module = THIS_MODULE,
  370. .print_debug_info = toi_file_print_debug_stats,
  371. .cleanup = toi_file_cleanup,
  372. .bio_allocator_ops = &toi_bio_fileops,
  373. .sysfs_data = sysfs_params,
  374. .num_sysfs_entries = sizeof(sysfs_params) / sizeof(struct toi_sysfs_data),
  375. };
  376. /* ---- Registration ---- */
  377. static __init int toi_file_load(void)
  378. {
  379. return toi_register_module(&toi_fileops);
  380. }
  381. #ifdef MODULE
  382. static __exit void toi_file_unload(void)
  383. {
  384. toi_unregister_module(&toi_fileops);
  385. }
  386. module_init(toi_file_load);
  387. module_exit(toi_file_unload);
  388. MODULE_LICENSE("GPL");
  389. MODULE_AUTHOR("Nigel Cunningham");
  390. MODULE_DESCRIPTION("TuxOnIce FileAllocator");
  391. #else
  392. late_initcall(toi_file_load);
  393. #endif