diff options
| author | Russell King <rmk+kernel@arm.linux.org.uk> | 2014-03-25 15:45:31 -0400 |
|---|---|---|
| committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2014-04-03 17:46:34 -0400 |
| commit | e26a9e00afc482b971afcaef1db8c9034d4d6d7c (patch) | |
| tree | 6ed907fccdafda9a6656794db0543692f7e5d46f | |
| parent | 83b3f64d46d1d9bf1e0be1d16f805ccdd52d31c6 (diff) | |
ARM: Better virt_to_page() handling
virt_to_page() is incredibly inefficient when virt-to-phys patching is
enabled. This is because we end up with this calculation:
page = &mem_map[asm virt_to_phys(addr) >> 12 - __pv_phys_offset >> 12]
in assembly. The asm virt_to_phys() is equivalent this this operation:
addr - PAGE_OFFSET + __pv_phys_offset
and we can see that because this is assembly, the compiler has no chance
to optimise some of that away. This should reduce down to:
page = &mem_map[(addr - PAGE_OFFSET) >> 12]
for the common cases. Permit the compiler to make this optimisation by
giving it more of the information it needs - do this by providing a
virt_to_pfn() macro.
Another issue which makes this more complex is that __pv_phys_offset is
a 64-bit type on all platforms. This is needlessly wasteful - if we
store the physical offset as a PFN, we can save a lot of work having
to deal with 64-bit values, which sometimes ends up producing incredibly
horrid code:
a4c: e3009000 movw r9, #0
a4c: R_ARM_MOVW_ABS_NC __pv_phys_offset
a50: e3409000 movt r9, #0 ; r9 = &__pv_phys_offset
a50: R_ARM_MOVT_ABS __pv_phys_offset
a54: e3002000 movw r2, #0
a54: R_ARM_MOVW_ABS_NC __pv_phys_offset
a58: e3402000 movt r2, #0 ; r2 = &__pv_phys_offset
a58: R_ARM_MOVT_ABS __pv_phys_offset
a5c: e5999004 ldr r9, [r9, #4] ; r9 = high word of __pv_phys_offset
a60: e3001000 movw r1, #0
a60: R_ARM_MOVW_ABS_NC mem_map
a64: e592c000 ldr ip, [r2] ; ip = low word of __pv_phys_offset
Reviewed-by: Nicolas Pitre <nico@linaro.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
| -rw-r--r-- | arch/arm/include/asm/memory.h | 41 | ||||
| -rw-r--r-- | arch/arm/kernel/armksyms.c | 2 | ||||
| -rw-r--r-- | arch/arm/kernel/head.S | 17 |
3 files changed, 34 insertions, 26 deletions
diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h index 8756e4bcdba0..2438d72cf4e6 100644 --- a/arch/arm/include/asm/memory.h +++ b/arch/arm/include/asm/memory.h | |||
| @@ -169,9 +169,17 @@ | |||
| 169 | * Physical vs virtual RAM address space conversion. These are | 169 | * Physical vs virtual RAM address space conversion. These are |
| 170 | * private definitions which should NOT be used outside memory.h | 170 | * private definitions which should NOT be used outside memory.h |
| 171 | * files. Use virt_to_phys/phys_to_virt/__pa/__va instead. | 171 | * files. Use virt_to_phys/phys_to_virt/__pa/__va instead. |
| 172 | * | ||
| 173 | * PFNs are used to describe any physical page; this means | ||
| 174 | * PFN 0 == physical address 0. | ||
| 172 | */ | 175 | */ |
| 173 | #ifndef __virt_to_phys | 176 | #if defined(__virt_to_phys) |
| 174 | #ifdef CONFIG_ARM_PATCH_PHYS_VIRT | 177 | #define PHYS_OFFSET PLAT_PHYS_OFFSET |
| 178 | #define PHYS_PFN_OFFSET ((unsigned long)(PHYS_OFFSET >> PAGE_SHIFT)) | ||
| 179 | |||
| 180 | #define virt_to_pfn(kaddr) (__pa(kaddr) >> PAGE_SHIFT) | ||
| 181 | |||
| 182 | #elif defined(CONFIG_ARM_PATCH_PHYS_VIRT) | ||
| 175 | 183 | ||
| 176 | /* | 184 | /* |
| 177 | * Constants used to force the right instruction encodings and shifts | 185 | * Constants used to force the right instruction encodings and shifts |
| @@ -180,12 +188,17 @@ | |||
| 180 | #define __PV_BITS_31_24 0x81000000 | 188 | #define __PV_BITS_31_24 0x81000000 |
| 181 | #define __PV_BITS_7_0 0x81 | 189 | #define __PV_BITS_7_0 0x81 |
| 182 | 190 | ||
| 183 | extern u64 __pv_phys_offset; | 191 | extern unsigned long __pv_phys_pfn_offset; |
| 184 | extern u64 __pv_offset; | 192 | extern u64 __pv_offset; |
| 185 | extern void fixup_pv_table(const void *, unsigned long); | 193 | extern void fixup_pv_table(const void *, unsigned long); |
| 186 | extern const void *__pv_table_begin, *__pv_table_end; | 194 | extern const void *__pv_table_begin, *__pv_table_end; |
| 187 | 195 | ||
| 188 | #define PHYS_OFFSET __pv_phys_offset | 196 | #define PHYS_OFFSET ((phys_addr_t)__pv_phys_pfn_offset << PAGE_SHIFT) |
| 197 | #define PHYS_PFN_OFFSET (__pv_phys_pfn_offset) | ||
| 198 | |||
| 199 | #define virt_to_pfn(kaddr) \ | ||
| 200 | ((((unsigned long)(kaddr) - PAGE_OFFSET) >> PAGE_SHIFT) + \ | ||
| 201 | PHYS_PFN_OFFSET) | ||
| 189 | 202 | ||
| 190 | #define __pv_stub(from,to,instr,type) \ | 203 | #define __pv_stub(from,to,instr,type) \ |
| 191 | __asm__("@ __pv_stub\n" \ | 204 | __asm__("@ __pv_stub\n" \ |
| @@ -246,6 +259,7 @@ static inline unsigned long __phys_to_virt(phys_addr_t x) | |||
| 246 | #else | 259 | #else |
| 247 | 260 | ||
| 248 | #define PHYS_OFFSET PLAT_PHYS_OFFSET | 261 | #define PHYS_OFFSET PLAT_PHYS_OFFSET |
| 262 | #define PHYS_PFN_OFFSET ((unsigned long)(PHYS_OFFSET >> PAGE_SHIFT)) | ||
| 249 | 263 | ||
| 250 | static inline phys_addr_t __virt_to_phys(unsigned long x) | 264 | static inline phys_addr_t __virt_to_phys(unsigned long x) |
| 251 | { | 265 | { |
| @@ -257,18 +271,11 @@ static inline unsigned long __phys_to_virt(phys_addr_t x) | |||
| 257 | return x - PHYS_OFFSET + PAGE_OFFSET; | 271 | return x - PHYS_OFFSET + PAGE_OFFSET; |
| 258 | } | 272 | } |
| 259 | 273 | ||
| 260 | #endif | 274 | #define virt_to_pfn(kaddr) \ |
| 261 | #endif | 275 | ((((unsigned long)(kaddr) - PAGE_OFFSET) >> PAGE_SHIFT) + \ |
| 276 | PHYS_PFN_OFFSET) | ||
| 262 | 277 | ||
| 263 | /* | 278 | #endif |
| 264 | * PFNs are used to describe any physical page; this means | ||
| 265 | * PFN 0 == physical address 0. | ||
| 266 | * | ||
| 267 | * This is the PFN of the first RAM page in the kernel | ||
| 268 | * direct-mapped view. We assume this is the first page | ||
| 269 | * of RAM in the mem_map as well. | ||
| 270 | */ | ||
| 271 | #define PHYS_PFN_OFFSET ((unsigned long)(PHYS_OFFSET >> PAGE_SHIFT)) | ||
| 272 | 279 | ||
| 273 | /* | 280 | /* |
| 274 | * These are *only* valid on the kernel direct mapped RAM memory. | 281 | * These are *only* valid on the kernel direct mapped RAM memory. |
| @@ -346,9 +353,9 @@ static inline __deprecated void *bus_to_virt(unsigned long x) | |||
| 346 | */ | 353 | */ |
| 347 | #define ARCH_PFN_OFFSET PHYS_PFN_OFFSET | 354 | #define ARCH_PFN_OFFSET PHYS_PFN_OFFSET |
| 348 | 355 | ||
| 349 | #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) | 356 | #define virt_to_page(kaddr) pfn_to_page(virt_to_pfn(kaddr)) |
| 350 | #define virt_addr_valid(kaddr) (((unsigned long)(kaddr) >= PAGE_OFFSET && (unsigned long)(kaddr) < (unsigned long)high_memory) \ | 357 | #define virt_addr_valid(kaddr) (((unsigned long)(kaddr) >= PAGE_OFFSET && (unsigned long)(kaddr) < (unsigned long)high_memory) \ |
| 351 | && pfn_valid(__pa(kaddr) >> PAGE_SHIFT) ) | 358 | && pfn_valid(virt_to_pfn(kaddr))) |
| 352 | 359 | ||
| 353 | #endif | 360 | #endif |
| 354 | 361 | ||
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c index 85e664b6a5f1..f7b450f97e68 100644 --- a/arch/arm/kernel/armksyms.c +++ b/arch/arm/kernel/armksyms.c | |||
| @@ -158,6 +158,6 @@ EXPORT_SYMBOL(__gnu_mcount_nc); | |||
| 158 | #endif | 158 | #endif |
| 159 | 159 | ||
| 160 | #ifdef CONFIG_ARM_PATCH_PHYS_VIRT | 160 | #ifdef CONFIG_ARM_PATCH_PHYS_VIRT |
| 161 | EXPORT_SYMBOL(__pv_phys_offset); | 161 | EXPORT_SYMBOL(__pv_phys_pfn_offset); |
| 162 | EXPORT_SYMBOL(__pv_offset); | 162 | EXPORT_SYMBOL(__pv_offset); |
| 163 | #endif | 163 | #endif |
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S index 914616e0bdcd..3aca959fee8d 100644 --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S | |||
| @@ -584,9 +584,10 @@ __fixup_pv_table: | |||
| 584 | subs r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET | 584 | subs r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET |
| 585 | add r4, r4, r3 @ adjust table start address | 585 | add r4, r4, r3 @ adjust table start address |
| 586 | add r5, r5, r3 @ adjust table end address | 586 | add r5, r5, r3 @ adjust table end address |
| 587 | add r6, r6, r3 @ adjust __pv_phys_offset address | 587 | add r6, r6, r3 @ adjust __pv_phys_pfn_offset address |
| 588 | add r7, r7, r3 @ adjust __pv_offset address | 588 | add r7, r7, r3 @ adjust __pv_offset address |
| 589 | str r8, [r6, #LOW_OFFSET] @ save computed PHYS_OFFSET to __pv_phys_offset | 589 | mov r0, r8, lsr #12 @ convert to PFN |
| 590 | str r0, [r6, #LOW_OFFSET] @ save computed PHYS_OFFSET to __pv_phys_pfn_offset | ||
| 590 | strcc ip, [r7, #HIGH_OFFSET] @ save to __pv_offset high bits | 591 | strcc ip, [r7, #HIGH_OFFSET] @ save to __pv_offset high bits |
| 591 | mov r6, r3, lsr #24 @ constant for add/sub instructions | 592 | mov r6, r3, lsr #24 @ constant for add/sub instructions |
| 592 | teq r3, r6, lsl #24 @ must be 16MiB aligned | 593 | teq r3, r6, lsl #24 @ must be 16MiB aligned |
| @@ -600,7 +601,7 @@ ENDPROC(__fixup_pv_table) | |||
| 600 | 1: .long . | 601 | 1: .long . |
| 601 | .long __pv_table_begin | 602 | .long __pv_table_begin |
| 602 | .long __pv_table_end | 603 | .long __pv_table_end |
| 603 | 2: .long __pv_phys_offset | 604 | 2: .long __pv_phys_pfn_offset |
| 604 | .long __pv_offset | 605 | .long __pv_offset |
| 605 | 606 | ||
| 606 | .text | 607 | .text |
| @@ -688,11 +689,11 @@ ENTRY(fixup_pv_table) | |||
| 688 | ENDPROC(fixup_pv_table) | 689 | ENDPROC(fixup_pv_table) |
| 689 | 690 | ||
| 690 | .data | 691 | .data |
| 691 | .globl __pv_phys_offset | 692 | .globl __pv_phys_pfn_offset |
| 692 | .type __pv_phys_offset, %object | 693 | .type __pv_phys_pfn_offset, %object |
| 693 | __pv_phys_offset: | 694 | __pv_phys_pfn_offset: |
| 694 | .quad 0 | 695 | .word 0 |
| 695 | .size __pv_phys_offset, . -__pv_phys_offset | 696 | .size __pv_phys_pfn_offset, . -__pv_phys_pfn_offset |
| 696 | 697 | ||
| 697 | .globl __pv_offset | 698 | .globl __pv_offset |
| 698 | .type __pv_offset, %object | 699 | .type __pv_offset, %object |
