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