/*
       * linux/ipc/sem.c
       * Copyright (C) 1992 Krishna Balasubramanian
       * Copyright (C) 1995 Eric Schenk, Bruno Haible
       *
       * IMPLEMENTATION NOTES ON CODE REWRITE (Eric Schenk, January 1995):
       * This code underwent a massive rewrite in order to solve some problems
       * with the original code. In particular the original code failed to
       * wake up processes that were waiting for semval to go to 0 if the
       * value went to 0 and was then incremented rapidly enough. In solving
       * this problem I have also modified the implementation so that it
       * processes pending operations in a FIFO manner, thus give a guarantee
       * that processes waiting for a lock on the semaphore won't starve
       * unless another locking process fails to unlock.
       * In addition the following two changes in behavior have been introduced:
       * - The original implementation of semop returned the value
       *   last semaphore element examined on success. This does not
       *   match the manual page specifications, and effectively
       *   allows the user to read the semaphore even if they do not
       *   have read permissions. The implementation now returns 0
       *   on success as stated in the manual page.
       * - There is some confusion over whether the set of undo adjustments
       *   to be performed at exit should be done in an atomic manner.
       *   That is, if we are attempting to decrement the semval should we queue
       *   up and wait until we can do so legally?
       *   The original implementation attempted to do this.
       *   The current implementation does not do so. This is because I don't
       *   think it is the right thing (TM) to do, and because I couldn't
       *   see a clean way to get the old behavior with the new design.
       *   The POSIX standard and SVID should be consulted to determine
       *   what behavior is mandated.
       *
       * Further notes on refinement (Christoph Rohland, December 1998):
       * - The POSIX standard says, that the undo adjustments simply should
       *   redo. So the current implementation is o.K.
       * - The previous code had two flaws:
       *   1) It actively gave the semaphore to the next waiting process
       *      sleeping on the semaphore. Since this process did not have the
       *      cpu this led to many unnecessary context switches and bad
       *      performance. Now we only check which process should be able to
       *      get the semaphore and if this process wants to reduce some
       *      semaphore value we simply wake it up without doing the
       *      operation. So it has to try to get it later. Thus e.g. the
       *      running process may reacquire the semaphore during the current
       *      time slice. If it only waits for zero or increases the semaphore,
       *      we do the operation in advance and wake it up.
       *   2) It did not wake up all zero waiting processes. We try to do
       *      better but only get the semops right which only wait for zero or
       *      increase. If there are decrement operations in the operations
       *      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/spinlock.h>
      #include <linux/init.h>
      #include <linux/proc_fs.h>
      #include <asm/uaccess.h>
      #include "util.h"
      
      
      #define sem_lock(id)	((struct sem_array*)ipc_lock(&sem_ids,id))
      #define sem_unlock(id)	ipc_unlock(&sem_ids,id)
      #define sem_rmid(id)	((struct sem_array*)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;
      
      static int newary (key_t, int, int);
      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
      
      #define SEMMSL_FAST	256 /* 512 bytes on stack */
      #define SEMOPM_FAST	64  /* ~ 372 bytes on stack */
      
      /*
       * linked list protection:
       *	sem_undo.id_next,
       *	sem_array.sem_pending{,last},
       *	sem_array.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;
      
 102  void __init sem_init (void)
      {
      	used_sems = 0;
      	ipc_init_ids(&sem_ids,sc_semmni);
      
      #ifdef CONFIG_PROC_FS
      	create_proc_read_entry("sysvipc/sem", 0, 0, sysvipc_sem_read_proc, NULL);
      #endif
      }
      
 112  static int newary (key_t key, int nsems, int semflg)
      {
      	int id;
      	struct sem_array *sma;
      	int size;
      
 118  	if (!nsems)
 119  		return -EINVAL;
 120  	if (used_sems + nsems > sc_semmns)
 121  		return -ENOSPC;
      
      	size = sizeof (*sma) + nsems * sizeof (struct sem);
      	sma = (struct sem_array *) ipc_alloc(size);
 125  	if (!sma) {
 126  		return -ENOMEM;
      	}
      	memset (sma, 0, size);
      	id = ipc_addid(&sem_ids, &sma->sem_perm, sc_semmni);
 130  	if(id == -1) {
      		ipc_free(sma, size);
 132  		return -ENOSPC;
      	}
      	used_sems += nsems;
      
      	sma->sem_perm.mode = (semflg & S_IRWXUGO);
      	sma->sem_perm.key = key;
      
      	sma->sem_base = (struct sem *) &sma[1];
      	/* sma->sem_pending = NULL; */
      	sma->sem_pending_last = &sma->sem_pending;
      	/* sma->undo = NULL; */
      	sma->sem_nsems = nsems;
      	sma->sem_ctime = CURRENT_TIME;
      	sem_unlock(id);
      
 147  	return sem_buildid(id, sma->sem_perm.seq);
      }
      
 150  asmlinkage long sys_semget (key_t key, int nsems, int semflg)
      {
      	int id, err = -EINVAL;
      	struct sem_array *sma;
      
 155  	if (nsems < 0 || nsems > sc_semmsl)
 156  		return -EINVAL;
      	down(&sem_ids.sem);
      	
 159  	if (key == IPC_PRIVATE) {
      		err = newary(key, nsems, semflg);
 161  	} else if ((id = ipc_findkey(&sem_ids, key)) == -1) {  /* key not used */
 162  		if (!(semflg & IPC_CREAT))
      			err = -ENOENT;
 164  		else
      			err = newary(key, nsems, semflg);
 166  	} else if (semflg & IPC_CREAT && semflg & IPC_EXCL) {
      		err = -EEXIST;
 168  	} else {
      		sma = sem_lock(id);
 170  		if(sma==NULL)
 171  			BUG();
 172  		if (nsems > sma->sem_nsems)
      			err = -EINVAL;
 174  		else if (ipcperms(&sma->sem_perm, semflg))
      			err = -EACCES;
 176  		else
      			err = sem_buildid(id, sma->sem_perm.seq);
      		sem_unlock(id);
      	}
      
      	up(&sem_ids.sem);
 182  	return err;
      }
      
      /* doesn't acquire the sem_lock on error! */
 186  static int sem_revalidate(int semid, struct sem_array* sma, int nsems, short flg)
      {
      	struct sem_array* smanew;
      
      	smanew = sem_lock(semid);
 191  	if(smanew==NULL)
 192  		return -EIDRM;
 193  	if(smanew != sma || sem_checkid(sma,semid) || sma->sem_nsems != nsems) {
      		sem_unlock(semid);
 195  		return -EIDRM;
      	}
      
 198  	if (ipcperms(&sma->sem_perm, flg)) {
      		sem_unlock(semid);
 200  		return -EACCES;
      	}
 202  	return 0;
      }
      /* Manage the doubly linked list sma->sem_pending as a FIFO:
       * insert new queue elements at the tail sma->sem_pending_last.
       */
 207  static inline void append_to_queue (struct sem_array * sma,
      				    struct sem_queue * q)
      {
      	*(q->prev = sma->sem_pending_last) = q;
      	*(sma->sem_pending_last = &q->next) = NULL;
      }
      
 214  static inline void prepend_to_queue (struct sem_array * sma,
      				     struct sem_queue * q)
      {
      	q->next = sma->sem_pending;
      	*(q->prev = &sma->sem_pending) = q;
 219  	if (q->next)
      		q->next->prev = &q->next;
 221  	else /* sma->sem_pending_last == &sma->sem_pending */
      		sma->sem_pending_last = &q->next;
      }
      
 225  static inline void remove_from_queue (struct sem_array * sma,
      				      struct sem_queue * q)
      {
      	*(q->prev) = q->next;
 229  	if (q->next)
      		q->next->prev = q->prev;
 231  	else /* sma->sem_pending_last == &q->next */
      		sma->sem_pending_last = q->prev;
      	q->prev = NULL; /* mark as removed */
      }
      
      /*
       * Determine whether a sequence of semaphore operations would succeed
       * all at once. Return 0 if yes, 1 if need to sleep, else return error code.
       */
      
 241  static int try_atomic_semop (struct sem_array * sma, struct sembuf * sops,
      			     int nsops, struct sem_undo *un, int pid,
      			     int do_undo)
      {
      	int result, sem_op;
      	struct sembuf *sop;
      	struct sem * curr;
      
 249  	for (sop = sops; sop < sops + nsops; sop++) {
      		curr = sma->sem_base + sop->sem_num;
      		sem_op = sop->sem_op;
      
 253  		if (!sem_op && curr->semval)
 254  			goto would_block;
      
      		curr->sempid = (curr->sempid << 16) | pid;
      		curr->semval += sem_op;
 258  		if (sop->sem_flg & SEM_UNDO)
      			un->semadj[sop->sem_num] -= sem_op;
      
 261  		if (curr->semval < 0)
 262  			goto would_block;
 263  		if (curr->semval > SEMVMX)
 264  			goto out_of_range;
      	}
      
 267  	if (do_undo)
      	{
      		sop--;
      		result = 0;
 271  		goto undo;
      	}
      
      	sma->sem_otime = CURRENT_TIME;
 275  	return 0;
      
      out_of_range:
      	result = -ERANGE;
 279  	goto undo;
      
      would_block:
 282  	if (sop->sem_flg & IPC_NOWAIT)
      		result = -EAGAIN;
 284  	else
      		result = 1;
      
      undo:
 288  	while (sop >= sops) {
      		curr = sma->sem_base + sop->sem_num;
      		curr->semval -= sop->sem_op;
      		curr->sempid >>= 16;
      
 293  		if (sop->sem_flg & SEM_UNDO)
      			un->semadj[sop->sem_num] += sop->sem_op;
      		sop--;
      	}
      
 298  	return result;
      }
      
      /* Go through the pending queue for the indicated semaphore
       * looking for tasks that can be completed.
       */
 304  static void update_queue (struct sem_array * sma)
      {
      	int error;
      	struct sem_queue * q;
      
 309  	for (q = sma->sem_pending; q; q = q->next) {
      			
 311  		if (q->status == 1)
 312  			continue;	/* this one was woken up before */
      
      		error = try_atomic_semop(sma, q->sops, q->nsops,
      					 q->undo, q->pid, q->alter);
      
      		/* Does q->sleeper still need to sleep? */
 318  		if (error <= 0) {
      				/* Found one, wake it up */
      			wake_up_process(q->sleeper);
 321  			if (error == 0 && q->alter) {
      				/* if q-> alter let it self try */
      				q->status = 1;
 324  				return;
      			}
      			q->status = error;
      			remove_from_queue(sma,q);
      		}
      	}
      }
      
      /* The following counts are associated to each semaphore:
       *   semncnt        number of tasks waiting on semval being nonzero
       *   semzcnt        number of tasks waiting on semval being zero
       * This model assumes that a task waits on exactly one semaphore.
       * Since semaphore operations are to be performed atomically, tasks actually
       * wait on a whole sequence of semaphores simultaneously.
       * The counts we return here are a rough approximation, but still
       * warrant that semncnt+semzcnt>0 if the task is on the pending queue.
       */
 341  static int count_semncnt (struct sem_array * sma, ushort semnum)
      {
      	int semncnt;
      	struct sem_queue * q;
      
      	semncnt = 0;
 347  	for (q = sma->sem_pending; q; q = q->next) {
      		struct sembuf * sops = q->sops;
      		int nsops = q->nsops;
      		int i;
 351  		for (i = 0; i < nsops; i++)
      			if (sops[i].sem_num == semnum
      			    && (sops[i].sem_op < 0)
 354  			    && !(sops[i].sem_flg & IPC_NOWAIT))
      				semncnt++;
      	}
 357  	return semncnt;
      }
 359  static int count_semzcnt (struct sem_array * sma, ushort semnum)
      {
      	int semzcnt;
      	struct sem_queue * q;
      
      	semzcnt = 0;
 365  	for (q = sma->sem_pending; q; q = q->next) {
      		struct sembuf * sops = q->sops;
      		int nsops = q->nsops;
      		int i;
 369  		for (i = 0; i < nsops; i++)
      			if (sops[i].sem_num == semnum
      			    && (sops[i].sem_op == 0)
 372  			    && !(sops[i].sem_flg & IPC_NOWAIT))
      				semzcnt++;
      	}
 375  	return semzcnt;
      }
      
      /* Free a semaphore set. */
 379  static void freeary (int id)
      {
      	struct sem_array *sma;
      	struct sem_undo *un;
      	struct sem_queue *q;
      	int size;
      
      	sma = sem_rmid(id);
      
      	/* Invalidate the existing undo structures for this semaphore set.
      	 * (They will be freed without any further action in sem_exit()
      	 * or during the next semop.)
      	 */
 392  	for (un = sma->undo; un; un = un->id_next)
      		un->semid = -1;
      
      	/* Wake up all pending processes and let them fail with EIDRM. */
 396  	for (q = sma->sem_pending; q; q = q->next) {
      		q->status = -EIDRM;
      		q->prev = NULL;
      		wake_up_process(q->sleeper); /* doesn't sleep */
      	}
      	sem_unlock(id);
      
      	used_sems -= sma->sem_nsems;
      	size = sizeof (*sma) + sma->sem_nsems * sizeof (struct sem);
      	ipc_free(sma, size);
      }
      
 408  static unsigned long copy_semid_to_user(void *buf, struct semid64_ds *in, int version)
      {
 410  	switch(version) {
 411  	case IPC_64:
 412  		return copy_to_user(buf, in, sizeof(*in));
 413  	case IPC_OLD:
      	    {
      		struct semid_ds out;
      
      		ipc64_perm_to_ipc_perm(&in->sem_perm, &out.sem_perm);
      
      		out.sem_otime	= in->sem_otime;
      		out.sem_ctime	= in->sem_ctime;
      		out.sem_nsems	= in->sem_nsems;
      
 423  		return copy_to_user(buf, &out, sizeof(out));
      	    }
 425  	default:
 426  		return -EINVAL;
      	}
      }
      
 430  int semctl_nolock(int semid, int semnum, int cmd, int version, union semun arg)
      {
      	int err = -EINVAL;
      
 434  	switch(cmd) {
 435  	case IPC_INFO:
 436  	case SEM_INFO:
      	{
      		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;
      		down(&sem_ids.sem);
 451  		if (cmd == SEM_INFO) {
      			seminfo.semusz = sem_ids.in_use;
      			seminfo.semaem = used_sems;
 454  		} else {
      			seminfo.semusz = SEMUSZ;
      			seminfo.semaem = SEMAEM;
      		}
      		max_id = sem_ids.max_id;
      		up(&sem_ids.sem);
 460  		if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo))) 
 461  			return -EFAULT;
 462  		return (max_id < 0) ? 0: max_id;
      	}
 464  	case SEM_STAT:
      	{
      		struct sem_array *sma;
      		struct semid64_ds tbuf;
      		int id;
      
 470  		if(semid > sem_ids.size)
 471  			return -EINVAL;
      
      		memset(&tbuf,0,sizeof(tbuf));
      
      		sma = sem_lock(semid);
 476  		if(sma == NULL)
 477  			return -EINVAL;
      
      		err = -EACCES;
 480  		if (ipcperms (&sma->sem_perm, S_IRUGO))
 481  			goto out_unlock;
      		id = sem_buildid(semid, sma->sem_perm.seq);
      
      		kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
      		tbuf.sem_otime  = sma->sem_otime;
      		tbuf.sem_ctime  = sma->sem_ctime;
      		tbuf.sem_nsems  = sma->sem_nsems;
      		sem_unlock(semid);
 489  		if (copy_semid_to_user (arg.buf, &tbuf, version))
 490  			return -EFAULT;
 491  		return id;
      	}
 493  	default:
 494  		return -EINVAL;
      	}
 496  	return err;
      out_unlock:
      	sem_unlock(semid);
 499  	return err;
      }
      
 502  int semctl_main(int semid, int semnum, int cmd, int version, union semun arg)
      {
      	struct sem_array *sma;
      	struct sem* curr;
      	int err;
      	ushort fast_sem_io[SEMMSL_FAST];
      	ushort* sem_io = fast_sem_io;
      	int nsems;
      
      	sma = sem_lock(semid);
 512  	if(sma==NULL)
 513  		return -EINVAL;
      
      	nsems = sma->sem_nsems;
      
      	err=-EIDRM;
 518  	if (sem_checkid(sma,semid))
 519  		goto out_unlock;
      
      	err = -EACCES;
 522  	if (ipcperms (&sma->sem_perm, (cmd==SETVAL||cmd==SETALL)?S_IWUGO:S_IRUGO))
 523  		goto out_unlock;
      
 525  	switch (cmd) {
 526  	case GETALL:
      	{
      		ushort *array = arg.array;
      		int i;
      
 531  		if(nsems > SEMMSL_FAST) {
      			sem_unlock(semid);			
      			sem_io = ipc_alloc(sizeof(ushort)*nsems);
 534  			if(sem_io == NULL)
 535  				return -ENOMEM;
      			err = sem_revalidate(semid, sma, nsems, S_IRUGO);
 537  			if(err)
 538  				goto out_free;
      		}
      
 541  		for (i = 0; i < sma->sem_nsems; i++)
      			sem_io[i] = sma->sem_base[i].semval;
      		sem_unlock(semid);
      		err = 0;
 545  		if(copy_to_user(array, sem_io, nsems*sizeof(ushort)))
      			err = -EFAULT;
 547  		goto out_free;
      	}
 549  	case SETALL:
      	{
      		int i;
      		struct sem_undo *un;
      
      		sem_unlock(semid);
      
 556  		if(nsems > SEMMSL_FAST) {
      			sem_io = ipc_alloc(sizeof(ushort)*nsems);
 558  			if(sem_io == NULL)
 559  				return -ENOMEM;
      		}
      
 562  		if (copy_from_user (sem_io, arg.array, nsems*sizeof(ushort))) {
      			err = -EFAULT;
 564  			goto out_free;
      		}
      
 567  		for (i = 0; i < nsems; i++) {
 568  			if (sem_io[i] > SEMVMX) {
      				err = -ERANGE;
 570  				goto out_free;
      			}
      		}
      		err = sem_revalidate(semid, sma, nsems, S_IWUGO);
 574  		if(err)
 575  			goto out_free;
      
 577  		for (i = 0; i < nsems; i++)
      			sma->sem_base[i].semval = sem_io[i];
 579  		for (un = sma->undo; un; un = un->id_next)
 580  			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;
 586  		goto out_unlock;
      	}
 588  	case IPC_STAT:
      	{
      		struct semid64_ds tbuf;
      		memset(&tbuf,0,sizeof(tbuf));
      		kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
      		tbuf.sem_otime  = sma->sem_otime;
      		tbuf.sem_ctime  = sma->sem_ctime;
      		tbuf.sem_nsems  = sma->sem_nsems;
      		sem_unlock(semid);
 597  		if (copy_semid_to_user (arg.buf, &tbuf, version))
 598  			return -EFAULT;
 599  		return 0;
      	}
      	/* GETVAL, GETPID, GETNCTN, GETZCNT, SETVAL: fall-through */
      	}
      	err = -EINVAL;
 604  	if(semnum < 0 || semnum >= nsems)
 605  		goto out_unlock;
      
      	curr = &sma->sem_base[semnum];
      
 609  	switch (cmd) {
 610  	case GETVAL:
      		err = curr->semval;
 612  		goto out_unlock;
 613  	case GETPID:
      		err = curr->sempid & 0xffff;
 615  		goto out_unlock;
 616  	case GETNCNT:
      		err = count_semncnt(sma,semnum);
 618  		goto out_unlock;
 619  	case GETZCNT:
      		err = count_semzcnt(sma,semnum);
 621  		goto out_unlock;
 622  	case SETVAL:
      	{
      		int val = arg.val;
      		struct sem_undo *un;
      		err = -ERANGE;
 627  		if (val > SEMVMX || val < 0)
 628  			goto out_unlock;
      
 630  		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;
 637  		goto out_unlock;
      	}
      	}
      out_unlock:
      	sem_unlock(semid);
      out_free:
 643  	if(sem_io != fast_sem_io)
      		ipc_free(sem_io, sizeof(ushort)*nsems);
 645  	return err;
      }
      
      struct sem_setbuf {
      	uid_t	uid;
      	gid_t	gid;
      	mode_t	mode;
      };
      
 654  static inline unsigned long copy_semid_from_user(struct sem_setbuf *out, void *buf, int version)
      {
 656  	switch(version) {
 657  	case IPC_64:
      	    {
      		struct semid64_ds tbuf;
      
 661  		if(copy_from_user(&tbuf, buf, sizeof(tbuf)))
 662  			return -EFAULT;
      
      		out->uid	= tbuf.sem_perm.uid;
      		out->gid	= tbuf.sem_perm.gid;
      		out->mode	= tbuf.sem_perm.mode;
      
 668  		return 0;
      	    }
 670  	case IPC_OLD:
      	    {
      		struct semid_ds tbuf_old;
      
 674  		if(copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
 675  			return -EFAULT;
      
      		out->uid	= tbuf_old.sem_perm.uid;
      		out->gid	= tbuf_old.sem_perm.gid;
      		out->mode	= tbuf_old.sem_perm.mode;
      
 681  		return 0;
      	    }
 683  	default:
 684  		return -EINVAL;
      	}
      }
      
 688  int semctl_down(int semid, int semnum, int cmd, int version, union semun arg)
      {
      	struct sem_array *sma;
      	int err;
      	struct sem_setbuf setbuf;
      	struct kern_ipc_perm *ipcp;
      
 695  	if(cmd == IPC_SET) {
 696  		if(copy_semid_from_user (&setbuf, arg.buf, version))
 697  			return -EFAULT;
      	}
      	sma = sem_lock(semid);
 700  	if(sma==NULL)
 701  		return -EINVAL;
      
 703  	if (sem_checkid(sma,semid)) {
      		err=-EIDRM;
 705  		goto out_unlock;
      	}	
      	ipcp = &sma->sem_perm;
      	
      	if (current->euid != ipcp->cuid && 
 710  	    current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) {
      	    	err=-EPERM;
 712  		goto out_unlock;
      	}
      
 715  	switch(cmd){
 716  	case IPC_RMID:
      		freeary(semid);
      		err = 0;
 719  		break;
 720  	case IPC_SET:
      		ipcp->uid = setbuf.uid;
      		ipcp->gid = setbuf.gid;
      		ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
      				| (setbuf.mode & S_IRWXUGO);
      		sma->sem_ctime = CURRENT_TIME;
      		sem_unlock(semid);
      		err = 0;
 728  		break;
 729  	default:
      		sem_unlock(semid);
      		err = -EINVAL;
 732  		break;
      	}
 734  	return err;
      
      out_unlock:
      	sem_unlock(semid);
 738  	return err;
      }
      
 741  asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg)
      {
      	int err = -EINVAL;
      	int version;
      
 746  	if (semid < 0)
 747  		return -EINVAL;
      
      	version = ipc_parse_version(&cmd);
      
 751  	switch(cmd) {
 752  	case IPC_INFO:
 753  	case SEM_INFO:
 754  	case SEM_STAT:
      		err = semctl_nolock(semid,semnum,cmd,version,arg);
 756  		return err;
 757  	case GETALL:
 758  	case GETVAL:
 759  	case GETPID:
 760  	case GETNCNT:
 761  	case GETZCNT:
 762  	case IPC_STAT:
 763  	case SETVAL:
 764  	case SETALL:
      		err = semctl_main(semid,semnum,cmd,version,arg);
 766  		return err;
 767  	case IPC_RMID:
 768  	case IPC_SET:
      		down(&sem_ids.sem);
      		err = semctl_down(semid,semnum,cmd,version,arg);
      		up(&sem_ids.sem);
 772  		return err;
 773  	default:
 774  		return -EINVAL;
      	}
      }
      
 778  static struct sem_undo* freeundos(struct sem_array *sma, struct sem_undo* un)
      {
      	struct sem_undo* u;
      	struct sem_undo** up;
      
 783  	for(up = ¤t->semundo;(u=*up);up=&u->proc_next) {
 784  		if(un==u) {
      			un=u->proc_next;
      			*up=un;
      			kfree(u);
 788  			return un;
      		}
      	}
      	printk ("freeundos undo list error id=%d\n", un->semid);
 792  	return un->proc_next;
      }
      
      /* returns without sem_lock on error! */
 796  static int alloc_undo(struct sem_array *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);
 806  	if (!un)
 807  		return -ENOMEM;
      
      	memset(un, 0, size);
      	error = sem_revalidate(semid, sma, nsems, alter ? S_IWUGO : S_IRUGO);
 811  	if(error) {
      		kfree(un);
 813  		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;
 823  	return 0;
      }
      
 826  asmlinkage long sys_semop (int semid, struct sembuf *tsops, unsigned nsops)
      {
      	int error = -EINVAL;
      	struct sem_array *sma;
      	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;
      
 836  	if (nsops < 1 || semid < 0)
 837  		return -EINVAL;
 838  	if (nsops > sc_semopm)
 839  		return -E2BIG;
 840  	if(nsops > SEMOPM_FAST) {
      		sops = kmalloc(sizeof(*sops)*nsops,GFP_KERNEL);
 842  		if(sops==NULL)
 843  			return -ENOMEM;
      	}
 845  	if (copy_from_user (sops, tsops, nsops * sizeof(*tsops))) {
      		error=-EFAULT;
 847  		goto out_free;
      	}
      	sma = sem_lock(semid);
      	error=-EINVAL;
 851  	if(sma==NULL)
 852  		goto out_free;
      	error = -EIDRM;
 854  	if (sem_checkid(sma,semid))
 855  		goto out_unlock_free;
      	error = -EFBIG;
 857  	for (sop = sops; sop < sops + nsops; sop++) {
 858  		if (sop->sem_num >= sma->sem_nsems)
 859  			goto out_unlock_free;
 860  		if (sop->sem_flg & SEM_UNDO)
      			undos++;
 862  		if (sop->sem_op < 0)
      			decrease = 1;
 864  		if (sop->sem_op > 0)
      			alter = 1;
      	}
      	alter |= decrease;
      
      	error = -EACCES;
 870  	if (ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO))
 871  		goto out_unlock_free;
 872  	if (undos) {
      		/* Make sure we have an undo structure
      		 * for this process and this semaphore set.
      		 */
      		un=current->semundo;
 877  		while(un != NULL) {
 878  			if(un->semid==semid)
 879  				break;
 880  			if(un->semid==-1)
      				un=freeundos(sma,un);
 882  			 else
      				un=un->proc_next;
      		}
 885  		if (!un) {
      			error = alloc_undo(sma,&un,semid,alter);
 887  			if(error)
 888  				goto out_free;
      		}
 890  	} else
      		un = NULL;
      
      	error = try_atomic_semop (sma, sops, nsops, un, current->pid, 0);
 894  	if (error <= 0)
 895  		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;
 908  	if (alter)
      		append_to_queue(sma ,&queue);
 910  	else
      		prepend_to_queue(sma ,&queue);
      	current->semsleeping = &queue;
      
 914  	for (;;) {
      		struct sem_array* tmp;
      		queue.status = -EINTR;
      		queue.sleeper = current;
      		current->state = TASK_INTERRUPTIBLE;
      		sem_unlock(semid);
      
      		schedule();
      
      		tmp = sem_lock(semid);
 924  		if(tmp==NULL) {
 925  			if(queue.status != -EIDRM)
 926  				BUG();
      			current->semsleeping = NULL;
      			error = -EIDRM;
 929  			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
      		 *
      		 */
 938  		if (queue.status == 1)
      		{
      			error = try_atomic_semop (sma, sops, nsops, un,
      						  current->pid,0);
 942  			if (error <= 0) 
 943  				break;
 944  		} else {
      			error = queue.status;
 946  			if (queue.prev) /* got Interrupt */
 947  				break;
      			/* Everything done by update_queue */
      			current->semsleeping = NULL;
 950  			goto out_unlock_free;
      		}
      	}
      	current->semsleeping = NULL;
      	remove_from_queue(sma,&queue);
      update:
 956  	if (alter)
      		update_queue (sma);
      out_unlock_free:
      	sem_unlock(semid);
      out_free:
 961  	if(sops != fast_sops)
      		kfree(sops);
 963  	return error;
      }
      
      /*
       * add semadj values to semaphores, free undo structures.
       * undo structures are not freed when semaphore arrays are destroyed
       * so some of them may be out of date.
       * IMPLEMENTATION NOTE: There is some confusion over whether the
       * set of adjustments that needs to be done should be done in an atomic
       * manner or not. That is, if we are attempting to decrement the semval
       * should we queue up and wait until we can do so legally?
       * The original implementation attempted to do this (queue and wait).
       * The current implementation does not do so. The POSIX standard
       * and SVID should be consulted to determine what behavior is mandated.
       */
 978  void sem_exit (void)
      {
      	struct sem_queue *q;
      	struct sem_undo *u, *un = NULL, **up, **unp;
      	struct sem_array *sma;
      	int nsems, i;
      
      	/* If the current process was sleeping for a semaphore,
      	 * remove it from the queue.
      	 */
 988  	if ((q = current->semsleeping)) {
      		int semid = q->id;
      		sma = sem_lock(semid);
      		current->semsleeping = NULL;
      
 993  		if (q->prev) {
 994  			if(sma==NULL)
 995  				BUG();
      			remove_from_queue(q->sma,q);
      		}
 998  		if(sma!=NULL)
      			sem_unlock(semid);
      	}
      
1002  	for (up = ¤t->semundo; (u = *up); *up = u->proc_next, kfree(u)) {
      		int semid = u->semid;
1004  		if(semid == -1)
1005  			continue;
      		sma = sem_lock(semid);
1007  		if (sma == NULL)
1008  			continue;
      
1010  		if (u->semid == -1)
1011  			goto next_entry;
      
1013  		if (sem_checkid(sma,u->semid))
1014  			goto next_entry;
      
      		/* remove u from the sma->undo list */
1017  		for (unp = &sma->undo; (un = *unp); unp = &un->id_next) {
1018  			if (u == un)
1019  				goto found;
      		}
      		printk ("sem_exit undo list error id=%d\n", u->semid);
1022  		goto next_entry;
      found:
      		*unp = un->id_next;
      		/* perform adjustments registered in u */
      		nsems = sma->sem_nsems;
1027  		for (i = 0; i < nsems; i++) {
      			struct sem * sem = &sma->sem_base[i];
      			sem->semval += u->semadj[i];
1030  			if (sem->semval < 0)
      				sem->semval = 0; /* shouldn't happen */
      			sem->sempid = current->pid;
      		}
      		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;
      }
      
      #ifdef CONFIG_PROC_FS
1044  static int sysvipc_sem_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
      {
      	off_t pos = 0;
      	off_t begin = 0;
      	int i, len = 0;
      
      	len += sprintf(buffer, "       key      semid perms      nsems   uid   gid  cuid  cgid      otime      ctime\n");
      	down(&sem_ids.sem);
      
1053  	for(i = 0; i <= sem_ids.max_id; i++) {
      		struct sem_array *sma;
      		sma = sem_lock(i);
1056  		if(sma) {
      			len += sprintf(buffer + len, "%10d %10d  %4o %10lu %5u %5u %5u %5u %10lu %10lu\n",
      				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;
1071  			if(pos < offset) {
      				len = 0;
      	    			begin = pos;
      			}
1075  			if(pos > offset + length)
1076  				goto done;
      		}
      	}
      	*eof = 1;
      done:
      	up(&sem_ids.sem);
      	*start = buffer + (offset - begin);
      	len -= (offset - begin);
1084  	if(len > length)
      		len = length;
1086  	if(len < 0)
      		len = 0;
1088  	return len;
      }
      #endif