xref: /openbmc/linux/arch/x86/kernel/cpu/mce/genpool.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
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