h39998
s 00016/00011/00573
d D 3.2 85/06/04 15:11:14 dock 2 1
c  include files
e
s 00584/00000/00000
d D 3.1 84/11/13 16:29:47 dan 1 0
c date and time created 84/11/13 16:29:47 by dan
e
u
U
t
T
I 1
D 2
/*  viomain.c -- VIOS main entry point and common routines
 *  copyright (c)
 *	Daniel Steinberg
 *	October, 1983
E 2
I 2

/*
 * %M%: version %I% of %H%
E 2
 *
I 2
 *  viomain.c -- VIOS main entry point and common routines
 *  copyright (c)
 *	Daniel Steinberg	October, 1983
E 2
 */
I 2
#ifdef SCCS
static char *sccsid = "%W%";
#endif
E 2

D 2
#include "viosconf.h"
#include "pktfuncs.h"
#include "devfuncs.h"
#include "handlers.h"
#include "poolfuncs.h"
#include "viocmds.h"
#include "viostatus.h"
E 2
I 2
#include "vinc/viosconf.h"
#include "vinc/pktfuncs.h"
#include "vinc/devfuncs.h"
#include "vinc/handlers.h"
#include "vinc/poolfuncs.h"
#include "vinc/viocmds.h"
#include "vinc/viostatus.h"
E 2


#define LOWPOOL 30



vios_entry (pkt)
    register PKT_PTR pkt;

/* vios_entry (pkt) -- Main VIOS entry point...initiate all pending I/O
 *
 *	in:	pkt		I/O request to queue, or NULL
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	If 'pkt' is not NULL, queue it to the active virtual i/o queue
 *	(Vqueue).  The dequeue all active i/o from Vqueue and process it
 *	until it is complete or has reached a wait state.
 */
{
#ifdef DEBUG3   /*************************************************************/
    register int i;
    if ((i=catch()) NE 0) error("Uncaught error %d in vios_entry", i);
#endif /* DEBUG3 *************************************************************/

    q_virtual(pkt);	/* queue the request, if it is not NULL */

    if (NOT Vioexecuting)	/* if not already executing... */
	{
	Vioexecuting = TRUE;		/* execution in progress */

	do
	    {
	    Vioscheduled = FALSE;	/* clear re-schedule flag */

	    /* Process packets from the Virtual I/O queue, until it is clear */
		    /* (processing might queue other packets) */

	    do_disp();

	    }  while(Vioscheduled);		/* repeat while rescheduled */

	Vioexecuting = FALSE;		/* no longer executing */
	} /*if*/

#ifdef DEBUG3   /*************************************************************/
    uncatch();
    if (Intdepth NE 0) error("Vios_entry() left interrupts disabled", NULL);
#endif /* DEBUG3 *************************************************************/
}

    static int;
do_disp ()

