File Index
Files and directories
Sign in
FILE

pdt.c

File preview with syntax highlighting.

linux-6.19.14/arch/parisc/kernel/pdt.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 *    Page Deallocation Table (PDT) support
  4 *
  5 *    The Page Deallocation Table (PDT) is maintained by firmware and holds a
  6 *    list of memory addresses in which memory errors were detected.
  7 *    The list contains both single-bit (correctable) and double-bit
  8 *    (uncorrectable) errors.
  9 *
 10 *    Copyright 2017 by Helge Deller <deller@gmx.de>
 11 *
 12 *    possible future enhancements:
 13 *    - add userspace interface via procfs or sysfs to clear PDT
 14 */
 15
 16#include <linux/memblock.h>
 17#include <linux/seq_file.h>
 18#include <linux/kthread.h>
 19#include <linux/proc_fs.h>
 20#include <linux/initrd.h>
 21#include <linux/pgtable.h>
 22#include <linux/mm.h>
 23
 24#include <asm/pdc.h>
 25#include <asm/pdcpat.h>
 26#include <asm/sections.h>
 27#include <asm/pgtable.h>
 28
 29enum pdt_access_type {
 30	PDT_NONE,
 31	PDT_PDC,
 32	PDT_PAT_NEW,
 33	PDT_PAT_CELL
 34};
 35
 36static enum pdt_access_type pdt_type;
 37
 38/* PDT poll interval: 1 minute if errors, 5 minutes if everything OK. */
 39#define PDT_POLL_INTERVAL_DEFAULT	(5*60*HZ)
 40#define PDT_POLL_INTERVAL_SHORT		(1*60*HZ)
 41static unsigned long pdt_poll_interval = PDT_POLL_INTERVAL_DEFAULT;
 42
 43/* global PDT status information */
 44static struct pdc_mem_retinfo pdt_status;
 45
 46#define MAX_PDT_TABLE_SIZE	PAGE_SIZE
 47#define MAX_PDT_ENTRIES		(MAX_PDT_TABLE_SIZE / sizeof(unsigned long))
 48static unsigned long pdt_entry[MAX_PDT_ENTRIES] __page_aligned_bss;
 49
 50/*
 51 * Constants for the pdt_entry format:
 52 * A pdt_entry holds the physical address in bits 0-57, bits 58-61 are
 53 * reserved, bit 62 is the perm bit and bit 63 is the error_type bit.
 54 * The perm bit indicates whether the error have been verified as a permanent
 55 * error (value of 1) or has not been verified, and may be transient (value
 56 * of 0). The error_type bit indicates whether the error is a single bit error
 57 * (value of 1) or a multiple bit error.
 58 * On non-PAT machines phys_addr is encoded in bits 0-59 and error_type in bit
 59 * 63. Those machines don't provide the perm bit.
 60 */
 61
 62#define PDT_ADDR_PHYS_MASK	(pdt_type != PDT_PDC ? ~0x3f : ~0x0f)
 63#define PDT_ADDR_PERM_ERR	(pdt_type != PDT_PDC ? 2UL : 0UL)
 64#define PDT_ADDR_SINGLE_ERR	1UL
 65
 66#ifdef CONFIG_PROC_FS
 67/* report PDT entries via /proc/meminfo */
 68void arch_report_meminfo(struct seq_file *m)
 69{
 70	if (pdt_type == PDT_NONE)
 71		return;
 72
 73	seq_printf(m, "PDT_max_entries: %7lu\n",
 74			pdt_status.pdt_size);
 75	seq_printf(m, "PDT_cur_entries: %7lu\n",
 76			pdt_status.pdt_entries);
 77}
 78#endif
 79
 80static int get_info_pat_new(void)
 81{
 82	struct pdc_pat_mem_retinfo pat_rinfo;
 83	int ret;
 84
 85	/* newer PAT machines like C8000 report info for all cells */
 86	if (is_pdc_pat())
 87		ret = pdc_pat_mem_pdt_info(&pat_rinfo);
 88	else
 89		return PDC_BAD_PROC;
 90
 91	pdt_status.pdt_size = pat_rinfo.max_pdt_entries;
 92	pdt_status.pdt_entries = pat_rinfo.current_pdt_entries;
 93	pdt_status.pdt_status = 0;
 94	pdt_status.first_dbe_loc = pat_rinfo.first_dbe_loc;
 95	pdt_status.good_mem = pat_rinfo.good_mem;
 96
 97	return ret;
 98}
 99
