/* 
 * System dependent file for SunOS4.x machines, using the kvm library.
/* John DiMarco, CSLab, University of Toronto <jdd@cs.toronto.edu> */

/* LINTLIBRARY */
#define MULTIPROCESSOR

#include <sys/types.h>
#include <sys/file.h>
#include <sys/dk.h>
#include <nlist.h>
#include <kvm.h>
#include <sundev/mbvar.h>

#ifdef OMNI
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/buf.h>
#include <sys/filio.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <sundev/mbvar.h>
#include "/usr/sys/sundev/nereg.h"

#define OMNICPU ns_st.nst_cpu_cur
#define OMNISTATES 2 /* system, idle */

static int nomni = 0;
#define NEMAX 16
#define NEBASE "/dev/ne"
static int omfd[NEMAX];
static int omnum[NEMAX];
extern int omniflag;
extern int open();

#endif

#ifndef MAXHOSTNAMELEN
  /* Some U*x deviants managed to not have this in sys/param.h */
# define MAXHOSTNAMELEN 64
#endif

/* define some extended processor states here, in case they're not defined
   in sys/dk.h (which is the case for 4.1.1 and below). */
#ifndef XPSTATES
#define XPSTATES 8
#define XP_SPIN 0
#define XP_DISK 1
#define XP_SERV 2
#endif /* XPSTATES */

static int x_cp_time;

static char *dk_name[DK_NDRIVE];
static int hz;
static long dk_bps[DK_NDRIVE];

extern char *xmalloc(/* int nbytes */);
extern void shorten(/* char *hname */);

extern int diskflag;
extern int cpuflag;
extern char *kernelSymbols;

#define NCPU 4  /* maximum possible number of CPUs. This isn't likely to   */
                /* change under SunOS4.x. SunOS5.x will require a new port */
                /* of xcpustate anyways, so no need to think ahead here.   */

#define DISKSTATES 3 /* idle, seek, transfer */
#define MPSTATES 6 /* idle, user, nice, system, spin, serv */

#define NULL (char *)0

static kvm_t *kd;

int ncpu;       /* number of CPUs */
int ndisk;      /* number of disks */
int procset;    /* flag of CPUs */

struct nlist	nl[] = {
#define N_NCPU          0
        { "_ncpu" },
#define N_DK_BPS        1
        { "_dk_bps" },
#define N_CP_TIME       2
        { "_cp_time" },
#define N_DK_TIME       3
        { "_dk_time" },
#define N_DK_WDS        4
        { "_dk_wds" },
#define N_MP_TIME       5
        { "_mp_time" },
#define N_XP_TIME       6
        { "_xp_time" },
#define N_MBDINIT       7
        { "_mbdinit" },
#define N_DK_IVEC       8
        { "_dk_ivec" },
#define N_HZ            9
        { "_hz" },
#define N_PROCSET      10
        { "_procset" },
	{ "" },
};

#define DK_WDS_SIZE 64.0
#define BUFFSIZE 64

extern void draw_bar(/*int bar_num, int *states, int num_states*/);

static char *kernel_getstring(address, info)
char *address;
char *info;
{
        static char buff[BUFFSIZE];

        if(kvm_read(kd, (u_long)address, (char *)buff, 
		sizeof(buff)) != sizeof(buff)){
                perror(info);
        }
        buff[BUFFSIZE-1]='\0'; /* just in case it's not null-terminated */
        return(buff);
}

static void kernel_get(addr, dest, size, info)
u_long addr;
char *dest;
u_int size;
char *info;
{
	/* get something from the kernel */
	if(kvm_read(kd, addr, dest, size)!=size){
		perror(info);
		exit(1);
	}
}

static void kernel_init(name)
char *name;
{
	/* initialize kernel */
	if((kvm_t *)NULL==(kd=kvm_open(kernelSymbols, NULL, NULL, O_RDONLY, 
			name))){
		perror("kvm_open");
		exit(1);
	}

	/* read namelist */
	if(-1==kvm_nlist(kd, nl)){
		perror("kvm_nlist");
		exit(1);
	}
}