/* do_disp () -- Dequeue active Virtual I/O requests and dispatch
 *
 *	return:	(int)		Number of requests dequeued and processed
 *	thrown:	(NONE)
 *
 *	Dequeue all i/o packets on the active queue (Vqueue) and dispatch
 *	them to their current handlers.
 *
 *	Handlers return by doing one of the following:
 *		1) Throw status:
 *			If V_ALL_FAILURE thrown, set packet to ALL_WAIT_STATE
 *			and put on wait queue.
 *			If V_SYS_WAIT thrown, set packet to SYS_WAIT_STATE
 *			and put on wait queue.
 *			Otherwise, thrown code is set as final i/o status in the
 *			packet.  State is set to DONE_STATE or DONE_WAIT_STATE,
 *			if the packet i/o count is non-zero.
 *		2) Return DONE_STATE:
 *			If packet i/o count is non-zero, set packet state to
 *			DONE_WAIT_STATE and put on i/o wait queue (Wqueue).
 *			Otherwise, call io_complete() to complete i/o packet
 *			processing.
 *		3) Return VIO_RUN_STATE:
 *			Call the current i/o packet handler again.  This is
 *			appropriate when the handler has changed the state
 *			number or handler routine.
 *		4) Return PIO_WAIT_STATE or PINP_WAIT_STATE or POUT_WAIT_STATE:
 *			Set the packet state to the returned state and queue
 *			it on the priority-ordered physical device wait queue
 *			(Pqueue).  It will become active again when the
 *			device becomes available.
 *		5) Return VIO_REQUEUED:
 *			Do nothing.  This indicates that the packet has been
 *			completely handled, to the point of removal or
 *			requeueing and should not be accessed again.
 *		6) Return anything else:
 *			Assume that the return value is a wait state value.
 *			Set the packet state to the return value and queue
 *			it on the i/o wait queue (Wqueue) where it will become
 *			reactivated when its wait condition is satisfied.
 *
 *	Note that interrupts are always re-enabled after a handler returns.
 *	If interrupts must remain disabled, handler should locally call subrs
 *	until critical code is completed.
 */
{
    register PKT_PTR pkt;	/* pointer to current request */
    register IO_PACKET *pk;	/* dereferenced pkt ptr */
    register PKT_STATE hstate;	/* current state of request */
    register int cnt = -1;	/* count of items dequeued */
    int i;

    while (cnt++, (pkt = dq_virtual()) NE NULL)
	{					/* while anything to do */
	switch (hstate=State(pk = *pkt))
	    {
	case NEW_STATE:

#ifdef DEBUG3   /*************************************************************/
    if (Iocount(pk)  OR  Stnum(pk)  OR  (Handler(pk) NE init_request))
	error("Bad new request at %x in do_disp", pk);
#endif /* DEBUG3 *************************************************************/

	    State(pk) = hstate = VIO_RUN_STATE;
	    Statcode(pk) = V_PENDING;

	case VIO_RUN_STATE:
	    /* the following wierd statement calls the handler routine */
	    /*     and loops until the handler returns a state change */
	    /*     or until a completion code is thrown back */
	    switch (i=catch())
		{
	    case 0:
		/* Call handler repeatedly until state change or throw() */
		while ( (hstate=(*Handler(*pkt))(pkt)) EQ VIO_RUN_STATE )
		    {
		    if (Intdepth NE 0)
			{
			Intdepth = 0;	/* make sure interrupts are enabled */
			ena_ints();
			}
		    } /*while-VIO_RUN_STATE*/
		uncatch();
		break;

	    case V_ALL_FAILURE:
		hstate = ALL_WAIT_STATE;
		break;

#ifdef V_OS /************ VIRTUAL OPERATING SYSTEM SUPPORT *************/
	    case V_SYS_WAIT:
		hstate = SYS_WAIT_STATE;
		break;
#endif /***************** VIRTUAL OPERATING SYSTEM SUPPORT *************/

	    default:
		hstate = (Iocount(pk = *pkt) ? DONE_WAIT_STATE : DONE_STATE);
		Statcode(pk) = i;	/* completion code thrown */
		} /*switch-catch*/

	    pk = *pkt;			/* refresh dereferenced pkt ptr */

#ifdef DEBUG3   /*************************************************************/
    if (hstate EQ NEW_STATE)
	error("Handler set bad state in request at %x", pk);
#endif /* DEBUG3 *************************************************************/

	    if (hstate EQ VIO_REQUEUED)	/* don't screw up packet if */
		break;			/* requeued already */

	    if ((State(pk) = hstate) NE DONE_STATE)
		{
		switch (hstate)
		    {
		case VIO_RUN_STATE:
		    q_virtual(pkt);	/* queue virtual I/O */
		    break;

		case PIO_WAIT_STATE:
		case PINP_WAIT_STATE:
		case POUT_WAIT_STATE:
		    q_physical(pkt);	/* queue I/O primitive */
		    break;

		default:
		    q_wait(pkt);	/* queue to wait queue */
		    break;
		    }
		break; /* to while */
		} /*if*/

	    /* fall through if handler returned DONE_STATE */

	case DONE_STATE:
	    if (Iocount(pk) NE 0)
		{
		State(pk) = DONE_WAIT_STATE;
		q_wait(pkt);
		}
	    else
		{
		if (Intdepth NE 0)
		    {
		    Intdepth = 0;	/* make sure interrupts are enabled */
		    ena_ints();
		    }
		io_complete(pkt);
		}
	    break;

#ifdef DEBUG2   /*************************************************************/
	default:
	    error("Request at %x has bad state (in do_vios)", pk);
#endif /* DEBUG2 *************************************************************/

	    } /*switch*/

	if (Intdepth NE 0)
	    {
	    Intdepth = 0;	/* make sure interrupts are enabled */
	    ena_ints();
	    }

	} /*while*/

    return(cnt);	/* return a count of the number of items dequeued */
}


