1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-17 10:26:15 +00:00

Populate the sysctl tree with any MCA records we collected.

The sequence number is used as the name of a sysctl node,
under which we add the MCA records using the CPU id as the
leaf  name.

Add the hw.mca.inject sysctl to provide a way to inject
MC errors and trigger machine checks.

PR:		ia64/113102
This commit is contained in:
Marcel Moolenaar 2010-04-13 22:20:12 +00:00
parent b74668b1df
commit 4658933f3a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=206570
2 changed files with 90 additions and 48 deletions

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2002 Marcel Moolenaar
* Copyright (c) 2002-2010 Marcel Moolenaar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -37,6 +37,7 @@
#include <vm/vm.h>
#include <vm/vm_kern.h>
#include <machine/mca.h>
#include <machine/pal.h>
#include <machine/sal.h>
#include <machine/smp.h>
@ -44,19 +45,19 @@ MALLOC_DEFINE(M_MCA, "MCA", "Machine Check Architecture");
struct mca_info {
STAILQ_ENTRY(mca_info) mi_link;
char mi_name[32];
u_long mi_seqnr;
u_int mi_cpuid;
size_t mi_recsz;
char mi_record[0];
};
static STAILQ_HEAD(, mca_info) mca_records =
STAILQ_HEAD_INITIALIZER(mca_records);
STAILQ_HEAD(mca_info_list, mca_info);
int64_t mca_info_size[SAL_INFO_TYPES];
vm_offset_t mca_info_block;
struct mtx mca_info_block_lock;
static int64_t mca_info_size[SAL_INFO_TYPES];
static vm_offset_t mca_info_block;
static struct mtx mca_info_block_lock;
SYSCTL_NODE(_hw, OID_AUTO, mca, CTLFLAG_RW, 0, "MCA container");
SYSCTL_NODE(_hw, OID_AUTO, mca, CTLFLAG_RW, NULL, "MCA container");
static int mca_count; /* Number of records stored. */
static int mca_first; /* First (lowest) record ID. */
@ -69,6 +70,32 @@ SYSCTL_INT(_hw_mca, OID_AUTO, first, CTLFLAG_RD, &mca_first, 0,
SYSCTL_INT(_hw_mca, OID_AUTO, last, CTLFLAG_RD, &mca_last, 0,
"Last record id");
static struct mtx mca_sysctl_lock;
static int
mca_sysctl_inject(SYSCTL_HANDLER_ARGS)
{
struct ia64_pal_result res;
u_int val;
int error;
val = 0;
error = sysctl_wire_old_buffer(req, sizeof(u_int));
if (!error)
error = sysctl_handle_int(oidp, &val, 0, req);
if (error != 0 || req->newptr == NULL)
return (error);
/* For example: val=137 causes a fatal CPU error. */
res = ia64_call_pal_stacked(PAL_MC_ERROR_INJECT, val, 0, 0);
printf("%s: %#lx, %#lx, %#lx, %#lx\n", __func__, res.pal_status,
res.pal_result[0], res.pal_result[1], res.pal_result[2]);
return (0);
}
SYSCTL_PROC(_hw_mca, OID_AUTO, inject, CTLTYPE_INT | CTLFLAG_RW, NULL, 0,
mca_sysctl_inject, "I", "set to trigger a MCA");
static int
mca_sysctl_handler(SYSCTL_HANDLER_ARGS)
{
@ -85,27 +112,8 @@ mca_sysctl_handler(SYSCTL_HANDLER_ARGS)
return (error);
}
void
ia64_mca_populate(void)
{
struct mca_info *rec;
mtx_lock_spin(&mca_info_block_lock);
while (!STAILQ_EMPTY(&mca_records)) {
rec = STAILQ_FIRST(&mca_records);
STAILQ_REMOVE_HEAD(&mca_records, mi_link);
mtx_unlock_spin(&mca_info_block_lock);
(void)SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_mca),
OID_AUTO, rec->mi_name, CTLTYPE_OPAQUE | CTLFLAG_RD,
rec->mi_record, rec->mi_recsz, mca_sysctl_handler, "S,MCA",
"Error record");
mtx_lock_spin(&mca_info_block_lock);
}
mtx_unlock_spin(&mca_info_block_lock);
}
void
ia64_mca_save_state(int type)
static void
ia64_mca_collect_state(int type, struct mca_info_list *reclst)
{
struct ia64_sal_result result;
struct mca_record_header *hdr;
@ -123,13 +131,13 @@ ia64_mca_save_state(int type)
if (mca_info_block == 0)
return;
mtx_lock_spin(&mca_info_block_lock);
while (1) {
mtx_lock_spin(&mca_info_block_lock);
result = ia64_sal_entry(SAL_GET_STATE_INFO, type, 0,
mca_info_block, 0, 0, 0, 0);
if (result.sal_status < 0) {
mtx_unlock_spin(&mca_info_block_lock);
return;
break;
}
hdr = (struct mca_record_header *)mca_info_block;
@ -142,9 +150,10 @@ ia64_mca_save_state(int type)
M_NOWAIT | M_ZERO);
if (rec == NULL)
/* XXX: Not sure what to do. */
return;
break;
sprintf(rec->mi_name, "%lld", (long long)seqnr);
rec->mi_seqnr = seqnr;
rec->mi_cpuid = PCPU_GET(cpuid);
mtx_lock_spin(&mca_info_block_lock);
@ -163,7 +172,6 @@ ia64_mca_save_state(int type)
if (seqnr != hdr->rh_seqnr) {
mtx_unlock_spin(&mca_info_block_lock);
free(rec, M_MCA);
mtx_lock_spin(&mca_info_block_lock);
continue;
}
}
@ -171,23 +179,51 @@ ia64_mca_save_state(int type)
rec->mi_recsz = recsz;
bcopy((char*)mca_info_block, rec->mi_record, recsz);
if (mca_count > 0) {
if (seqnr < mca_first)
mca_first = seqnr;
else if (seqnr > mca_last)
mca_last = seqnr;
} else
mca_first = mca_last = seqnr;
mca_count++;
STAILQ_INSERT_TAIL(&mca_records, rec, mi_link);
/*
* Clear the state so that we get any other records when
* they exist.
*/
result = ia64_sal_entry(SAL_CLEAR_STATE_INFO, type, 0, 0, 0,
0, 0, 0);
mtx_unlock_spin(&mca_info_block_lock);
STAILQ_INSERT_TAIL(reclst, rec, mi_link);
}
}
void
ia64_mca_save_state(int type)
{
char name[64];
struct mca_info_list reclst = STAILQ_HEAD_INITIALIZER(reclst);
struct mca_info *rec;
struct sysctl_oid *oid;
ia64_mca_collect_state(type, &reclst);
STAILQ_FOREACH(rec, &reclst, mi_link) {
sprintf(name, "%lu", rec->mi_seqnr);
oid = SYSCTL_ADD_NODE(NULL, SYSCTL_STATIC_CHILDREN(_hw_mca),
OID_AUTO, name, CTLFLAG_RW, NULL, name);
if (oid == NULL)
continue;
mtx_lock(&mca_sysctl_lock);
if (mca_count > 0) {
if (rec->mi_seqnr < mca_first)
mca_first = rec->mi_seqnr;
else if (rec->mi_seqnr > mca_last)
mca_last = rec->mi_seqnr;
} else
mca_first = mca_last = rec->mi_seqnr;
mca_count++;
mtx_unlock(&mca_sysctl_lock);
sprintf(name, "%u", rec->mi_cpuid);
SYSCTL_ADD_PROC(NULL, SYSCTL_CHILDREN(oid), rec->mi_cpuid,
name, CTLTYPE_OPAQUE | CTLFLAG_RD, rec->mi_record,
rec->mi_recsz, mca_sysctl_handler, "S,MCA", "MCA record");
}
}
@ -237,7 +273,14 @@ ia64_mca_init(void)
* should be rare. On top of that, performance is not an issue when
* dealing with machine checks...
*/
mtx_init(&mca_info_block_lock, "MCA spin lock", NULL, MTX_SPIN);
mtx_init(&mca_info_block_lock, "MCA info lock", NULL, MTX_SPIN);
/*
* Serialize sysctl operations with a sleep lock. Note that this
* implies that we update the sysctl tree in a context that allows
* sleeping.
*/
mtx_init(&mca_sysctl_lock, "MCA sysctl lock", NULL, MTX_DEF);
/*
* Get and save any processor and platfom error records. Note that in

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2002 Marcel Moolenaar
* Copyright (c) 2002-2010 Marcel Moolenaar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -240,7 +240,6 @@ struct mca_pcidev_reg {
#ifdef _KERNEL
void ia64_mca_init(void);
void ia64_mca_populate(void);
void ia64_mca_save_state(int);
#endif /* _KERNEL */