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);
}