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