/* * opq.c * * Code for handling an operation queue. * * Author: MontaVista Software, Inc. * Corey Minyard * source@mvista.com * * Copyright 2002,2003 MontaVista Software Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include struct opq_elem_s { int block; opq_handler_cb handler; void *handler_data; opq_done_cb done; void *done_data; struct opq_elem_s *next; ilist_item_t ilist_item; }; struct opq_s { ilist_t *ops; os_hnd_lock_t *lock; int in_handler; os_handler_t *os_hnd; opq_done_cb done_handler; void *done_data; int blocked; int in_destroy; }; static void opq_lock(opq_t *opq) { if (opq->lock) opq->os_hnd->lock(opq->os_hnd, opq->lock); } static void opq_unlock(opq_t *opq) { if (opq->lock) opq->os_hnd->unlock(opq->os_hnd, opq->lock); } opq_t * opq_alloc(os_handler_t *os_hnd) { int rv; opq_t *opq; opq = ipmi_mem_alloc(sizeof(*opq)); if (!opq) return NULL; memset(opq, 0, sizeof(*opq)); opq->os_hnd = os_hnd; opq->in_handler = 0; opq->ops = alloc_ilist(); if (!(opq->ops)) { ipmi_mem_free(opq); return NULL; } if (os_hnd->create_lock) { rv = os_hnd->create_lock(opq->os_hnd, &(opq->lock)); if (rv) { free_ilist(opq->ops); ipmi_mem_free(opq); return NULL; } } else { opq->lock = NULL; } return opq; } static void opq_destroy_item(ilist_iter_t *iter, void *item, void *cb_data) { opq_elem_t *elem = (opq_elem_t *) item; elem->handler(elem->handler_data, 1); /* Memory for this is in elem, so we must delete it before we free the elem. */ ilist_delete(iter); opq_free_elem(elem); } void opq_destroy(opq_t *opq) { /* Only allow this to be done once. Callbacks might call this again. */ opq_lock(opq); if (opq->in_destroy) { opq_unlock(opq); return; } opq->in_destroy = 1; opq_unlock(opq); ilist_iter(opq->ops, opq_destroy_item, NULL); free_ilist(opq->ops); if (opq->lock) opq->os_hnd->destroy_lock(opq->os_hnd, opq->lock); ipmi_mem_free(opq); } static void start_next_op(opq_t *opq) { ilist_iter_t iter; opq_elem_t *elem; int success; ilist_init_iter(&iter, opq->ops); ilist_first(&iter); elem = ilist_get(&iter); while (elem) { ilist_delete(&iter); opq->done_handler = elem->done; opq->done_data = elem->done_data; opq_unlock(opq); success = elem->handler(elem->handler_data, 0); opq_free_elem(elem); opq_lock(opq); if (success == OPQ_HANDLER_STARTED) break; ilist_first(&iter); elem = ilist_get(&iter); } if (!elem) opq->in_handler = 0; } opq_elem_t * opq_alloc_elem(void) { opq_elem_t *elem; elem = ipmi_mem_alloc(sizeof(opq_elem_t)); return elem; } void opq_free_elem(opq_elem_t *elem) { ipmi_mem_free(elem); } int opq_new_op_prio(opq_t *opq, opq_handler_cb handler, void *cb_data, int nowait, int prio, opq_elem_t *elem) { int success; opq_lock(opq); if (opq->in_handler) { if (nowait) { opq_unlock(opq); return -1; } if (!elem) { elem = opq_alloc_elem(); if (!elem) goto out_err; } elem->handler = handler; elem->done = NULL; elem->handler_data = cb_data; elem->block = 1; if (prio) ilist_add_head(opq->ops, elem, &elem->ilist_item); else ilist_add_tail(opq->ops, elem, &elem->ilist_item); opq->blocked = 0; opq_unlock(opq); } else { if (elem) opq_free_elem(elem); opq->blocked = 0; opq->in_handler = 1; opq->done_handler = NULL; opq_unlock(opq); success = handler(cb_data, 0); if (success == OPQ_HANDLER_ABORTED) { /* In case any were added while I was unlocked. */ opq_lock(opq); start_next_op(opq); opq_unlock(opq); } } return 1; out_err: opq_unlock(opq); return 0; } int opq_new_op(opq_t *opq, opq_handler_cb handler, void *cb_data, int nowait) { return opq_new_op_prio(opq, handler, cb_data, nowait, OPQ_ADD_TAIL, NULL); } int opq_new_op_with_done(opq_t *opq, opq_handler_cb handler, void *handler_data, opq_done_cb done, void *done_data) { opq_elem_t *elem; int success; opq_lock(opq); if (opq->in_handler) { elem = ipmi_mem_alloc(sizeof(*elem)); if (!elem) goto out_err; elem->handler = handler; elem->handler_data = handler_data; elem->done = done; elem->done_data = done_data; elem->block = opq->blocked; ilist_add_tail(opq->ops, elem, &elem->ilist_item); opq->blocked = 0; opq_unlock(opq); } else { opq->blocked = 0; opq->in_handler = 1; opq->done_handler = done; opq->done_data = done_data; opq_unlock(opq); success = handler(handler_data, 0); if (success == OPQ_HANDLER_ABORTED) { /* In case any were added while I was unlocked. */ opq_lock(opq); start_next_op(opq); opq_unlock(opq); } } return 1; out_err: opq_unlock(opq); return 0; } void opq_add_block(opq_t *opq) { opq_lock(opq); opq->blocked = 1; opq_unlock(opq); } void opq_op_done(opq_t *opq) { ilist_iter_t iter; opq_elem_t *elem; opq_elem_t *list = NULL; opq_elem_t *next; opq_elem_t **list_end = &list; opq_done_cb done_handler; void *done_data; /* First check for done handlers. */ opq_lock(opq); ilist_init_iter(&iter, opq->ops); ilist_first(&iter); elem = ilist_get(&iter); while (elem && (!elem->block)) { ilist_delete(&iter); elem->next = NULL; *list_end = elem; list_end = &(elem->next); elem = ilist_get(&iter); } done_handler = opq->done_handler; done_data = opq->done_data; opq->done_handler = NULL; if (done_handler || list) { /* There are done handlers to call, unlock and call them. */ opq_unlock(opq); if (done_handler) done_handler(done_data, 0); while (list) { next = list->next; list->done(list->done_data, 0); opq_free_elem(list); list = next; } opq_lock(opq); /* During the time we were unlocked, handlers may have been added. */ ilist_first(&iter); elem = ilist_get(&iter); } start_next_op(opq); opq_unlock(opq); } int opq_stuff_in_progress(opq_t *opq) { return opq->in_handler; }