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