diff options
Diffstat (limited to 'kernel/power')
| -rw-r--r-- | kernel/power/Kconfig | 11 | ||||
| -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, 236 insertions, 72 deletions
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 59dfdf1e1d20..dcd165f92a88 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig | |||
| @@ -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 | /** |
