0001
0002
0003
0004
0005
0006
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
0016
0017
0018
0019
0020
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
0030
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
0050
0051
0052
0053
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
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
0143 if (mce_evt_pool)
0144 return 0;
0145
0146 return mce_gen_pool_create();
0147 }