syscallt.c


/*************************************************************************
*                                                                        *
*   System Call Trap Handler                                             *
*                                                                        *
*   realizzato dal Gruppo 17 di Lab2 Anno Accademico 1995/96             *
*                                                                        *
*   Lorenzo     Claudio       Valerio    Riccardo    Emiliano            *
*   Coronati    Lanconelli    Paolini    Solmi       Trentini            *
*                                                                        *
**************************************************************************/

#include "../h/supconst.h"
#include "../h/tproc.h"
#include "../h/syscallt.h"

#include "../h/vqueue.e"
#include "../h/services.e"
#include "../h/supglobals.e"

/***
    formato dei messaggi ricevuti dal sys trap handler:

    A) invocazione di servizio (msg inviato dalla passup)
    m_sender: caller
    m_message: sate_t *

    B) VSend da un altro T-process
    m_sender: gestore del thread che fa la VSend
    m_message: vmsg_t *

    C) VReply da un altro T-process
    m_sender: gestore del thread che fa la VReply
    m_message: vmsg_t *

    il gestore mantiene 3 code (di tipo vqueue_t):
    - wait_recv:  coda dei thread che hanno fatto una VSend (in attesa di una VRecv)
    - wait_reply: coda dei thread che hanno fatto una VSend (in attesa di una VReply)
    - wait_send:  coda dei thread che hanno fatto una VRecv (in attesa di una VSend)

    Ora analizziamo uno ad uno tutti i casi partendo dallo scambio di
    Vmessaggi all'interno dello stesso T-process.

    1A) Il gestore riceve un messaggio di tipo (A) che richiede una VSend.
        Il gestore guarda nella coda wait_send se vi e` il thread destinatario
        della VSend; se e` presente lo estrae dalla coda, copia il messaggio,
        e lo risveglia (facendo una msgSend), infine mette il chiamante
        nella coda wait_reply;
        se invece non e` presente mette il thread chiamante nella coda
        wait_recv.

    2A) Il gestore riceve un messaggio di tipo (A) che richiede una VRecv.
        Il gestore guarda nella coda wait_recv se vi e` almeno un thread
        se e` stata richiesta una VRecvALL, altrimenti se vi e` il thread
        indicato in R4. Nel caso vi fosse lo estrae dalla coda, copia il
        messaggio, lo inserisce nella coda wait_reply e risveglia il thread
        chiamante; in caso contrario mette il thread chiamante nella coda
        wait_send.

    3A) Il gestore riceve un messaggio di tipo (A) che richiede una VReply.
        Il gestore guarda nella coda wait_reply se vi e` il thread indicato
        in R4; se e` presente lo estrae dalla coda e lo risveglia, inoltre
        risveglia anche il chiamante.

    4A) Il gestore riceve un messaggio di tipo (A) che richiede una VSend e
        si accorge che il thread destinatario e` "esterno".
        Il gestore invia un messaggio al gestore del thread destinatario con
        il seguente formato:
        v_msgtype:      SENDTAG
        v_msgfrom:      thread chiamante
        v_msgto:        thread destinarario
        v_msglength:    lunghezza del Vmessaggio
        v_msgbuffer:    vi viene copiato il Vmessaggio da mandare

    5A) Il gestore riceve un messaggio di tipo (A) che richiede una VReply.
        Il gestore guarda nella coda wait_reply se vi e` il thread indicato
        in R4; se e` presente ed e` un thread "esterno" lo estrae dalla coda
        e spedisce al suo gestore un messaggio dal seguente formato:
        v_msgtype:      REPLYTAG
        v_msgfrom:      thread chiamante
        v_msgto:        thread destinatario (della reply)
        v_msglenth:     N.U.
        v_msgbuffer:    N.U.
        infine risveglia il chiamante.

    6B) Il gestore riceve un messaggio di tipo (B) che richiede una VSend.
        Il gestore guarda nella coda wait_send se vi e` il thread destinatario
        (indicato in v_msgdsest) della VSend; se e` presente lo estrae dalla
        coda, copia il messaggio
        (dal v_msgbuffer), e lo risveglia (facendo una msgSend), infine mette
        il mittente nella coda wait_reply;
        se invece non e` presente mette il thread mittente nella coda
        wait_recv.

    7C) Il gestore riceve un messaggio di tipo (C) che richiede una VReply.
        Il gestore risveglia il thread destinatario della reply (indicato
        in v_msgto) facendogli una msgSend.
***/

void
syscallt()
{
    register thr_t *sender;                             /* R4 */
    register union {state_t *st; vmsg_t *vm} msg;       /* R3 */
    register tproc_t *tproc;
    int term_no;
    int j;

    /* inizializzazione dati del gestore */
    term_no = (int)tproc;
    tproc = tprocTable + term_no;

    /* marca tutti i buffer come liberi */
    for (j = 0; j < VBUFNO; j++)
        tproc->T_vbuffers[j].vmsg_type = FREETAG;

    /* inizializza le code */
    tproc->T_waitsend = tproc->T_waitrecv = tproc->T_waitrply = NULL;
    initVQueue(tproc);

    while(TRUE)
    {
        sender = ANYSENDER;
        DO_MSGRECV();

        /* controlla se il messaggio proviene dallo stesso T-process */
        if (INTERNAL(tproc, sender))
        {
            switch(msg.st->s_ps2.p2_sys.sys_no)
            {
            case V_MSGSEND:
                /* controllo parametri */
                if (msg.st->s_r[2] > MAXMSGLEN ||
                    !EVEN(msg.st->s_r[4]) )
                {
                    do_terminate(term_no);
                }
                else if (INTERNAL(tproc, msg.st->s_r[4]))        /* (1A) */
                {
                    msgVSend(tproc, sender, msg.st->s_r[3], msg.st);
                }
                else                            /* (4A) */
                {
                    state_t *sts = msg.st;
                    thr_t *from = sender;

                    if ( (sender = get_sct(msg.st->s_r[4])) == NULL )
                    {
                        /* significa che il destinatario non esiste!! */
                        /* manda un messaggio e termina il T-process */
                        do_terminate(term_no);
                    }
                    else
                    {
                        /* spedisce al gestore del destinatario un messaggio 
                         * con la copia del messaggio in memoria fisica */
                        msg.vm = NEXTVMSG(tproc);
                        msg.vm->vmsg_type = SENDTAG;
                        msg.vm->vmsg_from = from;
                        msg.vm->vmsg_to = sts->s_r[4];
                        msg.vm->vmsg_state = sts;
                        if (sts->s_r[2] > 0)
                            MOVBCK(sts->s_r[3], msg.vm->vmsg_buffer, sts->s_r[2]);
                    }
                    DO_MSGSEND();
                }
                break;
            case V_MSGRECV:                     /* (2A) */
                /* controllo parametri */
                if (msg.st->s_r[2] < 0 ||
                    !EVEN(msg.st->s_r[4]) )
                {
                    do_terminate(term_no);
                }
                else
                {
                    msgVRecv(tproc, sender, msg.st);
                }
                break;
            case V_MSGREPLY:
                /* controllo parametri */
                if ( !EVEN(msg.st->s_r[4]) )
                {
                    do_terminate(term_no);
                }
                else
                {
                    msgVReply(tproc, sender, msg.st);
                }
                break;
            case V_MSGRR:
                /* controllo parametri */
                if (msg.st->s_r[2] < 0 ||
                    !EVEN(msg.st->s_r[4]) )
                {
                    do_terminate(term_no);
                }
                else
                {
                    msgVR_R(tproc, sender, msg.st);
                }
                break;
            default:
                do_terminate(term_no);
                break;
            } /* switch */
        }
        else
        {
            switch(msg.vm->vmsg_type)
            {
            case SENDTAG:       /* (6B) */
                msgVSend(tproc, msg.vm->vmsg_from, msg.vm->vmsg_buffer, msg.vm->vmsg_state);
                break;
            case REPLYTAG:      /* (7C) */
                /* risveglia il destinatario della reply */
                sender = msg.vm->vmsg_to;
                DO_MSGSEND();

                /* marca il buffer come libero */
                msg.vm->vmsg_type = FREETAG;
                break;
            default:
                break;
            }
        } /* else */
    } /* while(TRUE) */
}

#if VBUFNO > 1
HIDDEN vmsg_t *
nextVmsg(tproc)
register tproc_t *tproc;
{
    register int i;

    for (i = 0; tproc->T_vbuffers[i].vmsg_type != FREETAG && i < VBUFNO; i++)
        ;
    return (i < VBUFNO) ? tproc->T_vbuffers + i : NULL;
}
#endif

/* Dato un thread, restituisce il suo system-call thread */
HIDDEN thr_t *
get_sct(thr)
register thr_t *thr;
{
    register int term_no = 0;

    while (tprocTable[term_no].T_process != thr->t_process && term_no < MAXTPROC)
        term_no++;
    return (term_no < MAXTPROC) ? tprocTable[term_no].T_syscallt : NULL;
}


HIDDEN void
msgVSend(tproc, from, address, st)
tproc_t *tproc;
thr_t *from;
void *address;
state_t *st;
{
    register int r4;
    register vqueue_t *p;
    register state_t *state = st;
    vmsg_t *vmsg;

    /* guarda nella coda wait_send se vi e` il destinatario della
     * VSend che attende il mittente o qualsiasi thread */
    if ( (p = keyVSend(tproc, from, state->s_r[4])) != NULL )
    {
        if (state->s_r[2] > 0)
            MOVBCK(address, p->vq_address,
                    MIN(state->s_r[2], p->vq_state->s_r[2]));

        /* memorizza il risultato nei registri del destinatario */
        p->vq_state->s_r[2] = state->s_r[2];
        p->vq_state->s_r[4] = from;
                    
        /* Se il destinatario aveva effettuato una VRecv (e non una
         * VRecv&Reply) mette il chiamante nella coda wait_reply.
         * I nuovi mitt. e dest. sono relativi alla reply
         */
        if (p->vq_type == VQ_RECV)
        {
            p->vq_from = state->s_r[4];
            p->vq_to = from;
            insVQueue(&tproc->T_waitrply, p);
        }
        else    /* type == VQ_RR */
        {
            freeVQueue(tproc, p);

            /* risveglia il chiamante */
            if (INTERNAL(tproc, from))
            {
                r4 = from;
                DO_MSGSEND();
            }
            else
            {
                /* spedisce al suo gestore un messaggio */
                vmsg = address;
                vmsg->vmsg_type = REPLYTAG;
                vmsg->vmsg_to = from;
                if ( (r4 = get_sct(from)) != NULL )
                    DO_MSGSEND();
                else
                {
                    /* Panico!!! probabile errore nella get_sct() */
                    HALT();
                }
            }
        }

        /* risveglia il destinatario */
        r4 = state->s_r[4];
        DO_MSGSEND();
    }
    else
    {
        /* mette il chiamante nella coda wait_recv */
        if ( (p = allocVQueue(tproc)) == NULL )
        {
            /* Panico!!! Aumentare VQUEUENO */
            HALT();
        }
        p->vq_type = VQ_SEND;
        p->vq_from = from;
        p->vq_to = state->s_r[4];
        p->vq_address = address;
        p->vq_state = state;
        insVQueue(&tproc->T_waitrecv, p);
    }
}

HIDDEN void
msgVRecv(tproc, sender, st)
tproc_t *tproc;
thr_t *sender;
state_t *st;
{
    register int r4;
    register vqueue_t *p;
    register state_t *state = st;

    /* controlla se la VSend relativa e` gia` stata fatta */
    if ( (p = keyVRecv(tproc, state->s_r[4], sender)) != NULL )
    {
        if (p->vq_state->s_r[2] > 0)
            MOVBCK(p->vq_address, state->s_r[3],
                    MIN(p->vq_state->s_r[2], state->s_r[2]));

        /* memorizza il risultato nei registri del chiamante */
        state->s_r[2] = p->vq_state->s_r[2];
        state->s_r[4] = p->vq_from;

        /* inserisce il mittente nella coda wait_reply */
        p->vq_type = VQ_RECV;
        p->vq_from = sender;
        p->vq_to = state->s_r[4];
        insVQueue(&tproc->T_waitrply, p);

        /* e risveglia il chiamante */
        r4 = sender;
        DO_MSGSEND();
    }
    else
    {
        /* mette il chiamante nella coda wait_send in attesa della
         * VSend relativa */
        if ( (p = allocVQueue(tproc)) == NULL )
        {
            /* Panico!!! Aumentare VQUEUENO */
            HALT();
        }
        p->vq_type = VQ_RECV;
        p->vq_from = state->s_r[4];
        p->vq_to = sender;
        p->vq_address = state->s_r[3];
        p->vq_state = state;
        insVQueue(&tproc->T_waitsend, p);
    }
}

