* pm-sleep: PM / hibernate: Restore processor state before using per-CPU variables x86/power/64: Always create temporary identity mapping correctly * pm-cpufreq: cpufreq: powernv: Fix crash in gpstate_timer_handler()
Unified · Split
| @@ -5,10 +5,10 @@ struct x86_mapping_info { · 4 unmodified lines | |||
| 5 | void *(*alloc_pgt_page)(void *); /* allocate buf for page table */ | 5 | void *(*alloc_pgt_page)(void *); /* allocate buf for page table */ |
| 6 | void *context; /* context for alloc_pgt_page */ | 6 | void *context; /* context for alloc_pgt_page */ |
| 7 | unsigned long pmd_flag; /* page flag for PMD entry */ | 7 | unsigned long pmd_flag; /* page flag for PMD entry */ |
| 8 | bool kernel_mapping; /* kernel mapping or ident mapping */ | 8 | unsigned long offset; /* ident mapping offset */ |
| 9 | }; | 9 | }; |
| 10 | 10 | ||
| 11 | int kernel_ident_mapping_init(struct x86_mapping_info *info, pgd_t *pgd_page, | 11 | int kernel_ident_mapping_init(struct x86_mapping_info *info, pgd_t *pgd_page, |
| 12 | unsigned long addr, unsigned long end); | 12 | unsigned long pstart, unsigned long pend); |
| 13 | 13 | ||
| 14 | #endif /* _ASM_X86_INIT_H */ | 14 | #endif /* _ASM_X86_INIT_H */ |
| @@ -3,15 +3,17 @@ · 2 unmodified lines | |||
| 3 | * included by both the compressed kernel and the regular kernel. | 3 | * included by both the compressed kernel and the regular kernel. |
| 4 | */ | 4 | */ |
| 5 | 5 | ||
| 6 | static void ident_pmd_init(unsigned long pmd_flag, pmd_t *pmd_page, | 6 | static void ident_pmd_init(struct x86_mapping_info *info, pmd_t *pmd_page, |
| 7 | unsigned long addr, unsigned long end) | 7 | unsigned long addr, unsigned long end) |
| 8 | { | 8 | { |
| 9 | addr &= PMD_MASK; | 9 | addr &= PMD_MASK; |
| 10 | for (; addr < end; addr += PMD_SIZE) { | 10 | for (; addr < end; addr += PMD_SIZE) { |
| 11 | pmd_t *pmd = pmd_page + pmd_index(addr); | 11 | pmd_t *pmd = pmd_page + pmd_index(addr); |
| 12 | 12 | ||
| 13 | if (!pmd_present(*pmd)) | 13 | if (pmd_present(*pmd)) |
| 14 | set_pmd(pmd, __pmd(addr | pmd_flag)); | 14 | continue; |
| 15 | |||
| 16 | set_pmd(pmd, __pmd((addr - info->offset) | info->pmd_flag)); | ||
| 15 | } | 17 | } |
| 16 | } | 18 | } |
| 17 | 19 | ||
| @@ -30,13 +32,13 @@ static int ident_pud_init(struct x86_mapping_info *info, pud_t *pud_page, · 12 unmodified lines | |||
| 30 | 32 | ||
| 31 | if (pud_present(*pud)) { | 33 | if (pud_present(*pud)) { |
| 32 | pmd = pmd_offset(pud, 0); | 34 | pmd = pmd_offset(pud, 0); |
| 33 | ident_pmd_init(info->pmd_flag, pmd, addr, next); | 35 | ident_pmd_init(info, pmd, addr, next); |
| 34 | continue; | 36 | continue; |
| 35 | } | 37 | } |
| 36 | pmd = (pmd_t *)info->alloc_pgt_page(info->context); | 38 | pmd = (pmd_t *)info->alloc_pgt_page(info->context); |
| 37 | if (!pmd) | 39 | if (!pmd) |
| 38 | return -ENOMEM; | 40 | return -ENOMEM; |
| 39 | ident_pmd_init(info->pmd_flag, pmd, addr, next); | 41 | ident_pmd_init(info, pmd, addr, next); |
| 40 | set_pud(pud, __pud(__pa(pmd) | _KERNPG_TABLE)); | 42 | set_pud(pud, __pud(__pa(pmd) | _KERNPG_TABLE)); |
| 41 | } | 43 | } |
| 42 | 44 | ||
| @@ -44,14 +46,15 @@ static int ident_pud_init(struct x86_mapping_info *info, pud_t *pud_page, · 1 unmodified line | |||
| 44 | } | 46 | } |
| 45 | 47 | ||
| 46 | int kernel_ident_mapping_init(struct x86_mapping_info *info, pgd_t *pgd_page, | 48 | int kernel_ident_mapping_init(struct x86_mapping_info *info, pgd_t *pgd_page, |
| 47 | unsigned long addr, unsigned long end) | 49 | unsigned long pstart, unsigned long pend) |
| 48 | { | 50 | { |
| 51 | unsigned long addr = pstart + info->offset; | ||
| 52 | unsigned long end = pend + info->offset; | ||
| 49 | unsigned long next; | 53 | unsigned long next; |
| 50 | int result; | 54 | int result; |
| 51 | int off = info->kernel_mapping ? pgd_index(__PAGE_OFFSET) : 0; | ||
| 52 | 55 | ||
| 53 | for (; addr < end; addr = next) { | 56 | for (; addr < end; addr = next) { |
| 54 | pgd_t *pgd = pgd_page + pgd_index(addr) + off; | 57 | pgd_t *pgd = pgd_page + pgd_index(addr); |
| 55 | pud_t *pud; | 58 | pud_t *pud; |
| 56 | 59 | ||
| 57 | next = (addr & PGDIR_MASK) + PGDIR_SIZE; | 60 | next = (addr & PGDIR_MASK) + PGDIR_SIZE; |
| @@ -87,7 +87,7 @@ static int set_up_temporary_mappings(void) · 86 unmodified lines | |||
| 87 | struct x86_mapping_info info = { | 87 | struct x86_mapping_info info = { |
| 88 | .alloc_pgt_page = alloc_pgt_page, | 88 | .alloc_pgt_page = alloc_pgt_page, |
| 89 | .pmd_flag = __PAGE_KERNEL_LARGE_EXEC, | 89 | .pmd_flag = __PAGE_KERNEL_LARGE_EXEC, |
| 90 | .kernel_mapping = true, | 90 | .offset = __PAGE_OFFSET, |
| 91 | }; | 91 | }; |
| 92 | unsigned long mstart, mend; | 92 | unsigned long mstart, mend; |
| 93 | pgd_t *pgd; | 93 | pgd_t *pgd; |
| @@ -145,11 +145,30 @@ static struct powernv_pstate_info { · 144 unmodified lines | |||
| 145 | /* Use following macros for conversions between pstate_id and index */ | 145 | /* Use following macros for conversions between pstate_id and index */ |
| 146 | static inline int idx_to_pstate(unsigned int i) | 146 | static inline int idx_to_pstate(unsigned int i) |
| 147 | { | 147 | { |
| 148 | if (unlikely(i >= powernv_pstate_info.nr_pstates)) { | ||
| 149 | pr_warn_once("index %u is out of bound\n", i); | ||
| 150 | return powernv_freqs[powernv_pstate_info.nominal].driver_data; | ||
| 151 | } | ||
| 152 | |||
| 148 | return powernv_freqs[i].driver_data; | 153 | return powernv_freqs[i].driver_data; |
| 149 | } | 154 | } |
| 150 | 155 | ||
| 151 | static inline unsigned int pstate_to_idx(int pstate) | 156 | static inline unsigned int pstate_to_idx(int pstate) |
| 152 | { | 157 | { |
| 158 | int min = powernv_freqs[powernv_pstate_info.min].driver_data; | ||
| 159 | int max = powernv_freqs[powernv_pstate_info.max].driver_data; | ||
| 160 | |||
| 161 | if (min > 0) { | ||
| 162 | if (unlikely((pstate < max) || (pstate > min))) { | ||
| 163 | pr_warn_once("pstate %d is out of bound\n", pstate); | ||
| 164 | return powernv_pstate_info.nominal; | ||
| 165 | } | ||
| 166 | } else { | ||
| 167 | if (unlikely((pstate > max) || (pstate < min))) { | ||
| 168 | pr_warn_once("pstate %d is out of bound\n", pstate); | ||
| 169 | return powernv_pstate_info.nominal; | ||
| 170 | } | ||
| 171 | } | ||
| 153 | /* | 172 | /* |
| 154 | * abs() is deliberately used so that is works with | 173 | * abs() is deliberately used so that is works with |
| 155 | * both monotonically increasing and decreasing | 174 | * both monotonically increasing and decreasing |
| @@ -593,7 +612,7 @@ void gpstate_timer_handler(unsigned long data) · 437 unmodified lines | |||
| 593 | } else { | 612 | } else { |
| 594 | gpstate_idx = calc_global_pstate(gpstates->elapsed_time, | 613 | gpstate_idx = calc_global_pstate(gpstates->elapsed_time, |
| 595 | gpstates->highest_lpstate_idx, | 614 | gpstates->highest_lpstate_idx, |
| 596 | freq_data.pstate_id); | 615 | gpstates->last_lpstate_idx); |
| 597 | } | 616 | } |
| 598 | 617 | ||
| 599 | /* | 618 | /* |
| @@ -300,12 +300,12 @@ static int create_image(int platform_mode) · 299 unmodified lines | |||
| 300 | save_processor_state(); | 300 | save_processor_state(); |
| 301 | trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, true); | 301 | trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, true); |
| 302 | error = swsusp_arch_suspend(); | 302 | error = swsusp_arch_suspend(); |
| 303 | /* Restore control flow magically appears here */ | ||
| 304 | restore_processor_state(); | ||
| 303 | trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, false); | 305 | trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, false); |
| 304 | if (error) | 306 | if (error) |
| 305 | printk(KERN_ERR "PM: Error %d creating hibernation image\n", | 307 | printk(KERN_ERR "PM: Error %d creating hibernation image\n", |
| 306 | error); | 308 | error); |
| 307 | /* Restore control flow magically appears here */ | ||
| 308 | restore_processor_state(); | ||
| 309 | if (!in_suspend) | 309 | if (!in_suspend) |
| 310 | events_check_enabled = false; | 310 | events_check_enabled = false; |
| 311 | 311 | ||