2021-10-11 22:19:34 -03:00

1237 lines
32 KiB
C

static char sccsid[] = "@(#)95 1.38 src/bos/kernel/io/errdd.c, syserrlg, bos41J, 9520A_all 5/11/95 19:30:01";
/*
* COMPONENT_NAME: SYSERRLG /dev/error pseudo-device driver
*
* FUNCTIONS: errread, errput, errsave, errnv_write, err_wakeup_dd,
* set_state, reset_state, errcdtf
*
* ORIGINS: 27
*
* IBM CONFIDENTIAL -- (IBM Confidential Restricted when
* combined with the aggregated modules for this product)
* SOURCE MATERIALS
* (C) COPYRIGHT International Business Machines Corp. 1988, 1993
* All Rights Reserved
*
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*/
/*
* /dev/error device driver
* This file contains the pinned portion of the device driver.
* The pageable functions are in errdd_pg.c, and the functions
* used during system initialization are in errdd_si.c.
* None of these routines run on a fixed stack.
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysmacros.h>
#include <sys/low.h>
#include <sys/m_intr.h>
#include <sys/malloc.h>
#include <sys/uio.h>
#include <sys/lockl.h>
#include <sys/intr.h>
#include <sys/trchkid.h>
#include <sys/sleep.h>
#include <sys/mdio.h>
#include <sys/dump.h>
#include <sys/err_rec.h>
#include <sys/erec.h>
#include <sys/syspest.h>
#include <sys/lock_def.h>
#include <sys/lock_def.h>
#include <errno.h>
#include "errdd.h"
#ifdef _POWER_MP
#include <sys/thread.h>
#include <sys/lock_alloc.h>
#include <sys/lockname.h>
Simple_lock errdd_lock;
#endif /* _POWER_MP */
int errread(dev_t mmdev,struct uio *uiop);
int errnv_write(char *ptr,int len);
void errput(struct errvec *ev,int len);
static void errpagable(struct err_rec *, int);
static void errput_active_demon(struct errvec *ev,int len);
static void errput_inactive_demon(struct errvec *ev,int len);
void err_wakeup_dd(int flag);
void set_state(int flag);
void reset_state(int flag);
void errsave(struct err_rec *ep, int len);
struct cdt_head *errcdtf();
int lockl_rc; /* return code from lockl */
struct errc errc = { /* buffer control structure */
0, /* state */
LOCK_AVAIL, /* e_lockword */
EVENT_NULL, /* e_sleepword_dd */
EVENT_NULL, /* e_sleepword_sync */
0, /* e_eize */
(char *)0, /* e_start */
(char *)0, /* e_end */
(char *)0, /* e_inptr */
(char *)0, /* e_outptr */
(char *)0, /* e_stale_data_ptr */
0, /* e_over_write_count */
0, /* e_err_count */
0, /* e_discard_count */
0, /* e_errid_1st_discarded */
0 /* e_errid_last_discarded */
};
static struct errc_io errc_io;
struct errstat errstat;
/*
* Calculate the UNOCCcupied SIZE of the buffer
*/
#define UNOCCSIZE() \
(errc.e_inptr - errc.e_outptr >= 0 ? EREC_MAX : errc.e_outptr - errc.e_inptr)
/* Test to see if we can page fault */
#define CAN_FAULT (csa->intpri == INTBASE)
/*
* NAME: errread
*
* FUNCTION: devsw[] read routine for /dev/error
*
* EXECUTION ENVIRONMENT:
* Preemptable : Yes
* Runs on Fixed Stack : No
* May Page Fault : Yes
* Serialization: : i_disable/i_enable for UP environment
* : disable_lock/unlock_enable for MP environment
*
* DATA STRUCTURES:
* errc.e_state, errc.e_outptr
*
* RETURNS:
* EFAULT (from uiomove) target address is invalid
* 0 Success
*/
int
errread(dev_t mmdev,struct uio *uiop)
{
int mdev;
int erec_len;
int ucount;
char buf[EREC_MAX];
int rv_error;
struct erec *ep;
int intpri;
rv_error = 0;
if(uiop->uio_resid == 0)
return(0);
loop:
if(!(errc.e_state & ST_RDOPEN)) { /* return EOF */
goto ret;
}
if(errc.e_state & ST_STOP) {
reset_state(ST_STOP);
goto ret; /* return EOF */
}
if(errc.e_err_count == 0){ /* empty buffer, nothing to read */
struct erec erec;
errnv_write((char*)&erec,0); /* 0 length will zero out nvram */
/* If interrupts are not disabled before setting
the state and left disabled, the sleep flag could
potentially get cleared before going to sleep
resulting in a sleep with no chance of wakeup.
Refer to defect 85290. */
#ifdef _POWER_MP
intpri = disable_lock(INTMAX, &errdd_lock);
#else
intpri = i_disable(INTMAX);
#endif /* _POWER_MP */
errc.e_state |= ST_SLEEP;
/* Wait for an error to be logged. The errput routines will
wake us up after placing and error into the buffer.
We will also be awakened if the errioctl for errstop is
executed. We want to make sure also that this wait will be
terminated by the occurrence of signals (requested with
the INTERRUPTIBLE flag in e_sleep_thread and the
EVENT_SIGRET flag in e_sleep). If we don't request this,
the errdemon would not be killed immediately with the
kill command (it would sit in this wait until errstop
was issued or until the next error was logged - which
would end up being lost). Defect 176056. */
#ifdef _POWER_MP
rv_error = e_sleep_thread(&errc.e_sleepword_dd, &errdd_lock, LOCK_HANDLER|INTERRUPTIBLE);
#else
rv_error = e_sleep(&errc.e_sleepword_dd, EVENT_SIGRET);
#endif /* _POWER_MP */
/* This if statement was added for defect 176056 to take
care of exiting when the errdemon process is killed.
We are only prepared to handle termination from
sleep due to an error being logged (errcount > 0) or
to errstop being issued (ST_STOP bit set). Anything
other than these reasons (ie. kill signal) will be
considered abnormal, and we will exit as though errstop
had been issued. */
if((errc.e_err_count == 0) && !(errc.e_state & ST_STOP)) {
errc.e_over_write_count = 0;
#ifdef _POWER_MP
unlock_enable(intpri, &errdd_lock);
#else
i_enable(intpri);
#endif /* _POWER_MP */
goto ret;
}
#ifdef _POWER_MP
unlock_enable(intpri, &errdd_lock);
#else
i_enable(intpri);
#endif /* _POWER_MP */
goto loop;
}
#ifdef _POWER_MP
intpri = disable_lock(INTMAX, &errdd_lock);
#else
intpri = i_disable(INTMAX);
#endif /* _POWER_MP */
ep = (struct erec *)errc.e_outptr;
erec_len = ep->erec_len;
/* The erec_len should never be greater than EREC_MAX, as a check
is made in both errsave() and errwrite(), and if the error length
is greater, it adjusts the length to equal EREC_MAX. So we will
assume that if the length value stored in the buffer is now
greater than EREC_MAX, that somehow the buffer has been corrupted.
If debug is enabled (ie. bosboot -D), and the following ASSERT
is encountered, we will fall into the low-level debugger. If
debug is not enabled, this ASSERT will be a no-op, and an attempt
will be made to just silently recover by resetting the buffer.
ASSERT(erec_len <= EREC_MAX); */
if(erec_len > EREC_MAX) {
ERR_TRCHKL(ERRREAD2,0,erec_len);
errc.e_outptr = errc.e_start; /* only way to fix buffer */
errc.e_inptr = errc.e_start;
errc.e_err_count = 0;
errc.e_over_write_count = 0;
errc.e_stale_data_ptr = errc.e_start;
errc.e_discard_count = 0;
errc.e_errid_1st_discarded = 0;
errc.e_errid_last_discarded = 0;
#ifdef _POWER_MP
unlock_enable(intpri, &errdd_lock); /* restore old interrupt priorities */
#else
i_enable(intpri);
#endif /* _POWER_MP */
goto loop;
}
ucount = MIN(uiop->uio_resid,erec_len);
bcopy(errc.e_outptr,buf,ucount);
errc.e_outptr += erec_len;
if ( (errc.e_outptr >= errc.e_end) ||
(errc.e_outptr >= errc.e_stale_data_ptr) )
{
if (errc.e_outptr == errc.e_inptr)
{
errc.e_inptr = errc.e_start;
errc.e_outptr = errc.e_start;
errc.e_stale_data_ptr = errc.e_inptr;
errc.e_err_count = 0;
}
else
{
errc.e_outptr = errc.e_start;
errc.e_stale_data_ptr = errc.e_inptr;
errc.e_err_count--;
}
}
else
errc.e_err_count--;
if (errc.e_outptr == errc.e_inptr)
{
errc.e_inptr = errc.e_start;
errc.e_outptr = errc.e_start;
errc.e_stale_data_ptr = errc.e_inptr;
errc.e_err_count = 0;
}
#ifdef _POWER_MP
unlock_enable(intpri, &errdd_lock); /* restore old interrupt priorities */
#else
i_enable(intpri);
#endif /* _POWER_MP */
rv_error = uiomove(buf,ucount,UIO_READ,uiop);
ret:
if(rv_error) {
errstat.es_readerr++;
ERR_TRCHK(ERRREAD,rv_error);
}
return(rv_error);
}
/*
* NAME: errput
*
* FUNCTION: Call the appropriate errput routine.
*
* INPUT:
* ev errvec vector for data areas to be moved.
* len length in bytes of the err_rec structure
*
* RETURNS: none
*/
void
errput(struct errvec *evp, int len)
{
if (errc.e_state & ST_RDOPEN)
errput_active_demon(evp,len);
else
errput_inactive_demon(evp,len);
}
/*
* NAME: errput_active_demon
*
* FUNCTION: Common buffer write routine for errwrite and errsave
* for when the errdemon is active
*
* INPUTS:
* ev errvec vector for data areas to be moved.
* len length in bytes of the err_rec structure
*
* EXECUTION ENVIRONMENT:
* Preemptable : No
* Runs on Fixed Stack : No
* May Page Fault : No
* Serialization: : i_disable/i_enable for UP environment
* : disable_lock/unlock_enable for MP environment
*
*
* RETURNS: NONE
*
*/
static void
errput_active_demon(struct errvec *ev,int len)
{
struct erec *epinptr, *epoutptr;
struct err_rec *current_event;
int intpri, prev_overflow, curr_overflow, i;
char *p;
/* If the error count field of errc is less than zero
at the start of this routine, there's something very
wrong and processing should not continue.
*/
assert(errc.e_err_count >= 0);
/*
* Put an error record into the kernel error buffer.
* The record will not always be added to the buffer.
* If there is not enough room in the buffer for the
* error, it will be discarded.
* Since no overwriting of events will take place, there
* is only one type of wrapping to be concerned with:
* Buffer wrap: Buffer wrap is simply the setting of
* the pointers to the start of the buffer when an
* operation would cause them to run off the end of
* the buffer.
*
*/
#ifdef _POWER_MP
intpri = disable_lock(INTMAX, &errdd_lock);
#else
intpri = i_disable(INTMAX);
#endif /* _POWER_MP */
curr_overflow = 0;
if (errc.e_discard_count > 0) {
/* A previous overflow condition existed, and certain
information about it must be logged in addition to
the current error. The processing at the end
will log a "lost events" entry before the current
entry if the prev_overflow flag is set and if it is
determined that there is enough room in the buffer
for both entries. */
prev_overflow = 1;
len+= LOST_EVTS_ERRLEN + sizeof(struct erec0);
}
else
prev_overflow = 0;
/*
* Case 1: in < out
* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
* |_|_|_|_|_|_|_ _ _ _ _ _ _ _|_|_|_|_|_|_|
* start in out end
*/
if (errc.e_inptr < errc.e_outptr) {
if (errc.e_inptr + len > errc.e_outptr) {
/* overflow condition */
if (errc.e_discard_count == 0)
errc.e_errid_1st_discarded
= ev->error_id;
errc.e_discard_count++;
errc.e_errid_last_discarded
= ev->error_id;
/* Set the curr_overflow flag. This will be used
to determine whether or not the error should
be written to the buffer. */
curr_overflow = 1;
}
} /* end if (in < out) */
/*
* Case 2: in = out
*
* - full buffer :
* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
* |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
* start in/out end
*
* - or - empty buffer :
* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
* |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|
* start/in/out end
*
*/
else if (errc.e_inptr == errc.e_outptr) {
if (errc.e_err_count > 0) {
/* Buffer is full. This is an overflow. */
if (errc.e_discard_count == 0)
errc.e_errid_1st_discarded
= ev->error_id;
errc.e_discard_count++;
errc.e_errid_last_discarded
= ev->error_id;
/* Set the curr_overflow flag. This will be used
to determine whether or not the error should
be written to the buffer. */
curr_overflow = 1;
}
else { /* non-overflow, empty buffer */
errc.e_inptr = errc.e_start;
errc.e_outptr = errc.e_start;
errc.e_stale_data_ptr = errc.e_inptr;
}
} /* end else if (in == out) */
/*
* Case 3: in > out
*
* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
* |_|_|_|_|_|_|_|_|_|_|_ _ _ _ _ _ _ _ _ _|
* start/out in end
*
* - or -
* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
* |_ _ _ _ _ _ _ _ _ _|_|_|_|_|_|_|_ _ _ _|
* start out in end
*
*/
else {
if ((errc.e_inptr + len) > errc.e_end) {
/* Possible overflow condition -
Won't fit at the end of the buffer.
Check to see if it will fit at the
beginning of the buffer, but don't
adjust the inptr or the staleptr
unless it does. */
if ((errc.e_start + len) > errc.e_outptr) {
/* Overflow condition -
It doesn't fit at the beginning or
end of the buffer, so discard it. */
if (errc.e_discard_count == 0)
errc.e_errid_1st_discarded
= ev->error_id;
errc.e_discard_count++;
errc.e_errid_last_discarded
= ev->error_id;
/* Set the curr_overflow flag. This will be used
to determine whether or not the error should
be written to the buffer. */
curr_overflow = 1;
}
else { /* This is a non-overflow condition and a
buffer wrap. It doesn't fit at the end
of the buffer, but it will fit at the
beginning of it. */
errc.e_stale_data_ptr = errc.e_inptr;
errc.e_inptr = errc.e_start;
}
}
} /* end else if (in > out) */
if (curr_overflow == 0) {
if (prev_overflow == 1) {
/* First write the lost events error
directly to the buffer, then clear
the errc overflow variables. */
errc.e_err_count++;
epinptr = (struct erec*)errc.e_inptr;
epinptr->erec_rec_len = LOST_EVTS_ERRLEN;
epinptr->erec_len = epinptr->erec_rec_len +
sizeof(struct erec0);
epinptr->erec_symp_len = 0;
epinptr->erec_timestamp = time;
epinptr->erec_rec.error_id = ERRID_LOST_EVENTS;
sprintf(epinptr->erec_rec.resource_name,"%s",
"syserrlg");
sprintf(epinptr->erec_rec.detail_data,
"%08x%08x%d",
errc.e_errid_1st_discarded,
errc.e_errid_last_discarded,
errc.e_discard_count);
if (errc.e_inptr == errc.e_stale_data_ptr)
errc.e_stale_data_ptr += epinptr->erec_len;
errc.e_inptr += epinptr->erec_len;
errstat.es_errcount++;
len -= (LOST_EVTS_ERRLEN + sizeof(struct erec0));
errc.e_discard_count = 0;
errc.e_errid_1st_discarded = 0;
errc.e_errid_last_discarded = 0;
} /* end if (prev_overflow == 1) */
/* Copy data into buffer at inptr. */
errc.e_err_count++;
epinptr = (struct erec *)errc.e_inptr;
/* Copy each data element to the buffer */
for (i=0, p=(char *)epinptr; i<ev->nel; i++) {
bcopy(ev->v[i].p,p,ev->v[i].len);
len -= ev->v[i].len;
p += ev->v[i].len;
}
assert(len == 0); /* Bad news if len didn't match vector! */
epinptr->erec_timestamp = time; /* Set the time stamp */
if (errc.e_inptr == errc.e_stale_data_ptr)
errc.e_stale_data_ptr += epinptr->erec_len;
errc.e_inptr += epinptr->erec_len; /* len + erec_len + timestamp */
errnv_write((char*)epinptr, epinptr->erec_len);
errstat.es_errcount++;
#ifdef _POWER_MP
/* The following is code from err_wakeup_dd. This needs to
go here, because we can't have nested simple locks. */
errc.e_state |= 0;
if (errc.e_state & ST_SLEEP) {
errc.e_state &= ~ST_SLEEP;
e_wakeup(&errc.e_sleepword_dd);
}
#else
err_wakeup_dd(0);
#endif /* _POWER_MP */
} /* end if (curr_overflow == 0) */
#ifdef _POWER_MP
unlock_enable(intpri, &errdd_lock);
#else
i_enable(intpri);
#endif /* _POWER_MP */
} /* end errput_active_errdemon() */
/*
* NAME: errput_inactive_demon()
*
* FUNCTION: common buffer write routine for errwrite and errsave
*
* INPUTS:
* ev errvec vector for data areas to be moved.
* len length in bytes of the err_rec structure
*
* EXECUTION ENVIRONMENT:
* Preemptable : No
* Runs on Fixed Stack : No
* May Page Fault : No
* Serialization: : i_disable/i_enable for UP environment
* : disable_lock/unlock_enable for MP environment
*
* DATA STRUCTURES:
* errc.e_state, errc.e_inptr
*
* RETURNS: NONE
*
*/
static void
errput_inactive_demon(struct errvec *ev,int len)
{
struct erec *epinptr, *epoutptr;
unsigned old_over;
int intpri, i;
char *p;
/* If the error count field of errc is less than zero
at the start of this routine, there's something very
wrong and processing should not continue.
*/
assert(errc.e_err_count >= 0);
/*
* put an error record into the kernel error buffer.
* The record will always be added to the buffer. This
* could entail writing over the oldest record.
* There are two types of wrapping to be concerned with:
* 1. Buffer wrap: Buffer wrap is simply the setting of
* the pointers to the start of the buffer when an
* operation would cause them to run off the end of
* the buffer.
* 2. Data wrap: Data wrap occurs when the data is not
* removed from the buffer fast enough and unprocessed
* data will be written over.
*
* Note:
* If an error record is too big to fit in the remainder of
* the buffer, then it will be added at the start.
* The good data to process always starts out and fills through.
*
* The following summarizes the wrapping that will take place
* for each of the possible states:
*
* State 1: in < out
* State 1A: in<out && in+len>out && in+len<=end
* data wrap, no buffer wrap
* State 1B: in<out && in+len>out && in+len>end
* data wrap, buffer wrap
* State 1C: in < out && in+len<out && in+len<=end
* no data wrap, no buffer wrap
*
* State 2: in = out
* State 2A: in=out && err_count>0 && in+len<=end
* data wrap, no buffer wrap
* State 2B: in=out && err_count>0 && in+len>end
* data wrap, buffer wrap
* State 2C: in=out && err_count=0
* no data wrap, no buffer wrap
*
* State 3: in > out
* State 3A: in>out, && in+len>end && start+len<=outptr
* buffer wrap, no data wrap
* State 3B: in>out && in+len>end && start+len>outptr
* buffer wrap, data wrap
* State 3C: in>out && in+len<=end
* no buffer wrap, no data wrap
*
*/
#ifdef _POWER_MP
intpri = disable_lock(INTMAX, &errdd_lock);
#else
intpri = i_disable(INTMAX);
#endif /* _POWER_MP */
/*
* Case 1: in < out
*
* |----------!---------------!------------|
* start in out end
*/
if (errc.e_inptr < errc.e_outptr)
{ /* watch out for data wrap*/
if (errc.e_inptr + len > errc.e_outptr)
{
/*
* data wrap, so compute how many records are being
* written over and subtract that from the count
*/
if (errc.e_inptr + len <= errc.e_end) /* State 1A */
{ /* will fit before buffer wrap */
while (errc.e_outptr < errc.e_inptr + len)
{ /* write over this record */
if (errc.e_outptr>=errc.e_stale_data_ptr)
{
errc.e_outptr = errc.e_end;
break;
/*
* hitting begin of stale data
* if we don't break here,
* we're going to trash
* valid records. force
* e_outptr past end.
*/
}
errc.e_over_write_count++;
errc.e_err_count--;
epoutptr =(struct erec *)errc.e_outptr;
errc.e_outptr += epoutptr->erec_len;
}
if (errc.e_outptr >= errc.e_end)
{
errc.e_outptr = errc.e_start;
errc.e_stale_data_ptr = errc.e_inptr + len;
/*
* set all data between end of this new
* record and the end to be stale
*/
}
}
else
{
/*
* State 1B:
*
* in+len > end, so need to buffer wrap to add the
* error. compute number of records to end.
*/
while (errc.e_outptr <= errc.e_end)
{ /* write over this record */
if (errc.e_outptr>=errc.e_stale_data_ptr)
{
break;
/*
* hitting begin of stale data
* if we don't break here,
* we're going to trash
* valid records.
* e_outptr will be set to
* e_start after this.
*/
}
errc.e_over_write_count++;
errc.e_err_count--;
epoutptr =(struct erec *)errc.e_outptr;
errc.e_outptr += epoutptr->erec_len;
}
errc.e_stale_data_ptr = errc.e_inptr;
/* consider everything after this stale */
/*
* put the new entry at start and out at the
* oldest entry. The oldest entry should now
* be the entry past the new.
*/
errc.e_inptr = errc.e_start;
errc.e_outptr = errc.e_start;
while (errc.e_outptr < (errc.e_inptr + len))
{
if (errc.e_outptr>=errc.e_stale_data_ptr)
{
errc.e_stale_data_ptr =
errc.e_inptr + len;
errc.e_outptr = errc.e_start;
break;
/*
* hitting stale data pointer
* in this case should be
* very rare. this must
* be a case of a very large
* new error coming in.
*/
}
errc.e_over_write_count++;
errc.e_err_count--;
epoutptr =(struct erec *)errc.e_outptr;
errc.e_outptr += epoutptr->erec_len;
}
}
}
} /* end in < out */
/*
* Case 2: in = out
*
* |--------------!----------------------|
* start in/out end
*/
else if (errc.e_inptr == errc.e_outptr)
{ /* buffer is empty or full */
if (errc.e_err_count > 0)
{ /* full, so data wrap */
if ((errc.e_inptr + len) <= errc.e_end) /* State 2A */
{ /* will fit before buffer wrap */
while (errc.e_outptr < (errc.e_inptr + len))
{ /* write over this record. */
if (errc.e_outptr>=errc.e_stale_data_ptr)
{
errc.e_outptr = errc.e_end;
break;
/*
* hitting begin of stale data
* if we don't break here,
* we're going to trash
* valid records. force
* e_outptr past end.
*/
}
errc.e_over_write_count++;
errc.e_err_count--;
epoutptr =(struct erec *)errc.e_outptr;
errc.e_outptr += epoutptr->erec_len;
}
if (errc.e_outptr >= errc.e_end)
{
errc.e_outptr = errc.e_start;
/* out buffer wrap */
errc.e_stale_data_ptr = errc.e_inptr + len;
/*
* set all data between end of this new
* record and the end to be stale
*/
}
}
else
{
/*
* State 2B:
*
* in+len > end so need to wrap to add the error
* compute number of records to end
*/
while (errc.e_outptr <= errc.e_stale_data_ptr)
{ /* write over this record */
if (errc.e_outptr==errc.e_stale_data_ptr)
{
break;
/*
* hitting begin of stale data
* if we don't break here,
* we're going to trash
* valid records.
* e_outptr will be set to
* e_start after this.
*/
}
errc.e_over_write_count++;
errc.e_err_count--;
epoutptr =(struct erec *)errc.e_outptr;
errc.e_outptr += epoutptr->erec_len;
}
errc.e_stale_data_ptr = errc.e_inptr;
/* consider everything after this stale */
/* put the new entry at start and out at the
* oldest entry. The oldest entry should now
* be the entry past the new.
*/
errc.e_inptr = errc.e_start;
errc.e_outptr = errc.e_start;
while (errc.e_outptr < errc.e_inptr + len)
{
if (errc.e_outptr>=errc.e_stale_data_ptr)
{
errc.e_outptr = errc.e_start;
errc.e_stale_data_ptr =
errc.e_inptr + len;
break;
/*
* hitting stale data pointer
* in this case should be
* very rare. this must
* be a case of a very large
* new error coming in.
*/
}
errc.e_over_write_count++;
errc.e_err_count--;
epoutptr =(struct erec *)errc.e_outptr;
errc.e_outptr += epoutptr->erec_len;
}
}
}
else
{/* buffer empty so reset to start to delay buf wrapping */
errc.e_inptr = errc.e_start;
errc.e_outptr = errc.e_start;
errc.e_stale_data_ptr = errc.e_inptr;
}
}
else
{
/*
* Case 3: in > out
*
* |------------!----------!---------|
* start out in end
*/
if ((errc.e_inptr + len) > errc.e_end) /* State 3A */
{ /* buffer wrap */
errc.e_stale_data_ptr = errc.e_inptr;
/* consider everything after this stale */
errc.e_inptr = errc.e_start;
if (errc.e_inptr+len > errc.e_outptr)
{/* data wrap - State 3B */
while (errc.e_outptr < errc.e_inptr+len)
{ /* write over this record. */
if (errc.e_outptr>=errc.e_stale_data_ptr)
{
errc.e_outptr = errc.e_start;
errc.e_stale_data_ptr =
errc.e_inptr + len;
break;
/*
* hitting stale data pointer
* in this case should be
* very rare. this must
* be a case of a very large
* new error coming in.
*/
}
errc.e_over_write_count++;
errc.e_err_count--;
epoutptr =(struct erec *)errc.e_outptr;
errc.e_outptr += epoutptr->erec_len;
}
}
}
}
/* copy data into buffer at in. */
errc.e_err_count++;
epinptr = (struct erec *)errc.e_inptr;
/* Copy each data element to the buffer */
for (i=0, p=(char *)epinptr; i<ev->nel; i++) {
bcopy(ev->v[i].p,p,ev->v[i].len);
len -= ev->v[i].len;
p += ev->v[i].len;
}
assert(len == 0); /* Bad news if len didn't match vector! */
epinptr->erec_timestamp = time; /* Set the time stamp */
if (errc.e_inptr == errc.e_stale_data_ptr)
errc.e_stale_data_ptr += epinptr->erec_len;
errc.e_inptr += epinptr->erec_len; /* len + erec_len + timestamp */
ERR_TRCHK(ERRPUT,0);
errnv_write((char*)epinptr, epinptr->erec_len);
errstat.es_errcount++;
#ifdef _POWER_MP
/* This is code from err_wakeup_dd. We need this here because
we can't have nested simple locks. */
errc.e_state |= 0;
if (errc.e_state & ST_SLEEP) {
errc.e_state &= ~ST_SLEEP;
e_wakeup(&errc.e_sleepword_dd);
}
#else
err_wakeup_dd(0);
#endif /* _POWER_MP */
#ifdef _POWER_MP
unlock_enable(intpri, &errdd_lock);
#else
i_enable(intpri);
#endif /* _POWER_MP */
}
/*
* NAME: errsave
*
* FUNCTION: error logging kernel service
*
* INPUTS:
* ep pointer to err_rec structure (see err_rec.h)
* len length in bytes of the err_rec structure
*
* EXECUTION ENVIRONMENT:
* Preemptable : Yes
* Runs on Fixed Stack : No
* May Page Fault : Yes
* Serialization: : none needed
* NOTE: We may be called disabled or not. If disabled, we can't
* page fault, but we can assume the data passed in is pinned.
*
* DATA STRUCTURES:
* errc.e_state, errc.e_inptr
*
*/
void
errsave(struct err_rec *ep, int len)
{
/* We use 2 address vector elements, one for the
* erec header (erec0) and one for the passed data.
*/
ALOC_ERRVEC(2) av;
struct erec0 err_hdr; /* Header record */
int data_len = sizeof(struct erec0)+len;
if (dump_started)
return;
if(errc.e_start == NULL)
return;
if(len > ERR_REC_MAX_SIZE)
len = ERR_REC_MAX_SIZE;
/* If we can page fault, go off and handle that case. */
if (CAN_FAULT) {
errpagable(ep,len);
return;
}
/* Setup error header (erec0) */
err_hdr.erec_len = sizeof(err_hdr)+len;
err_hdr.erec_rec_len = len;
err_hdr.erec_symp_len = 0;
/* Setup the address vector */
av.error_id = ep->error_id;
av.nel = 2;
av.v[0].p = &err_hdr; /* Get the data type header */
av.v[0].len = sizeof(err_hdr);
av.v[1].p = ep; /* point to the data */
av.v[1].len = len;
/* Put the data in the error log buffer */
errput(&av,data_len);
}
/*
* NAME: errpagable
*
* FUNCTION: error logging kernel service for pagable Kernel code.
* If the caller can page fault, we'd better copy their error
* log entry to pinned space before calling errput which
* disables.
*
* INPUTS:
* ep pointer to err_rec structure (see err_rec.h)
* len length in bytes of the err_rec structure
*
* EXECUTION ENVIRONMENT:
* Called from errsave if we're not disabled.
*
*/
static void
errpagable(struct err_rec *ep, int len)
{
int data_len;
struct erec *e;
struct errvec *evp; /* Address vector for errput() */
/* Allocate pinned buffer space.
* This must include the address vector.
*/
data_len = len+sizeof(struct erec0);
if (!(e = (struct erec *)xmalloc(data_len+sizeof(struct errvec),BPWSHIFT,pinned_heap))) {
ERR_TRCHK(ERRWRITE,EAGAIN);
return;
}
/* Point to address vector. */
evp = (struct errvec *)((int)e+data_len);
/* Get the data */
bcopy(ep,&e->erec_rec,len);
/* Setup the error record header and address vector */
e->erec_len = data_len;
e->erec_rec_len = len;
e->erec_symp_len = 0;
evp->error_id = e->erec_rec.error_id;
evp->nel = 1;
evp->v[0].p = e;
evp->v[0].len = data_len;
/* Put the data in the buffer. */
errput(evp,data_len);
xmfree(e,pinned_heap);
return;
}
/*
* NAME: errcdtf()
* FUNCTION: Called by dmp_do at dump time.
* RETURNS: Returns a pointer to a component dump table (cdt).
* The cdt for errlog is the errlog buffer control structure
* errc and the buffer itself.
*/
static struct {
struct cdt_head _cdt_head;
struct cdt_entry cdt_entry[3];
} errcdt = {
{ DMP_MAGIC, "errlg", sizeof(errcdt) },
{ { "errc", sizeof(errc), (char *)&errc, 0 },
{ "errc_io", sizeof(errc_io), (char *)&errc_io, 0 },
{ "log", 0, 0, 0 } }
};
struct cdt_head *errcdtf()
{
errc_io.e_start = errc.e_start;
errc_io.e_end = errc.e_end;
errc_io.e_inptr = errc.e_inptr;
errc_io.e_outptr = errc.e_outptr;
errc_io.e_stale_data_ptr = errc.e_stale_data_ptr;
errc_io.e_over_write_count = errc.e_over_write_count;
errc_io.e_err_count = errc.e_err_count;
errc_io.e_discard_count = errc.e_discard_count;
errc_io.e_errid_1st_discarded = errc.e_errid_1st_discarded;
errc_io.e_errid_last_discarded = errc.e_errid_last_discarded;
errcdt.cdt_entry[2].d_len = errc.e_size;
errcdt.cdt_entry[2].d_ptr = errc.e_start;
return((struct cdt_head *)&errcdt);
}
/*
* NAME: errnv_write()
* FUNCTION: Read/write one err_rec0 structure from offset ERRLOG.0 of nvram
* RETURNS: 1 Success
* -1 Failure
*/
int
errnv_write(char *ptr, int len)
{
int rv;
union errnv_buf errnv_buf;
if(len > sizeof(errnv_buf))
len = sizeof(errnv_buf);
if(len == 0)
{
/*
Make sure all length fields in erec structure are zeroed
*/
bzero(&(errnv_buf.erec), sizeof(errnv_buf.erec));
}
else {
bcopy(ptr,&errnv_buf,len);
errnv_buf.erec.erec_len = len;
}
rv = nvwrite(3,(char*)&errnv_buf,0,sizeof(errnv_buf));
if(rv != sizeof(errnv_buf))
return(-1);
return(1);
}
/*
* NAME: err_wakeup_dd()
* FUNCTION: Wakes up processes waiting on a non-empty buffer.
* (Refer to the definition of the errc structure in
* errdd.h for a description of the intended use of
* the sleep words.)
* RETURNS: NONE
*/
void
err_wakeup_dd(int flag)
{
int intpri;
#ifdef _POWER_MP
intpri = disable_lock(INTMAX, &errdd_lock);
#else
intpri = i_disable(INTMAX);
#endif /* _POWER_MP */
errc.e_state |= flag;
if (errc.e_state & ST_SLEEP) {
errc.e_state &= ~ST_SLEEP;
e_wakeup(&errc.e_sleepword_dd);
}
#ifdef _POWER_MP
unlock_enable(intpri, &errdd_lock);
#else
i_enable(intpri);
#endif /* _POWER_MP */
}
/*
* NAME: set_state()
* FUNCTION: Sets the state bit in errc.e_state as specified by
* the flag parameter, disabling and enabling interrupts
* around this operation.
* RETURNS: NONE
*/
void
set_state(int flag)
{
int intpri;
#ifdef _POWER_MP
intpri = disable_lock(INTMAX, &errdd_lock);
#else
intpri = i_disable(INTMAX);
#endif /* _POWER_MP */
errc.e_state |= flag;
#ifdef _POWER_MP
unlock_enable(intpri, &errdd_lock);
#else
i_enable(intpri);
#endif /* _POWER_MP */
}
/*
* NAME: reset_state()
* FUNCTION: resets the state bit in errc.e_state as specified by
* the flag parameter, disabling and enabling interrupts
* around this operation.
* RETURNS: NONE
*/
void
reset_state(int flag)
{
int intpri;
#ifdef _POWER_MP
intpri = disable_lock(INTMAX, &errdd_lock);
#else
intpri = i_disable(INTMAX);
#endif /* _POWER_MP */
errc.e_state &= ~flag;
#ifdef _POWER_MP
unlock_enable(intpri, &errdd_lock);
#else
i_enable(intpri);
#endif /* _POWER_MP */
}