diff options
| -rw-r--r-- | include/linux/swap.h | 5 | ||||
| -rw-r--r-- | kernel/power/swap.c | 93 | ||||
| -rw-r--r-- | mm/page_io.c | 25 |
3 files changed, 98 insertions, 25 deletions
diff --git a/include/linux/swap.h b/include/linux/swap.h index a2f5ad7c2d2e..3d434cbffe26 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h | |||
| @@ -12,6 +12,8 @@ | |||
| 12 | 12 | ||
| 13 | struct notifier_block; | 13 | struct notifier_block; |
| 14 | 14 | ||
| 15 | struct bio; | ||
| 16 | |||
| 15 | #define SWAP_FLAG_PREFER 0x8000 /* set if swap priority specified */ | 17 | #define SWAP_FLAG_PREFER 0x8000 /* set if swap priority specified */ |
| 16 | #define SWAP_FLAG_PRIO_MASK 0x7fff | 18 | #define SWAP_FLAG_PRIO_MASK 0x7fff |
| 17 | #define SWAP_FLAG_PRIO_SHIFT 0 | 19 | #define SWAP_FLAG_PRIO_SHIFT 0 |
| @@ -216,7 +218,8 @@ extern void swap_unplug_io_fn(struct backing_dev_info *, struct page *); | |||
| 216 | /* linux/mm/page_io.c */ | 218 | /* linux/mm/page_io.c */ |
| 217 | extern int swap_readpage(struct file *, struct page *); | 219 | extern int swap_readpage(struct file *, struct page *); |
| 218 | extern int swap_writepage(struct page *page, struct writeback_control *wbc); | 220 | extern int swap_writepage(struct page *page, struct writeback_control *wbc); |
| 219 | extern int rw_swap_page_sync(int, swp_entry_t, struct page *); | 221 | extern int rw_swap_page_sync(int rw, swp_entry_t entry, struct page *page, |
| 222 | struct bio **bio_chain); | ||
| 220 | 223 | ||
| 221 | /* linux/mm/swap_state.c */ | 224 | /* linux/mm/swap_state.c */ |
| 222 | extern struct address_space swapper_space; | 225 | extern struct address_space swapper_space; |
diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 79b66e734bdb..2dc883d361d5 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c | |||
| @@ -49,18 +49,16 @@ static int mark_swapfiles(swp_entry_t start) | |||
| 49 | { | 49 | { |
| 50 | int error; | 50 | int error; |
| 51 | 51 | ||
| 52 | rw_swap_page_sync(READ, | 52 | rw_swap_page_sync(READ, swp_entry(root_swap, 0), |
| 53 | swp_entry(root_swap, 0), | 53 | virt_to_page((unsigned long)&swsusp_header), NULL); |
| 54 | virt_to_page((unsigned long)&swsusp_header)); | ||
| 55 | if (!memcmp("SWAP-SPACE",swsusp_header.sig, 10) || | 54 | if (!memcmp("SWAP-SPACE",swsusp_header.sig, 10) || |
| 56 | !memcmp("SWAPSPACE2",swsusp_header.sig, 10)) { | 55 | !memcmp("SWAPSPACE2",swsusp_header.sig, 10)) { |
| 57 | memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10); | 56 | memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10); |
| 58 | memcpy(swsusp_header.sig,SWSUSP_SIG, 10); | 57 | memcpy(swsusp_header.sig,SWSUSP_SIG, 10); |
| 59 | swsusp_header.image = start; | 58 | swsusp_header.image = start; |
| 60 | error = rw_swap_page_sync(WRITE, | 59 | error = rw_swap_page_sync(WRITE, swp_entry(root_swap, 0), |
| 61 | swp_entry(root_swap, 0), | 60 | virt_to_page((unsigned long)&swsusp_header), |
| 62 | virt_to_page((unsigned long) | 61 | NULL); |
| 63 | &swsusp_header)); | ||
| 64 | } else { | 62 | } else { |
| 65 | pr_debug("swsusp: Partition is not swap space.\n"); | 63 | pr_debug("swsusp: Partition is not swap space.\n"); |
| 66 | error = -ENODEV; | 64 | error = -ENODEV; |
| @@ -88,16 +86,37 @@ static int swsusp_swap_check(void) /* This is called before saving image */ | |||
| 88 | * write_page - Write one page to given swap location. | 86 | * write_page - Write one page to given swap location. |
| 89 | * @buf: Address we're writing. | 87 | * @buf: Address we're writing. |
| 90 | * @offset: Offset of the swap page we're writing to. | 88 | * @offset: Offset of the swap page we're writing to. |
| 89 | * @bio_chain: Link the next write BIO here | ||
| 91 | */ | 90 | */ |
| 92 | 91 | ||
| 93 | static int write_page(void *buf, unsigned long offset) | 92 | static int write_page(void *buf, unsigned long offset, struct bio **bio_chain) |
| 94 | { | 93 | { |
| 95 | swp_entry_t entry; | 94 | swp_entry_t entry; |
| 96 | int error = -ENOSPC; | 95 | int error = -ENOSPC; |
| 97 | 96 | ||
| 98 | if (offset) { | 97 | if (offset) { |
| 98 | struct page *page = virt_to_page(buf); | ||
| 99 | |||
| 100 | if (bio_chain) { | ||
| 101 | /* | ||
| 102 | * Whether or not we successfully allocated a copy page, | ||
| 103 | * we take a ref on the page here. It gets undone in | ||
| 104 | * wait_on_bio_chain(). | ||
| 105 | */ | ||
| 106 | struct page *page_copy; | ||
| 107 | page_copy = alloc_page(GFP_ATOMIC); | ||
| 108 | if (page_copy == NULL) { | ||
| 109 | WARN_ON_ONCE(1); | ||
| 110 | bio_chain = NULL; /* Go synchronous */ | ||
| 111 | get_page(page); | ||
| 112 | } else { | ||
| 113 | memcpy(page_address(page_copy), | ||
| 114 | page_address(page), PAGE_SIZE); | ||
| 115 | page = page_copy; | ||
| 116 | } | ||
| 117 | } | ||
| 99 | entry = swp_entry(root_swap, offset); | 118 | entry = swp_entry(root_swap, offset); |
| 100 | error = rw_swap_page_sync(WRITE, entry, virt_to_page(buf)); | 119 | error = rw_swap_page_sync(WRITE, entry, page, bio_chain); |
| 101 | } | 120 | } |
| 102 | return error; | 121 | return error; |
| 103 | } | 122 | } |
| @@ -185,37 +204,68 @@ static int get_swap_writer(struct swap_map_handle *handle) | |||
| 185 | return 0; | 204 | return 0; |
| 186 | } | 205 | } |
| 187 | 206 | ||
| 188 | static int swap_write_page(struct swap_map_handle *handle, void *buf) | 207 | static int wait_on_bio_chain(struct bio **bio_chain) |
| 189 | { | 208 | { |
| 190 | int error; | 209 | struct bio *bio; |
| 210 | struct bio *next_bio; | ||
| 211 | int ret = 0; | ||
| 212 | |||
| 213 | if (bio_chain == NULL) | ||
| 214 | return 0; | ||
| 215 | |||
| 216 | bio = *bio_chain; | ||
| 217 | while (bio) { | ||
| 218 | struct page *page; | ||
| 219 | |||
| 220 | next_bio = bio->bi_private; | ||
| 221 | page = bio->bi_io_vec[0].bv_page; | ||
| 222 | wait_on_page_locked(page); | ||
| 223 | if (!PageUptodate(page) || PageError(page)) | ||
| 224 | ret = -EIO; | ||
| 225 | put_page(page); | ||
| 226 | bio_put(bio); | ||
| 227 | bio = next_bio; | ||
| 228 | } | ||
| 229 | *bio_chain = NULL; | ||
| 230 | return ret; | ||
| 231 | } | ||
| 232 | |||
| 233 | static int swap_write_page(struct swap_map_handle *handle, void *buf, | ||
| 234 | struct bio **bio_chain) | ||
| 235 | { | ||
| 236 | int error = 0; | ||
| 191 | unsigned long offset; | 237 | unsigned long offset; |
| 192 | 238 | ||
| 193 | if (!handle->cur) | 239 | if (!handle->cur) |
| 194 | return -EINVAL; | 240 | return -EINVAL; |
| 195 | offset = alloc_swap_page(root_swap, handle->bitmap); | 241 | offset = alloc_swap_page(root_swap, handle->bitmap); |
| 196 | error = write_page(buf, offset); | 242 | error = write_page(buf, offset, bio_chain); |
| 197 | if (error) | 243 | if (error) |
| 198 | return error; | 244 | return error; |
| 199 | handle->cur->entries[handle->k++] = offset; | 245 | handle->cur->entries[handle->k++] = offset; |
| 200 | if (handle->k >= MAP_PAGE_ENTRIES) { | 246 | if (handle->k >= MAP_PAGE_ENTRIES) { |
| 247 | error = wait_on_bio_chain(bio_chain); | ||
| 248 | if (error) | ||
| 249 | goto out; | ||
| 201 | offset = alloc_swap_page(root_swap, handle->bitmap); | 250 | offset = alloc_swap_page(root_swap, handle->bitmap); |
| 202 | if (!offset) | 251 | if (!offset) |
| 203 | return -ENOSPC; | 252 | return -ENOSPC; |
| 204 | handle->cur->next_swap = offset; | 253 | handle->cur->next_swap = offset; |
| 205 | error = write_page(handle->cur, handle->cur_swap); | 254 | error = write_page(handle->cur, handle->cur_swap, NULL); |
| 206 | if (error) | 255 | if (error) |
| 207 | return error; | 256 | goto out; |
| 208 | memset(handle->cur, 0, PAGE_SIZE); | 257 | memset(handle->cur, 0, PAGE_SIZE); |
| 209 | handle->cur_swap = offset; | 258 | handle->cur_swap = offset; |
| 210 | handle->k = 0; | 259 | handle->k = 0; |
| 211 | } | 260 | } |
| 212 | return 0; | 261 | out: |
| 262 | return error; | ||
| 213 | } | 263 | } |
| 214 | 264 | ||
| 215 | static int flush_swap_writer(struct swap_map_handle *handle) | 265 | static int flush_swap_writer(struct swap_map_handle *handle) |
| 216 | { | 266 | { |
| 217 | if (handle->cur && handle->cur_swap) | 267 | if (handle->cur && handle->cur_swap) |
| 218 | return write_page(handle->cur, handle->cur_swap); | 268 | return write_page(handle->cur, handle->cur_swap, NULL); |
| 219 | else | 269 | else |
| 220 | return -EINVAL; | 270 | return -EINVAL; |
| 221 | } | 271 | } |
| @@ -232,6 +282,8 @@ static int save_image(struct swap_map_handle *handle, | |||
| 232 | int ret; | 282 | int ret; |
| 233 | int error = 0; | 283 | int error = 0; |
| 234 | int nr_pages; | 284 | int nr_pages; |
| 285 | int err2; | ||
| 286 | struct bio *bio; | ||
| 235 | struct timeval start; | 287 | struct timeval start; |
| 236 | struct timeval stop; | 288 | struct timeval stop; |
| 237 | 289 | ||
| @@ -240,11 +292,13 @@ static int save_image(struct swap_map_handle *handle, | |||
| 240 | if (!m) | 292 | if (!m) |
| 241 | m = 1; | 293 | m = 1; |
| 242 | nr_pages = 0; | 294 | nr_pages = 0; |
| 295 | bio = NULL; | ||
| 243 | do_gettimeofday(&start); | 296 | do_gettimeofday(&start); |
| 244 | do { | 297 | do { |
| 245 | ret = snapshot_read_next(snapshot, PAGE_SIZE); | 298 | ret = snapshot_read_next(snapshot, PAGE_SIZE); |
| 246 | if (ret > 0) { | 299 | if (ret > 0) { |
| 247 | error = swap_write_page(handle, data_of(*snapshot)); | 300 | error = swap_write_page(handle, data_of(*snapshot), |
| 301 | &bio); | ||
| 248 | if (error) | 302 | if (error) |
| 249 | break; | 303 | break; |
| 250 | if (!(nr_pages % m)) | 304 | if (!(nr_pages % m)) |
| @@ -252,8 +306,11 @@ static int save_image(struct swap_map_handle *handle, | |||
| 252 | nr_pages++; | 306 | nr_pages++; |
| 253 | } | 307 | } |
| 254 | } while (ret > 0); | 308 | } while (ret > 0); |
| 309 | err2 = wait_on_bio_chain(&bio); | ||
| 255 | do_gettimeofday(&stop); | 310 | do_gettimeofday(&stop); |
| 256 | if (!error) | 311 | if (!error) |
| 312 | error = err2; | ||
| 313 | if (!error) | ||
| 257 | printk("\b\b\b\bdone\n"); | 314 | printk("\b\b\b\bdone\n"); |
| 258 | show_speed(&start, &stop, nr_to_write, "Wrote"); | 315 | show_speed(&start, &stop, nr_to_write, "Wrote"); |
| 259 | return error; | 316 | return error; |
| @@ -307,7 +364,7 @@ int swsusp_write(void) | |||
| 307 | error = get_swap_writer(&handle); | 364 | error = get_swap_writer(&handle); |
| 308 | if (!error) { | 365 | if (!error) { |
| 309 | unsigned long start = handle.cur_swap; | 366 | unsigned long start = handle.cur_swap; |
| 310 | error = swap_write_page(&handle, header); | 367 | error = swap_write_page(&handle, header, NULL); |
| 311 | if (!error) | 368 | if (!error) |
| 312 | error = save_image(&handle, &snapshot, | 369 | error = save_image(&handle, &snapshot, |
| 313 | header->pages - 1); | 370 | header->pages - 1); |
diff --git a/mm/page_io.c b/mm/page_io.c index d2f0a5783370..f46a9862b7ef 100644 --- a/mm/page_io.c +++ b/mm/page_io.c | |||
| @@ -156,10 +156,12 @@ out: | |||
| 156 | * We use end_swap_bio_read() even for writes, because it happens to do what | 156 | * We use end_swap_bio_read() even for writes, because it happens to do what |
| 157 | * we want. | 157 | * we want. |
| 158 | */ | 158 | */ |
| 159 | int rw_swap_page_sync(int rw, swp_entry_t entry, struct page *page) | 159 | int rw_swap_page_sync(int rw, swp_entry_t entry, struct page *page, |
| 160 | struct bio **bio_chain) | ||
| 160 | { | 161 | { |
| 161 | struct bio *bio; | 162 | struct bio *bio; |
| 162 | int ret = 0; | 163 | int ret = 0; |
| 164 | int bio_rw; | ||
| 163 | 165 | ||
| 164 | lock_page(page); | 166 | lock_page(page); |
| 165 | 167 | ||
| @@ -170,11 +172,22 @@ int rw_swap_page_sync(int rw, swp_entry_t entry, struct page *page) | |||
| 170 | goto out; | 172 | goto out; |
| 171 | } | 173 | } |
| 172 | 174 | ||
| 173 | submit_bio(rw | (1 << BIO_RW_SYNC), bio); | 175 | bio_rw = rw; |
| 174 | wait_on_page_locked(page); | 176 | if (!bio_chain) |
| 175 | 177 | bio_rw |= (1 << BIO_RW_SYNC); | |
| 176 | if (!PageUptodate(page) || PageError(page)) | 178 | if (bio_chain) |
| 177 | ret = -EIO; | 179 | bio_get(bio); |
| 180 | submit_bio(bio_rw, bio); | ||
| 181 | if (bio_chain == NULL) { | ||
| 182 | wait_on_page_locked(page); | ||
| 183 | |||
| 184 | if (!PageUptodate(page) || PageError(page)) | ||
| 185 | ret = -EIO; | ||
| 186 | } | ||
| 187 | if (bio_chain) { | ||
| 188 | bio->bi_private = *bio_chain; | ||
| 189 | *bio_chain = bio; | ||
| 190 | } | ||
| 178 | out: | 191 | out: |
| 179 | return ret; | 192 | return ret; |
| 180 | } | 193 | } |
