/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#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);
}