io_complete (pkt)
    PKT_PTR pkt;

/* io_complete (pkt) -- I/O completion routine
 *
 *	mod:	pkt		Target i/o packet
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	Do final processing of i/o packet as follows:
 *		1) If VIRTUAL OPERATING SYSTEM support,
 *		   unlock the primary data buffer, if any.
 *		   (See unlock_buffer() in hypersubs.c)
 *		2) If the i/o packet belongs to the VIOS and the Parentstat
 *		   offset is non-null, copy the i/o status block to the
 *		   i/o packet address specified by Parentstat.
 *		3) If the i/o packet belongs to the Hyper_exec,
 *			**** ????? ****
 *		4) If the i/o packet belongs to a Target OS, copy the
 *		   i/o status back if there is a status block specified
 *		   (see return_stat() in hypersubs.c).
 *		5) If the auxiliary request parameter is non-null, treat
 *		   as a pool bufptr and deallocate it.
 *		6) Decrement the i/o count of the parent i/o packet, if any.
 *		   If the count goes to zero and the packet is waiting for
 *		   i/o completion, activate it.
 *		   (See dec_iocount() below)
 *		7) If there is a target device, decrement its outstanding i/o
 *		   count.  If the count goes to zero and the device was marked
 *		   for removal on i/o completion, the device will be removed.
 *		   (See ddecioc() in rdwrtsubs.c)
 *		8) If the i/o packet belongs to a target Operating System, queue
 *		   an i/o completion packet to the target o.s.  (If allocation
 *		   failure for completion packet, reuse the current packet as a
 *		   completion packet and skip the next step.)
 *		9) Release the i/o packet to system pool.
 */
{
    register IO_PACKET *pk;
    register PKT_PTR ptr;
    register VDD_PTR dev;
    register OS_COMP **c;
    OS_IDENT id;
    int (*rout)();
    unsigned arg;

#ifdef V_OS /************ VIRTUAL OPERATING SYSTEM SUPPORT *************/
    unlock_buffer(pkt);		/* unlock user data buffer, if any */
#endif /***************** VIRTUAL OPERATING SYSTEM SUPPORT *************/

    switch ( id = Compos(pk = *pkt) )
	{
	case VIOS_ID:
	    if ( (ptr = (PKT_PTR) Parentstat(pk)) NE NULL )
		copy_stat (pkt, ptr);		/* copy i/o status */
	    break;

	case HYPER_ID:
	    break;

	case SIM_ID:
	    /* call Simulator Completion Routine with pkt as an argument */
	    (*(Comproutine(pk)))(pkt);
	    break;

	default:
	    return_stat(pkt);		/* send status back to os */
	    break;
	} /*switch*/

    free (Auxparam(pk));		/* free aux param, if any */

    /* decrement parent i/o count */
    if ((ptr = Parent(pk)) NE NULL)  dec_iocount(ptr);

    if ( ((dev = VDevice(pk)) NE NULL) )
	ddecioc(dev);		/* decrement Outstanding_IO of device */

    /* if Target OS i/o request and completion routine specified */
    if ( (id GT 0) AND (Comproutine(pk) NE NULL) )
	{
	/* save pkt information in case re-using current packet */
	rout = Comproutine(*pkt);
	arg = (unsigned) Cstataddr(*pkt);

	/* allocate a completion packet */
	if ( (c = (OS_COMP**) (alloc(sizeof(OS_COMP)))) EQ NULL )
	    c = (OS_COMP**) pkt;	/* alloc failure...reuse current pkt */
	
	/* Set the O.S. completion routine and status block address */
	O_crout(*c) = rout;
	O_carg(*c) = arg;
	O_cpkt(*c) = (int**) pkt;	/* set src pkt num (although invalid */

	q_item(c, &O_cqueue(*((OSD_PTR) id)));	/* put on OS completion queue */
	OSscheduled = TRUE;	/* **** set scheduled flag **** */

	if (c EQ (OS_COMP**) pkt)	/* if re-using current packet */
	    pkt = NULL;			/* don't deallocate it */

	} /*if-completion routine*/

    vfree_pkt(pkt);		/* release I/O packet */
}


