1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2014 Joyent, Inc.  All rights reserved.
  14  */
  15 
  16 #include <sys/scsi/adapters/mpt_sas/mptsas_hash.h>
  17 #include <sys/sysmacros.h>
  18 #include <sys/types.h>
  19 #include <sys/kmem.h>
  20 #include <sys/list.h>
  21 #include <sys/ddi.h>
  22 
  23 #ifdef lint
  24 extern refhash_link_t *obj_to_link(refhash_t *, void *);
  25 extern void *link_to_obj(refhash_t *, refhash_link_t *);
  26 extern void *obj_to_tag(refhash_t *, void *);
  27 #else
  28 #define obj_to_link(_h, _o)     \
  29         ((refhash_link_t *)(((char *)(_o)) + (_h)->rh_link_off))
  30 #define link_to_obj(_h, _l)     \
  31         ((void *)(((char *)(_l)) - (_h)->rh_link_off))
  32 #define obj_to_tag(_h, _o)      \
  33         ((void *)(((char *)(_o)) + (_h)->rh_tag_off))
  34 #endif
  35 
  36 refhash_t *
  37 refhash_create(uint_t bucket_count, refhash_hash_f hash,
  38     refhash_cmp_f cmp, refhash_dtor_f dtor, size_t obj_size, size_t link_off,
  39     size_t tag_off, int km_flags)
  40 {
  41         refhash_t *hp;
  42         uint_t i;
  43 
  44         hp = kmem_alloc(sizeof (refhash_t), km_flags);
  45         if (hp == NULL)
  46                 return (NULL);
  47         hp->rh_buckets = kmem_zalloc(bucket_count * sizeof (list_t), km_flags);
  48         if (hp->rh_buckets == NULL) {
  49                 kmem_free(hp, sizeof (refhash_t));
  50                 return (NULL);
  51         }
  52         hp->rh_bucket_count = bucket_count;
  53 
  54         for (i = 0; i < bucket_count; i++) {
  55                 list_create(&hp->rh_buckets[i], sizeof (refhash_link_t),
  56                     offsetof(refhash_link_t, rhl_chain_link));
  57         }
  58         list_create(&hp->rh_objs, sizeof (refhash_link_t),
  59             offsetof(refhash_link_t, rhl_global_link));
  60 
  61         hp->rh_obj_size = obj_size;
  62         hp->rh_link_off = link_off;
  63         hp->rh_tag_off = tag_off;
  64         hp->rh_hash = hash;
  65         hp->rh_cmp = cmp;
  66         hp->rh_dtor = dtor;
  67 
  68         return (hp);
  69 }
  70 
  71 void
  72 refhash_destroy(refhash_t *hp)
  73 {
  74         ASSERT(list_is_empty(&hp->rh_objs));
  75 
  76         kmem_free(hp->rh_buckets, hp->rh_bucket_count * sizeof (list_t));
  77         kmem_free(hp, sizeof (refhash_t));
  78 }
  79 
  80 void
  81 refhash_insert(refhash_t *hp, void *op)
  82 {
  83         uint_t bucket;
  84         refhash_link_t *lp = obj_to_link(hp, op);
  85 
  86         bucket = hp->rh_hash(obj_to_tag(hp, op)) % hp->rh_bucket_count;
  87         list_link_init(&lp->rhl_chain_link);
  88         list_link_init(&lp->rhl_global_link);
  89         lp->rhl_flags = 0;
  90         lp->rhl_refcnt = 0;
  91         list_insert_tail(&hp->rh_buckets[bucket], lp);
  92         list_insert_tail(&hp->rh_objs, lp);
  93 }
  94 
  95 static void
  96 refhash_delete(refhash_t *hp, void *op)
  97 {
  98         refhash_link_t *lp = obj_to_link(hp, op);
  99         uint_t bucket;
 100 
 101         bucket = hp->rh_hash(obj_to_tag(hp, op)) % hp->rh_bucket_count;
 102         list_remove(&hp->rh_buckets[bucket], lp);
 103         list_remove(&hp->rh_objs, lp);
 104         hp->rh_dtor(op);
 105 }
 106 
 107 void
 108 refhash_remove(refhash_t *hp, void *op)
 109 {
 110         refhash_link_t *lp = obj_to_link(hp, op);
 111 
 112         if (lp->rhl_refcnt > 0) {
 113                 lp->rhl_flags |= RHL_F_DEAD;
 114         } else {
 115                 refhash_delete(hp, op);
 116         }
 117 }
 118 
 119 void *
 120 refhash_lookup(refhash_t *hp, const void *tp)
 121 {
 122         uint_t bucket;
 123         refhash_link_t *lp;
 124         void *op;
 125 
 126         bucket = hp->rh_hash(tp) % hp->rh_bucket_count;
 127         for (lp = list_head(&hp->rh_buckets[bucket]); lp != NULL;
 128             lp = list_next(&hp->rh_buckets[bucket], lp)) {
 129                 op = link_to_obj(hp, lp);
 130                 if (hp->rh_cmp(obj_to_tag(hp, op), tp) == 0 &&
 131                     !(lp->rhl_flags & RHL_F_DEAD)) {
 132                         return (op);
 133                 }
 134         }
 135 
 136         return (NULL);
 137 }
 138 
 139 void *
 140 refhash_linear_search(refhash_t *hp, refhash_eval_f eval, void *arg)
 141 {
 142         void *op;
 143         refhash_link_t *lp;
 144 
 145         for (lp = list_head(&hp->rh_objs); lp != NULL;
 146             lp = list_next(&hp->rh_objs, lp)) {
 147                 op = link_to_obj(hp, lp);
 148                 if (eval(op, arg) == 0)
 149                         return (op);
 150         }
 151 
 152         return (NULL);
 153 }
 154 
 155 void
 156 refhash_hold(refhash_t *hp, void *op)
 157 {
 158         refhash_link_t *lp = obj_to_link(hp, op);
 159 
 160         ++lp->rhl_refcnt;
 161 }
 162 
 163 void
 164 refhash_rele(refhash_t *hp, void *op)
 165 {
 166         refhash_link_t *lp = obj_to_link(hp, op);
 167 
 168         ASSERT(lp->rhl_refcnt > 0);
 169 
 170         if (--lp->rhl_refcnt == 0 && (lp->rhl_flags & RHL_F_DEAD))
 171                 refhash_remove(hp, op);
 172 }
 173 
 174 void *
 175 refhash_first(refhash_t *hp)
 176 {
 177         refhash_link_t *lp;
 178 
 179         lp = list_head(&hp->rh_objs);
 180         if (lp == NULL)
 181                 return (NULL);
 182 
 183         ++lp->rhl_refcnt;
 184 
 185         return (link_to_obj(hp, lp));
 186 }
 187 
 188 void *
 189 refhash_next(refhash_t *hp, void *op)
 190 {
 191         refhash_link_t *lp;
 192 
 193         lp = obj_to_link(hp, op);
 194         while ((lp = list_next(&hp->rh_objs, lp)) != NULL) {
 195                 if (!(lp->rhl_flags & RHL_F_DEAD))
 196                         break;
 197         }
 198 
 199         refhash_rele(hp, op);
 200         if (lp == NULL)
 201                 return (NULL);
 202 
 203         ++lp->rhl_refcnt;
 204 
 205         return (link_to_obj(hp, lp));
 206 }
 207 
 208 boolean_t
 209 refhash_obj_valid(refhash_t *hp, const void *op)
 210 {
 211         /* LINTED - E_ARG_INCOMPATIBLE_WITH_ARG_L */
 212         const refhash_link_t *lp = obj_to_link(hp, op);
 213 
 214         return ((lp->rhl_flags & RHL_F_DEAD) != 0);
 215 }