From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9AFC7233952 for ; Fri, 31 Jul 2026 17:49:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785520173; cv=none; b=AotfuPfwpm/zQG1gqiiyd+KCDmBWEonOePjl9vNZe7bzUpVjhscXOWaSD8r8nameaGzKgjQLembLq6lNQk2k4KehRnJhBD0zdMnp+Iy8vJ8qyUAz5bM4+rz/IE19/eFBoVBl4c1Lqb9tAL5X3yjkoIuXxzzqzQefZ6BRmp1WRWo= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785520173; c=relaxed/simple; bh=h9wDEe7eUR8lbjGQUtUfysrBnXOxOvwdyCTwun9Z6so=; h=From:To:Cc:Subject:In-Reply-To:References:Date:Message-ID: MIME-Version:Content-Type; b=CkOnHX4GJz4xmtUcZd+wyu03wTj2zIvSoShiHl+BFmWKEFALT2ZPIlgzmLMcy7upVuysa8CQ44T+zb25iZoVydaJTc39H+8n9LGeJhu21ZLlkPaWfsqbvHLZFeOLY1tMfYN+ItcLK4eQPnfEtka+3qFr3mI1BPrj2AjqmpcFzxo= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=JYtB/zJE; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="JYtB/zJE" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E30A01F00AC4; Fri, 31 Jul 2026 17:49:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1785520172; bh=/+IlztmyqYuuXlGVT0cph9tanuKKLkq7z25QvgzYv6g=; h=From:To:Cc:Subject:In-Reply-To:References:Date; b=JYtB/zJEZ0LkWBkIp7+O/oltIUsdXqYZq0qhsQUFYpPDn/qFOCinYN9gucrzMUyl8 tJd4nZ5wkcviTVyevyDvtTy4n3fp7v7omMHe53yF1q7tAonA78OLe0XskXfmrVb9Tm tkd65uNjWxX8hayREMZ/2mWr0WdmsUvEwNhgXe5U5KL85WIsWGnoE9wM6k0bBaVbU+ OnuwKMMxSQhvUDP+9bdEwJMA/ba5xoLaG28zpQ+l7L4mnJMGrfTxJMoYIbtahMYMoY +gFwy5k2OxVm6YAef1tm0l2LR4AZAeYwnXolbHiAxE9Foy/lQKx8Vmywv/9fLDYM/5 PghZrsHzacl9Q== From: Pratyush Yadav To: Breno Leitao Cc: Andrew Morton , David Hildenbrand , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Baoquan He , Pasha Tatashin , Pratyush Yadav , Miaohe Lin , Naoya Horiguchi , linux-mm@kvack.org, linux-kernel@vger.kernel.org, kexec@lists.infradead.org, rmikey@meta.com, riel@surriel.com, kernel-team@meta.com, Kiryl Shutsemau Subject: Re: [PATCH v2] kexec: keep the next kernel off hardware-poisoned pages In-Reply-To: <20260730-kexec_posioned-v2-1-f92d18551f64@debian.org> (Breno Leitao's message of "Thu, 30 Jul 2026 08:55:50 -0700") References: <20260730-kexec_posioned-v2-1-f92d18551f64@debian.org> Date: Fri, 31 Jul 2026 19:49:27 +0200 Message-ID: <2vxzecgj6od4.fsf@kernel.org> User-Agent: Gnus/5.13 (Gnus v5.13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain On Thu, Jul 30 2026, Breno Leitao wrote: > Memory failures (such as unrecoverable ECCs errors) are getting more and > more common. The kernel knows how to handle it while running, marking it > as poisoned (and SIGBUS user tasks). > > Poisoned memory is removed from the buddy allocator, but, not from > other places. A current problem is that kexec will load new kernel > on top of a bad/poisoned memory, which is undesirable. > > If the next kernel's image, initrd or purgatory lands on poisoned frame, > the relocation copy writes to the bad memory and the machine checks > during the kexec. > > Skip hardware-poisoned frames when placing segments: check them in the > kexec_file hole finder so it lays the next kernel down on good memory, > and reject a poisoned destination in sanity_check_segment_list() for > the kexec_load path, which cannot relocate. > > Suggested-by: Kiryl Shutsemau > Signed-off-by: Breno Leitao > --- > Changes in v2: > - EDITME: describe what is new in this series revision. > - EDITME: use bulletpoints and terse descriptions. > - Link to v1: https://patch.msgid.link/20260728-kexec_posioned-v1-1-160c81d180fe@debian.org > --- > include/linux/mm.h | 8 ++++++++ > kernel/kexec_core.c | 10 ++++++++++ > kernel/kexec_file.c | 19 +++++++++++++++++++ > mm/memory-failure.c | 28 ++++++++++++++++++++++++++++ > 4 files changed, 65 insertions(+) > > diff --git a/include/linux/mm.h b/include/linux/mm.h > index 7fabe6c66b4b7..48cad9a519d08 100644 > --- a/include/linux/mm.h > +++ b/include/linux/mm.h > @@ -5192,6 +5192,8 @@ extern const struct attribute_group memory_failure_attr_group; > extern void memory_failure_queue(unsigned long pfn, int flags); > void num_poisoned_pages_inc(unsigned long pfn); > void num_poisoned_pages_sub(unsigned long pfn, long i); > +bool range_contains_hwpoison(phys_addr_t start, unsigned long size, > + phys_addr_t *poison); > #else > static inline void memory_failure_queue(unsigned long pfn, int flags) > { > @@ -5204,6 +5206,12 @@ static inline void num_poisoned_pages_inc(unsigned long pfn) > static inline void num_poisoned_pages_sub(unsigned long pfn, long i) > { > } > + > +static inline bool range_contains_hwpoison(phys_addr_t start, unsigned long size, > + phys_addr_t *poison) > +{ > + return false; > +} > #endif > > #if defined(CONFIG_MEMORY_FAILURE) && defined(CONFIG_MEMORY_HOTPLUG) > diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c > index dc770b9a6d053..9f6ed3c04299b 100644 > --- a/kernel/kexec_core.c > +++ b/kernel/kexec_core.c > @@ -212,6 +212,16 @@ int sanity_check_segment_list(struct kimage *image) > } > #endif > > + /* > + * Reject destinations that land on hardware-poisoned memory: the > + * relocation copy would machine-check on the bad frame. > + */ > + for (i = 0; i < nr_segments; i++) { > + if (range_contains_hwpoison(image->segment[i].mem, > + image->segment[i].memsz, NULL)) > + return -EADDRNOTAVAIL; > + } > + > /* > * The destination addresses are searched from system RAM rather than > * being allocated from the buddy allocator, so they are not guaranteed > diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c > index 59fb9d71e9d86..95cc981a557be 100644 > --- a/kernel/kexec_file.c > +++ b/kernel/kexec_file.c > @@ -475,6 +475,7 @@ static int locate_mem_hole_top_down(unsigned long start, unsigned long end, > { > struct kimage *image = kbuf->image; > unsigned long temp_start, temp_end; > + phys_addr_t poisoned_addr; > > temp_end = min(end, kbuf->buf_max); > temp_start = temp_end - kbuf->memsz + 1; > @@ -504,6 +505,14 @@ static int locate_mem_hole_top_down(unsigned long start, unsigned long end, > continue; > } > > + if (range_contains_hwpoison(temp_start, temp_end - temp_start + 1, > + &poisoned_addr)) { > + if (poisoned_addr < kbuf->memsz) > + return 0; > + temp_start = poisoned_addr - kbuf->memsz; > + continue; > + } > + > /* We found a suitable memory range */ > break; > } while (1); > @@ -520,6 +529,7 @@ static int locate_mem_hole_bottom_up(unsigned long start, unsigned long end, > { > struct kimage *image = kbuf->image; > unsigned long temp_start, temp_end; > + phys_addr_t poisoned_addr; > > temp_start = max(start, kbuf->buf_min); > > @@ -546,6 +556,15 @@ static int locate_mem_hole_bottom_up(unsigned long start, unsigned long end, > continue; > } > > + /* > + * Avoid placing the next kernel on hardware-poisoned memory. > + */ > + if (range_contains_hwpoison(temp_start, temp_end - temp_start + 1, > + &poisoned_addr)) { > + temp_start = poisoned_addr + PAGE_SIZE; > + continue; > + } > + > /* We found a suitable memory range */ > break; > } while (1); > diff --git a/mm/memory-failure.c b/mm/memory-failure.c > index a8b03e2920ba8..04a1883d51fda 100644 > --- a/mm/memory-failure.c > +++ b/mm/memory-failure.c > @@ -96,6 +96,34 @@ void num_poisoned_pages_sub(unsigned long pfn, long i) > memblk_nr_poison_sub(pfn, i); > } > > +/* > + * Return true if any online page in [start, start + size) is hardware > + * poisoned. On a hit, when @poison is not NULL, @poison is set to the > + * address of the first poisoned page, which is ugly, but I cannot only Won't the last page be better? If two poisoned pages land in the range, you'd like to search past the last one. > + * return phys_addr_t, thus this "extra" parameter, instead of returning > + * the hit. > + */ > +bool range_contains_hwpoison(phys_addr_t start, unsigned long size, > + phys_addr_t *poison) Perhaps phys_addr_t range_last_hwpoison(phys_addr_t start, unsigned long size) ? And if no poisoned page lands here then a default of 0 or ~0UL? > +{ > + unsigned long pfn, end_pfn; > + > + if (!size || !atomic_long_read(&num_poisoned_pages)) > + return false; > + > + end_pfn = PHYS_PFN(start + size - 1); > + for (pfn = PHYS_PFN(start); pfn <= end_pfn; pfn++) { > + struct page *page = pfn_to_online_page(pfn); > + > + if (page && PageHWPoison(page)) { > + if (poison) > + *poison = PFN_PHYS(pfn); > + return true; > + } > + } > + return false; > +} > + > /** > * MF_ATTR_RO - Create sysfs entry for each memory failure statistics. > * @_name: name of the file in the per NUMA sysfs directory. > > --- > base-commit: c5e32e86ca02b003f86e095d379b38148999293d > change-id: 20260727-kexec_posioned-72bb0a4143a0 > > Best regards, > -- > > Breno Leitao > -- Regards, Pratyush Yadav