linux/arch/x86/kernel/cpu/mcheck/mce-genpool.c
<<
>>
Prefs
   1/*
   2 * MCE event pool management in MCE context
   3 *
   4 * Copyright (C) 2015 Intel Corp.
   5 * Author: Chen, Gong <gong.chen@linux.intel.com>
   6 *
   7 * This file is licensed under GPLv2.
   8 */
   9#include <linux/smp.h>
  10#include <linux/mm.h>
  11#include <linux/genalloc.h>
  12#include <linux/llist.h>
  13#include "mce-internal.h"
  14
  15/*
  16 * printk() is not safe in MCE context. This is a lock-less memory allocator
  17 * used to save error information organized in a lock-less list.
  18 *
  19 * This memory pool is only to be used to save MCE records in MCE context.
  20 * MCE events are rare, so a fixed size memory pool should be enough. Use
  21 * 2 pages to save MCE events for now (~80 MCE records at most).
  22 */
  23#define MCE_POOLSZ      (2 * PAGE_SIZE)
  24
  25static struct gen_pool *mce_evt_pool;
  26static LLIST_HEAD(mce_event_llist);
  27static char gen_pool_buf[MCE_POOLSZ];
  28
  29/*
  30 * Compare the record "t" with each of the records on list "l" to see if
  31 * an equivalent one is present in the list.
  32 */
  33static bool is_duplicate_mce_record(struct mce_evt_llist *t, struct mce_evt_llist *l)
  34{
  35        struct mce_evt_llist *node;
  36        struct mce *m1, *m2;
  37
  38        m1 = &t->mce;
  39
  40        llist_for_each_entry(node, &l->llnode, llnode) {
  41                m2 = &node->mce;
  42
  43                if (!mce_cmp(m1, m2))
  44                        return true;
  45        }
  46        return false;
  47}
  48
  49/*
  50 * The system has panicked - we'd like to peruse the list of MCE records
  51 * that have been queued, but not seen by anyone yet.  The list is in
  52 * reverse time order, so we need to reverse it. While doing that we can
  53 * also drop duplicate records (these were logged because some banks are
  54 * shared between cores or by all threads on a socket).
  55 */
  56struct llist_node *mce_gen_pool_prepare_records(void)
  57{
  58        struct llist_node *head;
  59        LLIST_HEAD(new_head);
  60        struct mce_evt_llist *node, *t;
  61
  62        head = llist_del_all(&mce_event_llist);
  63        if (!head)
  64                return NULL;
  65
  66        /* squeeze out duplicates while reversing order */
  67        llist_for_each_entry_safe(node, t, head, llnode) {
  68                if (!is_duplicate_mce_record(node, t))
  69                        llist_add(&node->llnode, &new_head);
  70        }
  71
  72        return new_head.first;
  73}
  74
  75void mce_gen_pool_process(void)
  76{
  77        struct llist_node *head;
  78        struct mce_evt_llist *node, *tmp;
  79        struct mce *mce;
  80
  81        head = llist_del_all(&mce_event_llist);
  82        if (!head)
  83                return;
  84
  85        head = llist_reverse_order(head);
  86        llist_for_each_entry_safe(node, tmp, head, llnode) {
  87                mce = &node->mce;
  88                atomic_notifier_call_chain(&x86_mce_decoder_chain, 0, mce);
  89                gen_pool_free(mce_evt_pool, (unsigned long)node, sizeof(*node));
  90        }
  91}
  92
  93bool mce_gen_pool_empty(void)
  94{
  95        return llist_empty(&mce_event_llist);
  96}
  97
  98int mce_gen_pool_add(struct mce *mce)
  99{
 100        struct mce_evt_llist *node;
 101
 102        if (!mce_evt_pool)
 103                return -EINVAL;
 104
 105        node = (void *)gen_pool_alloc(mce_evt_pool, sizeof(*node));
 106        if (!node) {
 107                pr_warn_ratelimited("MCE records pool full!\n");
 108                return -ENOMEM;
 109        }
 110
 111        memcpy(&node->mce, mce, sizeof(*mce));
 112        llist_add(&node->llnode, &mce_event_llist);
 113
 114        return 0;
 115}
 116
 117static int mce_gen_pool_create(void)
 118{
 119        struct gen_pool *tmpp;
 120        int ret = -ENOMEM;
 121
 122        tmpp = gen_pool_create(ilog2(sizeof(struct mce_evt_llist)), -1);
 123        if (!tmpp)
 124                goto out;
 125
 126        ret = gen_pool_add(tmpp, (unsigned long)gen_pool_buf, MCE_POOLSZ, -1);
 127        if (ret) {
 128                gen_pool_destroy(tmpp);
 129                goto out;
 130        }
 131
 132        mce_evt_pool = tmpp;
 133
 134out:
 135        return ret;
 136}
 137
 138int mce_gen_pool_init(void)
 139{
 140        /* Just init mce_gen_pool once. */
 141        if (mce_evt_pool)
 142                return 0;
 143
 144        return mce_gen_pool_create();
 145}
 146