xref: /onnv-gate/usr/src/uts/common/os/bitset.c (revision 8408:7b4e48a75d0c)
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