13434Sesaxe /* 23434Sesaxe * CDDL HEADER START 33434Sesaxe * 43434Sesaxe * The contents of this file are subject to the terms of the 53434Sesaxe * Common Development and Distribution License (the "License"). 63434Sesaxe * You may not use this file except in compliance with the License. 73434Sesaxe * 83434Sesaxe * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 93434Sesaxe * or http://www.opensolaris.org/os/licensing. 103434Sesaxe * See the License for the specific language governing permissions 113434Sesaxe * and limitations under the License. 123434Sesaxe * 133434Sesaxe * When distributing Covered Code, include this CDDL HEADER in each 143434Sesaxe * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 153434Sesaxe * If applicable, add the following below this CDDL HEADER, with the 163434Sesaxe * fields enclosed by brackets "[]" replaced with your own identifying 173434Sesaxe * information: Portions Copyright [yyyy] [name of copyright owner] 183434Sesaxe * 193434Sesaxe * CDDL HEADER END 203434Sesaxe */ 213434Sesaxe /* 22*8408SEric.Saxe@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 233434Sesaxe * Use is subject to license terms. 243434Sesaxe */ 253434Sesaxe 263434Sesaxe #include <sys/bitset.h> 273434Sesaxe #include <sys/kmem.h> 283434Sesaxe #include <sys/systm.h> 29*8408SEric.Saxe@Sun.COM #include <sys/cpuvar.h> 303434Sesaxe #include <sys/cmn_err.h> 313434Sesaxe #include <sys/sysmacros.h> 323434Sesaxe 333434Sesaxe /* 343434Sesaxe * Initialize a bitset_t. 353434Sesaxe * After bitset_init(), the bitset will be zero sized. 363434Sesaxe */ 373434Sesaxe void 383434Sesaxe bitset_init(bitset_t *b) 393434Sesaxe { 403434Sesaxe bzero(b, sizeof (bitset_t)); 413434Sesaxe } 423434Sesaxe 433434Sesaxe /* 443434Sesaxe * Uninitialize a bitset_t. 453434Sesaxe * This will free the bitset's data, leaving it zero sized. 463434Sesaxe */ 473434Sesaxe void 483434Sesaxe bitset_fini(bitset_t *b) 493434Sesaxe { 503434Sesaxe if (b->bs_words > 0) 513434Sesaxe kmem_free(b->bs_set, b->bs_words * sizeof (ulong_t)); 523434Sesaxe } 533434Sesaxe 543434Sesaxe /* 553434Sesaxe * Resize a bitset to where it can hold sz number of bits. 563434Sesaxe * This can either grow or shrink the bitset holding capacity. 573434Sesaxe * In the case of shrinkage, elements that reside outside the new 583434Sesaxe * holding capacity of the bitset are lost. 593434Sesaxe */ 603434Sesaxe void 613434Sesaxe bitset_resize(bitset_t *b, uint_t sz) 623434Sesaxe { 633434Sesaxe uint_t nwords; 643434Sesaxe ulong_t *bset_new, *bset_tmp; 653434Sesaxe 663434Sesaxe nwords = BT_BITOUL(sz); 673434Sesaxe if (b->bs_words == nwords) 683434Sesaxe return; /* already properly sized */ 693434Sesaxe 703434Sesaxe /* 71*8408SEric.Saxe@Sun.COM * Allocate the new ulong_t array, and copy the old one, if there 72*8408SEric.Saxe@Sun.COM * was an old one. 733434Sesaxe */ 743434Sesaxe if (nwords > 0) { 753434Sesaxe bset_new = kmem_zalloc(nwords * sizeof (ulong_t), KM_SLEEP); 76*8408SEric.Saxe@Sun.COM if (b->bs_words > 0) 77*8408SEric.Saxe@Sun.COM bcopy(b->bs_set, bset_new, 78*8408SEric.Saxe@Sun.COM MIN(b->bs_words, nwords) * sizeof (ulong_t)); 793434Sesaxe } else { 803434Sesaxe bset_new = NULL; 813434Sesaxe } 823434Sesaxe 833434Sesaxe /* swap out the old ulong_t array for new one */ 843434Sesaxe bset_tmp = b->bs_set; 853434Sesaxe b->bs_set = bset_new; 863434Sesaxe 873434Sesaxe /* free up the old array */ 88*8408SEric.Saxe@Sun.COM if (b->bs_words > 0) 89*8408SEric.Saxe@Sun.COM kmem_free(bset_tmp, b->bs_words * sizeof (ulong_t)); 903434Sesaxe b->bs_words = nwords; 913434Sesaxe } 923434Sesaxe 933434Sesaxe /* 943434Sesaxe * Returns the current holding capacity of the bitset 953434Sesaxe */ 963434Sesaxe uint_t 973434Sesaxe bitset_capacity(bitset_t *b) 983434Sesaxe { 993434Sesaxe return (b->bs_words * BT_NBIPUL); 1003434Sesaxe } 1013434Sesaxe 1023434Sesaxe /* 1033434Sesaxe * Add and delete bits in the bitset. 1043434Sesaxe * 1053434Sesaxe * Adding a bit that is already set, and clearing a bit that's already clear 1063434Sesaxe * is legal. 1073434Sesaxe * 1083434Sesaxe * Adding or deleting an element that falls outside the bitset's current 1093434Sesaxe * holding capacity is illegal. 1103434Sesaxe */ 111*8408SEric.Saxe@Sun.COM 112*8408SEric.Saxe@Sun.COM /* 113*8408SEric.Saxe@Sun.COM * Set a bit 114*8408SEric.Saxe@Sun.COM */ 1153434Sesaxe void 1163434Sesaxe bitset_add(bitset_t *b, uint_t elt) 1173434Sesaxe { 1183434Sesaxe ASSERT(b->bs_words * BT_NBIPUL > elt); 1193434Sesaxe 1203434Sesaxe BT_SET(b->bs_set, elt); 1213434Sesaxe } 1223434Sesaxe 123*8408SEric.Saxe@Sun.COM /* 124*8408SEric.Saxe@Sun.COM * Set a bit in an atomically safe way 125*8408SEric.Saxe@Sun.COM */ 126*8408SEric.Saxe@Sun.COM void 127*8408SEric.Saxe@Sun.COM bitset_atomic_add(bitset_t *b, uint_t elt) 128*8408SEric.Saxe@Sun.COM { 129*8408SEric.Saxe@Sun.COM ASSERT(b->bs_words * BT_NBIPUL > elt); 130*8408SEric.Saxe@Sun.COM 131*8408SEric.Saxe@Sun.COM BT_ATOMIC_SET(b->bs_set, elt); 132*8408SEric.Saxe@Sun.COM } 133*8408SEric.Saxe@Sun.COM 134*8408SEric.Saxe@Sun.COM /* 135*8408SEric.Saxe@Sun.COM * Atomically test that a given bit isn't set, and set it. 136*8408SEric.Saxe@Sun.COM * Returns -1 if the bit was already set. 137*8408SEric.Saxe@Sun.COM */ 138*8408SEric.Saxe@Sun.COM int 139*8408SEric.Saxe@Sun.COM bitset_atomic_test_and_add(bitset_t *b, uint_t elt) 140*8408SEric.Saxe@Sun.COM { 141*8408SEric.Saxe@Sun.COM int r; 142*8408SEric.Saxe@Sun.COM 143*8408SEric.Saxe@Sun.COM ASSERT(b->bs_words * BT_NBIPUL > elt); 144*8408SEric.Saxe@Sun.COM BT_ATOMIC_SET_EXCL(b->bs_set, elt, r); 145*8408SEric.Saxe@Sun.COM 146*8408SEric.Saxe@Sun.COM return (r); 147*8408SEric.Saxe@Sun.COM } 148*8408SEric.Saxe@Sun.COM 149*8408SEric.Saxe@Sun.COM /* 150*8408SEric.Saxe@Sun.COM * Clear a bit 151*8408SEric.Saxe@Sun.COM */ 1523434Sesaxe void 1533434Sesaxe bitset_del(bitset_t *b, uint_t elt) 1543434Sesaxe { 1553434Sesaxe ASSERT(b->bs_words * BT_NBIPUL > elt); 1563434Sesaxe 1573434Sesaxe BT_CLEAR(b->bs_set, elt); 1583434Sesaxe } 1593434Sesaxe 1603434Sesaxe /* 161*8408SEric.Saxe@Sun.COM * Clear a bit in an atomically safe way 162*8408SEric.Saxe@Sun.COM */ 163*8408SEric.Saxe@Sun.COM void 164*8408SEric.Saxe@Sun.COM bitset_atomic_del(bitset_t *b, uint_t elt) 165*8408SEric.Saxe@Sun.COM { 166*8408SEric.Saxe@Sun.COM ASSERT(b->bs_words * BT_NBIPUL > elt); 167*8408SEric.Saxe@Sun.COM 168*8408SEric.Saxe@Sun.COM BT_ATOMIC_CLEAR(b->bs_set, elt); 169*8408SEric.Saxe@Sun.COM } 170*8408SEric.Saxe@Sun.COM 171*8408SEric.Saxe@Sun.COM /* 172*8408SEric.Saxe@Sun.COM * Atomically test that a bit is set, and clear it. 173*8408SEric.Saxe@Sun.COM * Returns -1 if the bit was already clear. 174*8408SEric.Saxe@Sun.COM */ 175*8408SEric.Saxe@Sun.COM int 176*8408SEric.Saxe@Sun.COM bitset_atomic_test_and_del(bitset_t *b, uint_t elt) 177*8408SEric.Saxe@Sun.COM { 178*8408SEric.Saxe@Sun.COM int r; 179*8408SEric.Saxe@Sun.COM 180*8408SEric.Saxe@Sun.COM ASSERT(b->bs_words * BT_NBIPUL > elt); 181*8408SEric.Saxe@Sun.COM BT_ATOMIC_CLEAR_EXCL(b->bs_set, elt, r); 182*8408SEric.Saxe@Sun.COM 183*8408SEric.Saxe@Sun.COM return (r); 184*8408SEric.Saxe@Sun.COM } 185*8408SEric.Saxe@Sun.COM 186*8408SEric.Saxe@Sun.COM /* 1873434Sesaxe * Return non-zero if the bit is present in the set 1883434Sesaxe */ 1893434Sesaxe int 1903434Sesaxe bitset_in_set(bitset_t *b, uint_t elt) 1913434Sesaxe { 1923693Sesaxe if (elt >= b->bs_words * BT_NBIPUL) 1933693Sesaxe return (0); 1943434Sesaxe 1953434Sesaxe return (BT_TEST(b->bs_set, elt)); 1963434Sesaxe } 1973434Sesaxe 1983434Sesaxe /* 1993434Sesaxe * Return non-zero if the bitset is empty 2003434Sesaxe */ 2013434Sesaxe int 2023434Sesaxe bitset_is_null(bitset_t *b) 2033434Sesaxe { 2043434Sesaxe int i; 2053434Sesaxe 2063434Sesaxe for (i = 0; i < b->bs_words; i++) 2073434Sesaxe if (b->bs_set[i] != 0) 2083434Sesaxe return (0); 2093434Sesaxe return (1); 2103434Sesaxe } 2113434Sesaxe 2123434Sesaxe /* 213*8408SEric.Saxe@Sun.COM * Perform a non-victimizing search for a set bit in a word 214*8408SEric.Saxe@Sun.COM * A "seed" is passed to pseudo-randomize the search. 215*8408SEric.Saxe@Sun.COM * Return -1 if no set bit was found 216*8408SEric.Saxe@Sun.COM */ 217*8408SEric.Saxe@Sun.COM static uint_t 218*8408SEric.Saxe@Sun.COM bitset_find_in_word(ulong_t w, uint_t seed) 219*8408SEric.Saxe@Sun.COM { 220*8408SEric.Saxe@Sun.COM uint_t rotate_bit, elt = (uint_t)-1; 221*8408SEric.Saxe@Sun.COM ulong_t rotated_word; 222*8408SEric.Saxe@Sun.COM 223*8408SEric.Saxe@Sun.COM if (w == (ulong_t)0) 224*8408SEric.Saxe@Sun.COM return (elt); 225*8408SEric.Saxe@Sun.COM 226*8408SEric.Saxe@Sun.COM rotate_bit = seed % BT_NBIPUL; 227*8408SEric.Saxe@Sun.COM rotated_word = (w >> rotate_bit) | (w << (BT_NBIPUL - rotate_bit)); 228*8408SEric.Saxe@Sun.COM elt = (uint_t)(lowbit(rotated_word) - 1); 229*8408SEric.Saxe@Sun.COM if (elt != (uint_t)-1) 230*8408SEric.Saxe@Sun.COM elt = ((elt + rotate_bit) % BT_NBIPUL); 231*8408SEric.Saxe@Sun.COM 232*8408SEric.Saxe@Sun.COM return (elt); 233*8408SEric.Saxe@Sun.COM } 234*8408SEric.Saxe@Sun.COM 235*8408SEric.Saxe@Sun.COM /* 236*8408SEric.Saxe@Sun.COM * Select a bit that is set in the bitset in a non-victimizing fashion 237*8408SEric.Saxe@Sun.COM * (e.g. doesn't bias the low/high order bits/words). 238*8408SEric.Saxe@Sun.COM * Return -1 if no set bit was found 2393434Sesaxe */ 2403434Sesaxe uint_t 2413434Sesaxe bitset_find(bitset_t *b) 2423434Sesaxe { 243*8408SEric.Saxe@Sun.COM uint_t start, i; 244*8408SEric.Saxe@Sun.COM uint_t elt = (uint_t)-1; 245*8408SEric.Saxe@Sun.COM uint_t seed; 2463434Sesaxe 247*8408SEric.Saxe@Sun.COM seed = CPU_PSEUDO_RANDOM(); 248*8408SEric.Saxe@Sun.COM start = seed % b->bs_words; 249*8408SEric.Saxe@Sun.COM 250*8408SEric.Saxe@Sun.COM i = start; 251*8408SEric.Saxe@Sun.COM do { 252*8408SEric.Saxe@Sun.COM elt = bitset_find_in_word(b->bs_set[i], seed); 2533434Sesaxe if (elt != (uint_t)-1) { 2543434Sesaxe elt += i * BT_NBIPUL; 2553434Sesaxe break; 2563434Sesaxe } 257*8408SEric.Saxe@Sun.COM if (++i == b->bs_words) 258*8408SEric.Saxe@Sun.COM i = 0; 259*8408SEric.Saxe@Sun.COM } while (i != start); 260*8408SEric.Saxe@Sun.COM 2613434Sesaxe return (elt); 2623434Sesaxe } 263