📄 snapshot.c
字号:
struct highmem_pbe *next;};/* List of highmem PBEs needed for restoring the highmem pages that were * allocated before the suspend and included in the suspend image, but have * also been allocated by the "resume" kernel, so their contents cannot be * written directly to their "original" page frames. */static struct highmem_pbe *highmem_pblist;/** * count_highmem_image_pages - compute the number of highmem pages in the * suspend image. The bits in the memory bitmap @bm that correspond to the * image pages are assumed to be set. */static unsigned int count_highmem_image_pages(struct memory_bitmap *bm){ unsigned long pfn; unsigned int cnt = 0; memory_bm_position_reset(bm); pfn = memory_bm_next_pfn(bm); while (pfn != BM_END_OF_MAP) { if (PageHighMem(pfn_to_page(pfn))) cnt++; pfn = memory_bm_next_pfn(bm); } return cnt;}/** * prepare_highmem_image - try to allocate as many highmem pages as * there are highmem image pages (@nr_highmem_p points to the variable * containing the number of highmem image pages). The pages that are * "safe" (ie. will not be overwritten when the suspend image is * restored) have the corresponding bits set in @bm (it must be * unitialized). * * NOTE: This function should not be called if there are no highmem * image pages. */static unsigned int safe_highmem_pages;static struct memory_bitmap *safe_highmem_bm;static intprepare_highmem_image(struct memory_bitmap *bm, unsigned int *nr_highmem_p){ unsigned int to_alloc; if (memory_bm_create(bm, GFP_ATOMIC, PG_SAFE)) return -ENOMEM; if (get_highmem_buffer(PG_SAFE)) return -ENOMEM; to_alloc = count_free_highmem_pages(); if (to_alloc > *nr_highmem_p) to_alloc = *nr_highmem_p; else *nr_highmem_p = to_alloc; safe_highmem_pages = 0; while (to_alloc-- > 0) { struct page *page; page = alloc_page(__GFP_HIGHMEM); if (!swsusp_page_is_free(page)) { /* The page is "safe", set its bit the bitmap */ memory_bm_set_bit(bm, page_to_pfn(page)); safe_highmem_pages++; } /* Mark the page as allocated */ swsusp_set_page_forbidden(page); swsusp_set_page_free(page); } memory_bm_position_reset(bm); safe_highmem_bm = bm; return 0;}/** * get_highmem_page_buffer - for given highmem image page find the buffer * that suspend_write_next() should set for its caller to write to. * * If the page is to be saved to its "original" page frame or a copy of * the page is to be made in the highmem, @buffer is returned. Otherwise, * the copy of the page is to be made in normal memory, so the address of * the copy is returned. * * If @buffer is returned, the caller of suspend_write_next() will write * the page's contents to @buffer, so they will have to be copied to the * right location on the next call to suspend_write_next() and it is done * with the help of copy_last_highmem_page(). For this purpose, if * @buffer is returned, @last_highmem page is set to the page to which * the data will have to be copied from @buffer. */static struct page *last_highmem_page;static void *get_highmem_page_buffer(struct page *page, struct chain_allocator *ca){ struct highmem_pbe *pbe; void *kaddr; if (swsusp_page_is_forbidden(page) && swsusp_page_is_free(page)) { /* We have allocated the "original" page frame and we can * use it directly to store the loaded page. */ last_highmem_page = page; return buffer; } /* The "original" page frame has not been allocated and we have to * use a "safe" page frame to store the loaded page. */ pbe = chain_alloc(ca, sizeof(struct highmem_pbe)); if (!pbe) { swsusp_free(); return NULL; } pbe->orig_page = page; if (safe_highmem_pages > 0) { struct page *tmp; /* Copy of the page will be stored in high memory */ kaddr = buffer; tmp = pfn_to_page(memory_bm_next_pfn(safe_highmem_bm)); safe_highmem_pages--; last_highmem_page = tmp; pbe->copy_page = tmp; } else { /* Copy of the page will be stored in normal memory */ kaddr = safe_pages_list; safe_pages_list = safe_pages_list->next; pbe->copy_page = virt_to_page(kaddr); } pbe->next = highmem_pblist; highmem_pblist = pbe; return kaddr;}/** * copy_last_highmem_page - copy the contents of a highmem image from * @buffer, where the caller of snapshot_write_next() has place them, * to the right location represented by @last_highmem_page . */static void copy_last_highmem_page(void){ if (last_highmem_page) { void *dst; dst = kmap_atomic(last_highmem_page, KM_USER0); memcpy(dst, buffer, PAGE_SIZE); kunmap_atomic(dst, KM_USER0); last_highmem_page = NULL; }}static inline int last_highmem_page_copied(void){ return !last_highmem_page;}static inline void free_highmem_data(void){ if (safe_highmem_bm) memory_bm_free(safe_highmem_bm, PG_UNSAFE_CLEAR); if (buffer) free_image_page(buffer, PG_UNSAFE_CLEAR);}#elsestatic inline int get_safe_write_buffer(void) { return 0; }static unsigned intcount_highmem_image_pages(struct memory_bitmap *bm) { return 0; }static inline intprepare_highmem_image(struct memory_bitmap *bm, unsigned int *nr_highmem_p){ return 0;}static inline void *get_highmem_page_buffer(struct page *page, struct chain_allocator *ca){ return NULL;}static inline void copy_last_highmem_page(void) {}static inline int last_highmem_page_copied(void) { return 1; }static inline void free_highmem_data(void) {}#endif /* CONFIG_HIGHMEM *//** * prepare_image - use the memory bitmap @bm to mark the pages that will * be overwritten in the process of restoring the system memory state * from the suspend image ("unsafe" pages) and allocate memory for the * image. * * The idea is to allocate a new memory bitmap first and then allocate * as many pages as needed for the image data, but not to assign these * pages to specific tasks initially. Instead, we just mark them as * allocated and create a lists of "safe" pages that will be used * later. On systems with high memory a list of "safe" highmem pages is * also created. */#define PBES_PER_LINKED_PAGE (LINKED_PAGE_DATA_SIZE / sizeof(struct pbe))static intprepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm){ unsigned int nr_pages, nr_highmem; struct linked_page *sp_list, *lp; int error; /* If there is no highmem, the buffer will not be necessary */ free_image_page(buffer, PG_UNSAFE_CLEAR); buffer = NULL; nr_highmem = count_highmem_image_pages(bm); error = mark_unsafe_pages(bm); if (error) goto Free; error = memory_bm_create(new_bm, GFP_ATOMIC, PG_SAFE); if (error) goto Free; duplicate_memory_bitmap(new_bm, bm); memory_bm_free(bm, PG_UNSAFE_KEEP); if (nr_highmem > 0) { error = prepare_highmem_image(bm, &nr_highmem); if (error) goto Free; } /* Reserve some safe pages for potential later use. * * NOTE: This way we make sure there will be enough safe pages for the * chain_alloc() in get_buffer(). It is a bit wasteful, but * nr_copy_pages cannot be greater than 50% of the memory anyway. */ sp_list = NULL; /* nr_copy_pages cannot be lesser than allocated_unsafe_pages */ nr_pages = nr_copy_pages - nr_highmem - allocated_unsafe_pages; nr_pages = DIV_ROUND_UP(nr_pages, PBES_PER_LINKED_PAGE); while (nr_pages > 0) { lp = get_image_page(GFP_ATOMIC, PG_SAFE); if (!lp) { error = -ENOMEM; goto Free; } lp->next = sp_list; sp_list = lp; nr_pages--; } /* Preallocate memory for the image */ safe_pages_list = NULL; nr_pages = nr_copy_pages - nr_highmem - allocated_unsafe_pages; while (nr_pages > 0) { lp = (struct linked_page *)get_zeroed_page(GFP_ATOMIC); if (!lp) { error = -ENOMEM; goto Free; } if (!swsusp_page_is_free(virt_to_page(lp))) { /* The page is "safe", add it to the list */ lp->next = safe_pages_list; safe_pages_list = lp; } /* Mark the page as allocated */ swsusp_set_page_forbidden(virt_to_page(lp)); swsusp_set_page_free(virt_to_page(lp)); nr_pages--; } /* Free the reserved safe pages so that chain_alloc() can use them */ while (sp_list) { lp = sp_list->next; free_image_page(sp_list, PG_UNSAFE_CLEAR); sp_list = lp; } return 0; Free: swsusp_free(); return error;}/** * get_buffer - compute the address that snapshot_write_next() should * set for its caller to write to. */static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca){ struct pbe *pbe; struct page *page = pfn_to_page(memory_bm_next_pfn(bm)); if (PageHighMem(page)) return get_highmem_page_buffer(page, ca); if (swsusp_page_is_forbidden(page) && swsusp_page_is_free(page)) /* We have allocated the "original" page frame and we can * use it directly to store the loaded page. */ return page_address(page); /* The "original" page frame has not been allocated and we have to * use a "safe" page frame to store the loaded page. */ pbe = chain_alloc(ca, sizeof(struct pbe)); if (!pbe) { swsusp_free(); return NULL; } pbe->orig_address = page_address(page); pbe->address = safe_pages_list; safe_pages_list = safe_pages_list->next; pbe->next = restore_pblist; restore_pblist = pbe; return pbe->address;}/** * snapshot_write_next - used for writing 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 write to the image. It must not be zero. * * On success the function returns a positive number. Then, the caller * is allowed to write up to the returned number of bytes to the memory * location computed by the data_of() macro. The number returned * may be smaller than @count, but this only happens if the write would * cross a page boundary otherwise. * * The function returns 0 to indicate the "end of file" 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_write_next(struct snapshot_handle *handle, size_t count){ static struct chain_allocator ca; int error = 0; /* Check if we have already loaded the entire image */ if (handle->prev && handle->cur > nr_meta_pages + nr_copy_pages) return 0; if (handle->offset == 0) { if (!buffer) /* This makes the buffer be freed by swsusp_free() */ buffer = get_image_page(GFP_ATOMIC, PG_ANY); if (!buffer) return -ENOMEM; handle->buffer = buffer; } handle->sync_read = 1; if (handle->prev < handle->cur) { if (handle->prev == 0) { error = load_header(buffer); if (error) return error; error = memory_bm_create(©_bm, GFP_ATOMIC, PG_ANY); if (error) return error; } else if (handle->prev <= nr_meta_pages) { unpack_orig_pfns(buffer, ©_bm); if (handle->prev == nr_meta_pages) { error = prepare_image(&orig_bm, ©_bm); if (error) return error; chain_init(&ca, GFP_ATOMIC, PG_SAFE); memory_bm_position_reset(&orig_bm); restore_pblist = NULL; handle->buffer = get_buffer(&orig_bm, &ca); handle->sync_read = 0; if (!handle->buffer) return -ENOMEM; } } else { copy_last_highmem_page(); handle->buffer = get_buffer(&orig_bm, &ca); if (handle->buffer != buffer) handle->sync_read = 0; } 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;}/** * snapshot_write_finalize - must be called after the last call to * snapshot_write_next() in case the last page in the image happens * to be a highmem page and its contents should be stored in the * highmem. Additionally, it releases the memory that will not be * used any more. */void snapshot_write_finalize(struct snapshot_handle *handle){ copy_last_highmem_page(); /* Free only if we have loaded the image entirely */ if (handle->prev && handle->cur > nr_meta_pages + nr_copy_pages) { memory_bm_free(&orig_bm, PG_UNSAFE_CLEAR); free_highmem_data(); }}int snapshot_image_loaded(struct snapshot_handle *handle){ return !(!nr_copy_pages || !last_highmem_page_copied() || handle->cur <= nr_meta_pages + nr_copy_pages);}#ifdef CONFIG_HIGHMEM/* Assumes that @buf is ready and points to a "safe" page */static inline voidswap_two_pages_data(struct page *p1, struct page *p2, void *buf){ void *kaddr1, *kaddr2; kaddr1 = kmap_atomic(p1, KM_USER0); kaddr2 = kmap_atomic(p2, KM_USER1); memcpy(buf, kaddr1, PAGE_SIZE); memcpy(kaddr1, kaddr2, PAGE_SIZE); memcpy(kaddr2, buf, PAGE_SIZE); kunmap_atomic(kaddr1, KM_USER0); kunmap_atomic(kaddr2, KM_USER1);}/** * restore_highmem - for each highmem page that was allocated before * the suspend and included in the suspend image, and also has been * allocated by the "resume" kernel swap its current (ie. "before * resume") contents with the previous (ie. "before suspend") one. * * If the resume eventually fails, we can call this function once * again and restore the "before resume" highmem state. */int restore_highmem(void){ struct highmem_pbe *pbe = highmem_pblist; void *buf; if (!pbe) return 0; buf = get_image_page(GFP_ATOMIC, PG_SAFE); if (!buf) return -ENOMEM; while (pbe) { swap_two_pages_data(pbe->copy_page, pbe->orig_page, buf); pbe = pbe->next; } free_image_page(buf, PG_UNSAFE_CLEAR); return 0;}#endif /* CONFIG_HIGHMEM */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -