// SPDX-License-Identifier: GPL-2.0-only
/*
* MCE event pool management in MCE context
*
* Copyright ( C ) 2015 Intel Corp .
* Author : Chen , Gong < gong . chen @ linux . intel . com >
*/
#include <linux/smp.h>
#include <linux/mm.h>
#include <linux/genalloc.h>
#include <linux/llist.h>
#include "internal.h"
/*
* printk ( ) is not safe in MCE context . This is a lock - less memory allocator
* used to save error information organized in a lock - less list .
*
* This memory pool is only to be used to save MCE records in MCE context .
* MCE events are rare , so a fixed size memory pool should be enough .
* Allocate on a sliding scale based on number of CPUs .
*/
#define MCE_MIN_ENTRIES 80
#define MCE_PER_CPU 2
static struct gen_pool *mce_evt_pool;
static LLIST_HEAD(mce_event_llist);
/*
* Compare the record " t " with each of the records on list " l " to see if
* an equivalent one is present in the list .
*/
static bool is_duplicate_mce_record(struct mce_evt_llist *t, struct mce_evt_llist *l)
{
struct mce_hw_err *err1, *err2;
struct mce_evt_llist *node;
err1 = &t->err;
llist_for_each_entry(node, &l->llnode, llnode) {
err2 = &node->err;
if (!mce_cmp(&err1->m, &err2->m))
return true ;
}
return false ;
}
/*
* The system has panicked - we ' d like to peruse the list of MCE records
* that have been queued , but not seen by anyone yet . The list is in
* reverse time order , so we need to reverse it . While doing that we can
* also drop duplicate records ( these were logged because some banks are
* shared between cores or by all threads on a socket ) .
*/
struct llist_node *mce_gen_pool_prepare_records(void )
{
struct llist_node *head;
LLIST_HEAD(new_head);
struct mce_evt_llist *node, *t;
head = llist_del_all(&mce_event_llist);
if (!head)
return NULL;
/* squeeze out duplicates while reversing order */
llist_for_each_entry_safe(node, t, head, llnode) {
if (!is_duplicate_mce_record(node, t))
llist_add(&node->llnode, &new_head);
}
return new_head.first;
}
void mce_gen_pool_process(struct work_struct *__unused)
{
struct mce_evt_llist *node, *tmp;
struct llist_node *head;
struct mce *mce;
head = llist_del_all(&mce_event_llist);
if (!head)
return ;
head = llist_reverse_order(head);
llist_for_each_entry_safe(node, tmp, head, llnode) {
mce = &node->err.m;
blocking_notifier_call_chain(&x86_mce_decoder_chain, 0 , mce);
gen_pool_free(mce_evt_pool, (unsigned long )node, sizeof (*node));
}
}
bool mce_gen_pool_empty(void )
{
return llist_empty(&mce_event_llist);
}
bool mce_gen_pool_add(struct mce_hw_err *err)
{
struct mce_evt_llist *node;
if (filter_mce(&err->m))
return false ;
if (!mce_evt_pool)
return false ;
node = (void *)gen_pool_alloc(mce_evt_pool, sizeof (*node));
if (!node) {
pr_warn_ratelimited("MCE records pool full!\n" );
return false ;
}
memcpy(&node->err, err, sizeof (*err));
llist_add(&node->llnode, &mce_event_llist);
return true ;
}
static bool mce_gen_pool_create(void )
{
int mce_numrecords, mce_poolsz, order;
struct gen_pool *gpool;
void *mce_pool;
order = order_base_2(sizeof (struct mce_evt_llist));
gpool = gen_pool_create(order, -1 );
if (!gpool)
return false ;
mce_numrecords = max(MCE_MIN_ENTRIES, num_possible_cpus() * MCE_PER_CPU);
mce_poolsz = mce_numrecords * (1 << order);
mce_pool = kmalloc(mce_poolsz, GFP_KERNEL);
if (!mce_pool) {
gen_pool_destroy(gpool);
return false ;
}
if (gen_pool_add(gpool, (unsigned long )mce_pool, mce_poolsz, -1 )) {
gen_pool_destroy(gpool);
kfree(mce_pool);
return false ;
}
mce_evt_pool = gpool;
return true ;
}
bool mce_gen_pool_init(void )
{
/* Just init mce_gen_pool once. */
if (mce_evt_pool)
return true ;
return mce_gen_pool_create();
}
Messung V0.5 in Prozent C=96 H=100 G=97
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-10-03)
¤
*© Formatika GbR, Deutschland