/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include "isns_server.h" #include "isns_protocol.h" #include "isns_log.h" #include "isns_sched.h" #include "isns_scn.h" #include "isns_esi.h" /* * extern variables. */ /* * global variables. */ pthread_mutex_t el_mtx = PTHREAD_MUTEX_INITIALIZER; /* * local variables. */ static el_key_t **il; static int icurr = 0; static el_notice_t *curr = NULL; static uint32_t DU; static uint32_t DT; static uint32_t LB; static uint32_t NLIM; /* * external variables. */ /* * local functions. */ /* * **************************************************************************** * * il_shift: * Shift the indexed-list to the most current time. * * t - most current time. * * **************************************************************************** */ static void il_shift( uint32_t t ) { el_notice_t *fn, *n; el_key_t *fk, *k; uint32_t nt; int c; k = il[icurr]; while (k->time < t) { fk = k; fn = k->notice; /* remove the dummy key and dummy notice */ fk->left->right = fk->right; fk->right->left = fk->left; fn->pred->sucd = fn->sucd; fn->sucd->pred = fn->pred; /* find the place where the dummy goes to */ k = il[(icurr + DU - 1) % DU]; if (k->time < INFINITY - DT) { nt = k->time + DT; /* next key time */ } else { nt = INFINITY - 1; /* the last second */ } while (k->time < nt) { k = k->right; } n = k->notice->pred; c = 1; while (n->time >= nt) { c ++; n = n->pred; } n = n->sucd; /* update lower bound */ LB = fk->time; /* insert the dummy key */ fk->time = nt; fk->count = k->count - c + 1; fk->left = k->left; fk->right = k; k->left->right = fk; k->left = fk; k->count = c; /* insert the dummy notice */ fn->time = nt; fn->pred = n->pred; fn->sucd = n; n->pred->sucd = fn; n->pred = fn; /* shift the current index */ icurr = (icurr + 1) % DU; k = il[icurr]; } } /* * global functions. */ /* * **************************************************************************** * * el_init: * Initialize the element list. * * du - Number of uint in the indexed-list. * dt - Time interval of the indexed-list. * nlim - Limit number of each notice. * return - 0: successful, otherwise failed. * * **************************************************************************** */ int el_init( uint32_t du, uint32_t dt, uint32_t nlim ) { el_key_t *k, *kleft; el_notice_t *n, *npred; uint32_t t = 0; int i; if (du < 1 || dt < 1 || nlim < 1) { return (1); } DU = du; DT = dt; LB = 0; NLIM = nlim; /* * initialize the event set */ /* first dummy notice */ n = (el_notice_t *)malloc(sizeof (el_notice_t)); if (n == NULL) { return (1); } n->time = LB; n->event = NULL; n->isdummy = 1; n->pred = NULL; npred = n; /* first dummy key */ k = (el_key_t *)malloc(sizeof (el_key_t)); if (k == NULL) { return (1); } k->time = LB; k->count = 1; k->notice = n; k->left = NULL; kleft = k; n->key = k; /* index list */ il = (el_key_t **)malloc((DU + 1) * sizeof (el_key_t *)); if (il == NULL) { return (1); } /* create notice list, key list & index list */ for (i = 0; i < DU; i++) { t += DT; n = (el_notice_t *)malloc(sizeof (el_notice_t)); if (n == NULL) { return (1); } n->time = t; n->event = NULL; n->isdummy = 1; n->pred = npred; npred->sucd = n; npred = n; k = (el_key_t *)malloc(sizeof (el_key_t)); if (k == NULL) { return (1); } k->time = t; k->count = 1; k->notice = n; k->left = kleft; kleft->right = k; kleft = k; n->key = k; il[i] = k; } /* last dummy notice */ n = (el_notice_t *)malloc(sizeof (el_notice_t)); if (n == NULL) { return (1); } n->time = INFINITY; /* the end of the world */ n->event = NULL; n->isdummy = 1; n->pred = npred; n->sucd = NULL; npred->sucd = n; /* last dummy key */ k = (el_key_t *)malloc(sizeof (el_key_t)); if (k == NULL) { return (1); } k->time = INFINITY; /* the end of the world */ k->count = 1; k->notice = n; k->left = kleft; k->right = NULL; kleft->right = k; n->key = k; /* last index */ il[DU] = k; return (0); } /* * **************************************************************************** * * el_add: * Add an event to the element list with it's execution time. * It might not actually put the event to the list if the event * is the most current one for execution. * * ev - The Event. * t - The time when the event is scheduled at. * evp - Pointer of event for returning. * return - Error code. * * **************************************************************************** */ int el_add( void *ev, uint32_t t, void **evp ) { int ec = 0; uint32_t t1 = 0; int i, j; el_key_t *k; el_notice_t *n; el_key_t *y; el_notice_t *x; /* lock the event set */ (void) pthread_mutex_lock(&el_mtx); /* strip it off from the event list which is being handled */ if (evf_again(ev) != 0) { /* if it is rescheduling an event and the event */ /* was waiting for execution after idle finishes */ if (evf_rem(ev) == 0 && evp != NULL && (curr == NULL || t <= curr->time)) { /* no need to reschedule it */ *evp = ev; goto add_done; } evl_strip(ev); /* if it is marked as a removed event, do not add it */ if (evf_rem(ev) != 0) { ev_free(ev); goto add_done; } } /* get the index in the il */ if (t == 0) { t = ev_intval(ev); /* not initialization time */ if (evf_init(ev) || evf_again(ev)) { t1 = get_stopwatch(evf_wakeup(ev)); /* make il up to date */ il_shift(t1); /* avoid overflow */ if (t1 >= INFINITY - t) { /* the last second */ t1 = INFINITY - t1 - 1; } } t += t1; } i = (t - LB) / DT; if (i >= DU) { i = DU; } else { i = (i + icurr) % DU; } /* find the right key */ k = (il[i])->left; while (k->time > t) { k = k->left; } k = k->right; /* need to split */ if (k->count == NLIM) { /* insert a new key */ y = (el_key_t *)malloc(sizeof (el_key_t)); if (y == NULL) { ec = ISNS_RSP_INTERNAL_ERROR; goto add_done; } k->count = NLIM / 2; x = k->notice; for (j = 1; j <= NLIM / 2; j++) { x = x->pred; } y->time = x->time; y->count = NLIM - NLIM / 2; y->notice = x; y->right = k; y->left = k->left; k->left->right = y; k->left = y; /* update the key */ x->key = y; /* shift */ if (y->time > t) { k = y; } } /* make a new notice */ x = (el_notice_t *)malloc(sizeof (el_notice_t)); if (x == NULL) { ec = ISNS_RSP_INTERNAL_ERROR; goto add_done; } x->time = t; x->event = ev; x->isdummy = 0; x->key = NULL; /* insert it */ n = k->notice; while (n->time > t) { n = n->pred; } x->pred = n; x->sucd = n->sucd; n->sucd->pred = x; n->sucd = x; /* increase number of notice */ k->count ++; /* reset current notice and wake up idle */ if (curr == NULL || curr->time > t) { curr = x; } /* clear event flags */ evf_zero(ev); isnslog(LOG_DEBUG, "el_add", "%s [%d] is scheduled at %d.", ((ev_t *)ev)->type == EV_ESI ? "ESI" : "REG_EXP", ((ev_t *)ev)->uid, t); add_done: /* unlock the event set */ (void) pthread_mutex_unlock(&el_mtx); /* failed, free it */ if (ec != 0) { ev_free(ev); isnslog(LOG_DEBUG, "el_add", "failed, no memory."); } return (ec); } /* * **************************************************************************** * * el_remove: * Remove or update an event from the element list. If the event is * currently not in the element list, it must be in a queue which * contains all of event which are being executing at the moment. * So it seeks the event from the list prior to the element list. * * id1 - The event ID. * id2 - The second ID for the event update. * pending - Do not actually remove, mark it for removal pending. * return - Error code. * * **************************************************************************** */ int el_remove( uint32_t id1, uint32_t id2, int pending ) { el_key_t *k, *kl, *kr; el_notice_t *n, *n2; (void) pthread_mutex_lock(&el_mtx); /* search the event from the event list which is being handled */ if (evl_remove(id1, id2, pending) != 0) { (void) pthread_mutex_unlock(&el_mtx); return (0); } /* search the notice starting from current */ n = curr; while (n != NULL) { /* found the match notice */ if (!n->isdummy && ev_match(n->event, id1) != 0) { if (ev_remove(n->event, id2, 1, pending) == 0) { /* update the key of the match notice */ k = n->key; if (k != NULL && k->count == 1) { /* no more notice */ k->left->right = k->right; k->right->left = k->left; free(k); } else { if (k != NULL) { k->notice = n->pred; k->notice->key = k; k->time = k->notice->time; } n2 = n; k = n2->key; while (k == NULL) { n2 = n2->sucd; k = n2->key; } /* decrease the count by one */ k->count --; /* merge the keys */ kl = k->left; kr = k->right; if (!kl->notice->isdummy && (kl->count + k->count) <= NLIM) { /* delete the left key */ k->count += kl->count; k->left = kl->left; k->left->right = k; kl->notice->key = NULL; free(kl); } else if (!k->notice->isdummy && (kr->count + k->count) <= NLIM) { /* delete this key */ kr->count += k->count; kr->left = k->left; kr->left->right = kr; k->notice->key = NULL; free(k); } } /* delete the match notice */ n->pred->sucd = n->sucd; n->sucd->pred = n->pred; /* update current */ if (n == curr) { n2 = n->sucd; while (n2 != NULL && n2->isdummy) { n2 = n2->sucd; } curr = n2; } free(n); } break; /* exit while loop */ } n = n->sucd; } (void) pthread_mutex_unlock(&el_mtx); return (0); } /* * **************************************************************************** * * el_first: * Fetch the first event from the element list. * * t - Pointer of time of the event for returning. * return - The event. * * **************************************************************************** */ void * el_first( uint32_t *t ) { void *p = NULL; el_notice_t *n; el_key_t *k; (void) pthread_mutex_lock(&el_mtx); if (curr != NULL) { /* remove current from the event set */ curr->pred->sucd = curr->sucd; curr->sucd->pred = curr->pred; /* decrease number of notice */ n = curr; while (n->key == NULL) { n = n->sucd; } k = n->key; k->count --; /* empty not-dummy key */ if (k->count == 0) { k->left->right = k->right; k->right->left = k->left; free(k); } /* get next notice */ n = curr->sucd; while (n != NULL && n->isdummy) { n = n->sucd; } /* return the time */ *t = curr->time; /* reset current notice */ p = curr->event; free(curr); curr = n; } /* the one that is being handled by esi_proc */ if (p) { evl_append(p); } (void) pthread_mutex_unlock(&el_mtx); if (p) { isnslog(LOG_DEBUG, "el_first", "%s [%d] is fetched.", ((ev_t *)p)->type == EV_ESI ? "ESI" : "REG_EXP", ((ev_t *)p)->uid); } return (p); }