Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

kexec: Improve & fix crash_exclude_mem_range() to handle overlapping ranges

The crash_exclude_mem_range() function can only handle one memory region a time.

It will fail in the case in which the passed in area covers several memory
regions. In this case, it will only exclude the first region, then return,
but leave the later regions unsolved.

E.g in a NEC system with two usable RAM regions inside the low 1M:

...
BIOS-e820: [mem 0x0000000000000000-0x000000000003efff] usable
BIOS-e820: [mem 0x000000000003f000-0x000000000003ffff] reserved
BIOS-e820: [mem 0x0000000000040000-0x000000000009ffff] usable

It will only exclude the memory region [0, 0x3efff], the memory region
[0x40000, 0x9ffff] will still be added into /proc/vmcore, which may cause
the following failure when dumping vmcore:

ioremap on RAM at 0x0000000000040000 - 0x0000000000040fff
WARNING: CPU: 0 PID: 665 at arch/x86/mm/ioremap.c:186 __ioremap_caller+0x2c7/0x2e0
...
RIP: 0010:__ioremap_caller+0x2c7/0x2e0
...
cp: error reading '/proc/vmcore': Cannot allocate memory
kdump: saving vmcore failed

In order to fix this bug, let's extend the crash_exclude_mem_range()
to handle the overlapping ranges.

[ mingo: Amended the changelog. ]

Signed-off-by: Lianbo Jiang <lijiang@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Acked-by: Dave Young <dyoung@redhat.com>
Link: https://lore.kernel.org/r/20200804044933.1973-3-lijiang@redhat.com

authored by

Lianbo Jiang and committed by
Ingo Molnar
a2e9a95d a3e1c3bb

+23 -12
+23 -12
kernel/kexec_file.c
··· 1157 1157 unsigned long long mstart, unsigned long long mend) 1158 1158 { 1159 1159 int i, j; 1160 - unsigned long long start, end; 1160 + unsigned long long start, end, p_start, p_end; 1161 1161 struct crash_mem_range temp_range = {0, 0}; 1162 1162 1163 1163 for (i = 0; i < mem->nr_ranges; i++) { 1164 1164 start = mem->ranges[i].start; 1165 1165 end = mem->ranges[i].end; 1166 + p_start = mstart; 1167 + p_end = mend; 1166 1168 1167 1169 if (mstart > end || mend < start) 1168 1170 continue; 1169 1171 1170 1172 /* Truncate any area outside of range */ 1171 1173 if (mstart < start) 1172 - mstart = start; 1174 + p_start = start; 1173 1175 if (mend > end) 1174 - mend = end; 1176 + p_end = end; 1175 1177 1176 1178 /* Found completely overlapping range */ 1177 - if (mstart == start && mend == end) { 1179 + if (p_start == start && p_end == end) { 1178 1180 mem->ranges[i].start = 0; 1179 1181 mem->ranges[i].end = 0; 1180 1182 if (i < mem->nr_ranges - 1) { ··· 1187 1185 mem->ranges[j].end = 1188 1186 mem->ranges[j+1].end; 1189 1187 } 1188 + 1189 + /* 1190 + * Continue to check if there are another overlapping ranges 1191 + * from the current position because of shifting the above 1192 + * mem ranges. 1193 + */ 1194 + i--; 1195 + mem->nr_ranges--; 1196 + continue; 1190 1197 } 1191 1198 mem->nr_ranges--; 1192 1199 return 0; 1193 1200 } 1194 1201 1195 - if (mstart > start && mend < end) { 1202 + if (p_start > start && p_end < end) { 1196 1203 /* Split original range */ 1197 - mem->ranges[i].end = mstart - 1; 1198 - temp_range.start = mend + 1; 1204 + mem->ranges[i].end = p_start - 1; 1205 + temp_range.start = p_end + 1; 1199 1206 temp_range.end = end; 1200 - } else if (mstart != start) 1201 - mem->ranges[i].end = mstart - 1; 1207 + } else if (p_start != start) 1208 + mem->ranges[i].end = p_start - 1; 1202 1209 else 1203 - mem->ranges[i].start = mend + 1; 1210 + mem->ranges[i].start = p_end + 1; 1204 1211 break; 1205 1212 } 1206 1213 ··· 1254 1243 * kexec-tools creates an extra PT_LOAD phdr for kernel text mapping 1255 1244 * area (for example, ffffffff80000000 - ffffffffa0000000 on x86_64). 1256 1245 * I think this is required by tools like gdb. So same physical 1257 - * memory will be mapped in two elf headers. One will contain kernel 1246 + * memory will be mapped in two elf headers. One will contain kernel 1258 1247 * text virtual addresses and other will have __va(physical) addresses. 1259 1248 */ 1260 1249 ··· 1281 1270 ehdr->e_ehsize = sizeof(Elf64_Ehdr); 1282 1271 ehdr->e_phentsize = sizeof(Elf64_Phdr); 1283 1272 1284 - /* Prepare one phdr of type PT_NOTE for each present cpu */ 1273 + /* Prepare one phdr of type PT_NOTE for each present CPU */ 1285 1274 for_each_present_cpu(cpu) { 1286 1275 phdr->p_type = PT_NOTE; 1287 1276 notes_addr = per_cpu_ptr_to_phys(per_cpu_ptr(crash_notes, cpu));