diff options
Diffstat (limited to 'ipc/sem.c')
| -rw-r--r-- | ipc/sem.c | 442 | 
1 files changed, 310 insertions, 132 deletions
diff --git a/ipc/sem.c b/ipc/sem.c index 70480a3aa698..41088899783d 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -95,8 +95,12 @@ struct sem {  	int	semval;		/* current value */  	int	sempid;		/* pid of last operation */  	spinlock_t	lock;	/* spinlock for fine-grained semtimedop */ -	struct list_head sem_pending; /* pending single-sop operations */ -}; +	struct list_head pending_alter; /* pending single-sop operations */ +					/* that alter the semaphore */ +	struct list_head pending_const; /* pending single-sop operations */ +					/* that do not alter the semaphore*/ +	time_t	sem_otime;	/* candidate for sem_otime */ +} ____cacheline_aligned_in_smp;  /* One queue for each sleeping process in the system. */  struct sem_queue { @@ -150,12 +154,15 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it);  #define SEMOPM_FAST	64  /* ~ 372 bytes on stack */  /* - * linked list protection: + * Locking:   *	sem_undo.id_next, - *	sem_array.sem_pending{,last}, - *	sem_array.sem_undo: sem_lock() for read/write + *	sem_array.complex_count, + *	sem_array.pending{_alter,_cont}, + *	sem_array.sem_undo: global sem_lock() for read/write   *	sem_undo.proc_next: only "current" is allowed to read/write that field.   *	 + *	sem_array.sem_base[i].pending_{const,alter}: + *		global or semaphore sem_lock() for read/write   */  #define sc_semmsl	sem_ctls[0] @@ -189,6 +196,53 @@ void __init sem_init (void)  				IPC_SEM_IDS, sysvipc_sem_proc_show);  } +/** + * unmerge_queues - unmerge queues, if possible. + * @sma: semaphore array + * + * The function unmerges the wait queues if complex_count is 0. + * It must be called prior to dropping the global semaphore array lock. + */ +static void unmerge_queues(struct sem_array *sma) +{ +	struct sem_queue *q, *tq; + +	/* complex operations still around? */ +	if (sma->complex_count) +		return; +	/* +	 * We will switch back to simple mode. +	 * Move all pending operation back into the per-semaphore +	 * queues. +	 */ +	list_for_each_entry_safe(q, tq, &sma->pending_alter, list) { +		struct sem *curr; +		curr = &sma->sem_base[q->sops[0].sem_num]; + +		list_add_tail(&q->list, &curr->pending_alter); +	} +	INIT_LIST_HEAD(&sma->pending_alter); +} + +/** + * merge_queues - Merge single semop queues into global queue + * @sma: semaphore array + * + * This function merges all per-semaphore queues into the global queue. + * It is necessary to achieve FIFO ordering for the pending single-sop + * operations when a multi-semop operation must sleep. + * Only the alter operations must be moved, the const operations can stay. + */ +static void merge_queues(struct sem_array *sma) +{ +	int i; +	for (i = 0; i < sma->sem_nsems; i++) { +		struct sem *sem = sma->sem_base + i; + +		list_splice_init(&sem->pending_alter, &sma->pending_alter); +	} +} +  /*   * If the request contains only one semaphore operation, and there are   * no complex transactions pending, lock only the semaphore involved. @@ -246,7 +300,7 @@ static inline int sem_lock(struct sem_array *sma, struct sembuf *sops,  		 * their critical section while the array lock is held.  		 */   lock_array: -		spin_lock(&sma->sem_perm.lock); +		ipc_lock_object(&sma->sem_perm);  		for (i = 0; i < sma->sem_nsems; i++) {  			struct sem *sem = sma->sem_base + i;  			spin_unlock_wait(&sem->lock); @@ -259,7 +313,8 @@ static inline int sem_lock(struct sem_array *sma, struct sembuf *sops,  static inline void sem_unlock(struct sem_array *sma, int locknum)  {  	if (locknum == -1) { -		spin_unlock(&sma->sem_perm.lock); +		unmerge_queues(sma); +		ipc_unlock_object(&sma->sem_perm);  	} else {  		struct sem *sem = sma->sem_base + locknum;  		spin_unlock(&sem->lock); @@ -337,7 +392,7 @@ static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s)   * Without the check/retry algorithm a lockless wakeup is possible:   * - queue.status is initialized to -EINTR before blocking.   * - wakeup is performed by - *	* unlinking the queue entry from sma->sem_pending + *	* unlinking the queue entry from the pending list   *	* setting queue.status to IN_WAKEUP   *	  This is the notification for the blocked thread that a   *	  result value is imminent. @@ -418,12 +473,14 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)  	sma->sem_base = (struct sem *) &sma[1];  	for (i = 0; i < nsems; i++) { -		INIT_LIST_HEAD(&sma->sem_base[i].sem_pending); +		INIT_LIST_HEAD(&sma->sem_base[i].pending_alter); +		INIT_LIST_HEAD(&sma->sem_base[i].pending_const);  		spin_lock_init(&sma->sem_base[i].lock);  	}  	sma->complex_count = 0; -	INIT_LIST_HEAD(&sma->sem_pending); +	INIT_LIST_HEAD(&sma->pending_alter); +	INIT_LIST_HEAD(&sma->pending_const);  	INIT_LIST_HEAD(&sma->list_id);  	sma->sem_nsems = nsems;  	sma->sem_ctime = get_seconds(); @@ -482,12 +539,19 @@ SYSCALL_DEFINE3(semget, key_t, key, int, nsems, int, semflg)  	return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params);  } -/* - * 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. +/** perform_atomic_semop - Perform (if possible) a semaphore operation + * @sma: semaphore array + * @sops: array with operations that should be checked + * @nsems: number of sops + * @un: undo array + * @pid: pid that did the change + * + * Returns 0 if the operation was possible. + * Returns 1 if the operation is impossible, the caller must sleep. + * Negative values are error codes.   */ -static int try_atomic_semop (struct sem_array * sma, struct sembuf * sops, +static int perform_atomic_semop(struct sem_array *sma, struct sembuf *sops,  			     int nsops, struct sem_undo *un, int pid)  {  	int result, sem_op; @@ -609,60 +673,132 @@ static void unlink_queue(struct sem_array *sma, struct sem_queue *q)   * update_queue is O(N^2) when it restarts scanning the whole queue of   * waiting operations. Therefore this function checks if the restart is   * really necessary. It is called after a previously waiting operation - * was completed. + * modified the array. + * Note that wait-for-zero operations are handled without restart.   */  static int check_restart(struct sem_array *sma, struct sem_queue *q)  { -	struct sem *curr; -	struct sem_queue *h; - -	/* if the operation didn't modify the array, then no restart */ -	if (q->alter == 0) -		return 0; - -	/* pending complex operations are too difficult to analyse */ -	if (sma->complex_count) +	/* pending complex alter operations are too difficult to analyse */ +	if (!list_empty(&sma->pending_alter))  		return 1;  	/* we were a sleeping complex operation. Too difficult */  	if (q->nsops > 1)  		return 1; -	curr = sma->sem_base + q->sops[0].sem_num; +	/* It is impossible that someone waits for the new value: +	 * - complex operations always restart. +	 * - wait-for-zero are handled seperately. +	 * - q is a previously sleeping simple operation that +	 *   altered the array. It must be a decrement, because +	 *   simple increments never sleep. +	 * - If there are older (higher priority) decrements +	 *   in the queue, then they have observed the original +	 *   semval value and couldn't proceed. The operation +	 *   decremented to value - thus they won't proceed either. +	 */ +	return 0; +} + +/** + * wake_const_ops(sma, semnum, pt) - Wake up non-alter tasks + * @sma: semaphore array. + * @semnum: semaphore that was modified. + * @pt: list head for the tasks that must be woken up. + * + * wake_const_ops must be called after a semaphore in a semaphore array + * was set to 0. If complex const operations are pending, wake_const_ops must + * be called with semnum = -1, as well as with the number of each modified + * semaphore. + * The tasks that must be woken up are added to @pt. The return code + * is stored in q->pid. + * The function returns 1 if at least one operation was completed successfully. + */ +static int wake_const_ops(struct sem_array *sma, int semnum, +				struct list_head *pt) +{ +	struct sem_queue *q; +	struct list_head *walk; +	struct list_head *pending_list; +	int semop_completed = 0; -	/* No-one waits on this queue */ -	if (list_empty(&curr->sem_pending)) -		return 0; +	if (semnum == -1) +		pending_list = &sma->pending_const; +	else +		pending_list = &sma->sem_base[semnum].pending_const; + +	walk = pending_list->next; +	while (walk != pending_list) { +		int error; + +		q = container_of(walk, struct sem_queue, list); +		walk = walk->next; + +		error = perform_atomic_semop(sma, q->sops, q->nsops, +						 q->undo, q->pid); -	/* the new semaphore value */ -	if (curr->semval) { -		/* It is impossible that someone waits for the new value: -		 * - q is a previously sleeping simple operation that -		 *   altered the array. It must be a decrement, because -		 *   simple increments never sleep. -		 * - The value is not 0, thus wait-for-zero won't proceed. -		 * - If there are older (higher priority) decrements -		 *   in the queue, then they have observed the original -		 *   semval value and couldn't proceed. The operation -		 *   decremented to value - thus they won't proceed either. +		if (error <= 0) { +			/* operation completed, remove from queue & wakeup */ + +			unlink_queue(sma, q); + +			wake_up_sem_queue_prepare(pt, q, error); +			if (error == 0) +				semop_completed = 1; +		} +	} +	return semop_completed; +} + +/** + * do_smart_wakeup_zero(sma, sops, nsops, pt) - wakeup all wait for zero tasks + * @sma: semaphore array + * @sops: operations that were performed + * @nsops: number of operations + * @pt: list head of the tasks that must be woken up. + * + * do_smart_wakeup_zero() checks all required queue for wait-for-zero + * operations, based on the actual changes that were performed on the + * semaphore array. + * The function returns 1 if at least one operation was completed successfully. + */ +static int do_smart_wakeup_zero(struct sem_array *sma, struct sembuf *sops, +					int nsops, struct list_head *pt) +{ +	int i; +	int semop_completed = 0; +	int got_zero = 0; + +	/* first: the per-semaphore queues, if known */ +	if (sops) { +		for (i = 0; i < nsops; i++) { +			int num = sops[i].sem_num; + +			if (sma->sem_base[num].semval == 0) { +				got_zero = 1; +				semop_completed |= wake_const_ops(sma, num, pt); +			} +		} +	} else { +		/* +		 * No sops means modified semaphores not known. +		 * Assume all were changed.  		 */ -		BUG_ON(q->sops[0].sem_op >= 0); -		return 0; +		for (i = 0; i < sma->sem_nsems; i++) { +			if (sma->sem_base[i].semval == 0) { +				got_zero = 1; +				semop_completed |= wake_const_ops(sma, i, pt); +			} +		}  	}  	/* -	 * semval is 0. Check if there are wait-for-zero semops. -	 * They must be the first entries in the per-semaphore queue +	 * If one of the modified semaphores got 0, +	 * then check the global queue, too.  	 */ -	h = list_first_entry(&curr->sem_pending, struct sem_queue, list); -	BUG_ON(h->nsops != 1); -	BUG_ON(h->sops[0].sem_num != q->sops[0].sem_num); - -	/* Yes, there is a wait-for-zero semop. Restart */ -	if (h->sops[0].sem_op == 0) -		return 1; +	if (got_zero) +		semop_completed |= wake_const_ops(sma, -1, pt); -	/* Again - no-one is waiting for the new value. */ -	return 0; +	return semop_completed;  } @@ -678,6 +814,8 @@ static int check_restart(struct sem_array *sma, struct sem_queue *q)   * semaphore.   * The tasks that must be woken up are added to @pt. The return code   * is stored in q->pid. + * The function internally checks if const operations can now succeed. + *   * The function return 1 if at least one semop was completed successfully.   */  static int update_queue(struct sem_array *sma, int semnum, struct list_head *pt) @@ -688,9 +826,9 @@ static int update_queue(struct sem_array *sma, int semnum, struct list_head *pt)  	int semop_completed = 0;  	if (semnum == -1) -		pending_list = &sma->sem_pending; +		pending_list = &sma->pending_alter;  	else -		pending_list = &sma->sem_base[semnum].sem_pending; +		pending_list = &sma->sem_base[semnum].pending_alter;  again:  	walk = pending_list->next; @@ -702,16 +840,15 @@ again:  		/* If we are scanning the single sop, per-semaphore list of  		 * one semaphore and that semaphore is 0, then it is not -		 * necessary to scan the "alter" entries: simple increments +		 * necessary to scan further: simple increments  		 * that affect only one entry succeed immediately and cannot  		 * be in the  per semaphore pending queue, and decrements  		 * cannot be successful if the value is already 0.  		 */ -		if (semnum != -1 && sma->sem_base[semnum].semval == 0 && -				q->alter) +		if (semnum != -1 && sma->sem_base[semnum].semval == 0)  			break; -		error = try_atomic_semop(sma, q->sops, q->nsops, +		error = perform_atomic_semop(sma, q->sops, q->nsops,  					 q->undo, q->pid);  		/* Does q->sleeper still need to sleep? */ @@ -724,6 +861,7 @@ again:  			restart = 0;  		} else {  			semop_completed = 1; +			do_smart_wakeup_zero(sma, q->sops, q->nsops, pt);  			restart = check_restart(sma, q);  		} @@ -742,8 +880,8 @@ again:   * @otime: force setting otime   * @pt: list head of the tasks that must be woken up.   * - * do_smart_update() does the required called to update_queue, based on the - * actual changes that were performed on the semaphore array. + * do_smart_update() does the required calls to update_queue and wakeup_zero, + * based on the actual changes that were performed on the semaphore array.   * Note that the function does not do the actual wake-up: the caller is   * responsible for calling wake_up_sem_queue_do(@pt).   * It is safe to perform this call after dropping all locks. @@ -752,49 +890,46 @@ static void do_smart_update(struct sem_array *sma, struct sembuf *sops, int nsop  			int otime, struct list_head *pt)  {  	int i; -	int progress; - -	progress = 1; -retry_global: -	if (sma->complex_count) { -		if (update_queue(sma, -1, pt)) { -			progress = 1; -			otime = 1; -			sops = NULL; -		} -	} -	if (!progress) -		goto done; -	if (!sops) { -		/* No semops; something special is going on. */ -		for (i = 0; i < sma->sem_nsems; i++) { -			if (update_queue(sma, i, pt)) { -				otime = 1; -				progress = 1; -			} -		} -		goto done_checkretry; -	} +	otime |= do_smart_wakeup_zero(sma, sops, nsops, pt); -	/* Check the semaphores that were modified. */ -	for (i = 0; i < nsops; i++) { -		if (sops[i].sem_op > 0 || -			(sops[i].sem_op < 0 && -				sma->sem_base[sops[i].sem_num].semval == 0)) -			if (update_queue(sma, sops[i].sem_num, pt)) { -				otime = 1; -				progress = 1; +	if (!list_empty(&sma->pending_alter)) { +		/* semaphore array uses the global queue - just process it. */ +		otime |= update_queue(sma, -1, pt); +	} else { +		if (!sops) { +			/* +			 * No sops, thus the modified semaphores are not +			 * known. Check all. +			 */ +			for (i = 0; i < sma->sem_nsems; i++) +				otime |= update_queue(sma, i, pt); +		} else { +			/* +			 * Check the semaphores that were increased: +			 * - No complex ops, thus all sleeping ops are +			 *   decrease. +			 * - if we decreased the value, then any sleeping +			 *   semaphore ops wont be able to run: If the +			 *   previous value was too small, then the new +			 *   value will be too small, too. +			 */ +			for (i = 0; i < nsops; i++) { +				if (sops[i].sem_op > 0) { +					otime |= update_queue(sma, +							sops[i].sem_num, pt); +				}  			} +		}  	} -done_checkretry: -	if (progress) { -		progress = 0; -		goto retry_global; +	if (otime) { +		if (sops == NULL) { +			sma->sem_base[0].sem_otime = get_seconds(); +		} else { +			sma->sem_base[sops[0].sem_num].sem_otime = +								get_seconds(); +		}  	} -done: -	if (otime) -		sma->sem_otime = get_seconds();  } @@ -813,14 +948,14 @@ static int count_semncnt (struct sem_array * sma, ushort semnum)  	struct sem_queue * q;  	semncnt = 0; -	list_for_each_entry(q, &sma->sem_base[semnum].sem_pending, list) { +	list_for_each_entry(q, &sma->sem_base[semnum].pending_alter, list) {  		struct sembuf * sops = q->sops;  		BUG_ON(sops->sem_num != semnum);  		if ((sops->sem_op < 0) && !(sops->sem_flg & IPC_NOWAIT))  			semncnt++;  	} -	list_for_each_entry(q, &sma->sem_pending, list) { +	list_for_each_entry(q, &sma->pending_alter, list) {  		struct sembuf * sops = q->sops;  		int nsops = q->nsops;  		int i; @@ -839,14 +974,14 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum)  	struct sem_queue * q;  	semzcnt = 0; -	list_for_each_entry(q, &sma->sem_base[semnum].sem_pending, list) { +	list_for_each_entry(q, &sma->sem_base[semnum].pending_const, list) {  		struct sembuf * sops = q->sops;  		BUG_ON(sops->sem_num != semnum);  		if ((sops->sem_op == 0) && !(sops->sem_flg & IPC_NOWAIT))  			semzcnt++;  	} -	list_for_each_entry(q, &sma->sem_pending, list) { +	list_for_each_entry(q, &sma->pending_const, list) {  		struct sembuf * sops = q->sops;  		int nsops = q->nsops;  		int i; @@ -872,7 +1007,7 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)  	int i;  	/* Free the existing undo structures for this semaphore set.  */ -	assert_spin_locked(&sma->sem_perm.lock); +	ipc_assert_locked_object(&sma->sem_perm);  	list_for_each_entry_safe(un, tu, &sma->list_id, list_id) {  		list_del(&un->list_id);  		spin_lock(&un->ulp->lock); @@ -884,13 +1019,22 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)  	/* Wake up all pending processes and let them fail with EIDRM. */  	INIT_LIST_HEAD(&tasks); -	list_for_each_entry_safe(q, tq, &sma->sem_pending, list) { +	list_for_each_entry_safe(q, tq, &sma->pending_const, list) { +		unlink_queue(sma, q); +		wake_up_sem_queue_prepare(&tasks, q, -EIDRM); +	} + +	list_for_each_entry_safe(q, tq, &sma->pending_alter, list) {  		unlink_queue(sma, q);  		wake_up_sem_queue_prepare(&tasks, q, -EIDRM);  	}  	for (i = 0; i < sma->sem_nsems; i++) {  		struct sem *sem = sma->sem_base + i; -		list_for_each_entry_safe(q, tq, &sem->sem_pending, list) { +		list_for_each_entry_safe(q, tq, &sem->pending_const, list) { +			unlink_queue(sma, q); +			wake_up_sem_queue_prepare(&tasks, q, -EIDRM); +		} +		list_for_each_entry_safe(q, tq, &sem->pending_alter, list) {  			unlink_queue(sma, q);  			wake_up_sem_queue_prepare(&tasks, q, -EIDRM);  		} @@ -931,6 +1075,21 @@ static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in,  	}  } +static time_t get_semotime(struct sem_array *sma) +{ +	int i; +	time_t res; + +	res = sma->sem_base[0].sem_otime; +	for (i = 1; i < sma->sem_nsems; i++) { +		time_t to = sma->sem_base[i].sem_otime; + +		if (to > res) +			res = to; +	} +	return res; +} +  static int semctl_nolock(struct ipc_namespace *ns, int semid,  			 int cmd, int version, void __user *p)  { @@ -1004,9 +1163,9 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid,  			goto out_unlock;  		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; +		tbuf.sem_otime = get_semotime(sma); +		tbuf.sem_ctime = sma->sem_ctime; +		tbuf.sem_nsems = sma->sem_nsems;  		rcu_read_unlock();  		if (copy_semid_to_user(p, &tbuf, version))  			return -EFAULT; @@ -1070,7 +1229,7 @@ static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,  	curr = &sma->sem_base[semnum]; -	assert_spin_locked(&sma->sem_perm.lock); +	ipc_assert_locked_object(&sma->sem_perm);  	list_for_each_entry(un, &sma->list_id, list_id)  		un->semadj[semnum] = 0; @@ -1199,7 +1358,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,  		for (i = 0; i < nsems; i++)  			sma->sem_base[i].semval = sem_io[i]; -		assert_spin_locked(&sma->sem_perm.lock); +		ipc_assert_locked_object(&sma->sem_perm);  		list_for_each_entry(un, &sma->list_id, list_id) {  			for (i = 0; i < nsems; i++)  				un->semadj[i] = 0; @@ -1289,39 +1448,43 @@ static int semctl_down(struct ipc_namespace *ns, int semid,  			return -EFAULT;  	} +	down_write(&sem_ids(ns).rw_mutex); +	rcu_read_lock(); +  	ipcp = ipcctl_pre_down_nolock(ns, &sem_ids(ns), semid, cmd,  				      &semid64.sem_perm, 0); -	if (IS_ERR(ipcp)) -		return PTR_ERR(ipcp); +	if (IS_ERR(ipcp)) { +		err = PTR_ERR(ipcp); +		goto out_unlock1; +	}  	sma = container_of(ipcp, struct sem_array, sem_perm);  	err = security_sem_semctl(sma, cmd); -	if (err) { -		rcu_read_unlock(); -		goto out_up; -	} +	if (err) +		goto out_unlock1; -	switch(cmd){ +	switch (cmd) {  	case IPC_RMID:  		sem_lock(sma, NULL, -1); +		/* freeary unlocks the ipc object and rcu */  		freeary(ns, ipcp);  		goto out_up;  	case IPC_SET:  		sem_lock(sma, NULL, -1);  		err = ipc_update_perm(&semid64.sem_perm, ipcp);  		if (err) -			goto out_unlock; +			goto out_unlock0;  		sma->sem_ctime = get_seconds();  		break;  	default: -		rcu_read_unlock();  		err = -EINVAL; -		goto out_up; +		goto out_unlock1;  	} -out_unlock: +out_unlock0:  	sem_unlock(sma, -1); +out_unlock1:  	rcu_read_unlock();  out_up:  	up_write(&sem_ids(ns).rw_mutex); @@ -1496,7 +1659,7 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)  	new->semid = semid;  	assert_spin_locked(&ulp->lock);  	list_add_rcu(&new->list_proc, &ulp->list_proc); -	assert_spin_locked(&sma->sem_perm.lock); +	ipc_assert_locked_object(&sma->sem_perm);  	list_add(&new->list_id, &sma->list_id);  	un = new; @@ -1533,7 +1696,6 @@ static int get_queue_result(struct sem_queue *q)  	return error;  } -  SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,  		unsigned, nsops, const struct timespec __user *, timeout)  { @@ -1631,7 +1793,8 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,  	if (un && un->semid == -1)  		goto out_unlock_free; -	error = try_atomic_semop (sma, sops, nsops, un, task_tgid_vnr(current)); +	error = perform_atomic_semop(sma, sops, nsops, un, +					task_tgid_vnr(current));  	if (error <= 0) {  		if (alter && error == 0)  			do_smart_update(sma, sops, nsops, 1, &tasks); @@ -1653,15 +1816,27 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,  		struct sem *curr;  		curr = &sma->sem_base[sops->sem_num]; -		if (alter) -			list_add_tail(&queue.list, &curr->sem_pending); -		else -			list_add(&queue.list, &curr->sem_pending); +		if (alter) { +			if (sma->complex_count) { +				list_add_tail(&queue.list, +						&sma->pending_alter); +			} else { + +				list_add_tail(&queue.list, +						&curr->pending_alter); +			} +		} else { +			list_add_tail(&queue.list, &curr->pending_const); +		}  	} else { +		if (!sma->complex_count) +			merge_queues(sma); +  		if (alter) -			list_add_tail(&queue.list, &sma->sem_pending); +			list_add_tail(&queue.list, &sma->pending_alter);  		else -			list_add(&queue.list, &sma->sem_pending); +			list_add_tail(&queue.list, &sma->pending_const); +  		sma->complex_count++;  	} @@ -1833,7 +2008,7 @@ void exit_sem(struct task_struct *tsk)  		}  		/* remove un from the linked lists */ -		assert_spin_locked(&sma->sem_perm.lock); +		ipc_assert_locked_object(&sma->sem_perm);  		list_del(&un->list_id);  		spin_lock(&ulp->lock); @@ -1882,6 +2057,9 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)  {  	struct user_namespace *user_ns = seq_user_ns(s);  	struct sem_array *sma = it; +	time_t sem_otime; + +	sem_otime = get_semotime(sma);  	return seq_printf(s,  			  "%10d %10d  %4o %10u %5u %5u %5u %5u %10lu %10lu\n", @@ -1893,7 +2071,7 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)  			  from_kgid_munged(user_ns, sma->sem_perm.gid),  			  from_kuid_munged(user_ns, sma->sem_perm.cuid),  			  from_kgid_munged(user_ns, sma->sem_perm.cgid), -			  sma->sem_otime, +			  sem_otime,  			  sma->sem_ctime);  }  #endif  | 
