/*
 *	Network Queueing System (NQS)
 *  This version of NQS is Copyright (C) 1992  John Roman
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 1, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*
*  PROJECT:     Network Queueing System
*  AUTHOR:      John Roman
*
*  Modification history:
*
*       Version Who     When            Description
*       -------+-------+---------------+-------------------------
*       V01.10  JRR                     Initial version.
*       V01.2   JRR     12-Feb-1992	Fixed static declaration of routines.
*       V01.3   JRR     24-Apr-1992     Added debugging messages.
*	V01.4	JRR	17-Jun-1992	Added header.
*	V01.5	JRR	20-Nov-1992	Modified for C prototypes.
*	V01.6	JRR	24-Aug-1993	Include more debugging information.
*	V01.7	JRR	01-Mar-1994	Added support for SOLARIS.
*/
/*++ nqs_psc.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.35.6/src/RCS/nqs_psc.c,v $
 *
 * DESCRIPTION:
 *
 *	This module contains the 3 functions:
 *
 *		psc_reqcom()
 *		psc_sched()
 *		psc_spawn()
 *
 *	which control the scheduling, and spawning of NQS pipe requests.
 *	This module can be modified to implement appropriate scheduling
 *	algorithms for a particular installation as necessary.
 *
 *
 *	Author:
 *	-------
 *	Brent A. Kingsbury, Sterling Software Incorporated.
 *	August 12, 1985.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.7 $ $Date: 1994/03/30 20:36:46 $ $State: Exp $)
 * $Log: nqs_psc.c,v $
 * Revision 1.7  1994/03/30  20:36:46  jrroma
 * Version 3.35.6
 *
 * Revision 1.6  93/09/10  13:57:18  jrroma
 * Version 3.35
 * 
 * Revision 1.5  92/12/22  15:41:26  jrroma
 * Version 3.30
 * 
 * Revision 1.4  92/06/18  17:31:14  jrroma
 * Added gnu header
 * 
 * Revision 1.3  92/05/06  10:42:10  jrroma
 *  Version 3.20
 * 
 * Revision 1.2  92/02/12  13:46:50  jrroma
 * Fixed static declaration of routines.
 * 
 * Revision 1.1  92/02/12  13:46:00  jrroma
 * Initial revision
 * 
 *
 */

#include "nqs.h"			/* NQS constants and data types */
#include "nqsxvars.h"			/* NQS global variables */


#ifndef __CEXTRACT__
#if __STDC__

static int enabled_dest ( struct nqsqueue *queue );

#else /* __STDC__ */

static int enabled_dest (/* struct nqsqueue *queue */);

#endif /* __STDC__ */
#endif /* __CEXTRACT__ */

/*** psc_reqcom
 *
 *
 *	void psc_reqcom():
 *
 *	This function is invoked whenever a running pipe request completes
 *	execution.  This function is free to spawn more pipe requests from
 *	ANY pipe queue.
 *
 *	All queues modified by the invocation of this procedure have
 *	had their corresponding NQS database queue state image updated
 *	(to match the internal queue states) upon return from this
 *	procedure.
 */
void psc_reqcom (struct request *request)
{

	/*
	 *  For the moment, we do not use any of the knowledge imparted
	 *  to us by the request that just finished execution,  Instead,
	 *  we spawn as many pipe requests as we can, within the limits
	 *  configured.
	 */
	psc_spawn();			/* Spawn all pipe requests */
}					/* within configured boundaries */


/*** psc_sched
 *
 *
 *	int psc_sched():
 *
 *	This function is invoked whenever a pipe req must be evaluated
 *	and assigned a priority by the NQS pipe req scheduling policies
 *	(which are implemented by this function).
 *
 *	The priority assigned to the req must be in the interval [0..32767].
 *	All pipe reqs with priority >= N within a given pipe queue, will,
 *	depending upon the precise scheduling criteria defined in:
 *
 *		psc_spawn()  and
 *		psc_reqcom()
 *
 *	be spawned before any pipe reqs in the same queue with a priority
 *	value < N.
 *	
 *	Returns:
 *		The assigned priority value for the specified pipe request.
 */
int psc_sched (struct rawreq *rawreq)
{
	if (rawreq->rpriority == -1) {
	    /*
	     *  The user did not assign a priority to the req; assign
	     *  a default priority value.
	     */
	    return (MAX_RPRIORITY / 2);
	}
	return (rawreq->rpriority);	/* For now, just the intra-queue */
}


/*** psc_spawn
 *
 *
 *	void psc_spawn():
 *
 *	This function is invoked whenever request activity indicates that
 *	it MAY be possible to spawn a pipe request.  It is up to the
 *	discretion of the pipe req scheduling/spawning algorithm
 *	implemented here to determine whether or not pipe req(s) should
 *	be spawned.
 *
 *	All queues modified by the invocation of this procedure have
 *	had their corresponding NQS database queue state image updated
 *	(to match the internal queue states) upon return from this
 *	procedure.
 */
