xref: /illumos-gate/usr/src/uts/common/os/semaphore.c (revision d39fefdf)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * This file contains the semaphore operations.
30  */
31 
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/systm.h>
35 #include <sys/semaphore.h>
36 #include <sys/sema_impl.h>
37 #include <sys/t_lock.h>
38 #include <sys/thread.h>
39 #include <sys/proc.h>
40 #include <sys/cmn_err.h>
41 #include <sys/debug.h>
42 #include <sys/disp.h>
43 #include <sys/sobject.h>
44 #include <sys/cpuvar.h>
45 #include <sys/sleepq.h>
46 #include <sys/sdt.h>
47 
48 static void sema_unsleep(kthread_t *t);
49 static void sema_change_pri(kthread_t *t, pri_t pri, pri_t *t_prip);
50 static kthread_t *sema_owner(ksema_t *);
51 
52 /*
53  * The sobj_ops vector exports a set of functions needed when a thread
54  * is asleep on a synchronization object of this type.
55  */
56 static sobj_ops_t sema_sobj_ops = {
57 	SOBJ_SEMA, sema_owner, sema_unsleep, sema_change_pri
58 };
59 
60 /*
61  * SEMA_BLOCK(sema_impl_t *s, disp_lock_t *lockp)
62  */
63 #define	SEMA_BLOCK(s, lockp)						\
64 	{								\
65 		kthread_t	*tp;					\
66 		kthread_t	**tpp;					\
67 		pri_t		cpri;					\
68 		klwp_t	*lwp = ttolwp(curthread);			\
69 		ASSERT(THREAD_LOCK_HELD(curthread));			\
70 		ASSERT(curthread != CPU->cpu_idle_thread);		\
71 		ASSERT(CPU_ON_INTR(CPU) == 0);				\
72 		ASSERT(curthread->t_wchan0 == NULL);			\
73 		ASSERT(curthread->t_wchan == NULL);			\
74 		ASSERT(curthread->t_state == TS_ONPROC);		\
75 		CL_SLEEP(curthread);					\
76 		THREAD_SLEEP(curthread, lockp);				\
77 		curthread->t_wchan = (caddr_t)s;			\
78 		curthread->t_sobj_ops = &sema_sobj_ops;			\
79 		DTRACE_SCHED(sleep);					\
80 		if (lwp != NULL) {					\
81 			lwp->lwp_ru.nvcsw++;				\
82 			(void) new_mstate(curthread, LMS_SLEEP);	\
83 		}							\
84 		cpri = DISP_PRIO(curthread);				\
85 		tpp = &s->s_slpq;					\
86 		while ((tp = *tpp) != NULL) {				\
87 			if (cpri > DISP_PRIO(tp))			\
88 				break;					\
89 			tpp = &tp->t_link;				\
90 		}							\
91 		*tpp = curthread;					\
92 		curthread->t_link = tp;					\
93 		ASSERT(s->s_slpq != NULL);				\
94 	}
95 
96 /* ARGSUSED */
97 void
98 sema_init(ksema_t *sp, unsigned count, char *name, ksema_type_t type, void *arg)
99 {
100 	((sema_impl_t *)sp)->s_count = count;
101 	((sema_impl_t *)sp)->s_slpq = NULL;
102 }
103 
104 void
105 sema_destroy(ksema_t *sp)
106 {
107 	ASSERT(((sema_impl_t *)sp)->s_slpq == NULL);
108 }
109 
110 /*
111  * Put a thread on the sleep queue for this semaphore.
112  */
113 static void
114 sema_queue(ksema_t *sp, kthread_t *t)
115 {
116 	kthread_t	**tpp;
117 	kthread_t	*tp;
118 	pri_t		cpri;
119 	sema_impl_t	*s;
120 
121 	ASSERT(THREAD_LOCK_HELD(t));
122 	s = (sema_impl_t *)sp;
123 	tpp = &s->s_slpq;
124 	cpri = DISP_PRIO(t);
125 	while ((tp = *tpp) != NULL) {
126 		if (cpri > DISP_PRIO(tp))
127 			break;
128 		tpp = &tp->t_link;
129 	}
130 	*tpp = t;
131 	t->t_link = tp;
132 }
133 
134 /*
135  * Remove a thread from the sleep queue for this
136  * semaphore.
137  */
138 static void
139 sema_dequeue(ksema_t *sp, kthread_t *t)
140 {
141 	kthread_t	**tpp;
142 	kthread_t	*tp;
143 	sema_impl_t	*s;
144 
145 	ASSERT(THREAD_LOCK_HELD(t));
146 	s = (sema_impl_t *)sp;
147 	tpp = &s->s_slpq;
148 	while ((tp = *tpp) != NULL) {
149 		if (tp == t) {
150 			*tpp = t->t_link;
151 			t->t_link = NULL;
152 			return;
153 		}
154 		tpp = &tp->t_link;
155 	}
156 }
157 
158 /* ARGSUSED */
159 static kthread_t *
160 sema_owner(ksema_t *sp)
161 {
162 	return ((kthread_t *)NULL);
163 }
164 
165 /*
166  * Wakeup a thread sleeping on a semaphore, and put it
167  * on the dispatch queue.
168  * Called via SOBJ_UNSLEEP().
169  */
170 static void
171 sema_unsleep(kthread_t *t)
172 {
173 	kthread_t	**tpp;
174 	kthread_t	*tp;
175 	sema_impl_t	*s;
176 
177 	ASSERT(THREAD_LOCK_HELD(t));
178 	s = (sema_impl_t *)t->t_wchan;
179 	tpp = &s->s_slpq;
180 	while ((tp = *tpp) != NULL) {
181 		if (tp == t) {
182 			*tpp = t->t_link;
183 			t->t_link = NULL;
184 			t->t_sobj_ops = NULL;
185 			t->t_wchan = NULL;
186 			t->t_wchan0 = NULL;
187 			/*
188 			 * Change thread to transition state and
189 			 * drop the semaphore sleep queue lock.
190 			 */
191 			THREAD_TRANSITION(t);
192 			CL_SETRUN(t);
193 			return;
194 		}
195 		tpp = &tp->t_link;
196 	}
197 }
198 
199 /*
200  * operations to perform when changing the priority
201  * of a thread asleep on a semaphore.
202  * Called via SOBJ_CHANGE_PRI() and SOBJ_CHANGE_EPRI().
203  */
204 static void
205 sema_change_pri(kthread_t *t, pri_t pri, pri_t *t_prip)
206 {
207 	ksema_t *sp;
208 
209 	if ((sp = (ksema_t *)t->t_wchan) != NULL) {
210 		sema_dequeue(sp, t);
211 		*t_prip = pri;
212 		sema_queue(sp, t);
213 	} else
214 		panic("sema_change_pri: %p not on sleep queue", (void *)t);
215 }
216 
217 /*
218  * the semaphore is granted when the semaphore's
219  * count is greater than zero and blocks when equal
220  * to zero.
221  */
222 void
223 sema_p(ksema_t *sp)
224 {
225 	sema_impl_t	*s;
226 	disp_lock_t	*sqlp;
227 
228 	s = (sema_impl_t *)sp;
229 	sqlp = &SQHASH(s)->sq_lock;
230 	disp_lock_enter(sqlp);
231 	ASSERT(s->s_count >= 0);
232 	while (s->s_count == 0) {
233 		if (panicstr) {
234 			disp_lock_exit(sqlp);
235 			return;
236 		}
237 		thread_lock_high(curthread);
238 		SEMA_BLOCK(s, sqlp);
239 		thread_unlock_nopreempt(curthread);
240 		swtch();
241 		disp_lock_enter(sqlp);
242 	}
243 	s->s_count--;
244 	disp_lock_exit(sqlp);
245 }
246 
247 /*
248  * similiar to sema_p except that it blocks at an interruptible
249  * priority. if a signal is present then return 1 otherwise 0.
250  */
251 int
252 sema_p_sig(ksema_t *sp)
253 {
254 	kthread_t	*t = curthread;
255 	klwp_t		*lwp = ttolwp(t);
256 	sema_impl_t	*s;
257 	disp_lock_t	*sqlp;
258 
259 	if (lwp == NULL) {
260 		sema_p(sp);
261 		return (0);
262 	}
263 
264 	s = (sema_impl_t *)sp;
265 	sqlp = &SQHASH(s)->sq_lock;
266 	disp_lock_enter(sqlp);
267 	ASSERT(s->s_count >= 0);
268 	while (s->s_count == 0) {
269 		proc_t *p = ttoproc(t);
270 		thread_lock_high(t);
271 		t->t_flag |= T_WAKEABLE;
272 		SEMA_BLOCK(s, sqlp);
273 		lwp->lwp_asleep = 1;
274 		lwp->lwp_sysabort = 0;
275 		thread_unlock_nopreempt(t);
276 		if (ISSIG(t, JUSTLOOKING) || MUSTRETURN(p, t))
277 			setrun(t);
278 		swtch();
279 		t->t_flag &= ~T_WAKEABLE;
280 		if (ISSIG(t, FORREAL) ||
281 		    lwp->lwp_sysabort || MUSTRETURN(p, t)) {
282 			kthread_t *sq, *tp;
283 			lwp->lwp_asleep = 0;
284 			lwp->lwp_sysabort = 0;
285 			disp_lock_enter(sqlp);
286 			sq = s->s_slpq;
287 			/*
288 			 * in case sema_v and interrupt happen
289 			 * at the same time, we need to pass the
290 			 * sema_v to the next thread.
291 			 */
292 			if ((sq != NULL) && (s->s_count > 0)) {
293 				tp = sq;
294 				ASSERT(THREAD_LOCK_HELD(tp));
295 				sq = sq->t_link;
296 				tp->t_link = NULL;
297 				DTRACE_SCHED1(wakeup, kthread_t *, tp);
298 				tp->t_sobj_ops = NULL;
299 				tp->t_wchan = NULL;
300 				ASSERT(tp->t_state == TS_SLEEP);
301 				CL_WAKEUP(tp);
302 				s->s_slpq = sq;
303 				disp_lock_exit_high(sqlp);
304 				thread_unlock(tp);
305 			} else {
306 				disp_lock_exit(sqlp);
307 			}
308 			return (1);
309 		}
310 		lwp->lwp_asleep = 0;
311 		disp_lock_enter(sqlp);
312 	}
313 	s->s_count--;
314 	disp_lock_exit(sqlp);
315 	return (0);
316 }
317 
318 /*
319  * the semaphore's count is incremented by one. a blocked thread
320  * is awakened and re-tries to acquire the semaphore.
321  */
322 void
323 sema_v(ksema_t *sp)
324 {
325 	sema_impl_t 	*s;
326 	kthread_t 	*sq, *tp;
327 	disp_lock_t	*sqlp;
328 
329 	s = (sema_impl_t *)sp;
330 	sqlp = &SQHASH(s)->sq_lock;
331 	disp_lock_enter(sqlp);
332 	if (panicstr) {
333 		disp_lock_exit(sqlp);
334 		return;
335 	}
336 	s->s_count++;
337 	sq = s->s_slpq;
338 	if (sq != NULL) {
339 		tp = sq;
340 		ASSERT(THREAD_LOCK_HELD(tp));
341 		sq = sq->t_link;
342 		tp->t_link = NULL;
343 		DTRACE_SCHED1(wakeup, kthread_t *, tp);
344 		tp->t_sobj_ops = NULL;
345 		tp->t_wchan = NULL;
346 		ASSERT(tp->t_state == TS_SLEEP);
347 		CL_WAKEUP(tp);
348 		s->s_slpq = sq;
349 		disp_lock_exit_high(sqlp);
350 		thread_unlock(tp);
351 	} else {
352 		disp_lock_exit(sqlp);
353 	}
354 }
355 
356 /*
357  * try to acquire the semaphore. if the semaphore is greater than
358  * zero, then the semaphore is granted and returns 1. otherwise
359  * return 0.
360  */
361 int
362 sema_tryp(ksema_t *sp)
363 {
364 	sema_impl_t	*s;
365 	sleepq_head_t	*sqh;
366 
367 	int	gotit = 0;
368 
369 	s = (sema_impl_t *)sp;
370 	sqh = SQHASH(s);
371 	disp_lock_enter(&sqh->sq_lock);
372 	if (s->s_count > 0) {
373 		s->s_count--;
374 		gotit = 1;
375 	}
376 	disp_lock_exit(&sqh->sq_lock);
377 	return (gotit);
378 }
379 
380 int
381 sema_held(ksema_t *sp)
382 {
383 	sema_impl_t	*s;
384 
385 
386 	s = (sema_impl_t *)sp;
387 	if (panicstr)
388 		return (1);
389 	else
390 		return (s->s_count <= 0);
391 }
392