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 "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 25 static struct gen_pool *mce_evt_pool; 26 static LLIST_HEAD(mce_event_llist); 27 static 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 */ 33 static 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 */ 56 struct 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 75 void mce_gen_pool_process(struct work_struct *__unused) 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 blocking_notifier_call_chain(&x86_mce_decoder_chain, 0, mce); 89 gen_pool_free(mce_evt_pool, (unsigned long)node, sizeof(*node)); 90 } 91 } 92 93 bool mce_gen_pool_empty(void) 94 { 95 return llist_empty(&mce_event_llist); 96 } 97 98 int 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 117 static 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 134 out: 135 return ret; 136 } 137 138 int 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