void psc_spawn()
{
	register struct nqsqueue *queue;	/* Pipe queue set walking */
	register int prevpipcount;	/* Prev loop values of Gblpipcount */

	/*
	 *  Note that we are very careful to make sure that all pipe
	 *  queues with higher priorities that also have pipe requests
	 *  that can run, get to spawn first.  This becomes critical when
	 *  the number of pipe requests that can run exceeds the maximum
	 *  number of pipe requests that are allowed to simultaneously
	 *  execute.
	 */
	if (Shutdown) return;		/* Do not spawn any requests if */
					/* NQS is shutting down */
	queue = Pripipqueset;		/* Prioritized pipe queue set */
	prevpipcount = Gblpipcount - 1;	/* Make loop go at least once */
	for ( ; queue != (struct nqsqueue *) 0 &&
	       Maxgblpiplimit > Gblpipcount && prevpipcount != Gblpipcount;
	       queue = queue->v1.pipe.nextpriority) {
	    if (Debug > 2) {
		printf("D$psc_spawn: starting loop for queue %s.\n",
		       queue->q.namev.name);
		fflush(stdout);
	    }
	    /*
	     *  Spawn as many requests as are allowed for this
	     *  queue.
	     */
	    if (Debug > 4) {
		printf ("D$psc_spawn: runlimit %d runcount %d\n", 
			queue->q.v1.pipe.runlimit, queue->q.runcount);
	        if (queue->q.status & QUE_RUNNING) 
				printf ("D$psc_spawn: queue running\n");
	        if (queue->queuedset == (struct request *) 0) 
			printf ("D$psc_spawn: No requests queued\n");
	        if ( enabled_dest (queue)) 
				printf ("D$psc_spawn: Are enabled dests\n");
	        printf ("D$psc_spawn: Maxgblpiplimit %d Gblpipcount %d\n", 
			Maxgblpiplimit,  Gblpipcount);
	        printf ("D$psc_spawn: prevpipcount %d Gblpipcount %d\n", 
			prevpipcount, Gblpipcount);
	    }
            if (!(queue->q.status & QUE_RUNNING)) continue;
	    if (queue->q.v1.pipe.runlimit <=  queue->q.runcount) {
                if (Debug) {
                    printf("I$psc_spawn: Rqst not scheduled due to queue runlimit.\n");
                    fflush(stdout);
                }
                continue;
	    }
	    if (queue->queuedset == (struct request *) 0) {
                if (Debug) {
                    printf("I$psc_spawn: Rqst not scheduled due to none there.\n");
                    fflush(stdout);
                }
                continue;
	    }
	    if (!enabled_dest (queue)) {
                if (Debug) {
                    printf("I$psc_spawn: Rqst not scheduled due to no enabled dest.\n");
                    fflush(stdout);
                }
                continue;
	    }
	    if (Maxgblpiplimit <= Gblpipcount) {
                if (Debug) {
                    printf("I$psc_spawn: Rqst not scheduled due to Maxgblpiplimit.\n");
                    fflush(stdout);
                }
                continue;
	    }
	    if (prevpipcount == Gblpipcount) {
                if (Debug) {
                    printf("I$psc_spawn: Rqst not scheduled due to Gblpipcount.\n");
                    fflush(stdout);
                }
                continue;
	    }
	    /*
	     *  There is a pipe request that can be spawned.
	     *  Try to spawn it.
	     *
	     *  Note that the spawning of a pipe request
	     *  requires the creation of a subrequest.
	     *  Furthermore, a new network queue may have
	     *  to be created.  It is therefore possible
	     *  that the successful spawning of a pipe
	     *  request would require more memory, than
	     *  there is available.  In such a situation,
	     *  the spawn quietly fails.  By watching
	     *  Gblpipcount, this procedure can tell when
	     *  such an event has happened.
	     */
	    prevpipcount = Gblpipcount;
	    nqs_spawn (queue->queuedset, (struct device *) 0);
	    if (queue->q.status & QUE_UPDATE) {
		/*
		 *  The database image of this queue needs to
		 *  be updated.
		 */
		udb_qorder (queue);	/* Update database image */
	    }				/* and clear QUE_UPDATE */
	}
}


/*** enabled_dest
 *
 *
 *	int enabled_dest():
 *
 *	Return boolean TRUE (non-zero), if there exists an enabled
 *	destination in the destination set for the specified pipe
 *	queue.
 */
static int enabled_dest (struct nqsqueue *queue)
{
	register struct qdestmap *qdestmap;	/* Pipe destination walking */

	qdestmap = queue->v1.pipe.destset;
	while (qdestmap != (struct qdestmap *) 0) {
		if (qdestmap->pipeto->status & DEST_ENABLED) {
			/*
			 *  There exists an enabled destination for
			 *  this pipe queue.
			 */
			return (1);
		}
		qdestmap = qdestmap->next;	/* Examine next destination */
	}
	return (0);				/* No enabled destinations */
}