copy_stat (from, to)
    PKT_PTR  from;
    PKT_PTR  to;

/* copy_stat (from, to) -- Copy I/O status from one packet to another
 *
 *	in:	from		Source i/o packet for status information
 *	mod:	to		Destination i/o packet for status information
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	Copy the i/o status block from the packet at 'from' to that at 'to'.
 *	This is the mechanism for filtering status back from nested, spawned
 *	i/o requests.
 */
{
    register int i;
    register char *f;
    register char *t;

    f = (char*) &Status(*from);		/* get starting source address */
    t = (char*) &Status(*to);		/*  and destination */
    for (i=0; i<sizeof(PKT_IOSTATUS); i++)
	*t++ = *f++;
}

    char **					/* Throws: V_ALL_FAILURE */
al_pool (nbytes)
    unsigned int nbytes;

/* al_pool (ptr) -- Allocate n-byte buffer...throw V_ALL_FAILURE if failure
 *
 *	in:	nbytes		Request size in "sizeof ..." units
 *	return:	(char **)	Bufptr to allocation
 *	thrown:	V_ALL_FAILURE	allocation failure
 *
 *	Call the system dynamic pool allocation routine (_a_pool() in pool.c)
 *	with special failure recovery handling.
 *
 *	On failure, set All_failed to the cumulative sum of failed allocation
 *	sizes and throw V_ALL_FAILURE.
 */
{
    register char **ptr;

    if ( (ptr = alloc(nbytes)) EQ NULL )
	{
	All_failed += nbytes;	/* add in allocation failure size */
	throw(V_ALL_FAILURE);
	}
    return(ptr);
}

    unsigned int
vf_pool (ptr)
    char **ptr;

/* vf_pool (ptr) -- Free a pool buffer and queue processes waiting for pool
 *
 *	in:	ptr		Bufptr of buffer to release, or NULL
 *	return:	(unsigned)	Size, in "sizeof" units, of free pool
 *	thrown:	(NONE)
 *
 *	Call the system dynamic pool deallocation routine (_f_pool() in pool.c).
 *	If the total free pool is greater than LOWPOOL and an allocation
 *	failure had occured (All_failure <> 0), re-activate all i/o packets
 *	that are currently waiting for pool (ALL_WAIT_STATE).
 */
{
    register unsigned i;

    if ( (ptr NE NULL) AND (NOT chk_pool(ptr)) )
	{

#ifdef DEBUG2   /*************************************************************/
	error("Vf_pool called with bad bufptr: %x", ptr);
#endif /* DEBUG2 *************************************************************/

	ptr = NULL;
	}

    if ( ((i=free(ptr)) GT LOWPOOL) AND All_failed )
	{
	All_failed = FALSE;	/* reset the allocation failure count */
	wq_activate (ALL_WAIT_STATE, 0);	/* activate all waiting pkts */
	} /*if*/

    return(i);		/* return free pool size */
}


dec_iocount (pkt)
    register PKT_PTR pkt;

/* dec_iocount (pkt) -- Decrement the I/O Count of an I/O request
 *
 *	mod:	pkt		Target i/o packet
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	Decrement the i/o count of an i/o packet.  If it is waiting for
 *	any i/o count change (CNT_WAIT_STATE), or if it goes to zero and
 *	it is waiting for i/o completion (VIO_WAIT_STATE or DONE_WAIT_STATE),
 *	activate it by removing it from the wait queue (Wqueue) and putting
 *	it on the active queue (Vqueue).
 */
{
    register _IOCOUNT i;

#ifdef DEBUG2   /*************************************************************/
    if (Iocount(*pkt) EQ 0)
	error("dec_iocount: packet at %x has zero count", *pkt);
#endif /* DEBUG2 *************************************************************/

    i = --(Iocount(*pkt));
    switch (State(*pkt))
	{
	case CNT_WAIT_STATE:	/* Activate on any count change */
	    State(*pkt) = VIO_RUN_STATE;	/* set it active */
	    goto Requeue;			/* and put it on run queue */

	case VIO_WAIT_STATE:
	case DONE_WAIT_STATE:
	    if (i NE 0) break;	/* I/O count not zero yet */
	    State(*pkt) *= -1;	/* Set active...either run or done */

Requeue:
	    q_virtual(uq_wait(pkt));	/* Re-queue pkt for processing */
	    break;

	} /*switch*/
}

