/* * 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 #include #include "isns_server.h" #include "isns_cache.h" #include "isns_htab.h" #include "isns_log.h" #define UID_REUSABLE(T, X) ((T) - (X)->t >= ONE_DAY) /* * external variables. */ extern int cache_flag; /* * **************************************************************************** * avl_search: * search a node from an AVL tree. * * tab - the hash table. * uid - the object UID. * return - the node which matches the object UID. * * **************************************************************************** */ static htab_itemx_t * avl_search( const htab_t *tab, const uint32_t uid ) { htab_itemx_t *x = tab->avlt; while (x != NULL) { if (x->uid > uid) { x = x->l; } else if (x->uid < uid) { x = x->r; } else { break; } } return (x); } /* * **************************************************************************** * avl_search_next: * search a node from an AVL tree, the object UID of the node * is next to the previous object UID. * * tab - the hash table. * uid - the previous object UID. * return - the next node. * * **************************************************************************** */ static htab_itemx_t * avl_search_next( const htab_t *tab, const uint32_t uid ) { htab_itemx_t *p = NULL; htab_itemx_t *x = tab->avlt; while (x != NULL) { if (x->uid > uid) { p = x; x = x->l; } else if (x->uid <= uid) { x = x->r; } } return (p); } /* * **************************************************************************** * avl_ll: * perform LL balance rotation on an AVL tree (or the subtree). * * a - the left child. * b - the right child. * return - the new root. * * **************************************************************************** */ static htab_itemx_t * avl_ll( htab_itemx_t *a, htab_itemx_t *b ) { /* rotate right */ a->l = b->r; a->bf = 0; b->r = a; b->bf = 0; return (b); } /* * **************************************************************************** * avl_rr: * perform RR balance rotation on an AVL tree (or the subtree). * * a - the left child. * b - the right child. * return - the new root. * * **************************************************************************** */ static htab_itemx_t * avl_rr( htab_itemx_t *a, htab_itemx_t *b ) { /* rotate left */ a->r = b->l; a->bf = 0; b->l = a; b->bf = 0; return (b); } /* * **************************************************************************** * avl_lr: * perform LR balance rotation on an AVL tree (or the subtree). * * a - the left child. * b - the right child. * return - the new root. * * **************************************************************************** */ static htab_itemx_t * avl_lr( htab_itemx_t *a, htab_itemx_t *b ) { htab_itemx_t *c; c = b->r; /* rotate left and then right */ a->l = c->r; c->r = a; b->r = c->l; c->l = b; /* update balance factor */ switch (c->bf) { case -1: /* on c's right */ a->bf = 0; b->bf = 1; break; case 0: /* on c itself */ a->bf = 0; b->bf = 0; break; case 1: /* on c's left */ a->bf = -1; b->bf = 0; break; } c->bf = 0; return (c); } /* * **************************************************************************** * avl_rl: * perform RL balance rotation on an AVL tree (or the subtree). * * a - the left child. * b - the right child. * return - the new root. * * **************************************************************************** */ static htab_itemx_t * avl_rl( htab_itemx_t *a, htab_itemx_t *b ) { htab_itemx_t *c; c = b->l; /* rotate right and then left */ a->r = c->l; c->l = a; b->l = c->r; c->r = b; /* update balance factor */ switch (c->bf) { case -1: /* on c's right */ a->bf = 1; b->bf = 0; break; case 0: /* on c itself */ a->bf = 0; b->bf = 0; break; case 1: /* on c's left */ a->bf = 0; b->bf = -1; break; } c->bf = 0; return (c); } /* * **************************************************************************** * avl_insert: * insert a node into an AVL tree. * * tab - the hash table. * x - the node being added. * * **************************************************************************** */ static void avl_insert( htab_t *tab, htab_itemx_t *x ) { htab_itemx_t *f, *a, *p, *q, *b, *c; int d; /* initialize the new one */ x->bf = 0; x->l = NULL; x->r = NULL; if (tab->avlt == NULL) { tab->avlt = x; } else { /* locate the position */ f = NULL; a = tab->avlt; p = tab->avlt; q = NULL; while (p != NULL) { if (p->bf != 0) { a = p; f = q; } q = p; if (x->uid < q->uid) { p = p->l; } else { p = p->r; } } /* insert it */ if (x->uid < q->uid) { q->l = x; } else { q->r = x; } /* update the balance factor between a to x */ if (x->uid < a->uid) { p = a->l; d = 1; } else { p = a->r; d = -1; } b = p; while (p != x) { if (x->uid < p->uid) { p->bf = 1; p = p->l; } else { p->bf = -1; p = p->r; } } /* brance is not broken */ if (a->bf == 0) { a->bf = d; goto bal_done; } else if (a->bf + d == 0) { a->bf = 0; goto bal_done; } /* rotate the tree */ if (d == 1) { if (b->bf == 1) { /* LL rotate */ c = avl_ll(a, b); } else if (b->bf == -1) { /* LR rotate */ c = avl_lr(a, b); } } else { if (b->bf == -1) { /* RR rotate */ c = avl_rr(a, b); } else if (b->bf == 1) { /* RL rotate */ c = avl_rl(a, b); } } /* update the parent */ if (f == NULL) { tab->avlt = c; } else if (f->l == a) { f->l = c; } else if (f->r == a) { f->r = c; } } bal_done: if (x->uid > tab->buid) { tab->buid = x->uid; } } /* * **************************************************************************** * new_uid: * allocate new node(s) of the avl tree. * * tab - the hash table. * uid - the UID of the node. * return - the newly allocated UID node. * * **************************************************************************** */ static htab_itemx_t * new_uid( htab_t *tab, uint32_t uid ) { htab_itemx_t *x = NULL; uint32_t start, end; /* overflow happened */ if (uid == 0) { /* search for an unused one */ uid ++; while (uid != 0 && avl_search(tab, uid) != NULL) { uid ++; } if (uid == 0) { /* all are used up, sigh! */ return (NULL); } } /* check if there is a gap and the gap needs to be filled up */ if (uid > tab->buid && (tab->flags & UID_FLAGS_SEQ) != 0) { start = tab->buid + 1; } else { start = uid; } end = uid; /* make new UID(s) */ do { if (x != NULL) { x->hval = BAD_HVAL_MASK; x->t = 0; /* put it to the start of the fifo list */ x->n = tab->list; tab->list = x; if (tab->tail == NULL) { tab->tail = x; } } x = (htab_itemx_t *)malloc(sizeof (htab_itemx_t)); if (x != NULL) { x->uid = start; x->n = NULL; /* insert it to the tree */ avl_insert(tab, x); } start ++; } while (x != NULL && start <= end && start != 0); return (x); } /* * **************************************************************************** * uid_insert: * insert a new UID node to the avl tree. * * tab - the hash table. * uid_p- the pointer of the UID. * hval - the hash value of the new node. * return - 0: no UID value assigned; * 1: assigned an UID. * -1: no memory. * -2: invalid UID. * * **************************************************************************** */ static int uid_insert( htab_t *tab, uint32_t *const uid_p, const uint32_t hval ) { int assignx = 0; uint32_t uid = *uid_p; htab_itemx_t *x, *n; if (uid != 0) { /* search the existing one from the tree */ x = avl_search(tab, uid); if (x == NULL) { x = new_uid(tab, uid); } else if (!BAD_HVAL(x->hval) && x->hval != hval) { /* the item with this uid will override an */ /* existing item, we treat this as an error */ return (-2); } } else { /* assign a value */ x = tab->list; /* strip off the used ones */ while (x != NULL && !BAD_HVAL(x->hval)) { n = x->n; x->n = NULL; x = n; } if (x == NULL || UID_REUSABLE(tab->c->timestamp(), x) == 0) { /* none is available, make a new one */ tab->list = x; x = new_uid(tab, tab->buid + 1); } else { n = x->n; x->n = NULL; tab->list = n; } /* update the available list */ if (tab->list == NULL) { tab->tail = NULL; } assignx = 1; if (x != NULL) { *uid_p = x->uid; } } if (x == NULL) { return (-1); /* no memory */ } x->hval = hval; x->t = 0; /* registration initial time */ return (assignx); } /* * **************************************************************************** * enlarge_htab: * enlarge the hash table when it gets too full. * * tab - the hash table. * * **************************************************************************** */ static void enlarge_htab( htab_t *tab ) { htab_item_t **items; uint16_t logsize; uint32_t oldsz, newsz, mask; htab_item_t *item, *tmp, **itemp; uint16_t i; uint32_t j; uint32_t uid; /* enlarge the logsize by one */ logsize = tab->logsize + 1; newsz = (1 << logsize); items = (htab_item_t **)calloc( newsz * tab->chunks, sizeof (htab_item_t *)); /* re-hash all items to the new table */ if (items != NULL) { mask = newsz - 1; oldsz = (1 << tab->logsize); i = 0; while (i < tab->chunks) { j = 0; while (j < oldsz) { item = tab->items[(i * oldsz) + j]; while (item != NULL) { tmp = item->next; itemp = &items[(i * newsz) + (item->hval & mask)]; uid = tab->c->get_uid(item->p); while (*itemp != NULL && tab->c->get_uid((*itemp)->p) > uid) { itemp = &(*itemp)->next; } item->next = *itemp; *itemp = item; item = tmp; } j ++; } i ++; } free(tab->items); tab->items = items; tab->logsize = logsize; tab->mask = mask; } else { isnslog(LOG_DEBUG, "enlarge_htab", "calloc() failed."); } } /* * **************************************************************************** * htab_init: * some generic initialization for the hash table. * * **************************************************************************** */ void htab_init( ) { /* do nothing */ } /* * **************************************************************************** * htab_create: * create a new hash table. * * flags - UID_FLAGS_SEQ: the UID in the table needs to be sequential. * logsize - the hash table logsize. * chunks - the number of seperated chunks of the table. * return - the newly created hash table. * * **************************************************************************** */ htab_t * htab_create( int flags, uint16_t logsize, uint16_t chunks ) { htab_t *tab = NULL; htab_item_t **items = NULL; uint32_t count; /* do not enlarge it if the logsize reaches the maximum */ if (logsize <= MAX_LOGSIZE && chunks > 0) { tab = (htab_t *)calloc(1, sizeof (htab_t)); if (tab != NULL) { count = (1 << logsize); items = (htab_item_t **)calloc( count * chunks, sizeof (htab_item_t *)); if (items != NULL) { tab->flags = flags; tab->items = items; tab->logsize = logsize; tab->chunks = chunks; tab->mask = count - 1; tab->count = 1; /* reserve one */ tab->avlt = NULL; tab->buid = 0; tab->list = NULL; tab->tail = NULL; } else { free(tab); tab = NULL; } } } return (tab); } /* * **************************************************************************** * htab_compute_hval: * compute a hash value for the specified key. * * key - the key of the hash. * return - the hash value. * * **************************************************************************** */ uint32_t htab_compute_hval( const uchar_t *key ) { /* use classic Dan Bernstein hash alorigthm */ uint32_t hash = 5381; int c; while ((c = *key++) != 0) { hash = ((hash << 5) + hash) + c; } return (hash); } /* * **************************************************************************** * htab_add: * add an object to the hash table. * * tab - the hash table. * p - the object. * flag - 0: not an association object; otherwise association object. * uid_p- pointer of UID for returning. * update_p - pointer of update flag for returning. * return - error code. * * **************************************************************************** */ int htab_add( htab_t *tab, void *p, int flag, uint32_t *uid_p, int *update_p ) { int ec = 0; htab_item_t *items = NULL, **itemp; uint32_t chunksz; uint32_t flags = 0; uint32_t hval; uint32_t uid = 0; int i; /* compute the hash value */ hval = VALID_HVAL(tab->c->get_hval(p, 0, &flags)); /* check for duplicate */ items = tab->items[hval & tab->mask]; while (items != NULL) { if (tab->c->cmp(items->p, p, 0) == 0) { if (flag == 0) { ec = tab->c->replace_hook(items->p, p, uid_p, update_p == NULL ? 1 : 0); } if (update_p != NULL) { *update_p = 1; } items = NULL; goto add_done; } items = items->next; } /* add new object */ if (update_p != NULL) { *update_p = 0; } /* make new items for the object */ items = (htab_item_t *)calloc(tab->chunks, sizeof (htab_item_t)); if (items == NULL || tab->count == 0 || (++tab->count) == 0) { /* no memory or table is full */ ec = ISNS_RSP_INTERNAL_ERROR; goto add_done; } /* check if the table needs is too full */ chunksz = (1 << tab->logsize); if (tab->count >= (chunksz * HASH_RATIO) && tab->logsize < MAX_LOGSIZE) { enlarge_htab(tab); chunksz = (1 << tab->logsize); } /* put the UID of the object to the avl tree */ uid = tab->c->get_uid(p); switch (uid_insert(tab, &uid, hval)) { case -2: ec = ISNS_RSP_INVALID_REGIS; goto add_done; case -1: ec = ISNS_RSP_INTERNAL_ERROR; goto add_done; case 0: break; case 1: tab->c->set_uid(p, uid); break; default: break; } /* update data store before putting to hash table */ if (flag == 0) { /* not association object */ ec = tab->c->add_hook(p); } /* put the object to the table */ for (i = 0; ec == 0; ) { items[i].hval = hval; items[i].p = p; itemp = &tab->items[(i * chunksz) + (hval & tab->mask)]; while (*itemp != NULL && tab->c->get_uid((*itemp)->p) > uid) { itemp = &(*itemp)->next; } items[i].next = *itemp; *itemp = &items[i]; i ++; if (i < tab->chunks) { hval = VALID_HVAL(tab->c->get_hval(p, i, &flags)); } else { break; } } /* cache has been successfully updated */ SET_CACHE_UPDATED(); /* successfully added */ items = NULL; if (ec == 0) { /* perform the Default DD behavior */ tab->c->ddd(p, '+'); /* set the return uid */ if (uid_p != NULL) { *uid_p = uid; } } add_done: if (ec != 0 && items != NULL) { free(items); } return (ec); } /* * **************************************************************************** * htab_remove: * remove an object from the hash table. * * tab - the hash table. * p - the lookup control for the object. * uid - the UID of the object. * clone_flag - indicate if the removing is for an association object. * return - the removed object. * * **************************************************************************** */ isns_obj_t * htab_remove( htab_t *tab, void *p, uint32_t uid, int clone_flag ) { void *zhizi = NULL; void *clone = NULL; htab_item_t *items = NULL; htab_item_t *item, **itemp; htab_itemx_t *x = NULL; uint32_t chunksz; uint32_t flags; uint32_t hval; int i; /* get the object hash value */ if (uid != 0) { x = avl_search(tab, uid); if (x != NULL && !BAD_HVAL(x->hval)) { hval = x->hval; } else { goto remove_done; } } else { flags = 0 | FLAGS_CTRL_MASK; hval = VALID_HVAL(tab->c->get_hval(p, 0, &flags)); } /* search the object from the table */ flags = 0; chunksz = (1 << tab->logsize); for (i = 0; ; ) { itemp = &tab->items[(i * chunksz) + (hval & tab->mask)]; item = *itemp; while (item != NULL) { /* found it */ if (tab->c->cmp(item->p, p, 1) == 0) { /* make an association object if the object */ /* has membership in user-defined DD(s). */ if (i == 0) { if ((clone = tab->c->clone(item->p, clone_flag)) == NULL) { tab->c->ddd(item->p, '-'); tab->count --; items = item; zhizi = item->p; } } if (clone == NULL) { /* remove it */ *itemp = item->next; } else if (clone == item->p) { /* itself is an association object */ goto remove_done; } else { /* replace it with association */ zhizi = item->p; item->p = clone; } if (i == 0) { /* obj has been removed or updated */ SET_CACHE_UPDATED(); } break; } itemp = &item->next; item = *itemp; } i ++; if (zhizi != NULL && i < tab->chunks) { hval = VALID_HVAL(tab->c->get_hval( zhizi, i, &flags)); } else { break; } } /* update the node in the avl tree */ if (items != NULL) { if (x == NULL) { uid = tab->c->get_uid(zhizi); ASSERT(uid != 0); x = avl_search(tab, uid); } ASSERT(x != NULL && !BAD_HVAL(x->hval)); /* mark the uid item as invalid */ x->hval |= BAD_HVAL_MASK; /* update the timestamp */ x->t = tab->c->timestamp(); /* put it to the end of fifo list */ if (tab->list != NULL) { tab->tail->n = x; } else { tab->list = x; } tab->tail = x; } remove_done: if (items != NULL) { free(items); } return (zhizi); } /* * **************************************************************************** * htab_lookup: * lookup an object from the hash table. * * tab - the hash table. * p - the lookup control for the item. * uid - the UID of the object. * uid_p- the pointer of UID for returning. * callback - callback function if the object is found. * rekey - flag that indicates if the callback function will update * the key of the object. * return - error code. * * **************************************************************************** */ int htab_lookup( htab_t *tab, void *p, uint32_t uid, uint32_t *uid_p, int (*callback)(void *, void *), int rekey ) { uint32_t ret = 0; void *zhizi = NULL; htab_item_t *item, **itemp; htab_itemx_t *x = NULL; uint32_t chunksz; uint32_t flags = 0 | FLAGS_CTRL_MASK; uint32_t hval; int i; /* compute the hash value */ if (uid != 0) { x = avl_search(tab, uid); if (x != NULL) { hval = x->hval; } else { hval = BAD_HVAL_MASK; } } else { hval = VALID_HVAL(tab->c->get_hval(p, 0, &flags)); } /* find the object */ if (!BAD_HVAL(hval)) { i = flags & FLAGS_CHUNK_MASK; chunksz = (1 << tab->logsize); itemp = &tab->items[(i * chunksz) + (hval & tab->mask)]; item = *itemp; while (item != NULL) { if (tab->c->cmp(item->p, p, 1) == 0) { zhizi = item->p; break; } itemp = &item->next; item = *itemp; } } /* found it */ if (zhizi != NULL) { /* set the return uid */ if (uid_p != NULL) { *uid_p = tab->c->get_uid(zhizi); } /* invoke callback */ if (callback != NULL) { ret = callback(zhizi, p); } if (rekey != 0 && ret == 0) { /* Rekey works for one-chunk hash table only. */ ASSERT(tab->chunks == 1 && x != NULL); /* remove from previous slot */ *itemp = item->next; /* add it to the new slot */ flags = 0; hval = VALID_HVAL(tab->c->get_hval(zhizi, 0, &flags)); x->hval = hval; item->hval = hval; itemp = &tab->items[(hval & tab->mask)]; while (*itemp != NULL && (tab->c->get_uid((*itemp)->p) > tab->c->get_uid(zhizi))) { itemp = &(*itemp)->next; } item->next = *itemp; *itemp = item; } } else if (uid_p != NULL) { /* set the return uid to 0 */ *uid_p = 0; } return (ret); } /* * **************************************************************************** * htab_get_next: * get the next object UID from the hash table. * * tab - the hash table. * uid - the previous objet UID. * return - the next object UID. * * **************************************************************************** */ uint32_t htab_get_next( htab_t *tab, uint32_t uid ) { htab_itemx_t *x; do { /* search the next node from the avl tree */ x = avl_search_next(tab, uid); if (x != NULL) { uid = x->uid; /* validate the node */ if (!BAD_HVAL(x->hval)) { return (uid); } } } while (x != NULL); /* no more node is available */ return (0); } /* * **************************************************************************** * htab_dump: * dump all objects stored in the hash table for debug purpose. * * tab - the hash table. * * **************************************************************************** */ #ifdef DEBUG void htab_dump( htab_t *tab ) { uint32_t chunksz; htab_item_t *items; uint32_t i; chunksz = (1 << tab->logsize); for (i = 0; i < chunksz; i++) { items = tab->items[i]; while (items != NULL) { tab->c->dump(items->p); items = items->next; } } } #endif