1 /*
2 * os_handler.c
3 *
4 * MontaVista IPMI os handler tools.
5 *
6 * Author: MontaVista Software, Inc.
7 * Corey Minyard <minyard@mvista.com>
8 * source@mvista.com
9 *
10 * Copyright 2006 MontaVista Software Inc.
11 *
12 * This software is available to you under a choice of one of two
13 * licenses. You may choose to be licensed under the terms of the GNU
14 * Lesser General Public License (GPL) Version 2 or the modified BSD
15 * license below. The following disclamer applies to both licenses:
16 *
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
23 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
25 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
26 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * GNU Lesser General Public Licence
29 *
30 * This program is free software; you can redistribute it and/or
31 * modify it under the terms of the GNU Lesser General Public License
32 * as published by the Free Software Foundation; either version 2 of
33 * the License, or (at your option) any later version.
34 *
35 * You should have received a copy of the GNU Lesser General Public
36 * License along with this program; if not, write to the Free
37 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
38 *
39 * Modified BSD Licence
40 *
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 *
45 * 1. Redistributions of source code must retain the above copyright
46 * notice, this list of conditions and the following disclaimer.
47 * 2. Redistributions in binary form must reproduce the above
48 * copyright notice, this list of conditions and the following
49 * disclaimer in the documentation and/or other materials provided
50 * with the distribution.
51 * 3. The name of the author may not be used to endorse or promote
52 * products derived from this software without specific prior
53 * written permission.
54 */
55
56 #include <stdlib.h>
57 #include <string.h>
58 #include <OpenIPMI/os_handler.h>
59 #include <OpenIPMI/internal/ipmi_malloc.h>
60 #include <errno.h>
61
62
63 /*
64 * This code is subtle, be careful.
65 *
66 * We handle three basic modes here:
67 *
68 * * single-threaded OS handler
69 * * multi-threaded OS handler, multi-threaded (num_threads > 0)
70 * * multi-threaded OS handler, single threaded support (num_threads == 0)
71 * (multi-single mode)
72 *
73 * Single threaded OS handler is simple. We just run the event loop
74 * until the wait condition is done.
75 *
76 * Full multi-threaded mode is simple, too. We allocate threads to
77 * run the event loop, then we use condition variables to operate.
78 *
79 * Multi-single mode is not simple. You can't just run the event
80 * loop. Another thread may be running the event loop, too, and thus
81 * handle the particular operation that frees up our event loop (thus
82 * we will still be stuck waiting in our event loop). We can't have
83 * another thread arbitrarily run the event loop, as we need to
84 * support single-threaded system.
85 *
86 * So what we do is allocate a single thread. It runs the event loop
87 * *only when something is waiting*. Thus we are single-threaded if
88 * the app is single threaded. But since condition variables are used
89 * for wakeups, we can support multi-threaded applications properly.
90 */
91
92
93 struct os_handler_waiter_factory_s
94 {
95 os_handler_t *os_hnd;
96 unsigned int num_threads;
97 int thread_priority;
98 int has_threads;
99
100 os_hnd_lock_t *lock;
101 os_hnd_cond_t *cond;
102
103 /* Number of currently running threads. */
104 unsigned int thread_count;
105
106 /* Number of wait structures we have out for this factory. */
107 unsigned int num_waiters;
108
109 /* Tells the main thread to stop. */
110 volatile int stop_threads;
111
112 /* Number of single-thread users. */
113 unsigned int single_thread_use_count;
114 os_hnd_cond_t *single_thread_cond;
115 };
116
117 struct os_handler_waiter_s
118 {
119 os_handler_waiter_factory_t *factory;
120
121 os_hnd_lock_t *lock;
122 os_hnd_cond_t *cond;
123
124 /* Am I using multi-single mode and waiting? */
125 int is_single;
126
127 unsigned int count;
128 };
129
130 /* This is a normal event loop thread for full multi-threaded mode. */
131 static void
waiter_thread(void * data)132 waiter_thread(void *data)
133 {
134 os_handler_waiter_factory_t *factory = data;
135 os_handler_t *os_hnd = factory->os_hnd;
136
137 while (!factory->stop_threads) {
138 struct timeval tv = { 1, 0 };
139 os_hnd->perform_one_op(os_hnd, &tv);
140 }
141
142 os_hnd->lock(os_hnd, factory->lock);
143 factory->thread_count--;
144 if (factory->thread_count == 0)
145 os_hnd->cond_wake(os_hnd, factory->cond);
146 os_hnd->unlock(os_hnd, factory->lock);
147 }
148
149 /* Event loop thread for multi-single mode. Subtle, be careful and
150 read the comments. */
151 static void
single_waiter_thread(void * data)152 single_waiter_thread(void *data)
153 {
154 os_handler_waiter_factory_t *factory = data;
155 os_handler_t *os_hnd = factory->os_hnd;
156
157 os_hnd->lock(os_hnd, factory->lock);
158 while (!factory->stop_threads) {
159 /* While things are waiting, run out thread. This is subtle
160 here. If we are truely single-threaded, the thing that
161 releases the wait is going to be running from this thread.
162 This single_thread_use_count will be set to zero before
163 returning from perform_one_op, and we just quit and wait to
164 be woken again and won't call more event loop operations.
165 If we are not single-threaded (the app is multi-threaded)
166 we still run from here, but the app better be ready for
167 multi-threaded operation. */
168 while (factory->single_thread_use_count) {
169 struct timeval tv = { 1, 0 };
170 os_hnd->unlock(os_hnd, factory->lock);
171 os_hnd->perform_one_op(os_hnd, &tv);
172 os_hnd->lock(os_hnd, factory->lock);
173 }
174
175 /* Wait for someone to tell us there are more event to run */
176 os_hnd->cond_wait(os_hnd, factory->single_thread_cond, factory->lock);
177 }
178
179 factory->thread_count--;
180 if (factory->thread_count == 0)
181 os_hnd->cond_wake(os_hnd, factory->cond);
182 os_hnd->unlock(os_hnd, factory->lock);
183 }
184
185 extern int ipmi_malloc_init(os_handler_t *os_hnd);
186
187 extern void ipmi_malloc_shutdown(void);
188
189 int
os_handler_alloc_waiter_factory(os_handler_t * os_hnd,unsigned int num_threads,int thread_priority,os_handler_waiter_factory_t ** factory)190 os_handler_alloc_waiter_factory(os_handler_t *os_hnd,
191 unsigned int num_threads,
192 int thread_priority,
193 os_handler_waiter_factory_t **factory)
194 {
195 os_handler_waiter_factory_t *nf;
196 int rv;
197 unsigned int i;
198 int has_threads = 0;
199
200 ipmi_malloc_init(os_hnd);
201
202 if (os_hnd->create_lock && os_hnd->create_cond
203 && os_hnd->create_thread)
204 has_threads = 1;
205
206 if ((num_threads > 0) && !has_threads) {
207 /* Asked for threads, but handler doesn't support them. */
208 return ENOSYS;
209 }
210
211 nf = ipmi_mem_alloc(sizeof(*nf));
212 if (!nf)
213 return ENOMEM;
214 memset(nf, 0, sizeof(*nf));
215
216 nf->has_threads = has_threads;
217 nf->os_hnd = os_hnd;
218 nf->thread_priority = thread_priority;
219 nf->num_threads = num_threads;
220
221 if (has_threads) {
222 rv = os_hnd->create_lock(os_hnd, &nf->lock);
223 if (rv) {
224 ipmi_mem_free(nf);
225 return rv;
226 }
227
228 rv = os_hnd->create_cond(os_hnd, &nf->cond);
229 if (rv) {
230 os_hnd->destroy_lock(os_hnd, nf->lock);
231 ipmi_mem_free(nf);
232 return rv;
233 }
234 }
235
236 if (num_threads > 0) {
237 for (i=0; i<num_threads; i++) {
238 nf->thread_count++;
239 rv = os_hnd->create_thread(os_hnd, thread_priority,
240 waiter_thread, nf);
241 if (rv) {
242 nf->thread_count--;
243 os_handler_free_waiter_factory(nf);
244 return rv;
245 }
246 }
247 } else if (has_threads) {
248 rv = os_hnd->create_cond(os_hnd, &nf->single_thread_cond);
249 if (rv) {
250 os_handler_free_waiter_factory(nf);
251 return rv;
252 }
253
254 nf->thread_count++;
255 rv = os_hnd->create_thread(os_hnd, thread_priority,
256 single_waiter_thread, nf);
257 if (rv) {
258 nf->thread_count--;
259 os_handler_free_waiter_factory(nf);
260 return rv;
261 }
262 }
263
264 *factory = nf;
265
266 return 0;
267 }
268
269 void
os_handler_global_shutdown(void)270 os_handler_global_shutdown(void)
271 {
272 ipmi_malloc_shutdown();
273 }
274
275 int
os_handler_free_waiter_factory(os_handler_waiter_factory_t * factory)276 os_handler_free_waiter_factory(os_handler_waiter_factory_t *factory)
277 {
278 os_handler_t *os_hnd = factory->os_hnd;
279
280 if (factory->lock)
281 os_hnd->lock(os_hnd, factory->lock);
282
283 if (factory->stop_threads)
284 return EINVAL;
285 if (factory->num_waiters > 0)
286 return EAGAIN;
287
288 if (factory->thread_count > 0) {
289 factory->stop_threads = 1;
290 if (factory->single_thread_cond)
291 os_hnd->cond_wake(os_hnd, factory->single_thread_cond);
292 os_hnd->cond_wait(os_hnd, factory->cond, factory->lock);
293 }
294
295 if (factory->has_threads) {
296 os_hnd->unlock(os_hnd, factory->lock);
297 os_hnd->destroy_lock(os_hnd, factory->lock);
298 os_hnd->destroy_cond(os_hnd, factory->cond);
299 }
300 if (factory->single_thread_cond)
301 os_hnd->destroy_cond(os_hnd, factory->single_thread_cond);
302
303 ipmi_mem_free(factory);
304 return 0;
305 }
306
307 os_handler_waiter_t *
os_handler_alloc_waiter(os_handler_waiter_factory_t * factory)308 os_handler_alloc_waiter(os_handler_waiter_factory_t *factory)
309 {
310 os_handler_waiter_t *nw;
311 os_handler_t *os_hnd = factory->os_hnd;
312 int rv;
313
314 nw = ipmi_mem_alloc(sizeof(*nw));
315 if (!nw)
316 return NULL;
317 memset(nw, 0, sizeof(*nw));
318
319 nw->factory = factory;
320
321 if (factory->has_threads) {
322 rv = os_hnd->create_lock(os_hnd, &nw->lock);
323 if (rv) {
324 ipmi_mem_free(nw);
325 return NULL;
326 }
327
328 rv = os_hnd->create_cond(os_hnd, &nw->cond);
329 if (rv) {
330 os_hnd->destroy_lock(os_hnd, nw->lock);
331 ipmi_mem_free(nw);
332 return NULL;
333 }
334 }
335
336 if (factory->lock)
337 os_hnd->lock(os_hnd, factory->lock);
338 factory->num_waiters++;
339 if (factory->lock)
340 os_hnd->unlock(os_hnd, factory->lock);
341
342 nw->count = 1;
343 return nw;
344 }
345
346 int
os_handler_free_waiter(os_handler_waiter_t * waiter)347 os_handler_free_waiter(os_handler_waiter_t *waiter)
348 {
349 os_handler_t *os_hnd = waiter->factory->os_hnd;
350
351 if (waiter->count > 0)
352 return EAGAIN;
353
354 if (waiter->factory->lock)
355 os_hnd->lock(os_hnd, waiter->factory->lock);
356 waiter->factory->num_waiters--;
357 if (waiter->factory->lock)
358 os_hnd->unlock(os_hnd, waiter->factory->lock);
359
360 if (waiter->lock)
361 os_hnd->destroy_lock(os_hnd, waiter->lock);
362 if (waiter->cond)
363 os_hnd->destroy_cond(os_hnd, waiter->cond);
364 ipmi_mem_free(waiter);
365 return 0;
366 }
367
368 void
os_handler_waiter_use(os_handler_waiter_t * waiter)369 os_handler_waiter_use(os_handler_waiter_t *waiter)
370 {
371 os_handler_t *os_hnd = waiter->factory->os_hnd;
372
373 if (waiter->lock)
374 os_hnd->lock(os_hnd, waiter->lock);
375 waiter->count++;
376 if (waiter->lock)
377 os_hnd->unlock(os_hnd, waiter->lock);
378 }
379
380 void
os_handler_waiter_release(os_handler_waiter_t * waiter)381 os_handler_waiter_release(os_handler_waiter_t *waiter)
382 {
383 os_handler_t *os_hnd = waiter->factory->os_hnd;
384
385 if (waiter->lock)
386 os_hnd->lock(os_hnd, waiter->lock);
387 if (waiter->count == 0) {
388 os_hnd->log(os_hnd, IPMI_LOG_SEVERE,
389 "os_handler_waiter_release: Got a release when the"
390 " wait count was already zero");
391 } else {
392 waiter->count--;
393 if (waiter->lock && (waiter->count == 0)) {
394 if (waiter->is_single) {
395 /* We handle the single thread count here and not in
396 the waiter to avoid a race condition. See comments
397 at the beginning of this file. */
398 os_hnd->lock(os_hnd, waiter->factory->lock);
399 waiter->factory->single_thread_use_count--;
400 os_hnd->unlock(os_hnd, waiter->factory->lock);
401 waiter->is_single = 0;
402 }
403 os_hnd->cond_wake(os_hnd, waiter->cond);
404 }
405 }
406 if (waiter->lock)
407 os_hnd->unlock(os_hnd, waiter->lock);
408 }
409
410 int
os_handler_waiter_wait(os_handler_waiter_t * waiter,struct timeval * timeout)411 os_handler_waiter_wait(os_handler_waiter_t *waiter, struct timeval *timeout)
412 {
413 os_handler_waiter_factory_t *factory = waiter->factory;
414 os_handler_t *os_hnd = waiter->factory->os_hnd;
415 int rv = 0;
416
417 if (waiter->lock) {
418 os_hnd->lock(os_hnd, waiter->lock);
419 if (waiter->count > 0) {
420 if (factory->num_threads == 0) {
421 /* Threaded, but we don't have a simultaneous running
422 event loop. */
423 os_hnd->lock(os_hnd, factory->lock);
424 if (factory->single_thread_use_count == 0) {
425 /* Wake the event loop thread. */
426 os_hnd->cond_wake(os_hnd, factory->single_thread_cond);
427 }
428 factory->single_thread_use_count++;
429 os_hnd->unlock(os_hnd, factory->lock);
430 waiter->is_single = 1;
431 }
432
433 rv = os_hnd->cond_timedwait(os_hnd, waiter->cond,
434 waiter->lock, timeout);
435 /* single_thread_use_count is decremented by the waker
436 unless it failes to receive the wakeup. */
437 if (rv)
438 factory->single_thread_use_count--;
439 }
440 os_hnd->unlock(os_hnd, waiter->lock);
441 } else {
442 while (waiter->count > 0)
443 os_hnd->perform_one_op(os_hnd, timeout);
444 }
445
446 return rv;
447 }
448
449