/*
 *	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     02-Mar-1992	Added Cosmic V2 changes.
*       V01.3   JRR     08-Apr-1992     Added CERN enhancements.
*               JRR     16-Apr-1992     Added complex user run limits.
*	V01.4	JRR	17-Jun-1992	Added header.
*	V01.5	JRR	28-Aug-1992	Verqueusr must check complexes,  too.
*	V01.6	JRR	07-May-1993	Verqueusr not count queued req.
*	V01.7	JRR	18-Aug-1993	Miniscule clean-up.
*	V01.8	JRR	20-Oct-1993	Fix so Info messages only printed if
*					debug is on.
*	V01.9	JRR	01-Mar-1994	Added support for SOLARIS.
*/
/*++ nqs_bsc.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.35.6/src/RCS/nqs_bsc.c,v $
 *
 * DESCRIPTION:
 *
 *	This module contains the 3 functions:
 *
 *		bsc_reqcom()
 *		bsc_sched()
 *		bsc_spawn()
 *
 *	which control the scheduling, and spawning of NQS batch 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.9 $ $Date: 1994/03/30 20:36:31 $ $State: Exp $)
 * $Log: nqs_bsc.c,v $
 * Revision 1.9  1994/03/30  20:36:31  jrroma
 * Version 3.35.6
 *
 * Revision 1.8  94/02/24  21:30:51  jrroma
 * Version 3.35.3
 * 
 * Revision 1.7  93/09/10  13:57:12  jrroma
 * Version 3.35
 * 
 * Revision 1.6  93/07/13  21:33:47  jrroma
 * Version 3.34
 * 
 * Revision 1.5  92/12/22  15:39:48  jrroma
 * Version 3.30
 * 
 * Revision 1.4  92/06/18  17:30:55  jrroma
 * Added gnu header
 * 
 * Revision 1.3  92/05/06  10:39:18  jrroma
 *  Version 3.20
 * 
 * Revision 1.2  92/03/02  13:28:45  jrroma
 * Added Cosmic V2 changes.
 * 
 * Revision 1.1  92/03/02  13:24:10  jrroma
 * Initial revision
 * 
 *
 */

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

#ifndef __CEXTRACT__
#if __STDC__

static int check_complex ( struct nqsqueue *queue, struct request *walkreq );
static int complex_userlimit ( uid_t uid, struct qcomplex *qcomplex );
static int userlimit ( uid_t uid, struct nqsqueue *queue );
static int userrun ( uid_t uid, struct nqsqueue *queue );
static int ver_check_complex ( struct nqsqueue *queue, uid_t uid );

#else /* __STDC__ */

static int check_complex (/* struct nqsqueue *queue, struct request *walkreq */);
static int complex_userlimit (/* uid_t uid, struct qcomplex *qcomplex */);
static int userlimit (/* uid_t uid, struct nqsqueue *queue */);
static int userrun (/* uid_t uid, struct nqsqueue *queue */);
static int ver_check_complex (/* struct nqsqueue *queue, uid_t uid */);

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


/*** bsc_reqcom
 *
 *
 *	void bsc_reqcom():
 *
 *	This function is invoked whenever a running batch request completes
 *	execution.  This function is free to spawn more batch requests from
 *	ANY batch 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 bsc_reqcom (request)
struct request *request;		/* The completed batch 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 batch requests as we can, within the limits
	 *  configured.
	 */
	bsc_spawn();			/* Spawn all batch requests */
        /*
         *      Intergraph addition:
         *      should we spawn a device request
         */
        if (request->queue->v1.batch.qcomplex[0] != (struct qcomplex *) 0)
                dsc_spawn();
}					/* within configured boundaries */


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


