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