diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-03-23 23:44:19 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-03-23 23:44:19 -0500 |
commit | 1ebbe2b20091d306453a5cf480a87e6cd28ae76f (patch) | |
tree | f5cd7a0fa69b8b1938cb5a0faed2e7b0628072a5 /kernel/power/snapshot.c | |
parent | ac58c9059da8886b5e8cde012a80266b18ca146e (diff) | |
parent | 674a396c6d2ba0341ebdd7c1c9950f32f018e2dd (diff) |
Merge branch 'linus'
Diffstat (limited to 'kernel/power/snapshot.c')
-rw-r--r-- | kernel/power/snapshot.c | 335 |
1 files changed, 327 insertions, 8 deletions
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 8d5a5986d621..c5863d02c89e 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c | |||
@@ -10,6 +10,7 @@ | |||
10 | */ | 10 | */ |
11 | 11 | ||
12 | 12 | ||
13 | #include <linux/version.h> | ||
13 | #include <linux/module.h> | 14 | #include <linux/module.h> |
14 | #include <linux/mm.h> | 15 | #include <linux/mm.h> |
15 | #include <linux/suspend.h> | 16 | #include <linux/suspend.h> |
@@ -34,7 +35,9 @@ | |||
34 | #include "power.h" | 35 | #include "power.h" |
35 | 36 | ||
36 | struct pbe *pagedir_nosave; | 37 | struct pbe *pagedir_nosave; |
37 | unsigned int nr_copy_pages; | 38 | static unsigned int nr_copy_pages; |
39 | static unsigned int nr_meta_pages; | ||
40 | static unsigned long *buffer; | ||
38 | 41 | ||
39 | #ifdef CONFIG_HIGHMEM | 42 | #ifdef CONFIG_HIGHMEM |
40 | unsigned int count_highmem_pages(void) | 43 | unsigned int count_highmem_pages(void) |
@@ -80,7 +83,7 @@ static int save_highmem_zone(struct zone *zone) | |||
80 | void *kaddr; | 83 | void *kaddr; |
81 | unsigned long pfn = zone_pfn + zone->zone_start_pfn; | 84 | unsigned long pfn = zone_pfn + zone->zone_start_pfn; |
82 | 85 | ||
83 | if (!(pfn%1000)) | 86 | if (!(pfn%10000)) |
84 | printk("."); | 87 | printk("."); |
85 | if (!pfn_valid(pfn)) | 88 | if (!pfn_valid(pfn)) |
86 | continue; | 89 | continue; |
@@ -119,13 +122,15 @@ int save_highmem(void) | |||
119 | struct zone *zone; | 122 | struct zone *zone; |
120 | int res = 0; | 123 | int res = 0; |
121 | 124 | ||
122 | pr_debug("swsusp: Saving Highmem\n"); | 125 | pr_debug("swsusp: Saving Highmem"); |
126 | drain_local_pages(); | ||
123 | for_each_zone (zone) { | 127 | for_each_zone (zone) { |
124 | if (is_highmem(zone)) | 128 | if (is_highmem(zone)) |
125 | res = save_highmem_zone(zone); | 129 | res = save_highmem_zone(zone); |
126 | if (res) | 130 | if (res) |
127 | return res; | 131 | return res; |
128 | } | 132 | } |
133 | printk("\n"); | ||
129 | return 0; | 134 | return 0; |
130 | } | 135 | } |
131 | 136 | ||
@@ -235,7 +240,7 @@ static void copy_data_pages(struct pbe *pblist) | |||
235 | * free_pagedir - free pages allocated with alloc_pagedir() | 240 | * free_pagedir - free pages allocated with alloc_pagedir() |
236 | */ | 241 | */ |
237 | 242 | ||
238 | void free_pagedir(struct pbe *pblist) | 243 | static void free_pagedir(struct pbe *pblist) |
239 | { | 244 | { |
240 | struct pbe *pbe; | 245 | struct pbe *pbe; |
241 | 246 | ||
@@ -301,7 +306,7 @@ struct eaten_page { | |||
301 | 306 | ||
302 | static struct eaten_page *eaten_pages = NULL; | 307 | static struct eaten_page *eaten_pages = NULL; |
303 | 308 | ||
304 | void release_eaten_pages(void) | 309 | static void release_eaten_pages(void) |
305 | { | 310 | { |
306 | struct eaten_page *p, *q; | 311 | struct eaten_page *p, *q; |
307 | 312 | ||
@@ -376,7 +381,6 @@ struct pbe *alloc_pagedir(unsigned int nr_pages, gfp_t gfp_mask, int safe_needed | |||
376 | if (!nr_pages) | 381 | if (!nr_pages) |
377 | return NULL; | 382 | return NULL; |
378 | 383 | ||
379 | pr_debug("alloc_pagedir(): nr_pages = %d\n", nr_pages); | ||
380 | pblist = alloc_image_page(gfp_mask, safe_needed); | 384 | pblist = alloc_image_page(gfp_mask, safe_needed); |
381 | /* FIXME: rewrite this ugly loop */ | 385 | /* FIXME: rewrite this ugly loop */ |
382 | for (pbe = pblist, num = PBES_PER_PAGE; pbe && num < nr_pages; | 386 | for (pbe = pblist, num = PBES_PER_PAGE; pbe && num < nr_pages; |
@@ -388,7 +392,7 @@ struct pbe *alloc_pagedir(unsigned int nr_pages, gfp_t gfp_mask, int safe_needed | |||
388 | free_pagedir(pblist); | 392 | free_pagedir(pblist); |
389 | pblist = NULL; | 393 | pblist = NULL; |
390 | } else | 394 | } else |
391 | create_pbe_list(pblist, nr_pages); | 395 | create_pbe_list(pblist, nr_pages); |
392 | return pblist; | 396 | return pblist; |
393 | } | 397 | } |
394 | 398 | ||
@@ -414,6 +418,10 @@ void swsusp_free(void) | |||
414 | } | 418 | } |
415 | } | 419 | } |
416 | } | 420 | } |
421 | nr_copy_pages = 0; | ||
422 | nr_meta_pages = 0; | ||
423 | pagedir_nosave = NULL; | ||
424 | buffer = NULL; | ||
417 | } | 425 | } |
418 | 426 | ||
419 | 427 | ||
@@ -437,7 +445,7 @@ static int enough_free_mem(unsigned int nr_pages) | |||
437 | (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE); | 445 | (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE); |
438 | } | 446 | } |
439 | 447 | ||
440 | int alloc_data_pages(struct pbe *pblist, gfp_t gfp_mask, int safe_needed) | 448 | static int alloc_data_pages(struct pbe *pblist, gfp_t gfp_mask, int safe_needed) |
441 | { | 449 | { |
442 | struct pbe *p; | 450 | struct pbe *p; |
443 | 451 | ||
@@ -504,7 +512,318 @@ asmlinkage int swsusp_save(void) | |||
504 | */ | 512 | */ |
505 | 513 | ||
506 | nr_copy_pages = nr_pages; | 514 | nr_copy_pages = nr_pages; |
515 | nr_meta_pages = (nr_pages * sizeof(long) + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
507 | 516 | ||
508 | printk("swsusp: critical section/: done (%d pages copied)\n", nr_pages); | 517 | printk("swsusp: critical section/: done (%d pages copied)\n", nr_pages); |
509 | return 0; | 518 | return 0; |
510 | } | 519 | } |
520 | |||
521 | static void init_header(struct swsusp_info *info) | ||
522 | { | ||
523 | memset(info, 0, sizeof(struct swsusp_info)); | ||
524 | info->version_code = LINUX_VERSION_CODE; | ||
525 | info->num_physpages = num_physpages; | ||
526 | memcpy(&info->uts, &system_utsname, sizeof(system_utsname)); | ||
527 | info->cpus = num_online_cpus(); | ||
528 | info->image_pages = nr_copy_pages; | ||
529 | info->pages = nr_copy_pages + nr_meta_pages + 1; | ||
530 | info->size = info->pages; | ||
531 | info->size <<= PAGE_SHIFT; | ||
532 | } | ||
533 | |||
534 | /** | ||
535 | * pack_orig_addresses - the .orig_address fields of the PBEs from the | ||
536 | * list starting at @pbe are stored in the array @buf[] (1 page) | ||
537 | */ | ||
538 | |||
539 | static inline struct pbe *pack_orig_addresses(unsigned long *buf, struct pbe *pbe) | ||
540 | { | ||
541 | int j; | ||
542 | |||
543 | for (j = 0; j < PAGE_SIZE / sizeof(long) && pbe; j++) { | ||
544 | buf[j] = pbe->orig_address; | ||
545 | pbe = pbe->next; | ||
546 | } | ||
547 | if (!pbe) | ||
548 | for (; j < PAGE_SIZE / sizeof(long); j++) | ||
549 | buf[j] = 0; | ||
550 | return pbe; | ||
551 | } | ||
552 | |||
553 | /** | ||
554 | * snapshot_read_next - used for reading the system memory snapshot. | ||
555 | * | ||
556 | * On the first call to it @handle should point to a zeroed | ||
557 | * snapshot_handle structure. The structure gets updated and a pointer | ||
558 | * to it should be passed to this function every next time. | ||
559 | * | ||
560 | * The @count parameter should contain the number of bytes the caller | ||
561 | * wants to read from the snapshot. It must not be zero. | ||
562 | * | ||
563 | * On success the function returns a positive number. Then, the caller | ||
564 | * is allowed to read up to the returned number of bytes from the memory | ||
565 | * location computed by the data_of() macro. The number returned | ||
566 | * may be smaller than @count, but this only happens if the read would | ||
567 | * cross a page boundary otherwise. | ||
568 | * | ||
569 | * The function returns 0 to indicate the end of data stream condition, | ||
570 | * and a negative number is returned on error. In such cases the | ||
571 | * structure pointed to by @handle is not updated and should not be used | ||
572 | * any more. | ||
573 | */ | ||
574 | |||
575 | int snapshot_read_next(struct snapshot_handle *handle, size_t count) | ||
576 | { | ||
577 | if (handle->page > nr_meta_pages + nr_copy_pages) | ||
578 | return 0; | ||
579 | if (!buffer) { | ||
580 | /* This makes the buffer be freed by swsusp_free() */ | ||
581 | buffer = alloc_image_page(GFP_ATOMIC, 0); | ||
582 | if (!buffer) | ||
583 | return -ENOMEM; | ||
584 | } | ||
585 | if (!handle->offset) { | ||
586 | init_header((struct swsusp_info *)buffer); | ||
587 | handle->buffer = buffer; | ||
588 | handle->pbe = pagedir_nosave; | ||
589 | } | ||
590 | if (handle->prev < handle->page) { | ||
591 | if (handle->page <= nr_meta_pages) { | ||
592 | handle->pbe = pack_orig_addresses(buffer, handle->pbe); | ||
593 | if (!handle->pbe) | ||
594 | handle->pbe = pagedir_nosave; | ||
595 | } else { | ||
596 | handle->buffer = (void *)handle->pbe->address; | ||
597 | handle->pbe = handle->pbe->next; | ||
598 | } | ||
599 | handle->prev = handle->page; | ||
600 | } | ||
601 | handle->buf_offset = handle->page_offset; | ||
602 | if (handle->page_offset + count >= PAGE_SIZE) { | ||
603 | count = PAGE_SIZE - handle->page_offset; | ||
604 | handle->page_offset = 0; | ||
605 | handle->page++; | ||
606 | } else { | ||
607 | handle->page_offset += count; | ||
608 | } | ||
609 | handle->offset += count; | ||
610 | return count; | ||
611 | } | ||
612 | |||
613 | /** | ||
614 | * mark_unsafe_pages - mark the pages that cannot be used for storing | ||
615 | * the image during resume, because they conflict with the pages that | ||
616 | * had been used before suspend | ||
617 | */ | ||
618 | |||
619 | static int mark_unsafe_pages(struct pbe *pblist) | ||
620 | { | ||
621 | struct zone *zone; | ||
622 | unsigned long zone_pfn; | ||
623 | struct pbe *p; | ||
624 | |||
625 | if (!pblist) /* a sanity check */ | ||
626 | return -EINVAL; | ||
627 | |||
628 | /* Clear page flags */ | ||
629 | for_each_zone (zone) { | ||
630 | for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) | ||
631 | if (pfn_valid(zone_pfn + zone->zone_start_pfn)) | ||
632 | ClearPageNosaveFree(pfn_to_page(zone_pfn + | ||
633 | zone->zone_start_pfn)); | ||
634 | } | ||
635 | |||
636 | /* Mark orig addresses */ | ||
637 | for_each_pbe (p, pblist) { | ||
638 | if (virt_addr_valid(p->orig_address)) | ||
639 | SetPageNosaveFree(virt_to_page(p->orig_address)); | ||
640 | else | ||
641 | return -EFAULT; | ||
642 | } | ||
643 | |||
644 | return 0; | ||
645 | } | ||
646 | |||
647 | static void copy_page_backup_list(struct pbe *dst, struct pbe *src) | ||
648 | { | ||
649 | /* We assume both lists contain the same number of elements */ | ||
650 | while (src) { | ||
651 | dst->orig_address = src->orig_address; | ||
652 | dst = dst->next; | ||
653 | src = src->next; | ||
654 | } | ||
655 | } | ||
656 | |||
657 | static int check_header(struct swsusp_info *info) | ||
658 | { | ||
659 | char *reason = NULL; | ||
660 | |||
661 | if (info->version_code != LINUX_VERSION_CODE) | ||
662 | reason = "kernel version"; | ||
663 | if (info->num_physpages != num_physpages) | ||
664 | reason = "memory size"; | ||
665 | if (strcmp(info->uts.sysname,system_utsname.sysname)) | ||
666 | reason = "system type"; | ||
667 | if (strcmp(info->uts.release,system_utsname.release)) | ||
668 | reason = "kernel release"; | ||
669 | if (strcmp(info->uts.version,system_utsname.version)) | ||
670 | reason = "version"; | ||
671 | if (strcmp(info->uts.machine,system_utsname.machine)) | ||
672 | reason = "machine"; | ||
673 | if (reason) { | ||
674 | printk(KERN_ERR "swsusp: Resume mismatch: %s\n", reason); | ||
675 | return -EPERM; | ||
676 | } | ||
677 | return 0; | ||
678 | } | ||
679 | |||
680 | /** | ||
681 | * load header - check the image header and copy data from it | ||
682 | */ | ||
683 | |||
684 | static int load_header(struct snapshot_handle *handle, | ||
685 | struct swsusp_info *info) | ||
686 | { | ||
687 | int error; | ||
688 | struct pbe *pblist; | ||
689 | |||
690 | error = check_header(info); | ||
691 | if (!error) { | ||
692 | pblist = alloc_pagedir(info->image_pages, GFP_ATOMIC, 0); | ||
693 | if (!pblist) | ||
694 | return -ENOMEM; | ||
695 | pagedir_nosave = pblist; | ||
696 | handle->pbe = pblist; | ||
697 | nr_copy_pages = info->image_pages; | ||
698 | nr_meta_pages = info->pages - info->image_pages - 1; | ||
699 | } | ||
700 | return error; | ||
701 | } | ||
702 | |||
703 | /** | ||
704 | * unpack_orig_addresses - copy the elements of @buf[] (1 page) to | ||
705 | * the PBEs in the list starting at @pbe | ||
706 | */ | ||
707 | |||
708 | static inline struct pbe *unpack_orig_addresses(unsigned long *buf, | ||
709 | struct pbe *pbe) | ||
710 | { | ||
711 | int j; | ||
712 | |||
713 | for (j = 0; j < PAGE_SIZE / sizeof(long) && pbe; j++) { | ||
714 | pbe->orig_address = buf[j]; | ||
715 | pbe = pbe->next; | ||
716 | } | ||
717 | return pbe; | ||
718 | } | ||
719 | |||
720 | /** | ||
721 | * create_image - use metadata contained in the PBE list | ||
722 | * pointed to by pagedir_nosave to mark the pages that will | ||
723 | * be overwritten in the process of restoring the system | ||
724 | * memory state from the image and allocate memory for | ||
725 | * the image avoiding these pages | ||
726 | */ | ||
727 | |||
728 | static int create_image(struct snapshot_handle *handle) | ||
729 | { | ||
730 | int error = 0; | ||
731 | struct pbe *p, *pblist; | ||
732 | |||
733 | p = pagedir_nosave; | ||
734 | error = mark_unsafe_pages(p); | ||
735 | if (!error) { | ||
736 | pblist = alloc_pagedir(nr_copy_pages, GFP_ATOMIC, 1); | ||
737 | if (pblist) | ||
738 | copy_page_backup_list(pblist, p); | ||
739 | free_pagedir(p); | ||
740 | if (!pblist) | ||
741 | error = -ENOMEM; | ||
742 | } | ||
743 | if (!error) | ||
744 | error = alloc_data_pages(pblist, GFP_ATOMIC, 1); | ||
745 | if (!error) { | ||
746 | release_eaten_pages(); | ||
747 | pagedir_nosave = pblist; | ||
748 | } else { | ||
749 | pagedir_nosave = NULL; | ||
750 | handle->pbe = NULL; | ||
751 | nr_copy_pages = 0; | ||
752 | nr_meta_pages = 0; | ||
753 | } | ||
754 | return error; | ||
755 | } | ||
756 | |||
757 | /** | ||
758 | * snapshot_write_next - used for writing the system memory snapshot. | ||
759 | * | ||
760 | * On the first call to it @handle should point to a zeroed | ||
761 | * snapshot_handle structure. The structure gets updated and a pointer | ||
762 | * to it should be passed to this function every next time. | ||
763 | * | ||
764 | * The @count parameter should contain the number of bytes the caller | ||
765 | * wants to write to the image. It must not be zero. | ||
766 | * | ||
767 | * On success the function returns a positive number. Then, the caller | ||
768 | * is allowed to write up to the returned number of bytes to the memory | ||
769 | * location computed by the data_of() macro. The number returned | ||
770 | * may be smaller than @count, but this only happens if the write would | ||
771 | * cross a page boundary otherwise. | ||
772 | * | ||
773 | * The function returns 0 to indicate the "end of file" condition, | ||
774 | * and a negative number is returned on error. In such cases the | ||
775 | * structure pointed to by @handle is not updated and should not be used | ||
776 | * any more. | ||
777 | */ | ||
778 | |||
779 | int snapshot_write_next(struct snapshot_handle *handle, size_t count) | ||
780 | { | ||
781 | int error = 0; | ||
782 | |||
783 | if (handle->prev && handle->page > nr_meta_pages + nr_copy_pages) | ||
784 | return 0; | ||
785 | if (!buffer) { | ||
786 | /* This makes the buffer be freed by swsusp_free() */ | ||
787 | buffer = alloc_image_page(GFP_ATOMIC, 0); | ||
788 | if (!buffer) | ||
789 | return -ENOMEM; | ||
790 | } | ||
791 | if (!handle->offset) | ||
792 | handle->buffer = buffer; | ||
793 | if (handle->prev < handle->page) { | ||
794 | if (!handle->prev) { | ||
795 | error = load_header(handle, (struct swsusp_info *)buffer); | ||
796 | if (error) | ||
797 | return error; | ||
798 | } else if (handle->prev <= nr_meta_pages) { | ||
799 | handle->pbe = unpack_orig_addresses(buffer, handle->pbe); | ||
800 | if (!handle->pbe) { | ||
801 | error = create_image(handle); | ||
802 | if (error) | ||
803 | return error; | ||
804 | handle->pbe = pagedir_nosave; | ||
805 | handle->buffer = (void *)handle->pbe->address; | ||
806 | } | ||
807 | } else { | ||
808 | handle->pbe = handle->pbe->next; | ||
809 | handle->buffer = (void *)handle->pbe->address; | ||
810 | } | ||
811 | handle->prev = handle->page; | ||
812 | } | ||
813 | handle->buf_offset = handle->page_offset; | ||
814 | if (handle->page_offset + count >= PAGE_SIZE) { | ||
815 | count = PAGE_SIZE - handle->page_offset; | ||
816 | handle->page_offset = 0; | ||
817 | handle->page++; | ||
818 | } else { | ||
819 | handle->page_offset += count; | ||
820 | } | ||
821 | handle->offset += count; | ||
822 | return count; | ||
823 | } | ||
824 | |||
825 | int snapshot_image_loaded(struct snapshot_handle *handle) | ||
826 | { | ||
827 | return !(!handle->pbe || handle->pbe->next || !nr_copy_pages || | ||
828 | handle->page <= nr_meta_pages + nr_copy_pages); | ||
829 | } | ||