/*** bsc_spawn
 *
 *
 *	void bsc_spawn():
 *
 *	This function is invoked whenever request activity indicates that
 *	it MAY be possible to spawn a batch request.  It is up to the
 *	discretion of the batch request scheduling/spawning algorithm
 *	implemented here to determine whether or not batch 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 bsc_spawn()
{
	register struct nqsqueue *queue;	/* Batch queue set walking */
        register struct qcomplex *qcomplex;     /* Queue complex walking */
	register int prevbatcount;	/* Prev loop value of Gblbatcount */
	struct	 request *preq;		/* pointer to a request */
        register int full;              /* Some qcomplex is full flag */
        register int i;

	/*
	 *  Note that we are very careful to make sure that all batch
	 *  queues with higher priorities that also have batch requests
	 *  that can run, get to spawn first.  This becomes critical when
	 *  the number of batch requests that can run exceeds the maximum
	 *  number of batch requests that are allowed to simultaneously
	 *  execute.
	 */
	if (Shutdown) return;		/* Do not spawn any requests if */
					/* NQS is shutting down */
	queue = Pribatqueset;		/* Prioritized batch queue set */
	prevbatcount = Gblbatcount - 1;	/* Make loop go at least once */
	for ( ; queue != (struct nqsqueue *) 0 &&
	    Maxgblbatlimit > Gblbatcount && prevbatcount != Gblbatcount;
	    queue = queue->v1.batch.nextpriority) {
	    /*
	     *  Spawn as many batch requests as we can for this queue.
	     */
	    if (Debug > 2) {
		printf("D$bsc_spawn: Starting loop for queue %s.\n",
		   queue->q.namev.name);
		fflush(stdout);
	    }
	    if (!(queue->q.status & QUE_RUNNING)) continue;
	    for (preq = queue->queuedset; preq != (struct request *) 0;
			preq = preq->next) {
		if (queue->q.v1.batch.runlimit <= queue->q.runcount ) {
		    if (Debug) {
			printf("I$bsc_spawn: Rqst not scheduled due to queue runlimit.\n");
			fflush(stdout);
		    }
		    continue;
		}
		if ( Maxgblbatlimit <= Gblbatcount) {
		    if (Debug) {
			printf("I$bsc_spawn: Rqst not scheduled due to Gblbatcount.\n");
			fflush(stdout);
		    }
                    continue; 
                }
		if (check_complex (queue, preq)) continue;        /* and not break */
            	if (queue->q.v1.batch.userlimit <= 
		            userlimit (preq->v1.req.uid, queue)) {
		    if (Debug) {
			printf("I$bsc_spawn: Rqst not scheduled due to batch userlimits.\n");
			fflush(stdout);
		    }
		    continue;
		}
		/*
		 *  There is a batch request that can be spawned.
		 *  Try to spawn it.
		 *
		 *  Note that when spawning a batch request,
		 *  subrequests must be created and queued in
		 *  the appropriate network queues--which
		 *  themselves may have to be created.  It is
		 *  therefore possible that the successful
		 *  spawning of a batch request would require
		 *  more memory, than there is available.  In
		 *  such a situation, the spawn quietly fails.
		 *  By watching Gblbatcount, this procedure
		 *  can tell when such an event has occurred....
		 */
		prevbatcount = Gblbatcount;
		nqs_spawn (preq, (struct device *) 0);
	    }
	    if (queue->q.status & QUE_UPDATE) {
		/*
		 *  The database image of this queue must be
		 *  updated.
		 */
		udb_qorder (queue);	/* Update image and clear */
	    }				/* the QUE_UPDATE bit */
	}
}
static
int userlimit(uid, queue)
uid_t uid;
struct nqsqueue *queue;
{
        struct nqsqueue *walkqueue;
        struct request *walkreq;
        int number_running = 0;

        walkqueue = Pribatqueset;
        while (walkqueue != (struct nqsqueue *) 0) {
            if (queue == (struct nqsqueue *) 0 || strcmp (queue->q.namev.name,
			walkqueue->q.namev.name) == 0) {
                for (walkreq = walkqueue->runset;
                        walkreq != (struct request *) 0;
                        walkreq = walkreq->next)  {
                   if (walkreq->v1.req.uid == uid) number_running++; 
                }
            }
            walkqueue = walkqueue->v1.batch.nextpriority;
        }
        return(number_running);
}


/*** bsc_verqueusr
 *
 *
 *	long bsc_verqueusr():
 *	Verify user-limit for a queue.
 *
 *	This function is invoked whenever queue activity indicates that
 *	it MAY be possible to spawn a batch request. It is the main part
 *	of the load balancing mechanism. Note that the queue may be
 *	specified by a pointer to its structure or by name (requests
 *	coming through the network).
 *
 *	Returns:
 *		TCML_SUBMITTED if successful. Otherwise TCML_QUEBUSY.
 */
