1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28*0Sstevel@tonic-gate /*	  All Rights Reserved  	*/
29*0Sstevel@tonic-gate 
30*0Sstevel@tonic-gate 
31*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate /*
34*0Sstevel@tonic-gate  * Inter-Process Communication Semaphore Facility.
35*0Sstevel@tonic-gate  *
36*0Sstevel@tonic-gate  * See os/ipc.c for a description of common IPC functionality.
37*0Sstevel@tonic-gate  *
38*0Sstevel@tonic-gate  * Resource controls
39*0Sstevel@tonic-gate  * -----------------
40*0Sstevel@tonic-gate  *
41*0Sstevel@tonic-gate  * Control:      project.max-sem-ids (rc_project_semmni)
42*0Sstevel@tonic-gate  * Description:  Maximum number of semaphore ids allowed a project.
43*0Sstevel@tonic-gate  *
44*0Sstevel@tonic-gate  *   When semget() is used to allocate a semaphore set, one id is
45*0Sstevel@tonic-gate  *   allocated.  If the id allocation doesn't succeed, semget() fails
46*0Sstevel@tonic-gate  *   and errno is set to ENOSPC.  Upon successful semctl(, IPC_RMID)
47*0Sstevel@tonic-gate  *   the id is deallocated.
48*0Sstevel@tonic-gate  *
49*0Sstevel@tonic-gate  * Control:      process.max-sem-nsems (rc_process_semmsl)
50*0Sstevel@tonic-gate  * Description:  Maximum number of semaphores allowed per semaphore set.
51*0Sstevel@tonic-gate  *
52*0Sstevel@tonic-gate  *   When semget() is used to allocate a semaphore set, the size of the
53*0Sstevel@tonic-gate  *   set is compared with this limit.  If the number of semaphores
54*0Sstevel@tonic-gate  *   exceeds the limit, semget() fails and errno is set to EINVAL.
55*0Sstevel@tonic-gate  *
56*0Sstevel@tonic-gate  * Control:      process.max-sem-ops (rc_process_semopm)
57*0Sstevel@tonic-gate  * Description:  Maximum number of semaphore operations allowed per
58*0Sstevel@tonic-gate  *               semop call.
59*0Sstevel@tonic-gate  *
60*0Sstevel@tonic-gate  *   When semget() successfully allocates a semaphore set, the minimum
61*0Sstevel@tonic-gate  *   enforced value of this limit is used to initialize the
62*0Sstevel@tonic-gate  *   "system-imposed maximum" number of operations a semop() call for
63*0Sstevel@tonic-gate  *   this set can perform.
64*0Sstevel@tonic-gate  *
65*0Sstevel@tonic-gate  * Undo structures
66*0Sstevel@tonic-gate  * ---------------
67*0Sstevel@tonic-gate  *
68*0Sstevel@tonic-gate  * Removing the undo structure tunables involved a serious redesign of
69*0Sstevel@tonic-gate  * how they were implemented.  There is now one undo structure for
70*0Sstevel@tonic-gate  * every process/semaphore array combination (lazily allocated, of
71*0Sstevel@tonic-gate  * course), and each is equal in size to the semaphore it corresponds
72*0Sstevel@tonic-gate  * to.  To avoid scalability and performance problems, the undo
73*0Sstevel@tonic-gate  * structures are stored in two places: a per-process AVL tree sorted
74*0Sstevel@tonic-gate  * by ksemid pointer (p_semacct, protected by p_lock) and an unsorted
75*0Sstevel@tonic-gate  * per-semaphore linked list (sem_undos, protected by the semaphore's
76*0Sstevel@tonic-gate  * ID lock).  The former is used by semop, where a lookup is performed
77*0Sstevel@tonic-gate  * once and cached if SEM_UNDO is specified for any of the operations,
78*0Sstevel@tonic-gate  * and at process exit where the undoable operations are rolled back.
79*0Sstevel@tonic-gate  * The latter is used when removing the semaphore, so the undo
80*0Sstevel@tonic-gate  * structures can be removed from the appropriate processes' trees.
81*0Sstevel@tonic-gate  *
82*0Sstevel@tonic-gate  * The undo structure itself contains pointers to the ksemid and proc
83*0Sstevel@tonic-gate  * to which it corresponds, a list node, an AVL node, and an array of
84*0Sstevel@tonic-gate  * adjust-on-exit (AOE) values.  When an undo structure is allocated it
85*0Sstevel@tonic-gate  * is immediately added to both the process's tree and the semaphore's
86*0Sstevel@tonic-gate  * list.  Lastly, the reference count on the semaphore is increased.
87*0Sstevel@tonic-gate  *
88*0Sstevel@tonic-gate  * Avoiding a lock ordering violation between p_lock and the ID lock,
89*0Sstevel@tonic-gate  * wont to occur when there is a race between a process exiting and the
90*0Sstevel@tonic-gate  * removal of a semaphore, mandates the delicate dance that exists
91*0Sstevel@tonic-gate  * between semexit and sem_rmid.
92*0Sstevel@tonic-gate  *
93*0Sstevel@tonic-gate  * sem_rmid, holding the ID lock, iterates through all undo structures
94*0Sstevel@tonic-gate  * and for each takes the appropriate process's p_lock and checks to
95*0Sstevel@tonic-gate  * see if p_semacct is NULL.  If it is, it skips that undo structure
96*0Sstevel@tonic-gate  * and continues to the next.  Otherwise, it removes the undo structure
97*0Sstevel@tonic-gate  * from both the AVL tree and the semaphore's list, and releases the
98*0Sstevel@tonic-gate  * hold that the undo structure had on the semaphore.
99*0Sstevel@tonic-gate  *
100*0Sstevel@tonic-gate  * The important other half of this is semexit, which will immediately
101*0Sstevel@tonic-gate  * take p_lock, obtain the AVL pointer, clear p_semacct, and drop
102*0Sstevel@tonic-gate  * p_lock.  From this point on it is semexit's responsibility to clean
103*0Sstevel@tonic-gate  * up all undo structures found in the tree -- a coexecuting sem_rmid
104*0Sstevel@tonic-gate  * will see the NULL p_semacct and skip that undo structure.  It walks
105*0Sstevel@tonic-gate  * the AVL tree (using avl_destroy_nodes) and for each undo structure
106*0Sstevel@tonic-gate  * takes the appropriate semaphore's ID lock (always legal since the
107*0Sstevel@tonic-gate  * undo structure has a hold on the semaphore), updates all semaphores
108*0Sstevel@tonic-gate  * with non-zero AOE values, and removes the structure from the
109*0Sstevel@tonic-gate  * semaphore's list.  It then drops the structure's reference on the
110*0Sstevel@tonic-gate  * semaphore, drops the ID lock, and frees the undo structure.
111*0Sstevel@tonic-gate  */
112*0Sstevel@tonic-gate 
113*0Sstevel@tonic-gate #include <sys/types.h>
114*0Sstevel@tonic-gate #include <sys/t_lock.h>
115*0Sstevel@tonic-gate #include <sys/param.h>
116*0Sstevel@tonic-gate #include <sys/systm.h>
117*0Sstevel@tonic-gate #include <sys/sysmacros.h>
118*0Sstevel@tonic-gate #include <sys/cred.h>
119*0Sstevel@tonic-gate #include <sys/vmem.h>
120*0Sstevel@tonic-gate #include <sys/kmem.h>
121*0Sstevel@tonic-gate #include <sys/errno.h>
122*0Sstevel@tonic-gate #include <sys/time.h>
123*0Sstevel@tonic-gate #include <sys/ipc.h>
124*0Sstevel@tonic-gate #include <sys/ipc_impl.h>
125*0Sstevel@tonic-gate #include <sys/sem.h>
126*0Sstevel@tonic-gate #include <sys/sem_impl.h>
127*0Sstevel@tonic-gate #include <sys/user.h>
128*0Sstevel@tonic-gate #include <sys/proc.h>
129*0Sstevel@tonic-gate #include <sys/cpuvar.h>
130*0Sstevel@tonic-gate #include <sys/debug.h>
131*0Sstevel@tonic-gate #include <sys/var.h>
132*0Sstevel@tonic-gate #include <sys/cmn_err.h>
133*0Sstevel@tonic-gate #include <sys/modctl.h>
134*0Sstevel@tonic-gate #include <sys/syscall.h>
135*0Sstevel@tonic-gate #include <sys/avl.h>
136*0Sstevel@tonic-gate #include <sys/list.h>
137*0Sstevel@tonic-gate #include <sys/zone.h>
138*0Sstevel@tonic-gate 
139*0Sstevel@tonic-gate #include <c2/audit.h>
140*0Sstevel@tonic-gate 
141*0Sstevel@tonic-gate extern rctl_hndl_t rc_project_semmni;
142*0Sstevel@tonic-gate extern rctl_hndl_t rc_process_semmsl;
143*0Sstevel@tonic-gate extern rctl_hndl_t rc_process_semopm;
144*0Sstevel@tonic-gate static ipc_service_t *sem_svc;
145*0Sstevel@tonic-gate static zone_key_t sem_zone_key;
146*0Sstevel@tonic-gate 
147*0Sstevel@tonic-gate /*
148*0Sstevel@tonic-gate  * The following tunables are obsolete.  Though for compatibility we
149*0Sstevel@tonic-gate  * still read and interpret seminfo_semmsl, seminfo_semopm and
150*0Sstevel@tonic-gate  * seminfo_semmni (see os/project.c and os/rctl_proc.c), the preferred
151*0Sstevel@tonic-gate  * mechanism for administrating the IPC Semaphore facility is through
152*0Sstevel@tonic-gate  * the resource controls described at the top of this file.
153*0Sstevel@tonic-gate  */
154*0Sstevel@tonic-gate int seminfo_semaem = 16384;	/* (obsolete) */
155*0Sstevel@tonic-gate int seminfo_semmap = 10;	/* (obsolete) */
156*0Sstevel@tonic-gate int seminfo_semmni = 10;	/* (obsolete) */
157*0Sstevel@tonic-gate int seminfo_semmns = 60;	/* (obsolete) */
158*0Sstevel@tonic-gate int seminfo_semmnu = 30;	/* (obsolete) */
159*0Sstevel@tonic-gate int seminfo_semmsl = 25;	/* (obsolete) */
160*0Sstevel@tonic-gate int seminfo_semopm = 10;	/* (obsolete) */
161*0Sstevel@tonic-gate int seminfo_semume = 10;	/* (obsolete) */
162*0Sstevel@tonic-gate int seminfo_semusz = 96;	/* (obsolete) */
163*0Sstevel@tonic-gate int seminfo_semvmx = 32767;	/* (obsolete) */
164*0Sstevel@tonic-gate 
165*0Sstevel@tonic-gate #define	SEM_MAXUCOPS	4096	/* max # of unchecked ops per semop call */
166*0Sstevel@tonic-gate #define	SEM_UNDOSZ(n)	(sizeof (struct sem_undo) + (n - 1) * sizeof (int))
167*0Sstevel@tonic-gate 
168*0Sstevel@tonic-gate static int semsys(int opcode, uintptr_t a0, uintptr_t a1,
169*0Sstevel@tonic-gate     uintptr_t a2, uintptr_t a3);
170*0Sstevel@tonic-gate static void sem_dtor(kipc_perm_t *);
171*0Sstevel@tonic-gate static void sem_rmid(kipc_perm_t *);
172*0Sstevel@tonic-gate static void sem_remove_zone(zoneid_t, void *);
173*0Sstevel@tonic-gate 
174*0Sstevel@tonic-gate static struct sysent ipcsem_sysent = {
175*0Sstevel@tonic-gate 	5,
176*0Sstevel@tonic-gate 	SE_NOUNLOAD | SE_ARGC | SE_32RVAL1,
177*0Sstevel@tonic-gate 	semsys
178*0Sstevel@tonic-gate };
179*0Sstevel@tonic-gate 
180*0Sstevel@tonic-gate /*
181*0Sstevel@tonic-gate  * Module linkage information for the kernel.
182*0Sstevel@tonic-gate  */
183*0Sstevel@tonic-gate static struct modlsys modlsys = {
184*0Sstevel@tonic-gate 	&mod_syscallops, "System V semaphore facility", &ipcsem_sysent
185*0Sstevel@tonic-gate };
186*0Sstevel@tonic-gate 
187*0Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
188*0Sstevel@tonic-gate static struct modlsys modlsys32 = {
189*0Sstevel@tonic-gate 	&mod_syscallops32, "32-bit System V semaphore facility", &ipcsem_sysent
190*0Sstevel@tonic-gate };
191*0Sstevel@tonic-gate #endif
192*0Sstevel@tonic-gate 
193*0Sstevel@tonic-gate static struct modlinkage modlinkage = {
194*0Sstevel@tonic-gate 	MODREV_1,
195*0Sstevel@tonic-gate 	&modlsys,
196*0Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
197*0Sstevel@tonic-gate 	&modlsys32,
198*0Sstevel@tonic-gate #endif
199*0Sstevel@tonic-gate 	NULL
200*0Sstevel@tonic-gate };
201*0Sstevel@tonic-gate 
202*0Sstevel@tonic-gate 
203*0Sstevel@tonic-gate int
204*0Sstevel@tonic-gate _init(void)
205*0Sstevel@tonic-gate {
206*0Sstevel@tonic-gate 	int result;
207*0Sstevel@tonic-gate 
208*0Sstevel@tonic-gate 	sem_svc = ipcs_create("semids", rc_project_semmni, sizeof (ksemid_t),
209*0Sstevel@tonic-gate 	    sem_dtor, sem_rmid, AT_IPC_SEM,
210*0Sstevel@tonic-gate 	    offsetof(kproject_data_t, kpd_semmni));
211*0Sstevel@tonic-gate 	zone_key_create(&sem_zone_key, NULL, sem_remove_zone, NULL);
212*0Sstevel@tonic-gate 
213*0Sstevel@tonic-gate 	if ((result = mod_install(&modlinkage)) == 0)
214*0Sstevel@tonic-gate 		return (0);
215*0Sstevel@tonic-gate 
216*0Sstevel@tonic-gate 	(void) zone_key_delete(sem_zone_key);
217*0Sstevel@tonic-gate 	ipcs_destroy(sem_svc);
218*0Sstevel@tonic-gate 
219*0Sstevel@tonic-gate 	return (result);
220*0Sstevel@tonic-gate }
221*0Sstevel@tonic-gate 
222*0Sstevel@tonic-gate int
223*0Sstevel@tonic-gate _fini(void)
224*0Sstevel@tonic-gate {
225*0Sstevel@tonic-gate 	return (EBUSY);
226*0Sstevel@tonic-gate }
227*0Sstevel@tonic-gate 
228*0Sstevel@tonic-gate int
229*0Sstevel@tonic-gate _info(struct modinfo *modinfop)
230*0Sstevel@tonic-gate {
231*0Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
232*0Sstevel@tonic-gate }
233*0Sstevel@tonic-gate 
234*0Sstevel@tonic-gate static void
235*0Sstevel@tonic-gate sem_dtor(kipc_perm_t *perm)
236*0Sstevel@tonic-gate {
237*0Sstevel@tonic-gate 	ksemid_t *sp = (ksemid_t *)perm;
238*0Sstevel@tonic-gate 
239*0Sstevel@tonic-gate 	kmem_free(sp->sem_base,
240*0Sstevel@tonic-gate 	    P2ROUNDUP(sp->sem_nsems * sizeof (struct sem), 64));
241*0Sstevel@tonic-gate 	list_destroy(&sp->sem_undos);
242*0Sstevel@tonic-gate }
243*0Sstevel@tonic-gate 
244*0Sstevel@tonic-gate /*
245*0Sstevel@tonic-gate  * sem_undo_add - Create or update adjust on exit entry.
246*0Sstevel@tonic-gate  */
247*0Sstevel@tonic-gate static int
248*0Sstevel@tonic-gate sem_undo_add(short val, ushort_t num, struct sem_undo *undo)
249*0Sstevel@tonic-gate {
250*0Sstevel@tonic-gate 	int newval = undo->un_aoe[num] - val;
251*0Sstevel@tonic-gate 
252*0Sstevel@tonic-gate 	if (newval > USHRT_MAX || newval < -USHRT_MAX)
253*0Sstevel@tonic-gate 		return (ERANGE);
254*0Sstevel@tonic-gate 	undo->un_aoe[num] = newval;
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate 	return (0);
257*0Sstevel@tonic-gate }
258*0Sstevel@tonic-gate 
259*0Sstevel@tonic-gate /*
260*0Sstevel@tonic-gate  * sem_undo_clear - clears all undo entries for specified semaphores
261*0Sstevel@tonic-gate  *
262*0Sstevel@tonic-gate  * Used when semaphores are reset by SETVAL or SETALL.
263*0Sstevel@tonic-gate  */
264*0Sstevel@tonic-gate static void
265*0Sstevel@tonic-gate sem_undo_clear(ksemid_t *sp, ushort_t low, ushort_t high)
266*0Sstevel@tonic-gate {
267*0Sstevel@tonic-gate 	struct sem_undo *undo;
268*0Sstevel@tonic-gate 	int i;
269*0Sstevel@tonic-gate 
270*0Sstevel@tonic-gate 	ASSERT(low <= high);
271*0Sstevel@tonic-gate 	ASSERT(high < sp->sem_nsems);
272*0Sstevel@tonic-gate 
273*0Sstevel@tonic-gate 	for (undo = list_head(&sp->sem_undos); undo;
274*0Sstevel@tonic-gate 	    undo = list_next(&sp->sem_undos, undo))
275*0Sstevel@tonic-gate 		for (i = low; i <= high; i++)
276*0Sstevel@tonic-gate 			undo->un_aoe[i] = 0;
277*0Sstevel@tonic-gate }
278*0Sstevel@tonic-gate 
279*0Sstevel@tonic-gate /*
280*0Sstevel@tonic-gate  * sem_rollback - roll back work done so far if unable to complete operation
281*0Sstevel@tonic-gate  */
282*0Sstevel@tonic-gate static void
283*0Sstevel@tonic-gate sem_rollback(ksemid_t *sp, struct sembuf *op, int n, struct sem_undo *undo)
284*0Sstevel@tonic-gate {
285*0Sstevel@tonic-gate 	struct sem *semp;	/* semaphore ptr */
286*0Sstevel@tonic-gate 
287*0Sstevel@tonic-gate 	for (op += n - 1; n--; op--) {
288*0Sstevel@tonic-gate 		if (op->sem_op == 0)
289*0Sstevel@tonic-gate 			continue;
290*0Sstevel@tonic-gate 		semp = &sp->sem_base[op->sem_num];
291*0Sstevel@tonic-gate 		semp->semval -= op->sem_op;
292*0Sstevel@tonic-gate 		if (op->sem_flg & SEM_UNDO) {
293*0Sstevel@tonic-gate 			ASSERT(undo != NULL);
294*0Sstevel@tonic-gate 			(void) sem_undo_add(-op->sem_op, op->sem_num, undo);
295*0Sstevel@tonic-gate 		}
296*0Sstevel@tonic-gate 	}
297*0Sstevel@tonic-gate }
298*0Sstevel@tonic-gate 
299*0Sstevel@tonic-gate static void
300*0Sstevel@tonic-gate sem_rmid(kipc_perm_t *perm)
301*0Sstevel@tonic-gate {
302*0Sstevel@tonic-gate 	ksemid_t *sp = (ksemid_t *)perm;
303*0Sstevel@tonic-gate 	struct sem *semp;
304*0Sstevel@tonic-gate 	struct sem_undo *undo;
305*0Sstevel@tonic-gate 	size_t size = SEM_UNDOSZ(sp->sem_nsems);
306*0Sstevel@tonic-gate 	int i;
307*0Sstevel@tonic-gate 
308*0Sstevel@tonic-gate 	/*LINTED*/
309*0Sstevel@tonic-gate 	while (undo = list_head(&sp->sem_undos)) {
310*0Sstevel@tonic-gate 		list_remove(&sp->sem_undos, undo);
311*0Sstevel@tonic-gate 		mutex_enter(&undo->un_proc->p_lock);
312*0Sstevel@tonic-gate 		if (undo->un_proc->p_semacct == NULL) {
313*0Sstevel@tonic-gate 			mutex_exit(&undo->un_proc->p_lock);
314*0Sstevel@tonic-gate 			continue;
315*0Sstevel@tonic-gate 		}
316*0Sstevel@tonic-gate 		avl_remove(undo->un_proc->p_semacct, undo);
317*0Sstevel@tonic-gate 		mutex_exit(&undo->un_proc->p_lock);
318*0Sstevel@tonic-gate 		kmem_free(undo, size);
319*0Sstevel@tonic-gate 		ipc_rele_locked(sem_svc, (kipc_perm_t *)sp);
320*0Sstevel@tonic-gate 	}
321*0Sstevel@tonic-gate 
322*0Sstevel@tonic-gate 	for (i = 0; i < sp->sem_nsems; i++) {
323*0Sstevel@tonic-gate 		semp = &sp->sem_base[i];
324*0Sstevel@tonic-gate 		semp->semval = semp->sempid = 0;
325*0Sstevel@tonic-gate 		if (semp->semncnt) {
326*0Sstevel@tonic-gate 			cv_broadcast(&semp->semncnt_cv);
327*0Sstevel@tonic-gate 			semp->semncnt = 0;
328*0Sstevel@tonic-gate 		}
329*0Sstevel@tonic-gate 		if (semp->semzcnt) {
330*0Sstevel@tonic-gate 			cv_broadcast(&semp->semzcnt_cv);
331*0Sstevel@tonic-gate 			semp->semzcnt = 0;
332*0Sstevel@tonic-gate 		}
333*0Sstevel@tonic-gate 	}
334*0Sstevel@tonic-gate }
335*0Sstevel@tonic-gate 
336*0Sstevel@tonic-gate /*
337*0Sstevel@tonic-gate  * semctl - Semctl system call.
338*0Sstevel@tonic-gate  */
339*0Sstevel@tonic-gate static int
340*0Sstevel@tonic-gate semctl(int semid, uint_t semnum, int cmd, uintptr_t arg)
341*0Sstevel@tonic-gate {
342*0Sstevel@tonic-gate 	ksemid_t		*sp;	/* ptr to semaphore header */
343*0Sstevel@tonic-gate 	struct sem		*p;	/* ptr to semaphore */
344*0Sstevel@tonic-gate 	unsigned int		i;	/* loop control */
345*0Sstevel@tonic-gate 	ushort_t		*vals, *vp;
346*0Sstevel@tonic-gate 	size_t			vsize = 0;
347*0Sstevel@tonic-gate 	int			error = 0;
348*0Sstevel@tonic-gate 	int			retval = 0;
349*0Sstevel@tonic-gate 	struct cred		*cr;
350*0Sstevel@tonic-gate 	kmutex_t		*lock;
351*0Sstevel@tonic-gate 	model_t			mdl = get_udatamodel();
352*0Sstevel@tonic-gate 	STRUCT_DECL(semid_ds, sid);
353*0Sstevel@tonic-gate 	struct semid_ds64	ds64;
354*0Sstevel@tonic-gate 
355*0Sstevel@tonic-gate 	STRUCT_INIT(sid, mdl);
356*0Sstevel@tonic-gate 	cr = CRED();
357*0Sstevel@tonic-gate 
358*0Sstevel@tonic-gate 	/*
359*0Sstevel@tonic-gate 	 * Perform pre- or non-lookup actions (e.g. copyins, RMID).
360*0Sstevel@tonic-gate 	 */
361*0Sstevel@tonic-gate 	switch (cmd) {
362*0Sstevel@tonic-gate 	case IPC_SET:
363*0Sstevel@tonic-gate 		if (copyin((void *)arg, STRUCT_BUF(sid), STRUCT_SIZE(sid)))
364*0Sstevel@tonic-gate 			return (set_errno(EFAULT));
365*0Sstevel@tonic-gate 		break;
366*0Sstevel@tonic-gate 
367*0Sstevel@tonic-gate 	case IPC_SET64:
368*0Sstevel@tonic-gate 		if (copyin((void *)arg, &ds64, sizeof (struct semid_ds64)))
369*0Sstevel@tonic-gate 			return (set_errno(EFAULT));
370*0Sstevel@tonic-gate 		break;
371*0Sstevel@tonic-gate 
372*0Sstevel@tonic-gate 	case SETALL:
373*0Sstevel@tonic-gate 		if ((lock = ipc_lookup(sem_svc, semid,
374*0Sstevel@tonic-gate 		    (kipc_perm_t **)&sp)) == NULL)
375*0Sstevel@tonic-gate 			return (set_errno(EINVAL));
376*0Sstevel@tonic-gate 		vsize = sp->sem_nsems * sizeof (*vals);
377*0Sstevel@tonic-gate 		mutex_exit(lock);
378*0Sstevel@tonic-gate 
379*0Sstevel@tonic-gate 		/* allocate space to hold all semaphore values */
380*0Sstevel@tonic-gate 		vals = kmem_alloc(vsize, KM_SLEEP);
381*0Sstevel@tonic-gate 
382*0Sstevel@tonic-gate 		if (copyin((void *)arg, vals, vsize)) {
383*0Sstevel@tonic-gate 			kmem_free(vals, vsize);
384*0Sstevel@tonic-gate 			return (set_errno(EFAULT));
385*0Sstevel@tonic-gate 		}
386*0Sstevel@tonic-gate 		break;
387*0Sstevel@tonic-gate 
388*0Sstevel@tonic-gate 	case IPC_RMID:
389*0Sstevel@tonic-gate 		if (error = ipc_rmid(sem_svc, semid, cr))
390*0Sstevel@tonic-gate 			return (set_errno(error));
391*0Sstevel@tonic-gate 		return (0);
392*0Sstevel@tonic-gate 	}
393*0Sstevel@tonic-gate 
394*0Sstevel@tonic-gate 	if ((lock = ipc_lookup(sem_svc, semid, (kipc_perm_t **)&sp)) == NULL) {
395*0Sstevel@tonic-gate 		if (vsize != 0)
396*0Sstevel@tonic-gate 			kmem_free(vals, vsize);
397*0Sstevel@tonic-gate 		return (set_errno(EINVAL));
398*0Sstevel@tonic-gate 	}
399*0Sstevel@tonic-gate 	switch (cmd) {
400*0Sstevel@tonic-gate 	/* Set ownership and permissions. */
401*0Sstevel@tonic-gate 	case IPC_SET:
402*0Sstevel@tonic-gate 
403*0Sstevel@tonic-gate 		if (error = ipcperm_set(sem_svc, cr, &sp->sem_perm,
404*0Sstevel@tonic-gate 		    &STRUCT_BUF(sid)->sem_perm, mdl)) {
405*0Sstevel@tonic-gate 			mutex_exit(lock);
406*0Sstevel@tonic-gate 			return (set_errno(error));
407*0Sstevel@tonic-gate 		}
408*0Sstevel@tonic-gate 		sp->sem_ctime = gethrestime_sec();
409*0Sstevel@tonic-gate 		mutex_exit(lock);
410*0Sstevel@tonic-gate 		return (0);
411*0Sstevel@tonic-gate 
412*0Sstevel@tonic-gate 	/* Get semaphore data structure. */
413*0Sstevel@tonic-gate 	case IPC_STAT:
414*0Sstevel@tonic-gate 
415*0Sstevel@tonic-gate 		if (error = ipcperm_access(&sp->sem_perm, SEM_R, cr)) {
416*0Sstevel@tonic-gate 			mutex_exit(lock);
417*0Sstevel@tonic-gate 			return (set_errno(error));
418*0Sstevel@tonic-gate 		}
419*0Sstevel@tonic-gate 
420*0Sstevel@tonic-gate 		ipcperm_stat(&STRUCT_BUF(sid)->sem_perm, &sp->sem_perm, mdl);
421*0Sstevel@tonic-gate 		STRUCT_FSETP(sid, sem_base, NULL);	/* kernel addr */
422*0Sstevel@tonic-gate 		STRUCT_FSET(sid, sem_nsems, sp->sem_nsems);
423*0Sstevel@tonic-gate 		STRUCT_FSET(sid, sem_otime, sp->sem_otime);
424*0Sstevel@tonic-gate 		STRUCT_FSET(sid, sem_ctime, sp->sem_ctime);
425*0Sstevel@tonic-gate 		STRUCT_FSET(sid, sem_binary, sp->sem_binary);
426*0Sstevel@tonic-gate 		mutex_exit(lock);
427*0Sstevel@tonic-gate 
428*0Sstevel@tonic-gate 		if (copyout(STRUCT_BUF(sid), (void *)arg, STRUCT_SIZE(sid)))
429*0Sstevel@tonic-gate 			return (set_errno(EFAULT));
430*0Sstevel@tonic-gate 		return (0);
431*0Sstevel@tonic-gate 
432*0Sstevel@tonic-gate 	case IPC_SET64:
433*0Sstevel@tonic-gate 
434*0Sstevel@tonic-gate 		if (error = ipcperm_set64(sem_svc, cr, &sp->sem_perm,
435*0Sstevel@tonic-gate 		    &ds64.semx_perm)) {
436*0Sstevel@tonic-gate 			mutex_exit(lock);
437*0Sstevel@tonic-gate 			return (set_errno(error));
438*0Sstevel@tonic-gate 		}
439*0Sstevel@tonic-gate 		sp->sem_ctime = gethrestime_sec();
440*0Sstevel@tonic-gate 		mutex_exit(lock);
441*0Sstevel@tonic-gate 		return (0);
442*0Sstevel@tonic-gate 
443*0Sstevel@tonic-gate 	case IPC_STAT64:
444*0Sstevel@tonic-gate 
445*0Sstevel@tonic-gate 		ipcperm_stat64(&ds64.semx_perm, &sp->sem_perm);
446*0Sstevel@tonic-gate 		ds64.semx_nsems = sp->sem_nsems;
447*0Sstevel@tonic-gate 		ds64.semx_otime = sp->sem_otime;
448*0Sstevel@tonic-gate 		ds64.semx_ctime = sp->sem_ctime;
449*0Sstevel@tonic-gate 
450*0Sstevel@tonic-gate 		mutex_exit(lock);
451*0Sstevel@tonic-gate 		if (copyout(&ds64, (void *)arg, sizeof (struct semid_ds64)))
452*0Sstevel@tonic-gate 			return (set_errno(EFAULT));
453*0Sstevel@tonic-gate 
454*0Sstevel@tonic-gate 		return (0);
455*0Sstevel@tonic-gate 
456*0Sstevel@tonic-gate 	/* Get # of processes sleeping for greater semval. */
457*0Sstevel@tonic-gate 	case GETNCNT:
458*0Sstevel@tonic-gate 		if (error = ipcperm_access(&sp->sem_perm, SEM_R, cr)) {
459*0Sstevel@tonic-gate 			mutex_exit(lock);
460*0Sstevel@tonic-gate 			return (set_errno(error));
461*0Sstevel@tonic-gate 		}
462*0Sstevel@tonic-gate 		if (semnum >= sp->sem_nsems) {
463*0Sstevel@tonic-gate 			mutex_exit(lock);
464*0Sstevel@tonic-gate 			return (set_errno(EINVAL));
465*0Sstevel@tonic-gate 		}
466*0Sstevel@tonic-gate 		retval = sp->sem_base[semnum].semncnt;
467*0Sstevel@tonic-gate 		mutex_exit(lock);
468*0Sstevel@tonic-gate 		return (retval);
469*0Sstevel@tonic-gate 
470*0Sstevel@tonic-gate 	/* Get pid of last process to operate on semaphore. */
471*0Sstevel@tonic-gate 	case GETPID:
472*0Sstevel@tonic-gate 		if (error = ipcperm_access(&sp->sem_perm, SEM_R, cr)) {
473*0Sstevel@tonic-gate 			mutex_exit(lock);
474*0Sstevel@tonic-gate 			return (set_errno(error));
475*0Sstevel@tonic-gate 		}
476*0Sstevel@tonic-gate 		if (semnum >= sp->sem_nsems) {
477*0Sstevel@tonic-gate 			mutex_exit(lock);
478*0Sstevel@tonic-gate 			return (set_errno(EINVAL));
479*0Sstevel@tonic-gate 		}
480*0Sstevel@tonic-gate 		retval = sp->sem_base[semnum].sempid;
481*0Sstevel@tonic-gate 		mutex_exit(lock);
482*0Sstevel@tonic-gate 		return (retval);
483*0Sstevel@tonic-gate 
484*0Sstevel@tonic-gate 	/* Get semval of one semaphore. */
485*0Sstevel@tonic-gate 	case GETVAL:
486*0Sstevel@tonic-gate 		if (error = ipcperm_access(&sp->sem_perm, SEM_R, cr)) {
487*0Sstevel@tonic-gate 			mutex_exit(lock);
488*0Sstevel@tonic-gate 			return (set_errno(error));
489*0Sstevel@tonic-gate 		}
490*0Sstevel@tonic-gate 		if (semnum >= sp->sem_nsems) {
491*0Sstevel@tonic-gate 			mutex_exit(lock);
492*0Sstevel@tonic-gate 			return (set_errno(EINVAL));
493*0Sstevel@tonic-gate 		}
494*0Sstevel@tonic-gate 		retval = sp->sem_base[semnum].semval;
495*0Sstevel@tonic-gate 		mutex_exit(lock);
496*0Sstevel@tonic-gate 		return (retval);
497*0Sstevel@tonic-gate 
498*0Sstevel@tonic-gate 	/* Get all semvals in set. */
499*0Sstevel@tonic-gate 	case GETALL:
500*0Sstevel@tonic-gate 		if (error = ipcperm_access(&sp->sem_perm, SEM_R, cr)) {
501*0Sstevel@tonic-gate 			mutex_exit(lock);
502*0Sstevel@tonic-gate 			return (set_errno(error));
503*0Sstevel@tonic-gate 		}
504*0Sstevel@tonic-gate 
505*0Sstevel@tonic-gate 		/* allocate space to hold all semaphore values */
506*0Sstevel@tonic-gate 		vsize = sp->sem_nsems * sizeof (*vals);
507*0Sstevel@tonic-gate 		vals = vp = kmem_alloc(vsize, KM_SLEEP);
508*0Sstevel@tonic-gate 
509*0Sstevel@tonic-gate 		for (i = sp->sem_nsems, p = sp->sem_base; i--; p++, vp++)
510*0Sstevel@tonic-gate 			bcopy(&p->semval, vp, sizeof (p->semval));
511*0Sstevel@tonic-gate 
512*0Sstevel@tonic-gate 		mutex_exit(lock);
513*0Sstevel@tonic-gate 
514*0Sstevel@tonic-gate 		if (copyout((void *)vals, (void *)arg, vsize)) {
515*0Sstevel@tonic-gate 			kmem_free(vals, vsize);
516*0Sstevel@tonic-gate 			return (set_errno(EFAULT));
517*0Sstevel@tonic-gate 		}
518*0Sstevel@tonic-gate 
519*0Sstevel@tonic-gate 		kmem_free(vals, vsize);
520*0Sstevel@tonic-gate 		return (0);
521*0Sstevel@tonic-gate 
522*0Sstevel@tonic-gate 	/* Get # of processes sleeping for semval to become zero. */
523*0Sstevel@tonic-gate 	case GETZCNT:
524*0Sstevel@tonic-gate 		if (error = ipcperm_access(&sp->sem_perm, SEM_R, cr)) {
525*0Sstevel@tonic-gate 			mutex_exit(lock);
526*0Sstevel@tonic-gate 			return (set_errno(error));
527*0Sstevel@tonic-gate 		}
528*0Sstevel@tonic-gate 		if (semnum >= sp->sem_nsems) {
529*0Sstevel@tonic-gate 			mutex_exit(lock);
530*0Sstevel@tonic-gate 			return (set_errno(EINVAL));
531*0Sstevel@tonic-gate 		}
532*0Sstevel@tonic-gate 		retval = sp->sem_base[semnum].semzcnt;
533*0Sstevel@tonic-gate 		mutex_exit(lock);
534*0Sstevel@tonic-gate 		return (retval);
535*0Sstevel@tonic-gate 
536*0Sstevel@tonic-gate 	/* Set semval of one semaphore. */
537*0Sstevel@tonic-gate 	case SETVAL:
538*0Sstevel@tonic-gate 		if (error = ipcperm_access(&sp->sem_perm, SEM_A, cr)) {
539*0Sstevel@tonic-gate 			mutex_exit(lock);
540*0Sstevel@tonic-gate 			return (set_errno(error));
541*0Sstevel@tonic-gate 		}
542*0Sstevel@tonic-gate 		if (semnum >= sp->sem_nsems) {
543*0Sstevel@tonic-gate 			mutex_exit(lock);
544*0Sstevel@tonic-gate 			return (set_errno(EINVAL));
545*0Sstevel@tonic-gate 		}
546*0Sstevel@tonic-gate 		if ((uint_t)arg > USHRT_MAX) {
547*0Sstevel@tonic-gate 			mutex_exit(lock);
548*0Sstevel@tonic-gate 			return (set_errno(ERANGE));
549*0Sstevel@tonic-gate 		}
550*0Sstevel@tonic-gate 		p = &sp->sem_base[semnum];
551*0Sstevel@tonic-gate 		if ((p->semval = (ushort_t)arg) != 0) {
552*0Sstevel@tonic-gate 			if (p->semncnt) {
553*0Sstevel@tonic-gate 				cv_broadcast(&p->semncnt_cv);
554*0Sstevel@tonic-gate 			}
555*0Sstevel@tonic-gate 		} else if (p->semzcnt) {
556*0Sstevel@tonic-gate 			cv_broadcast(&p->semzcnt_cv);
557*0Sstevel@tonic-gate 		}
558*0Sstevel@tonic-gate 		p->sempid = curproc->p_pid;
559*0Sstevel@tonic-gate 		sem_undo_clear(sp, (ushort_t)semnum, (ushort_t)semnum);
560*0Sstevel@tonic-gate 		mutex_exit(lock);
561*0Sstevel@tonic-gate 		return (0);
562*0Sstevel@tonic-gate 
563*0Sstevel@tonic-gate 	/* Set semvals of all semaphores in set. */
564*0Sstevel@tonic-gate 	case SETALL:
565*0Sstevel@tonic-gate 		/* Check if semaphore set has been deleted and reallocated. */
566*0Sstevel@tonic-gate 		if (sp->sem_nsems * sizeof (*vals) != vsize) {
567*0Sstevel@tonic-gate 			error = set_errno(EINVAL);
568*0Sstevel@tonic-gate 			goto seterr;
569*0Sstevel@tonic-gate 		}
570*0Sstevel@tonic-gate 		if (error = ipcperm_access(&sp->sem_perm, SEM_A, cr)) {
571*0Sstevel@tonic-gate 			error = set_errno(error);
572*0Sstevel@tonic-gate 			goto seterr;
573*0Sstevel@tonic-gate 		}
574*0Sstevel@tonic-gate 		sem_undo_clear(sp, 0, sp->sem_nsems - 1);
575*0Sstevel@tonic-gate 		for (i = 0, p = sp->sem_base; i < sp->sem_nsems;
576*0Sstevel@tonic-gate 		    (p++)->sempid = curproc->p_pid) {
577*0Sstevel@tonic-gate 			if ((p->semval = vals[i++]) != 0) {
578*0Sstevel@tonic-gate 				if (p->semncnt) {
579*0Sstevel@tonic-gate 					cv_broadcast(&p->semncnt_cv);
580*0Sstevel@tonic-gate 				}
581*0Sstevel@tonic-gate 			} else if (p->semzcnt) {
582*0Sstevel@tonic-gate 				cv_broadcast(&p->semzcnt_cv);
583*0Sstevel@tonic-gate 			}
584*0Sstevel@tonic-gate 		}
585*0Sstevel@tonic-gate seterr:
586*0Sstevel@tonic-gate 		mutex_exit(lock);
587*0Sstevel@tonic-gate 		kmem_free(vals, vsize);
588*0Sstevel@tonic-gate 		return (error);
589*0Sstevel@tonic-gate 
590*0Sstevel@tonic-gate 	default:
591*0Sstevel@tonic-gate 		mutex_exit(lock);
592*0Sstevel@tonic-gate 		return (set_errno(EINVAL));
593*0Sstevel@tonic-gate 	}
594*0Sstevel@tonic-gate 
595*0Sstevel@tonic-gate 	/* NOTREACHED */
596*0Sstevel@tonic-gate }
597*0Sstevel@tonic-gate 
598*0Sstevel@tonic-gate /*
599*0Sstevel@tonic-gate  * semexit - Called by exit() to clean up on process exit.
600*0Sstevel@tonic-gate  */
601*0Sstevel@tonic-gate void
602*0Sstevel@tonic-gate semexit(proc_t *pp)
603*0Sstevel@tonic-gate {
604*0Sstevel@tonic-gate 	avl_tree_t	*tree;
605*0Sstevel@tonic-gate 	struct sem_undo	*undo;
606*0Sstevel@tonic-gate 	void		*cookie = NULL;
607*0Sstevel@tonic-gate 
608*0Sstevel@tonic-gate 	mutex_enter(&pp->p_lock);
609*0Sstevel@tonic-gate 	tree = pp->p_semacct;
610*0Sstevel@tonic-gate 	pp->p_semacct = NULL;
611*0Sstevel@tonic-gate 	mutex_exit(&pp->p_lock);
612*0Sstevel@tonic-gate 
613*0Sstevel@tonic-gate 	while (undo = avl_destroy_nodes(tree, &cookie)) {
614*0Sstevel@tonic-gate 		ksemid_t *sp = undo->un_sp;
615*0Sstevel@tonic-gate 		size_t size = SEM_UNDOSZ(sp->sem_nsems);
616*0Sstevel@tonic-gate 		int i;
617*0Sstevel@tonic-gate 
618*0Sstevel@tonic-gate 		(void) ipc_lock(sem_svc, sp->sem_perm.ipc_id);
619*0Sstevel@tonic-gate 		if (!IPC_FREE(&sp->sem_perm)) {
620*0Sstevel@tonic-gate 			for (i = 0; i < sp->sem_nsems; i++) {
621*0Sstevel@tonic-gate 				int adj = undo->un_aoe[i];
622*0Sstevel@tonic-gate 				if (adj) {
623*0Sstevel@tonic-gate 					struct sem *semp = &sp->sem_base[i];
624*0Sstevel@tonic-gate 					int v = (int)semp->semval + adj;
625*0Sstevel@tonic-gate 
626*0Sstevel@tonic-gate 					if (v < 0 || v > USHRT_MAX)
627*0Sstevel@tonic-gate 						continue;
628*0Sstevel@tonic-gate 					semp->semval = (ushort_t)v;
629*0Sstevel@tonic-gate 					if (v == 0 && semp->semzcnt)
630*0Sstevel@tonic-gate 						cv_broadcast(&semp->semzcnt_cv);
631*0Sstevel@tonic-gate 					if (adj > 0 && semp->semncnt)
632*0Sstevel@tonic-gate 						cv_broadcast(&semp->semncnt_cv);
633*0Sstevel@tonic-gate 				}
634*0Sstevel@tonic-gate 			}
635*0Sstevel@tonic-gate 			list_remove(&sp->sem_undos, undo);
636*0Sstevel@tonic-gate 		}
637*0Sstevel@tonic-gate 		ipc_rele(sem_svc, (kipc_perm_t *)sp);
638*0Sstevel@tonic-gate 		kmem_free(undo, size);
639*0Sstevel@tonic-gate 	}
640*0Sstevel@tonic-gate 
641*0Sstevel@tonic-gate 	avl_destroy(tree);
642*0Sstevel@tonic-gate 	kmem_free(tree, sizeof (avl_tree_t));
643*0Sstevel@tonic-gate }
644*0Sstevel@tonic-gate 
645*0Sstevel@tonic-gate /*
646*0Sstevel@tonic-gate  * Remove all semaphores associated with a given zone.  Called by
647*0Sstevel@tonic-gate  * zone_shutdown when the zone is halted.
648*0Sstevel@tonic-gate  */
649*0Sstevel@tonic-gate /*ARGSUSED1*/
650*0Sstevel@tonic-gate static void
651*0Sstevel@tonic-gate sem_remove_zone(zoneid_t zoneid, void *arg)
652*0Sstevel@tonic-gate {
653*0Sstevel@tonic-gate 	ipc_remove_zone(sem_svc, zoneid);
654*0Sstevel@tonic-gate }
655*0Sstevel@tonic-gate 
656*0Sstevel@tonic-gate /*
657*0Sstevel@tonic-gate  * semget - Semget system call.
658*0Sstevel@tonic-gate  */
659*0Sstevel@tonic-gate static int
660*0Sstevel@tonic-gate semget(key_t key, int nsems, int semflg)
661*0Sstevel@tonic-gate {
662*0Sstevel@tonic-gate 	ksemid_t	*sp;
663*0Sstevel@tonic-gate 	kmutex_t	*lock;
664*0Sstevel@tonic-gate 	int		id, error;
665*0Sstevel@tonic-gate 	proc_t		*pp = curproc;
666*0Sstevel@tonic-gate 
667*0Sstevel@tonic-gate top:
668*0Sstevel@tonic-gate 	if (error = ipc_get(sem_svc, key, semflg, (kipc_perm_t **)&sp, &lock))
669*0Sstevel@tonic-gate 		return (set_errno(error));
670*0Sstevel@tonic-gate 
671*0Sstevel@tonic-gate 	if (!IPC_FREE(&sp->sem_perm)) {
672*0Sstevel@tonic-gate 		/*
673*0Sstevel@tonic-gate 		 * A semaphore with the requested key exists.
674*0Sstevel@tonic-gate 		 */
675*0Sstevel@tonic-gate 		if (!((nsems >= 0) && (nsems <= sp->sem_nsems))) {
676*0Sstevel@tonic-gate 			mutex_exit(lock);
677*0Sstevel@tonic-gate 			return (set_errno(EINVAL));
678*0Sstevel@tonic-gate 		}
679*0Sstevel@tonic-gate 	} else {
680*0Sstevel@tonic-gate 		/*
681*0Sstevel@tonic-gate 		 * This is a new semaphore set.  Finish initialization.
682*0Sstevel@tonic-gate 		 */
683*0Sstevel@tonic-gate 		if (nsems <= 0 || (rctl_test(rc_process_semmsl, pp->p_rctls, pp,
684*0Sstevel@tonic-gate 		    nsems, RCA_SAFE) & RCT_DENY)) {
685*0Sstevel@tonic-gate 			mutex_exit(lock);
686*0Sstevel@tonic-gate 			mutex_exit(&pp->p_lock);
687*0Sstevel@tonic-gate 			ipc_cleanup(sem_svc, (kipc_perm_t *)sp);
688*0Sstevel@tonic-gate 			return (set_errno(EINVAL));
689*0Sstevel@tonic-gate 		}
690*0Sstevel@tonic-gate 		mutex_exit(lock);
691*0Sstevel@tonic-gate 		mutex_exit(&pp->p_lock);
692*0Sstevel@tonic-gate 
693*0Sstevel@tonic-gate 		/*
694*0Sstevel@tonic-gate 		 * We round the allocation up to coherency granularity
695*0Sstevel@tonic-gate 		 * so that multiple semaphore allocations won't result
696*0Sstevel@tonic-gate 		 * in the false sharing of their sem structures.
697*0Sstevel@tonic-gate 		 */
698*0Sstevel@tonic-gate 		sp->sem_base =
699*0Sstevel@tonic-gate 		    kmem_zalloc(P2ROUNDUP(nsems * sizeof (struct sem), 64),
700*0Sstevel@tonic-gate 		    KM_SLEEP);
701*0Sstevel@tonic-gate 		sp->sem_binary = (nsems == 1);
702*0Sstevel@tonic-gate 		sp->sem_nsems = (ushort_t)nsems;
703*0Sstevel@tonic-gate 		sp->sem_ctime = gethrestime_sec();
704*0Sstevel@tonic-gate 		sp->sem_otime = 0;
705*0Sstevel@tonic-gate 		list_create(&sp->sem_undos, sizeof (struct sem_undo),
706*0Sstevel@tonic-gate 		    offsetof(struct sem_undo, un_list));
707*0Sstevel@tonic-gate 
708*0Sstevel@tonic-gate 		if (error = ipc_commit_begin(sem_svc, key, semflg,
709*0Sstevel@tonic-gate 		    (kipc_perm_t *)sp)) {
710*0Sstevel@tonic-gate 			if (error == EAGAIN)
711*0Sstevel@tonic-gate 				goto top;
712*0Sstevel@tonic-gate 			return (set_errno(error));
713*0Sstevel@tonic-gate 		}
714*0Sstevel@tonic-gate 		sp->sem_maxops =
715*0Sstevel@tonic-gate 		    rctl_enforced_value(rc_process_semopm, pp->p_rctls, pp);
716*0Sstevel@tonic-gate 		if (rctl_test(rc_process_semmsl, pp->p_rctls, pp, nsems,
717*0Sstevel@tonic-gate 		    RCA_SAFE) & RCT_DENY) {
718*0Sstevel@tonic-gate 			ipc_cleanup(sem_svc, (kipc_perm_t *)sp);
719*0Sstevel@tonic-gate 			return (set_errno(EINVAL));
720*0Sstevel@tonic-gate 		}
721*0Sstevel@tonic-gate 		lock = ipc_commit_end(sem_svc, &sp->sem_perm);
722*0Sstevel@tonic-gate 	}
723*0Sstevel@tonic-gate #ifdef C2_AUDIT
724*0Sstevel@tonic-gate 	if (audit_active)
725*0Sstevel@tonic-gate 		audit_ipcget(AT_IPC_SEM, (void *)sp);
726*0Sstevel@tonic-gate #endif
727*0Sstevel@tonic-gate 	id = sp->sem_perm.ipc_id;
728*0Sstevel@tonic-gate 	mutex_exit(lock);
729*0Sstevel@tonic-gate 	return (id);
730*0Sstevel@tonic-gate }
731*0Sstevel@tonic-gate 
732*0Sstevel@tonic-gate /*
733*0Sstevel@tonic-gate  * semids system call.
734*0Sstevel@tonic-gate  */
735*0Sstevel@tonic-gate static int
736*0Sstevel@tonic-gate semids(int *buf, uint_t nids, uint_t *pnids)
737*0Sstevel@tonic-gate {
738*0Sstevel@tonic-gate 	int error;
739*0Sstevel@tonic-gate 
740*0Sstevel@tonic-gate 	if (error = ipc_ids(sem_svc, buf, nids, pnids))
741*0Sstevel@tonic-gate 		return (set_errno(error));
742*0Sstevel@tonic-gate 
743*0Sstevel@tonic-gate 	return (0);
744*0Sstevel@tonic-gate }
745*0Sstevel@tonic-gate 
746*0Sstevel@tonic-gate 
747*0Sstevel@tonic-gate /*
748*0Sstevel@tonic-gate  * Helper function for semop - copies in the provided timespec and
749*0Sstevel@tonic-gate  * computes the absolute future time after which we must return.
750*0Sstevel@tonic-gate  */
751*0Sstevel@tonic-gate static int
752*0Sstevel@tonic-gate compute_timeout(timespec_t **tsp, timespec_t *ts, timespec_t *now,
753*0Sstevel@tonic-gate 	timespec_t *timeout)
754*0Sstevel@tonic-gate {
755*0Sstevel@tonic-gate 	model_t datamodel = get_udatamodel();
756*0Sstevel@tonic-gate 
757*0Sstevel@tonic-gate 	if (datamodel == DATAMODEL_NATIVE) {
758*0Sstevel@tonic-gate 		if (copyin(timeout, ts, sizeof (timespec_t)))
759*0Sstevel@tonic-gate 			return (EFAULT);
760*0Sstevel@tonic-gate 	} else {
761*0Sstevel@tonic-gate 		timespec32_t ts32;
762*0Sstevel@tonic-gate 
763*0Sstevel@tonic-gate 		if (copyin(timeout, &ts32, sizeof (timespec32_t)))
764*0Sstevel@tonic-gate 			return (EFAULT);
765*0Sstevel@tonic-gate 		TIMESPEC32_TO_TIMESPEC(ts, &ts32)
766*0Sstevel@tonic-gate 	}
767*0Sstevel@tonic-gate 
768*0Sstevel@tonic-gate 	if (itimerspecfix(ts))
769*0Sstevel@tonic-gate 		return (EINVAL);
770*0Sstevel@tonic-gate 
771*0Sstevel@tonic-gate 	/*
772*0Sstevel@tonic-gate 	 * Convert the timespec value into absolute time.
773*0Sstevel@tonic-gate 	 */
774*0Sstevel@tonic-gate 	timespecadd(ts, now);
775*0Sstevel@tonic-gate 	*tsp = ts;
776*0Sstevel@tonic-gate 
777*0Sstevel@tonic-gate 	return (0);
778*0Sstevel@tonic-gate }
779*0Sstevel@tonic-gate 
780*0Sstevel@tonic-gate /*
781*0Sstevel@tonic-gate  * Undo structure comparator.  We sort based on ksemid_t pointer.
782*0Sstevel@tonic-gate  */
783*0Sstevel@tonic-gate static int
784*0Sstevel@tonic-gate sem_undo_compar(const void *x, const void *y)
785*0Sstevel@tonic-gate {
786*0Sstevel@tonic-gate 	struct sem_undo *undo1 = (struct sem_undo *)x;
787*0Sstevel@tonic-gate 	struct sem_undo *undo2 = (struct sem_undo *)y;
788*0Sstevel@tonic-gate 
789*0Sstevel@tonic-gate 	if (undo1->un_sp < undo2->un_sp)
790*0Sstevel@tonic-gate 		return (-1);
791*0Sstevel@tonic-gate 	if (undo1->un_sp > undo2->un_sp)
792*0Sstevel@tonic-gate 		return (1);
793*0Sstevel@tonic-gate 	return (0);
794*0Sstevel@tonic-gate }
795*0Sstevel@tonic-gate 
796*0Sstevel@tonic-gate /*
797*0Sstevel@tonic-gate  * Helper function for semop - creates an undo structure and adds it to
798*0Sstevel@tonic-gate  * the process's avl tree and the semaphore's list.
799*0Sstevel@tonic-gate  */
800*0Sstevel@tonic-gate static int
801*0Sstevel@tonic-gate sem_undo_alloc(proc_t *pp, ksemid_t *sp, kmutex_t **lock,
802*0Sstevel@tonic-gate     struct sem_undo *template, struct sem_undo **un)
803*0Sstevel@tonic-gate {
804*0Sstevel@tonic-gate 	size_t size;
805*0Sstevel@tonic-gate 	struct sem_undo *undo;
806*0Sstevel@tonic-gate 	avl_tree_t *tree = NULL;
807*0Sstevel@tonic-gate 	avl_index_t where;
808*0Sstevel@tonic-gate 
809*0Sstevel@tonic-gate 	mutex_exit(*lock);
810*0Sstevel@tonic-gate 
811*0Sstevel@tonic-gate 	size = SEM_UNDOSZ(sp->sem_nsems);
812*0Sstevel@tonic-gate 	undo = kmem_zalloc(size, KM_SLEEP);
813*0Sstevel@tonic-gate 	undo->un_proc = pp;
814*0Sstevel@tonic-gate 	undo->un_sp = sp;
815*0Sstevel@tonic-gate 
816*0Sstevel@tonic-gate 	if (pp->p_semacct == NULL)
817*0Sstevel@tonic-gate 		tree = kmem_alloc(sizeof (avl_tree_t), KM_SLEEP);
818*0Sstevel@tonic-gate 
819*0Sstevel@tonic-gate 	*lock = ipc_lock(sem_svc, sp->sem_perm.ipc_id);
820*0Sstevel@tonic-gate 	if (IPC_FREE(&sp->sem_perm)) {
821*0Sstevel@tonic-gate 		kmem_free(undo, size);
822*0Sstevel@tonic-gate 		if (tree)
823*0Sstevel@tonic-gate 			kmem_free(tree, sizeof (avl_tree_t));
824*0Sstevel@tonic-gate 		return (EIDRM);
825*0Sstevel@tonic-gate 	}
826*0Sstevel@tonic-gate 
827*0Sstevel@tonic-gate 	mutex_enter(&pp->p_lock);
828*0Sstevel@tonic-gate 	if (tree) {
829*0Sstevel@tonic-gate 		if (pp->p_semacct == NULL) {
830*0Sstevel@tonic-gate 			avl_create(tree, sem_undo_compar,
831*0Sstevel@tonic-gate 			    sizeof (struct sem_undo),
832*0Sstevel@tonic-gate 			    offsetof(struct sem_undo, un_avl));
833*0Sstevel@tonic-gate 			pp->p_semacct = tree;
834*0Sstevel@tonic-gate 		} else {
835*0Sstevel@tonic-gate 			kmem_free(tree, sizeof (avl_tree_t));
836*0Sstevel@tonic-gate 		}
837*0Sstevel@tonic-gate 	}
838*0Sstevel@tonic-gate 
839*0Sstevel@tonic-gate 	if (*un = avl_find(pp->p_semacct, template, &where)) {
840*0Sstevel@tonic-gate 		mutex_exit(&pp->p_lock);
841*0Sstevel@tonic-gate 		kmem_free(undo, size);
842*0Sstevel@tonic-gate 	} else {
843*0Sstevel@tonic-gate 		*un = undo;
844*0Sstevel@tonic-gate 		avl_insert(pp->p_semacct, undo, where);
845*0Sstevel@tonic-gate 		mutex_exit(&pp->p_lock);
846*0Sstevel@tonic-gate 		list_insert_head(&sp->sem_undos, undo);
847*0Sstevel@tonic-gate 		ipc_hold(sem_svc, (kipc_perm_t *)sp);
848*0Sstevel@tonic-gate 	}
849*0Sstevel@tonic-gate 
850*0Sstevel@tonic-gate 
851*0Sstevel@tonic-gate 	return (0);
852*0Sstevel@tonic-gate }
853*0Sstevel@tonic-gate 
854*0Sstevel@tonic-gate /*
855*0Sstevel@tonic-gate  * semop - Semop system call.
856*0Sstevel@tonic-gate  */
857*0Sstevel@tonic-gate static int
858*0Sstevel@tonic-gate semop(int semid, struct sembuf *sops, size_t nsops, timespec_t *timeout)
859*0Sstevel@tonic-gate {
860*0Sstevel@tonic-gate 	ksemid_t	*sp = NULL;
861*0Sstevel@tonic-gate 	kmutex_t	*lock;
862*0Sstevel@tonic-gate 	struct sembuf	*op;	/* ptr to operation */
863*0Sstevel@tonic-gate 	int		i;	/* loop control */
864*0Sstevel@tonic-gate 	struct sem	*semp;	/* ptr to semaphore */
865*0Sstevel@tonic-gate 	int 		error = 0;
866*0Sstevel@tonic-gate 	struct sembuf	*uops;	/* ptr to copy of user ops */
867*0Sstevel@tonic-gate 	struct sembuf 	x_sem;	/* avoid kmem_alloc's */
868*0Sstevel@tonic-gate 	timespec_t	now, ts, *tsp = NULL;
869*0Sstevel@tonic-gate 	int		timecheck = 0;
870*0Sstevel@tonic-gate 	int		cvres, needundo, mode;
871*0Sstevel@tonic-gate 	struct sem_undo	*undo;
872*0Sstevel@tonic-gate 	proc_t		*pp = curproc;
873*0Sstevel@tonic-gate 	int		held = 0;
874*0Sstevel@tonic-gate 
875*0Sstevel@tonic-gate 	CPU_STATS_ADDQ(CPU, sys, sema, 1); /* bump semaphore op count */
876*0Sstevel@tonic-gate 
877*0Sstevel@tonic-gate 	/*
878*0Sstevel@tonic-gate 	 * To avoid the cost of copying in 'timeout' in the common
879*0Sstevel@tonic-gate 	 * case, we could only grab the time here and defer the copyin
880*0Sstevel@tonic-gate 	 * and associated computations until we are about to block.
881*0Sstevel@tonic-gate 	 *
882*0Sstevel@tonic-gate 	 * The down side to this is that we would then have to spin
883*0Sstevel@tonic-gate 	 * some goto top nonsense to avoid the copyin behind the semid
884*0Sstevel@tonic-gate 	 * lock.  As a common use of timed semaphores is as an explicit
885*0Sstevel@tonic-gate 	 * blocking mechanism, this could incur a greater penalty.
886*0Sstevel@tonic-gate 	 *
887*0Sstevel@tonic-gate 	 * If we eventually decide that this would be a wise route to
888*0Sstevel@tonic-gate 	 * take, the deferrable functionality is completely contained
889*0Sstevel@tonic-gate 	 * in 'compute_timeout', and the interface is defined such that
890*0Sstevel@tonic-gate 	 * we can legally not validate 'timeout' if it is unused.
891*0Sstevel@tonic-gate 	 */
892*0Sstevel@tonic-gate 	if (timeout != NULL) {
893*0Sstevel@tonic-gate 		timecheck = timechanged;
894*0Sstevel@tonic-gate 		gethrestime(&now);
895*0Sstevel@tonic-gate 		if (error = compute_timeout(&tsp, &ts, &now, timeout))
896*0Sstevel@tonic-gate 			return (set_errno(error));
897*0Sstevel@tonic-gate 	}
898*0Sstevel@tonic-gate 
899*0Sstevel@tonic-gate 	/*
900*0Sstevel@tonic-gate 	 * Allocate space to hold the vector of semaphore ops.  If
901*0Sstevel@tonic-gate 	 * there is only 1 operation we use a preallocated buffer on
902*0Sstevel@tonic-gate 	 * the stack for speed.
903*0Sstevel@tonic-gate 	 *
904*0Sstevel@tonic-gate 	 * Since we don't want to allow the user to allocate an
905*0Sstevel@tonic-gate 	 * arbitrary amount of kernel memory, we need to check against
906*0Sstevel@tonic-gate 	 * the number of operations allowed by the semaphore.  We only
907*0Sstevel@tonic-gate 	 * bother doing this if the number of operations is larger than
908*0Sstevel@tonic-gate 	 * SEM_MAXUCOPS.
909*0Sstevel@tonic-gate 	 */
910*0Sstevel@tonic-gate 	if (nsops == 1)
911*0Sstevel@tonic-gate 		uops = &x_sem;
912*0Sstevel@tonic-gate 	else if (nsops == 0)
913*0Sstevel@tonic-gate 		return (0);
914*0Sstevel@tonic-gate 	else if (nsops <= SEM_MAXUCOPS)
915*0Sstevel@tonic-gate 		uops = kmem_alloc(nsops * sizeof (*uops), KM_SLEEP);
916*0Sstevel@tonic-gate 
917*0Sstevel@tonic-gate 	if (nsops > SEM_MAXUCOPS) {
918*0Sstevel@tonic-gate 		if ((lock = ipc_lookup(sem_svc, semid,
919*0Sstevel@tonic-gate 		    (kipc_perm_t **)&sp)) == NULL)
920*0Sstevel@tonic-gate 			return (set_errno(EFAULT));
921*0Sstevel@tonic-gate 
922*0Sstevel@tonic-gate 		if (nsops > sp->sem_maxops) {
923*0Sstevel@tonic-gate 			mutex_exit(lock);
924*0Sstevel@tonic-gate 			return (set_errno(E2BIG));
925*0Sstevel@tonic-gate 		}
926*0Sstevel@tonic-gate 		held = 1;
927*0Sstevel@tonic-gate 		ipc_hold(sem_svc, (kipc_perm_t *)sp);
928*0Sstevel@tonic-gate 		mutex_exit(lock);
929*0Sstevel@tonic-gate 
930*0Sstevel@tonic-gate 		uops = kmem_alloc(nsops * sizeof (*uops), KM_SLEEP);
931*0Sstevel@tonic-gate 		if (copyin(sops, uops, nsops * sizeof (*op))) {
932*0Sstevel@tonic-gate 			error = EFAULT;
933*0Sstevel@tonic-gate 			(void) ipc_lock(sem_svc, sp->sem_perm.ipc_id);
934*0Sstevel@tonic-gate 			goto semoperr;
935*0Sstevel@tonic-gate 		}
936*0Sstevel@tonic-gate 
937*0Sstevel@tonic-gate 		lock = ipc_lock(sem_svc, sp->sem_perm.ipc_id);
938*0Sstevel@tonic-gate 		if (IPC_FREE(&sp->sem_perm)) {
939*0Sstevel@tonic-gate 			error = EIDRM;
940*0Sstevel@tonic-gate 			goto semoperr;
941*0Sstevel@tonic-gate 		}
942*0Sstevel@tonic-gate 	} else {
943*0Sstevel@tonic-gate 		/*
944*0Sstevel@tonic-gate 		 * This could be interleaved with the above code, but
945*0Sstevel@tonic-gate 		 * keeping them separate improves readability.
946*0Sstevel@tonic-gate 		 */
947*0Sstevel@tonic-gate 		if (copyin(sops, uops, nsops * sizeof (*op))) {
948*0Sstevel@tonic-gate 			error = EFAULT;
949*0Sstevel@tonic-gate 			goto semoperr_unlocked;
950*0Sstevel@tonic-gate 		}
951*0Sstevel@tonic-gate 
952*0Sstevel@tonic-gate 		if ((lock = ipc_lookup(sem_svc, semid,
953*0Sstevel@tonic-gate 		    (kipc_perm_t **)&sp)) == NULL) {
954*0Sstevel@tonic-gate 			error = EINVAL;
955*0Sstevel@tonic-gate 			goto semoperr_unlocked;
956*0Sstevel@tonic-gate 		}
957*0Sstevel@tonic-gate 
958*0Sstevel@tonic-gate 		if (nsops > sp->sem_maxops) {
959*0Sstevel@tonic-gate 			error = E2BIG;
960*0Sstevel@tonic-gate 			goto semoperr;
961*0Sstevel@tonic-gate 		}
962*0Sstevel@tonic-gate 	}
963*0Sstevel@tonic-gate 
964*0Sstevel@tonic-gate 	/*
965*0Sstevel@tonic-gate 	 * Scan all operations.  Verify that sem #s are in range and
966*0Sstevel@tonic-gate 	 * this process is allowed the requested operations.  If any
967*0Sstevel@tonic-gate 	 * operations are marked SEM_UNDO, find (or allocate) the undo
968*0Sstevel@tonic-gate 	 * structure for this process and semaphore.
969*0Sstevel@tonic-gate 	 */
970*0Sstevel@tonic-gate 	needundo = 0;
971*0Sstevel@tonic-gate 	mode = 0;
972*0Sstevel@tonic-gate 	for (i = 0, op = uops; i++ < nsops; op++) {
973*0Sstevel@tonic-gate 		mode |= op->sem_op ? SEM_A : SEM_R;
974*0Sstevel@tonic-gate 		if (op->sem_num >= sp->sem_nsems) {
975*0Sstevel@tonic-gate 			error = EFBIG;
976*0Sstevel@tonic-gate 			goto semoperr;
977*0Sstevel@tonic-gate 		}
978*0Sstevel@tonic-gate 		if ((op->sem_flg & SEM_UNDO) && op->sem_op)
979*0Sstevel@tonic-gate 			needundo = 1;
980*0Sstevel@tonic-gate 	}
981*0Sstevel@tonic-gate 	if (error = ipcperm_access(&sp->sem_perm, mode, CRED()))
982*0Sstevel@tonic-gate 		goto semoperr;
983*0Sstevel@tonic-gate 
984*0Sstevel@tonic-gate 	if (needundo) {
985*0Sstevel@tonic-gate 		struct sem_undo template;
986*0Sstevel@tonic-gate 
987*0Sstevel@tonic-gate 		template.un_sp = sp;
988*0Sstevel@tonic-gate 		mutex_enter(&pp->p_lock);
989*0Sstevel@tonic-gate 		if (pp->p_semacct)
990*0Sstevel@tonic-gate 			undo = avl_find(pp->p_semacct, &template, NULL);
991*0Sstevel@tonic-gate 		else
992*0Sstevel@tonic-gate 			undo = NULL;
993*0Sstevel@tonic-gate 		mutex_exit(&pp->p_lock);
994*0Sstevel@tonic-gate 		if (undo == NULL) {
995*0Sstevel@tonic-gate 			if (error = sem_undo_alloc(pp, sp, &lock, &template,
996*0Sstevel@tonic-gate 			    &undo))
997*0Sstevel@tonic-gate 				goto semoperr;
998*0Sstevel@tonic-gate 
999*0Sstevel@tonic-gate 			/* sem_undo_alloc unlocks the semaphore */
1000*0Sstevel@tonic-gate 			if (error = ipcperm_access(&sp->sem_perm, mode, CRED()))
1001*0Sstevel@tonic-gate 				goto semoperr;
1002*0Sstevel@tonic-gate 		}
1003*0Sstevel@tonic-gate 	}
1004*0Sstevel@tonic-gate 
1005*0Sstevel@tonic-gate check:
1006*0Sstevel@tonic-gate 	/*
1007*0Sstevel@tonic-gate 	 * Loop waiting for the operations to be satisfied atomically.
1008*0Sstevel@tonic-gate 	 * Actually, do the operations and undo them if a wait is needed
1009*0Sstevel@tonic-gate 	 * or an error is detected.
1010*0Sstevel@tonic-gate 	 */
1011*0Sstevel@tonic-gate 	for (i = 0; i < nsops; i++) {
1012*0Sstevel@tonic-gate 		op = &uops[i];
1013*0Sstevel@tonic-gate 		semp = &sp->sem_base[op->sem_num];
1014*0Sstevel@tonic-gate 
1015*0Sstevel@tonic-gate 		/*
1016*0Sstevel@tonic-gate 		 * Raise the semaphore (i.e. sema_v)
1017*0Sstevel@tonic-gate 		 */
1018*0Sstevel@tonic-gate 		if (op->sem_op > 0) {
1019*0Sstevel@tonic-gate 			if (op->sem_op + (int)semp->semval > USHRT_MAX ||
1020*0Sstevel@tonic-gate 			    ((op->sem_flg & SEM_UNDO) &&
1021*0Sstevel@tonic-gate 			    (error = sem_undo_add(op->sem_op, op->sem_num,
1022*0Sstevel@tonic-gate 			    undo)))) {
1023*0Sstevel@tonic-gate 				if (i)
1024*0Sstevel@tonic-gate 					sem_rollback(sp, uops, i, undo);
1025*0Sstevel@tonic-gate 				if (error == 0)
1026*0Sstevel@tonic-gate 					error = ERANGE;
1027*0Sstevel@tonic-gate 				goto semoperr;
1028*0Sstevel@tonic-gate 			}
1029*0Sstevel@tonic-gate 			semp->semval += op->sem_op;
1030*0Sstevel@tonic-gate 			/*
1031*0Sstevel@tonic-gate 			 * If we are only incrementing the semaphore value
1032*0Sstevel@tonic-gate 			 * by one on a binary semaphore, we can cv_signal.
1033*0Sstevel@tonic-gate 			 */
1034*0Sstevel@tonic-gate 			if (semp->semncnt) {
1035*0Sstevel@tonic-gate 				if (op->sem_op == 1 && sp->sem_binary)
1036*0Sstevel@tonic-gate 					cv_signal(&semp->semncnt_cv);
1037*0Sstevel@tonic-gate 				else
1038*0Sstevel@tonic-gate 					cv_broadcast(&semp->semncnt_cv);
1039*0Sstevel@tonic-gate 			}
1040*0Sstevel@tonic-gate 			if (semp->semzcnt && !semp->semval)
1041*0Sstevel@tonic-gate 				cv_broadcast(&semp->semzcnt_cv);
1042*0Sstevel@tonic-gate 			continue;
1043*0Sstevel@tonic-gate 		}
1044*0Sstevel@tonic-gate 
1045*0Sstevel@tonic-gate 		/*
1046*0Sstevel@tonic-gate 		 * Lower the semaphore (i.e. sema_p)
1047*0Sstevel@tonic-gate 		 */
1048*0Sstevel@tonic-gate 		if (op->sem_op < 0) {
1049*0Sstevel@tonic-gate 			if (semp->semval >= (unsigned)(-op->sem_op)) {
1050*0Sstevel@tonic-gate 				if ((op->sem_flg & SEM_UNDO) &&
1051*0Sstevel@tonic-gate 				    (error = sem_undo_add(op->sem_op,
1052*0Sstevel@tonic-gate 				    op->sem_num, undo))) {
1053*0Sstevel@tonic-gate 					if (i)
1054*0Sstevel@tonic-gate 						sem_rollback(sp, uops, i, undo);
1055*0Sstevel@tonic-gate 					goto semoperr;
1056*0Sstevel@tonic-gate 				}
1057*0Sstevel@tonic-gate 				semp->semval += op->sem_op;
1058*0Sstevel@tonic-gate 				if (semp->semzcnt && !semp->semval)
1059*0Sstevel@tonic-gate 					cv_broadcast(&semp->semzcnt_cv);
1060*0Sstevel@tonic-gate 				continue;
1061*0Sstevel@tonic-gate 			}
1062*0Sstevel@tonic-gate 			if (i)
1063*0Sstevel@tonic-gate 				sem_rollback(sp, uops, i, undo);
1064*0Sstevel@tonic-gate 			if (op->sem_flg & IPC_NOWAIT) {
1065*0Sstevel@tonic-gate 				error = EAGAIN;
1066*0Sstevel@tonic-gate 				goto semoperr;
1067*0Sstevel@tonic-gate 			}
1068*0Sstevel@tonic-gate 
1069*0Sstevel@tonic-gate 			/*
1070*0Sstevel@tonic-gate 			 * Mark the semaphore set as not a binary type
1071*0Sstevel@tonic-gate 			 * if we are decrementing the value by more than 1.
1072*0Sstevel@tonic-gate 			 *
1073*0Sstevel@tonic-gate 			 * V operations will resort to cv_broadcast
1074*0Sstevel@tonic-gate 			 * for this set because there are too many weird
1075*0Sstevel@tonic-gate 			 * cases that have to be caught.
1076*0Sstevel@tonic-gate 			 */
1077*0Sstevel@tonic-gate 			if (op->sem_op < -1)
1078*0Sstevel@tonic-gate 				sp->sem_binary = 0;
1079*0Sstevel@tonic-gate 			if (!held) {
1080*0Sstevel@tonic-gate 				held = 1;
1081*0Sstevel@tonic-gate 				ipc_hold(sem_svc, (kipc_perm_t *)sp);
1082*0Sstevel@tonic-gate 			}
1083*0Sstevel@tonic-gate 			semp->semncnt++;
1084*0Sstevel@tonic-gate 			cvres = cv_waituntil_sig(&semp->semncnt_cv, lock,
1085*0Sstevel@tonic-gate 				tsp, timecheck);
1086*0Sstevel@tonic-gate 			lock = ipc_relock(sem_svc, sp->sem_perm.ipc_id, lock);
1087*0Sstevel@tonic-gate 
1088*0Sstevel@tonic-gate 			if (!IPC_FREE(&sp->sem_perm)) {
1089*0Sstevel@tonic-gate 				ASSERT(semp->semncnt != 0);
1090*0Sstevel@tonic-gate 				semp->semncnt--;
1091*0Sstevel@tonic-gate 				if (cvres > 0)	/* normal wakeup */
1092*0Sstevel@tonic-gate 					goto check;
1093*0Sstevel@tonic-gate 			}
1094*0Sstevel@tonic-gate 
1095*0Sstevel@tonic-gate 			/* EINTR or EAGAIN overrides EIDRM */
1096*0Sstevel@tonic-gate 			if (cvres == 0)
1097*0Sstevel@tonic-gate 				error = EINTR;
1098*0Sstevel@tonic-gate 			else if (cvres < 0)
1099*0Sstevel@tonic-gate 				error = EAGAIN;
1100*0Sstevel@tonic-gate 			else
1101*0Sstevel@tonic-gate 				error = EIDRM;
1102*0Sstevel@tonic-gate 			goto semoperr;
1103*0Sstevel@tonic-gate 		}
1104*0Sstevel@tonic-gate 
1105*0Sstevel@tonic-gate 		/*
1106*0Sstevel@tonic-gate 		 * Wait for zero value
1107*0Sstevel@tonic-gate 		 */
1108*0Sstevel@tonic-gate 		if (semp->semval) {
1109*0Sstevel@tonic-gate 			if (i)
1110*0Sstevel@tonic-gate 				sem_rollback(sp, uops, i, undo);
1111*0Sstevel@tonic-gate 			if (op->sem_flg & IPC_NOWAIT) {
1112*0Sstevel@tonic-gate 				error = EAGAIN;
1113*0Sstevel@tonic-gate 				goto semoperr;
1114*0Sstevel@tonic-gate 			}
1115*0Sstevel@tonic-gate 
1116*0Sstevel@tonic-gate 			if (!held) {
1117*0Sstevel@tonic-gate 				held = 1;
1118*0Sstevel@tonic-gate 				ipc_hold(sem_svc, (kipc_perm_t *)sp);
1119*0Sstevel@tonic-gate 			}
1120*0Sstevel@tonic-gate 			semp->semzcnt++;
1121*0Sstevel@tonic-gate 			cvres = cv_waituntil_sig(&semp->semzcnt_cv, lock,
1122*0Sstevel@tonic-gate 				tsp, timecheck);
1123*0Sstevel@tonic-gate 			lock = ipc_relock(sem_svc, sp->sem_perm.ipc_id, lock);
1124*0Sstevel@tonic-gate 
1125*0Sstevel@tonic-gate 			/*
1126*0Sstevel@tonic-gate 			 * Don't touch semp if the semaphores have been removed.
1127*0Sstevel@tonic-gate 			 */
1128*0Sstevel@tonic-gate 			if (!IPC_FREE(&sp->sem_perm)) {
1129*0Sstevel@tonic-gate 				ASSERT(semp->semzcnt != 0);
1130*0Sstevel@tonic-gate 				semp->semzcnt--;
1131*0Sstevel@tonic-gate 				if (cvres > 0)	/* normal wakeup */
1132*0Sstevel@tonic-gate 					goto check;
1133*0Sstevel@tonic-gate 			}
1134*0Sstevel@tonic-gate 
1135*0Sstevel@tonic-gate 			/* EINTR or EAGAIN overrides EIDRM */
1136*0Sstevel@tonic-gate 			if (cvres == 0)
1137*0Sstevel@tonic-gate 				error = EINTR;
1138*0Sstevel@tonic-gate 			else if (cvres < 0)
1139*0Sstevel@tonic-gate 				error = EAGAIN;
1140*0Sstevel@tonic-gate 			else
1141*0Sstevel@tonic-gate 				error = EIDRM;
1142*0Sstevel@tonic-gate 			goto semoperr;
1143*0Sstevel@tonic-gate 		}
1144*0Sstevel@tonic-gate 	}
1145*0Sstevel@tonic-gate 
1146*0Sstevel@tonic-gate 	/* All operations succeeded.  Update sempid for accessed semaphores. */
1147*0Sstevel@tonic-gate 	for (i = 0, op = uops; i++ < nsops;
1148*0Sstevel@tonic-gate 	    sp->sem_base[(op++)->sem_num].sempid = pp->p_pid)
1149*0Sstevel@tonic-gate 		;
1150*0Sstevel@tonic-gate 	sp->sem_otime = gethrestime_sec();
1151*0Sstevel@tonic-gate 	if (held)
1152*0Sstevel@tonic-gate 		ipc_rele(sem_svc, (kipc_perm_t *)sp);
1153*0Sstevel@tonic-gate 	else
1154*0Sstevel@tonic-gate 		mutex_exit(lock);
1155*0Sstevel@tonic-gate 
1156*0Sstevel@tonic-gate 	/* Before leaving, deallocate the buffer that held the user semops */
1157*0Sstevel@tonic-gate 	if (nsops != 1)
1158*0Sstevel@tonic-gate 		kmem_free(uops, sizeof (*uops) * nsops);
1159*0Sstevel@tonic-gate 	return (0);
1160*0Sstevel@tonic-gate 
1161*0Sstevel@tonic-gate 	/*
1162*0Sstevel@tonic-gate 	 * Error return labels
1163*0Sstevel@tonic-gate 	 */
1164*0Sstevel@tonic-gate semoperr:
1165*0Sstevel@tonic-gate 	if (held)
1166*0Sstevel@tonic-gate 		ipc_rele(sem_svc, (kipc_perm_t *)sp);
1167*0Sstevel@tonic-gate 	else
1168*0Sstevel@tonic-gate 		mutex_exit(lock);
1169*0Sstevel@tonic-gate 
1170*0Sstevel@tonic-gate semoperr_unlocked:
1171*0Sstevel@tonic-gate 
1172*0Sstevel@tonic-gate 	/* Before leaving, deallocate the buffer that held the user semops */
1173*0Sstevel@tonic-gate 	if (nsops != 1)
1174*0Sstevel@tonic-gate 		kmem_free(uops, sizeof (*uops) * nsops);
1175*0Sstevel@tonic-gate 	return (set_errno(error));
1176*0Sstevel@tonic-gate }
1177*0Sstevel@tonic-gate 
1178*0Sstevel@tonic-gate /*
1179*0Sstevel@tonic-gate  * semsys - System entry point for semctl, semget, and semop system calls.
1180*0Sstevel@tonic-gate  */
1181*0Sstevel@tonic-gate static int
1182*0Sstevel@tonic-gate semsys(int opcode, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4)
1183*0Sstevel@tonic-gate {
1184*0Sstevel@tonic-gate 	int error;
1185*0Sstevel@tonic-gate 
1186*0Sstevel@tonic-gate 	switch (opcode) {
1187*0Sstevel@tonic-gate 	case SEMCTL:
1188*0Sstevel@tonic-gate 		error = semctl((int)a1, (uint_t)a2, (int)a3, a4);
1189*0Sstevel@tonic-gate 		break;
1190*0Sstevel@tonic-gate 	case SEMGET:
1191*0Sstevel@tonic-gate 		error = semget((key_t)a1, (int)a2, (int)a3);
1192*0Sstevel@tonic-gate 		break;
1193*0Sstevel@tonic-gate 	case SEMOP:
1194*0Sstevel@tonic-gate 		error = semop((int)a1, (struct sembuf *)a2, (size_t)a3, 0);
1195*0Sstevel@tonic-gate 		break;
1196*0Sstevel@tonic-gate 	case SEMIDS:
1197*0Sstevel@tonic-gate 		error = semids((int *)a1, (uint_t)a2, (uint_t *)a3);
1198*0Sstevel@tonic-gate 		break;
1199*0Sstevel@tonic-gate 	case SEMTIMEDOP:
1200*0Sstevel@tonic-gate 		error = semop((int)a1, (struct sembuf *)a2, (size_t)a3,
1201*0Sstevel@tonic-gate 		    (timespec_t *)a4);
1202*0Sstevel@tonic-gate 		break;
1203*0Sstevel@tonic-gate 	default:
1204*0Sstevel@tonic-gate 		error = set_errno(EINVAL);
1205*0Sstevel@tonic-gate 		break;
1206*0Sstevel@tonic-gate 	}
1207*0Sstevel@tonic-gate 	return (error);
1208*0Sstevel@tonic-gate }
1209