Files
Arquivotheca.SunOS-4.1.4/sys/rfs/comm.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

724 lines
18 KiB
C

/* @(#)comm.c 1.1 94/10/31 SMI */
/* Copyright (c) 1984 AT&T */
/* All Rights Reserved */
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
/* The copyright notice above does not evidence any */
/* actual or intended publication of such source code. */
/* #ident "@(#)kern-port:nudnix/comm.c 10.31" */
/* These are the communications routines called from the UNIX kernel.
* They deal with send and receive descriptors, and with sending
* and receiving messages.
*/
#include <sys/types.h>
#include <sys/param.h>
#include <rfs/rfs_misc.h>
#include <rfs/sema.h>
#include <sys/sysmacros.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/signal.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/file.h>
#include <sys/vnode.h>
#include <sys/stream.h>
#include <sys/mount.h>
#include <rfs/message.h>
#include <rfs/comm.h>
#include <rfs/nserve.h>
#include <rfs/cirmgr.h>
#include <sys/debug.h>
#include <rfs/rdebug.h>
#include <rfs/rfs_node.h>
#include <rfs/rfs_mnt.h>
#include <rfs/rfs_serve.h>
#include <rfs/recover.h>
/*
* Don't let RFS start unless we have enough resources:
* SNDD: one for rfdaemon, one for mount, one for request, one for cache
* RCVD: mount, signals, rfdaemon, cache, one to do something
* MINGDP: at least one circuit
*/
#define MINSNDD 4
#define MINRCVD 5
#define MINGDP 1
/* Global variables used by this routine. */
int rdfree; /* number of free receive descriptors */
int sdfree; /* number of free send descriptors */
sndd_t sdfreelist; /* free send desc link list */
rcvd_t rdfreelist; /* free recv desc link list */
rcvd_t sigrd; /* rd for signals */
rcvd_t cfrd; /* rd for mounts */
static ushort connid;
int sdlowmark; /* sd free count low mark */
int serverslp = 0; /* server sleep for buffer flag */
int clientslp = 0; /* client sleep for buffer flag */
mblk_t *server_bp = NULL; /* server stream buffer */
mblk_t *client_bp = NULL; /* client stream buffer */
int nserverbuf = 1; /* number of 2k server stream buffer */
sndd_t cache_sd; /* SD for sending cache disable messages */
rcvd_t cache_rd; /* RD for receiving cache disable responses */
extern rcvd_t rd_recover; /* recovery receive descriptor */
extern void strunbcall();
extern void del_rduser();
void commdinit();
/*
* create a send descriptor.
*/
sndd_t
cr_sndd ()
{
register sndd_t retsndd;
register int s;
extern int nservers;
s = splrf();
/* Make sure every server can get a send-descriptor. */
if ((retsndd = sdfreelist) == (sndd_t) NULL) {
(void) splx (s);
return ((sndd_t) NULL);
}
/* Got a free sndd. */
DUPRINT2(DB_SD_RD, "cr_sndd: sd %x\n",retsndd);
sdfreelist = retsndd->sd_next;
sdfree--;
retsndd->sd_refcnt = 1;
retsndd->sd_queue = NULL;
retsndd->sd_copycnt = 0;
retsndd->sd_connid = 0;
retsndd->sd_sindex = 0;
retsndd->sd_stat = SDUSED;
retsndd->sd_fhandle = 0;
retsndd->sd_count = 0;
retsndd->sd_offset = 0;
(void) splx(s);
return (retsndd);
}
set_sndd (sd, queue, index, nconnid)
register sndd_t sd;
int nconnid;
queue_t *queue;
index_t index;
{
register int s;
DUPRINT5(DB_SD_RD, "set_sndd: sndd %x queue %x index %x proc %x\n",
sd, queue, index,u.u_procp);
ASSERT(sd->sd_stat != SDUNUSED);
s = splrf();
sd->sd_queue = queue;
sd->sd_sindex = index;
sd->sd_connid = nconnid;
(void) splx(s);
}
free_sndd (sd)
sndd_t sd;
{
int s;
DUPRINT3(DB_SD_RD, "free_sndd: sd %x, proc %x \n",sd,u.u_procp);
ASSERT(sd->sd_stat != SDUNUSED);
s = splrf();
sd->sd_next = sdfreelist;
sdfreelist = sd;
sd->sd_stat = SDUNUSED;
sd->sd_connid = 0;
sdfree++;
(void) splx(s);
}
int
del_sndd (sd)
sndd_t sd;
{
int error = 0;
DUPRINT2(DB_SD_RD, "del_sndd: sd %x\n", sd);
ASSERT(sd->sd_stat != SDUNUSED);
if(sd->sd_refcnt > 1) {
sd->sd_refcnt--;
return(0);
}
error = du_iput(sd, u.u_cred);
if (error == ENOLINK)
error = 0;
if (!error)
free_sndd(sd);
DUPRINT3(DB_SYSCALL, "del_sndd: DUIPUT sd %x, result %d\n", sd, error);
return (error);
}
/* send a message.
* set up all the proper fields in the message. Fill in the gift
* field in the message if there is a gift.
*/
int
sndmsg (sd, bp, bytes, gift)
sndd_t sd; /* which send descriptor to send on */
mblk_t *bp; /* message pointer */
int bytes; /* how much of the buffer to send */
rcvd_t gift; /* gift, if there is one */
{
register struct message *msg;
register struct gdp *tgdp;
queue_t *rq, *wq;
register int s;
DUPRINT4(DB_COMM,"sndmsg: sd %x bp %x gift %x\n", sd, bp, gift);
DUPRINT4(DB_COMM, "BEFORE sndmsg: m_dest %x m_connid %x m_gindex %x\n",
sd->sd_sindex,sd->sd_connid,rdtoinx (gift));
ASSERT(sd && sd->sd_stat != SDUNUSED);
if (sd->sd_stat & SDLINKDOWN) {
DUPRINT1(DB_RECOVER, "sndmsg: trying to send over dead link\n");
freemsg (bp);
return (ENOLINK);
}
rq = sd->sd_queue;
ASSERT(rq);
tgdp = (struct gdp *)rq->q_ptr;
wq = WR(rq);
s = splrf();
while (!canput(wq)) {
DUPRINT2(DB_GDPERR, "sndmsg: queue full on sd %x\n", sd);
(void) sleep((caddr_t) tgdp, PZERO-1);
if (sd->sd_stat & SDLINKDOWN) {
DUPRINT1(DB_RECOVER, "sndmsg: trying to send over dead link\n");
freemsg (bp);
(void) splx(s);
return (ENOLINK);
}
}
(void) splx(s);
msg = (struct message *) bp->b_rptr;
((struct request *) PTOMSG(bp->b_rptr))->rq_pid =
u.u_procp->p_pid; /* store the pid for signals */
bp->b_wptr = bp->b_rptr + sizeof(struct message) + bytes;
msg->m_dest = sd->sd_sindex;
msg->m_connid = sd->sd_connid;
msg->m_cmd = 0; /* not used */
msg->m_stat |= VER1;
if (gift) {
msg->m_stat |= GIFT;
msg->m_gindex = rdtoinx (gift);
msg->m_gconnid = gift->rd_connid;
ASSERT(gift->rd_stat != RDUNUSED);
/* Keep track of who gets gift. */
if (gift->rd_qtype & SPECIFIC)
(struct queue *) gift->rd_user_list = rq;
/* (Keep track of GENERAL RDs in make_gift.) */
}
msg->m_size = bytes + sizeof (struct message);
DUPRINT4(DB_COMM, "sndmsg: m_dest %x m_connid %x m_gindex %x\n",
msg->m_dest,msg->m_connid,msg->m_gindex);
/*
* convert RFS headers and data to canonical forms
*/
rftocanon(bp, tgdp->hetero);
putq(wq, bp);
return (0);
}
/*
* Get a buffer. Doesn't return until it has the buffer. Saves signals
* which may occur while sleeping waiting for a buffer.
*/
mblk_t *
rfs_getbuf(size, bpri)
int size;
int bpri; /* buffer allocation priority */
{
int sig;
char cursig;
mblk_t *bp;
SAVE_SIG(u.u_procp, sig, cursig);
while ((bp = alocbuf(size, bpri)) == NULL)
ACCUM_SIG(u.u_procp, sig, cursig);
RESTORE_SIG(u.u_procp, sig, cursig);
return(bp);
}
mblk_t *
alocbuf (size, bpri)
int size;
int bpri; /* buffer allocation priority */
{
register mblk_t *mbp;
extern setrun();
ASSERT (size <= MSGBUFSIZE);
while ((mbp = allocb (size + sizeof (struct message), bpri)) == NULL) {
DUPRINT1(DB_GDPERR, "alocbuf: allocb fail, using bufcall()\n");
/* wait for buffer to become available */
if (bufcall((uint)(size + sizeof(struct message)),
bpri, setrun, (long) u.u_procp) == 0) {
DUPRINT1(DB_GDPERR, "alocbuf: bufcall fail\n");
return(NULL);
}
if (sleep((caddr_t)&(u.u_procp->p_flag), PREMOTE|PCATCH) != 0) {
/* wake up due to signal */
strunbcall(size + sizeof(struct message), u.u_procp);
DUPRINT1(DB_GDPERR, "alocbuf: wake up by signal\n");
return(NULL);
}
strunbcall(size + sizeof(struct message), u.u_procp);
}
mbp->b_wptr += sizeof(struct message);
bzero((char *)mbp->b_rptr, sizeof(struct message)+
sizeof(struct response)-DATASIZE);
return (mbp);
}
/*
* allocate a send buffer -- server side, uses private buffer pool if
* none available in general pool. Note: doesn't return until it has
* a buffer.
*/
mblk_t *
salocbuf (size, bpri)
int size;
int bpri; /* buffer allocation priority */
{
register mblk_t *mbp;
register mblk_t *nbp;
int s;
extern mblk_t *server_bp;
extern wake_serverbp();
ASSERT (size <= MSGBUFSIZE);
while ((mbp = allocb (size + sizeof (struct message), bpri)) == NULL) {
/* server process will use its own stream buffer */
for (mbp = server_bp; mbp; mbp = mbp->b_next) {
if (mbp->b_datap->db_ref == 1) {
DUPRINT2(DB_GDPERR, "alocbuf: use serv buf %x\n",
mbp);
if ((nbp = dupmsg(mbp)) == NULL)
break;
nbp->b_rptr = nbp->b_datap->db_base;
nbp->b_wptr = nbp->b_datap->db_base +
sizeof(struct message);
bzero((char *)nbp->b_rptr,
sizeof(struct message) +
sizeof(struct response)-DATASIZE);
return (nbp);
}
}
DUPRINT1(DB_GDPERR, "alocbuf: fail to use server buffer\n");
s = splrf();
if (serverslp == 0) {
serverslp++;
/* sleep for one sec */
timeout((int (*)()) wake_serverbp, (caddr_t)0, 100);
}
(void) splx(s);
(void) sleep((caddr_t)&server_bp, PZERO);
}
mbp->b_wptr += sizeof(struct message);
((struct message *)mbp->b_rptr)->m_stat = 0;
return (mbp);
}
wake_serverbp()
{
serverslp = 0;
wakeup((caddr_t)&server_bp);
}
wake_clientbp()
{
clientslp = 0;
wakeup((caddr_t)&client_bp);
}
/*
* Reuse incoming message buffer for response if the size
* is large enough for the response.
* Otherwise, allocate a buffer big enough for the response.
* This will prevent possible deadlock when server fails to get a
* buffer for sending response.
*/
mblk_t *
reusebuf (bp, size)
mblk_t *bp;
int size;
{
mblk_t *nbp;
if (size <= (bp->b_datap->db_lim - bp->b_datap->db_base) &&
bp->b_datap->db_ref == 1) {
/* reuse the incoming buffer */
nbp = dupb(bp);
if (nbp != NULL) {
nbp->b_rptr = nbp->b_datap->db_base;
nbp->b_wptr = nbp->b_datap->db_base + sizeof(struct message);
((struct message *)nbp->b_rptr)->m_stat = 0;
return (nbp);
}
}
/* fail to reuse the buffer */
/* allocate a buffer that is big enough */
nbp = rfs_getbuf(size, BPRI_MED);
return (nbp);
}
/*
* getcbp = get client buffer pointer. Called by copyin to
* ensure that a buffer will be returned and thus prevent deadlock
*/
mblk_t *
getcbp(size)
int size;
{
int s;
mblk_t *nbp = NULL;
extern wake_clientbp();
/* allocate a buffer that is big enough
* if you fail to allocate a buffer try
* to use the preallocated client_bp
* If you fail go allocate client_bp sleep for .5 sec
* and try reusing your original buffer.
* this is an end condition and should rarely happen
* depending on the number of buffers configured.
*/
while ((nbp = allocb (size + sizeof (struct message), BPRI_MED)) == NULL) {
if (client_bp->b_datap->db_ref == 1) {
if ((nbp = dupmsg(client_bp)) == NULL)
continue;
nbp->b_rptr = nbp->b_datap->db_base;
nbp->b_wptr = nbp->b_datap->db_base + sizeof(struct message);
bzero((char *)nbp->b_rptr, sizeof(struct message)+
sizeof(struct response)-DATASIZE);
return (nbp);
}
s = splrf();
if (clientslp == 0) {
clientslp++;
timeout((int (*)()) wake_clientbp, (caddr_t)0, 60);
}
(void) splx(s);
(void) sleep ((caddr_t)&client_bp, PZERO);
continue;
}
bzero((char *)nbp->b_rptr, sizeof(struct message)+
sizeof(struct response)-DATASIZE);
return (nbp);
}
/*
* Create a receive descriptor.
*/
rcvd_t
cr_rcvd (qsize, type)
int qsize; /* max no. messages for this rcvd */
char type;
{
register rcvd_t retrcvd; /* return value of the function */
if ((retrcvd = rdfreelist) != NULL) {
rdfreelist = retrcvd->rd_next;
rdfree--;
retrcvd->rd_qsize = qsize;
retrcvd->rd_qcnt = 0;
retrcvd->rd_vnode = NULL;
retrcvd->rd_refcnt = 1;
retrcvd->rd_act_cnt = 0;
retrcvd->rd_qtype = type;
retrcvd->rd_user_list = NULL;
retrcvd->rd_connid = connid++;
retrcvd->rd_rcvdq.qc_head = NULL;
retrcvd->rd_stat = RDUSED;
initsema(&retrcvd->rd_qslp, 0);
DUPRINT3(DB_SD_RD,"cr_rcvd: rcvd %x index %x\n",
retrcvd-rcvd, retrcvd);
}
else
DUPRINT1(DB_SD_RD, "cr_rcvd: out of rcvd\n");
return (retrcvd);
}
/*
* Decrement the count on a receive descriptor.
* If count goes to zero, free it and clean text.
* If sp is non-null, the rd_user structs associated with
* sp->s_mntindx will be released.
*/
del_rcvd (rd, sp)
register rcvd_t rd;
struct serv_proc *sp;
{
DUPRINT4(DB_SD_RD, "del_rcvd: rd %x count %x connid %x\n",
rd, rd->rd_refcnt,rd->rd_connid);
ASSERT(rd);
ASSERT(rd->rd_stat != RDUNUSED);
if (sp)
del_rduser(rd, sp);
if (--rd->rd_refcnt)
return;
free_rcvd(rd);
return;
}
/*
* Return a receive descriptor to the freelist.
*/
free_rcvd (rd)
register rcvd_t rd;
{
DUPRINT2(DB_SD_RD, "free_rcvd: rd %x\n", rd);
ASSERT (rd && rd->rd_stat != RDUNUSED);
rd->rd_stat = RDUNUSED;
rdfree++;
ASSERT(!((rd->rd_qtype & GENERAL) && (rd->rd_user_list != NULL)));
rd->rd_user_list = NULL;
rd->rd_next = rdfreelist;
rdfreelist = rd;
}
/*
* dequeue a message off of the receive queue.
* Construct a gift from the message if there is one.
* The error recovery for no more send descriptors may cause
* this routine to sleep waiting for one.
* Returns RCVQEMP or SUCCESS.
*/
dequeue (rd, bufp, sd, size)
register rcvd_t rd;
mblk_t **bufp; /* output - the address of the message buffer */
sndd_t sd; /* output - the gift, if there was one */
int *size; /* output - how many bytes there are */
{
struct message *msg;
mblk_t *bp;
mblk_t *deque();
int s;
ASSERT(rd);
/* need to raise priority because arrmsg() calls enque()
and add_to_msgs_list() at stream priority */
s = splrf();
if ((bp = deque (&rd->rd_rcvdq)) == NULL) {
(void) splx(s);
DUPRINT3(DB_COMM, "dequeue: que %x empty,act_cnt=%d\n", rd,rd->rd_act_cnt);
return (RCVQEMP);
}
ASSERT(rd->rd_qcnt > 0);
DUPRINT5(DB_COMM, "dequeue: rd_act_cnt=%d que=%x, msg %x qcnt %x\n", rd->rd_act_cnt,rd,bp,rd->rd_qcnt);
rd->rd_qcnt--;
msg = (struct message *)bp->b_rptr;
if ((rcvdemp(rd)) && (rd->rd_qtype & GENERAL))
rm_msgs_list(rd);
(void) splx(s);
*bufp = bp;
*size = msg->m_size - sizeof (struct message);
if (msg->m_stat & GIFT) {
struct request *msg_in;
ASSERT(sd);
msg_in = (struct request *) PTOMSG (msg);
set_sndd (sd, (queue_t *)msg->m_queue,
(index_t) msg->m_gindex, (int) msg->m_gconnid);
sd->sd_mntindx = msg_in->rq_mntindx;
}
return (RFS_SUCCESS);
}
/* Initialize the communications data structures. */
comminit ()
{
extern rcvd_t sigrd;
register sndd_t tmp;
rcvd_t rd;
register mblk_t *bp;
register int i;
if (nsndd < MINSNDD || nrcvd < MINRCVD || maxgdp < MINGDP)
return(RFS_FAILURE);
if (nsndd <= maxserve) {
maxserve = (nsndd - 1);
printf ("maxserve changed to %d - not enough send descriptors\n",
maxserve);
}
if (maxserve < minserve) {
minserve = maxserve;
printf ("minserve changed to %d (maxserve) \n", minserve);
}
for (tmp = sndd ; tmp < &sndd[nsndd]; tmp++) {
tmp->sd_stat = SDUNUSED;
tmp->sd_next = tmp + 1;
}
sndd[nsndd - 1].sd_next = NULL;
sdfreelist = sndd;
if ((sdlowmark = nsndd/10) == 0)
sdlowmark = 1;
for (rd = rcvd; rd < &rcvd[nrcvd]; rd++) {
rd->rd_stat = RDUNUSED;
rd->rd_connid = 0;
rd->rd_next = rd + 1;
}
rcvd[nrcvd - 1].rd_next = NULL;
rdfreelist = rcvd;
rdfree = nrcvd;
sdfree = nsndd;
/* create well-known RDs & SDs */
cfrd = cr_rcvd(NSQSIZE, GENERAL); /* chow fun */
sigrd = cr_rcvd(SIGQSIZE, GENERAL); /* signals */
rd_recover = cr_rcvd(NSQSIZE, SPECIFIC); /* recovery */
cache_rd = cr_rcvd (1, SPECIFIC); /* cache RD */
cache_sd = cr_sndd (); /* cache SD */
connid = 0;
/*
*Allocate one buffer for client
*/
if ((bp = allocb (sizeof(struct message) + sizeof(struct response), BPRI_MED)) == NULL) {
printf("WARNING: not enough stream buffers for RFS, RFS failed\n");
u.u_error = ENOMEM;
commdinit();
return(RFS_FAILURE);
}
client_bp = bp;
/* allocate maxserve number of 2K stream buffers for server usage */
for (i = 0; i < nserverbuf; i++) {
if ((bp = allocb(sizeof(struct message) +
sizeof(struct response), BPRI_MED)) == NULL) {
/* fail to get enough stream buffers for server usage,
fail the RFS startup and free stream buffers */
printf("WARNING: not enough stream buffers for RFS, RFS failed\n");
u.u_error = ENOMEM;
commdinit();
return(RFS_FAILURE);
}
bp->b_wptr += sizeof(struct message);
bzero((char *)bp->b_rptr, sizeof(struct message)+
sizeof(struct response)-DATASIZE);
bp->b_next = server_bp;
server_bp = bp;
}
return(RFS_SUCCESS);
}
/* De-initialize the communications data structures. */
void
commdinit()
{
register mblk_t *bp;
DUPRINT1 (DB_RFSTART, "commdinit \n");
(void) del_rcvd (cfrd, (struct serv_proc *) NULL);
(void) del_rcvd (sigrd, (struct serv_proc *) NULL);
(void) del_rcvd (rd_recover, (struct serv_proc *) NULL);
(void) del_rcvd (cache_rd, (struct serv_proc *) NULL);
free_sndd(cache_sd);
while (bp = server_bp) {
server_bp = bp->b_next;
freemsg(bp);
}
if (client_bp) {
freemsg(client_bp);
client_bp = NULL;
}
}
arrmsg (bp)
mblk_t *bp;
{
register struct message *msgp = (struct message *)bp->b_rptr;
register rcvd_t rd;
extern rcvd_t sigrd;
struct serv_proc *found;
extern int msgflag;
/*
* put the message in the right receive queue
*/
ASSERT (msgp->m_dest >= 0 && msgp->m_dest < nrcvd);
rd = inxtord (msgp->m_dest);
DUPRINT3(DB_COMM, "arrmsg: msg %x for rd %x\n", bp, rd);
/*
ASSERT (rd->rd_connid == msgp->m_connid);
*/
if (msgp->m_stat & SIGNAL) {
if(rd->rd_stat == RDUNUSED){
freemsg(bp);
DUPRINT2(DB_SIGNAL,"arrmsg:SIG for bad rd %x ignored\n",
rd);
return;
}
DUPRINT3(DB_SIGNAL,"arrmsg:m_stat == SIGNAL rd %x sigrd %x\n",
rd,sigrd);
rd = sigrd;
ASSERT (rd->rd_stat != RDUNUSED);
}
if (rd->rd_stat == RDUNUSED) {
printf("RFS: got request for inactive rd %d, ignored\n",
msgp->m_dest);
freemsg(bp);
return;
}
enque(&rd->rd_rcvdq, bp);
rd->rd_qcnt++;
if (rd->rd_qtype & SPECIFIC)
cvsema((caddr_t) &rd->rd_qslp);
else {
/*dispatch the first free process*/
if (found = get_proc(&s_idle))
vsema((caddr_t) &found->s_sleep, 0);
add_to_msgs_list(rd);
if (!found)
if(nservers < maxserve){
msgflag |= MORE_SERVE;
wakeup((caddr_t) &rd_recover->rd_qslp);
/* rfdaemon nacks if no servers and no streams bufs */
} else if ( nservers == maxserve &&
testb(2048,BPRI_MED) == 0){
msgflag |= DEADLOCK;
wakeup((caddr_t) &rd_recover->rd_qslp);
}
}
}