diff options
author | Andrew Morton <akpm@osdl.org> | 2006-09-26 02:32:42 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-09-26 11:48:58 -0400 |
commit | ab954160350c91c77ae03740ef90458c3ad5412c (patch) | |
tree | 28f99d765c2c6d497a1f5543b1867875cd6102a5 | |
parent | 3a4f7577c9ef393ca80c783f02ffbc125de771c7 (diff) |
[PATCH] swsusp: write speedup
Switch the swsusp writeout code from 4k-at-a-time to 4MB-at-a-time.
Crufty old PIII testbox:
12.9 MB/s -> 20.9 MB/s
Sony Vaio:
14.7 MB/s -> 26.5 MB/s
The implementation is crude. A better one would use larger BIOs, but wouldn't
gain any performance.
The memcpys will be mostly pipelined with the IO and basically come for free.
The ENOMEM path has not been tested. It should be.
Cc: Pavel Machek <pavel@ucw.cz>
Cc: "Rafael J. Wysocki" <rjw@sisk.pl>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-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 | } |