#ifdef OMNI
static int omni_init()
{
	/* count OMNI network coprocessors, initialize omfd/num tables */

	char buff[BUFFSIZE];
	int fd, there=0, i, n=0;

	/* probe /dev/ne0../dev/neNEMAX-1, check if present */
	for(i=0;i<NEMAX;i++){
		(void) sprintf(buff, "%s%d", NEBASE, i);
		if(-1!=(fd=open(buff, O_RDONLY))){
			if(-1!=ioctl(fd, NIOISTHERE, &there)) {
				if(there) {
					omfd[n]=fd;
					omnum[n]=i;
					n++;
				}
			}
		}
	}
	return(n);
}
#endif /* OMNI */

static int cpu_init()
{
	/* count cpus */

	int n=1; /* assume uniprocessor by default */

	if(N_UNDF!=nl[N_NCPU].n_type) {
		kernel_get(nl[N_NCPU].n_value, (char *)&n, sizeof(n), "ncpu");
		if(NCPU<n || n<1){
			/* if n is unreasonable, assume uniprocessor */
			n=1;
		}
		procset = 1;
		kernel_get(nl[N_PROCSET].n_value, (char *)&procset, sizeof(procset), "procset");
	} 
	return(n);
}

static int disk_init()
{
	/* count disks, initialize dk_name table */

	int n=0;
	if(N_UNDF!=nl[N_DK_IVEC].n_type){
		/* devinfo machine (openboot prom) */
		struct dk_ivec div, *div_p;

		for(div_p=(struct dk_ivec *)nl[N_DK_IVEC].n_value;
			n<DK_NDRIVE;div_p++){
			char *name;

			kernel_get((u_long)div_p, (char *)&div, sizeof(div),
					"div");
			if(NULL==div.dk_name) break;
			name=kernel_getstring(div.dk_name, "div.dk_name");
			if('\0'==*name) continue; 

			dk_name[n]=xmalloc(BUFFSIZE);
			(void) sprintf(dk_name[n], "%s%d", name, div.dk_unit);
			n++;
		}
	} else {
		/* mainbus machine */
		struct mb_device mbd, *mbd_p;
		struct mb_driver mdr;

		for(mbd_p=(struct mb_device *)nl[N_MBDINIT].n_value;
			n<DK_NDRIVE;mbd_p++){
			kernel_get((u_long)mbd_p, (char *)&mbd, sizeof(mbd),
					 "mbd");

			if(NULL==(char *)mbd.md_driver) break; 

			if(mbd.md_dk<0 || 0==mbd.md_alive) continue; 

			kernel_get((u_long)mbd.md_driver, (char *)&mdr, 
					sizeof(mdr), "mdr");
			dk_name[mbd.md_dk]=xmalloc(BUFFSIZE);
			(void) sprintf(dk_name[mbd.md_dk],
				"%s%d", kernel_getstring(mdr.mdr_dname, 
							 "mdr_dname"),
				mbd.md_unit);
			n++;
		}
	}

#if 0
	/* pad names so that they're the same length */
	for(i=0;i<n;i++){
		tmp=strlen(dk_name[i]);	
		if(tmp>maxlen) maxlen=tmp;
	}
	for(i=0;i<n;i++){
		tmp=strlen(dk_name[i]);
		for(j=tmp;j<maxlen;j++){
			*(dk_name[i]+j)=' ';
		}
		*(dk_name[i]+maxlen)='\0';
	}
#endif /* 0 */

	return(n);
}

/* Called by -version */
void
version()
{
	printf("SunOS 4.x: maxcpu=%d\n", NCPU);
#ifdef OMNI
	printf("Omni Network Coprocessors: maxcpu=%d\n", NEMAX);
#endif /* OMNI */
}

/* Called at the beginning to inquire how many bars are needed. */
int
num_bars()
{

	int nbars=0; 

	/* Open the kernel interface */
	kernel_init("xcpustate");

	if(cpuflag){
		ncpu=cpu_init();
		if(ncpu>0) {
			nbars += ncpu;
		} else {
			cpuflag=0;
		}
	}
#ifdef OMNI
	if(omniflag){
		nomni=omni_init();
		if(nomni>0) {
			nbars += nomni;
		} else {
			omniflag=0;
		}
	}
#endif /* OMNI */
	if(diskflag){
		ndisk=disk_init();
		if(ndisk>0) {
			nbars += ndisk;
		} else {
			diskflag=0;
		}
	}
	return(nbars);
}

/*
 * Indicates how many levels each bar has.  For most machines, each bar will
 * have the same stuff.  But one can, for instance, display memory use on one
 * bar, processor levels on others, etc.
 */
