# /* * SYSTEM CODE FOR AWAIT, AWTENB, AWTDIS AND CAPACITY * This version implements two system calls, to access * both the 'old' and 'new' await calls * * Extended to cover network special files 7/28/78 S.Y. Chiu * Modified by BBN(dan) 9/22/78: Moved error codes to user.h and changed values * Modified by BBN(dan) 2/28/79: Changed capac to return -1, -1 on ordinary and * block special files. Provided new routine nullcap to be used on char * special files which want to return -1, -1. * Modified by BBN(dan) 4/9/79: Combined awtenb and awtdis code to get * some I space back, combined char device driver enable and disable * functions, added new routine "ablei" for them to call, and moved * the await and capac entries into cdevsw. * Modified by BBN(dan) 4/12/79: Removed old await system call code. */ /* Define AWDEBUG to get messages on system console */ /* #define AWDEBUG 1 */ /* undefined by BBN: mek (5/3/79) */ /* * Operating parameters */ #define AWTPER 2 /* max # procs enabled for a given inode */ #define PIPSIZ 1024 /* from "ken/pipe.c" */ #define PAWAIT 1 /* max priority when awaking */ #define SAWAKE 0200 /* a flag bit in p_flag */ #include "../h/param.h" #include "../h/netparam.h" #include "../h/conf.h" #include "../h/proc.h" #include "../h/user.h" #include "../h/inode.h" #include "../h/file.h" #include "../h/reg.h" #include "../h/net.h" /* * Data Structures * Note that these are not referenced externally, but are * declared extern for debugging */ /* * Structure which says which processes are enabled for * activity on an inode. The parameter AWTPER controls how many * processes may shmultaneously have any given inode 'enabled'. * The itab array consists of AWTPER*NINODE*2 bytes. The * slots for each inode are grouped, and they occur * at itab[inodeindex*AWTPER*2], i.e. the area associated with * inode[n] is itab[n*AWTPER*2] through itab[((n+1)*AWTPER*2)-1]. * * The first byte of each pair contains the process id of the * associated process, or zero, if no process is using the slot. * * The succeeding byte contains the file descriptor used to enable * the inode for that process, and indicates that activity has * occurred for that inode by making the file descriptor negative. * Because file descriptor 0 is legal, these are stored after * incrementing by 1. */ extern char itab[NINODE*AWTPER*2]; /* maps inodes to pids and fds */ /* * awttim is the primary data structure for timeouts. It * holds a number for each awaiting process. This number is * the time (according to "now") at which the process should * be awakened. * * Times in "now" are values in the range 0 - 255. A value of * 0 indicates that the process is not awaiting. */ extern char awttim[NPROC]; extern int nxttim; /* next time when some process needs poke */ extern int now; /* current time, 0 means not running */ int deb_proc { -1}; /* The AWAIT, CAPACITY, AWTENB, and AWTDIS system calls are * multiplexed according to the following subclasses: * * AWAIT : 0 * AWTENB : 1 * AWTDIS : 2 * CAPAC : 3 */ await() { register int class, arg1; int arg2,arg3; #ifdef AWDEBUG int p; p=u.u_procp->p_pid; #endif class = u.u_ar0[R0]; arg1 = u.u_arg[0]; if (class==3 || class==0) { arg2 = u.u_arg[1]; /* random unless class == 3 or 0 */ arg3 = u.u_arg[2]; /* ditto */ } switch(class) { case 0: /* AWAIT */ #ifdef AWDEBUG if (p == deb_proc) printf("\nAWAIT: p=%d time=%d buf=%d len=%d", p, arg1, arg2, arg3); #endif awt(arg1,arg2,arg3); break; case 1: /* AWTENB */ #ifdef AWDEBUG if (p == deb_proc) printf("\nAWTENB: p=%d fd=%d", p, arg1); #endif awtenb(arg1); break; case 2: /* AWTDIS */ #ifdef AWDEBUG if (p == deb_proc) printf("\nAWTDIS, p:%d, fd:%d",p,arg1); #endif awtdis(arg1); break; case 3: /* CAPAC */ capac(arg1,arg2,arg3); break; default: u.u_error = EBADCLASS; } } /* * awt (AWAIT) routine - await a change or a timeout (in seconds) * * The result area is supplied by the caller. It is simply a * byte array. When waking up a process, awt fills in the byte * array with the file descriptors of all enabled files which * had activity since the last time the process exitted from awt. * The end of the list is signalled by a -1. If this signal occurs * as the last byte of the supplied array, it implies that more * activity may have occurred which couldn't be reported, in which * case the caller should perform capacity checks to determine the * true state of all activity. * * Modified BBN(dan) 3/1/79: to fix itab_ptr++ bug */ awt(tim,bbp,bbl) int tim; /* seconds to await */ int bbl; /* length of result area, in bytes */ char *bbp; /* user address of result area */ { register int i; register char *ip; /* itab scanner */ register int p; /* process id */ struct proc *pp; extern int awtto(); pp = u.u_procp; p = pp->p_pid; /* return if already awakened */ if ((tim <= 0) || (pp->p_flag & SAWAKE)) goto awtex; /* set up timeout */ if (tim > 255) tim = 255; /* max allowed time to wait */ spl1(); /* disables callouts so 'now' doesn't change */ i = (now + tim); /* compute new time */ if (now == 0) i++; /* will start clock shortly */ if ((i =& 0377) == 0) i++; /* zero is illegal */ awttim[p] = i; /* store time to wakeup */ /* set 'next interesting time' for awtto */ if (now == 0) { /* if no other awaiting, start up */ now++; nxttim = i; /* next time something to do */ timeout(awtto, 0, 60); /* call awtto every second */ } else nxttim = cirmin(now, nxttim, i); /* sooner if needed */ #ifdef AWDEBUG if (p == deb_proc) printf("\nNOW: %o, p: %d slp til: %o", now, pp->p_pid, i); #endif sleep(&(proc[p].p_flag), PAWAIT); /* sleep on flag address */ spl0(); /* allow callouts */ #ifdef AWDEBUG if (p == deb_proc) printf("\np: %d waking up",pp->p_pid); #endif /* Exit from await. Collect info about activity and pass to the user */ awtex: pp->p_flag =& ~SAWAKE ; /* indicates awakened */ awttim[p] = 0; /* and not awaiting */ /* Scan itab, fill in user-supplied result area with fds that had activity */ for (ip = &itab; ip < &itab[NINODE * AWTPER * 2];) { if (*ip++ == p) /* This process? */ { if (*ip < 0) /* Activity? */ { *ip = -*ip; /* turn off */ /* if room, report to user */ /* save one byte for the endflag */ #ifdef AWDEBUG if (p == deb_proc) printf("\nAWTex: p=%d fd=%d", p, (*ip) - 1); #endif if (--bbl > 0) subyte(bbp++, (*ip) - 1); else bbl++; } } ip++; /* skip over fd byte */ } if (--bbl >= 0) subyte(bbp, -1); /* indicate end of list */ } /**/ /* -------------------------- A W T E N B --------------------------- */ /* * Enable the specified file descriptor. */ awtenb(fd) int fd; { enbdis('e', fd); } /* -------------------------- A W T D I S --------------------------- */ /* * Disable the specified file descriptor. */ awtdis(fd) int fd; { if (fd == -1) /* Disable all */ awtclr(); else enbdis('d', fd); } /* -------------------------- A W T C L R --------------------------- */ /* * Disable all of the file descriptors for a process. * Called from exit code. */ awtclr() { register int fd, p; p = u.u_procp -> p_pid; for (fd = 0; fd < NOFILE; fd++) enbdis('d', fd); awttim[p] = 0; u.u_error = 0; /* ignore errors from non-existent fd */ } /* -------------------------- E N B D I S --------------------------- */ /* * Common code for awtenb/awtdis/awtclr. "type" is either 'e' (enable) or * 'd' (disable). */ enbdis(type, fd) char type; int fd; { register struct inode *ip; struct file *f; extern char *ablei(); #ifndef MSG register int *soc; #endif MSG #ifdef NETWORK #ifdef MSG struct rdskt *sktp; #endif MSG #endif NETWORK if ((f = getf(fd)) == NULL) return; /* error code set by getf */ ip = f->f_inode; /* pick up inode pointer */ #ifdef NETWORK /* * the "size1" field of an inode that represents a socket * is used to hold the pointer to itab that "awake" uses to * wake up processes waiting for activity on this socket. */ if (f->f_flag & FNET) { #ifndef MSG ip = soc = f->f_netnode[f_rdnode]; if (ip) *(--soc) = ablei(type, 0, ip, fd); ip = soc = f->f_netnode[f_wrtnode]; if (ip) *(--soc) = ablei(type, 0, ip, fd); #endif MSG #ifdef MSG sktp = f->f_netnode[f_rdnode]; if (sktp) sktp->itab_p = ablei(type, 0, sktp, fd); sktp = f->f_netnode[f_wrtnode]; if (sktp) sktp->itab_p = ablei(type, 0, sktp, fd); #endif MSG } else #endif NETWORK if (f->f_flag & FPIPE) ablei(type, 0, ip, fd); else if ((ip->i_mode & IFMT) == IFCHR) (*cdevsw[ip->i_addr[0].d_major].d_awt) (type, ip, fd); else u.u_error = EBADDEV; } /* -------------------------- A B L E I ----------------------------- */ /* * ablei -- given type and arguments to enablei or disablei, switch off * to either enablei or disablei. Return the value obtained from them. * This routine enables the knowledge about legitimate values for 'type' * to be kept out of the device drivers. */ char * ablei(type, ppn, ip, fd) char type; int ppn; struct inode *ip; int fd; { extern char *enablei(); extern char *disablei(); if (type == 'e') return(enablei(ppn, ip, fd)); else if (type == 'd') return(disablei(ppn, ip, fd)); else panic("enbdis"); /* Temporary consistency check */ } /* -------------------------- E N A B L E I ------------------------- */ /* * enablei -- given process id, inode pointer, and fds, enables await * for that inode and process. Process id 0 is used to select * the current process. * * Note that a process may separately enable two or more file descriptors * which are associated with the same inode. * * Returns pointer to top of area in itab for specified inode. * The u.u_error code is also set on errors. */ char * enablei(ppn,ip,fd) int ppn; struct inode *ip; int fd; { extern struct inode inode[]; extern struct user u; register char *itp; register char p; /* process id */ char *ifree, *itps; p = ppn? ppn : u.u_procp->p_pid; fd++; /* fds stored incremented */ ifree = 0; /* ptr to free slot */ itps = &itab[(ip-inode)*AWTPER*2]; /* beginning for this inode */ for (itp=itps;itp < &itps[AWTPER*2];) { if ((*itp==p) && ((itp[1] == fd) || (itp[1] == -fd))) return(itps); /* already enabled */ if (*itp==0) ifree=itp; /* remember free slot */ itp =+ 2; /* slots are 2 bytes */ } if (ifree == 0) u.u_error = EAWTMAX; /* no free slots */ else { itp = ifree; *itp++ = p; /* put pid into table */ *itp = fd; /* and incremented file descriptor */ #ifdef AWDEBUG if (p == deb_proc) printf("\nENB, p:%d, i:%o, f: %d",p,itps,fd-1); #endif } return(itps); /* return pointer to head of table */ } /* -------------------------- D I S A B L E I ----------------------- */ /* * disablei -- given process id, inode pointer, and fds, disables await * for that inode and process. Process id 0 is used to select * the current process. * returns pointer to itab, unless all processes have been removed * in which case 0 is returned. */ char * disablei(ppn,ip,fd) int ppn, fd; struct inode *ip; { extern struct inode inode[]; extern struct user u; register char *itp; register char p; /* process id */ char *itps; char useflag; /* flag to detect last use of inode */ useflag = 0; fd++; /* because they are stored incremented */ p = ppn?ppn:u.u_procp->p_pid; itps = &itab[(ip-inode)*AWTPER*2]; /* beginning for this inode */ for (itp=itps;itp < &itps[AWTPER*2];) { if ((*itp==p) && ((itp[1] == fd) || (itp[1] == -fd))) { *itp++ = 0; /* remove pid */ *itp = 0; /* clear fds as well */ } else if (*itp++ != 0) useflag++; itp++; /* skip fds byte */ } #ifdef AWDEBUG if ( p == deb_proc ) printf("\nDIS, p:%d, i:%o, f:%d",p,itps,fd-1); #endif return(useflag?itps:0); } /* * CAPACITY SYSTEM CALL * * capac(fd,*buffer,length) * * If fd >= 0, capacity is placed in the supplied buffer. * * If fd == -1, the buffer is assumed to contain a table * whose format is a file descriptor followed by two * words into which the capacity values will be placed. * If an error occurs for any file descriptor, the error * code will be negated and returned in place of the file * descriptor. However, the call itself will return * a successful indication. */ capac(fd,buf,len) int fd,len,buf; { register struct inode *ip; int v[3]; /* result area */ struct file *fp; register int *v1, *v2; extern struct user u; /* if table supplied, recursively call capac for individual * entries. If error is indicated, change file descriptor * element of the table to contain the negative of the * error code */ if (fd < 0) { while (len >= 6) { if ((fd=fuword(buf)) >= 0) capac(fd,buf+2,4); else u.u_error = EINVAL; if (u.u_error != 0) suword(buf, -u.u_error); buf =+ 6; /* next triple */ len =- 6; /* watch for end */ u.u_error = 0; /* ignore errors */ } if (len != 0) u.u_error = EINVAL; /* bad table length */ return; } /* not a table, handle a single file descriptor */ if(len != 4) { /* room for answers? */ u.u_error = EINVAL; return; } if((fp = getf(fd)) == NULL) return; /* error code set by getf */ ip = fp->f_inode; /* pointer to inode */ v2 = v1 = v; v2++; /* ptrs to result area */ #ifdef RMI /* * The capacities are: * Read: the number of bytes left to be read in the input que, * including those in any message being read and * those in all other messages (if any) waiting. * Write: if a message is being written by the user * the min of what's left and the available buffer space * else the min of MAXMSG and the available buffer space * If there is no read or no write socket, that one gets -1. * * FRAW check added jsq BBN 3-14-79 */ if ((fp -> f_flag & FRAW) && rawcap(fp, v1, v2)); else #endif RMI #ifdef NCP /* changed to NCP from NETWORK by jsq bbn 10/19/78 */ /* * if fd is a network special file descriptor, there may be * two inodes to chase, one representing the read socket * and the other the write socket. * The capac of a read socket is the number of bytes * in its read queue. * -1 is returned as the read capacity if there is no * read socket associated with fd. * The capac of a write socket is the number of bytes allocated * by the foreign host to the write socket (max. of 1000 * because that's the max. net msg size). * As with its read counterpart, -1 is returned as the * write capacity if there is no write socket associated with * fd. */ if (fp->f_flag & FNET) { *v2 = *v1 = -1; ip=fp->f_netnode[f_rdnode]; if (ip) *v1=ip->r_qtotal; ip=fp->f_netnode[f_wrtnode]; if (ip) *v2=anyalloc(ip); } else #endif NCP /* handle pipes here */ if(fp->f_flag & FPIPE) { *v1 = ip->i_size1 - fp->f_offset[1]; *v2 = PIPSIZ - ip->i_size1; /* following line was looking at fp -> f_flag ... */ if ( ip->i_flag & IEOFP ) *v1 = -1; /* jsq BBN 3-31-79 */ if(ip->i_count < 2) { *v1 = -1; *v2 = -1; } } /* handle char special devices */ else if ((ip->i_mode&IFMT) == IFCHR) /* dispatch via table */ (*cdevsw[ip->i_addr[0].d_major].d_cap)(ip,v1); /* other devices are unsupported */ else { *v1 = -1; *v2 = -1; } /* if no error, return results in user area */ if (u.u_error == 0) { suword(buf,*v1); suword(buf+2,*v2); } }; /* * capacity, etc. for unsupported devices */ nocap(iip,vv) struct inode *iip; { extern struct user u; u.u_error = EBADDEV ; }; /* * capacity for only slightly supported devices */ nullcap(iip, vv) int *vv; { *vv++ = -1; *vv = -1; } /* * AWTTO - await timeout. This routine implements the * timeout aspect of the AWAIT system call. It * is called every second by timeout, checks and * wakes any processes which should have timed out * - note that even at 'nxttim', no process need be * awakened, if it was poked or killed some other way * * Note that awtto runs at priority level 5 (as a callout) */ awtto() { register int a,p; register struct proc *pp; if(now++ > 255) now=1; /*skip over zero*/ if(now == nxttim) /* time to do something ? */ { nxttim = 1000; /* we are searching for the new one */ for(p = 0; p < NPROC; p++) { a = awttim[p] & 0377; if(a == now) { /* time to wake this process */ pp = &proc[p]; #ifdef AWDEBUG if ( pp->p_pid == deb_proc ) printf("\nTimeout, p:%d\n",pp->p_pid); #endif pp->p_flag =| SAWAKE ; wakeup(&(pp->p_flag)) ; } else if (a != 0) nxttim = cirmin(now,nxttim,a); } if(nxttim == 1000) /* no processes waiting */ { now = 0; /* turn off background */ nxttim=0; return; } } timeout(awtto,0,60); /* reschedule interrupt */ } /* * AWAKE - wakes up a process that is awaiting. */ /* awakei -- internal call, to awaken everything associated with an inode */ awakei(ip,pf) struct inode *ip; /* pointer to inode of interest */ char pf; /* 'process flag' -- i.e. who is doing the awake */ { extern struct inode inode[]; awake(&itab[(ip-inode)*AWTPER*2],pf); } /* awake -- called with itab pointer and process flag */ awake(itps,pf) char *itps; /* pointer to itab area */ int pf; /* process flag -1 -- current user, 0 -- system */ { register char p ; /* process id */ register char *itp; extern struct proc proc[]; register struct proc *pp; p = pf?u.u_procp->p_pid:0; /* process number of waker */ for (itp=itps;itp<&itps[AWTPER*2];) { if ((*itp != 0) && (*itp != p)) { /* don't awake yourself */ pp = &proc[*itp++]; /* proc struct of wakee */ pp->p_flag =| SAWAKE ; wakeup(&pp->p_flag); if (*itp >= 0) *itp = -*itp; /* indicate activity */ #ifdef AWDEBUG if ( pp == deb_proc ) printf("\nSignal activity for %d on %d\n",p,*itp); #endif itp++; } else itp =+ 2; /* skip to next */ } } /* * UTILITY ROUTINES */ /* cirmin - circular minimum computes the circular minimum * between two eight bit values * Returns the value which is 'closest' to the relative * value (arg 1) * Note: the values are not actually restricted to 8 bits * * Ah!!!! The value returned has to be mod 256, otherwise it is * possible for a process to never awaken from its await, the * way cirmin is used in awt and awtto. * The proper changes were made on Feb. 8, 1979. S.Y. Chiu (BBN). */ cirmin(r,x,y) int r,x,y; { if(x < r) x =+ 256; if(y < r) y =+ 256; /* if(x < y) return(x); */ if(x < y) return(x%256); /* return(y); */ return(y%256); }