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 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include "lint.h"
30 #include "thr_uberdata.h"
31
32 static uint32_t _semvaluemax;
33
34 /*
35 * Check to see if anyone is waiting for this semaphore.
36 */
37 #pragma weak _sema_held = sema_held
38 int
sema_held(sema_t * sp)39 sema_held(sema_t *sp)
40 {
41 return (sp->count == 0);
42 }
43
44 #pragma weak _sema_init = sema_init
45 /* ARGSUSED3 */
46 int
sema_init(sema_t * sp,unsigned int count,int type,void * arg)47 sema_init(sema_t *sp, unsigned int count, int type, void *arg)
48 {
49 if (_semvaluemax == 0)
50 _semvaluemax = (uint32_t)_sysconf(_SC_SEM_VALUE_MAX);
51 if ((type != USYNC_THREAD && type != USYNC_PROCESS) ||
52 (count > _semvaluemax))
53 return (EINVAL);
54 (void) memset(sp, 0, sizeof (*sp));
55 sp->count = count;
56 sp->type = (uint16_t)type;
57 sp->magic = SEMA_MAGIC;
58
59 /*
60 * This should be at the beginning of the function,
61 * but for the sake of old broken applications that
62 * do not have proper alignment for their semaphores
63 * (and don't check the return code from sema_init),
64 * we put it here, after initializing the semaphore regardless.
65 */
66 if (((uintptr_t)sp & (_LONG_LONG_ALIGNMENT - 1)) &&
67 curthread->ul_misaligned == 0)
68 return (EINVAL);
69
70 return (0);
71 }
72
73 #pragma weak _sema_destroy = sema_destroy
74 int
sema_destroy(sema_t * sp)75 sema_destroy(sema_t *sp)
76 {
77 sp->magic = 0;
78 tdb_sync_obj_deregister(sp);
79 return (0);
80 }
81
82 static int
sema_wait_impl(sema_t * sp,timespec_t * tsp)83 sema_wait_impl(sema_t *sp, timespec_t *tsp)
84 {
85 lwp_sema_t *lsp = (lwp_sema_t *)sp;
86 ulwp_t *self = curthread;
87 uberdata_t *udp = self->ul_uberdata;
88 tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp);
89 hrtime_t begin_sleep = 0;
90 uint_t count;
91 int error = 0;
92
93 /*
94 * All variations of sema_wait() are cancellation points.
95 */
96 _cancelon();
97
98 if (ssp)
99 tdb_incr(ssp->sema_wait);
100
101 self->ul_sp = stkptr();
102 self->ul_wchan = lsp;
103 if (__td_event_report(self, TD_SLEEP, udp)) {
104 self->ul_td_evbuf.eventnum = TD_SLEEP;
105 self->ul_td_evbuf.eventdata = lsp;
106 tdb_event(TD_SLEEP, udp);
107 }
108 /* just a guess, but it looks like we will sleep */
109 if (ssp && lsp->count == 0) {
110 begin_sleep = gethrtime();
111 if (lsp->count == 0) /* still looks like sleep */
112 tdb_incr(ssp->sema_wait_sleep);
113 else /* we changed our mind */
114 begin_sleep = 0;
115 }
116
117 if (lsp->type == USYNC_PROCESS) { /* kernel-level */
118 set_parking_flag(self, 1);
119 if (self->ul_cursig != 0 ||
120 (self->ul_cancelable && self->ul_cancel_pending))
121 set_parking_flag(self, 0);
122 /* the kernel always does FIFO queueing */
123 error = ___lwp_sema_timedwait(lsp, tsp, 1);
124 set_parking_flag(self, 0);
125 } else if (!udp->uberflags.uf_mt && /* single threaded */
126 lsp->count != 0) { /* and non-blocking */
127 /*
128 * Since we are single-threaded, we don't need the
129 * protection of queue_lock(). However, we do need
130 * to block signals while modifying the count.
131 */
132 sigoff(self);
133 lsp->count--;
134 sigon(self);
135 } else { /* multithreaded or blocking */
136 queue_head_t *qp;
137 ulwp_t *ulwp;
138 lwpid_t lwpid = 0;
139
140 qp = queue_lock(lsp, CV);
141 while (error == 0 && lsp->count == 0) {
142 /*
143 * SUSV3 requires FIFO queueing for semaphores,
144 * at least for SCHED_FIFO and SCHED_RR scheduling.
145 */
146 enqueue(qp, self, 1);
147 lsp->sema_waiters = 1;
148 set_parking_flag(self, 1);
149 queue_unlock(qp);
150 /*
151 * We may have received SIGCANCEL before we
152 * called queue_lock(). If so and we are
153 * cancelable we should return EINTR.
154 */
155 if (self->ul_cursig != 0 ||
156 (self->ul_cancelable && self->ul_cancel_pending))
157 set_parking_flag(self, 0);
158 error = __lwp_park(tsp, 0);
159 set_parking_flag(self, 0);
160 qp = queue_lock(lsp, CV);
161 if (self->ul_sleepq) /* timeout or spurious wakeup */
162 lsp->sema_waiters = dequeue_self(qp);
163 }
164 if (error == 0)
165 lsp->count--;
166 if (lsp->count != 0 && lsp->sema_waiters) {
167 int more;
168 if ((ulwp = dequeue(qp, &more)) != NULL) {
169 no_preempt(self);
170 lwpid = ulwp->ul_lwpid;
171 }
172 lsp->sema_waiters = more;
173 }
174 queue_unlock(qp);
175 if (lwpid) {
176 (void) __lwp_unpark(lwpid);
177 preempt(self);
178 }
179 }
180
181 self->ul_wchan = NULL;
182 self->ul_sp = 0;
183 if (ssp) {
184 if (error == 0) {
185 /* we just decremented the count */
186 count = lsp->count;
187 if (ssp->sema_min_count > count)
188 ssp->sema_min_count = count;
189 }
190 if (begin_sleep)
191 ssp->sema_wait_sleep_time += gethrtime() - begin_sleep;
192 }
193
194 if (error == EINTR)
195 _canceloff();
196 else
197 _canceloff_nocancel();
198 return (error);
199 }
200
201 #pragma weak _sema_wait = sema_wait
202 int
sema_wait(sema_t * sp)203 sema_wait(sema_t *sp)
204 {
205 ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
206 return (sema_wait_impl(sp, NULL));
207 }
208
209 int
sema_reltimedwait(sema_t * sp,const timespec_t * reltime)210 sema_reltimedwait(sema_t *sp, const timespec_t *reltime)
211 {
212 timespec_t tslocal = *reltime;
213
214 ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
215 return (sema_wait_impl(sp, &tslocal));
216 }
217
218 int
sema_timedwait(sema_t * sp,const timespec_t * abstime)219 sema_timedwait(sema_t *sp, const timespec_t *abstime)
220 {
221 timespec_t tslocal;
222
223 ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
224 abstime_to_reltime(CLOCK_REALTIME, abstime, &tslocal);
225 return (sema_wait_impl(sp, &tslocal));
226 }
227
228 #pragma weak _sema_trywait = sema_trywait
229 int
sema_trywait(sema_t * sp)230 sema_trywait(sema_t *sp)
231 {
232 lwp_sema_t *lsp = (lwp_sema_t *)sp;
233 ulwp_t *self = curthread;
234 uberdata_t *udp = self->ul_uberdata;
235 tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp);
236 uint_t count;
237 int error = 0;
238
239 ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
240
241 if (ssp)
242 tdb_incr(ssp->sema_trywait);
243
244 if (lsp->type == USYNC_PROCESS) { /* kernel-level */
245 error = _lwp_sema_trywait(lsp);
246 } else if (!udp->uberflags.uf_mt) { /* single threaded */
247 sigoff(self);
248 if (lsp->count == 0)
249 error = EBUSY;
250 else
251 lsp->count--;
252 sigon(self);
253 } else { /* multithreaded */
254 queue_head_t *qp;
255 ulwp_t *ulwp;
256 lwpid_t lwpid = 0;
257
258 qp = queue_lock(lsp, CV);
259 if (lsp->count == 0)
260 error = EBUSY;
261 else if (--lsp->count != 0 && lsp->sema_waiters) {
262 int more;
263 if ((ulwp = dequeue(qp, &more)) != NULL) {
264 no_preempt(self);
265 lwpid = ulwp->ul_lwpid;
266 }
267 lsp->sema_waiters = more;
268 }
269 queue_unlock(qp);
270 if (lwpid) {
271 (void) __lwp_unpark(lwpid);
272 preempt(self);
273 }
274 }
275
276 if (error == 0) {
277 if (ssp) {
278 /* we just decremented the count */
279 count = lsp->count;
280 if (ssp->sema_min_count > count)
281 ssp->sema_min_count = count;
282 }
283 } else {
284 if (ssp)
285 tdb_incr(ssp->sema_trywait_fail);
286 if (__td_event_report(self, TD_LOCK_TRY, udp)) {
287 self->ul_td_evbuf.eventnum = TD_LOCK_TRY;
288 tdb_event(TD_LOCK_TRY, udp);
289 }
290 }
291
292 return (error);
293 }
294
295 #pragma weak _sema_post = sema_post
296 int
sema_post(sema_t * sp)297 sema_post(sema_t *sp)
298 {
299 lwp_sema_t *lsp = (lwp_sema_t *)sp;
300 ulwp_t *self = curthread;
301 uberdata_t *udp = self->ul_uberdata;
302 tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp);
303 uint_t count;
304 int error = 0;
305
306 if (ssp)
307 tdb_incr(ssp->sema_post);
308 if (_semvaluemax == 0)
309 _semvaluemax = (uint32_t)_sysconf(_SC_SEM_VALUE_MAX);
310
311 if (lsp->type == USYNC_PROCESS) { /* kernel-level */
312 error = _lwp_sema_post(lsp);
313 } else if (!udp->uberflags.uf_mt) { /* single threaded */
314 sigoff(self);
315 if (lsp->count >= _semvaluemax)
316 error = EOVERFLOW;
317 else
318 lsp->count++;
319 sigon(self);
320 } else { /* multithreaded */
321 queue_head_t *qp;
322 ulwp_t *ulwp;
323 lwpid_t lwpid = 0;
324
325 qp = queue_lock(lsp, CV);
326 if (lsp->count >= _semvaluemax)
327 error = EOVERFLOW;
328 else if (lsp->count++ == 0 && lsp->sema_waiters) {
329 int more;
330 if ((ulwp = dequeue(qp, &more)) != NULL) {
331 no_preempt(self);
332 lwpid = ulwp->ul_lwpid;
333 }
334 lsp->sema_waiters = more;
335 }
336 queue_unlock(qp);
337 if (lwpid) {
338 (void) __lwp_unpark(lwpid);
339 preempt(self);
340 }
341 }
342
343 if (error == 0) {
344 if (ssp) {
345 /* we just incremented the count */
346 count = lsp->count;
347 if (ssp->sema_max_count < count)
348 ssp->sema_max_count = count;
349 }
350 }
351
352 return (error);
353 }
354