diff options
author | Ingo Molnar <mingo@elte.hu> | 2008-08-11 07:27:47 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-08-11 07:27:47 -0400 |
commit | c4c0c56a7a85ed5725786219e4fbca7e840b1531 (patch) | |
tree | c9d6b35a571fd5e80ddf5bf4a60142480eaa18d8 /kernel/power | |
parent | 5127bed588a2f8f3a1f732de2a8a190b7df5dce3 (diff) | |
parent | 796aadeb1b2db9b5d463946766c5bbfd7717158c (diff) |
Merge branch 'linus' into core/rcu
Diffstat (limited to 'kernel/power')
-rw-r--r-- | kernel/power/Kconfig | 13 | ||||
-rw-r--r-- | kernel/power/main.c | 201 | ||||
-rw-r--r-- | kernel/power/power.h | 2 | ||||
-rw-r--r-- | kernel/power/poweroff.c | 4 | ||||
-rw-r--r-- | kernel/power/process.c | 2 | ||||
-rw-r--r-- | kernel/power/snapshot.c | 88 |
6 files changed, 237 insertions, 73 deletions
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index b45da40e8d25..dcd165f92a88 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig | |||
@@ -82,7 +82,7 @@ config PM_SLEEP_SMP | |||
82 | 82 | ||
83 | config PM_SLEEP | 83 | config PM_SLEEP |
84 | bool | 84 | bool |
85 | depends on SUSPEND || HIBERNATION | 85 | depends on SUSPEND || HIBERNATION || XEN_SAVE_RESTORE |
86 | default y | 86 | default y |
87 | 87 | ||
88 | config SUSPEND | 88 | config SUSPEND |
@@ -94,6 +94,17 @@ config SUSPEND | |||
94 | powered and thus its contents are preserved, such as the | 94 | powered and thus its contents are preserved, such as the |
95 | suspend-to-RAM state (e.g. the ACPI S3 state). | 95 | suspend-to-RAM state (e.g. the ACPI S3 state). |
96 | 96 | ||
97 | config PM_TEST_SUSPEND | ||
98 | bool "Test suspend/resume and wakealarm during bootup" | ||
99 | depends on SUSPEND && PM_DEBUG && RTC_LIB=y | ||
100 | ---help--- | ||
101 | This option will let you suspend your machine during bootup, and | ||
102 | make it wake up a few seconds later using an RTC wakeup alarm. | ||
103 | Enable this with a kernel parameter like "test_suspend=mem". | ||
104 | |||
105 | You probably want to have your system's RTC driver statically | ||
106 | linked, ensuring that it's available when this test runs. | ||
107 | |||
97 | config SUSPEND_FREEZER | 108 | config SUSPEND_FREEZER |
98 | bool "Enable freezer for suspend to RAM/standby" \ | 109 | bool "Enable freezer for suspend to RAM/standby" \ |
99 | if ARCH_WANTS_FREEZER_CONTROL || BROKEN | 110 | if ARCH_WANTS_FREEZER_CONTROL || BROKEN |
diff --git a/kernel/power/main.c b/kernel/power/main.c index 3398f4651aa1..0b7476f5d2a6 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c | |||
@@ -132,6 +132,61 @@ static inline int suspend_test(int level) { return 0; } | |||
132 | 132 | ||
133 | #ifdef CONFIG_SUSPEND | 133 | #ifdef CONFIG_SUSPEND |
134 | 134 | ||
135 | #ifdef CONFIG_PM_TEST_SUSPEND | ||
136 | |||
137 | /* | ||
138 | * We test the system suspend code by setting an RTC wakealarm a short | ||
139 | * time in the future, then suspending. Suspending the devices won't | ||
140 | * normally take long ... some systems only need a few milliseconds. | ||
141 | * | ||
142 | * The time it takes is system-specific though, so when we test this | ||
143 | * during system bootup we allow a LOT of time. | ||
144 | */ | ||
145 | #define TEST_SUSPEND_SECONDS 5 | ||
146 | |||
147 | static unsigned long suspend_test_start_time; | ||
148 | |||
149 | static void suspend_test_start(void) | ||
150 | { | ||
151 | /* FIXME Use better timebase than "jiffies", ideally a clocksource. | ||
152 | * What we want is a hardware counter that will work correctly even | ||
153 | * during the irqs-are-off stages of the suspend/resume cycle... | ||
154 | */ | ||
155 | suspend_test_start_time = jiffies; | ||
156 | } | ||
157 | |||
158 | static void suspend_test_finish(const char *label) | ||
159 | { | ||
160 | long nj = jiffies - suspend_test_start_time; | ||
161 | unsigned msec; | ||
162 | |||
163 | msec = jiffies_to_msecs(abs(nj)); | ||
164 | pr_info("PM: %s took %d.%03d seconds\n", label, | ||
165 | msec / 1000, msec % 1000); | ||
166 | |||
167 | /* Warning on suspend means the RTC alarm period needs to be | ||
168 | * larger -- the system was sooo slooowwww to suspend that the | ||
169 | * alarm (should have) fired before the system went to sleep! | ||
170 | * | ||
171 | * Warning on either suspend or resume also means the system | ||
172 | * has some performance issues. The stack dump of a WARN_ON | ||
173 | * is more likely to get the right attention than a printk... | ||
174 | */ | ||
175 | WARN_ON(msec > (TEST_SUSPEND_SECONDS * 1000)); | ||
176 | } | ||
177 | |||
178 | #else | ||
179 | |||
180 | static void suspend_test_start(void) | ||
181 | { | ||
182 | } | ||
183 | |||
184 | static void suspend_test_finish(const char *label) | ||
185 | { | ||
186 | } | ||
187 | |||
188 | #endif | ||
189 | |||
135 | /* This is just an arbitrary number */ | 190 | /* This is just an arbitrary number */ |
136 | #define FREE_PAGE_NUMBER (100) | 191 | #define FREE_PAGE_NUMBER (100) |
137 | 192 | ||
@@ -266,12 +321,13 @@ int suspend_devices_and_enter(suspend_state_t state) | |||
266 | goto Close; | 321 | goto Close; |
267 | } | 322 | } |
268 | suspend_console(); | 323 | suspend_console(); |
324 | suspend_test_start(); | ||
269 | error = device_suspend(PMSG_SUSPEND); | 325 | error = device_suspend(PMSG_SUSPEND); |
270 | if (error) { | 326 | if (error) { |
271 | printk(KERN_ERR "PM: Some devices failed to suspend\n"); | 327 | printk(KERN_ERR "PM: Some devices failed to suspend\n"); |
272 | goto Recover_platform; | 328 | goto Recover_platform; |
273 | } | 329 | } |
274 | 330 | suspend_test_finish("suspend devices"); | |
275 | if (suspend_test(TEST_DEVICES)) | 331 | if (suspend_test(TEST_DEVICES)) |
276 | goto Recover_platform; | 332 | goto Recover_platform; |
277 | 333 | ||
@@ -293,7 +349,9 @@ int suspend_devices_and_enter(suspend_state_t state) | |||
293 | if (suspend_ops->finish) | 349 | if (suspend_ops->finish) |
294 | suspend_ops->finish(); | 350 | suspend_ops->finish(); |
295 | Resume_devices: | 351 | Resume_devices: |
352 | suspend_test_start(); | ||
296 | device_resume(PMSG_RESUME); | 353 | device_resume(PMSG_RESUME); |
354 | suspend_test_finish("resume devices"); | ||
297 | resume_console(); | 355 | resume_console(); |
298 | Close: | 356 | Close: |
299 | if (suspend_ops->end) | 357 | if (suspend_ops->end) |
@@ -521,3 +579,144 @@ static int __init pm_init(void) | |||
521 | } | 579 | } |
522 | 580 | ||
523 | core_initcall(pm_init); | 581 | core_initcall(pm_init); |
582 | |||
583 | |||
584 | #ifdef CONFIG_PM_TEST_SUSPEND | ||
585 | |||
586 | #include <linux/rtc.h> | ||
587 | |||
588 | /* | ||
589 | * To test system suspend, we need a hands-off mechanism to resume the | ||
590 | * system. RTCs wake alarms are a common self-contained mechanism. | ||
591 | */ | ||
592 | |||
593 | static void __init test_wakealarm(struct rtc_device *rtc, suspend_state_t state) | ||
594 | { | ||
595 | static char err_readtime[] __initdata = | ||
596 | KERN_ERR "PM: can't read %s time, err %d\n"; | ||
597 | static char err_wakealarm [] __initdata = | ||
598 | KERN_ERR "PM: can't set %s wakealarm, err %d\n"; | ||
599 | static char err_suspend[] __initdata = | ||
600 | KERN_ERR "PM: suspend test failed, error %d\n"; | ||
601 | static char info_test[] __initdata = | ||
602 | KERN_INFO "PM: test RTC wakeup from '%s' suspend\n"; | ||
603 | |||
604 | unsigned long now; | ||
605 | struct rtc_wkalrm alm; | ||
606 | int status; | ||
607 | |||
608 | /* this may fail if the RTC hasn't been initialized */ | ||
609 | status = rtc_read_time(rtc, &alm.time); | ||
610 | if (status < 0) { | ||
611 | printk(err_readtime, rtc->dev.bus_id, status); | ||
612 | return; | ||
613 | } | ||
614 | rtc_tm_to_time(&alm.time, &now); | ||
615 | |||
616 | memset(&alm, 0, sizeof alm); | ||
617 | rtc_time_to_tm(now + TEST_SUSPEND_SECONDS, &alm.time); | ||
618 | alm.enabled = true; | ||
619 | |||
620 | status = rtc_set_alarm(rtc, &alm); | ||
621 | if (status < 0) { | ||
622 | printk(err_wakealarm, rtc->dev.bus_id, status); | ||
623 | return; | ||
624 | } | ||
625 | |||
626 | if (state == PM_SUSPEND_MEM) { | ||
627 | printk(info_test, pm_states[state]); | ||
628 | status = pm_suspend(state); | ||
629 | if (status == -ENODEV) | ||
630 | state = PM_SUSPEND_STANDBY; | ||
631 | } | ||
632 | if (state == PM_SUSPEND_STANDBY) { | ||
633 | printk(info_test, pm_states[state]); | ||
634 | status = pm_suspend(state); | ||
635 | } | ||
636 | if (status < 0) | ||
637 | printk(err_suspend, status); | ||
638 | |||
639 | /* Some platforms can't detect that the alarm triggered the | ||
640 | * wakeup, or (accordingly) disable it after it afterwards. | ||
641 | * It's supposed to give oneshot behavior; cope. | ||
642 | */ | ||
643 | alm.enabled = false; | ||
644 | rtc_set_alarm(rtc, &alm); | ||
645 | } | ||
646 | |||
647 | static int __init has_wakealarm(struct device *dev, void *name_ptr) | ||
648 | { | ||
649 | struct rtc_device *candidate = to_rtc_device(dev); | ||
650 | |||
651 | if (!candidate->ops->set_alarm) | ||
652 | return 0; | ||
653 | if (!device_may_wakeup(candidate->dev.parent)) | ||
654 | return 0; | ||
655 | |||
656 | *(char **)name_ptr = dev->bus_id; | ||
657 | return 1; | ||
658 | } | ||
659 | |||
660 | /* | ||
661 | * Kernel options like "test_suspend=mem" force suspend/resume sanity tests | ||
662 | * at startup time. They're normally disabled, for faster boot and because | ||
663 | * we can't know which states really work on this particular system. | ||
664 | */ | ||
665 | static suspend_state_t test_state __initdata = PM_SUSPEND_ON; | ||
666 | |||
667 | static char warn_bad_state[] __initdata = | ||
668 | KERN_WARNING "PM: can't test '%s' suspend state\n"; | ||
669 | |||
670 | static int __init setup_test_suspend(char *value) | ||
671 | { | ||
672 | unsigned i; | ||
673 | |||
674 | /* "=mem" ==> "mem" */ | ||
675 | value++; | ||
676 | for (i = 0; i < PM_SUSPEND_MAX; i++) { | ||
677 | if (!pm_states[i]) | ||
678 | continue; | ||
679 | if (strcmp(pm_states[i], value) != 0) | ||
680 | continue; | ||
681 | test_state = (__force suspend_state_t) i; | ||
682 | return 0; | ||
683 | } | ||
684 | printk(warn_bad_state, value); | ||
685 | return 0; | ||
686 | } | ||
687 | __setup("test_suspend", setup_test_suspend); | ||
688 | |||
689 | static int __init test_suspend(void) | ||
690 | { | ||
691 | static char warn_no_rtc[] __initdata = | ||
692 | KERN_WARNING "PM: no wakealarm-capable RTC driver is ready\n"; | ||
693 | |||
694 | char *pony = NULL; | ||
695 | struct rtc_device *rtc = NULL; | ||
696 | |||
697 | /* PM is initialized by now; is that state testable? */ | ||
698 | if (test_state == PM_SUSPEND_ON) | ||
699 | goto done; | ||
700 | if (!valid_state(test_state)) { | ||
701 | printk(warn_bad_state, pm_states[test_state]); | ||
702 | goto done; | ||
703 | } | ||
704 | |||
705 | /* RTCs have initialized by now too ... can we use one? */ | ||
706 | class_find_device(rtc_class, NULL, &pony, has_wakealarm); | ||
707 | if (pony) | ||
708 | rtc = rtc_class_open(pony); | ||
709 | if (!rtc) { | ||
710 | printk(warn_no_rtc); | ||
711 | goto done; | ||
712 | } | ||
713 | |||
714 | /* go for it */ | ||
715 | test_wakealarm(rtc, test_state); | ||
716 | rtc_class_close(rtc); | ||
717 | done: | ||
718 | return 0; | ||
719 | } | ||
720 | late_initcall(test_suspend); | ||
721 | |||
722 | #endif /* CONFIG_PM_TEST_SUSPEND */ | ||
diff --git a/kernel/power/power.h b/kernel/power/power.h index 700f44ec8406..acc0c101dbd5 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h | |||
@@ -53,8 +53,6 @@ extern int hibernation_platform_enter(void); | |||
53 | 53 | ||
54 | extern int pfn_is_nosave(unsigned long); | 54 | extern int pfn_is_nosave(unsigned long); |
55 | 55 | ||
56 | extern struct mutex pm_mutex; | ||
57 | |||
58 | #define power_attr(_name) \ | 56 | #define power_attr(_name) \ |
59 | static struct kobj_attribute _name##_attr = { \ | 57 | static struct kobj_attribute _name##_attr = { \ |
60 | .attr = { \ | 58 | .attr = { \ |
diff --git a/kernel/power/poweroff.c b/kernel/power/poweroff.c index 678ec736076b..72016f051477 100644 --- a/kernel/power/poweroff.c +++ b/kernel/power/poweroff.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <linux/pm.h> | 10 | #include <linux/pm.h> |
11 | #include <linux/workqueue.h> | 11 | #include <linux/workqueue.h> |
12 | #include <linux/reboot.h> | 12 | #include <linux/reboot.h> |
13 | #include <linux/cpumask.h> | ||
13 | 14 | ||
14 | /* | 15 | /* |
15 | * When the user hits Sys-Rq o to power down the machine this is the | 16 | * When the user hits Sys-Rq o to power down the machine this is the |
@@ -25,7 +26,8 @@ static DECLARE_WORK(poweroff_work, do_poweroff); | |||
25 | 26 | ||
26 | static void handle_poweroff(int key, struct tty_struct *tty) | 27 | static void handle_poweroff(int key, struct tty_struct *tty) |
27 | { | 28 | { |
28 | schedule_work(&poweroff_work); | 29 | /* run sysrq poweroff on boot cpu */ |
30 | schedule_work_on(first_cpu(cpu_online_map), &poweroff_work); | ||
29 | } | 31 | } |
30 | 32 | ||
31 | static struct sysrq_key_op sysrq_poweroff_op = { | 33 | static struct sysrq_key_op sysrq_poweroff_op = { |
diff --git a/kernel/power/process.c b/kernel/power/process.c index 5fb87652f214..278946aecaf0 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c | |||
@@ -149,7 +149,7 @@ static int try_to_freeze_tasks(bool sig_only) | |||
149 | unsigned long end_time; | 149 | unsigned long end_time; |
150 | unsigned int todo; | 150 | unsigned int todo; |
151 | struct timeval start, end; | 151 | struct timeval start, end; |
152 | s64 elapsed_csecs64; | 152 | u64 elapsed_csecs64; |
153 | unsigned int elapsed_csecs; | 153 | unsigned int elapsed_csecs; |
154 | 154 | ||
155 | do_gettimeofday(&start); | 155 | do_gettimeofday(&start); |
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 5f91a07c4eac..5d2ab836e998 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c | |||
@@ -205,8 +205,7 @@ static void chain_free(struct chain_allocator *ca, int clear_page_nosave) | |||
205 | * objects. The main list's elements are of type struct zone_bitmap | 205 | * objects. The main list's elements are of type struct zone_bitmap |
206 | * and each of them corresonds to one zone. For each zone bitmap | 206 | * and each of them corresonds to one zone. For each zone bitmap |
207 | * object there is a list of objects of type struct bm_block that | 207 | * object there is a list of objects of type struct bm_block that |
208 | * represent each blocks of bit chunks in which information is | 208 | * represent each blocks of bitmap in which information is stored. |
209 | * stored. | ||
210 | * | 209 | * |
211 | * struct memory_bitmap contains a pointer to the main list of zone | 210 | * struct memory_bitmap contains a pointer to the main list of zone |
212 | * bitmap objects, a struct bm_position used for browsing the bitmap, | 211 | * bitmap objects, a struct bm_position used for browsing the bitmap, |
@@ -224,26 +223,27 @@ static void chain_free(struct chain_allocator *ca, int clear_page_nosave) | |||
224 | * pfns that correspond to the start and end of the represented zone. | 223 | * pfns that correspond to the start and end of the represented zone. |
225 | * | 224 | * |
226 | * struct bm_block contains a pointer to the memory page in which | 225 | * struct bm_block contains a pointer to the memory page in which |
227 | * information is stored (in the form of a block of bit chunks | 226 | * information is stored (in the form of a block of bitmap) |
228 | * of type unsigned long each). It also contains the pfns that | 227 | * It also contains the pfns that correspond to the start and end of |
229 | * correspond to the start and end of the represented memory area and | 228 | * the represented memory area. |
230 | * the number of bit chunks in the block. | ||
231 | */ | 229 | */ |
232 | 230 | ||
233 | #define BM_END_OF_MAP (~0UL) | 231 | #define BM_END_OF_MAP (~0UL) |
234 | 232 | ||
235 | #define BM_CHUNKS_PER_BLOCK (PAGE_SIZE / sizeof(long)) | ||
236 | #define BM_BITS_PER_CHUNK (sizeof(long) << 3) | ||
237 | #define BM_BITS_PER_BLOCK (PAGE_SIZE << 3) | 233 | #define BM_BITS_PER_BLOCK (PAGE_SIZE << 3) |
238 | 234 | ||
239 | struct bm_block { | 235 | struct bm_block { |
240 | struct bm_block *next; /* next element of the list */ | 236 | struct bm_block *next; /* next element of the list */ |
241 | unsigned long start_pfn; /* pfn represented by the first bit */ | 237 | unsigned long start_pfn; /* pfn represented by the first bit */ |
242 | unsigned long end_pfn; /* pfn represented by the last bit plus 1 */ | 238 | unsigned long end_pfn; /* pfn represented by the last bit plus 1 */ |
243 | unsigned int size; /* number of bit chunks */ | 239 | unsigned long *data; /* bitmap representing pages */ |
244 | unsigned long *data; /* chunks of bits representing pages */ | ||
245 | }; | 240 | }; |
246 | 241 | ||
242 | static inline unsigned long bm_block_bits(struct bm_block *bb) | ||
243 | { | ||
244 | return bb->end_pfn - bb->start_pfn; | ||
245 | } | ||
246 | |||
247 | struct zone_bitmap { | 247 | struct zone_bitmap { |
248 | struct zone_bitmap *next; /* next element of the list */ | 248 | struct zone_bitmap *next; /* next element of the list */ |
249 | unsigned long start_pfn; /* minimal pfn in this zone */ | 249 | unsigned long start_pfn; /* minimal pfn in this zone */ |
@@ -257,7 +257,6 @@ struct zone_bitmap { | |||
257 | struct bm_position { | 257 | struct bm_position { |
258 | struct zone_bitmap *zone_bm; | 258 | struct zone_bitmap *zone_bm; |
259 | struct bm_block *block; | 259 | struct bm_block *block; |
260 | int chunk; | ||
261 | int bit; | 260 | int bit; |
262 | }; | 261 | }; |
263 | 262 | ||
@@ -272,12 +271,6 @@ struct memory_bitmap { | |||
272 | 271 | ||
273 | /* Functions that operate on memory bitmaps */ | 272 | /* Functions that operate on memory bitmaps */ |
274 | 273 | ||
275 | static inline void memory_bm_reset_chunk(struct memory_bitmap *bm) | ||
276 | { | ||
277 | bm->cur.chunk = 0; | ||
278 | bm->cur.bit = -1; | ||
279 | } | ||
280 | |||
281 | static void memory_bm_position_reset(struct memory_bitmap *bm) | 274 | static void memory_bm_position_reset(struct memory_bitmap *bm) |
282 | { | 275 | { |
283 | struct zone_bitmap *zone_bm; | 276 | struct zone_bitmap *zone_bm; |
@@ -285,7 +278,7 @@ static void memory_bm_position_reset(struct memory_bitmap *bm) | |||
285 | zone_bm = bm->zone_bm_list; | 278 | zone_bm = bm->zone_bm_list; |
286 | bm->cur.zone_bm = zone_bm; | 279 | bm->cur.zone_bm = zone_bm; |
287 | bm->cur.block = zone_bm->bm_blocks; | 280 | bm->cur.block = zone_bm->bm_blocks; |
288 | memory_bm_reset_chunk(bm); | 281 | bm->cur.bit = 0; |
289 | } | 282 | } |
290 | 283 | ||
291 | static void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free); | 284 | static void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free); |
@@ -394,12 +387,10 @@ memory_bm_create(struct memory_bitmap *bm, gfp_t gfp_mask, int safe_needed) | |||
394 | bb->start_pfn = pfn; | 387 | bb->start_pfn = pfn; |
395 | if (nr >= BM_BITS_PER_BLOCK) { | 388 | if (nr >= BM_BITS_PER_BLOCK) { |
396 | pfn += BM_BITS_PER_BLOCK; | 389 | pfn += BM_BITS_PER_BLOCK; |
397 | bb->size = BM_CHUNKS_PER_BLOCK; | ||
398 | nr -= BM_BITS_PER_BLOCK; | 390 | nr -= BM_BITS_PER_BLOCK; |
399 | } else { | 391 | } else { |
400 | /* This is executed only once in the loop */ | 392 | /* This is executed only once in the loop */ |
401 | pfn += nr; | 393 | pfn += nr; |
402 | bb->size = DIV_ROUND_UP(nr, BM_BITS_PER_CHUNK); | ||
403 | } | 394 | } |
404 | bb->end_pfn = pfn; | 395 | bb->end_pfn = pfn; |
405 | bb = bb->next; | 396 | bb = bb->next; |
@@ -478,8 +469,8 @@ static int memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn, | |||
478 | } | 469 | } |
479 | zone_bm->cur_block = bb; | 470 | zone_bm->cur_block = bb; |
480 | pfn -= bb->start_pfn; | 471 | pfn -= bb->start_pfn; |
481 | *bit_nr = pfn % BM_BITS_PER_CHUNK; | 472 | *bit_nr = pfn; |
482 | *addr = bb->data + pfn / BM_BITS_PER_CHUNK; | 473 | *addr = bb->data; |
483 | return 0; | 474 | return 0; |
484 | } | 475 | } |
485 | 476 | ||
@@ -528,36 +519,6 @@ static int memory_bm_test_bit(struct memory_bitmap *bm, unsigned long pfn) | |||
528 | return test_bit(bit, addr); | 519 | return test_bit(bit, addr); |
529 | } | 520 | } |
530 | 521 | ||
531 | /* Two auxiliary functions for memory_bm_next_pfn */ | ||
532 | |||
533 | /* Find the first set bit in the given chunk, if there is one */ | ||
534 | |||
535 | static inline int next_bit_in_chunk(int bit, unsigned long *chunk_p) | ||
536 | { | ||
537 | bit++; | ||
538 | while (bit < BM_BITS_PER_CHUNK) { | ||
539 | if (test_bit(bit, chunk_p)) | ||
540 | return bit; | ||
541 | |||
542 | bit++; | ||
543 | } | ||
544 | return -1; | ||
545 | } | ||
546 | |||
547 | /* Find a chunk containing some bits set in given block of bits */ | ||
548 | |||
549 | static inline int next_chunk_in_block(int n, struct bm_block *bb) | ||
550 | { | ||
551 | n++; | ||
552 | while (n < bb->size) { | ||
553 | if (bb->data[n]) | ||
554 | return n; | ||
555 | |||
556 | n++; | ||
557 | } | ||
558 | return -1; | ||
559 | } | ||
560 | |||
561 | /** | 522 | /** |
562 | * memory_bm_next_pfn - find the pfn that corresponds to the next set bit | 523 | * memory_bm_next_pfn - find the pfn that corresponds to the next set bit |
563 | * in the bitmap @bm. If the pfn cannot be found, BM_END_OF_MAP is | 524 | * in the bitmap @bm. If the pfn cannot be found, BM_END_OF_MAP is |
@@ -571,40 +532,33 @@ static unsigned long memory_bm_next_pfn(struct memory_bitmap *bm) | |||
571 | { | 532 | { |
572 | struct zone_bitmap *zone_bm; | 533 | struct zone_bitmap *zone_bm; |
573 | struct bm_block *bb; | 534 | struct bm_block *bb; |
574 | int chunk; | ||
575 | int bit; | 535 | int bit; |
576 | 536 | ||
577 | do { | 537 | do { |
578 | bb = bm->cur.block; | 538 | bb = bm->cur.block; |
579 | do { | 539 | do { |
580 | chunk = bm->cur.chunk; | ||
581 | bit = bm->cur.bit; | 540 | bit = bm->cur.bit; |
582 | do { | 541 | bit = find_next_bit(bb->data, bm_block_bits(bb), bit); |
583 | bit = next_bit_in_chunk(bit, bb->data + chunk); | 542 | if (bit < bm_block_bits(bb)) |
584 | if (bit >= 0) | 543 | goto Return_pfn; |
585 | goto Return_pfn; | 544 | |
586 | |||
587 | chunk = next_chunk_in_block(chunk, bb); | ||
588 | bit = -1; | ||
589 | } while (chunk >= 0); | ||
590 | bb = bb->next; | 545 | bb = bb->next; |
591 | bm->cur.block = bb; | 546 | bm->cur.block = bb; |
592 | memory_bm_reset_chunk(bm); | 547 | bm->cur.bit = 0; |
593 | } while (bb); | 548 | } while (bb); |
594 | zone_bm = bm->cur.zone_bm->next; | 549 | zone_bm = bm->cur.zone_bm->next; |
595 | if (zone_bm) { | 550 | if (zone_bm) { |
596 | bm->cur.zone_bm = zone_bm; | 551 | bm->cur.zone_bm = zone_bm; |
597 | bm->cur.block = zone_bm->bm_blocks; | 552 | bm->cur.block = zone_bm->bm_blocks; |
598 | memory_bm_reset_chunk(bm); | 553 | bm->cur.bit = 0; |
599 | } | 554 | } |
600 | } while (zone_bm); | 555 | } while (zone_bm); |
601 | memory_bm_position_reset(bm); | 556 | memory_bm_position_reset(bm); |
602 | return BM_END_OF_MAP; | 557 | return BM_END_OF_MAP; |
603 | 558 | ||
604 | Return_pfn: | 559 | Return_pfn: |
605 | bm->cur.chunk = chunk; | 560 | bm->cur.bit = bit + 1; |
606 | bm->cur.bit = bit; | 561 | return bb->start_pfn + bit; |
607 | return bb->start_pfn + chunk * BM_BITS_PER_CHUNK + bit; | ||
608 | } | 562 | } |
609 | 563 | ||
610 | /** | 564 | /** |