diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/power/power.h | 18 | ||||
-rw-r--r-- | kernel/power/snapshot.c | 145 | ||||
-rw-r--r-- | kernel/power/swap.c | 8 | ||||
-rw-r--r-- | kernel/power/user.c | 37 |
4 files changed, 88 insertions, 120 deletions
diff --git a/kernel/power/power.h b/kernel/power/power.h index 46c5a26630a3..b1e207dde1c2 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h | |||
@@ -97,24 +97,12 @@ extern int hibernate_preallocate_memory(void); | |||
97 | */ | 97 | */ |
98 | 98 | ||
99 | struct snapshot_handle { | 99 | struct snapshot_handle { |
100 | loff_t offset; /* number of the last byte ready for reading | ||
101 | * or writing in the sequence | ||
102 | */ | ||
103 | unsigned int cur; /* number of the block of PAGE_SIZE bytes the | 100 | unsigned int cur; /* number of the block of PAGE_SIZE bytes the |
104 | * next operation will refer to (ie. current) | 101 | * next operation will refer to (ie. current) |
105 | */ | 102 | */ |
106 | unsigned int cur_offset; /* offset with respect to the current | ||
107 | * block (for the next operation) | ||
108 | */ | ||
109 | unsigned int prev; /* number of the block of PAGE_SIZE bytes that | ||
110 | * was the current one previously | ||
111 | */ | ||
112 | void *buffer; /* address of the block to read from | 103 | void *buffer; /* address of the block to read from |
113 | * or write to | 104 | * or write to |
114 | */ | 105 | */ |
115 | unsigned int buf_offset; /* location to read from or write to, | ||
116 | * given as a displacement from 'buffer' | ||
117 | */ | ||
118 | int sync_read; /* Set to one to notify the caller of | 106 | int sync_read; /* Set to one to notify the caller of |
119 | * snapshot_write_next() that it may | 107 | * snapshot_write_next() that it may |
120 | * need to call wait_on_bio_chain() | 108 | * need to call wait_on_bio_chain() |
@@ -125,12 +113,12 @@ struct snapshot_handle { | |||
125 | * snapshot_read_next()/snapshot_write_next() is allowed to | 113 | * snapshot_read_next()/snapshot_write_next() is allowed to |
126 | * read/write data after the function returns | 114 | * read/write data after the function returns |
127 | */ | 115 | */ |
128 | #define data_of(handle) ((handle).buffer + (handle).buf_offset) | 116 | #define data_of(handle) ((handle).buffer) |
129 | 117 | ||
130 | extern unsigned int snapshot_additional_pages(struct zone *zone); | 118 | extern unsigned int snapshot_additional_pages(struct zone *zone); |
131 | extern unsigned long snapshot_get_image_size(void); | 119 | extern unsigned long snapshot_get_image_size(void); |
132 | extern int snapshot_read_next(struct snapshot_handle *handle, size_t count); | 120 | extern int snapshot_read_next(struct snapshot_handle *handle); |
133 | extern int snapshot_write_next(struct snapshot_handle *handle, size_t count); | 121 | extern int snapshot_write_next(struct snapshot_handle *handle); |
134 | extern void snapshot_write_finalize(struct snapshot_handle *handle); | 122 | extern void snapshot_write_finalize(struct snapshot_handle *handle); |
135 | extern int snapshot_image_loaded(struct snapshot_handle *handle); | 123 | extern int snapshot_image_loaded(struct snapshot_handle *handle); |
136 | 124 | ||
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index be861c26dda7..25ce010e9f8b 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c | |||
@@ -1604,14 +1604,9 @@ pack_pfns(unsigned long *buf, struct memory_bitmap *bm) | |||
1604 | * snapshot_handle structure. The structure gets updated and a pointer | 1604 | * snapshot_handle structure. The structure gets updated and a pointer |
1605 | * to it should be passed to this function every next time. | 1605 | * to it should be passed to this function every next time. |
1606 | * | 1606 | * |
1607 | * The @count parameter should contain the number of bytes the caller | ||
1608 | * wants to read from the snapshot. It must not be zero. | ||
1609 | * | ||
1610 | * On success the function returns a positive number. Then, the caller | 1607 | * On success the function returns a positive number. Then, the caller |
1611 | * is allowed to read up to the returned number of bytes from the memory | 1608 | * is allowed to read up to the returned number of bytes from the memory |
1612 | * location computed by the data_of() macro. The number returned | 1609 | * location computed by the data_of() macro. |
1613 | * may be smaller than @count, but this only happens if the read would | ||
1614 | * cross a page boundary otherwise. | ||
1615 | * | 1610 | * |
1616 | * The function returns 0 to indicate the end of data stream condition, | 1611 | * The function returns 0 to indicate the end of data stream condition, |
1617 | * and a negative number is returned on error. In such cases the | 1612 | * and a negative number is returned on error. In such cases the |
@@ -1619,7 +1614,7 @@ pack_pfns(unsigned long *buf, struct memory_bitmap *bm) | |||
1619 | * any more. | 1614 | * any more. |
1620 | */ | 1615 | */ |
1621 | 1616 | ||
1622 | int snapshot_read_next(struct snapshot_handle *handle, size_t count) | 1617 | int snapshot_read_next(struct snapshot_handle *handle) |
1623 | { | 1618 | { |
1624 | if (handle->cur > nr_meta_pages + nr_copy_pages) | 1619 | if (handle->cur > nr_meta_pages + nr_copy_pages) |
1625 | return 0; | 1620 | return 0; |
@@ -1630,7 +1625,7 @@ int snapshot_read_next(struct snapshot_handle *handle, size_t count) | |||
1630 | if (!buffer) | 1625 | if (!buffer) |
1631 | return -ENOMEM; | 1626 | return -ENOMEM; |
1632 | } | 1627 | } |
1633 | if (!handle->offset) { | 1628 | if (!handle->cur) { |
1634 | int error; | 1629 | int error; |
1635 | 1630 | ||
1636 | error = init_header((struct swsusp_info *)buffer); | 1631 | error = init_header((struct swsusp_info *)buffer); |
@@ -1639,42 +1634,30 @@ int snapshot_read_next(struct snapshot_handle *handle, size_t count) | |||
1639 | handle->buffer = buffer; | 1634 | handle->buffer = buffer; |
1640 | memory_bm_position_reset(&orig_bm); | 1635 | memory_bm_position_reset(&orig_bm); |
1641 | memory_bm_position_reset(©_bm); | 1636 | memory_bm_position_reset(©_bm); |
1642 | } | 1637 | } else if (handle->cur <= nr_meta_pages) { |
1643 | if (handle->prev < handle->cur) { | 1638 | memset(buffer, 0, PAGE_SIZE); |
1644 | if (handle->cur <= nr_meta_pages) { | 1639 | pack_pfns(buffer, &orig_bm); |
1645 | memset(buffer, 0, PAGE_SIZE); | 1640 | } else { |
1646 | pack_pfns(buffer, &orig_bm); | 1641 | struct page *page; |
1647 | } else { | ||
1648 | struct page *page; | ||
1649 | 1642 | ||
1650 | page = pfn_to_page(memory_bm_next_pfn(©_bm)); | 1643 | page = pfn_to_page(memory_bm_next_pfn(©_bm)); |
1651 | if (PageHighMem(page)) { | 1644 | if (PageHighMem(page)) { |
1652 | /* Highmem pages are copied to the buffer, | 1645 | /* Highmem pages are copied to the buffer, |
1653 | * because we can't return with a kmapped | 1646 | * because we can't return with a kmapped |
1654 | * highmem page (we may not be called again). | 1647 | * highmem page (we may not be called again). |
1655 | */ | 1648 | */ |
1656 | void *kaddr; | 1649 | void *kaddr; |
1657 | 1650 | ||
1658 | kaddr = kmap_atomic(page, KM_USER0); | 1651 | kaddr = kmap_atomic(page, KM_USER0); |
1659 | memcpy(buffer, kaddr, PAGE_SIZE); | 1652 | memcpy(buffer, kaddr, PAGE_SIZE); |
1660 | kunmap_atomic(kaddr, KM_USER0); | 1653 | kunmap_atomic(kaddr, KM_USER0); |
1661 | handle->buffer = buffer; | 1654 | handle->buffer = buffer; |
1662 | } else { | 1655 | } else { |
1663 | handle->buffer = page_address(page); | 1656 | handle->buffer = page_address(page); |
1664 | } | ||
1665 | } | 1657 | } |
1666 | handle->prev = handle->cur; | ||
1667 | } | ||
1668 | handle->buf_offset = handle->cur_offset; | ||
1669 | if (handle->cur_offset + count >= PAGE_SIZE) { | ||
1670 | count = PAGE_SIZE - handle->cur_offset; | ||
1671 | handle->cur_offset = 0; | ||
1672 | handle->cur++; | ||
1673 | } else { | ||
1674 | handle->cur_offset += count; | ||
1675 | } | 1658 | } |
1676 | handle->offset += count; | 1659 | handle->cur++; |
1677 | return count; | 1660 | return PAGE_SIZE; |
1678 | } | 1661 | } |
1679 | 1662 | ||
1680 | /** | 1663 | /** |
@@ -2133,14 +2116,9 @@ static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca) | |||
2133 | * snapshot_handle structure. The structure gets updated and a pointer | 2116 | * snapshot_handle structure. The structure gets updated and a pointer |
2134 | * to it should be passed to this function every next time. | 2117 | * to it should be passed to this function every next time. |
2135 | * | 2118 | * |
2136 | * The @count parameter should contain the number of bytes the caller | ||
2137 | * wants to write to the image. It must not be zero. | ||
2138 | * | ||
2139 | * On success the function returns a positive number. Then, the caller | 2119 | * On success the function returns a positive number. Then, the caller |
2140 | * is allowed to write up to the returned number of bytes to the memory | 2120 | * is allowed to write up to the returned number of bytes to the memory |
2141 | * location computed by the data_of() macro. The number returned | 2121 | * location computed by the data_of() macro. |
2142 | * may be smaller than @count, but this only happens if the write would | ||
2143 | * cross a page boundary otherwise. | ||
2144 | * | 2122 | * |
2145 | * The function returns 0 to indicate the "end of file" condition, | 2123 | * The function returns 0 to indicate the "end of file" condition, |
2146 | * and a negative number is returned on error. In such cases the | 2124 | * and a negative number is returned on error. In such cases the |
@@ -2148,16 +2126,18 @@ static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca) | |||
2148 | * any more. | 2126 | * any more. |
2149 | */ | 2127 | */ |
2150 | 2128 | ||
2151 | int snapshot_write_next(struct snapshot_handle *handle, size_t count) | 2129 | int snapshot_write_next(struct snapshot_handle *handle) |
2152 | { | 2130 | { |
2153 | static struct chain_allocator ca; | 2131 | static struct chain_allocator ca; |
2154 | int error = 0; | 2132 | int error = 0; |
2155 | 2133 | ||
2156 | /* Check if we have already loaded the entire image */ | 2134 | /* Check if we have already loaded the entire image */ |
2157 | if (handle->prev && handle->cur > nr_meta_pages + nr_copy_pages) | 2135 | if (handle->cur > 1 && handle->cur > nr_meta_pages + nr_copy_pages) |
2158 | return 0; | 2136 | return 0; |
2159 | 2137 | ||
2160 | if (handle->offset == 0) { | 2138 | handle->sync_read = 1; |
2139 | |||
2140 | if (!handle->cur) { | ||
2161 | if (!buffer) | 2141 | if (!buffer) |
2162 | /* This makes the buffer be freed by swsusp_free() */ | 2142 | /* This makes the buffer be freed by swsusp_free() */ |
2163 | buffer = get_image_page(GFP_ATOMIC, PG_ANY); | 2143 | buffer = get_image_page(GFP_ATOMIC, PG_ANY); |
@@ -2166,56 +2146,43 @@ int snapshot_write_next(struct snapshot_handle *handle, size_t count) | |||
2166 | return -ENOMEM; | 2146 | return -ENOMEM; |
2167 | 2147 | ||
2168 | handle->buffer = buffer; | 2148 | handle->buffer = buffer; |
2169 | } | 2149 | } else if (handle->cur == 1) { |
2170 | handle->sync_read = 1; | 2150 | error = load_header(buffer); |
2171 | if (handle->prev < handle->cur) { | 2151 | if (error) |
2172 | if (handle->prev == 0) { | 2152 | return error; |
2173 | error = load_header(buffer); | ||
2174 | if (error) | ||
2175 | return error; | ||
2176 | 2153 | ||
2177 | error = memory_bm_create(©_bm, GFP_ATOMIC, PG_ANY); | 2154 | error = memory_bm_create(©_bm, GFP_ATOMIC, PG_ANY); |
2178 | if (error) | 2155 | if (error) |
2179 | return error; | 2156 | return error; |
2157 | |||
2158 | } else if (handle->cur <= nr_meta_pages + 1) { | ||
2159 | error = unpack_orig_pfns(buffer, ©_bm); | ||
2160 | if (error) | ||
2161 | return error; | ||
2180 | 2162 | ||
2181 | } else if (handle->prev <= nr_meta_pages) { | 2163 | if (handle->cur == nr_meta_pages + 1) { |
2182 | error = unpack_orig_pfns(buffer, ©_bm); | 2164 | error = prepare_image(&orig_bm, ©_bm); |
2183 | if (error) | 2165 | if (error) |
2184 | return error; | 2166 | return error; |
2185 | 2167 | ||
2186 | if (handle->prev == nr_meta_pages) { | 2168 | chain_init(&ca, GFP_ATOMIC, PG_SAFE); |
2187 | error = prepare_image(&orig_bm, ©_bm); | 2169 | memory_bm_position_reset(&orig_bm); |
2188 | if (error) | 2170 | restore_pblist = NULL; |
2189 | return error; | ||
2190 | |||
2191 | chain_init(&ca, GFP_ATOMIC, PG_SAFE); | ||
2192 | memory_bm_position_reset(&orig_bm); | ||
2193 | restore_pblist = NULL; | ||
2194 | handle->buffer = get_buffer(&orig_bm, &ca); | ||
2195 | handle->sync_read = 0; | ||
2196 | if (IS_ERR(handle->buffer)) | ||
2197 | return PTR_ERR(handle->buffer); | ||
2198 | } | ||
2199 | } else { | ||
2200 | copy_last_highmem_page(); | ||
2201 | handle->buffer = get_buffer(&orig_bm, &ca); | 2171 | handle->buffer = get_buffer(&orig_bm, &ca); |
2172 | handle->sync_read = 0; | ||
2202 | if (IS_ERR(handle->buffer)) | 2173 | if (IS_ERR(handle->buffer)) |
2203 | return PTR_ERR(handle->buffer); | 2174 | return PTR_ERR(handle->buffer); |
2204 | if (handle->buffer != buffer) | ||
2205 | handle->sync_read = 0; | ||
2206 | } | 2175 | } |
2207 | handle->prev = handle->cur; | ||
2208 | } | ||
2209 | handle->buf_offset = handle->cur_offset; | ||
2210 | if (handle->cur_offset + count >= PAGE_SIZE) { | ||
2211 | count = PAGE_SIZE - handle->cur_offset; | ||
2212 | handle->cur_offset = 0; | ||
2213 | handle->cur++; | ||
2214 | } else { | 2176 | } else { |
2215 | handle->cur_offset += count; | 2177 | copy_last_highmem_page(); |
2178 | handle->buffer = get_buffer(&orig_bm, &ca); | ||
2179 | if (IS_ERR(handle->buffer)) | ||
2180 | return PTR_ERR(handle->buffer); | ||
2181 | if (handle->buffer != buffer) | ||
2182 | handle->sync_read = 0; | ||
2216 | } | 2183 | } |
2217 | handle->offset += count; | 2184 | handle->cur++; |
2218 | return count; | 2185 | return PAGE_SIZE; |
2219 | } | 2186 | } |
2220 | 2187 | ||
2221 | /** | 2188 | /** |
@@ -2230,7 +2197,7 @@ void snapshot_write_finalize(struct snapshot_handle *handle) | |||
2230 | { | 2197 | { |
2231 | copy_last_highmem_page(); | 2198 | copy_last_highmem_page(); |
2232 | /* Free only if we have loaded the image entirely */ | 2199 | /* Free only if we have loaded the image entirely */ |
2233 | if (handle->prev && handle->cur > nr_meta_pages + nr_copy_pages) { | 2200 | if (handle->cur > 1 && handle->cur > nr_meta_pages + nr_copy_pages) { |
2234 | memory_bm_free(&orig_bm, PG_UNSAFE_CLEAR); | 2201 | memory_bm_free(&orig_bm, PG_UNSAFE_CLEAR); |
2235 | free_highmem_data(); | 2202 | free_highmem_data(); |
2236 | } | 2203 | } |
diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 66824d71983a..7f2a17e4067b 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c | |||
@@ -431,7 +431,7 @@ static int save_image(struct swap_map_handle *handle, | |||
431 | bio = NULL; | 431 | bio = NULL; |
432 | do_gettimeofday(&start); | 432 | do_gettimeofday(&start); |
433 | while (1) { | 433 | while (1) { |
434 | ret = snapshot_read_next(snapshot, PAGE_SIZE); | 434 | ret = snapshot_read_next(snapshot); |
435 | if (ret <= 0) | 435 | if (ret <= 0) |
436 | break; | 436 | break; |
437 | ret = swap_write_page(handle, data_of(*snapshot), &bio); | 437 | ret = swap_write_page(handle, data_of(*snapshot), &bio); |
@@ -492,7 +492,7 @@ int swsusp_write(unsigned int flags) | |||
492 | return error; | 492 | return error; |
493 | } | 493 | } |
494 | memset(&snapshot, 0, sizeof(struct snapshot_handle)); | 494 | memset(&snapshot, 0, sizeof(struct snapshot_handle)); |
495 | error = snapshot_read_next(&snapshot, PAGE_SIZE); | 495 | error = snapshot_read_next(&snapshot); |
496 | if (error < PAGE_SIZE) { | 496 | if (error < PAGE_SIZE) { |
497 | if (error >= 0) | 497 | if (error >= 0) |
498 | error = -EFAULT; | 498 | error = -EFAULT; |
@@ -615,7 +615,7 @@ static int load_image(struct swap_map_handle *handle, | |||
615 | bio = NULL; | 615 | bio = NULL; |
616 | do_gettimeofday(&start); | 616 | do_gettimeofday(&start); |
617 | for ( ; ; ) { | 617 | for ( ; ; ) { |
618 | error = snapshot_write_next(snapshot, PAGE_SIZE); | 618 | error = snapshot_write_next(snapshot); |
619 | if (error <= 0) | 619 | if (error <= 0) |
620 | break; | 620 | break; |
621 | error = swap_read_page(handle, data_of(*snapshot), &bio); | 621 | error = swap_read_page(handle, data_of(*snapshot), &bio); |
@@ -660,7 +660,7 @@ int swsusp_read(unsigned int *flags_p) | |||
660 | *flags_p = swsusp_header->flags; | 660 | *flags_p = swsusp_header->flags; |
661 | 661 | ||
662 | memset(&snapshot, 0, sizeof(struct snapshot_handle)); | 662 | memset(&snapshot, 0, sizeof(struct snapshot_handle)); |
663 | error = snapshot_write_next(&snapshot, PAGE_SIZE); | 663 | error = snapshot_write_next(&snapshot); |
664 | if (error < PAGE_SIZE) | 664 | if (error < PAGE_SIZE) |
665 | return error < 0 ? error : -EFAULT; | 665 | return error < 0 ? error : -EFAULT; |
666 | header = (struct swsusp_info *)data_of(snapshot); | 666 | header = (struct swsusp_info *)data_of(snapshot); |
diff --git a/kernel/power/user.c b/kernel/power/user.c index a8c96212bc1b..e819e17877ca 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c | |||
@@ -151,6 +151,7 @@ static ssize_t snapshot_read(struct file *filp, char __user *buf, | |||
151 | { | 151 | { |
152 | struct snapshot_data *data; | 152 | struct snapshot_data *data; |
153 | ssize_t res; | 153 | ssize_t res; |
154 | loff_t pg_offp = *offp & ~PAGE_MASK; | ||
154 | 155 | ||
155 | mutex_lock(&pm_mutex); | 156 | mutex_lock(&pm_mutex); |
156 | 157 | ||
@@ -159,14 +160,19 @@ static ssize_t snapshot_read(struct file *filp, char __user *buf, | |||
159 | res = -ENODATA; | 160 | res = -ENODATA; |
160 | goto Unlock; | 161 | goto Unlock; |
161 | } | 162 | } |
162 | res = snapshot_read_next(&data->handle, count); | 163 | if (!pg_offp) { /* on page boundary? */ |
163 | if (res > 0) { | 164 | res = snapshot_read_next(&data->handle); |
164 | if (copy_to_user(buf, data_of(data->handle), res)) | 165 | if (res <= 0) |
165 | res = -EFAULT; | 166 | goto Unlock; |
166 | else | 167 | } else { |
167 | *offp = data->handle.offset; | 168 | res = PAGE_SIZE - pg_offp; |
168 | } | 169 | } |
169 | 170 | ||
171 | res = simple_read_from_buffer(buf, count, &pg_offp, | ||
172 | data_of(data->handle), res); | ||
173 | if (res > 0) | ||
174 | *offp += res; | ||
175 | |||
170 | Unlock: | 176 | Unlock: |
171 | mutex_unlock(&pm_mutex); | 177 | mutex_unlock(&pm_mutex); |
172 | 178 | ||
@@ -178,18 +184,25 @@ static ssize_t snapshot_write(struct file *filp, const char __user *buf, | |||
178 | { | 184 | { |
179 | struct snapshot_data *data; | 185 | struct snapshot_data *data; |
180 | ssize_t res; | 186 | ssize_t res; |
187 | loff_t pg_offp = *offp & ~PAGE_MASK; | ||
181 | 188 | ||
182 | mutex_lock(&pm_mutex); | 189 | mutex_lock(&pm_mutex); |
183 | 190 | ||
184 | data = filp->private_data; | 191 | data = filp->private_data; |
185 | res = snapshot_write_next(&data->handle, count); | 192 | |
186 | if (res > 0) { | 193 | if (!pg_offp) { |
187 | if (copy_from_user(data_of(data->handle), buf, res)) | 194 | res = snapshot_write_next(&data->handle); |
188 | res = -EFAULT; | 195 | if (res <= 0) |
189 | else | 196 | goto unlock; |
190 | *offp = data->handle.offset; | 197 | } else { |
198 | res = PAGE_SIZE - pg_offp; | ||
191 | } | 199 | } |
192 | 200 | ||
201 | res = simple_write_to_buffer(data_of(data->handle), res, &pg_offp, | ||
202 | buf, count); | ||
203 | if (res > 0) | ||
204 | *offp += res; | ||
205 | unlock: | ||
193 | mutex_unlock(&pm_mutex); | 206 | mutex_unlock(&pm_mutex); |
194 | 207 | ||
195 | return res; | 208 | return res; |