'\"macro stdmacro
.if n .pH ddi_dki.hdelog @(#)hdelog	40.8 of 10/10/89
.\" Copyright 1989 AT&T
.de IX
.ie '\\n(.z'' .tm .Index: \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9	\\n%
.el \\!.IX \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
..
.nr X
.if \nX=0 .ds x} hdelog D3D "3B2" "DDI" "\&"
.if \nX=1 .ds x} hdelog D3D "3B2" "DDI"
.if \nX=2 .ds x} hdelog D3D "" "\&"
.if \nX=3 .ds x} hdelog "" "" "\&"
.TH \*(x}
.IX "\f4hdelog\fP(D3D)"
.SH NAME
\f4hdelog\f1 \- log hard disk error
.SH SYNOPSIS
.nf
.na
\f4#include <sys/types.h>
#include <sys/hdelog.h>
#include <sys/mkdev.h>
.sp 0.5
int hdelog(struct hdedata *\f2eptr\f4);\f1
.ad
.fi
.SH ARGUMENT
.RS 0n 10
.IP "\f2eptr\f1" 10n
Pointer to the \f4hdedata\f1(D4D) structure defined in
\f4sys/hdelog.h\f1.
The driver developer places information in the structure
before \f4hdelog\f1 is called.
.RE
.SH DESCRIPTION
\f4hdelog\f1 logs a hard disk error in the error logging queue and
displays a warning message on the console to alert the operator to the
problem.
.P
The console message is
.P
.RS
.nf
.ft 4
\f4WARNING:  \f2severity readtype\f4 hard disk error:
maj/min = \f2external-major-num, external-minor-num\f1
.ft 1
.fi
.RE
.P
where
\f2severity\f1 is ``\f4marginal\f1'' or ``\f4unreadable\f1'',
and
\f2readtype\f1 is ``\f4CRC\f1'' (cyclic redundancy check) or 
``\f4ECC\f1'' (error check and correction).
.P
\f4hdeeqd\f1(D3D) must be called once before this function to initialize
error logging.  \f4hdelog\f1 logs disk drive media errors.
\f3NOTE:\f1
This function is not part of the default kernel.
Ensure that the \f4HDE\f1 bootable object module is placed
in the \f4/boot\f1 directory.
.P
Before calling this function,
values must be assigned to the \f4hdedata\f1(D4D) structure.
These members
include the device number; the disk pack serial number; the physical block
address; the type of read operation CRC or ECC; whether the error is marginal
or whether the disk is unreadable; the number of unreadable tries; the bit
width of the corrected error; and a time stamp.
.SH RETURN VALUE
Under all conditions,
a \f40\f1 is returned.
However,
an internal error can
occur in \f4hdelog\f1 causing a warning message to display on the
console.
This error occurs when the error logging table is full.
In this
case,
the usual disk error warning message is prefaced with
.P
.RS
.na
\f4WARNING:  HDE queue full, following report not logged\f1
.ad
.RE
.SH LEVEL
Base or Interrupt
.SH SEE ALSO
\f2BCI Driver Development Guide\f1, Chapter 12, ``Error Reporting''
.P
.na
\f4hdeeqd\f1(D3D),
\f4hdedata\f1(D4D)
.ad
.SH EXAMPLE
.IX "\f4hdedata\fP(D4D), example"
.IX "\f4hdelog\fP(D3D), example"
A driver interrupt routine must check for data transfer errors
(called data checks).  When a data check occurs (reported by
the device in the status or error register), the driver determines if there
have been sufficient attempts to resolve the error.  If so, the driver
abandons the I/O request by marking the buffer as being in error, logging an
unresolved error (line 60), and marking the I/O operation complete (line
61).  When an error persists in spite of multiple attempts to resolve it, the
driver logs marginal errors (line 75) and attempts the I/O operation again.
The driver may try to resolve the error with software by using the error
correction bits in an error check and correction (ECC) register.  See
\f4hdedata\f1(D4D) for a description of the
\f4xx_edata\f1 structure shown in this example line 17).
.ne 4
.P
.nf
.ft 4
.ps 7
 1  struct  device  {            
 2                                    /* layout of physical device regs */
 3          char      reserve[4];     /* reserve space on card          */
 4          ushort    control;        /* physical device control word   */
 5          char      status;         /* physical device status word    */
 6          char      ivec_num;       /* device interrupt vector no. in */
 7                                    /* 0xf0; subdevice in 0x0f        */
 8          paddr_t   addr;           /* address of data read/written   */
 9          int       count;          /* amount of data read/written    */
10   }; /* end device */
11
12  struct xx_  {            
13                                    /* logical device structure       */
14         struct buf    *xx_head;    /* I/O buffer queue head pointer  */
15         struct buf    *xx_tail;    /* I/O buffer queue tail pointer  */
16         short          xx_flag;    /* logical status flag            */
17         struct hdedata xx_edata;   /* hard disk error record         */
18         struct iostat  xx_stat;    /* unit I/O stats for setting an  */
19                                   /* error rate during error logging */
20  }; /* end xx_ */
21
22  struct xx_info  {          
23                                /* information on disk control sector */
24         long    xx_id;             /* device id code                 */
25         long    xx_cyl;            /* total number of cylinders      */
26         long    xx_trk;            /* number of tracks per cylinder  */
27         long    xx_sec;            /* number of sectors per track    */
28         char    xx_serial[12];     /* device serial number           */
29  }; /* end xx_info */
30  extern struct xx_     xx_devtab[];/* logical dev structures table   */
31  extern struct device *xx_addr[];  /* physical dev register location */
32  extern struct xx_info xx_info[];  /* device control information     */
33  extern int            xx_cnt;     /* number of devices              */
34  xx_int(board)
35          int  board;
36  {                                              /* get dev registers */
37    register struct device  *rp = xx_addr[board];
38    register struct xx_     *dp;
39    register struct buf     *bp;
40    register int    unit;
41
42    unit = (board << 4) | (rp->ivec_num & 0xf);   /* make unit number */
43    dp = &xx_devtab[unit];
44    if ((rp->status & DATACHK) != 0) { 
45                                      /* if data check error occurred */
46      if (++dp->xx_edata.badrtcnt > XX_MAXTRY)  {   /* if sufficient  */
47             /* attempts have been made, then abandon the I/O request */
48          bp = dp->xx_head;              /* get buffer from I/O queue */
49          dp->xx_head = bp->av_forw;  /* remove buffer from I/O queue */
50          bp->b_flags |= B_ERROR;    /* mark buffer as being in error */
51          bp->b_error = EIO;                /* supply error condition */
52                       /* supply information needed for error logging */
53          dp->xx_edata.diskdev = cmpdev(bp->b_dev);  /* device number */
54          dp->xx_edata.blkaddr = bp->b_blkno;   /* block no. in error */
55          dp->xx_edata.readtype = HDEECC;  /* error type: error check */
56          dp->xx_edata.severity = HDEUNRD;  /* data was unreadable    */
57          dp->xx_edata.bitwidth = 0;
58          dp->xx_edata.timestmp = time;   /* time recording occurred  */
59          bcopy(dp->xx_edata.dskserno, xx_info[unit].serial, 12);
60          hdelog(&dp->xx_edata);       /* log abandoned I/O operations*/
61          biodone(bp);                 /* mark I/O operation complete */
62  
63      } else if(dp->xx_edata.badrtcnt > 1) {/* if more than one retry */
64                                             /* log error as marginal */
65         bp = dp->xx_head;  /* get buffer from I/O queue but leave on */
66                       /* I/O queue so that I/O operation is repeated */
67         /* supply information needed for error logging */
68         dp->xx_edata.diskdev = cmpdev(bp->b_dev);   /* device number */
69         dp->xx_edata.blkaddr = bp->b_blkno;    /* error block number */
70         dp->xx_edata.readtype = HDEECC;    /* err. type: error check */
71         dp->xx_edata.severity = HDEMARG;           /* marginal error */
72         dp->xx_edata.bitwidth = 0;
73         dp->xx_edata.timestmp = time;     /* time recording occurred */
74         bcopy(dp->xx_edata.dskserno, xx_info[unit].serial, 12);
75         hdelog(&dp->xx_edata);              /* log data check error  */
76      } /* endif */
77    } /* endif */
78  }
.ps
.ft 1
.fi
.P
.FG "hdelog \- log media errors
