aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Morton <akpm@osdl.org>2006-09-26 02:32:42 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-09-26 11:48:58 -0400
commitab954160350c91c77ae03740ef90458c3ad5412c (patch)
tree28f99d765c2c6d497a1f5543b1867875cd6102a5
parent3a4f7577c9ef393ca80c783f02ffbc125de771c7 (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.h5
-rw-r--r--kernel/power/swap.c93
-rw-r--r--mm/page_io.c25
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
13struct notifier_block; 13struct notifier_block;
14 14
15struct 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 */
217extern int swap_readpage(struct file *, struct page *); 219extern int swap_readpage(struct file *, struct page *);
218extern int swap_writepage(struct page *page, struct writeback_control *wbc); 220extern int swap_writepage(struct page *page, struct writeback_control *wbc);
219extern int rw_swap_page_sync(int, swp_entry_t, struct page *); 221extern 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 */
222extern struct address_space swapper_space; 225extern 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
93static int write_page(void *buf, unsigned long offset) 92static 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
188static int swap_write_page(struct swap_map_handle *handle, void *buf) 207static 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
233static 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; 261out:
262 return error;
213} 263}
214 264
215static int flush_swap_writer(struct swap_map_handle *handle) 265static 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 */
159int rw_swap_page_sync(int rw, swp_entry_t entry, struct page *page) 159int 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 }
178out: 191out:
179 return ret; 192 return ret;
180} 193}