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