| 1 | // SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 2 | /* | 
|---|
| 3 | * MCE event pool management in MCE context | 
|---|
| 4 | * | 
|---|
| 5 | * Copyright (C) 2015 Intel Corp. | 
|---|
| 6 | * Author: Chen, Gong <gong.chen@linux.intel.com> | 
|---|
| 7 | */ | 
|---|
| 8 | #include <linux/smp.h> | 
|---|
| 9 | #include <linux/mm.h> | 
|---|
| 10 | #include <linux/genalloc.h> | 
|---|
| 11 | #include <linux/llist.h> | 
|---|
| 12 | #include "internal.h" | 
|---|
| 13 |  | 
|---|
| 14 | /* | 
|---|
| 15 | * printk() is not safe in MCE context. This is a lock-less memory allocator | 
|---|
| 16 | * used to save error information organized in a lock-less list. | 
|---|
| 17 | * | 
|---|
| 18 | * This memory pool is only to be used to save MCE records in MCE context. | 
|---|
| 19 | * MCE events are rare, so a fixed size memory pool should be enough. | 
|---|
| 20 | * Allocate on a sliding scale based on number of CPUs. | 
|---|
| 21 | */ | 
|---|
| 22 | #define MCE_MIN_ENTRIES	80 | 
|---|
| 23 | #define MCE_PER_CPU	2 | 
|---|
| 24 |  | 
|---|
| 25 | static struct gen_pool *mce_evt_pool; | 
|---|
| 26 | static LLIST_HEAD(mce_event_llist); | 
|---|
| 27 |  | 
|---|
| 28 | /* | 
|---|
| 29 | * Compare the record "t" with each of the records on list "l" to see if | 
|---|
| 30 | * an equivalent one is present in the list. | 
|---|
| 31 | */ | 
|---|
| 32 | static bool is_duplicate_mce_record(struct mce_evt_llist *t, struct mce_evt_llist *l) | 
|---|
| 33 | { | 
|---|
| 34 | struct mce_hw_err *err1, *err2; | 
|---|
| 35 | struct mce_evt_llist *node; | 
|---|
| 36 |  | 
|---|
| 37 | err1 = &t->err; | 
|---|
| 38 |  | 
|---|
| 39 | llist_for_each_entry(node, &l->llnode, llnode) { | 
|---|
| 40 | err2 = &node->err; | 
|---|
| 41 |  | 
|---|
| 42 | if (!mce_cmp(m1: &err1->m, m2: &err2->m)) | 
|---|
| 43 | return true; | 
|---|
| 44 | } | 
|---|
| 45 | return false; | 
|---|
| 46 | } | 
|---|
| 47 |  | 
|---|
| 48 | /* | 
|---|
| 49 | * The system has panicked - we'd like to peruse the list of MCE records | 
|---|
| 50 | * that have been queued, but not seen by anyone yet.  The list is in | 
|---|
| 51 | * reverse time order, so we need to reverse it. While doing that we can | 
|---|
| 52 | * also drop duplicate records (these were logged because some banks are | 
|---|
| 53 | * shared between cores or by all threads on a socket). | 
|---|
| 54 | */ | 
|---|
| 55 | struct llist_node *mce_gen_pool_prepare_records(void) | 
|---|
| 56 | { | 
|---|
| 57 | struct llist_node *head; | 
|---|
| 58 | LLIST_HEAD(new_head); | 
|---|
| 59 | struct mce_evt_llist *node, *t; | 
|---|
| 60 |  | 
|---|
| 61 | head = llist_del_all(head: &mce_event_llist); | 
|---|
| 62 | if (!head) | 
|---|
| 63 | return NULL; | 
|---|
| 64 |  | 
|---|
| 65 | /* squeeze out duplicates while reversing order */ | 
|---|
| 66 | llist_for_each_entry_safe(node, t, head, llnode) { | 
|---|
| 67 | if (!is_duplicate_mce_record(t: node, l: t)) | 
|---|
| 68 | llist_add(new: &node->llnode, head: &new_head); | 
|---|
| 69 | } | 
|---|
| 70 |  | 
|---|
| 71 | return new_head.first; | 
|---|
| 72 | } | 
|---|
| 73 |  | 
|---|
| 74 | void mce_gen_pool_process(struct work_struct *__unused) | 
|---|
| 75 | { | 
|---|
| 76 | struct mce_evt_llist *node, *tmp; | 
|---|
| 77 | struct llist_node *head; | 
|---|
| 78 | struct mce *mce; | 
|---|
| 79 |  | 
|---|
| 80 | head = llist_del_all(head: &mce_event_llist); | 
|---|
| 81 | if (!head) | 
|---|
| 82 | return; | 
|---|
| 83 |  | 
|---|
| 84 | head = llist_reverse_order(head); | 
|---|
| 85 | llist_for_each_entry_safe(node, tmp, head, llnode) { | 
|---|
| 86 | mce = &node->err.m; | 
|---|
| 87 | blocking_notifier_call_chain(nh: &x86_mce_decoder_chain, val: 0, v: mce); | 
|---|
| 88 | gen_pool_free(pool: mce_evt_pool, addr: (unsigned long)node, size: sizeof(*node)); | 
|---|
| 89 | } | 
|---|
| 90 | } | 
|---|
| 91 |  | 
|---|
| 92 | bool mce_gen_pool_empty(void) | 
|---|
| 93 | { | 
|---|
| 94 | return llist_empty(head: &mce_event_llist); | 
|---|
| 95 | } | 
|---|
| 96 |  | 
|---|
| 97 | bool mce_gen_pool_add(struct mce_hw_err *err) | 
|---|
| 98 | { | 
|---|
| 99 | struct mce_evt_llist *node; | 
|---|
| 100 |  | 
|---|
| 101 | if (filter_mce(m: &err->m)) | 
|---|
| 102 | return false; | 
|---|
| 103 |  | 
|---|
| 104 | if (!mce_evt_pool) | 
|---|
| 105 | return false; | 
|---|
| 106 |  | 
|---|
| 107 | node = (void *)gen_pool_alloc(pool: mce_evt_pool, size: sizeof(*node)); | 
|---|
| 108 | if (!node) { | 
|---|
| 109 | pr_warn_ratelimited( "MCE records pool full!\n"); | 
|---|
| 110 | return false; | 
|---|
| 111 | } | 
|---|
| 112 |  | 
|---|
| 113 | memcpy(to: &node->err, from: err, len: sizeof(*err)); | 
|---|
| 114 | llist_add(new: &node->llnode, head: &mce_event_llist); | 
|---|
| 115 |  | 
|---|
| 116 | return true; | 
|---|
| 117 | } | 
|---|
| 118 |  | 
|---|
| 119 | static bool mce_gen_pool_create(void) | 
|---|
| 120 | { | 
|---|
| 121 | int mce_numrecords, mce_poolsz, order; | 
|---|
| 122 | struct gen_pool *gpool; | 
|---|
| 123 | void *mce_pool; | 
|---|
| 124 |  | 
|---|
| 125 | order = order_base_2(sizeof(struct mce_evt_llist)); | 
|---|
| 126 | gpool = gen_pool_create(order, -1); | 
|---|
| 127 | if (!gpool) | 
|---|
| 128 | return false; | 
|---|
| 129 |  | 
|---|
| 130 | mce_numrecords = max(MCE_MIN_ENTRIES, num_possible_cpus() * MCE_PER_CPU); | 
|---|
| 131 | mce_poolsz = mce_numrecords * (1 << order); | 
|---|
| 132 | mce_pool = kmalloc(mce_poolsz, GFP_KERNEL); | 
|---|
| 133 | if (!mce_pool) { | 
|---|
| 134 | gen_pool_destroy(gpool); | 
|---|
| 135 | return false; | 
|---|
| 136 | } | 
|---|
| 137 |  | 
|---|
| 138 | if (gen_pool_add(pool: gpool, addr: (unsigned long)mce_pool, size: mce_poolsz, nid: -1)) { | 
|---|
| 139 | gen_pool_destroy(gpool); | 
|---|
| 140 | kfree(objp: mce_pool); | 
|---|
| 141 | return false; | 
|---|
| 142 | } | 
|---|
| 143 |  | 
|---|
| 144 | mce_evt_pool = gpool; | 
|---|
| 145 |  | 
|---|
| 146 | return true; | 
|---|
| 147 | } | 
|---|
| 148 |  | 
|---|
| 149 | bool mce_gen_pool_init(void) | 
|---|
| 150 | { | 
|---|
| 151 | /* Just init mce_gen_pool once. */ | 
|---|
| 152 | if (mce_evt_pool) | 
|---|
| 153 | return true; | 
|---|
| 154 |  | 
|---|
| 155 | return mce_gen_pool_create(); | 
|---|
| 156 | } | 
|---|
| 157 |  | 
|---|