long bsc_verqueusr (uid, queue, que_name)
uid_t uid;					/* User id */
struct nqsqueue *queue;				/* Queue in question */
char *que_name;					/* Queue name in question */
{
	struct nqsqueue *walkque;			/* Batch queue set walking */
	int count;

	/*
	 *  Find the queue structure if needed.
	 */
	if ((walkque = queue) == (struct nqsqueue *) 0)
		walkque = nqs_fndnnq (que_name);
	/*
	 *  Is there a free initiator ?
	 */
	if (Maxgblbatlimit <= Gblbatcount) {
	    if (Debug > 2) {
		printf ("I$bsc_verqueuser: Rqst not accepted due to global batch count\n");
		fflush (stdout);
	    }
	    return (TCML_QUEBUSY);
	}
	
	if ( walkque->q.v1.batch.runlimit <= (walkque->q.runcount +
	/*
	 *  All the arriving requests have to be counted as
	 *  running because with the load-balancing mechanism, on the local
	 *  batch queues, all of them should be running...
	 */
		walkque->q.arrivecount 
	/*
	 * Don't count the queued requests.  They are not running and
	 * may have a good reason for not running, like the other user
	 * has hit her run limit...
	 */
	    /* + walkque->q.queuedcount	*/
					) ) {
	    if (Debug > 2) {
		printf ("I$bsc_verqueuser: Rqst not accepted due to queue runlimit\n");
		fflush (stdout);
	    }
	    return (TCML_QUEBUSY);
	}
	count = userrun (uid,  walkque);
        if (walkque->q.v1.batch.userlimit <= count ) {
	    if (Debug > 2) {
		printf ("I$bsc_verqueuser: Rqst not accepted due to queue user limit\n");
		fflush (stdout);
	    }
	    return (TCML_QUEBUSY);
	}
	if ( !ver_check_complex(walkque, uid) ) {
	    if (Debug > 2) {
		printf ("I$bsc_verqueuser: Rqst not accepted due to complex limit\n");
		fflush (stdout);
	    }
	    return (TCML_QUEBUSY);
	}
	return (TCML_SUBMITTED);
}

/*** userrun
 *
 *      int userrun():
 *      Return the global or queue run count of the specified user.
 *
 */
static
int userrun (uid, queue)
uid_t uid;
struct nqsqueue *queue;
{
	register struct request *walkreq;	/* Queue request set walking */
	register int run_count;

	run_count = 0;
	for (walkreq = queue->runset; walkreq != (struct request *) 0;
	     walkreq = walkreq->next) {
		if (walkreq->v1.req.uid == uid)
			run_count++;		/* A match ! */
	}
	/*
	 * And also check the arriving requests as well.
	 */
	for (walkreq = queue->arriveset; walkreq != (struct request *) 0;
	     walkreq = walkreq->next) {
		if (walkreq->v1.req.uid == uid)
			run_count++;		/* A match ! */
	}
	return (run_count);
}
/*
 *   check_complex()
 *
 *   Check complex limits to see if this request can run.
 *
 *   Return TRUE if cannot, FALSE otherwise.
 *
 */
static
int check_complex(queue, walkreq)
struct nqsqueue *queue;
struct request *walkreq;
{
    int i;
    struct qcomplex *qcomplex;

    for (i = MAX_COMPLXSPERQ; --i >= 0;) {
        qcomplex = queue->v1.batch.qcomplex[i];
        if (qcomplex == (struct qcomplex *)0) continue;
        if (qcomplex->runlimit <= qcomplex->runcount) {
	    if (Debug) {
		printf("I$bsc_spawn: Rqst not scheduled due to complex run limits.\n");
		fflush(stdout);
	    }
	    return (1);
	}
	if (qcomplex->userlimit <= complex_userlimit(walkreq->v1.req.uid, qcomplex)){
	    if (Debug) {
		printf("I$bsc_spawn: Rqst not scheduled due to complex user limits.\n");
		fflush(stdout);
	    }
	    return (1);
	}
    }
    return (0);
}
/*
 *   ver_check_complex()
 *
 *   Check complex limits to see if this request can run.  This version
 *   is especially for bsc_verqueusr,  above.
 *
 *   Return TRUE if can, FALSE otherwise.
 *
 */
static
int ver_check_complex(queue, uid)
struct nqsqueue *queue;
uid_t uid;
{
    int i;
    struct qcomplex *qcomplex;

    for (i = MAX_COMPLXSPERQ; --i >= 0;) {
        qcomplex = queue->v1.batch.qcomplex[i];
        if (qcomplex == (struct qcomplex *)0) continue;
        if (qcomplex->runlimit <= qcomplex->runcount) {
	    return (0);
	}
	if (qcomplex->userlimit <= complex_userlimit(uid, qcomplex)){
	    return (0);
	}
    }
    return (1);
}
/*
 * complex_userlimit()
 *
 * Count the number of requests running by this user in this queue complex.
 * Return the number of running requests in this complex.
 *
 */
static
int complex_userlimit(uid, qcomplex)
uid_t uid;
struct qcomplex *qcomplex;
{
    int i;
    int run_count;

    run_count = 0;
    for ( i = 0; i < MAX_QSPERCOMPLX; i++) {
	if (qcomplex->queue[i] != (struct nqsqueue *) 0)
	    run_count += userlimit(uid, qcomplex->queue[i]);
    }
    return (run_count);
}
