xref: /dflybsd-src/sys/vfs/devfs/devfs_helper.c (revision dae650601b7a1502cc3804a70c39222b46186f48)
121864bc5SMatthew Dillon /*
21991e949SMatthew Dillon  * Copyright (c) 2009-2019 The DragonFly Project.  All rights reserved.
321864bc5SMatthew Dillon  *
421864bc5SMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
521864bc5SMatthew Dillon  * by Alex Hornung <ahornung@gmail.com>
621864bc5SMatthew Dillon  *
721864bc5SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
821864bc5SMatthew Dillon  * modification, are permitted provided that the following conditions
921864bc5SMatthew Dillon  * are met:
1021864bc5SMatthew Dillon  *
1121864bc5SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
1221864bc5SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
1321864bc5SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
1421864bc5SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
1521864bc5SMatthew Dillon  *    the documentation and/or other materials provided with the
1621864bc5SMatthew Dillon  *    distribution.
1721864bc5SMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
1821864bc5SMatthew Dillon  *    contributors may be used to endorse or promote products derived
1921864bc5SMatthew Dillon  *    from this software without specific, prior written permission.
2021864bc5SMatthew Dillon  *
2121864bc5SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2221864bc5SMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2321864bc5SMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2421864bc5SMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
2521864bc5SMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2621864bc5SMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
2721864bc5SMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2821864bc5SMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2921864bc5SMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3021864bc5SMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3121864bc5SMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3221864bc5SMatthew Dillon  * SUCH DAMAGE.
3321864bc5SMatthew Dillon  */
3421864bc5SMatthew Dillon #include <sys/param.h>
3521864bc5SMatthew Dillon #include <sys/systm.h>
3621864bc5SMatthew Dillon #include <sys/kernel.h>
37*dae65060Szrj #include <sys/malloc.h>
3821864bc5SMatthew Dillon #include <machine/limits.h>
392c1e28ddSAlex Hornung #include <sys/devfs.h>
4021864bc5SMatthew Dillon 
4121864bc5SMatthew Dillon MALLOC_DECLARE(M_DEVFS);
4221864bc5SMatthew Dillon 
4321864bc5SMatthew Dillon /*
441991e949SMatthew Dillon  * Locallized lock to ensure the integrity of the bitmap/cloning
451991e949SMatthew Dillon  * code.  Callers using chk/set sequences must still check for races
461991e949SMatthew Dillon  * or have their own locks.
471991e949SMatthew Dillon  *
481991e949SMatthew Dillon  * We use a recursive lock only to allow *_get() to call *_set().
491991e949SMatthew Dillon  */
501991e949SMatthew Dillon static struct lock devfs_bitmap_lock =
511991e949SMatthew Dillon 	LOCK_INITIALIZER("dbmap", 0, LK_CANRECURSE);
521991e949SMatthew Dillon 
531991e949SMatthew Dillon /*
5421864bc5SMatthew Dillon  * DEVFS clone bitmap functions
5521864bc5SMatthew Dillon  */
5621864bc5SMatthew Dillon void
devfs_clone_bitmap_init(struct devfs_bitmap * bitmap)5721864bc5SMatthew Dillon devfs_clone_bitmap_init(struct devfs_bitmap *bitmap)
5821864bc5SMatthew Dillon {
5921864bc5SMatthew Dillon 	bitmap->chunks = DEVFS_BITMAP_INITIAL_SIZE;
60898c91eeSMatthew Dillon 	bitmap->bitmap = kmalloc(bitmap->chunks * sizeof(u_long),
61898c91eeSMatthew Dillon 				 M_DEVFS, M_WAITOK);
6271f27d2dSMatthew Dillon 	memset(bitmap->bitmap, -1, bitmap->chunks * sizeof(u_long));
6321864bc5SMatthew Dillon }
6421864bc5SMatthew Dillon 
6521864bc5SMatthew Dillon 
6621864bc5SMatthew Dillon void
devfs_clone_bitmap_uninit(struct devfs_bitmap * bitmap)6721864bc5SMatthew Dillon devfs_clone_bitmap_uninit(struct devfs_bitmap *bitmap)
6821864bc5SMatthew Dillon {
69ca8d7677SMatthew Dillon 	kfree(bitmap->bitmap, M_DEVFS);
7021864bc5SMatthew Dillon }
7121864bc5SMatthew Dillon 
72b0564672SMatthew Dillon /*
73b0564672SMatthew Dillon  * Extend the bitmap past (not just up to) 'newchunks' chunks.  While
74b0564672SMatthew Dillon  * probably not necessary, we go a little further and give ourselves
75b0564672SMatthew Dillon  * one extra chunk's worth of bitmap beyond what is required.
76b0564672SMatthew Dillon  */
77b0564672SMatthew Dillon static void
devfs_clone_bitmap_extend(struct devfs_bitmap * bitmap,int newchunks)78b0564672SMatthew Dillon devfs_clone_bitmap_extend(struct devfs_bitmap *bitmap, int newchunks)
7921864bc5SMatthew Dillon {
8021864bc5SMatthew Dillon 	int oldchunks = bitmap->chunks;
8121864bc5SMatthew Dillon 
82898c91eeSMatthew Dillon 	bitmap->chunks = newchunks + 2;
83898c91eeSMatthew Dillon 	bitmap->bitmap = krealloc(bitmap->bitmap,
84898c91eeSMatthew Dillon 				  sizeof(u_long) * bitmap->chunks,
85898c91eeSMatthew Dillon 				  M_DEVFS, M_WAITOK);
8671f27d2dSMatthew Dillon 	memset(bitmap->bitmap + oldchunks, -1,
87898c91eeSMatthew Dillon 	       sizeof(u_long) * (bitmap->chunks - oldchunks));
8821864bc5SMatthew Dillon }
8921864bc5SMatthew Dillon 
90b0564672SMatthew Dillon /*
91b0564672SMatthew Dillon  * This helper function determines the next available free unit in the
92b0564672SMatthew Dillon  * bitmap, extending the bitmap if necessary.  It cannot fail.
93b0564672SMatthew Dillon  */
94b0564672SMatthew Dillon static int
devfs_clone_bitmap_fff(struct devfs_bitmap * bitmap)9521864bc5SMatthew Dillon devfs_clone_bitmap_fff(struct devfs_bitmap *bitmap)
9621864bc5SMatthew Dillon {
97898c91eeSMatthew Dillon 	u_long curbitmap;
9821864bc5SMatthew Dillon 	int bit, i;
99898c91eeSMatthew Dillon 	int chunks;
100898c91eeSMatthew Dillon 
101898c91eeSMatthew Dillon 	chunks = bitmap->chunks;
10221864bc5SMatthew Dillon 
10321864bc5SMatthew Dillon 	for (i = 0; i < chunks + 1; i++) {
10421864bc5SMatthew Dillon 		if (i == chunks)
105b0564672SMatthew Dillon 			devfs_clone_bitmap_extend(bitmap, i);
10621864bc5SMatthew Dillon 		curbitmap = bitmap->bitmap[i];
10721864bc5SMatthew Dillon 
108898c91eeSMatthew Dillon 		if (curbitmap) {
10921864bc5SMatthew Dillon 			curbitmap &= (~curbitmap)+1;
11021864bc5SMatthew Dillon 			for (bit = 1; curbitmap != 1; bit++)
111898c91eeSMatthew Dillon 				curbitmap >>= 1;
11221864bc5SMatthew Dillon 
113898c91eeSMatthew Dillon 			return (bit - 1 + (i << 3) * sizeof(curbitmap));
11421864bc5SMatthew Dillon 		}
11521864bc5SMatthew Dillon 	}
11621864bc5SMatthew Dillon 
117898c91eeSMatthew Dillon 	/*
118898c91eeSMatthew Dillon 	 * NOT REACHED
119898c91eeSMatthew Dillon 	 */
120898c91eeSMatthew Dillon 	panic("devfs_clone_bitmap_fff");
12121864bc5SMatthew Dillon 	return -1;
12221864bc5SMatthew Dillon }
12321864bc5SMatthew Dillon 
12440986f5eSMatthew Dillon /*
12540986f5eSMatthew Dillon  * Caller wants to know if the specified unit has been allocated
12640986f5eSMatthew Dillon  * or not.  Return 0, indicating that it has not been allocated.
12740986f5eSMatthew Dillon  *
1281991e949SMatthew Dillon  * Caller must hold a lock to prevent chk-to-set races.  Devfs also
1291991e949SMatthew Dillon  * obtains a temporary lock, juse in case, to ensure structural
1301991e949SMatthew Dillon  * integrity.
131b0564672SMatthew Dillon  *
13240986f5eSMatthew Dillon  * (the bitmap implements 0=allocated, 1=not-allocated but the
13340986f5eSMatthew Dillon  * return value is inverted).
13440986f5eSMatthew Dillon  */
13521864bc5SMatthew Dillon int
devfs_clone_bitmap_chk(struct devfs_bitmap * bitmap,int unit)13621864bc5SMatthew Dillon devfs_clone_bitmap_chk(struct devfs_bitmap *bitmap, int unit)
13721864bc5SMatthew Dillon {
138898c91eeSMatthew Dillon 	int chunk;
1391991e949SMatthew Dillon 	int res;
140898c91eeSMatthew Dillon 
141898c91eeSMatthew Dillon 	chunk = unit / (sizeof(u_long) * 8);
142898c91eeSMatthew Dillon 	unit -= chunk * (sizeof(u_long) * 8);
14321864bc5SMatthew Dillon 
1441991e949SMatthew Dillon 	lockmgr(&devfs_bitmap_lock, LK_EXCLUSIVE);
1451991e949SMatthew Dillon 	if (chunk >= bitmap->chunks) {
1461991e949SMatthew Dillon 		lockmgr(&devfs_bitmap_lock, LK_RELEASE);
14740986f5eSMatthew Dillon 		return 0;		/* device does not exist */
1481991e949SMatthew Dillon 	}
1491991e949SMatthew Dillon 	res = !((bitmap->bitmap[chunk]) & (1UL << unit));
1501991e949SMatthew Dillon 	lockmgr(&devfs_bitmap_lock, LK_RELEASE);
15121864bc5SMatthew Dillon 
1521991e949SMatthew Dillon 	return res;
15321864bc5SMatthew Dillon }
15421864bc5SMatthew Dillon 
155b0564672SMatthew Dillon /*
156b0564672SMatthew Dillon  * Unconditionally mark the specified unit as allocated in the bitmap.
157b0564672SMatthew Dillon  *
1581991e949SMatthew Dillon  * Caller must hold a lock to prevent chk-to-set races, or otherwise
1591991e949SMatthew Dillon  * check for a return value < 0 from this routine.  If the unit had
160b0564672SMatthew Dillon  * never been allocated in the past, a token lock might not be sufficient
161b0564672SMatthew Dillon  * to avoid races (if this function must extend the bitmap it could block
162b0564672SMatthew Dillon  * temporary in kmalloc()).
1631991e949SMatthew Dillon  *
1641991e949SMatthew Dillon  * devfs acquires a temporary lock to ensure structural integrity.
1651991e949SMatthew Dillon  *
1661991e949SMatthew Dillon  * If the bit is already clear (indicating that the unit is already
1671991e949SMatthew Dillon  * allocated), return -1.  Otherwise return 0.
168b0564672SMatthew Dillon  */
1691991e949SMatthew Dillon int
devfs_clone_bitmap_set(struct devfs_bitmap * bitmap,int unit)17021864bc5SMatthew Dillon devfs_clone_bitmap_set(struct devfs_bitmap *bitmap, int unit)
17121864bc5SMatthew Dillon {
172898c91eeSMatthew Dillon 	int chunk;
1731991e949SMatthew Dillon 	int res;
17421864bc5SMatthew Dillon 
175898c91eeSMatthew Dillon 	chunk = unit / (sizeof(u_long) * 8);
176898c91eeSMatthew Dillon 	unit -= chunk * (sizeof(u_long) * 8);
177898c91eeSMatthew Dillon 
1781991e949SMatthew Dillon 	lockmgr(&devfs_bitmap_lock, LK_EXCLUSIVE);
179898c91eeSMatthew Dillon 	if (chunk >= bitmap->chunks)
180b0564672SMatthew Dillon 		devfs_clone_bitmap_extend(bitmap, chunk);
1811991e949SMatthew Dillon 	if (bitmap->bitmap[chunk] & (1UL << unit)) {
1824dfcb8deSSascha Wildner 		bitmap->bitmap[chunk] &= ~(1UL << unit);
1831991e949SMatthew Dillon 		res = 0;
1841991e949SMatthew Dillon 	} else {
1851991e949SMatthew Dillon 		res = -1;
1861991e949SMatthew Dillon 	}
1871991e949SMatthew Dillon 	lockmgr(&devfs_bitmap_lock, LK_RELEASE);
1881991e949SMatthew Dillon 
1891991e949SMatthew Dillon 	return res;
19021864bc5SMatthew Dillon }
19121864bc5SMatthew Dillon 
1922281ff2eSMatthew Dillon /*
193b0564672SMatthew Dillon  * Unconditionally deallocate a unit number.  Caller must synchronize any
194b0564672SMatthew Dillon  * destroy_dev()'s the device called before clearing the bitmap bit to
195b0564672SMatthew Dillon  * avoid races against a new clone.
1962281ff2eSMatthew Dillon  */
19721864bc5SMatthew Dillon void
devfs_clone_bitmap_put(struct devfs_bitmap * bitmap,int unit)1982281ff2eSMatthew Dillon devfs_clone_bitmap_put(struct devfs_bitmap *bitmap, int unit)
19921864bc5SMatthew Dillon {
200898c91eeSMatthew Dillon 	int chunk;
201898c91eeSMatthew Dillon 
2022281ff2eSMatthew Dillon 	devfs_config();
2032281ff2eSMatthew Dillon 
204898c91eeSMatthew Dillon 	chunk = unit / (sizeof(u_long) * 8);
205898c91eeSMatthew Dillon 	unit -= chunk * (sizeof(u_long) * 8);
20621864bc5SMatthew Dillon 
2071991e949SMatthew Dillon 	lockmgr(&devfs_bitmap_lock, LK_EXCLUSIVE);
2081991e949SMatthew Dillon 	if (chunk < bitmap->chunks)
2094dfcb8deSSascha Wildner 		bitmap->bitmap[chunk] |= (1UL << unit);
2101991e949SMatthew Dillon 	lockmgr(&devfs_bitmap_lock, LK_RELEASE);
21121864bc5SMatthew Dillon }
21221864bc5SMatthew Dillon 
2132281ff2eSMatthew Dillon /*
214b0564672SMatthew Dillon  * Conditionally allocate a unit from the bitmap.  Returns -1 if no
215b0564672SMatthew Dillon  * more units are available.
216b0564672SMatthew Dillon  *
217b0564672SMatthew Dillon  * Caller must hold a lock to avoid bitmap races.  Since *_fff() below
218b0564672SMatthew Dillon  * pre-extends the bitmap as necessary, a token lock is sufficient.
2192281ff2eSMatthew Dillon  */
22021864bc5SMatthew Dillon int
devfs_clone_bitmap_get(struct devfs_bitmap * bitmap,int limit)22121864bc5SMatthew Dillon devfs_clone_bitmap_get(struct devfs_bitmap *bitmap, int limit)
22221864bc5SMatthew Dillon {
22321864bc5SMatthew Dillon 	int unit;
22421864bc5SMatthew Dillon 
2251991e949SMatthew Dillon 	lockmgr(&devfs_bitmap_lock, LK_EXCLUSIVE);
226898c91eeSMatthew Dillon 	unit = devfs_clone_bitmap_fff(bitmap);
2271991e949SMatthew Dillon 	if (limit > 0 && unit > limit) {
2281991e949SMatthew Dillon 		unit = -1;
2291991e949SMatthew Dillon 	} else {
23021864bc5SMatthew Dillon 		devfs_clone_bitmap_set(bitmap, unit);
2311991e949SMatthew Dillon 	}
2321991e949SMatthew Dillon 	lockmgr(&devfs_bitmap_lock, LK_RELEASE);
23321864bc5SMatthew Dillon 
23421864bc5SMatthew Dillon 	return unit;
23521864bc5SMatthew Dillon }
236