📄 snapshot.c
字号:
#else#define page_is_saveable(zone, pfn) saveable_page(pfn)static inline voidcopy_data_page(unsigned long dst_pfn, unsigned long src_pfn){ do_copy_page(page_address(pfn_to_page(dst_pfn)), page_address(pfn_to_page(src_pfn)));}#endif /* CONFIG_HIGHMEM */static voidcopy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm){ struct zone *zone; unsigned long pfn; for_each_zone(zone) { unsigned long max_zone_pfn; mark_free_pages(zone); max_zone_pfn = zone->zone_start_pfn + zone->spanned_pages; for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++) if (page_is_saveable(zone, pfn)) memory_bm_set_bit(orig_bm, pfn); } memory_bm_position_reset(orig_bm); memory_bm_position_reset(copy_bm); do { pfn = memory_bm_next_pfn(orig_bm); if (likely(pfn != BM_END_OF_MAP)) copy_data_page(memory_bm_next_pfn(copy_bm), pfn); } while (pfn != BM_END_OF_MAP);}/* Total number of image pages */static unsigned int nr_copy_pages;/* Number of pages needed for saving the original pfns of the image pages */static unsigned int nr_meta_pages;/** * swsusp_free - free pages allocated for the suspend. * * Suspend pages are alocated before the atomic copy is made, so we * need to release them after the resume. */void swsusp_free(void){ struct zone *zone; unsigned long pfn, max_zone_pfn; for_each_zone(zone) { max_zone_pfn = zone->zone_start_pfn + zone->spanned_pages; for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++) if (pfn_valid(pfn)) { struct page *page = pfn_to_page(pfn); if (swsusp_page_is_forbidden(page) && swsusp_page_is_free(page)) { swsusp_unset_page_forbidden(page); swsusp_unset_page_free(page); __free_page(page); } } } nr_copy_pages = 0; nr_meta_pages = 0; restore_pblist = NULL; buffer = NULL;}#ifdef CONFIG_HIGHMEM/** * count_pages_for_highmem - compute the number of non-highmem pages * that will be necessary for creating copies of highmem pages. */static unsigned int count_pages_for_highmem(unsigned int nr_highmem){ unsigned int free_highmem = count_free_highmem_pages(); if (free_highmem >= nr_highmem) nr_highmem = 0; else nr_highmem -= free_highmem; return nr_highmem;}#elsestatic unsigned intcount_pages_for_highmem(unsigned int nr_highmem) { return 0; }#endif /* CONFIG_HIGHMEM *//** * enough_free_mem - Make sure we have enough free memory for the * snapshot image. */static int enough_free_mem(unsigned int nr_pages, unsigned int nr_highmem){ struct zone *zone; unsigned int free = 0, meta = 0; for_each_zone(zone) { meta += snapshot_additional_pages(zone); if (!is_highmem(zone)) free += zone_page_state(zone, NR_FREE_PAGES); } nr_pages += count_pages_for_highmem(nr_highmem); pr_debug("swsusp: Normal pages needed: %u + %u + %u, available pages: %u\n", nr_pages, PAGES_FOR_IO, meta, free); return free > nr_pages + PAGES_FOR_IO + meta;}#ifdef CONFIG_HIGHMEM/** * get_highmem_buffer - if there are some highmem pages in the suspend * image, we may need the buffer to copy them and/or load their data. */static inline int get_highmem_buffer(int safe_needed){ buffer = get_image_page(GFP_ATOMIC | __GFP_COLD, safe_needed); return buffer ? 0 : -ENOMEM;}/** * alloc_highmem_image_pages - allocate some highmem pages for the image. * Try to allocate as many pages as needed, but if the number of free * highmem pages is lesser than that, allocate them all. */static inline unsigned intalloc_highmem_image_pages(struct memory_bitmap *bm, unsigned int nr_highmem){ unsigned int to_alloc = count_free_highmem_pages(); if (to_alloc > nr_highmem) to_alloc = nr_highmem; nr_highmem -= to_alloc; while (to_alloc-- > 0) { struct page *page; page = alloc_image_page(__GFP_HIGHMEM); memory_bm_set_bit(bm, page_to_pfn(page)); } return nr_highmem;}#elsestatic inline int get_highmem_buffer(int safe_needed) { return 0; }static inline unsigned intalloc_highmem_image_pages(struct memory_bitmap *bm, unsigned int n) { return 0; }#endif /* CONFIG_HIGHMEM *//** * swsusp_alloc - allocate memory for the suspend image * * We first try to allocate as many highmem pages as there are * saveable highmem pages in the system. If that fails, we allocate * non-highmem pages for the copies of the remaining highmem ones. * * In this approach it is likely that the copies of highmem pages will * also be located in the high memory, because of the way in which * copy_data_pages() works. */static intswsusp_alloc(struct memory_bitmap *orig_bm, struct memory_bitmap *copy_bm, unsigned int nr_pages, unsigned int nr_highmem){ int error; error = memory_bm_create(orig_bm, GFP_ATOMIC | __GFP_COLD, PG_ANY); if (error) goto Free; error = memory_bm_create(copy_bm, GFP_ATOMIC | __GFP_COLD, PG_ANY); if (error) goto Free; if (nr_highmem > 0) { error = get_highmem_buffer(PG_ANY); if (error) goto Free; nr_pages += alloc_highmem_image_pages(copy_bm, nr_highmem); } while (nr_pages-- > 0) { struct page *page = alloc_image_page(GFP_ATOMIC | __GFP_COLD); if (!page) goto Free; memory_bm_set_bit(copy_bm, page_to_pfn(page)); } return 0; Free: swsusp_free(); return -ENOMEM;}/* Memory bitmap used for marking saveable pages (during suspend) or the * suspend image pages (during resume) */static struct memory_bitmap orig_bm;/* Memory bitmap used on suspend for marking allocated pages that will contain * the copies of saveable pages. During resume it is initially used for * marking the suspend image pages, but then its set bits are duplicated in * @orig_bm and it is released. Next, on systems with high memory, it may be * used for marking "safe" highmem pages, but it has to be reinitialized for * this purpose. */static struct memory_bitmap copy_bm;asmlinkage int swsusp_save(void){ unsigned int nr_pages, nr_highmem; printk("swsusp: critical section: \n"); drain_local_pages(); nr_pages = count_data_pages(); nr_highmem = count_highmem_pages(); printk("swsusp: Need to copy %u pages\n", nr_pages + nr_highmem); if (!enough_free_mem(nr_pages, nr_highmem)) { printk(KERN_ERR "swsusp: Not enough free memory\n"); return -ENOMEM; } if (swsusp_alloc(&orig_bm, ©_bm, nr_pages, nr_highmem)) { printk(KERN_ERR "swsusp: Memory allocation failed\n"); return -ENOMEM; } /* During allocating of suspend pagedir, new cold pages may appear. * Kill them. */ drain_local_pages(); copy_data_pages(©_bm, &orig_bm); /* * End of critical section. From now on, we can write to memory, * but we should not touch disk. This specially means we must _not_ * touch swap space! Except we must write out our image of course. */ nr_pages += nr_highmem; nr_copy_pages = nr_pages; nr_meta_pages = DIV_ROUND_UP(nr_pages * sizeof(long), PAGE_SIZE); printk("swsusp: critical section: done (%d pages copied)\n", nr_pages); return 0;}static void init_header(struct swsusp_info *info){ memset(info, 0, sizeof(struct swsusp_info)); info->version_code = LINUX_VERSION_CODE; info->num_physpages = num_physpages; memcpy(&info->uts, init_utsname(), sizeof(struct new_utsname)); info->cpus = num_online_cpus(); info->image_pages = nr_copy_pages; info->pages = nr_copy_pages + nr_meta_pages + 1; info->size = info->pages; info->size <<= PAGE_SHIFT;}/** * pack_pfns - pfns corresponding to the set bits found in the bitmap @bm * are stored in the array @buf[] (1 page at a time) */static inline voidpack_pfns(unsigned long *buf, struct memory_bitmap *bm){ int j; for (j = 0; j < PAGE_SIZE / sizeof(long); j++) { buf[j] = memory_bm_next_pfn(bm); if (unlikely(buf[j] == BM_END_OF_MAP)) break; }}/** * snapshot_read_next - used for reading the system memory snapshot. * * On the first call to it @handle should point to a zeroed * snapshot_handle structure. The structure gets updated and a pointer * to it should be passed to this function every next time. * * The @count parameter should contain the number of bytes the caller * wants to read from the snapshot. It must not be zero. * * On success the function returns a positive number. Then, the caller * is allowed to read up to the returned number of bytes from the memory * location computed by the data_of() macro. The number returned * may be smaller than @count, but this only happens if the read would * cross a page boundary otherwise. * * The function returns 0 to indicate the end of data stream condition, * and a negative number is returned on error. In such cases the * structure pointed to by @handle is not updated and should not be used * any more. */int snapshot_read_next(struct snapshot_handle *handle, size_t count){ if (handle->cur > nr_meta_pages + nr_copy_pages) return 0; if (!buffer) { /* This makes the buffer be freed by swsusp_free() */ buffer = get_image_page(GFP_ATOMIC, PG_ANY); if (!buffer) return -ENOMEM; } if (!handle->offset) { init_header((struct swsusp_info *)buffer); handle->buffer = buffer; memory_bm_position_reset(&orig_bm); memory_bm_position_reset(©_bm); } if (handle->prev < handle->cur) { if (handle->cur <= nr_meta_pages) { memset(buffer, 0, PAGE_SIZE); pack_pfns(buffer, &orig_bm); } else { struct page *page; page = pfn_to_page(memory_bm_next_pfn(©_bm)); if (PageHighMem(page)) { /* Highmem pages are copied to the buffer, * because we can't return with a kmapped * highmem page (we may not be called again). */ void *kaddr; kaddr = kmap_atomic(page, KM_USER0); memcpy(buffer, kaddr, PAGE_SIZE); kunmap_atomic(kaddr, KM_USER0); handle->buffer = buffer; } else { handle->buffer = page_address(page); } } handle->prev = handle->cur; } handle->buf_offset = handle->cur_offset; if (handle->cur_offset + count >= PAGE_SIZE) { count = PAGE_SIZE - handle->cur_offset; handle->cur_offset = 0; handle->cur++; } else { handle->cur_offset += count; } handle->offset += count; return count;}/** * mark_unsafe_pages - mark the pages that cannot be used for storing * the image during resume, because they conflict with the pages that * had been used before suspend */static int mark_unsafe_pages(struct memory_bitmap *bm){ struct zone *zone; unsigned long pfn, max_zone_pfn; /* Clear page flags */ for_each_zone(zone) { max_zone_pfn = zone->zone_start_pfn + zone->spanned_pages; for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++) if (pfn_valid(pfn)) swsusp_unset_page_free(pfn_to_page(pfn)); } /* Mark pages that correspond to the "original" pfns as "unsafe" */ memory_bm_position_reset(bm); do { pfn = memory_bm_next_pfn(bm); if (likely(pfn != BM_END_OF_MAP)) { if (likely(pfn_valid(pfn))) swsusp_set_page_free(pfn_to_page(pfn)); else return -EFAULT; } } while (pfn != BM_END_OF_MAP); allocated_unsafe_pages = 0; return 0;}static voidduplicate_memory_bitmap(struct memory_bitmap *dst, struct memory_bitmap *src){ unsigned long pfn; memory_bm_position_reset(src); pfn = memory_bm_next_pfn(src); while (pfn != BM_END_OF_MAP) { memory_bm_set_bit(dst, pfn); pfn = memory_bm_next_pfn(src); }}static inline int check_header(struct swsusp_info *info){ char *reason = NULL; if (info->version_code != LINUX_VERSION_CODE) reason = "kernel version"; if (info->num_physpages != num_physpages) reason = "memory size"; if (strcmp(info->uts.sysname,init_utsname()->sysname)) reason = "system type"; if (strcmp(info->uts.release,init_utsname()->release)) reason = "kernel release"; if (strcmp(info->uts.version,init_utsname()->version)) reason = "version"; if (strcmp(info->uts.machine,init_utsname()->machine)) reason = "machine"; if (reason) { printk(KERN_ERR "swsusp: Resume mismatch: %s\n", reason); return -EPERM; } return 0;}/** * load header - check the image header and copy data from it */static intload_header(struct swsusp_info *info){ int error; restore_pblist = NULL; error = check_header(info); if (!error) { nr_copy_pages = info->image_pages; nr_meta_pages = info->pages - info->image_pages - 1; } return error;}/** * unpack_orig_pfns - for each element of @buf[] (1 page at a time) set * the corresponding bit in the memory bitmap @bm */static inline voidunpack_orig_pfns(unsigned long *buf, struct memory_bitmap *bm){ int j; for (j = 0; j < PAGE_SIZE / sizeof(long); j++) { if (unlikely(buf[j] == BM_END_OF_MAP)) break; memory_bm_set_bit(bm, buf[j]); }}/* List of "safe" pages that may be used to store data loaded from the suspend * image */static struct linked_page *safe_pages_list;#ifdef CONFIG_HIGHMEM/* struct highmem_pbe is used for creating the list of highmem pages that * should be restored atomically during the resume from disk, because the page * frames they have occupied before the suspend are in use. */struct highmem_pbe { struct page *copy_page; /* data is here now */ struct page *orig_page; /* data was here before the suspend */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -