493 lines
12 KiB
C
493 lines
12 KiB
C
/****************************************************************************/
|
|
/* */
|
|
/* (c) Copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands */
|
|
/* */
|
|
/* This product is part of the Amoeba distributed operating system. */
|
|
/* */
|
|
/* Permission to use, sell, duplicate or disclose this software must be */
|
|
/* obtained in writing. Requests for such permissions may be sent to */
|
|
/* */
|
|
/* */
|
|
/* Dr. Andrew S. Tanenbaum */
|
|
/* Dept. of Mathematics and Computer Science */
|
|
/* Vrije Universiteit */
|
|
/* Postbus 7161 */
|
|
/* 1007 MC Amsterdam */
|
|
/* The Netherlands */
|
|
/* */
|
|
/****************************************************************************/
|
|
|
|
#define NDEBUG
|
|
#define PORTCACHE
|
|
|
|
/*
|
|
* This module does the port management. It keeps track of the local servers
|
|
* doing a ``getreq'' on a port, local clients waiting for a server on some
|
|
* port, and interlocal servers addressed by some port. This last category of
|
|
* ports may be forgotten, or may be incorrect.
|
|
*
|
|
* The following routines are defined:
|
|
* portinstall(port, where, wait);
|
|
* portlookup(port, wait, delete);
|
|
* portremove(port, location);
|
|
* portquit(port, task);
|
|
*
|
|
* ``Portinstall'' is called when a port is assumed to be at location
|
|
* ``where.'' If ``wait'' is set, the port is local.
|
|
* ``Portlookup'' is called to find a port in the cache. If ``wait'' is
|
|
* set, the routine will block until the port is found. If ``delete'' is
|
|
* set, the port must be removed when it is found.
|
|
* ``Portremove'' removes a port from the cache which is thought
|
|
* of to be at the specified location.
|
|
* When a port doesn't have to be located anymore for some task, ``portquit''
|
|
* takes care of that.
|
|
*/
|
|
|
|
#include "kernel.h"
|
|
#include "amoeba.h"
|
|
#include "global.h"
|
|
#include "task.h"
|
|
#include "internet.h"
|
|
#include "assert.h"
|
|
|
|
extern struct task task[];
|
|
|
|
#ifdef STATISTICS
|
|
#include "portstat.h"
|
|
|
|
struct portstat portstat;
|
|
#define STINC(x) portstat.x++
|
|
#else
|
|
#define STINC(x)
|
|
#endif
|
|
|
|
#include "conf.h"
|
|
|
|
#define LOGHASH 5 /* log sizeof hash table of local ports */
|
|
|
|
struct porttab {
|
|
port p_port; /* the port this entry is about */
|
|
unshort p_idle; /* idle timer */
|
|
address p_location; /* where is it? (0 = being located) */
|
|
address p_asker; /* address of someone interested */
|
|
struct porttab *p_nextport; /* port with same hash value */
|
|
struct task *p_tasklist; /* list of tasks */
|
|
};
|
|
|
|
#define NILPORTTAB ((struct porttab *) 0)
|
|
|
|
#define NHASH (1 << LOGHASH)
|
|
#define HASHMASK (NHASH - 1)
|
|
|
|
#define hash(p) (* (unshort *) (p) & HASHMASK)
|
|
|
|
/* MINIX can't allocate memory in the kernel at run-time
|
|
extern unshort nport;
|
|
PRIVATE struct porttab *porttab, *lastport, *hashtab[NHASH], *portfree;
|
|
*/
|
|
PRIVATE struct porttab porttab[NPORTS];
|
|
PRIVATE struct porttab *lastport, *hashtab[NHASH], *portfree;
|
|
|
|
#ifndef NONET
|
|
#ifndef NOCLIENT
|
|
|
|
#define NLOCATE 8 /* max. number of ports to locate */
|
|
PRIVATE port loctab[NLOCATE];
|
|
PRIVATE unshort loccnt, loctim, locthissweep;
|
|
extern unshort minloccnt, maxloccnt;
|
|
|
|
#endif
|
|
#endif
|
|
|
|
/* Allocate an entry in the hash table at location ``ht.'' */
|
|
static struct porttab *allocate(ht, p)
|
|
struct porttab **ht;
|
|
port *p;
|
|
{
|
|
register struct porttab *pt;
|
|
STINC(pts_alloc);
|
|
if ((pt=portfree) == 0) {
|
|
STINC(pts_full);
|
|
portpurge(); /* total cleanup, not likely to happen */
|
|
if ((pt=portfree) == 0)
|
|
return 0;
|
|
}
|
|
portfree = pt->p_nextport;
|
|
pt->p_nextport = *ht;
|
|
*ht = pt;
|
|
pt->p_port = *p;
|
|
return pt;
|
|
}
|
|
|
|
/* Install a port in the hash table. If ``wait'' is set, the location will
|
|
* be this machine and is certain. If not, the location is somewhere else
|
|
* and uncertain.
|
|
*/
|
|
portinstall(p, where, wait)
|
|
register port *p;
|
|
address where;
|
|
{
|
|
register struct porttab **ht, *pt;
|
|
register struct task *t;
|
|
extern address local;
|
|
|
|
ht = &hashtab[hash(p)];
|
|
for (pt = *ht; pt != NILPORTTAB; pt = pt->p_nextport)
|
|
if (PortCmp(&pt->p_port, p)) {
|
|
if (pt->p_location == SOMEWHERE) {
|
|
compare(pt->p_tasklist, !=, NILTASK);
|
|
do {
|
|
t = pt->p_tasklist;
|
|
t->pe_location = where;
|
|
STINC(pts_wakeup);
|
|
wakeup((event_t) &t->pe_location);
|
|
} while ((pt->p_tasklist = t->pe_link) != NILTASK);
|
|
}
|
|
#ifndef NOCLIENT
|
|
else if (siteaddr(pt->p_location) == local && !wait &&
|
|
pt->p_tasklist != 0)
|
|
return;
|
|
#endif
|
|
break;
|
|
}
|
|
if (pt == NILPORTTAB && (pt = allocate(ht, p)) == NILPORTTAB)
|
|
#ifndef NOCLIENT
|
|
if (wait)
|
|
#endif
|
|
panic("portcache full for servers", 0x8000);
|
|
#ifndef NOCLIENT
|
|
else /* no room left, so forget it */
|
|
return;
|
|
#endif
|
|
pt->p_location = where;
|
|
#ifndef NOCLIENT
|
|
if (wait) { /* task is going to await a client, so leave it immortal */
|
|
#endif
|
|
compare(area(where), ==, LOCAL);
|
|
t = &task[tasknum(where)];
|
|
t->pe_location = where;
|
|
t->pe_link = pt->p_tasklist;
|
|
pt->p_tasklist = t;
|
|
#ifndef NONET
|
|
if (pt->p_asker != NOWHERE) {
|
|
STINC(pts_here);
|
|
hereport(pt->p_asker, p, (unsigned )1);
|
|
pt->p_asker = NOWHERE;
|
|
}
|
|
#endif
|
|
#ifndef NOCLIENT
|
|
}
|
|
#endif
|
|
pt->p_idle = 0;
|
|
}
|
|
|
|
#ifndef NONET
|
|
#ifndef NOCLIENT
|
|
/* Broadcast a locate message
|
|
*/
|
|
static sendloc(){
|
|
register struct porttab *pt;
|
|
register unsigned n = 0;
|
|
|
|
if (locthissweep) {
|
|
/* During this clocktick we already sent out a broadcast packet.
|
|
* To prevent buggy userprograms from creating a broadcast storm
|
|
* we do not send another one, we just prepare for it to be done
|
|
*/
|
|
STINC(pts_nolocate);
|
|
loccnt = maxloccnt;
|
|
return;
|
|
}
|
|
for (pt = porttab; pt < lastport; pt++)
|
|
if (pt->p_location == SOMEWHERE) {
|
|
loctab[n++] = pt->p_port;
|
|
if (n == NLOCATE)
|
|
break;
|
|
}
|
|
if (n) {
|
|
locthissweep = 1;
|
|
whereport(loctab, n); /* send out the broadcast locate */
|
|
} else
|
|
loctim = 0; /* No locates necessary anymore */
|
|
loccnt = 0;
|
|
}
|
|
|
|
#endif NOCLIENT
|
|
#endif NONET
|
|
|
|
/* Look whether port p is in the portcache. You can specify whether you
|
|
* want to wait for the information and whether you want to delete it.
|
|
*/
|
|
address portlookup(p, wait, del)
|
|
register port *p;
|
|
{
|
|
register struct porttab **ht, *pt;
|
|
register struct task *c, *t;
|
|
register address location;
|
|
|
|
STINC(pts_lookup);
|
|
ht = &hashtab[hash(p)];
|
|
for (pt = *ht; pt != NILPORTTAB; pt = pt->p_nextport)
|
|
if (PortCmp(&pt->p_port, p)) { /* found it */
|
|
location = pt->p_location;
|
|
switch (area(location)) {
|
|
case LOCAL: /* local server */
|
|
if (pt->p_tasklist == 0)
|
|
break;
|
|
if (del) { /* remove it */
|
|
pt->p_tasklist = pt->p_tasklist->pe_link;
|
|
if ((t = pt->p_tasklist) != NILTASK)
|
|
pt->p_location = t->pe_location;
|
|
}
|
|
pt->p_idle = 0;
|
|
STINC(pts_flocal);
|
|
return(location);
|
|
|
|
case GLOBAL: /* remote server */
|
|
compare(pt->p_tasklist, ==, NILTASK);
|
|
pt->p_idle = 0;
|
|
STINC(pts_fglobal);
|
|
return(location);
|
|
|
|
case DONTKNOW: /* somebody else wants to know too */
|
|
compare(pt->p_tasklist, !=, NILTASK);
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
break;
|
|
}
|
|
/* The port wasn't in the port cache */
|
|
#ifndef NOCLIENT
|
|
if (wait) { /* wait for it */
|
|
if (pt == NILPORTTAB && (pt = allocate(ht, p)) == NILPORTTAB)
|
|
panic("portcache full for clients", 0x8000);
|
|
pt->p_location = SOMEWHERE;
|
|
c = curtask;
|
|
c->pe_link = pt->p_tasklist;
|
|
pt->p_tasklist = c;
|
|
#ifndef NONET
|
|
STINC(pts_locate);
|
|
sendloc();
|
|
loctim = minloccnt;
|
|
#endif
|
|
c->pe_location = SOMEWHERE;
|
|
if (sleep((event_t) &c->pe_location))
|
|
assert(pt->p_tasklist != c);
|
|
pt->p_idle = 0;
|
|
return(c->pe_location);
|
|
}
|
|
else
|
|
#endif NOCLIENT
|
|
return(NOWHERE);
|
|
}
|
|
|
|
/* Port p isn't at location ``location'' anymore */
|
|
portremove(p, location)
|
|
port *p;
|
|
address location;
|
|
{
|
|
register struct porttab *pt, **ht;
|
|
register struct task *t;
|
|
|
|
for (ht = &hashtab[hash(p)]; (pt= *ht) != NILPORTTAB; ht = &pt->p_nextport)
|
|
if (PortCmp(&pt->p_port, p)) {
|
|
if (pt->p_location == location) {
|
|
if ((t = pt->p_tasklist) != NILTASK) {
|
|
compare(area(location), ==, LOCAL);
|
|
compare(t->pe_location, ==, location);
|
|
if ((pt->p_tasklist = t->pe_link) != NILTASK) {
|
|
pt->p_location =
|
|
pt->p_tasklist->pe_location;
|
|
break;
|
|
}
|
|
} else {
|
|
*ht = pt->p_nextport;
|
|
pt->p_location = NOWHERE; /* for dump */
|
|
pt->p_nextport = portfree;
|
|
portfree = pt;
|
|
}
|
|
}
|
|
else if ((t = pt->p_tasklist) != NILTASK)
|
|
while (t->pe_link != NILTASK)
|
|
if (t->pe_link->pe_location == location) {
|
|
t->pe_link = t->pe_link->pe_link;
|
|
break;
|
|
}
|
|
else
|
|
t = t->pe_link;
|
|
return;
|
|
}
|
|
}
|
|
|
|
#ifndef NOCLIENT
|
|
/* Task ``s'' isn't interested anymore */
|
|
portquit(p, s)
|
|
register port *p;
|
|
register struct task *s;
|
|
{
|
|
register struct porttab *pt, **ht;
|
|
register struct task *t;
|
|
|
|
for (ht = &hashtab[hash(p)]; (pt= *ht) != NILPORTTAB; ht = &pt->p_nextport)
|
|
if (PortCmp(&pt->p_port, p)) {
|
|
if (pt->p_location != SOMEWHERE)
|
|
return;
|
|
if ((t = pt->p_tasklist) == s) {
|
|
if ((pt->p_tasklist = s->pe_link) == NILTASK) {
|
|
*ht = pt->p_nextport;
|
|
pt->p_location = NOWHERE; /* for dump */
|
|
pt->p_nextport = portfree;
|
|
portfree = pt;
|
|
}
|
|
s->pe_location = NOWHERE;
|
|
wakeup((event_t) &s->pe_location);
|
|
}
|
|
else {
|
|
while (t != NILTASK)
|
|
if (t->pe_link == s) {
|
|
t->pe_link = s->pe_link;
|
|
s->pe_location = NOWHERE;
|
|
wakeup((event_t) &s->pe_location);
|
|
break;
|
|
}
|
|
else
|
|
t = t->pe_link;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
#endif NOCLIENT
|
|
|
|
#ifndef NONET
|
|
|
|
#define NHERE 8
|
|
PRIVATE port heretab[NHERE];
|
|
|
|
portask(asker, ptab, nports) /* handle locate request */
|
|
address asker;
|
|
register port *ptab;
|
|
unsigned nports;
|
|
{
|
|
register port *p,*q;
|
|
register struct porttab **ht, *pt;
|
|
register unsigned nfound;
|
|
|
|
STINC(pts_portask);
|
|
nfound = 0; q = heretab;
|
|
for(p=ptab; nports--; p++) {
|
|
ht = &hashtab[hash(p)];
|
|
for (pt = *ht; pt != NILPORTTAB; pt = pt->p_nextport)
|
|
if (PortCmp(&pt->p_port, p)) { /* found it */
|
|
if (area(pt->p_location) == LOCAL) {
|
|
if (pt->p_tasklist == 0) {
|
|
/* just record someone was interested */
|
|
pt->p_asker = asker;
|
|
break;
|
|
}
|
|
if (nfound < NHERE) {
|
|
*q++ = *p;
|
|
nfound++;
|
|
}
|
|
pt->p_idle = 0;
|
|
}
|
|
}
|
|
}
|
|
if (nfound) {
|
|
STINC(pts_portyes);
|
|
hereport(asker, heretab, nfound);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef NDEBUG
|
|
portdump(){
|
|
register struct porttab *pt;
|
|
register struct task *t;
|
|
|
|
printf("\n PORT LOCATION IDLE TASKS\n");
|
|
for (pt = porttab; pt < lastport; pt++)
|
|
if (pt->p_location != NOWHERE) {
|
|
prport(&pt->p_port);
|
|
printf(" %4x %4d", pt->p_location, pt->p_idle);
|
|
for (t = pt->p_tasklist; t != NILTASK; t = t->pe_link)
|
|
printf(" {%d, %x}", t - task, t->pe_location);
|
|
printf("\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Initialize tables and free list */
|
|
portinit(){
|
|
register struct porttab *pt;
|
|
|
|
/* MINIX can't allocate data in the kernel at run-time
|
|
extern char *aalloc();
|
|
|
|
porttab = (struct porttab *) aalloc(nport * sizeof(struct porttab), 0);
|
|
lastport = &porttab[nport];
|
|
*/
|
|
lastport = &porttab[NPORTS];
|
|
for (pt = porttab; pt < lastport; pt++) {
|
|
pt->p_nextport = portfree;
|
|
portfree = pt;
|
|
}
|
|
}
|
|
|
|
/* called when freelist was empty, will throw away all mortal ports */
|
|
portpurge() {
|
|
register struct porttab *pt,**ht,**htp;
|
|
|
|
for (htp=hashtab; htp< &hashtab[NHASH]; htp++) {
|
|
ht = htp;
|
|
while ((pt = *ht) != 0) {
|
|
if (pt->p_tasklist == 0){
|
|
*ht = pt->p_nextport;
|
|
pt->p_location = NOWHERE; /* for dump */
|
|
pt->p_nextport = portfree;
|
|
portfree = pt;
|
|
} else
|
|
ht = &pt->p_nextport;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define MAXSWEEP 3000 /* dseconds maximum idle time for port */
|
|
#define SWEEPRESOLUTION 100 /* accuracy */
|
|
|
|
portsweep() {
|
|
register struct porttab *pt,**ht,**htp;
|
|
static unshort cnt;
|
|
|
|
#ifndef NOCLIENT
|
|
#ifndef NONET
|
|
locthissweep = 0;
|
|
if (loctim && ++loccnt > loctim) { /* send a locate message */
|
|
STINC(pts_relocate);
|
|
sendloc();
|
|
loctim <<= 1;
|
|
if (loctim > maxloccnt)
|
|
loctim = maxloccnt;
|
|
locthissweep = 0;
|
|
}
|
|
#endif NONET
|
|
#endif
|
|
if (++cnt<SWEEPRESOLUTION)
|
|
return;
|
|
for (htp=hashtab; htp< &hashtab[NHASH]; htp++) {
|
|
ht = htp;
|
|
while ((pt = *ht) != 0) {
|
|
if (pt->p_tasklist == 0 && (pt->p_idle += cnt) > MAXSWEEP) {
|
|
*ht = pt->p_nextport;
|
|
pt->p_location = NOWHERE; /* for dump */
|
|
pt->p_nextport = portfree;
|
|
portfree = pt;
|
|
STINC(pts_aged);
|
|
} else
|
|
ht = &pt->p_nextport;
|
|
}
|
|
}
|
|
cnt=0;
|
|
}
|