#ifdef DEBUG_MAP   /***********************************************************/

_map_vios (map)
    char map[];

/* _map_vios (map) -- Add VIOS structures to pool bitmap
 *
 *	mod:	map		Pool bytemap array
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	Add VIOS allocated structures to the pool bytemap initialized by
 *	_map_pool() (in pool.c).
 *
 *	Does the following:
 *		1) Marks Class Handling Modules and
 *			Implementation Modules with:        "cc...."
 *		2) Marks Virtual Device Descriptors with:   "vv...."
 *		3) Marks Physical Device Descriptors with:  "pp...."
 *		4) Marks active i/o requests with:          "rr...."
 *		5) Marks requests waiting for a device with:"rr...."
 *		6) Marks other waiting i/o requests with:   "ww...."
 *		7) Calls the physical device dispatchers with the
 *		   PDMAPPOOL command, to mark any specially allocated
 *		   pool structures.
 */
{
    register int *ip;		/* pointer to one thing or another */
    register int i;

    /* map class handling modules */
    ip = (int*) Class_modules.next;
    errpri("                  ...marking Class Handling modules...\r", NULL);
    while (ip NE NULL)
	{
	errpri("%d     \r", C_class(*(CLASS_MODULE**)ip));
	_map_add (map, ip, 'c');
	ip = (int*) Next(*(CLASS_MODULE**)ip);
	}

    /* map configuration modules */
    ip = (int*) Imp_modules.next;
    errpri("                  ...marking Implementation modules...\r", NULL);
    while (ip NE NULL)
	{
	errpri("%s     \r", *(Imp_name(*(IMP_MODULE**)ip)));
	_map_add (map, ip, 'c');
	_map_add (map, Imp_name(*(IMP_MODULE**)ip), 'c');
	ip = (int*) Next(*(IMP_MODULE**)ip);
	}

    /* map device lists */
    ip = (int*) Vdevices.next;
    errpri("                       ...marking Virtual devices...\r", NULL);
    while (ip NE NULL)
	{
	errpri("%s     \r", *Devname(*(VDD_PTR)ip));
	_map_add (map, ip, 'v');
	_map_add (map, Devname(*(VDD_PTR)ip), 'v');
	ip = (int*) Next(*(VDD_PTR)ip);
	}

    ip = (int*) Pdevices.next;
    errpri("                       ...marking Physical devices...\r", NULL);
    while (ip NE NULL)
	{
	errpri("%s     \r", *Devname(*(PDD_PTR)ip));
	_map_add (map, ip, 'p');
	_map_add (map, Devname(*(PDD_PTR)ip), 'p');

	/* Call the device dispatcher to mark handler-allocated buffers */
	(*Dispatcher(*(PDD_PTR)ip)) (PDMAPPOOL, ip, map);

	ip = (int*) Next(*(PDD_PTR)ip);
	}

    /* mark i/o queues */
    ip = (int*) Vqueue.next;
    errpri("           ...marking active Virtual I/O requests...    \r", NULL);
    while (ip NE NULL)
	{
	errpri("%x  \r", *ip);
	_map_add (map,ip,'r');
	_map_add (map,Auxparam(*(PKT_PTR)ip),'r');
	ip = (int*) Next(*(PKT_PTR)ip);
	}

    ip = (int*) Pqueue.next;
    errpri("           ...marking queued Physical I/O requests...   \r", NULL);
    while (ip NE NULL)
	{
	errpri("%x  \r", *ip);
	_map_add (map,ip,'r');
	_map_add (map,Auxparam(*(PKT_PTR)ip),'r');
	ip = (int*) Next(*(PKT_PTR)ip);
	}

    ip = (int*) Wqueue.next;
    errpri("           ...marking waiting I/O requests...           \r", NULL);
    while (ip NE NULL)
	{
	errpri("%x  \r", *ip);
	_map_add (map,ip,'w');
	_map_add (map,Auxparam(*(PKT_PTR)ip),'w');
	ip = (int*) Next(*(PKT_PTR)ip);
	}
}
#endif /* DEBUG_MAP ***********************************************************/
E 1