100static int get_info_pat_cell(void)
101{
102	struct pdc_pat_mem_cell_pdt_retinfo cell_rinfo;
103	int ret;
104
105	/* older PAT machines like rp5470 report cell info only */
106	if (is_pdc_pat())
107		ret = pdc_pat_mem_pdt_cell_info(&cell_rinfo, parisc_cell_num);
108	else
109		return PDC_BAD_PROC;
110
111	pdt_status.pdt_size = cell_rinfo.max_pdt_entries;
112	pdt_status.pdt_entries = cell_rinfo.current_pdt_entries;
113	pdt_status.pdt_status = 0;
114	pdt_status.first_dbe_loc = cell_rinfo.first_dbe_loc;
115	pdt_status.good_mem = cell_rinfo.good_mem;
116
117	return ret;
118}
119
120static void report_mem_err(unsigned long pde)
121{
122	struct pdc_pat_mem_phys_mem_location loc;
123	unsigned long addr;
124	char dimm_txt[32];
125
126	addr = pde & PDT_ADDR_PHYS_MASK;
127
128	/* show DIMM slot description on PAT machines */
129	if (is_pdc_pat()) {
130		pdc_pat_mem_get_dimm_phys_location(&loc, addr);
131		sprintf(dimm_txt, "DIMM slot %02x, ", loc.dimm_slot);
132	} else
133		dimm_txt[0] = 0;
134
135	pr_warn("PDT: BAD MEMORY at 0x%08lx, %s%s%s-bit error.\n",
136		addr, dimm_txt,
137		pde & PDT_ADDR_PERM_ERR ? "permanent ":"",
138		pde & PDT_ADDR_SINGLE_ERR ? "single":"multi");
139}
140
141
142/*
143 * pdc_pdt_init()
144 *
145 * Initialize kernel PDT structures, read initial PDT table from firmware,
146 * report all current PDT entries and mark bad memory with memblock_reserve()
147 * to avoid that the kernel will use broken memory areas.
148 *
149 */
150void __init pdc_pdt_init(void)
151{
152	int ret, i;
153	unsigned long entries;
154	struct pdc_mem_read_pdt pdt_read_ret;
155
156	pdt_type = PDT_PAT_NEW;
157	ret = get_info_pat_new();
158
159	if (ret != PDC_OK) {
160		pdt_type = PDT_PAT_CELL;
161		ret = get_info_pat_cell();
162	}
163
164	if (ret != PDC_OK) {
165		pdt_type = PDT_PDC;
166		/* non-PAT machines provide the standard PDC call */
167		ret = pdc_mem_pdt_info(&pdt_status);
168	}
169
170	if (ret != PDC_OK) {
171		pdt_type = PDT_NONE;
172		pr_info("PDT: Firmware does not provide any page deallocation"
173			" information.\n");
174		return;
175	}
176
177	entries = pdt_status.pdt_entries;
178	if (WARN_ON(entries > MAX_PDT_ENTRIES))
179		entries = pdt_status.pdt_entries = MAX_PDT_ENTRIES;
180
181	pr_info("PDT: type %s, size %lu, entries %lu, status %lu, dbe_loc 0x%lx,"
182		" good_mem %lu MB\n",
183			pdt_type == PDT_PDC ? __stringify(PDT_PDC) :
184			pdt_type == PDT_PAT_CELL ? __stringify(PDT_PAT_CELL)
185						 : __stringify(PDT_PAT_NEW),
186			pdt_status.pdt_size, pdt_status.pdt_entries,
187			pdt_status.pdt_status, pdt_status.first_dbe_loc,
188			pdt_status.good_mem / 1024 / 1024);
189
190	if (entries == 0) {
191		pr_info("PDT: Firmware reports all memory OK.\n");
192		return;
193	}
194
195	if (pdt_status.first_dbe_loc &&
196		pdt_status.first_dbe_loc <= __pa((unsigned long)&_end))
197		pr_crit("CRITICAL: Bad memory inside kernel image memory area!\n");
198
199	pr_warn("PDT: Firmware reports %lu entries of faulty memory:\n",
200		entries);
201
202	if (pdt_type == PDT_PDC)
203		ret = pdc_mem_pdt_read_entries(&pdt_read_ret, pdt_entry);
204	else {
205#ifdef CONFIG_64BIT
206		struct pdc_pat_mem_read_pd_retinfo pat_pret;
207
208		if (pdt_type == PDT_PAT_CELL)
209			ret = pdc_pat_mem_read_cell_pdt(&pat_pret, pdt_entry,
210				MAX_PDT_ENTRIES);
211		else
212			ret = pdc_pat_mem_read_pd_pdt(&pat_pret, pdt_entry,
213				MAX_PDT_TABLE_SIZE, 0);
214#else
215		ret = PDC_BAD_PROC;
216#endif
217	}
218
219	if (ret != PDC_OK) {
220		pdt_type = PDT_NONE;
221		pr_warn("PDT: Get PDT entries failed with %d\n", ret);
222		return;
223	}
224
225	for (i = 0; i < pdt_status.pdt_entries; i++) {
226		unsigned long addr;
227
228		report_mem_err(pdt_entry[i]);
229
230		addr = pdt_entry[i] & PDT_ADDR_PHYS_MASK;
231		if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) &&
232			addr >= initrd_start && addr < initrd_end)
233			pr_crit("CRITICAL: initrd possibly broken "
234				"due to bad memory!\n");
235
236		/* mark memory page bad */
237		memblock_reserve(pdt_entry[i] & PAGE_MASK, PAGE_SIZE);
238		num_poisoned_pages_inc(addr >> PAGE_SHIFT);
239	}
240}
241
242
243/*
244 * This is the PDT kernel thread main loop.
245 */
246
247static int pdt_mainloop(void *unused)
248{
249	struct pdc_mem_read_pdt pdt_read_ret;
250	struct pdc_pat_mem_read_pd_retinfo pat_pret __maybe_unused;
251	unsigned long old_num_entries;
252	unsigned long *bad_mem_ptr;
253	int num, ret;
254
255	for (;;) {
256		set_current_state(TASK_INTERRUPTIBLE);
257
258		old_num_entries = pdt_status.pdt_entries;
259
260		schedule_timeout(pdt_poll_interval);
261		if (kthread_should_stop())
262			break;
263
264		/* Do we have new PDT entries? */
265		switch (pdt_type) {
266		case PDT_PAT_NEW:
267			ret = get_info_pat_new();
268			break;
269		case PDT_PAT_CELL:
270			ret = get_info_pat_cell();
271			break;
272		default:
273			ret = pdc_mem_pdt_info(&pdt_status);
274			break;
275		}
276
277		if (ret != PDC_OK) {
278			pr_warn("PDT: unexpected failure %d\n", ret);
279			return -EINVAL;
280		}
281
282		/* if no new PDT entries, just wait again */
283		num = pdt_status.pdt_entries - old_num_entries;
284		if (num <= 0)
285			continue;
286
287		/* decrease poll interval in case we found memory errors */
288		if (pdt_status.pdt_entries &&
289			pdt_poll_interval == PDT_POLL_INTERVAL_DEFAULT)
290			pdt_poll_interval = PDT_POLL_INTERVAL_SHORT;
291
292		/* limit entries to get */
293		if (num > MAX_PDT_ENTRIES) {
294			num = MAX_PDT_ENTRIES;
295			pdt_status.pdt_entries = old_num_entries + num;
296		}
297
298		/* get new entries */
299		switch (pdt_type) {
300#ifdef CONFIG_64BIT
301		case PDT_PAT_CELL:
302			if (pdt_status.pdt_entries > MAX_PDT_ENTRIES) {
303				pr_crit("PDT: too many entries.\n");
304				return -ENOMEM;
305			}
306			ret = pdc_pat_mem_read_cell_pdt(&pat_pret, pdt_entry,
307				MAX_PDT_ENTRIES);
308			bad_mem_ptr = &pdt_entry[old_num_entries];
309			break;
310		case PDT_PAT_NEW:
311			ret = pdc_pat_mem_read_pd_pdt(&pat_pret,
312				pdt_entry,
313				num * sizeof(unsigned long),
314				old_num_entries * sizeof(unsigned long));
315			bad_mem_ptr = &pdt_entry[0];
316			break;
317#endif
318		default:
319			ret = pdc_mem_pdt_read_entries(&pdt_read_ret,
320				pdt_entry);
321			bad_mem_ptr = &pdt_entry[old_num_entries];
322			break;
323		}
324
325		/* report and mark memory broken */
326		while (num--) {
327			unsigned long pde = *bad_mem_ptr++;
328
329			report_mem_err(pde);
330
331#ifdef CONFIG_MEMORY_FAILURE
332			if ((pde & PDT_ADDR_PERM_ERR) ||
333			    ((pde & PDT_ADDR_SINGLE_ERR) == 0))
334				memory_failure(pde >> PAGE_SHIFT, 0);
335			else
336				soft_offline_page(pde >> PAGE_SHIFT, 0);
337#else
338			pr_crit("PDT: memory error at 0x%lx ignored.\n"
339				"Rebuild kernel with CONFIG_MEMORY_FAILURE=y "
340				"for real handling.\n",
341				pde & PDT_ADDR_PHYS_MASK);
342#endif
343
344		}
345	}
346
347	return 0;
348}
349
350
351static int __init pdt_initcall(void)
352{
353	struct task_struct *kpdtd_task;
354
355	if (pdt_type == PDT_NONE)
356		return -ENODEV;
357
358	kpdtd_task = kthread_run(pdt_mainloop, NULL, "kpdtd");
359
360	return PTR_ERR_OR_ZERO(kpdtd_task);
361}
362
363late_initcall(pdt_initcall);
~/ Read-only file preview.