Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * MCE event pool management in MCE context
0004  *
0005  * Copyright (C) 2015 Intel Corp.
0006  * Author: Chen, Gong <gong.chen@linux.intel.com>
0007  */
0008 #include <linux/smp.h>
0009 #include <linux/mm.h>
0010 #include <linux/genalloc.h>
0011 #include <linux/llist.h>
0012 #include "internal.h"
0013 
0014 /*
0015  * printk() is not safe in MCE context. This is a lock-less memory allocator
0016  * used to save error information organized in a lock-less list.
0017  *
0018  * This memory pool is only to be used to save MCE records in MCE context.
0019  * MCE events are rare, so a fixed size memory pool should be enough. Use
0020  * 2 pages to save MCE events for now (~80 MCE records at most).
0021  */
0022 #define MCE_POOLSZ  (2 * PAGE_SIZE)
0023 
0024 static struct gen_pool *mce_evt_pool;
0025 static LLIST_HEAD(mce_event_llist);
0026 static char gen_pool_buf[MCE_POOLSZ];
0027 
0028 /*
0029  * Compare the record "t" with each of the records on list "l" to see if
0030  * an equivalent one is present in the list.
0031  */
0032 static bool is_duplicate_mce_record(struct mce_evt_llist *t, struct mce_evt_llist *l)
0033 {
0034     struct mce_evt_llist *node;
0035     struct mce *m1, *m2;
0036 
0037     m1 = &t->mce;
0038 
0039     llist_for_each_entry(node, &l->llnode, llnode) {
0040         m2 = &node->mce;
0041 
0042         if (!mce_cmp(m1, m2))
0043             return true;
0044     }
0045     return false;
0046 }
0047 
0048 /*
0049  * The system has panicked - we'd like to peruse the list of MCE records
0050  * that have been queued, but not seen by anyone yet.  The list is in
0051  * reverse time order, so we need to reverse it. While doing that we can
0052  * also drop duplicate records (these were logged because some banks are
0053  * shared between cores or by all threads on a socket).
0054  */
0055 struct llist_node *mce_gen_pool_prepare_records(void)
0056 {
0057     struct llist_node *head;
0058     LLIST_HEAD(new_head);
0059     struct mce_evt_llist *node, *t;
0060 
0061     head = llist_del_all(&mce_event_llist);
0062     if (!head)
0063         return NULL;
0064 
0065     /* squeeze out duplicates while reversing order */
0066     llist_for_each_entry_safe(node, t, head, llnode) {
0067         if (!is_duplicate_mce_record(node, t))
0068             llist_add(&node->llnode, &new_head);
0069     }
0070 
0071     return new_head.first;
0072 }
0073 
0074 void mce_gen_pool_process(struct work_struct *__unused)
0075 {
0076     struct llist_node *head;
0077     struct mce_evt_llist *node, *tmp;
0078     struct mce *mce;
0079 
0080     head = llist_del_all(&mce_event_llist);
0081     if (!head)
0082         return;
0083 
0084     head = llist_reverse_order(head);
0085     llist_for_each_entry_safe(node, tmp, head, llnode) {
0086         mce = &node->mce;
0087         blocking_notifier_call_chain(&x86_mce_decoder_chain, 0, mce);
0088         gen_pool_free(mce_evt_pool, (unsigned long)node, sizeof(*node));
0089     }
0090 }
0091 
0092 bool mce_gen_pool_empty(void)
0093 {
0094     return llist_empty(&mce_event_llist);
0095 }
0096 
0097 int mce_gen_pool_add(struct mce *mce)
0098 {
0099     struct mce_evt_llist *node;
0100 
0101     if (filter_mce(mce))
0102         return -EINVAL;
0103 
0104     if (!mce_evt_pool)
0105         return -EINVAL;
0106 
0107     node = (void *)gen_pool_alloc(mce_evt_pool, sizeof(*node));
0108     if (!node) {
0109         pr_warn_ratelimited("MCE records pool full!\n");
0110         return -ENOMEM;
0111     }
0112 
0113     memcpy(&node->mce, mce, sizeof(*mce));
0114     llist_add(&node->llnode, &mce_event_llist);
0115 
0116     return 0;
0117 }
0118 
0119 static int mce_gen_pool_create(void)
0120 {
0121     struct gen_pool *tmpp;
0122     int ret = -ENOMEM;
0123 
0124     tmpp = gen_pool_create(ilog2(sizeof(struct mce_evt_llist)), -1);
0125     if (!tmpp)
0126         goto out;
0127 
0128     ret = gen_pool_add(tmpp, (unsigned long)gen_pool_buf, MCE_POOLSZ, -1);
0129     if (ret) {
0130         gen_pool_destroy(tmpp);
0131         goto out;
0132     }
0133 
0134     mce_evt_pool = tmpp;
0135 
0136 out:
0137     return ret;
0138 }
0139 
0140 int mce_gen_pool_init(void)
0141 {
0142     /* Just init mce_gen_pool once. */
0143     if (mce_evt_pool)
0144         return 0;
0145 
0146     return mce_gen_pool_create();
0147 }