void
bar_items(nbars, items)
int nbars;
int items[];    /* nbars items in this */
{
    int i, n=0;

    if(cpuflag){
	for(i=0; i<ncpu; i++,n++){
		items[n] = (ncpu>1)?MPSTATES:CPUSTATES;
	}
    }
#ifdef OMNI
    if(omniflag){
	for(i=0; i<nomni; i++,n++){
		items[n] = OMNISTATES;
	}
    }
#endif /* OMNI */
    if(diskflag){
	for(i=0; i<ndisk; i++,n++){
		items[n] = DISKSTATES;
	}
    }
}

/* Called after num_bars to ask for the bar names */
char **
label_bars(nbars)
{
        char **names;
        int i, j, base;
        extern char *strcpy();
        static char hname[MAXHOSTNAMELEN + 1];
	char buf[MAXHOSTNAMELEN + 1 + BUFFSIZE];
	char *cpname="";

        hname[MAXHOSTNAMELEN] = '\0';
        if (0 >gethostname(hname, MAXHOSTNAMELEN)) {
                perror("gethostname");
                *hname = '\0';
        }
        shorten(hname);
        names = (char **) xmalloc(nbars * sizeof(char *));

	base=0; 

#ifdef OMNI
	if(omniflag) cpname="CP"; /* to distinguish main CPU(s) from nc400s */
#endif /* OMNI */

	if(cpuflag) {
		/* do cpu names */
		for(i=0, j=0; j<NCPU; j++) {
		    if ((procset >> j) & 0x01) {
			(void) sprintf(buf, "%s%s%s%d", hname, 
					hname[0]?" ":"", cpname, j);
			names[base+i] = strcpy(xmalloc(strlen(buf) + 1), buf);
			i++;
		    }
		}
		base+=ncpu;
	}
#ifdef OMNI
	if(omniflag) {
		/* do NC400 names */
		for(i=0; i<nomni; i++) {
			(void) sprintf(buf, "%s%s%s%d", hname, 
					hname[0]?" ":"", "NE", omnum[i]);
			names[base+i] = strcpy(xmalloc(strlen(buf) + 1), buf);
		}
		base+=nomni;
	}
#endif /* OMNI */
	if(diskflag) {
		/* do disk names */
		for(i=0; i <ndisk; i++) {
			(void) sprintf(buf, "%s%s%s", hname, hname[0]?" ":"", 
					dk_name[i]);
			names[base+i] = strcpy(xmalloc(strlen(buf) + 1), buf);
		}
		base+=ndisk;
        }
        return names;
}

/* 
 *  Called after the bars are created to perform any machine dependent
 *  initializations.
 */
/* ARGSUSED */
void
init_bars(nbars)
int nbars;
{
	if(diskflag){
		kernel_get(nl[N_HZ].n_value, (char *)&hz, sizeof(hz), "hz");
		kernel_get(nl[N_DK_BPS].n_value, (char *)dk_bps, 
			sizeof(dk_bps), "dk_bps");
	}
	if(cpuflag){
		if(ncpu>1){
			x_cp_time=N_MP_TIME;
		} else {
			x_cp_time=N_CP_TIME;
		}
	}
}

#ifdef OMNI
static void
display_omni(base)
int base;
{
	/* display OMNI bars */
	int states[OMNISTATES], nstates, i;
	struct ne_usrstat u;
	struct iovec iov;

	iov.iov_base=(char *)&u;
	iov.iov_len=sizeof(u);

	for(i=0;i<nomni;i++){
		nstates=0;

		u.OMNICPU = 0; /* in case the ioctl fails */
		(void)ioctl(omfd[i], NIOGETSTAT, &iov);

		states[nstates++] = 100-u.OMNICPU;
		states[nstates++] = u.OMNICPU;
		draw_bar(i+base, states, nstates);
	}
}
#endif /* OMNI */

static void
display_cpu(base)
int base;
{
	int states[CPUSTATES+XPSTATES], nstates, i, j, p;
	long cp_time[NCPU][CPUSTATES], xp_time[NCPU][XPSTATES];
	static long cp_old[NCPU][CPUSTATES], xp_old[NCPU][XPSTATES];

	kernel_get(nl[x_cp_time].n_value, (char *)cp_time, sizeof(cp_time), 
			"cp_time");
	if(ncpu>1){
		kernel_get(nl[N_XP_TIME].n_value, (char *)xp_time, 
				sizeof(xp_time), "xp_time");
	}
#define xdelta(cpu, cpustate) ((int) (xp_time[cpu][(cpustate)] - \
        xp_old[cpu][(cpustate)]))

