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