Sample Kernel Extension
The following sample is a kernel extension that takes a live dump and a system dump.
The sample_callback function takes a live dump and a system dump using the commands that are sent by the system and passed to the ras_control kernel service. The sample only shows the handling of the dump commands. Normally, the callback must handle component trace and error checking commands.
/*
* This sample creates a component, makes it dump-aware, and handles both live
* and system dump.
*/
#include <sys/types.h>
#include <sys/syspest.h>
#include <sys/uio.h>
#include <sys/processor.h>
#include <sys/systemcfg.h>
#include <sys/malloc.h>
#include <sys/ras.h>
#include <sys/livedump.h>
#include <sys/kerrnodefs.h>
#include <sys/eyec.h>
#include <sys/raschk.h>
#include <sys/param.h>
/* Component name and handle */
const char Compname[] = "sample_comp";
ras_block_t Rascb=NULL;
/*
* The sample data dumped consists of a header plus an unlimited number
* of chained control blocks.
* The header is dumped first.
* Because we are using an unlimited dump table, the callback then gets an
* "AGAIN" call to dump the rest of the data, the control blocks.
* We'll dump these 3 at a time until finished.
* The staging area is used for the dump table.
*/
#define NENTRIES 3
/* Staging area size for dumping the header. */
#define SZ1 (sizeof(struct cdt_nn_head) + sizeof(struct cdt_entry_u))
/* Staging area size for 3 control blocks. */
#define SZ2 (sizeof(struct cdt_nn_head) + DMP_DUL_SIZE(NENTRIES))
/* Staging buffer size is the MAX of SZ1 and SZ2 above. */
size_t Sbufsz;
/*
* To estimate the dump table space, we need SZ1 plus the unlimited entry
* for how many control blocks we have at dump time.
*/
#define TBLESTSZ(n) (SZ1 + DMP_DUL_SIZE(n))
/*
* This is the sample data.
* It would normally be protected with a lock, however, that is not shown here.
*/
typedef struct sample_cb {
eye_catch8b_t scb_eyec; /* must be EYEC_SCB */
struct sample_cb *scb_next; /* list ptr, terminated with
INVALID_SCB_PTR */
long scb_flags;
long scb_fld1;
} sample_cb_t;
#define EYEC_SCB __EYEC8('s','a','m','p','l','A','B','C')
#define INVALID_SCB_PTR INVALID_PTR(EYEC_SCB)
typedef struct sample_hdr {
eye_catch8b_t sh_eyec; /* must be EYEC_SH */
long sh_flags;
sample_cb_t *sh_cbp; /* ptr to first cb */
long sh_fld1;
} sample_hdr_t;
#define EYEC_SH __EYEC8('s','a','h','d','r','A','B','C')
sample_hdr_t Sample_hdr = {EYEC_SH, 0, INVALID_SCB_PTR, 0};
kerrno_t sample_callback(ras_block_t cb, ras_cmd_t cmd, void *arg, void *priv);
int alloc_sample(int n);
void free_sample();
static sample_cb_t *get_scbp(sample_cb_t *addr, dumpid_t id, int ldmpflag);
/*
* Entry point called when this kernel extension is loaded.
*
* Input:
* cmd - unused (typically 1=config, 2=unconfig)
* uiop - points to the uio structure.
*/
int
sampleext(int cmd, struct uio *uiop)
{
kerrno_t rv = 0;
int rc;
/* cmd should be 1 or 2 */
if (cmd == 2) {
/* Unloading */
if (Rascb) ras_unregister(Rascb);
free_sample();
return(0);
}
if (cmd != 1) return(EINVAL);
/* Set up local variables. */
Sbufsz = MAX(SZ1, SZ2);
/* The extension is being loaded, set up the sample data. */
rc = alloc_sample(NENTRIES);
if (rc) return(rc);
/* Register the component as dump aware */
rv = ras_register(&Rascb,
(char*)Compname,
(ras_block_t)0,
RAS_TYPE_OTHER,
"sample component",
RASF_DUMP_AWARE,
sample_callback,
NULL);
if (rv) return(KERROR2ERRNO(rv));
/* Make the component system and live dump aware. */
rv = ras_control(Rascb, RASCD_SET_SDMP_ON, 0, 0);
if (rv) return(KERROR2ERRNO(rv));
rv = ras_control(Rascb, RASCD_SET_LDMP_ON, 0, 0);
if (rv) return(KERROR2ERRNO(rv));
/*
* System dump staging buffer space must be set up before a
* system dump occurs.
* Staging buffer space for live dumps is set up by the callback at
* live dump time.
*/
rv = ras_control(Rascb, RASCD_SET_SDMP_STAGING,
(void*)Sbufsz, 0);
if (rv) return(KERROR2ERRNO(rv));
/*
* The component must be customized.
* It uses the default level, CD_LVL_NORMAL.
*/
rv = ras_customize(Rascb);
if (rv) return(KERROR2ERRNO(rv));
return(0);
}
/*
* Sample callback that is called for live and system dumps.
*
* The data to dump consists of a header and
* control blocks. The data is dumped using an unlimited dump table.
* The header is dumped first, followed by the control blocks, dumped 3 at a
* time until all have been dumped.
*
* Input:
* cb - Contains the component's ras_block_t
* cmd - ras_control command
* arg - command argument
* priv - private data, unused
*/
kerrno_t
sample_callback(ras_block_t cb, ras_cmd_t cmd, void *arg, void *priv)
{
kerrno_t rv = 0;
sample_cb_t *cbp, **wkptr;
switch(cmd) {
/* Live dump */
case RASCD_LDMP_ON: {
/* Turn live dump on. */
rv = ras_control(cb, RASCD_SET_LDMP_ON, 0, 0);
break;
}
case RASCD_LDMP_OFF: {
/* Turn live dump off. */
rv = ras_control(cb, RASCD_SET_LDMP_OFF, 0, 0);
break;
}
case RASCD_LDMP_LVL: {
/* Set the detail level at which this component will dump. */
rv = ras_control(cb, RASCD_SET_LDMP_LVL, arg, 0);
break;
}
case RASCD_LDMP_ESTIMATE: /* fall through */
case RASCD_LDMP_PREPARE:{
/*
* An estimate, as a prepare, is done in the same way.
* The estimate is received if the livedumpstart command is used
* and the -e flag is specified.
* The prepare is received when the component is participating
* in a live dump. The prepare call is used to request
* staging buffer space and provide an estimate of the amount
* of data to be dumped. The sample also requests that the component
* trace be dumped at this time.
*/
ldmp_prepare_t *p = (ldmp_prepare_t*)arg;
int n = 0;
/* Staging buffer used for dump table */
p->ldpr_sbufsz = Sbufsz;
/* Data size - need # cbs */
for (cbp=get_scbp(Sample_hdr.sh_cbp, p->ldpr_dumpid, 1), n=0;
cbp!=INVALID_SCB_PTR;
cbp=get_scbp(cbp->scb_next, p->ldpr_dumpid, 1), n++);
p->ldpr_datasize = TBLESTSZ(n) + sizeof(Sample_hdr) +
n*sizeof(sample_cb_t);
/* Dump all of our component trace. */
rv = dmp_ct(0, p, "", Rascb, 0);
/*
* If an error occurred, ldmp_errstr() puts the message in
* the dump. The sample returns 0, so the dump
* proceeds normally.
* If the sample returned a value > 0,
* livedump() would put a generic error containing the
* return value in the dump.
*/
if (rv) {
char str[40];
sprintf(str, "dmp_ct returned 0x%lx.\n", rv);
rv = 0;
(void)ldmp_errstr(p->ldpr_dumpid, Rascb, str);
}
break;
}
case RASCD_LDMP_START:{
/*
* This is received to provide the dump table.
* Because the table is an unlimited table, subsequent
* RASCD_LDMP_AGAIN calls will be received.
*/
ldmp_start_t *p = (ldmp_start_t*)arg;
struct cdt_nn_head_u *hp;
struct cdt_entry_u *ep;
/* The dump table goes in the staging area */
hp = (struct cdt_nn_head_u*)p->ldmpst_buffer;
ep = (struct cdt_entry_u*)((struct cdt_nn_u*)hp)->cdtnu_entry;
/* Set up cdt_nn_head_u */
hp->cdtnu_magic = DMP_MAGIC_NU;
hp->cdtnu_nentries = 1;
/* Set up cdt_entry_u */
ep->du_magic = DMP_MAGIC_UD;
strcpy(ep->du_name, "header");
ep->du_len = sizeof(Sample_hdr);
ep->du_ptr = &Sample_hdr
ep->du_segval = DUMP_GEN_SEGVAL;
ep->du_xmemp = NULL;
p->ldmpst_table = hp;
/*
* There is a work area in the ldmp_prepare_t and ldmp_start_t
* data areas for use in keeping the state across dump calls,
* generally between RASCD_LDMP_START and RASCD_LDMP_AGAIN
* commands.
* In our case, we'll keep a pointer to the next cb to dump,
* NULL initially.
*/
wkptr = (sample_cb_t**)p->ldmpst_wk;
*wkptr = NULL;
break;
}
case RASCD_LDMP_AGAIN:{
/*
* This is similar to the RASCD_LDMP_START command, but is received to dump
* subsequent data for an unlimited dump table.
*/
int i;
ldmp_start_t *p = (ldmp_start_t*)arg;
struct cdt_nn_head_u *hp;
struct cdt_entry_ul *up;
sample_cb_t *cbp;
/* The dump table goes in the staging area */
hp = (struct cdt_nn_head_u*)p->ldmpst_buffer;
up = (struct cdt_entry_ul*)((struct cdt_nn_u*)hp)->cdtnu_entry;
/* Point to the first/next cb */
wkptr = (sample_cb_t**)p->ldmpst_wk;
/* For the first AGAIN call, cbp will be Sample_hdr.sh_cbp. */
cbp = (*wkptr)? *wkptr: Sample_hdr.sh_cbp;
/* Validate the pointer. */
cbp = get_scbp(cbp, p->ldmpst_dumpid, 1);
/* Set up cdt_nn_head_u */
hp->cdtnu_magic = DMP_MAGIC_NU;
hp->cdtnu_nentries = 1;
/* Set up cdt_entry_ul */
up->dul_magic = DMP_MAGIC_UL;
strcpy(up->dul_name, "cb");
up->dul_nentries = 0;
up->dul_len = sizeof(sample_cb_t);
/* Dump up to 3, NENTRIES, control blocks. */
for (i=0; iscb_next, p->ldmpst_dumpid, 1)) {
up->dul_entry[i].dle_vmhandle = DUMP_GEN_SEGVAL;
up->dul_entry[i].dle_ptr = cbp;
up->dul_nentries++;
}
/* Save address of next cb. */
*wkptr = cbp;
/* Set the table address to NULL when finished. */
p->ldmpst_table = (!up->dul_nentries)? NULL: p->ldmpst_buffer;
break;
}
case RASCD_LDMP_FINISHED:
/* Nothing to do here. */
break;
/* System dump */
case RASCD_SDMP_ON: {
/* Turn system dump on. */
rv = ras_control(cb, RASCD_SET_SDMP_ON, 0, 0);
break;
}
case RASCD_SDMP_OFF: {
/* Turn system dump off. */
rv = ras_control(cb, RASCD_SET_SDMP_OFF, 0, 0);
break;
}
case RASCD_SDMP_LVL: {
/* Set the detail level at which this component will dump. */
rv = ras_control(cb, RASCD_SET_SDMP_LVL, arg, 0);
break;
}
case RASCD_SDMP_ESTIMATE:{
sysdump_estimate_t *p = (sysdump_estimate_t*)arg;
int n;
/* Data size - need # cbs */
for (cbp=get_scbp(Sample_hdr.sh_cbp, 0, 0), n=0;
cbp!=INVALID_SCB_PTR;
cbp=get_scbp(cbp->scb_next, 0, 0), n++);
p->se_value = TBLESTSZ(n) + sizeof(Sample_hdr) +
n*sizeof(sample_cb_t);
break;
}
case RASCD_SDMP_START:{
/*
* This is received to provide the dump table.
* Since the table is an unlimited table, subsequent
* RASCD_SDMP_AGAIN calls will be received.
*/
sdmp_start_t *p = (sdmp_start_t*)arg;
struct cdt_nn_head_u *hp;
struct cdt_entry_u *ep;
/* The dump table goes in the staging area */
hp = (struct cdt_nn_head_u*)p->sdmpst_buffer;
ep = (struct cdt_entry_u*)((struct cdt_nn_u*)hp)->cdtnu_entry;
/* Set up cdt_nn_head_u */
hp->cdtnu_magic = DMP_MAGIC_NU;
hp->cdtnu_nentries = 1;
/* Set up cdt_entry_u */
ep->du_magic = DMP_MAGIC_UD;
strcpy(ep->du_name, "header");
ep->du_len = sizeof(Sample_hdr);
ep->du_ptr = &Sample_hdr
ep->du_segval = DUMP_GEN_SEGVAL;
ep->du_xmemp = NULL;
p->sdmpst_table = hp;
/*
* There is a work area in the sdmp_start_t data area
* for use in keeping the state across dump calls, generally
* between RASCD_SDMP_START and RASCD_SDMP_AGAIN
* commands.
* In our case, we'll keep a pointer to the next cb to dump,
* NULL initially.
*/
wkptr = (sample_cb_t**)p->sdmpst_wk;
*wkptr = NULL;
break;
}
case RASCD_SDMP_AGAIN:{
/*
* This is similar to RASCD_SDMP_START, but is received to dump
* subsequent data for an unlimited dump table.
*/
int i;
sdmp_start_t *p = (sdmp_start_t*)arg;
struct cdt_nn_head_u *hp;
struct cdt_entry_ul *up;
sample_cb_t *cbp;
/* The dump table goes in the staging area */
hp = (struct cdt_nn_head_u*)p->sdmpst_buffer;
up = (struct cdt_entry_ul*)((struct cdt_nn_u*)hp)->cdtnu_entry;
/* Point to the first/next cb */
wkptr = (sample_cb_t**)p->sdmpst_wk;
/* For the first AGAIN call, cbp will be Sample_hdr.sh_cbp. */
cbp = (*wkptr)? *wkptr: Sample_hdr.sh_cbp;
/* Validate the pointer. */
cbp = get_scbp(cbp, 0, 0);
/* Set up cdt_nn_head_u */
hp->cdtnu_magic = DMP_MAGIC_NU;
hp->cdtnu_nentries = 1;
/* Set up cdt_entry_ul */
up->dul_magic = DMP_MAGIC_UL;
strcpy(up->dul_name, "cb");
up->dul_nentries = 0;
up->dul_len = sizeof(sample_cb_t);
/* Dump up to 3, NENTRIES, control blocks. */
for (i=0; iscb_next, 0, 0)) {
up->dul_entry[i].dle_vmhandle = DUMP_GEN_SEGVAL;
up->dul_entry[i].dle_ptr = cbp;
up->dul_nentries++;
}
/* Save address of next cb. */
*wkptr = cbp;
/* Set the table address to NULL when finished. */
p->sdmpst_table = (!up->dul_nentries)? NULL: p->sdmpst_buffer;
break;
}
case RASCD_SDMP_FINISHED:
/* Nothing to do here. */
break;
case RASCD_DMP_PASS_THROUGH:{
/* pass through */
printf("%s\n", arg);
break;
}
default: {
printf("bad ras_control command.\n");
rv = EINVAL_RAS_CONTROL_BADCMD;
}
}
return(rv);
}
/*
* Allocate sample data
*
* Input:
* n - number of sample cbs to allocate.
*
* Returns:
* 0 - success
* errno - errno from failure.
*/
int
alloc_sample(int n)
{
sample_cb_t *cbp, *prev_cbp=NULL;
/* Allocate n cbs */
while (n--) {
cbp = xmalloc(sizeof(*cbp), 3, kernel_heap);
if (!cbp) {
free_sample();
return(ENOMEM);
}
if (!prev_cbp) Sample_hdr.sh_cbp = cbp;
else prev_cbp->scb_next = cbp;
cbp->scb_eyec = EYEC_SCB;
cbp->scb_next = INVALID_SCB_PTR;
prev_cbp = cbp;
}
return(0);
}
/*
* Free sample data
*/
void
free_sample()
{
sample_cb_t *cbp = Sample_hdr.sh_cbp;
/* Validate cbp */
cbp = get_scbp(cbp, 0, 0);
while(cbp != INVALID_SCB_PTR) {
sample_cb_t *save_cbp = cbp;
cbp = get_scbp(cbp->scb_next, 0, 0);
xmfree(save_cbp, kernel_heap);
}
}
/*
* Validate the cb at the supplied address.
* This ensures we won't get an exception for a bad scb_next ptr.
*
* Input:
* addr - - address to read from
* id - - dump ID
* ldmpflag - 1 if this is a live dump.
*
* Returns:
* The pointer at the address.
* INVALID_SCB_PTR if the memory at addr is bad.
*/
static sample_cb_t *
get_scbp(sample_cb_t *addr, dumpid_t id, int ldmpflag)
{
sample_cb_t scb;
char str[80];
/* Just return if addr is the terminating value. */
if (addr == INVALID_SCB_PTR) return(addr);
/* Carefully get the storage at addr. */
if (raschk_safe_read(addr, &scb, sizeof(scb), RAS_SR_NOFAULT)) {
/* addr is bad. */
if (ldmpflag) {
sprintf(str, "The scb address 0x%lx is bad.\n", addr);
(void)ldmp_errstr(id, Rascb, str);
}
addr = INVALID_SCB_PTR;
}
else {
/* Validate the control block */
if (scb.scb_eyec != EYEC_SCB) {
/* cb appears bad. */
if (ldmpflag) {
sprintf(str, "cb at 0x%lx is invalid, eyec = 0x%lx.\n",
addr, scb.scb_eyec);
(void)ldmp_errstr(id, Rascb, str);
}
addr = INVALID_SCB_PTR;
}
}
return(addr);
}