/* @(#)sem.c 1.5 */ /* ** Inter-Process Communication Semaphore Facility. */ #include "sys/types.h" #include "sys/param.h" #include "sys/dir.h" #ifdef u3b #include "sys/istk.h" #endif #include "sys/map.h" #include "sys/errno.h" #include "sys/signal.h" #include "sys/ipc.h" #include "sys/sem.h" #include "sys/user.h" #include "sys/seg.h" #include "sys/proc.h" #include "sys/buf.h" #ifdef pdp11 #define MOVE sempimove #else #define MOVE iomove #endif extern struct semid_ds sema[]; /* semaphore data structures */ extern struct sem sem[]; /* semaphores */ extern struct map semmap[]; /* sem allocation map */ extern struct sem_undo *sem_undo[]; /* undo table pointers */ extern struct sem_undo semu[]; /* operation adjust on exit table */ extern struct seminfo seminfo; /* param information structure */ extern union { short semvals[1]; /* set semaphore values */ struct semid_ds ds; /* set permission values */ struct sembuf semops[1]; /* operation holding area */ } semtmp; struct sem_undo *semunp; /* ptr to head of undo chain */ struct sem_undo *semfup; /* ptr to head of free undo chain */ extern time_t time; /* system idea of date */ struct ipc_perm *ipcget(); struct semid_ds *semconv(); /* ** semaoe - Create or update adjust on exit entry. */ semaoe(val, id, num) short val, /* operation value to be adjusted on exit */ num; /* semaphore # */ int id; /* semid */ { register struct undo *uup, /* ptr to entry to update */ *uup2; /* ptr to move entry */ register struct sem_undo *up, /* ptr to process undo struct */ *up2; /* ptr to undo list */ register int i, /* loop control */ found; /* matching entry found flag */ if(val == 0) return(0); if(val > seminfo.semaem || val < -seminfo.semaem) { u.u_error = ERANGE; return(1); } if((up = sem_undo[u.u_procp - proc]) == NULL) if (up = semfup) { semfup = up->un_np; up->un_np = NULL; sem_undo[u.u_procp - proc] = up; } else { u.u_error = ENOSPC; return(1); } for(uup = up->un_ent, found = i = 0;i < up->un_cnt;i++) { if(uup->un_id < id || (uup->un_id == id && uup->un_num < num)) { uup++; continue; } if(uup->un_id == id && uup->un_num == num) found = 1; break; } if(!found) { if(up->un_cnt >= seminfo.semume) { u.u_error = EINVAL; return(1); } if(up->un_cnt == 0) { up->un_np = semunp; semunp = up; } uup2 = &up->un_ent[up->un_cnt++]; while(uup2-- > uup) *(uup2 + 1) = *uup2; uup->un_id = id; uup->un_num = num; uup->un_aoe = -val; return(0); } uup->un_aoe -= val; if(uup->un_aoe > seminfo.semaem || uup->un_aoe < -seminfo.semaem) { u.u_error = ERANGE; uup->un_aoe += val; return(1); } if(uup->un_aoe == 0) { uup2 = &up->un_ent[--(up->un_cnt)]; while(uup++ < uup2) *(uup - 1) = *uup; if(up->un_cnt == 0) { /* Remove process from undo list. */ if(semunp == up) semunp = up->un_np; else for(up2 = semunp;up2 != NULL;up2 = up2->un_np) if(up2->un_np == up) { up2->un_np = up->un_np; break; } up->un_np = NULL; } } return(0); } /* ** semconv - Convert user supplied semid into a ptr to the associated ** semaphore header. */ struct semid_ds * semconv(s) register int s; /* semid */ { register struct semid_ds *sp; /* ptr to associated header */ sp = &sema[s % seminfo.semmni]; if((sp->sem_perm.mode & IPC_ALLOC) == 0 || s / seminfo.semmni != sp->sem_perm.seq) { u.u_error = EINVAL; return(NULL); } return(sp); } /* ** semctl - Semctl system call. */ semctl() { register struct a { int semid; uint semnum; int cmd; int arg; } *uap = (struct a *)u.u_ap; register struct semid_ds *sp; /* ptr to semaphore header */ register struct sem *p; /* ptr to semaphore */ register int i; /* loop control */ register struct user *up; if((sp = semconv(uap->semid)) == NULL) return; up = &u; up->u_rval1 = 0; switch(uap->cmd) { /* Remove semaphore set. */ case IPC_RMID: if(up->u_uid != sp->sem_perm.uid && up->u_uid != sp->sem_perm.cuid && !suser()) return; semunrm(uap->semid, 0, sp->sem_nsems); for(i = sp->sem_nsems, p = sp->sem_base;i--;p++) { p->semval = p->sempid = 0; if(p->semncnt) { wakeup((caddr_t)&p->semncnt); p->semncnt = 0; } if(p->semzcnt) { wakeup((caddr_t)&p->semzcnt); p->semzcnt = 0; } } mfree(semmap, (int)sp->sem_nsems, (sp->sem_base - sem) + 1); if(uap->semid + seminfo.semmni < 0) sp->sem_perm.seq = 0; else sp->sem_perm.seq++; sp->sem_perm.mode = 0; return; /* Set ownership and permissions. */ case IPC_SET: if(up->u_uid != sp->sem_perm.uid && up->u_uid != sp->sem_perm.cuid && !suser()) return; if(copyin((caddr_t)uap->arg, (caddr_t)&semtmp.ds, sizeof(semtmp.ds))) { up->u_error = EFAULT; return; } sp->sem_perm.uid = semtmp.ds.sem_perm.uid; sp->sem_perm.gid = semtmp.ds.sem_perm.gid; sp->sem_perm.mode = semtmp.ds.sem_perm.mode & 0777 | IPC_ALLOC; sp->sem_ctime = time; return; /* Get semaphore data structure. */ case IPC_STAT: if(ipcaccess(&sp->sem_perm, SEM_R)) return; if(copyout((caddr_t)sp, (caddr_t)uap->arg, sizeof(*sp))) { up->u_error = EFAULT; return; } return; /* Get # of processes sleeping for greater semval. */ case GETNCNT: if(ipcaccess(&sp->sem_perm, SEM_R)) return; if(uap->semnum >= sp->sem_nsems) { up->u_error = EINVAL; return; } up->u_rval1 = (sp->sem_base + uap->semnum)->semncnt; return; /* Get pid of last process to operate on semaphore. */ case GETPID: if(ipcaccess(&sp->sem_perm, SEM_R)) return; if(uap->semnum >= sp->sem_nsems) { up->u_error = EINVAL; return; } up->u_rval1 = (sp->sem_base + uap->semnum)->sempid; return; /* Get semval of one semaphore. */ case GETVAL: if(ipcaccess(&sp->sem_perm, SEM_R)) return; if(uap->semnum >= sp->sem_nsems) { up->u_error = EINVAL; return; } up->u_rval1 = (sp->sem_base + uap->semnum)->semval; return; /* Get all semvals in set. */ case GETALL: if(ipcaccess(&sp->sem_perm, SEM_R)) return; up->u_base = (caddr_t)uap->arg; up->u_offset = 0; up->u_segflg = 0; for(i = sp->sem_nsems, p = sp->sem_base;i--;p++) { MOVE((caddr_t)&p->semval, sizeof(p->semval), B_READ); if(up->u_error) return; } return; /* Get # of processes sleeping for semval to become zero. */ case GETZCNT: if(ipcaccess(&sp->sem_perm, SEM_R)) return; if(uap->semnum >= sp->sem_nsems) { up->u_error = EINVAL; return; } up->u_rval1 = (sp->sem_base + uap->semnum)->semzcnt; return; /* Set semval of one semaphore. */ case SETVAL: if(ipcaccess(&sp->sem_perm, SEM_A)) return; if(uap->semnum >= sp->sem_nsems) { up->u_error = EINVAL; return; } if((unsigned)uap->arg > seminfo.semvmx) { up->u_error = ERANGE; return; } if((p = sp->sem_base + uap->semnum)->semval = uap->arg) { if(p->semncnt) { p->semncnt = 0; wakeup((caddr_t)&p->semncnt); } } else if(p->semzcnt) { p->semzcnt = 0; wakeup((caddr_t)&p->semzcnt); } p->sempid = up->u_procp->p_pid; semunrm(uap->semid, uap->semnum, uap->semnum); return; /* Set semvals of all semaphores in set. */ case SETALL: if(ipcaccess(&sp->sem_perm, SEM_A)) return; up->u_base = (caddr_t)uap->arg; up->u_offset = 0; up->u_segflg = 0; MOVE((caddr_t)semtmp.semvals, (int)(sizeof(semtmp.semvals[0]) * sp->sem_nsems), B_WRITE); if(up->u_error) return; for(i = 0;i < sp->sem_nsems;) if(semtmp.semvals[i++] > seminfo.semvmx) { up->u_error = ERANGE; return; } semunrm(uap->semid, 0, sp->sem_nsems); for(i = 0, p = sp->sem_base;i < sp->sem_nsems; (p++)->sempid = up->u_procp->p_pid) { if(p->semval = semtmp.semvals[i++]) { if(p->semncnt) { p->semncnt = 0; wakeup((caddr_t)&p->semncnt); } } else if(p->semzcnt) { p->semzcnt = 0; wakeup((caddr_t)&p->semzcnt); } } return; default: up->u_error = EINVAL; return; } } /* ** semexit - Called by exit(sys1.c) to clean up on process exit. */ semexit() { register struct sem_undo *up, /* process undo struct ptr */ *p; /* undo struct ptr */ register struct semid_ds *sp; /* semid being undone ptr */ register int i; /* loop control */ register long v; /* adjusted value */ register struct sem *semp; /* semaphore ptr */ if((up = sem_undo[u.u_procp - proc]) == NULL) return; if(up->un_cnt == 0) goto cleanup; for(i = up->un_cnt;i--;) { if((sp = semconv(up->un_ent[i].un_id)) == NULL) continue; v = (long)(semp = sp->sem_base + up->un_ent[i].un_num)->semval + up->un_ent[i].un_aoe; if(v < 0 || v > seminfo.semvmx) continue; semp->semval = v; if(v == 0 && semp->semzcnt) { semp->semzcnt = 0; wakeup((caddr_t)&semp->semzcnt); } if(up->un_ent[i].un_aoe > 0 && semp->semncnt) { semp->semncnt = 0; wakeup((caddr_t)&semp->semncnt); } } up->un_cnt = 0; if(semunp == up) semunp = up->un_np; else for(p = semunp;p != NULL;p = p->un_np) if(p->un_np == up) { p->un_np = up->un_np; break; } cleanup: up->un_np = semfup; semfup = up; sem_undo[u.u_procp - proc] = NULL; } /* ** semget - Semget system call. */ semget() { register struct a { key_t key; int nsems; int semflg; } *uap = (struct a *)u.u_ap; register struct semid_ds *sp; /* semaphore header ptr */ register int i; /* temp */ int s; /* ipcget status return */ if((sp = (struct semid_ds *) ipcget(uap->key, uap->semflg, (struct ipc_perm *)sema, seminfo.semmni, sizeof(*sp), &s)) == NULL) return; if(s) { /* This is a new semaphore set. Finish initialization. */ if(uap->nsems <= 0 || uap->nsems > seminfo.semmsl) { u.u_error = EINVAL; sp->sem_perm.mode = 0; return; } if((i = malloc(semmap, uap->nsems)) == NULL) { u.u_error = ENOSPC; sp->sem_perm.mode = 0; return; } sp->sem_base = sem + (i - 1); sp->sem_nsems = uap->nsems; sp->sem_ctime = time; } else if(uap->nsems && sp->sem_nsems < uap->nsems) { u.u_error = EINVAL; return; } u.u_rval1 = sp->sem_perm.seq * seminfo.semmni + (sp - sema); } /* ** seminit - Called by main(main.c) to initialize the semaphore map. */ seminit() { register i; mapinit(semmap, seminfo.semmap); mfree(semmap, seminfo.semmns, 1); semfup = semu; for (i = 0; i < seminfo.semmnu - 1; i++) { semfup->un_np = (struct sem_undo *)((uint)semfup+seminfo.semusz); semfup = semfup->un_np; } semfup->un_np = NULL; semfup = semu; } /* ** semop - Semop system call. */ semop() { register struct a { int semid; struct sembuf *sops; uint nsops; } *uap = (struct a *)u.u_ap; register struct sembuf *op; /* ptr to operation */ register int i; /* loop control */ register struct semid_ds *sp; /* ptr to associated header */ register struct sem *semp; /* ptr to semaphore */ int again; if((sp = semconv(uap->semid)) == NULL) return; if(uap->nsops > seminfo.semopm) { u.u_error = E2BIG; return; } u.u_base = (caddr_t)uap->sops; u.u_offset = 0; u.u_segflg = 0; MOVE((caddr_t)semtmp.semops, (int)(uap->nsops * sizeof(*op)), B_WRITE); if(u.u_error) return; /* Verify that sem #s are in range and permissions are granted. */ for(i = 0, op = semtmp.semops;i++ < uap->nsops;op++) { if(ipcaccess(&sp->sem_perm, (ushort)(op->sem_op?SEM_A:SEM_R))) return; if(op->sem_num >= sp->sem_nsems) { u.u_error = EFBIG; return; } } again = 0; check: /* Loop waiting for the operations to be satisified atomically. */ /* Actually, do the operations and undo them if a wait is needed or an error is detected. */ if (again) { /* Verify that the semaphores haven't been removed. */ if(semconv(uap->semid) == NULL) { u.u_error = EIDRM; return; } /* copy in user operation list after sleep */ u.u_base = (caddr_t)uap->sops; u.u_offset = 0; u.u_segflg = 0; MOVE((caddr_t)semtmp.semops, (int)(uap->nsops * sizeof(*op)), B_WRITE); if(u.u_error) return; } again = 1; for(i = 0, op = semtmp.semops;i < uap->nsops;i++, op++) { semp = sp->sem_base + op->sem_num; if(op->sem_op > 0) { if(op->sem_op + (long)semp->semval > seminfo.semvmx || (op->sem_flg & SEM_UNDO && semaoe(op->sem_op, uap->semid, (short)op->sem_num))) { if(u.u_error == 0) u.u_error = ERANGE; if(i) semundo(semtmp.semops, i, uap->semid, sp); return; } semp->semval += op->sem_op; if(semp->semncnt) { semp->semncnt = 0; wakeup((caddr_t)&semp->semncnt); } continue; } if(op->sem_op < 0) { if(semp->semval >= -op->sem_op) { if(op->sem_flg & SEM_UNDO && semaoe(op->sem_op, uap->semid, (short)op->sem_num)) { if(i) semundo(semtmp.semops, i, uap->semid, sp); return; } semp->semval += op->sem_op; if(semp->semval == 0 && semp->semzcnt) { semp->semzcnt = 0; wakeup((caddr_t)&semp->semzcnt); } continue; } if(i) semundo(semtmp.semops, i, uap->semid, sp); if(op->sem_flg & IPC_NOWAIT) { u.u_error = EAGAIN; return; } semp->semncnt++; if(sleep((caddr_t)&semp->semncnt, PCATCH | PSEMN)) { if((semp->semncnt)-- <= 1) { semp->semncnt = 0; wakeup((caddr_t)&semp->semncnt); } u.u_error = EINTR; return; } goto check; } if(semp->semval) { if(i) semundo(semtmp.semops, i, uap->semid, sp); if(op->sem_flg & IPC_NOWAIT) { u.u_error = EAGAIN; return; } semp->semzcnt++; if(sleep((caddr_t)&semp->semzcnt, PCATCH | PSEMZ)) { if((semp->semzcnt)-- <= 1) { semp->semzcnt = 0; wakeup((caddr_t)&semp->semzcnt); } u.u_error = EINTR; return; } goto check; } } /* All operations succeeded. Update sempid for accessed semaphores. */ for(i = 0, op = semtmp.semops;i++ < uap->nsops; (sp->sem_base + (op++)->sem_num)->sempid = u.u_procp->p_pid); sp->sem_otime = time; u.u_rval1 = 0; } #ifdef pdp11 /* ** sempimove - PDP 11 pimove interface for possibly large copies. */ sempimove(base, count, mode) paddr_t base; /* base address */ register unsigned count; /* byte count */ int mode; /* transfer mode */ { register unsigned tcount; /* current transfer count */ while(u.u_error == 0 && count) { tcount = count > 8064 ? 8064 : count; pimove(base, tcount, mode); base += tcount; count -= tcount; } } #endif /* ** semsys - System entry point for semctl, semget, and semop system calls. */ semsys() { int semctl(), semget(), semop(); static int (*calls[])() = {semctl, semget, semop}; register struct a { uint id; /* function code id */ } *uap = (struct a *)u.u_ap; if(uap->id > 2) { u.u_error = EINVAL; return; } u.u_ap = &u.u_arg[1]; (*calls[uap->id])(); } /* ** semundo - Undo work done up to finding an operation that can't be done. */ semundo(op, n, id, sp) register struct sembuf *op; /* first operation that was done ptr */ register int n, /* # of operations that were done */ id; /* semaphore id */ register struct semid_ds *sp; /* semaphore data structure ptr */ { register struct sem *semp; /* semaphore ptr */ for(op += n - 1;n--;op--) { if(op->sem_op == 0) continue; semp = sp->sem_base + op->sem_num; semp->semval -= op->sem_op; if(op->sem_flg & SEM_UNDO) (void) semaoe(-op->sem_op, id, (short)op->sem_num); } } /* ** semunrm - Undo entry remover. ** ** This routine is called to clear all undo entries for a set of semaphores ** that are being removed from the system or are being reset by SETVAL or ** SETVALS commands to semctl. */ semunrm(id, low, high) int id; /* semid */ ushort low, /* lowest semaphore being changed */ high; /* highest semaphore being changed */ { register struct sem_undo *pp, /* ptr to predecessor to p */ *p; /* ptr to current entry */ register struct undo *up; /* ptr to undo entry */ register int i, /* loop control */ j; /* loop control */ pp = NULL; p = semunp; while(p != NULL) { /* Search through current structure for matching entries. */ for(up = p->un_ent, i = 0;i < p->un_cnt;) { if(id < up->un_id) break; if(id > up->un_id || low > up->un_num) { up++; i++; continue; } if(high < up->un_num) break; for(j = i;++j < p->un_cnt; p->un_ent[j - 1] = p->un_ent[j]); p->un_cnt--; } /* Reset pointers for next round. */ if(p->un_cnt == 0) /* Remove from linked list. */ if(pp == NULL) { semunp = p->un_np; p->un_np = NULL; p = semunp; } else { pp->un_np = p->un_np; p->un_np = NULL; p = pp->un_np; } else { pp = p; p = p->un_np; } } }