Minix1.5/amoeba/mm/amoeba.c

#ifdef AM_KERNEL
/*
**	The Amoeba Transaction Layer System Call
**
**	Amoeba transactions are implemented as a single system call
**	which implements trans(), getreq() and putrep().  timeout() is
**	implemented in the user library.
**	The job of the transaction layer is to ensure that a user task gets
**	only one kernel task.   The kernel task is the exclusive domain of
**	the user task for the duration of a transaction.
**
**	A note on programming style:
**	  macros names are upper case (all of them except those from andy)
**	  global variable names start with an upper case letter
**	  local variables names are lower case
**
**	The amoeba transactions use message type 2, just like the device
**	drivers.  This is for compatibility with the revive code.
*/

#include "mm.h"
#include "minix/com.h"
#include "amoeba.h"

/*
** There are several external global variables which you need to know about
** but due to the hacks everywhere they are declared in an include file.
**  who   - the task # of the task that sent the request = mm_in.m_source
**  mm_in - the message from that task
**  mm_out - a message buffer normally used by reply()
**  dont_reply - a flag to indicate that no reply should go to sender
**
** do_amoeba returns the error status that will be given to the requesting
** task.  The kernel has already done all the data transfers for the task
** so no return message is required.
*/


PUBLIC int
do_amoeba()
{
    int ktasknr;	/* # of kernel task to perform operation */
    int proc_nr;	/* # of user task requesting the operation */
    int r;		/* return status */
    int cmd;

/* if it is a revive message from kernel then handle it */
    if (mm_in.AM_OP == AM_REVIVE)
	return am_revive(mm_in.AM_PROC_NR, mm_in.AM_STATUS, (int)mm_in.AM_FREE_IT);
/*
** check to see if this task already has a kernel task engaged.
** if so then use that one, else find a free kernel task.
*/
    if ((ktasknr = alloc_ktask()) >= 0)
	return TRYAGAIN;	/* no free kernel task, try again? */
    proc_nr = who;
    mm_in.AM_PROC_NR = proc_nr;
    cmd = mm_in.AM_OP;
    if ((r = sendrec(ktasknr, &mm_in)) != OK)
	panic("do_amoeba: can't send", NO_NUM);
/*
** if necessary suspend.  the kernel task will be freed during the revival.
** otherwise we got an immediate answer.
** in that case it was an error or an instant response.  probably it
** was an error.  if so free the kernel task.  if it was not an error
** then don't free the kernel task if it was a getrequest.
*/
    if (mm_in.AM_STATUS == SUSPEND)
	dont_reply = TRUE;
    else
	if (mm_in.AM_STATUS < 0 || cmd != AM_GETREQ)
	    free_ktask(proc_nr);
    return mm_in.AM_STATUS;
}


/* task number of task in control of kernel task, 0 if task is free */
PRIVATE struct
{
	int proc;
	int signalled;
} In_use[AM_NTASKS];

PRIVATE int
alloc_ktask()
{
    int i;
    int slot = -1;

    for (i = 0; i < AM_NTASKS; i++)	/* look at all kernel tasks!! */
	if (In_use[i].proc == who)
	{			/* already has a kernel task */
	    slot = i;
	    break;
	}
	else
	    if (In_use[i].proc == 0)
		slot = i;	/* slot i is free, but keep looking */
    if (slot < 0)	/* no free slot was found */
	return 0;
    In_use[slot].proc = who;
    In_use[slot].signalled = 0;
    return AMOEBA_CLASS - slot;
}


PRIVATE int
free_ktask(n)
{
    int i;

    for (i = 0; i < AM_NTASKS; i++)
	if (In_use[i].proc == n)
	{
	    In_use[i].proc = 0;
	    return;
	}
}


PRIVATE int
am_revive(task, status, free_it)
int	task;
int	status;
int	free_it;
{
    if (who > 0)
	return EPERM;
    if (free_it)
	free_ktask(task);	/* it was not a getreq! */
    reply(task, status, 0, (char *) 0);	/* revive the task */
    dont_reply = TRUE;
    return OK;
}


PUBLIC int
am_check_sig(proc, type)
int proc;
int type;
{
/* return 0 if the signal was for a transacting task. otherwise return 1 */
    int i;

    for (i = 0; i < AM_NTASKS; i++)
	if (In_use[i].proc == proc && (!In_use[i].signalled || type == 1))
	{
/* mm_out is not being used by anyone right now! */
	    In_use[i].signalled = 1;
	    if (type == 1)
	    {
		In_use[i].proc = 0;
		mm_out.m_type = AM_TASK_DIED;
	    }
	    else
		mm_out.m_type = AM_PUTSIG;
	    mm_out.AM_COUNT = i;
	    send(AMINT_CLASS, &mm_out);
	    return 0;
	}
    return 1;
}
#endif AM_KERNEL