#define delta(cpu, cpustate) ((int) (cp_time[cpu][(cpustate)] - \
        cp_old[cpu][(cpustate)]))


	for(p=0,i=0; i<NCPU; i++){
	    if ((procset >> i) & 0x01) {
		nstates=0;
		states[nstates++] = delta(i, CP_IDLE);
		states[nstates++] = delta(i, CP_USER);
		states[nstates++] = delta(i, CP_NICE);
		if(ncpu>1){
			long sys, spin, serv;
			
			sys = delta(i, CP_SYS);
			spin = xdelta(i, XP_SPIN);
			serv = xdelta(i, XP_SERV);
			
			states[nstates++] = sys - (spin + serv);
			states[nstates++] = spin;
			states[nstates++] = serv;
		} else {
			states[nstates++] = delta(i, CP_SYS);
		}
		draw_bar(p+base, states, nstates);

		/* save old values */
		for (j = 0; j < XPSTATES; j++){
			xp_old[i][j] = xp_time[i][j];
		}
		for (j = 0; j < CPUSTATES; j ++){
			cp_old[i][j] = cp_time[i][j];
		}
		p++;
	    }
	}
}

static void
display_disk(base)
int base;
{
	int states[DISKSTATES], i;
	long dk_time[DK_NDRIVE], dk_wds[DK_NDRIVE], dk_cp_time[CPUSTATES];
	static long old_dk_time[DK_NDRIVE], old_dk_wds[DK_NDRIVE];
	static double oldtime;
	double newtime, elapsedtime;

	/* compute elapsed time since last call */
	kernel_get(nl[N_CP_TIME].n_value, (char *)dk_cp_time, 
			sizeof(dk_cp_time), "dk_cp_time");
	newtime=0.0;
	for(i=0;i<CPUSTATES;i++) newtime += (double)dk_cp_time[i];
	if(newtime<=0.0) newtime=1.0;
	newtime /= (double)hz;
	elapsedtime = newtime - oldtime;
	oldtime = newtime;

	kernel_get(nl[N_DK_WDS].n_value, (char *)dk_wds, sizeof(dk_wds), 
			"dk_wds");
	kernel_get(nl[N_DK_TIME].n_value, (char *)dk_time, sizeof(dk_time), 
			"dk_time");

	for(i=0; i<ndisk; i++){
		int nstates=0;
		double activetime, transfersize, diskrate, transfertime;
		double seektime, idletime;

		activetime = (double)(dk_time[i]-old_dk_time[i])/(double)hz;
		diskrate = (double)dk_bps[i]*elapsedtime;
		transfersize = (double)(DK_WDS_SIZE*(dk_wds[i]-old_dk_wds[i]));
		if(diskrate > 0.0){
			transfertime = transfersize/diskrate;
			if(transfertime>1.0) transfertime=1.0;
		} else {
			transfertime = 0.0;
		}
		transfertime *= activetime;

		seektime = activetime - transfertime;
		if(seektime<0.0) seektime=0.0;

		idletime = elapsedtime - (transfertime + seektime);
		if(idletime<0.0) idletime=0.0;

		states[nstates++] = (long)(hz*idletime*elapsedtime);
		states[nstates++] = (long)(hz*seektime*elapsedtime);
		states[nstates++] = (long)(hz*transfertime*elapsedtime);

		draw_bar(i+base, states, nstates);

		old_dk_wds[i]=dk_wds[i];
		old_dk_time[i]=dk_time[i];
	}
}


/* 
 *  This procedure gets called every interval to compute and display the
 *  bars. It should call draw_bar() with the bar number, the array of
 *  integer values to display in the bar, and the number of values in
 *  the array.
 */
/* ARGSUSED */
void
display_bars(nbars)
int nbars;
{
	int n=0;
	
	if(cpuflag) {
		display_cpu(n);
		n+=ncpu;
	}
#ifdef OMNI
	if(omniflag) {
		display_omni(n);
		n+=nomni;
	}
#endif /* OMNI */
	if(diskflag) {
		display_disk(n);
		n+=ndisk;
	}
}


syntax highlighted by Code2HTML, v. 0.9.1