xref: /dragonfly/sys/vfs/devfs/devfs_helper.c (revision b608d1d3)
1 /*
2  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Alex Hornung <ahornung@gmail.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/types.h>
38 #include <machine/limits.h>
39 #include <sys/devfs.h>
40 
41 MALLOC_DECLARE(M_DEVFS);
42 
43 /*
44  * DEVFS clone bitmap functions
45  */
46 void
47 devfs_clone_bitmap_init(struct devfs_bitmap *bitmap)
48 {
49 	bitmap->chunks = DEVFS_BITMAP_INITIAL_SIZE;
50 	bitmap->bitmap = kmalloc(bitmap->chunks * sizeof(u_long),
51 				 M_DEVFS, M_WAITOK);
52 	memset(bitmap->bitmap, -1, bitmap->chunks * sizeof(u_long));
53 }
54 
55 
56 void
57 devfs_clone_bitmap_uninit(struct devfs_bitmap *bitmap)
58 {
59 	kfree(bitmap->bitmap, M_DEVFS);
60 }
61 
62 /*
63  * Extend the bitmap past (not just up to) 'newchunks' chunks.  While
64  * probably not necessary, we go a little further and give ourselves
65  * one extra chunk's worth of bitmap beyond what is required.
66  */
67 static void
68 devfs_clone_bitmap_extend(struct devfs_bitmap *bitmap, int newchunks)
69 {
70 	int oldchunks = bitmap->chunks;
71 
72 	bitmap->chunks = newchunks + 2;
73 	bitmap->bitmap = krealloc(bitmap->bitmap,
74 				  sizeof(u_long) * bitmap->chunks,
75 				  M_DEVFS, M_WAITOK);
76 	memset(bitmap->bitmap + oldchunks, -1,
77 	       sizeof(u_long) * (bitmap->chunks - oldchunks));
78 }
79 
80 /*
81  * This helper function determines the next available free unit in the
82  * bitmap, extending the bitmap if necessary.  It cannot fail.
83  */
84 static int
85 devfs_clone_bitmap_fff(struct devfs_bitmap *bitmap)
86 {
87 	u_long curbitmap;
88 	int bit, i;
89 	int chunks;
90 
91 	chunks = bitmap->chunks;
92 
93 	for (i = 0; i < chunks + 1; i++) {
94 		if (i == chunks)
95 			devfs_clone_bitmap_extend(bitmap, i);
96 		curbitmap = bitmap->bitmap[i];
97 
98 		if (curbitmap) {
99 			curbitmap &= (~curbitmap)+1;
100 			for (bit = 1; curbitmap != 1; bit++)
101 				curbitmap >>= 1;
102 
103 			return (bit - 1 + (i << 3) * sizeof(curbitmap));
104 		}
105 	}
106 
107 	/*
108 	 * NOT REACHED
109 	 */
110 	panic("devfs_clone_bitmap_fff");
111 	return -1;
112 }
113 
114 /*
115  * Caller wants to know if the specified unit has been allocated
116  * or not.  Return 0, indicating that it has not been allocated.
117  *
118  * Caller must hold a lock to prevent chk-to-set races.
119  *
120  * (the bitmap implements 0=allocated, 1=not-allocated but the
121  * return value is inverted).
122  */
123 int
124 devfs_clone_bitmap_chk(struct devfs_bitmap *bitmap, int unit)
125 {
126 	int chunk;
127 
128 	chunk = unit / (sizeof(u_long) * 8);
129 	unit -= chunk * (sizeof(u_long) * 8);
130 
131 	if (chunk >= bitmap->chunks)
132 		return 0;		/* device does not exist */
133 
134 	return !((bitmap->bitmap[chunk]) & (1UL << unit));
135 }
136 
137 /*
138  * Unconditionally mark the specified unit as allocated in the bitmap.
139  *
140  * Caller must hold a lock to prevent chk-to-set races.  If the unit had
141  * never been allocated in the past, a token lock might not be sufficient
142  * to avoid races (if this function must extend the bitmap it could block
143  * temporary in kmalloc()).
144  */
145 void
146 devfs_clone_bitmap_set(struct devfs_bitmap *bitmap, int unit)
147 {
148 	int chunk;
149 
150 	chunk = unit / (sizeof(u_long) * 8);
151 	unit -= chunk * (sizeof(u_long) * 8);
152 
153 	if (chunk >= bitmap->chunks)
154 		devfs_clone_bitmap_extend(bitmap, chunk);
155 	bitmap->bitmap[chunk] &= ~(1UL << unit);
156 }
157 
158 /*
159  * Unconditionally deallocate a unit number.  Caller must synchronize any
160  * destroy_dev()'s the device called before clearing the bitmap bit to
161  * avoid races against a new clone.
162  */
163 void
164 devfs_clone_bitmap_put(struct devfs_bitmap *bitmap, int unit)
165 {
166 	int chunk;
167 
168 	devfs_config();
169 
170 	chunk = unit / (sizeof(u_long) * 8);
171 	unit -= chunk * (sizeof(u_long) * 8);
172 
173 	if (chunk >= bitmap->chunks)
174 		return;
175 	bitmap->bitmap[chunk] |= (1UL << unit);
176 }
177 
178 /*
179  * Conditionally allocate a unit from the bitmap.  Returns -1 if no
180  * more units are available.
181  *
182  * Caller must hold a lock to avoid bitmap races.  Since *_fff() below
183  * pre-extends the bitmap as necessary, a token lock is sufficient.
184  */
185 int
186 devfs_clone_bitmap_get(struct devfs_bitmap *bitmap, int limit)
187 {
188 	int unit;
189 
190 	unit = devfs_clone_bitmap_fff(bitmap);
191 	if ((limit > 0) && (unit > limit))
192 		return -1;
193 	devfs_clone_bitmap_set(bitmap, unit);
194 
195 	return unit;
196 }
197