1 /*
2  * opq.c
3  *
4  * Code for handling an operation queue.
5  *
6  * Author: MontaVista Software, Inc.
7  *         Corey Minyard <minyard@mvista.com>
8  *         source@mvista.com
9  *
10  * Copyright 2002,2003 MontaVista Software Inc.
11  *
12  *  This program is free software; you can redistribute it and/or
13  *  modify it under the terms of the GNU Lesser General Public License
14  *  as published by the Free Software Foundation; either version 2 of
15  *  the License, or (at your option) any later version.
16  *
17  *
18  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
19  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
24  *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
26  *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
27  *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  *  You should have received a copy of the GNU Lesser General Public
30  *  License along with this program; if not, write to the Free
31  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32  */
33 
34 #include <stdlib.h>
35 #include <string.h>
36 
37 #include <OpenIPMI/os_handler.h>
38 
39 #include <OpenIPMI/internal/ipmi_int.h>
40 #include <OpenIPMI/internal/ilist.h>
41 #include <OpenIPMI/internal/opq.h>
42 
43 struct opq_elem_s
44 {
45     int               block;
46     opq_handler_cb    handler;
47     void              *handler_data;
48     opq_done_cb       done;
49     void              *done_data;
50     struct opq_elem_s *next;
51     ilist_item_t      ilist_item;
52 };
53 
54 struct opq_s
55 {
56     ilist_t        *ops;
57     os_hnd_lock_t  *lock;
58     int            in_handler;
59     os_handler_t   *os_hnd;
60     opq_done_cb    done_handler;
61     void           *done_data;
62     int            blocked;
63     int            in_destroy;
64 };
65 
66 static void
opq_lock(opq_t * opq)67 opq_lock(opq_t *opq)
68 {
69     if (opq->lock)
70 	opq->os_hnd->lock(opq->os_hnd, opq->lock);
71 }
72 
73 static void
opq_unlock(opq_t * opq)74 opq_unlock(opq_t *opq)
75 {
76     if (opq->lock)
77 	opq->os_hnd->unlock(opq->os_hnd, opq->lock);
78 }
79 
80 opq_t *
opq_alloc(os_handler_t * os_hnd)81 opq_alloc(os_handler_t *os_hnd)
82 {
83     int   rv;
84     opq_t *opq;
85 
86     opq = ipmi_mem_alloc(sizeof(*opq));
87     if (!opq)
88 	return NULL;
89     memset(opq, 0, sizeof(*opq));
90 
91     opq->os_hnd = os_hnd;
92     opq->in_handler = 0;
93     opq->ops = alloc_ilist();
94     if (!(opq->ops)) {
95 	ipmi_mem_free(opq);
96 	return NULL;
97     }
98 
99     if (os_hnd->create_lock) {
100 	rv = os_hnd->create_lock(opq->os_hnd, &(opq->lock));
101 	if (rv) {
102 	    free_ilist(opq->ops);
103 	    ipmi_mem_free(opq);
104 	    return NULL;
105 	}
106     } else {
107 	opq->lock = NULL;
108     }
109 
110     return opq;
111 }
112 
113 static void
opq_destroy_item(ilist_iter_t * iter,void * item,void * cb_data)114 opq_destroy_item(ilist_iter_t *iter, void *item, void *cb_data)
115 {
116     opq_elem_t *elem = (opq_elem_t *) item;
117 
118     elem->handler(elem->handler_data, 1);
119     /* Memory for this is in elem, so we must delete it before we free
120        the elem. */
121     ilist_delete(iter);
122     opq_free_elem(elem);
123 }
124 
125 void
opq_destroy(opq_t * opq)126 opq_destroy(opq_t *opq)
127 {
128     /* Only allow this to be done once.  Callbacks might call this
129        again. */
130     opq_lock(opq);
131     if (opq->in_destroy) {
132 	opq_unlock(opq);
133 	return;
134     }
135     opq->in_destroy = 1;
136     opq_unlock(opq);
137 
138     ilist_iter(opq->ops, opq_destroy_item, NULL);
139     free_ilist(opq->ops);
140     if (opq->lock)
141 	opq->os_hnd->destroy_lock(opq->os_hnd, opq->lock);
142     ipmi_mem_free(opq);
143 }
144 
145 static void
start_next_op(opq_t * opq)146 start_next_op(opq_t *opq)
147 {
148     ilist_iter_t iter;
149     opq_elem_t   *elem;
150     int          success;
151 
152     ilist_init_iter(&iter, opq->ops);
153     ilist_first(&iter);
154     elem = ilist_get(&iter);
155     while (elem) {
156 	ilist_delete(&iter);
157 	opq->done_handler = elem->done;
158 	opq->done_data = elem->done_data;
159 	opq_unlock(opq);
160 	success = elem->handler(elem->handler_data, 0);
161 	opq_free_elem(elem);
162 	opq_lock(opq);
163 	if (success == OPQ_HANDLER_STARTED)
164 	    break;
165 	ilist_first(&iter);
166 	elem = ilist_get(&iter);
167     }
168     if (!elem)
169 	opq->in_handler = 0;
170 }
171 
172 opq_elem_t *
opq_alloc_elem(void)173 opq_alloc_elem(void)
174 {
175     opq_elem_t *elem;
176     elem = ipmi_mem_alloc(sizeof(opq_elem_t));
177     return elem;
178 }
179 
180 void
opq_free_elem(opq_elem_t * elem)181 opq_free_elem(opq_elem_t *elem)
182 {
183     ipmi_mem_free(elem);
184 }
185 
186 int
opq_new_op_prio(opq_t * opq,opq_handler_cb handler,void * cb_data,int nowait,int prio,opq_elem_t * elem)187 opq_new_op_prio(opq_t *opq, opq_handler_cb handler, void *cb_data,
188 		int nowait, int prio, opq_elem_t *elem)
189 {
190     int        success;
191 
192     opq_lock(opq);
193     if (opq->in_handler) {
194 	if (nowait) {
195 	    opq_unlock(opq);
196 	    return -1;
197 	}
198 	if (!elem) {
199 	    elem = opq_alloc_elem();
200 	    if (!elem)
201 		goto out_err;
202 	}
203 	elem->handler = handler;
204 	elem->done = NULL;
205 	elem->handler_data = cb_data;
206 	elem->block = 1;
207 	if (prio)
208 	    ilist_add_head(opq->ops, elem, &elem->ilist_item);
209 	else
210 	    ilist_add_tail(opq->ops, elem, &elem->ilist_item);
211 	opq->blocked = 0;
212 	opq_unlock(opq);
213     } else {
214 	if (elem)
215 	    opq_free_elem(elem);
216 	opq->blocked = 0;
217 	opq->in_handler = 1;
218 	opq->done_handler = NULL;
219 	opq_unlock(opq);
220 	success = handler(cb_data, 0);
221 	if (success == OPQ_HANDLER_ABORTED) {
222 	    /* In case any were added while I was unlocked. */
223 	    opq_lock(opq);
224 	    start_next_op(opq);
225 	    opq_unlock(opq);
226 	}
227     }
228 
229     return 1;
230 
231  out_err:
232     opq_unlock(opq);
233     return 0;
234 }
235 
236 int
opq_new_op(opq_t * opq,opq_handler_cb handler,void * cb_data,int nowait)237 opq_new_op(opq_t *opq, opq_handler_cb handler, void *cb_data, int nowait)
238 {
239     return opq_new_op_prio(opq, handler, cb_data, nowait, OPQ_ADD_TAIL, NULL);
240 }
241 
242 int
opq_new_op_with_done(opq_t * opq,opq_handler_cb handler,void * handler_data,opq_done_cb done,void * done_data)243 opq_new_op_with_done(opq_t          *opq,
244 		     opq_handler_cb handler,
245 		     void           *handler_data,
246 		     opq_done_cb    done,
247 		     void           *done_data)
248 {
249     opq_elem_t *elem;
250     int        success;
251 
252     opq_lock(opq);
253     if (opq->in_handler) {
254 	elem = ipmi_mem_alloc(sizeof(*elem));
255 	if (!elem)
256 	    goto out_err;
257 	elem->handler = handler;
258 	elem->handler_data = handler_data;
259 	elem->done = done;
260 	elem->done_data = done_data;
261 	elem->block = opq->blocked;
262 	ilist_add_tail(opq->ops, elem, &elem->ilist_item);
263 	opq->blocked = 0;
264 	opq_unlock(opq);
265     } else {
266 	opq->blocked = 0;
267 	opq->in_handler = 1;
268 	opq->done_handler = done;
269 	opq->done_data = done_data;
270 	opq_unlock(opq);
271 	success = handler(handler_data, 0);
272 	if (success == OPQ_HANDLER_ABORTED) {
273 	    /* In case any were added while I was unlocked. */
274 	    opq_lock(opq);
275 	    start_next_op(opq);
276 	    opq_unlock(opq);
277 	}
278     }
279 
280     return 1;
281 
282  out_err:
283     opq_unlock(opq);
284     return 0;
285 }
286 
287 void
opq_add_block(opq_t * opq)288 opq_add_block(opq_t *opq)
289 {
290     opq_lock(opq);
291     opq->blocked = 1;
292     opq_unlock(opq);
293 }
294 
295 void
opq_op_done(opq_t * opq)296 opq_op_done(opq_t *opq)
297 {
298     ilist_iter_t   iter;
299     opq_elem_t     *elem;
300     opq_elem_t     *list = NULL;
301     opq_elem_t     *next;
302     opq_elem_t     **list_end = &list;
303     opq_done_cb    done_handler;
304     void           *done_data;
305 
306     /* First check for done handlers. */
307     opq_lock(opq);
308     ilist_init_iter(&iter, opq->ops);
309     ilist_first(&iter);
310     elem = ilist_get(&iter);
311     while (elem && (!elem->block)) {
312 	ilist_delete(&iter);
313 	elem->next = NULL;
314 	*list_end = elem;
315 	list_end = &(elem->next);
316 	elem = ilist_get(&iter);
317     }
318     done_handler = opq->done_handler;
319     done_data = opq->done_data;
320     opq->done_handler = NULL;
321     if (done_handler || list) {
322 	/* There are done handlers to call, unlock and call them. */
323 	opq_unlock(opq);
324 
325 	if (done_handler)
326 	    done_handler(done_data, 0);
327 	while (list) {
328 	    next = list->next;
329 	    list->done(list->done_data, 0);
330 	    opq_free_elem(list);
331 	    list = next;
332 	}
333 
334 	opq_lock(opq);
335 	/* During the time we were unlocked, handlers may have been
336            added. */
337 	ilist_first(&iter);
338 	elem = ilist_get(&iter);
339     }
340     start_next_op(opq);
341     opq_unlock(opq);
342 }
343 
344 int
opq_stuff_in_progress(opq_t * opq)345 opq_stuff_in_progress(opq_t *opq)
346 {
347     return opq->in_handler;
348 }
349