path: root/mm
diff options
authorEugene Surovegin <>2011-12-15 11:25:59 -0800
committerGreg Kroah-Hartman <>2012-01-06 14:17:00 -0800
commit2c141bbe1ddfb7f91749af4411f884125ea6e14e (patch)
tree69b3b7fd9a5900809cebfcff4eb3293b7d85a9c1 /mm
parent8246905d452b12e1ce535705c746c3ff66660e09 (diff)
percpu: fix per_cpu_ptr_to_phys() handling of non-page-aligned addresses
commit 9f57bd4d6dc69a4e3bf43044fa00fcd24dd363e3 upstream. per_cpu_ptr_to_phys() incorrectly rounds up its result for non-kmalloc case to the page boundary, which is bogus for any non-page-aligned address. This affects the only in-tree user of this function - sysfs handler for per-cpu 'crash_notes' physical address. The trouble is that the crash_notes per-cpu variable is not page-aligned: crash_notes = 0xc08e8ed4 PER-CPU OFFSET VALUES: CPU 0: 3711f000 CPU 1: 37129000 CPU 2: 37133000 CPU 3: 3713d000 So, the per-cpu addresses are: crash_notes on CPU 0: f7a07ed4 => phys 36b57ed4 crash_notes on CPU 1: f7a11ed4 => phys 36b4ded4 crash_notes on CPU 2: f7a1bed4 => phys 36b43ed4 crash_notes on CPU 3: f7a25ed4 => phys 36b39ed4 However, /sys/devices/system/cpu/cpu*/crash_notes says: /sys/devices/system/cpu/cpu0/crash_notes: 36b57000 /sys/devices/system/cpu/cpu1/crash_notes: 36b4d000 /sys/devices/system/cpu/cpu2/crash_notes: 36b43000 /sys/devices/system/cpu/cpu3/crash_notes: 36b39000 As you can see, all values are rounded down to a page boundary. Consequently, this is where kexec sets up the NOTE segments, and thus where the secondary kernel is looking for them. However, when the first kernel crashes, it saves the notes to the unaligned addresses, where they are not found. Fix it by adding offset_in_page() to the translated page address. -tj: Combined Eugene's and Petr's commit messages. Signed-off-by: Eugene Surovegin <> Signed-off-by: Tejun Heo <> Reported-by: Petr Tesarik <> Signed-off-by: Greg Kroah-Hartman <>
Diffstat (limited to 'mm')
1 files changed, 4 insertions, 2 deletions
diff --git a/mm/percpu.c b/mm/percpu.c
index 93b5a7c96a7e..0ae7a09141e8 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -1011,9 +1011,11 @@ phys_addr_t per_cpu_ptr_to_phys(void *addr)
if (!is_vmalloc_addr(addr))
return __pa(addr);
- return page_to_phys(vmalloc_to_page(addr));
+ return page_to_phys(vmalloc_to_page(addr)) +
+ offset_in_page(addr);
} else
- return page_to_phys(pcpu_addr_to_page(addr));
+ return page_to_phys(pcpu_addr_to_page(addr)) +
+ offset_in_page(addr);