Old s-ncr.c
  1 /* System dependent file for NCR System V Release 4 MP/RAS     */
  2 
  3 /* $Id: s-ncr.c,v 1.2 1994/09/07 00:44:31 jdd Exp $ */
  4 
  5 #include "s.h"
  6 #include <stdio.h>
  7 #include <nlist.h>
  8 #include <sys/types.h>
  9 #include <sys/fcntl.h>
 10 #include <sys/unistd.h>
 11 #include <sys/mman.h>
 12 
 13 #include <sys/utsname.h>
 14 #include <sys/pic.h>
 15 #include <sys/sysinfo.h>
 16 #include <sys/mp/percpu.h>
 17 #include <sys/immu.h>
 18 
 19 #include <sys/scsi/cs.h>
 20 
 21 #include <errno.h>
 22 
 23 extern int   diskflag, cpuflag;
 24 extern char *kernelSymbols;
 25 
 26 extern void draw_bar(int bar_num, int *states, int num_states);
 27 extern void display_bars(int);
 28 
 29 struct cpu_names
 30 {
 31   int   cpu_type;
 32   char  cpu_type_name[8];
 33 };
 34 
 35 const struct cpu_names  cpu_model_names[] =
 36 {
 37   { CPU_386,     "i386" },
 38   { CPU_486,     "i486" },
 39 #ifdef CPU_586
 40   { CPU_586,     "Pentium" },
 41 #elif defined CPU_Pentium
 42   { CPU_Pentium, "Pentium" },
 43 #endif
 44 };
 45 
 46 
 47 /*  The following defines are used to index the variable kernel_nl[] */
 48 #define KERNEL_UTSNAME   0
 49 #define KERNEL_PERCPUP   1
 50 #define KERNEL_CPU_INFOS 2
 51 #define KERNEL_NCPU      3
 52 #define KERNEL_SCSI_STAT 4
 53 #define KERNEL_LBOLT     5
 54 
 55 /* kernel_nl[]  --  list of symbol addresses needed.
 56 */
 57 static struct nlist kernel_nl[] =
 58 {
 59   { "utsname",       0 },
 60   { "percpup",       0 },
 61   { "cpu_infos",     0 },
 62   { "ncpu",          0 },
 63   { "SCSI_Stat_Ptr", 0 },
 64   { "lbolt",         0 },
 65 
 66   { 0,               0 }
 67 };
 68 
 69 
 70 
 71 /* kernel_symbols[] -- list of places to look for the running kernel's symbols
 72 */
 73 char *kernel_symbols[] = {
 74   0,                   /* used by -kernel option  */
 75   "/stand/unix.diag",
 76   "/stand/unix",
 77   "/stand/unix.old",
 78 
 79   0
 80 };
 81   
 82 
 83 
 84 enum stype
 85 {
 86   CPU_STATE,
 87   DISK_STATE,
 88 };
 89 
 90 enum stype      state = CPU_STATE;
 91 int             kmem;              /* file descriptor for /dev/kmem      */
 92 char           *kernel_name;       /* name of kernel (/stand/unix)       */
 93 struct utsname  nodename;          /* nodename.nodename                  */
 94 int             kmem_is_mapped;
 95 int             kmem_is_reading;
 96 int             kmem_can_map    = 1;
 97 
 98 
 99 /*  Fake kernel memory mapping information
100 */
101 typedef struct kmem_map
102 {
103   struct kmem_map *km_next;
104   void            *km_addr;
105   size_t           km_size;
106   char             km_buf[4];
107 } kmem_map_t;
108 
109 kmem_map_t *kmem_list;             /* linked list of fake memory mappings */
110 
111 /*  CPU Information
112 */
113 int             kncpu;             /* maximum number of cpu's on system  */
114 int             ncpus;             /* number of cpu's which exist        */
115 percpu_t      **kpercpup;          /* array of percpu_t pointers         */
116 percpu_t      **xpercpup;          /* percpu_t pointers when mmapped     */
117 percpu_t      **mypercpup    = 0;  /* Pointers to each cpu's percpu area */
118 time_t         *lastcpu;           /* last snapshot of each cpu's times  */
119 cpu_info_t     *kcpu_infos;        /* State of each cpu                  */
120 
121 
122 /* SCSI Disk Information
123 */
124 int             ndisks;
125 long            kscsi_stat_ptr_addr;
126 long            kaddr;
127 
128 typedef struct diskinfo
129 {
130   dev_t            di_dev;
131   SCSI_Device_t   *di_kscsi;
132   StatStructure_t  di_stats;
133 } diskinfo_t;
134 
135 long           *lboltp;
136 long            last_lbolt, new_lbolt;
137 diskinfo_t     *kscsi_stat;
138 SCSI_Device_t  *lscsi_stat;
139 SCSI_Device_t   tscsi_stat;
140 
141 
142 
143 /* The size of the array which holds the cpu
144    execution information.
145 */
146 #if defined( CPU_Pentium )
147 #define  MPSYSINFO     c_sysinfo
148 #else
149 #define  MPSYSINFO     c_mpinfo
150 #endif
151 
152 #define CPU_STATE_SZ   sizeof(mypercpup[0]->MPSYSINFO.cpu)
153 
154 
155 /* Computes the time spent in the specified state.
156 */
157 
158 #define delta(state) \
159    ((int) mypercpup[cpu]->MPSYSINFO.cpu[state] - cputime[state])
160 
161 
162 /* Save the state times for a specific cpu
163 */
164 #define savecpuinfo(cpu) \
165    memcpy(cputime, mypercpup[cpu]->MPSYSINFO.cpu, CPU_STATE_SZ);
166 
167 
168 /* The size of the local state time array
169 */
170 #define NSTATES   CPU_STATE_SZ
171 #define CPUSTATES (CPU_STATE_SZ / sizeof(mypercpup[0]->MPSYSINFO.cpu[0]))-1
172 
173 /* Display an error message based upon errno and
174    return 0 to caller
175 */
176 #define ERR_EXIT(msg) \
177 {               \
178    perror(msg); \
179    return 0;    \
180 }
181 
182 
183 /* Read a specified number of bytes of kernel memory
184    and report any error conditions which might arise.
185 */
186 #define seekreadok(desc, seekpos, buf, sz, msg) \
187   if (-1 == lseek(desc, seekpos, SEEK_SET))  \
188     ERR_EXIT("lseeking " msg);               \
189  \
190   if ((sz) != read(desc, buf, (sz)))         \
191     ERR_EXIT("reading " msg);
192 
193 #define seekreaderr(desc, seekpos, buf, sz, msg) \
194    ( -1  == lseek(desc, seekpos, SEEK_SET) ||  \
195     (sz) != read(desc, buf, (sz)))
196 
197 
198 /* fakethis()  -- provide faked out memory mappings if unable to mmap kernel.
199 */
200 void *
201 fakethis(void *loc, size_t sz)
202 {
203   kmem_map_t *mapping = (kmem_map_t *)malloc( sizeof(kmem_map_t) + sz );
204 
205   if (!mapping)
206     return 0;
207 
208   mapping->km_addr = loc;
209   mapping->km_size = sz;
210   mapping->km_next = kmem_list;
211 
212   seekreadok(kmem, mapping->km_addr, mapping->km_buf, mapping->km_size,
213              "faking mmap");
214 
215   if (!kmem_list && kmem_can_map)
216     fprintf(stderr, "Unable to mmap kernel addresses, using lseek/read\n");
217 
218   kmem_list = mapping;
219   kmem_is_reading++;
220 
221   return (void *) mapping->km_buf;
222 }
223 
224 /* refreshfakes()  --  Refresh fake mmap snapshots.
225 */
226 int
227 refreshfakes(void)
228 {
229   kmem_map_t *mapping;
230   int         errs    = 0;
231 
232   for (mapping = kmem_list; mapping; mapping = mapping->km_next)
233     if (seekreaderr(kmem, mapping->km_addr, mapping->km_buf, mapping->km_size,
234                     "refreshing fake mmap's"))
235       errs++;
236 
237   return errs;
238 }
239 
240 /*  Given an address (and size in bytes) in kernel virtual memory,
241     this function uses the mmap function to provide a mapping for
242     those addresses in the current process.  The result is either
243     a pointer to the data in this processes virtual space or
244     (void *)-1 if unable to provide the mapping.
245 */
246 void *
247 mapthis(int desc, void *locp, unsigned long sz)
248 {
249   unsigned long pgoffs = (unsigned long) locp & POFFMASK;
250   unsigned long pgstrt = (unsigned long) locp & PG_ADDR;
251   unsigned long pgend  = ((unsigned long) locp + sz) & PG_ADDR;
252   unsigned long mapsz  = pgend - pgstrt + NBPP;
253   char         *map    = (char *)-1;
254 
255   if (kmem_can_map)
256     map = mmap(0, mapsz, PROT_READ, MAP_SHARED, desc, pgstrt);
257 
258   if ((char *)-1 == map)
259     {
260       if (kmem_is_mapped)
261         fprintf(stderr, "Mixing mapping and lseek/read kernel memory?\n");
262 
263       map = fakethis(locp, sz);
264       return map ? map : (void *)-1;
265     }
266 
267   kmem_is_mapped++;
268   return (void *) (map + pgoffs);
269 }
270 
271 
272 
273 /* get_kernel_symbols  --  Find the file containing the symbol definitions
274                            for the currently running kernel.
275 
276    The kernel must be running out of /stand (on an svr4 system) and the
277    symbol value for utsname must match the name returned by the uname()
278    system call.
279 
280    This function is called given a list of possible files from /stand which
281    may be running and returns the name of the first file which appears
282    to be correct.
283 */
284 
285 char *
286 get_kernel_symbols(int kmemfd, char **knamelist)
287 {
288   char **cpp;
289   int    nf, errs;
290   int    nsz = strlen(nodename.nodename) + 1;
291   struct utsname nname;
292 
293   if (1 >= nsz)
294     fprintf(stderr, "xcpustate: Empty node name\n");
295 
296   if (kernelSymbols)
297     *knamelist = kernelSymbols;
298 
299   if (!*knamelist) knamelist++;  /* skip over initial NULL pointer */
300 
301   for (cpp = knamelist; *cpp; cpp++)
302     {
303       for (nf = 0; kernel_nl[nf].n_name; nf++)
304         kernel_nl[nf].n_value = 0;
305 
306       nf = nlist(*cpp, kernel_nl);
307       if (-1 == nf || 0 == kernel_nl[KERNEL_UTSNAME].n_value)
308         continue;
309 
310       if (!seekreaderr(kmemfd, kernel_nl[KERNEL_UTSNAME].n_value,
311                      &nname.nodename, nsz, "utsname") &&
312           0 == strncmp(nodename.nodename, nname.nodename, nsz))
313         {
314           for (errs = nf = 0; kernel_nl[nf].n_name; nf ++)
315             if (0 == kernel_nl[nf].n_value)
316               {
317                 fprintf(stderr, "kernel symbol `%s' not found\n",
318                         kernel_nl[nf].n_name);
319                 errs++;
320               }
321 
322           if (errs)
323             return 0;
324 
325           return *cpp;
326         }
327     }
328 
329   fprintf(stderr, "xcpustate: No kernel symbol file matches running kernel\n");
330   return 0;
331 }
332 
333 
334 
335 /* init_cpu_init()  --  Initialize for CPU display
336 */
337 int
338 init_cpu_info(void)
339 {
340   int nf;
341 
342   seekreadok(kmem,
343              kernel_nl[KERNEL_NCPU].n_value,
344              &kncpu,
345              sizeof(kncpu),
346              "/dev/kmem [ncpu]");
347 
348   if (kncpu > 64 || kncpu < 0)
349     {
350       fprintf(stderr, "Unlikely value for ncpu found.\n");
351       return 0;
352     }
353 
354   kcpu_infos = mapthis(kmem,
355                        (void *)kernel_nl[KERNEL_CPU_INFOS].n_value,
356                        kncpu*sizeof(cpu_info_t));
357   if (-1 == (int)kcpu_infos)
358     ERR_EXIT("mapping cpu_infos[]");
359 
360   kpercpup   = mapthis(kmem,
361                        (void *)kernel_nl[KERNEL_PERCPUP].n_value,
362                        kncpu * sizeof(percpu_t *));
363   if (-1 == (int)kpercpup)
364     ERR_EXIT("mapping percpup[]");
365 
366   mypercpup = (percpu_t **) malloc( sizeof(percpu_t *) * kncpu );
367   if (!mypercpup)
368     ERR_EXIT("allocating memory");
369 
370   xpercpup   = (percpu_t  **) malloc( kncpu * sizeof(percpu_t *));
371   if (!kpercpup)
372     ERR_EXIT("allocating memory");
373 
374   memset(xpercpup, 0, kncpu * sizeof(percpu_t *));
375 
376   for (ncpus = nf = 0;  nf < kncpu; nf++)
377     if (kcpu_infos[nf].cpu_flag & CPU_EXISTS)
378       {
379         if (kpercpup[nf])
380           {
381             xpercpup[ncpus] = kpercpup[nf];
382             mypercpup[ncpus] = mapthis(kmem,
383                                        xpercpup[ncpus],
384                                        sizeof(*(kpercpup[nf])));
385             if (-1 == mypercpup[ncpus])
386               ERR_EXIT("mapping percpu area");
387           }
388         ncpus++;
389       }
390 
391   nf = ncpus * CPU_STATE_SZ;
392   lastcpu = (time_t *) malloc(nf);
393   memset(lastcpu, 0, nf);
394 
395   return ncpus;
396 }
397 
398 
399 /* scsitype()  --  return string naming scsi device type.
400 */
401 char *
402 scsitype(int t)
403 {
404   switch (t)
405     {
406     case SCSI_DAD_TYPE:     return "disk";
407     case SCSI_SAD_TYPE:     return "tape";
408     case SCSI_PRT_TYPE:     return "printer";
409     case SCSI_PROC_TYPE:    return "proc";
410     case SCSI_WORM_TYPE:    return "worm";
411     case SCSI_CD_ROM_TYPE:  return "cdrom";
412     case SCSI_SCAN_TYPE:    return "scanner";
413     case SCSI_OPTICAL_TYPE: return "optical";
414     case SCSI_CHANGER_TYPE: return "changer";
415     case SCSI_COMM_TYPE:    return "comm";
416     }
417 
418   return "<unknown>";
419 }
420 
421 
422 /* diskname()   --  Return string naming disk device.
423 */
424 char *
425 diskname(Partition_Identifier_t *s)
426 {
427   static char name[52];
428 
429   if (0 == s->IO_Bus)
430     {
431       if (0 == s->SCSI_Controller)
432         sprintf(name, "c%xt%xd%xs%x",
433                 s->SCSI_Bus,
434                 s->PUN, s->LUN, s->Partition);
435       else
436         sprintf(name, "c%x%xt%xd%xs%x",
437                 s->SCSI_Controller, s->SCSI_Bus,
438                 s->PUN, s->LUN, s->Partition);
439     }
440   else
441     sprintf(name, "c%x%x%xt%xd%xs%x",
442             s->IO_Bus, s->SCSI_Controller, s->SCSI_Bus,
443             s->PUN,    s->LUN,             s->Partition);
444 
445   return name;
446 }
447 
448 int
449 init_disk_info(void)
450 {
451   long kaddr;
452   int  cdisk;
453   int head = 0, nprocs = 0, ncdroms = 0, ntapes = 0;
454   
455   seekreadok(kmem,
456              kernel_nl[KERNEL_SCSI_STAT].n_value,
457              &kscsi_stat_ptr_addr,
458              sizeof(kscsi_stat_ptr_addr),
459              "/dev/kmem [SCSI_Stat_Ptr]");
460 
461   kaddr  = kscsi_stat_ptr_addr;
462   ndisks = 0;
463 
464   do
465     {
466       seekreadok(kmem,kaddr,&tscsi_stat,sizeof(tscsi_stat),"/dev/kmem [SCSI]");
467       switch (tscsi_stat.inquiry_data.Periph_Device_Type)
468         {
469         case SCSI_DAD_TYPE:     ndisks++;   break;
470         case SCSI_SAD_TYPE:     ntapes++;   break;
471         case SCSI_CD_ROM_TYPE:  ncdroms++;  break;
472         case SCSI_PROC_TYPE:    nprocs++;   break;
473         }
474       kaddr = (long) tscsi_stat.Statistics.ForwardPointer;
475     }
476   while (kscsi_stat_ptr_addr != kaddr);
477 
478   fprintf(stderr, "SCSI: %d disks, %d tapes, %d cdroms, %d controllers\n",
479           ndisks, ntapes, ncdroms, nprocs);
480 
481   kscsi_stat = (diskinfo_t *) malloc((1+ndisks) * sizeof(diskinfo_t));
482   if (!kscsi_stat)
483     ERR_EXIT("Allocate SCSI Disk memory");
484 #if 0
485   lscsi_stat = (SCSI_Device_t *) malloc((1+ndisks) * sizeof(SCSI_Device_t));
486   if (!lscsi_stat)
487     ERR_EXIT("Allocate SCSI Statistics array");
488   memset(lscsi_stat, 0, ndisks * sizeof(SCSI_Device_t));
489 #endif
490 
491   kaddr  = kscsi_stat_ptr_addr;
492   cdisk = 0;
493 
494   lboltp = (long *) mapthis(kmem, (void *)kernel_nl[KERNEL_LBOLT].n_value,
495                             sizeof(long));
496   if (-1 == (long) lboltp)
497     {
498       perror("Can't map lbolt");
499       return 0;
500     }
501   last_lbolt = *lboltp;
502 
503   do
504     {
505       lscsi_stat = (SCSI_Device_t *) mapthis(kmem, (void *)kaddr,
506                                              sizeof(tscsi_stat));
507       if (-1 == (long) lscsi_stat)
508         {
509           perror("Can't map SCSI information");
510           return 0;
511         }
512 
513       kscsi_stat[cdisk].di_kscsi = (SCSI_Device_t *) lscsi_stat;
514       kscsi_stat[cdisk].di_dev   = lscsi_stat->dev;
515       memcpy(&kscsi_stat[cdisk].di_stats, &lscsi_stat->Statistics,
516              sizeof(lscsi_stat->Statistics));
517 
518       if (!head++)
519        fprintf(stderr,"  #  Bus  Ctrlr  SBus  PUN  LUN  Part  dev_num Name\n");
520 
521       fprintf(stderr, "%3d  %3d  %5d  %4d  %3d  %3d  %4d %08x %s (%s)\n",
522               cdisk,
523               lscsi_stat->SCSI_device.IO_Bus,
524               lscsi_stat->SCSI_device.SCSI_Controller,
525               lscsi_stat->SCSI_device.SCSI_Bus,
526               lscsi_stat->SCSI_device.PUN,
527               lscsi_stat->SCSI_device.LUN,
528               lscsi_stat->SCSI_device.Partition,
529               lscsi_stat->dev,
530               diskname(&lscsi_stat->SCSI_device),
531               scsitype(lscsi_stat->inquiry_data.Periph_Device_Type));
532               
533       kaddr = (long) lscsi_stat->Statistics.ForwardPointer;
534 
535       if (SCSI_DAD_TYPE == lscsi_stat->inquiry_data.Periph_Device_Type)
536         cdisk++;
537     }
538   while (kscsi_stat_ptr_addr != kaddr);
539 
540   if (cdisk != ndisks)
541     ERR_EXIT("Error counting disks");
542 
543   return ndisks;
544 }
545 
546 
547 
548 /* Called at the beginning to inquire how many bars are needed
549 */
550 int
551 num_bars(void)
552 {
553   int         nf;
554   int         errs;
555   int         sz;
556   char       *pgs    = 0;
557 
558   kmem   = open("/dev/kmem", O_RDONLY);
559   if (-1 == kmem)
560     ERR_EXIT("opening /dev/kmem");
561 
562   if (-1 == uname(&nodename))
563     ERR_EXIT("uname");
564 
565   kernel_name = get_kernel_symbols(kmem, kernel_symbols);
566   if (!kernel_name)
567     return 0;
568 
569   if (diskflag)
570     {
571       if (cpuflag)
572         fprintf(stderr, "Only disk or cpu state can be monitored\n");
573       state  = DISK_STATE;
574       return init_disk_info();
575     }
576 
577   return init_cpu_info();
578 }
579 
580 
581 
582 
583 /*
584  * Indicates how many levels each bar has.  For most machines, each bar will
585  * have the same stuff.  But one can, for instance, display memory use on one
586  * bar, processor levels on others, etc.
587  */
588 void
589 bar_items(nbars, items)
590 int nbars;
591 int items[];    /* nbars items in this */
592 {
593   int i;
594   int nlevels = (DISK_STATE == state) ? 2 : CPUSTATES;
595 
596   for (i = 0; i < nbars; )
597     items[i++] = nlevels;
598 }
599 
600 
601 /* Called after num_bars to ask for bar names */
602 char **
603 label_cpu_bars(nbars)
604 {
605   char **names;
606   int    hsz   = strlen(nodename.nodename);
607 /*****                  ' '   '#'   %d    ' '   'i486'   ' '  *****/
608   int    rowsz = hsz +   1 +   1 +   3 +   1 +      7 +   2;
609   int    sz    = nbars * rowsz;
610   int    cpu, i;
611   char  *cp;
612 
613   shorten(nodename.nodename);
614 
615   names  = (char **)malloc(sz + sizeof(char *) * nbars);
616 
617   cp     = (char *)names + sizeof(char *) * nbars;
618 
619   for (cpu = i = 0; i < kncpu; i++)
620     if (kcpu_infos[i].cpu_flag & CPU_EXISTS)
621       {
622         names[cpu++] = cp;
623 
624         if (1 == ncpus)
625           sprintf(cp, "%s", nodename.nodename);
626         else if (0 == i)
627           sprintf(cp, "%s #%d", nodename.nodename, i);
628         else
629           sprintf(cp, "%*s #%d", hsz, "", i);
630 
631         cp += rowsz;
632       }
633 
634   return names;
635 }
636 
637 char **
638 label_disk_bars(nbars)
639 {
640   char **names;
641   char  *cp;
642   int    i;
643   int    hsz    = sizeof("cxxxtxdxsx");
644 
645   names = (char **) malloc( hsz * nbars + nbars * sizeof(char *) );
646   cp    = (char *) names + sizeof(char *) * nbars;
647 
648   for (i = 0; i < nbars; i++)
649     {
650       names[i]   = cp;
651       lscsi_stat = kscsi_stat[i].di_kscsi;
652       strcpy(cp, diskname(&lscsi_stat->SCSI_device));
653       cp += hsz;
654     }
655 
656   return names;
657 }
658 
659 
660 char **
661 label_bars(nbars)
662 {
663   switch (state)
664     {
665     case CPU_STATE:  return label_cpu_bars(nbars);
666     case DISK_STATE: return label_disk_bars(nbars);
667     }
668 
669   return NULL;
670 }
671 
672 
673 /*  Called after the bars are created to do machine dependent inits */
674 void
675 init_bars(nbars)
676   int nbars;
677 {
678   (void) display_bars(nbars);
679 }
680 
681 
682 
683 
684 /*
685    This function is called every interval to compute and display
686    the bars.  It should call draw_bar() with the bar number, the array of
687    integer values to display in the bar, and the number of values in the
688    array.
689 */
690 void
691 display_cpu_bars(nbars)
692 {
693   int states[NSTATES];
694   int nstates;
695   int cpu, i;
696   time_t *cputime = lastcpu;
697 
698   for (i = cpu = 0; i < kncpu && cpu < nbars; i++)
699     {
700       if (0 == (kcpu_infos[i].cpu_flag & CPU_EXISTS))
701         continue;
702 
703       nstates = 0;
704 
705       if (kcpu_infos[i].cpu_flag & CPU_RUNNING && kpercpup[i])
706         {
707           if (kpercpup[cpu] != xpercpup[i])
708             { /* mapping changed or became available */
709               xpercpup[cpu] = kpercpup[i];
710               mypercpup[ i] = mapthis(kmem, xpercpup[cpu],
711                                       sizeof(*(xpercpup[cpu])));
712             }
713           states[nstates++] = delta(CPU_IDLE);
714           states[nstates++] = delta(CPU_USER);
715           states[nstates++] = delta(CPU_KERNEL);
716           states[nstates++] = delta(CPU_WAIT) + delta(CPU_SXBRK);
717 #if 0
718           states[nstates++] = delta(CPU_SXBRK);
719 #endif
720         }
721       else
722         {
723           states[nstates++] = 100;  /* CPU_IDLE   */
724           states[nstates++] =   0;  /* CPU_USER   */
725           states[nstates++] =   0;  /* CPU_KERNEL */
726           states[nstates++] =   0;  /* CPU_WAIT   */
727 #if 0
728           states[nstates++] =   0;  /* CPU_SXBRK  */
729 #endif
730         }
731       savecpuinfo(cpu);
732       draw_bar(cpu, states, nstates);
733 
734       cpu++;
735       cputime += CPU_STATE_SZ / sizeof(time_t);
736     }
737 }
738 
739 
740 void
741 display_disk_bars(nbars)
742 {
743   int    states[2];
744   int    i;
745   time_t t;
746   static time_t maxt = 0;
747   static int    npr  = 0;
748   time_t dlt;
749   struct iotime *new, *old;
750 
751   new_lbolt  = *lboltp;
752   dlt        = new_lbolt - last_lbolt;
753   last_lbolt = new_lbolt;
754   
755   for (i = 0; i < ndisks; i++)
756     {
757       lscsi_stat = kscsi_stat[i].di_kscsi;
758 
759       if (kscsi_stat[i].di_dev != lscsi_stat->dev)
760         { /* device changed */
761           states[0] = 100;  /* idle */
762           states[1] = 0;    /* busy */
763         }
764       else
765         {
766           new       = &lscsi_stat->Statistics.SCSI_Stats;
767           old       = &kscsi_stat[i].di_stats.SCSI_Stats;
768           t         = new->io_act - old->io_act;
769 
770           states[1] = t;
771           if (states[1] > dlt)
772             states[0] = 0;
773           else
774             states[0] = dlt - states[1];
775 
776           old->io_act  = new->io_act;
777           old->io_resp = new->io_resp;
778         }
779       draw_bar(i, states, 2);
780     }
781 }
782 
783 void
784 display_bars(nbars)
785 {
786   if (kmem_list)
787     refreshfakes();
788 
789   switch (state)
790     {
791     case CPU_STATE:  display_cpu_bars(nbars);
792     case DISK_STATE: display_disk_bars(nbars);
793     }
794 
795   return;
796 }