V8/usr/sys/chncp/chclock.c
#include "../chunix/chsys.h"
#include "../chunix/chconf.h"
#include "../chaos/chaos.h"
/*
* Clock level processing.
* ch_clock should be called each clock tick (HZ per second)
* at a priority equal to or higher that LOCK.
*
* Terminology:
* Packet aging: Retransmitting packets that are not acked within
* AGERATE ticks
*
* Probe: The sending of a SNS packet if not all of the packets
* we have sent have been acknowledged
*
* Responding: Send a SNS every so often to see if the guy is still
* alive (after NRESPONDS we declare him dead)
*
* RFC aging: The retransmission of RFC packets
*
* Route aging: Increase the cost of transmitting over gateways so we
* will use another gateway if the current gateway goes
* down.
* Route broadcast: If we are connected to more than one subnet, broad
* cast our bridge status every BRIDGERATE seconds.
*
* Interface hung: Checking periodically for dead interfaces (or dead
* "other end"'s of point-to-point links).
*
* These rates might want to vary with the cost of getting to the host.
* They also might want to reside in the chconf.h file if they are not a real
* network standard.
*
* Since these rates are dependent on a run-time variable
* (This is a good idea if you think about it long enough),
* We might want to initialize specific variables at run-time to
* avoid recalculation if the profile of chclock is disturbing.
*/
#define MINRATE ORATE /* Minimum of following rates */
#define HANGRATE (Chhz>>1) /* How often to check for hung
interfaces */
#define AGERATE (Chhz) /* Re-xmit pkt if not rcptd in time */
#define PROBERATE (Chhz<<3) /* Send SNS to get STS for receipts or
to make sure the conn. is alive */
#define ORATE (Chhz>>1) /* Xmit current (stream) output packet
if not touched in this time */
#define TESTRATE (Chhz*45) /* Respond testing rate */
#define ROUTERATE (Chhz<<2) /* Route aging rate */
#define BRIDGERATE (Chhz*15) /* Routing broadcast rate */
#define NRESPONDS 3 /* Test this many times before timing
out the connection */
#define UPTIME (NRESPONDS*TESTRATE) /* Nothing in this time and
the connection is flushed */
#define RFCRATE (Chhz*5) /* Retransmit RFC's this often */
#define RFCTIME (RFCRATE*CHRFCTRYS) /* Try CHRFCTRYS times to RFC */
chtime Chclock;
ch_clock()
{
register struct connection *conn;
register struct connection **connptr;
register struct packet *pkt;
chtime inactive;
int probing; /* are we probing this time ? */
static chtime nextclk = 1; /* next time to do anything */
static chtime nextprobe = 1; /* next time to probe */
static chtime nexthang = 1; /* next time to chxtime() */
static chtime nextroute = 1; /* next time to age routing */
static chtime nextbridge = 1; /* next time to send routing */
if (nextclk != ++Chclock)
return;
nextclk += MINRATE;
if (cmp_gt(Chclock, nextprobe)) {
probing = 1;
nextprobe += PROBERATE;
} else
probing = 0;
if (cmp_gt(Chclock, nexthang)) {
chxtime();
nexthang += HANGRATE;
}
if (cmp_gt(Chclock, nextroute)) {
chroutage();
nextroute += ROUTERATE;
}
if (cmp_gt(Chclock, nextbridge)) {
chbridge();
nextbridge += BRIDGERATE;
}
debug(DNOCLK,return);
for (connptr = &Chconntab[0]; connptr < &Chconntab[CHNCONNS]; connptr++)
if ((conn = *connptr) == NOCONN)
continue;
else if (conn->cn_state == CSOPEN) {
#ifdef CHSTRCODE
/*
* Timeout the current output stream packet.
* The timeout value should vary per connection.
* (shades of x.29!!)
*/
if ((pkt = conn->cn_toutput) != NOPKT &&
cmp_gt(Chclock, pkt->pk_time + ORATE) &&
!chtfull(conn)) {
conn->cn_toutput = NOPKT;
/*
* We don't care if the packet will
* be lost since either the connection
* is no longer open anyway, or an
* ANSOP was sent in the wrong state.
*/
(void)ch_write(conn, pkt);
}
#endif
if (conn->cn_thead != NOPKT)
clkretran(conn);
if (probing) {
inactive = Chclock - conn->cn_active;
if (inactive >= UPTIME)
chdead(conn);
else if (conn->cn_tacked != conn->cn_tlast &&
inactive >= PROBERATE ||
inactive >= TESTRATE) {
debug(DCONN,
printf("Conn #%x: Probe: %D\n",
conn->cn_lidx,
inactive));
sendsns(conn);
}
}
} else if (conn->cn_state == CSRFCSENT) {
/*
* The RFC packet, if it has finished being sent out
* will be at cn_thead. If it is still in the process
* of being sent it will not be there yet.
*/
pkt = conn->cn_thead;
inactive = Chclock - conn->cn_active;
if (inactive >= RFCTIME) {
debug(DCONN|DABNOR, printf("Conn #%x: RFC Timeout\n",conn->cn_lidx));
clsconn(conn, CSCLOSED, NOPKT);
} else if (pkt != NOPKT &&
cmp_gt(Chclock, pkt->pk_time + RFCRATE)) {
debug(DCONN|DABNOR,printf("Conn #%x: RFC Retransmit\n",conn->cn_lidx));
conn->cn_ttail = conn->cn_thead = NOPKT;
senddata(pkt);
}
}
}
clkretran(conn)
struct connection *conn;
{
register struct packet *pkt, **opkt;
register struct packet *lastpkt;
struct packet *firstpkt = NOPKT;
for (opkt = &conn->cn_thead; pkt = *opkt;)
if (cmp_gt(Chclock, pkt->pk_time + AGERATE)) {
if (firstpkt == NOPKT)
firstpkt = pkt;
else
lastpkt->pk_next = pkt;
lastpkt = pkt;
*opkt = pkt->pk_next;
pkt->pk_next = NOPKT;
} else
opkt = &pkt->pk_next;
if (firstpkt != NOPKT) {
debug(DCONN|DABNOR,
printf("Conn #%x: Rexmit (op:%d, pkn:%d)\n",
conn->cn_lidx, firstpkt->pk_op,
firstpkt->pk_pkn));
senddata(firstpkt);
}
}
/*
* The connection has been inactive too long, close it.
*/
chdead(conn)
register struct connection *conn;
{
static char tomsg[] = "Foreign host not responding";
register struct packet *pkt;
debug(DCONN|DABNOR,printf("Conn #%x: Timeout\n", conn->cn_lidx));
if ((pkt = pkalloc(sizeof(tomsg), 1)) != NOPKT) {
pkt = pktstr(pkt, tomsg, sizeof(tomsg));
pkt->pk_op = LOSOP;
}
clsconn(conn, CSINCT, pkt);
}
/*
* Increase the cost of accessing a subnet via a gateway
*/
chroutage()
{
register struct chroute *r;
for (r = Chroutetab; r < &Chroutetab[CHNSUBNET]; r++)
if ((r->rt_type == CHBRIDGE || r->rt_type == CHDIRECT) &&
r->rt_cost < CHHCOST)
r->rt_cost++;
}
/*
* Send routing packets on all directly connected subnets, unless we are on
* only one.
*/
chbridge()
{
register struct chroute *r;
register struct packet *pkt;
register struct rut_data *rd;
register int ndirect;
register int n;
/*
* Count the number of subnets to which we are directly connected.
* If not more than one, then we are not a bridge and shouldn't
* send out routing packets at all.
* While we're at it, count the number of subnets we know we
* have any access to. This number determines the size of the
* routine packet we need to send, if any.
*/
n = ndirect = 0;
for (r = Chroutetab; r <= &Chroutetab[CHNSUBNET]; r++)
switch(r->rt_type) {
case CHDIRECT:
ndirect++;
default:
n++;
break;
case CHNOPATH:
;
}
if (ndirect <= 1 ||
(pkt = pkalloc(n * sizeof(struct rut_data), 1)) == NOPKT)
return;
/*
* Build the routing packet to send out on each directly connected
* subnet. It is complete except for the cost of the directly
* connected subnet we are sending it out on. This cost must be
* added to each entry in the packet each time it is sent.
*/
pkt->pk_len = n * sizeof(struct rut_data);
pkt->pk_op = RUTOP;
pkt->pk_type = pkt->pk_daddr = pkt->pk_sidx = pkt->pk_didx = 0;
pkt->pk_next = NOPKT;
rd = pkt->pk_rutdata;
for (n = 0, r = Chroutetab; r < &Chroutetab[CHNSUBNET]; r++, n++)
if (r->rt_type != CHNOPATH) {
rd->pk_subnet = n;
rd->pk_cost = r->rt_cost;
rd++;
}
/*
* Now send out this packet on each directly connected subnet.
* ndirect becomes zero on last such subnet.
*/
for (r = Chroutetab; r < &Chroutetab[CHNSUBNET]; r++)
if (r->rt_type == CHDIRECT)
sendrut(pkt, r->rt_xcvr, r->rt_cost, --ndirect);
}