diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-01-29 01:41:54 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-01-29 01:41:54 +0000 |
commit | f969d69ba9f952e5bdd38278e25e26a3e4a61a70 (patch) | |
tree | b3530d803df59d726afaabebc6626987dee1ca05 /ipc/sem.c | |
parent | a10ce7ef2066b455d69187643ddf2073bfc4db24 (diff) |
Merge with 2.3.27.
Diffstat (limited to 'ipc/sem.c')
-rw-r--r-- | ipc/sem.c | 978 |
1 files changed, 581 insertions, 397 deletions
@@ -50,111 +50,101 @@ * array we do the same as before. * * /proc/sysvipc/sem support (c) 1999 Dragos Acostachioaie <dragos@iname.com> + * + * SMP-threaded, sysctl's added + * (c) 1999 Manfred Spraul <manfreds@colorfullife.com> */ #include <linux/config.h> #include <linux/malloc.h> -#include <linux/smp_lock.h> +#include <linux/spinlock.h> #include <linux/init.h> #include <linux/proc_fs.h> - #include <asm/uaccess.h> +#include "util.h" + + +#define sem_lock(id) ((struct semid_ds*)ipc_lock(&sem_ids,id)) +#define sem_unlock(id) ipc_unlock(&sem_ids,id) +#define sem_rmid(id) ((struct semid_ds*)ipc_rmid(&sem_ids,id)) +#define sem_checkid(sma, semid) \ + ipc_checkid(&sem_ids,&sma->sem_perm,semid) +#define sem_buildid(id, seq) \ + ipc_buildid(&sem_ids, id, seq) +static struct ipc_ids sem_ids; -extern int ipcperms (struct ipc_perm *ipcp, short semflg); static int newary (key_t, int, int); -static int findkey (key_t key); static void freeary (int id); #ifdef CONFIG_PROC_FS static int sysvipc_sem_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data); #endif -static struct semid_ds *semary[SEMMNI]; -static int used_sems = 0, used_semids = 0; -static DECLARE_WAIT_QUEUE_HEAD(sem_lock); -static int max_semid = 0; +#define SEMMSL_FAST 256 /* 512 bytes on stack */ +#define SEMOPM_FAST 64 /* ~ 372 bytes on stack */ -static unsigned short sem_seq = 0; +/* + * linked list protection: + * sem_undo.id_next, + * semid_ds.sem_pending{,last}, + * semid_ds.sem_undo: sem_lock() for read/write + * sem_undo.proc_next: only "current" is allowed to read/write that field. + * + */ + +int sem_ctls[4] = {SEMMSL, SEMMNS, SEMOPM, SEMMNI}; +#define sc_semmsl (sem_ctls[0]) +#define sc_semmns (sem_ctls[1]) +#define sc_semopm (sem_ctls[2]) +#define sc_semmni (sem_ctls[3]) + +static int used_sems = 0; void __init sem_init (void) { - int i; -#ifdef CONFIG_PROC_FS - struct proc_dir_entry *ent; -#endif + used_sems = 0; + ipc_init_ids(&sem_ids,sc_semmni); - init_waitqueue_head(&sem_lock); - used_sems = used_semids = max_semid = sem_seq = 0; - for (i = 0; i < SEMMNI; i++) - semary[i] = (struct semid_ds *) IPC_UNUSED; #ifdef CONFIG_PROC_FS - ent = create_proc_entry("sysvipc/sem", 0, 0); - ent->read_proc = sysvipc_sem_read_proc; + create_proc_read_entry("sysvipc/sem", 0, 0, sysvipc_sem_read_proc, NULL); #endif - return; -} - -static int findkey (key_t key) -{ - int id; - struct semid_ds *sma; - - for (id = 0; id <= max_semid; id++) { - while ((sma = semary[id]) == IPC_NOID) - interruptible_sleep_on (&sem_lock); - if (sma == IPC_UNUSED) - continue; - if (key == sma->sem_perm.key) - return id; - } - return -1; } static int newary (key_t key, int nsems, int semflg) { int id; struct semid_ds *sma; - struct ipc_perm *ipcp; int size; if (!nsems) return -EINVAL; - if (used_sems + nsems > SEMMNS) + if (used_sems + nsems > sc_semmns) return -ENOSPC; - for (id = 0; id < SEMMNI; id++) - if (semary[id] == IPC_UNUSED) { - semary[id] = (struct semid_ds *) IPC_NOID; - goto found; - } - return -ENOSPC; -found: + size = sizeof (*sma) + nsems * sizeof (struct sem); - used_sems += nsems; - sma = (struct semid_ds *) kmalloc (size, GFP_KERNEL); + sma = (struct semid_ds *) ipc_alloc(size); if (!sma) { - semary[id] = (struct semid_ds *) IPC_UNUSED; - used_sems -= nsems; - wake_up (&sem_lock); return -ENOMEM; } memset (sma, 0, size); + id = ipc_addid(&sem_ids, &sma->sem_perm, sc_semmni); + if(id == -1) { + ipc_free(sma, size); + return -ENOSPC; + } + used_sems += nsems; + + sma->sem_perm.mode = (semflg & S_IRWXUGO); + sma->sem_perm.key = key; + sma->sem_base = (struct sem *) &sma[1]; - ipcp = &sma->sem_perm; - ipcp->mode = (semflg & S_IRWXUGO); - ipcp->key = key; - ipcp->cuid = ipcp->uid = current->euid; - ipcp->gid = ipcp->cgid = current->egid; - sma->sem_perm.seq = sem_seq; /* sma->sem_pending = NULL; */ sma->sem_pending_last = &sma->sem_pending; /* sma->undo = NULL; */ sma->sem_nsems = nsems; sma->sem_ctime = CURRENT_TIME; - if (id > max_semid) - max_semid = id; - used_semids++; - semary[id] = sma; - wake_up (&sem_lock); - return (unsigned int) sma->sem_perm.seq * SEMMNI + id; + sem_unlock(id); + + return sem_buildid(id, sma->sem_perm.seq); } asmlinkage long sys_semget (key_t key, int nsems, int semflg) @@ -162,12 +152,13 @@ asmlinkage long sys_semget (key_t key, int nsems, int semflg) int id, err = -EINVAL; struct semid_ds *sma; - lock_kernel(); - if (nsems < 0 || nsems > SEMMSL) - goto out; + if (nsems < 0 || nsems > sc_semmsl) + return -EINVAL; + down(&sem_ids.sem); + if (key == IPC_PRIVATE) { err = newary(key, nsems, semflg); - } else if ((id = findkey (key)) == -1) { /* key not used */ + } else if ((id = ipc_findkey(&sem_ids, key)) == -1) { /* key not used */ if (!(semflg & IPC_CREAT)) err = -ENOENT; else @@ -175,42 +166,69 @@ asmlinkage long sys_semget (key_t key, int nsems, int semflg) } else if (semflg & IPC_CREAT && semflg & IPC_EXCL) { err = -EEXIST; } else { - sma = semary[id]; + sma = sem_lock(id); + if(sma==NULL) + BUG(); if (nsems > sma->sem_nsems) err = -EINVAL; else if (ipcperms(&sma->sem_perm, semflg)) err = -EACCES; else - err = (int) sma->sem_perm.seq * SEMMNI + id; + err = sem_buildid(id, sma->sem_perm.seq); + sem_unlock(id); } -out: - unlock_kernel(); + + up(&sem_ids.sem); return err; } +/* doesn't acquire the sem_lock on error! */ +static int sem_revalidate(int semid, struct semid_ds* sma, int nsems, short flg) +{ + struct semid_ds* smanew; + + smanew = sem_lock(semid); + if(smanew==NULL) + return -EIDRM; + if(smanew != sma) + goto out_EIDRM; + if(sem_checkid(sma,semid)) + goto out_EIDRM; + if(sma->sem_nsems != nsems) { +out_EIDRM: + sem_unlock(semid); + return -EIDRM; + } + + if (ipcperms(&sma->sem_perm, flg)) { + sem_unlock(semid); + return -EACCES; + } + return 0; +} /* Manage the doubly linked list sma->sem_pending as a FIFO: * insert new queue elements at the tail sma->sem_pending_last. */ static inline void append_to_queue (struct semid_ds * sma, - struct sem_queue * q) + struct sem_queue * q) { *(q->prev = sma->sem_pending_last) = q; *(sma->sem_pending_last = &q->next) = NULL; } static inline void prepend_to_queue (struct semid_ds * sma, - struct sem_queue * q) + struct sem_queue * q) { - q->next = sma->sem_pending; - *(q->prev = &sma->sem_pending) = q; - if (q->next) - q->next->prev = &q->next; - else /* sma->sem_pending_last == &sma->sem_pending */ - sma->sem_pending_last = &q->next; + q->next = sma->sem_pending; + *(q->prev = &sma->sem_pending) = q; + if (q->next) + q->next->prev = &q->next; + else /* sma->sem_pending_last == &sma->sem_pending */ + sma->sem_pending_last = &q->next; } static inline void remove_from_queue (struct semid_ds * sma, - struct sem_queue * q) + struct sem_queue * q) { *(q->prev) = q->next; if (q->next) @@ -226,8 +244,8 @@ static inline void remove_from_queue (struct semid_ds * sma, */ static int try_atomic_semop (struct semid_ds * sma, struct sembuf * sops, - int nsops, struct sem_undo *un, int pid, - int do_undo) + int nsops, struct sem_undo *un, int pid, + int do_undo) { int result, sem_op; struct sembuf *sop; @@ -251,12 +269,12 @@ static int try_atomic_semop (struct semid_ds * sma, struct sembuf * sops, goto out_of_range; } - if (do_undo) - { - sop--; - result = 0; - goto undo; - } + if (do_undo) + { + sop--; + result = 0; + goto undo; + } sma->sem_otime = CURRENT_TIME; return 0; @@ -272,7 +290,7 @@ would_block: result = 1; undo: - while (sop >= sops) { + while (sop >= sops) { curr = sma->sem_base + sop->sem_num; curr->semval -= sop->sem_op; curr->sempid >>= 16; @@ -293,27 +311,27 @@ static void update_queue (struct semid_ds * sma) int error; struct sem_queue * q; - for (q = sma->sem_pending; q; q = q->next) { - - if (q->status == 1) - return; /* wait for other process */ - - error = try_atomic_semop(sma, q->sops, q->nsops, - q->undo, q->pid, q->alter); - - /* Does q->sleeper still need to sleep? */ - if (error <= 0) { - /* Found one, wake it up */ - wake_up_interruptible(&q->sleeper); - if (error == 0 && q->alter) { - /* if q-> alter let it self try */ - q->status = 1; - return; - } - q->status = error; - remove_from_queue(sma,q); - } - } + for (q = sma->sem_pending; q; q = q->next) { + + if (q->status == 1) + return; /* wait for other process */ + + error = try_atomic_semop(sma, q->sops, q->nsops, + q->undo, q->pid, q->alter); + + /* Does q->sleeper still need to sleep? */ + if (error <= 0) { + /* Found one, wake it up */ + wake_up_process(q->sleeper); + if (error == 0 && q->alter) { + /* if q-> alter let it self try */ + q->status = 1; + return; + } + q->status = error; + remove_from_queue(sma,q); + } + } } /* The following counts are associated to each semaphore: @@ -365,21 +383,16 @@ static int count_semzcnt (struct semid_ds * sma, ushort semnum) /* Free a semaphore set. */ static void freeary (int id) { - struct semid_ds *sma = semary[id]; + struct semid_ds *sma; struct sem_undo *un; struct sem_queue *q; + int size; - /* Invalidate this semaphore set */ - sma->sem_perm.seq++; - sem_seq = (sem_seq+1) % ((unsigned)(1<<31)/SEMMNI); /* increment, but avoid overflow */ - used_sems -= sma->sem_nsems; - if (id == max_semid) - while (max_semid && (semary[--max_semid] == IPC_UNUSED)); - semary[id] = (struct semid_ds *) IPC_UNUSED; - used_semids--; + sma = sem_rmid(id); /* Invalidate the existing undo structures for this semaphore set. - * (They will be freed without any further action in sem_exit().) + * (They will be freed without any further action in sem_exit() + * or during the next semop.) */ for (un = sma->undo; un; un = un->id_next) un->semid = -1; @@ -388,354 +401,503 @@ static void freeary (int id) for (q = sma->sem_pending; q; q = q->next) { q->status = -EIDRM; q->prev = NULL; - wake_up_interruptible(&q->sleeper); /* doesn't sleep! */ + wake_up_process(q->sleeper); /* doesn't sleep */ } + sem_unlock(id); - kfree(sma); + used_sems -= sma->sem_nsems; + size = sizeof (*sma) + sma->sem_nsems * sizeof (struct sem); + ipc_free(sma, size); } -asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg) +int semctl_nolock(int semid, int semnum, int cmd, union semun arg) { - struct semid_ds *buf = NULL; - struct semid_ds tbuf; - int i, id, val = 0; - struct semid_ds *sma; - struct ipc_perm *ipcp; - struct sem *curr = NULL; - struct sem_undo *un; - unsigned int nsems; - ushort *array = NULL; - ushort sem_io[SEMMSL]; int err = -EINVAL; - lock_kernel(); - if (semid < 0 || semnum < 0 || cmd < 0) - goto out; - - switch (cmd) { + switch(cmd) { case IPC_INFO: case SEM_INFO: { - struct seminfo seminfo, *tmp = arg.__buf; - seminfo.semmni = SEMMNI; - seminfo.semmns = SEMMNS; - seminfo.semmsl = SEMMSL; - seminfo.semopm = SEMOPM; + struct seminfo seminfo; + int max_id; + + memset(&seminfo,0,sizeof(seminfo)); + seminfo.semmni = sc_semmni; + seminfo.semmns = sc_semmns; + seminfo.semmsl = sc_semmsl; + seminfo.semopm = sc_semopm; seminfo.semvmx = SEMVMX; seminfo.semmnu = SEMMNU; seminfo.semmap = SEMMAP; seminfo.semume = SEMUME; - seminfo.semusz = SEMUSZ; - seminfo.semaem = SEMAEM; + down(&sem_ids.sem); if (cmd == SEM_INFO) { - seminfo.semusz = used_semids; + seminfo.semusz = sem_ids.in_use; seminfo.semaem = used_sems; + } else { + seminfo.semusz = SEMUSZ; + seminfo.semaem = SEMAEM; } - err = -EFAULT; - if (copy_to_user (tmp, &seminfo, sizeof(struct seminfo))) - goto out; - err = max_semid; - goto out; + max_id = sem_ids.max_id; + up(&sem_ids.sem); + if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo))) + return -EFAULT; + return (max_id < 0) ? 0: max_id; } - case SEM_STAT: - buf = arg.buf; - err = -EINVAL; - if (semid > max_semid) - goto out; - sma = semary[semid]; - if (sma == IPC_UNUSED || sma == IPC_NOID) - goto out; + { + struct semid_ds *sma; + struct semid_ds tbuf; + int id; + + if(semid > sem_ids.size) + return -EINVAL; + + sma = sem_lock(semid); + if(sma == NULL) + return -EINVAL; + err = -EACCES; if (ipcperms (&sma->sem_perm, S_IRUGO)) - goto out; - id = (unsigned int) sma->sem_perm.seq * SEMMNI + semid; + goto out_unlock; + id = sem_buildid(semid, sma->sem_perm.seq); + + memset(&tbuf,0,sizeof(tbuf)); tbuf.sem_perm = sma->sem_perm; tbuf.sem_otime = sma->sem_otime; tbuf.sem_ctime = sma->sem_ctime; tbuf.sem_nsems = sma->sem_nsems; - err = -EFAULT; - if (copy_to_user (buf, &tbuf, sizeof(*buf)) == 0) - err = id; - goto out; + sem_unlock(semid); + if (copy_to_user (arg.buf, &tbuf, sizeof(tbuf))) + return -EFAULT; + return id; + } + default: + return -EINVAL; } + return err; +out_unlock: + sem_unlock(semid); + return err; +} - id = (unsigned int) semid % SEMMNI; - sma = semary [id]; - err = -EINVAL; - if (sma == IPC_UNUSED || sma == IPC_NOID) - goto out; - ipcp = &sma->sem_perm; - nsems = sma->sem_nsems; - err = -EIDRM; - if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI) - goto out; +int semctl_main(int semid, int semnum, int cmd, union semun arg) +{ + struct semid_ds *sma; + struct sem* curr; + int err; + ushort fast_sem_io[SEMMSL_FAST]; + ushort* sem_io = fast_sem_io; + int nsems; + + sma = sem_lock(semid); + if(sma==NULL) + return -EINVAL; - switch (cmd) { - case GETVAL: - case GETPID: - case GETNCNT: - case GETZCNT: - case SETVAL: - err = -EINVAL; - if (semnum >= nsems) - goto out; - curr = &sma->sem_base[semnum]; - break; - } + err=-EIDRM; + if (sem_checkid(sma,semid)) + goto out_unlock; + + err = -EACCES; + if (ipcperms (&sma->sem_perm, (cmd==SETVAL||cmd==SETALL)?S_IWUGO:S_IRUGO)) + goto out_unlock; + nsems = sma->sem_nsems; switch (cmd) { - case GETVAL: - case GETPID: - case GETNCNT: - case GETZCNT: case GETALL: - err = -EACCES; - if (ipcperms (ipcp, S_IRUGO)) - goto out; - switch (cmd) { - case GETVAL : err = curr->semval; goto out; - case GETPID : err = curr->sempid & 0xffff; goto out; - case GETNCNT: err = count_semncnt(sma,semnum); goto out; - case GETZCNT: err = count_semzcnt(sma,semnum); goto out; - case GETALL: - array = arg.array; - break; - } - break; - case SETVAL: - val = arg.val; - err = -ERANGE; - if (val > SEMVMX || val < 0) - goto out; - break; - case IPC_RMID: - if (current->euid == ipcp->cuid || - current->euid == ipcp->uid || capable(CAP_SYS_ADMIN)) { - freeary (id); - err = 0; - goto out; + { + ushort *array = arg.array; + int i; + + if(nsems > SEMMSL_FAST) { + sem_unlock(semid); + sem_io = ipc_alloc(sizeof(ushort)*nsems); + if(sem_io == NULL) + return -ENOMEM; + err = sem_revalidate(semid, sma, nsems, S_IRUGO); + if(err) + goto out_free; } - err = -EPERM; - goto out; - case SETALL: /* arg is a pointer to an array of ushort */ - array = arg.array; - err = -EFAULT; - if (copy_from_user (sem_io, array, nsems*sizeof(ushort))) - goto out; + + for (i = 0; i < sma->sem_nsems; i++) + sem_io[i] = sma->sem_base[i].semval; + sem_unlock(semid); err = 0; - for (i = 0; i < nsems; i++) + if(copy_to_user(array, sem_io, nsems*sizeof(ushort))) + err = -EFAULT; + goto out_free; + } + case SETALL: + { + int i; + struct sem_undo *un; + + sem_unlock(semid); + + if(nsems > SEMMSL_FAST) { + sem_io = ipc_alloc(sizeof(ushort)*nsems); + if(sem_io == NULL) + return -ENOMEM; + } + + if (copy_from_user (sem_io, arg.array, nsems*sizeof(ushort))) { + err = -EFAULT; + goto out_free; + } + + for (i = 0; i < nsems; i++) { if (sem_io[i] > SEMVMX) { err = -ERANGE; - goto out; + goto out_free; } - break; + } + err = sem_revalidate(semid, sma, nsems, S_IWUGO); + if(err) + goto out_free; + + for (i = 0; i < nsems; i++) + sma->sem_base[i].semval = sem_io[i]; + for (un = sma->undo; un; un = un->id_next) + for (i = 0; i < nsems; i++) + un->semadj[i] = 0; + sma->sem_ctime = CURRENT_TIME; + /* maybe some queued-up processes were waiting for this */ + update_queue(sma); + err = 0; + goto out_unlock; + } case IPC_STAT: - buf = arg.buf; - break; - case IPC_SET: - buf = arg.buf; - err = copy_from_user (&tbuf, buf, sizeof (*buf)); - if (err) - err = -EFAULT; - break; + { + struct semid_ds tbuf; + memset(&tbuf,0,sizeof(tbuf)); + tbuf.sem_perm = sma->sem_perm; + tbuf.sem_otime = sma->sem_otime; + tbuf.sem_ctime = sma->sem_ctime; + tbuf.sem_nsems = sma->sem_nsems; + sem_unlock(semid); + if (copy_to_user (arg.buf, &tbuf, sizeof(tbuf))) + return -EFAULT; + return 0; } + /* GETVAL, GETPID, GETNCTN, GETZCNT, SETVAL: fall-through */ + } + err = -EINVAL; + if(semnum < 0 || semnum >= nsems) + goto out_unlock; - err = -EIDRM; - if (semary[id] == IPC_UNUSED || semary[id] == IPC_NOID) - goto out; - if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI) - goto out; + curr = &sma->sem_base[semnum]; switch (cmd) { - case GETALL: - err = -EACCES; - if (ipcperms (ipcp, S_IRUGO)) - goto out; - for (i = 0; i < sma->sem_nsems; i++) - sem_io[i] = sma->sem_base[i].semval; - if (copy_to_user (array, sem_io, nsems*sizeof(ushort))) - err = -EFAULT; - break; + case GETVAL: + err = curr->semval; + goto out_unlock; + case GETPID: + err = curr->sempid & 0xffff; + goto out_unlock; + case GETNCNT: + err = count_semncnt(sma,semnum); + goto out_unlock; + case GETZCNT: + err = count_semzcnt(sma,semnum); + goto out_unlock; case SETVAL: - err = -EACCES; - if (ipcperms (ipcp, S_IWUGO)) - goto out; + { + int val = arg.val; + struct sem_undo *un; + err = -ERANGE; + if (val > SEMVMX || val < 0) + goto out_unlock; + for (un = sma->undo; un; un = un->id_next) un->semadj[semnum] = 0; curr->semval = val; sma->sem_ctime = CURRENT_TIME; /* maybe some queued-up processes were waiting for this */ update_queue(sma); + err = 0; + goto out_unlock; + } + } +out_unlock: + sem_unlock(semid); +out_free: + if(sem_io != fast_sem_io) + ipc_free(sem_io, sizeof(ushort)*nsems); + return err; +} + +int semctl_down(int semid, int semnum, int cmd, union semun arg) +{ + struct semid_ds *sma; + int err; + struct semid_ds tbuf; + struct ipc_perm *ipcp; + + if(cmd == IPC_SET) { + if(copy_from_user (&tbuf, arg.buf, sizeof (tbuf))) + return -EFAULT; + } + sma = sem_lock(semid); + if(sma==NULL) + return -EINVAL; + + if (sem_checkid(sma,semid)) { + err=-EIDRM; + goto out_unlock; + } + ipcp = &sma->sem_perm; + + if (current->euid != ipcp->cuid && + current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) { + err=-EPERM; + goto out_unlock; + } + + switch(cmd){ + case IPC_RMID: + freeary(semid); + err = 0; break; case IPC_SET: - if (current->euid == ipcp->cuid || - current->euid == ipcp->uid || capable(CAP_SYS_ADMIN)) { - ipcp->uid = tbuf.sem_perm.uid; - ipcp->gid = tbuf.sem_perm.gid; - ipcp->mode = (ipcp->mode & ~S_IRWXUGO) + ipcp->uid = tbuf.sem_perm.uid; + ipcp->gid = tbuf.sem_perm.gid; + ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | (tbuf.sem_perm.mode & S_IRWXUGO); - sma->sem_ctime = CURRENT_TIME; - err = 0; - goto out; - } - err = -EPERM; - goto out; - case IPC_STAT: - err = -EACCES; - if (ipcperms (ipcp, S_IRUGO)) - goto out; - tbuf.sem_perm = sma->sem_perm; - tbuf.sem_otime = sma->sem_otime; - tbuf.sem_ctime = sma->sem_ctime; - tbuf.sem_nsems = sma->sem_nsems; - if (copy_to_user (buf, &tbuf, sizeof(*buf))) - err = -EFAULT; - break; - case SETALL: - err = -EACCES; - if (ipcperms (ipcp, S_IWUGO)) - goto out; - for (i = 0; i < nsems; i++) - sma->sem_base[i].semval = sem_io[i]; - for (un = sma->undo; un; un = un->id_next) - for (i = 0; i < nsems; i++) - un->semadj[i] = 0; sma->sem_ctime = CURRENT_TIME; - /* maybe some queued-up processes were waiting for this */ - update_queue(sma); + sem_unlock(semid); + err = 0; break; default: + sem_unlock(semid); err = -EINVAL; - goto out; + break; } - err = 0; -out: - unlock_kernel(); + return err; + +out_unlock: + sem_unlock(semid); return err; } +asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg) +{ + int err = -EINVAL; + + if (semid < 0) + return -EINVAL; + + switch(cmd) { + case IPC_INFO: + case SEM_INFO: + case SEM_STAT: + err = semctl_nolock(semid,semnum,cmd,arg); + return err; + case GETALL: + case GETVAL: + case GETPID: + case GETNCNT: + case GETZCNT: + case IPC_STAT: + case SETVAL: + case SETALL: + err = semctl_main(semid,semnum,cmd,arg); + return err; + case IPC_RMID: + case IPC_SET: + down(&sem_ids.sem); + err = semctl_down(semid,semnum,cmd,arg); + up(&sem_ids.sem); + return err; + default: + return -EINVAL; + } +} + +static struct sem_undo* freeundos(struct semid_ds *sma, struct sem_undo* un) +{ + struct sem_undo* u; + struct sem_undo** up; + + for(up = ¤t->semundo;(u=*up);up=&u->proc_next) { + if(un==u) { + un=u->proc_next; + *up=un; + kfree(u); + return un; + } + } + printk ("freeundos undo list error id=%d\n", un->semid); + return un->proc_next; +} + +/* returns without sem_lock on error! */ +static int alloc_undo(struct semid_ds *sma, struct sem_undo** unp, int semid, int alter) +{ + int size, nsems, error; + struct sem_undo *un; + + nsems = sma->sem_nsems; + size = sizeof(struct sem_undo) + sizeof(short)*nsems; + sem_unlock(semid); + + un = (struct sem_undo *) kmalloc(size, GFP_KERNEL); + if (!un) + return -ENOMEM; + + memset(un, 0, size); + error = sem_revalidate(semid, sma, nsems, alter ? S_IWUGO : S_IRUGO); + if(error) { + kfree(un); + return error; + } + + un->semadj = (short *) &un[1]; + un->semid = semid; + un->proc_next = current->semundo; + current->semundo = un; + un->id_next = sma->undo; + sma->undo = un; + *unp = un; + return 0; +} + asmlinkage long sys_semop (int semid, struct sembuf *tsops, unsigned nsops) { - int id, size, error = -EINVAL; + int error = -EINVAL; struct semid_ds *sma; - struct sembuf sops[SEMOPM], *sop; + struct sembuf fast_sops[SEMOPM_FAST]; + struct sembuf* sops = fast_sops, *sop; struct sem_undo *un; int undos = 0, decrease = 0, alter = 0; struct sem_queue queue; - lock_kernel(); if (nsops < 1 || semid < 0) - goto out; - error = -E2BIG; - if (nsops > SEMOPM) - goto out; - error = -EFAULT; - if (copy_from_user (sops, tsops, nsops * sizeof(*tsops))) - goto out; - id = (unsigned int) semid % SEMMNI; - error = -EINVAL; - if ((sma = semary[id]) == IPC_UNUSED || sma == IPC_NOID) - goto out; + return -EINVAL; + if (nsops > sc_semopm) + return -E2BIG; + if(nsops > SEMOPM_FAST) { + sops = kmalloc(sizeof(*sops)*nsops,GFP_KERNEL); + if(sops==NULL) + return -ENOMEM; + } + if (copy_from_user (sops, tsops, nsops * sizeof(*tsops))) { + error=-EFAULT; + goto out_free; + } + sma = sem_lock(semid); + error=-EINVAL; + if(sma==NULL) + goto out_free; error = -EIDRM; - if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI) - goto out; - - error = -EFBIG; + if (sem_checkid(sma,semid)) + goto out_unlock_free; + error = -EFBIG; for (sop = sops; sop < sops + nsops; sop++) { if (sop->sem_num >= sma->sem_nsems) - goto out; + goto out_unlock_free; if (sop->sem_flg & SEM_UNDO) undos++; if (sop->sem_op < 0) decrease = 1; - if (sop->sem_op > 0) - alter = 1; + if (sop->sem_op > 0) + alter = 1; } - alter |= decrease; + alter |= decrease; error = -EACCES; if (ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO)) - goto out; + goto out_unlock_free; if (undos) { /* Make sure we have an undo structure * for this process and this semaphore set. */ - for (un = current->semundo; un; un = un->proc_next) - if (un->semid == semid) + un=current->semundo; + while(un != NULL) { + if(un->semid==semid) break; + if(un->semid==-1) + un=freeundos(sma,un); + else + un=un->proc_next; + } if (!un) { - size = sizeof(struct sem_undo) + sizeof(short)*sma->sem_nsems; - un = (struct sem_undo *) kmalloc(size, GFP_ATOMIC); - if (!un) { - error = -ENOMEM; - goto out; - } - memset(un, 0, size); - un->semadj = (short *) &un[1]; - un->semid = semid; - un->proc_next = current->semundo; - current->semundo = un; - un->id_next = sma->undo; - sma->undo = un; + error = alloc_undo(sma,&un,semid,alter); + if(error) + goto out_free; } } else un = NULL; error = try_atomic_semop (sma, sops, nsops, un, current->pid, 0); if (error <= 0) - goto update; - - /* We need to sleep on this operation, so we put the current - * task into the pending queue and go to sleep. - */ - - queue.sma = sma; - queue.sops = sops; - queue.nsops = nsops; - queue.undo = un; - queue.pid = current->pid; - queue.alter = decrease; - current->semsleeping = &queue; - if (alter) - append_to_queue(sma ,&queue); - else - prepend_to_queue(sma ,&queue); - - for (;;) { - queue.status = -EINTR; - init_waitqueue_head(&queue.sleeper); - interruptible_sleep_on(&queue.sleeper); - - /* - * If queue.status == 1 we where woken up and - * have to retry else we simply return. - * If an interrupt occurred we have to clean up the - * queue - * - */ - if (queue.status == 1) - { - error = try_atomic_semop (sma, sops, nsops, un, - current->pid,0); - if (error <= 0) - break; - } else { - error = queue.status;; - if (queue.prev) /* got Interrupt */ - break; - /* Everything done by update_queue */ - current->semsleeping = NULL; - goto out; - } - } - current->semsleeping = NULL; - remove_from_queue(sma,&queue); + goto update; + + /* We need to sleep on this operation, so we put the current + * task into the pending queue and go to sleep. + */ + + queue.sma = sma; + queue.sops = sops; + queue.nsops = nsops; + queue.undo = un; + queue.pid = current->pid; + queue.alter = decrease; + queue.id = semid; + if (alter) + append_to_queue(sma ,&queue); + else + prepend_to_queue(sma ,&queue); + current->semsleeping = &queue; + + for (;;) { + struct semid_ds* tmp; + queue.status = -EINTR; + queue.sleeper = current; + current->state = TASK_INTERRUPTIBLE; + sem_unlock(semid); + + schedule(); + + tmp = sem_lock(semid); + if(tmp==NULL) { + if(queue.status != -EIDRM) + BUG(); + current->semsleeping = NULL; + error = -EIDRM; + goto out_free; + } + /* + * If queue.status == 1 we where woken up and + * have to retry else we simply return. + * If an interrupt occurred we have to clean up the + * queue + * + */ + if (queue.status == 1) + { + error = try_atomic_semop (sma, sops, nsops, un, + current->pid,0); + if (error <= 0) + break; + } else { + error = queue.status; + if (queue.prev) /* got Interrupt */ + break; + /* Everything done by update_queue */ + current->semsleeping = NULL; + goto out_unlock_free; + } + } + current->semsleeping = NULL; + remove_from_queue(sma,&queue); update: - if (alter) - update_queue (sma); -out: - unlock_kernel(); + if (alter) + update_queue (sma); +out_unlock_free: + sem_unlock(semid); +out_free: + if(sops != fast_sops) + kfree(sops); return error; } @@ -762,26 +924,40 @@ void sem_exit (void) * remove it from the queue. */ if ((q = current->semsleeping)) { - if (q->prev) - remove_from_queue(q->sma,q); + int semid = q->id; + sma = sem_lock(semid); current->semsleeping = NULL; + + if (q->prev) { + if(sma==NULL) + BUG(); + remove_from_queue(q->sma,q); + } + if(sma!=NULL) + sem_unlock(semid); } for (up = ¤t->semundo; (u = *up); *up = u->proc_next, kfree(u)) { - if (u->semid == -1) - continue; - sma = semary[(unsigned int) u->semid % SEMMNI]; - if (sma == IPC_UNUSED || sma == IPC_NOID) + int semid = u->semid; + if(semid == -1) continue; - if (sma->sem_perm.seq != (unsigned int) u->semid / SEMMNI) + sma = sem_lock(semid); + if (sma == NULL) continue; + + if (u->semid == -1) + goto next_entry; + + if (sem_checkid(sma,u->semid)) + goto next_entry; + /* remove u from the sma->undo list */ for (unp = &sma->undo; (un = *unp); unp = &un->id_next) { if (u == un) goto found; } printk ("sem_exit undo list error id=%d\n", u->semid); - break; + goto next_entry; found: *unp = un->id_next; /* perform adjustments registered in u */ @@ -796,6 +972,8 @@ found: sma->sem_otime = CURRENT_TIME; /* maybe some queued-up processes were waiting for this */ update_queue(sma); +next_entry: + sem_unlock(semid); } current->semundo = NULL; } @@ -808,31 +986,37 @@ static int sysvipc_sem_read_proc(char *buffer, char **start, off_t offset, int l int i, len = 0; len += sprintf(buffer, " key semid perms nsems uid gid cuid cgid otime ctime\n"); + down(&sem_ids.sem); - for(i = 0; i < SEMMNI; i++) - if(semary[i] != IPC_UNUSED) { + for(i = 0; i <= sem_ids.max_id; i++) { + struct semid_ds *sma; + sma = sem_lock(i); + if(sma) { len += sprintf(buffer + len, "%10d %10d %4o %5u %5u %5u %5u %5u %10lu %10lu\n", - semary[i]->sem_perm.key, - semary[i]->sem_perm.seq * SEMMNI + i, - semary[i]->sem_perm.mode, - semary[i]->sem_nsems, - semary[i]->sem_perm.uid, - semary[i]->sem_perm.gid, - semary[i]->sem_perm.cuid, - semary[i]->sem_perm.cgid, - semary[i]->sem_otime, - semary[i]->sem_ctime); + sma->sem_perm.key, + sem_buildid(i,sma->sem_perm.seq), + sma->sem_perm.mode, + sma->sem_nsems, + sma->sem_perm.uid, + sma->sem_perm.gid, + sma->sem_perm.cuid, + sma->sem_perm.cgid, + sma->sem_otime, + sma->sem_ctime); + sem_unlock(i); pos += len; if(pos < offset) { len = 0; - begin = pos; + begin = pos; } if(pos > offset + length) goto done; } + } *eof = 1; done: + up(&sem_ids.sem); *start = buffer + (offset - begin); len -= (offset - begin); if(len > length) |