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