1*82664963SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
221afaf18SBorislav Petkov /*
321afaf18SBorislav Petkov * MCE event pool management in MCE context
421afaf18SBorislav Petkov *
521afaf18SBorislav Petkov * Copyright (C) 2015 Intel Corp.
621afaf18SBorislav Petkov * Author: Chen, Gong <gong.chen@linux.intel.com>
721afaf18SBorislav Petkov */
821afaf18SBorislav Petkov #include <linux/smp.h>
921afaf18SBorislav Petkov #include <linux/mm.h>
1021afaf18SBorislav Petkov #include <linux/genalloc.h>
1121afaf18SBorislav Petkov #include <linux/llist.h>
1221afaf18SBorislav Petkov #include "internal.h"
1321afaf18SBorislav Petkov
1421afaf18SBorislav Petkov /*
1521afaf18SBorislav Petkov * printk() is not safe in MCE context. This is a lock-less memory allocator
1621afaf18SBorislav Petkov * used to save error information organized in a lock-less list.
1721afaf18SBorislav Petkov *
1821afaf18SBorislav Petkov * This memory pool is only to be used to save MCE records in MCE context.
1921afaf18SBorislav Petkov * MCE events are rare, so a fixed size memory pool should be enough. Use
2021afaf18SBorislav Petkov * 2 pages to save MCE events for now (~80 MCE records at most).
2121afaf18SBorislav Petkov */
2221afaf18SBorislav Petkov #define MCE_POOLSZ (2 * PAGE_SIZE)
2321afaf18SBorislav Petkov
2421afaf18SBorislav Petkov static struct gen_pool *mce_evt_pool;
2521afaf18SBorislav Petkov static LLIST_HEAD(mce_event_llist);
2621afaf18SBorislav Petkov static char gen_pool_buf[MCE_POOLSZ];
2721afaf18SBorislav Petkov
2821afaf18SBorislav Petkov /*
2921afaf18SBorislav Petkov * Compare the record "t" with each of the records on list "l" to see if
3021afaf18SBorislav Petkov * an equivalent one is present in the list.
3121afaf18SBorislav Petkov */
is_duplicate_mce_record(struct mce_evt_llist * t,struct mce_evt_llist * l)3221afaf18SBorislav Petkov static bool is_duplicate_mce_record(struct mce_evt_llist *t, struct mce_evt_llist *l)
3321afaf18SBorislav Petkov {
3421afaf18SBorislav Petkov struct mce_evt_llist *node;
3521afaf18SBorislav Petkov struct mce *m1, *m2;
3621afaf18SBorislav Petkov
3721afaf18SBorislav Petkov m1 = &t->mce;
3821afaf18SBorislav Petkov
3921afaf18SBorislav Petkov llist_for_each_entry(node, &l->llnode, llnode) {
4021afaf18SBorislav Petkov m2 = &node->mce;
4121afaf18SBorislav Petkov
4221afaf18SBorislav Petkov if (!mce_cmp(m1, m2))
4321afaf18SBorislav Petkov return true;
4421afaf18SBorislav Petkov }
4521afaf18SBorislav Petkov return false;
4621afaf18SBorislav Petkov }
4721afaf18SBorislav Petkov
4821afaf18SBorislav Petkov /*
4921afaf18SBorislav Petkov * The system has panicked - we'd like to peruse the list of MCE records
5021afaf18SBorislav Petkov * that have been queued, but not seen by anyone yet. The list is in
5121afaf18SBorislav Petkov * reverse time order, so we need to reverse it. While doing that we can
5221afaf18SBorislav Petkov * also drop duplicate records (these were logged because some banks are
5321afaf18SBorislav Petkov * shared between cores or by all threads on a socket).
5421afaf18SBorislav Petkov */
mce_gen_pool_prepare_records(void)5521afaf18SBorislav Petkov struct llist_node *mce_gen_pool_prepare_records(void)
5621afaf18SBorislav Petkov {
5721afaf18SBorislav Petkov struct llist_node *head;
5821afaf18SBorislav Petkov LLIST_HEAD(new_head);
5921afaf18SBorislav Petkov struct mce_evt_llist *node, *t;
6021afaf18SBorislav Petkov
6121afaf18SBorislav Petkov head = llist_del_all(&mce_event_llist);
6221afaf18SBorislav Petkov if (!head)
6321afaf18SBorislav Petkov return NULL;
6421afaf18SBorislav Petkov
6521afaf18SBorislav Petkov /* squeeze out duplicates while reversing order */
6621afaf18SBorislav Petkov llist_for_each_entry_safe(node, t, head, llnode) {
6721afaf18SBorislav Petkov if (!is_duplicate_mce_record(node, t))
6821afaf18SBorislav Petkov llist_add(&node->llnode, &new_head);
6921afaf18SBorislav Petkov }
7021afaf18SBorislav Petkov
7121afaf18SBorislav Petkov return new_head.first;
7221afaf18SBorislav Petkov }
7321afaf18SBorislav Petkov
mce_gen_pool_process(struct work_struct * __unused)7421afaf18SBorislav Petkov void mce_gen_pool_process(struct work_struct *__unused)
7521afaf18SBorislav Petkov {
7621afaf18SBorislav Petkov struct llist_node *head;
7721afaf18SBorislav Petkov struct mce_evt_llist *node, *tmp;
7821afaf18SBorislav Petkov struct mce *mce;
7921afaf18SBorislav Petkov
8021afaf18SBorislav Petkov head = llist_del_all(&mce_event_llist);
8121afaf18SBorislav Petkov if (!head)
8221afaf18SBorislav Petkov return;
8321afaf18SBorislav Petkov
8421afaf18SBorislav Petkov head = llist_reverse_order(head);
8521afaf18SBorislav Petkov llist_for_each_entry_safe(node, tmp, head, llnode) {
8621afaf18SBorislav Petkov mce = &node->mce;
8721afaf18SBorislav Petkov blocking_notifier_call_chain(&x86_mce_decoder_chain, 0, mce);
8821afaf18SBorislav Petkov gen_pool_free(mce_evt_pool, (unsigned long)node, sizeof(*node));
8921afaf18SBorislav Petkov }
9021afaf18SBorislav Petkov }
9121afaf18SBorislav Petkov
mce_gen_pool_empty(void)9221afaf18SBorislav Petkov bool mce_gen_pool_empty(void)
9321afaf18SBorislav Petkov {
9421afaf18SBorislav Petkov return llist_empty(&mce_event_llist);
9521afaf18SBorislav Petkov }
9621afaf18SBorislav Petkov
mce_gen_pool_add(struct mce * mce)9721afaf18SBorislav Petkov int mce_gen_pool_add(struct mce *mce)
9821afaf18SBorislav Petkov {
9921afaf18SBorislav Petkov struct mce_evt_llist *node;
10021afaf18SBorislav Petkov
10145d4b7b9SYazen Ghannam if (filter_mce(mce))
10245d4b7b9SYazen Ghannam return -EINVAL;
10345d4b7b9SYazen Ghannam
10421afaf18SBorislav Petkov if (!mce_evt_pool)
10521afaf18SBorislav Petkov return -EINVAL;
10621afaf18SBorislav Petkov
10721afaf18SBorislav Petkov node = (void *)gen_pool_alloc(mce_evt_pool, sizeof(*node));
10821afaf18SBorislav Petkov if (!node) {
10921afaf18SBorislav Petkov pr_warn_ratelimited("MCE records pool full!\n");
11021afaf18SBorislav Petkov return -ENOMEM;
11121afaf18SBorislav Petkov }
11221afaf18SBorislav Petkov
11321afaf18SBorislav Petkov memcpy(&node->mce, mce, sizeof(*mce));
11421afaf18SBorislav Petkov llist_add(&node->llnode, &mce_event_llist);
11521afaf18SBorislav Petkov
11621afaf18SBorislav Petkov return 0;
11721afaf18SBorislav Petkov }
11821afaf18SBorislav Petkov
mce_gen_pool_create(void)11921afaf18SBorislav Petkov static int mce_gen_pool_create(void)
12021afaf18SBorislav Petkov {
12121afaf18SBorislav Petkov struct gen_pool *tmpp;
12221afaf18SBorislav Petkov int ret = -ENOMEM;
12321afaf18SBorislav Petkov
12421afaf18SBorislav Petkov tmpp = gen_pool_create(ilog2(sizeof(struct mce_evt_llist)), -1);
12521afaf18SBorislav Petkov if (!tmpp)
12621afaf18SBorislav Petkov goto out;
12721afaf18SBorislav Petkov
12821afaf18SBorislav Petkov ret = gen_pool_add(tmpp, (unsigned long)gen_pool_buf, MCE_POOLSZ, -1);
12921afaf18SBorislav Petkov if (ret) {
13021afaf18SBorislav Petkov gen_pool_destroy(tmpp);
13121afaf18SBorislav Petkov goto out;
13221afaf18SBorislav Petkov }
13321afaf18SBorislav Petkov
13421afaf18SBorislav Petkov mce_evt_pool = tmpp;
13521afaf18SBorislav Petkov
13621afaf18SBorislav Petkov out:
13721afaf18SBorislav Petkov return ret;
13821afaf18SBorislav Petkov }
13921afaf18SBorislav Petkov
mce_gen_pool_init(void)14021afaf18SBorislav Petkov int mce_gen_pool_init(void)
14121afaf18SBorislav Petkov {
14221afaf18SBorislav Petkov /* Just init mce_gen_pool once. */
14321afaf18SBorislav Petkov if (mce_evt_pool)
14421afaf18SBorislav Petkov return 0;
14521afaf18SBorislav Petkov
14621afaf18SBorislav Petkov return mce_gen_pool_create();
14721afaf18SBorislav Petkov }
148