HIDDEN void
msgVReply(tproc, sender, state)
tproc_t *tproc;
thr_t *sender;
state_t *state;
{
    register int r4;
    register vmsg_t *vmsg;
    register vqueue_t *p;

    /* guarda nella coda wait_reply se vi e` il thread indicato */
    if ( (p = keyVReply(tproc, sender, state->s_r[4])) != NULL )
    {
        if (INTERNAL(tproc, state->s_r[4]))      /* (3A) */
        {
            freeVQueue(tproc, p);

            r4 = state->s_r[4];
            DO_MSGSEND();
        }
        else                                    /* (5A) */
        {
            vmsg = p->vq_address;
            freeVQueue(tproc, p);
                        
            /* spedisce al suo gestore un messaggio */
            vmsg->vmsg_type = REPLYTAG;
            vmsg->vmsg_to = state->s_r[4];
            if ( (r4 = get_sct(state->s_r[4])) != NULL )
                DO_MSGSEND();
            else
            {
                /* Panico!!! probabile errore nella get_sct() */
                HALT();
            }
        }
    }
    /* else --> significa che viene fatta la VReply ad un thread
     * che non ci ha fatto la VSend: la ignoriamo */

    /* risveglia il chiamante */
    r4 = sender;
    DO_MSGSEND();
}

HIDDEN void
msgVR_R(tproc, sender, state)
tproc_t *tproc;
thr_t *sender;
state_t *state;
{
    register int r4;
    register vqueue_t *p;
    register vmsg_t *vmsg;

    /* controlla se la VSend relativa e` gia` stata fatta */
    if ( (p = keyVRecv(tproc, state->s_r[4], sender)) != NULL )
    {
        if (p->vq_state->s_r[2] > 0)
            MOVBCK(p->vq_address, state->s_r[3],
                    MIN(p->vq_state->s_r[2], state->s_r[2]));

        /* memorizza il risultato nei registri del chiamante */
        state->s_r[2] = p->vq_state->s_r[2];
        state->s_r[4] = p->vq_from;
        vmsg = p->vq_address;

        freeVQueue(tproc, p);

        /* controlla se il mittente e` esterno o interno al T-process */
        if (INTERNAL(tproc, state->s_r[4]))      /* (3A) */
        {
            /* risveglia il mittente (colui che ha fatto la VSend) */
            r4 = state->s_r[4];
            DO_MSGSEND();
        }
        else                                    /* (5A) */
        {
            /* spedisce al suo gestore un messaggio */
            vmsg->vmsg_type = REPLYTAG;
            vmsg->vmsg_to = state->s_r[4];
            if ( (r4 = get_sct(state->s_r[4])) != NULL )
                DO_MSGSEND();
            else
            {
                /* Panico!!! probabile errore nella get_sct() */
                HALT();
            }
        }

        /* e risveglia il chiamante */
        r4 = sender;
        DO_MSGSEND();
    }
    else
    {
        /* mette il chiamante nella coda wait_send in attesa della
         * VSend relativa. A differenza della VRecv, all'arrivo della
         * VSend relativa il mittente non verra` bloccato per attendere la reply
         */
        if ( (p = allocVQueue(tproc)) == NULL )
        {
            /* Panico!!! Aumentare VQUEUENO */
            HALT();
        }
        p->vq_type = VQ_RR;
        p->vq_from = state->s_r[4];
        p->vq_to = sender;
        p->vq_address = state->s_r[3];
        p->vq_state = state;
        insVQueue(&tproc->T_waitsend, p);
    }
}



[INDICE CODICE]