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 29void mce_gen_pool_process(void) 30{ 31 struct llist_node *head; 32 struct mce_evt_llist *node, *tmp; 33 struct mce *mce; 34 35 head = llist_del_all(&mce_event_llist); 36 if (!head) 37 return; 38 39 head = llist_reverse_order(head); 40 llist_for_each_entry_safe(node, tmp, head, llnode) { 41 mce = &node->mce; 42 atomic_notifier_call_chain(&x86_mce_decoder_chain, 0, mce); 43 gen_pool_free(mce_evt_pool, (unsigned long)node, sizeof(*node)); 44 } 45} 46 47bool mce_gen_pool_empty(void) 48{ 49 return llist_empty(&mce_event_llist); 50} 51 52int mce_gen_pool_add(struct mce *mce) 53{ 54 struct mce_evt_llist *node; 55 56 if (!mce_evt_pool) 57 return -EINVAL; 58 59 node = (void *)gen_pool_alloc(mce_evt_pool, sizeof(*node)); 60 if (!node) { 61 pr_warn_ratelimited("MCE records pool full!\n"); 62 return -ENOMEM; 63 } 64 65 memcpy(&node->mce, mce, sizeof(*mce)); 66 llist_add(&node->llnode, &mce_event_llist); 67 68 return 0; 69} 70 71static int mce_gen_pool_create(void) 72{ 73 struct gen_pool *tmpp; 74 int ret = -ENOMEM; 75 76 tmpp = gen_pool_create(ilog2(sizeof(struct mce_evt_llist)), -1); 77 if (!tmpp) 78 goto out; 79 80 ret = gen_pool_add(tmpp, (unsigned long)gen_pool_buf, MCE_POOLSZ, -1); 81 if (ret) { 82 gen_pool_destroy(tmpp); 83 goto out; 84 } 85 86 mce_evt_pool = tmpp; 87 88out: 89 return ret; 90} 91 92int mce_gen_pool_init(void) 93{ 94 /* Just init mce_gen_pool once. */ 95 if (mce_evt_pool) 96 return 0; 97 98 return mce_gen_pool_create(); 99} 100