kern_conf.c revision 125846
111126Sjulian/*-
2103722Sphk * Copyright (c) 1999-2002 Poul-Henning Kamp
311126Sjulian * All rights reserved.
411126Sjulian *
511126Sjulian * Redistribution and use in source and binary forms, with or without
611126Sjulian * modification, are permitted provided that the following conditions
711126Sjulian * are met:
811126Sjulian * 1. Redistributions of source code must retain the above copyright
911126Sjulian *    notice, this list of conditions and the following disclaimer.
1011126Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1111126Sjulian *    notice, this list of conditions and the following disclaimer in the
1211126Sjulian *    documentation and/or other materials provided with the distribution.
1311126Sjulian *
14103722Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15103722Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1611126Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17103722Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1811126Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1911126Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2011126Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2111126Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2211126Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2311126Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2411126Sjulian * SUCH DAMAGE.
2511126Sjulian */
2611126Sjulian
27116182Sobrien#include <sys/cdefs.h>
28116182Sobrien__FBSDID("$FreeBSD: head/sys/kern/kern_conf.c 125846 2004-02-15 10:35:33Z phk $");
29116182Sobrien
3011126Sjulian#include <sys/param.h>
3148936Sphk#include <sys/kernel.h>
3283366Sjulian#include <sys/systm.h>
33111179Sphk#include <sys/bio.h>
3490737Sgreen#include <sys/lock.h>
3590737Sgreen#include <sys/mutex.h>
3636735Sdfr#include <sys/sysctl.h>
3748936Sphk#include <sys/module.h>
3811126Sjulian#include <sys/malloc.h>
3912954Sjulian#include <sys/conf.h>
4048936Sphk#include <sys/vnode.h>
41120514Sphk#include <sys/queue.h>
4265374Sphk#include <sys/poll.h>
43126078Sphk#include <sys/ctype.h>
44147982Srwatson#include <machine/stdarg.h>
4549535Sphk
4611126Sjulianstatic MALLOC_DEFINE(M_DEVT, "dev_t", "dev_t storage");
47149144Sphk
48149144Sphk/* Built at compile time from sys/conf/majors */
49131996Sphkextern unsigned char reserved_majors[256];
5048936Sphk
51150342Sphk/*
52142242Sphk * This is the number of hash-buckets.  Experiements with 'real-life'
53147982Srwatson * udev_t's show that a prime halfway between two powers of two works
54147982Srwatson * best.
55147982Srwatson */
56126082Sphk#define DEVT_HASH 83
57170950Skib
58170950Skib/* The number of dev_t's we can create before malloc(9) kick in.  */
59170950Skib#define DEVT_STASH 50
60135600Sphk
61135600Sphkstatic struct cdev devt_stash[DEVT_STASH];
62126082Sphk
63151450Sjhbstatic LIST_HEAD(, cdev) dev_hash[DEVT_HASH];
64126082Sphk
65126082Sphkstatic LIST_HEAD(, cdev) dev_free;
66126082Sphk
67170950Skibstatic int free_devt;
68170950SkibSYSCTL_INT(_debug, OID_AUTO, free_devt, CTLFLAG_RW, &free_devt, 0, "");
69170950Skib
70170950Skibint
71170950Skibnullop(void)
72170950Skib{
73170950Skib
74170950Skib	return (0);
75170950Skib}
76170950Skib
77170950Skibint
78170950Skibeopnotsupp(void)
79170950Skib{
80170950Skib
81170950Skib	return (EOPNOTSUPP);
82170950Skib}
83170950Skib
84170950Skibstatic int
85170950Skibenxio(void)
86170950Skib{
87170950Skib	return (ENXIO);
88170950Skib}
89170950Skib
90170950Skibstatic int
91170950Skibenodev(void)
92135600Sphk{
93135600Sphk	return (ENODEV);
94126082Sphk}
95135704Sphk
96126082Sphk/* Define a dead_cdevsw for use when devices leave unexpectedly. */
97126082Sphk
98126082Sphk#define dead_open	(d_open_t *)enxio
99126082Sphk#define dead_close	(d_close_t *)enxio
100144385Sphk#define dead_read	(d_read_t *)enxio
101144385Sphk#define dead_write	(d_write_t *)enxio
102144385Sphk#define dead_ioctl	(d_ioctl_t *)enxio
103144385Sphk#define dead_poll	(d_poll_t *)enodev
104144385Sphk#define dead_mmap	(d_mmap_t *)enodev
105144385Sphk
106144385Sphkstatic void
107144385Sphkdead_strategy(struct bio *bp)
108144385Sphk{
109144385Sphk
110144384Sphk	biofinish(bp, NULL, ENXIO);
111126082Sphk}
112135704Sphk
113142232Sphk#define dead_dump	(dumper_t *)enxio
114126082Sphk#define dead_kqfilter	(d_kqfilter_t *)enxio
115126082Sphk
116126082Sphkstatic struct cdevsw dead_cdevsw = {
117126082Sphk	.d_open =	dead_open,
118142242Sphk	.d_close =	dead_close,
119126082Sphk	.d_read =	dead_read,
120142242Sphk	.d_write =	dead_write,
121135600Sphk	.d_ioctl =	dead_ioctl,
122136014Sphk	.d_poll =	dead_poll,
123136014Sphk	.d_mmap =	dead_mmap,
124126082Sphk	.d_strategy =	dead_strategy,
125126082Sphk	.d_name =	"dead",
126126082Sphk	.d_maj =	255,
127150342Sphk	.d_dump =	dead_dump,
128142242Sphk	.d_kqfilter =	dead_kqfilter
129142242Sphk};
130150342Sphk
131150342Sphk/* Default methods if driver does not specify method */
132150342Sphk
133154029Sbz#define null_open	(d_open_t *)nullop
134126082Sphk#define null_close	(d_close_t *)nullop
135136014Sphk#define no_read		(d_read_t *)enodev
136136014Sphk#define no_write	(d_write_t *)enodev
137136014Sphk#define no_ioctl	(d_ioctl_t *)enodev
138136014Sphk#define no_mmap		(d_mmap_t *)enodev
139150342Sphk
140126082Sphkstatic int
141136014Sphkno_kqfilter(dev_t dev __unused, struct knote *kn __unused)
142135704Sphk{
143135704Sphk
144135704Sphk	return (1);
145135704Sphk}
146126082Sphk
147135704Sphkstatic void
148135704Sphkno_strategy(struct bio *bp)
149135704Sphk{
150135704Sphk
151135704Sphk	biofinish(bp, NULL, ENODEV);
152135704Sphk}
153135704Sphk
154135704Sphkstatic int
155135704Sphkno_poll(dev_t dev __unused, int events, struct thread *td __unused)
156163529Skib{
157163529Skib	/*
158163529Skib	 * Return true for read/write.  If the user asked for something
159163529Skib	 * special, return POLLNVAL, so that clients have a way of
160163529Skib	 * determining reliably whether or not the extended
161163529Skib	 * functionality is present without hard-coding knowledge
162163529Skib	 * of specific filesystem implementations.
163163529Skib	 * Stay in sync with vop_nopoll().
164163529Skib	 */
165163529Skib	if (events & ~POLLSTANDARD)
166163529Skib		return (POLLNVAL);
167163529Skib
168163529Skib	return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM));
169163529Skib}
170163529Skib
171163529Skib#define no_dump		(dumper_t *)enodev
172163529Skib
173163529Skibstruct cdevsw *
174135704Sphkdevsw(dev_t dev)
175135704Sphk{
176135704Sphk	if (dev->si_devsw)
177135704Sphk		return (dev->si_devsw);
178135704Sphk	return (&dead_cdevsw);
179135704Sphk}
180135704Sphk
181135704Sphk/*
182135704Sphk * dev_t and u_dev_t primitives
183135704Sphk */
184120514Sphk
185120514Sphkint
186120514Sphkmajor(dev_t x)
18785603Sphk{
188120514Sphk	if (x == NODEV)
189120514Sphk		return NOUDEV;
190120514Sphk	return((x->si_udev >> 8) & 0xff);
191120514Sphk}
192120514Sphk
193120514Sphkint
194120514Sphkminor(dev_t x)
195120514Sphk{
196120514Sphk	if (x == NODEV)
197120514Sphk		return NOUDEV;
198111179Sphk	return(x->si_udev & 0xffff00ff);
199111179Sphk}
200111179Sphk
201111179Sphkint
202111179Sphkdev2unit(dev_t x)
203111179Sphk{
204120514Sphk	int i;
205120514Sphk
206120514Sphk	if (x == NODEV)
207120514Sphk		return NOUDEV;
208120514Sphk	i = minor(x);
209120514Sphk	return ((i & 0xff) | (i >> 8));
210120514Sphk}
211120514Sphk
212111179Sphkint
213111179Sphkunit2minor(int unit)
214111179Sphk{
215111179Sphk
216111179Sphk	KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit));
217120514Sphk	return ((unit & 0xff) | ((unit << 8) & ~0xffff));
218120514Sphk}
219111179Sphk
220111179Sphkstatic dev_t
221111179Sphkallocdev(void)
222111179Sphk{
223111179Sphk	static int stashed;
224111179Sphk	struct cdev *si;
225111179Sphk
226111179Sphk	if (LIST_FIRST(&dev_free)) {
227111220Sphk		si = LIST_FIRST(&dev_free);
228111179Sphk		LIST_REMOVE(si, si_hash);
229111179Sphk	} else if (stashed >= DEVT_STASH) {
230111179Sphk		MALLOC(si, struct cdev *, sizeof(*si), M_DEVT,
231126080Sphk		    M_USE_RESERVE | M_ZERO | M_WAITOK);
232126080Sphk	} else {
233111815Sphk		si = devt_stash + stashed++;
234111815Sphk		bzero(si, sizeof *si);
235111815Sphk		si->si_flags |= SI_STASHED;
236111815Sphk	}
237111815Sphk	si->__si_namebuf[0] = '\0';
238111815Sphk	si->si_name = si->__si_namebuf;
239111815Sphk	LIST_INIT(&si->si_children);
240111815Sphk	TAILQ_INIT(&si->si_snapshots);
241111815Sphk	return (si);
242111815Sphk}
243111815Sphk
244111179Sphkdev_t
245111179Sphkmakedev(int x, int y)
246120514Sphk{
247111179Sphk	struct cdev *si;
248120514Sphk	udev_t	udev;
249120514Sphk	int hash;
250120514Sphk
251120514Sphk	if (x == umajor(NOUDEV) && y == uminor(NOUDEV))
252120514Sphk		panic("makedev of NOUDEV");
253120514Sphk	udev = (x << 8) | y;
254133741Sjmg	hash = udev % DEVT_HASH;
255120514Sphk	LIST_FOREACH(si, &dev_hash[hash], si_hash) {
256120514Sphk		if (si->si_udev == udev)
257120514Sphk			return (si);
258120514Sphk	}
259120514Sphk	si = allocdev();
260120514Sphk	si->si_udev = udev;
261120514Sphk	LIST_INSERT_HEAD(&dev_hash[hash], si, si_hash);
262120514Sphk        return (si);
263120514Sphk}
264130585Sphk
265120514Sphkvoid
266120514Sphkfreedev(dev_t dev)
267120514Sphk{
268120514Sphk
269120514Sphk	if (!free_devt)
270120514Sphk		return;
271120514Sphk	if (SLIST_FIRST(&dev->si_hlist))
272120514Sphk		return;
273120514Sphk	if (dev->si_devsw || dev->si_drv1 || dev->si_drv2)
274120514Sphk		return;
275120514Sphk	LIST_REMOVE(dev, si_hash);
276120514Sphk	if (dev->si_flags & SI_STASHED) {
277120514Sphk		bzero(dev, sizeof(*dev));
278120514Sphk		dev->si_flags |= SI_STASHED;
279120514Sphk		LIST_INSERT_HEAD(&dev_free, dev, si_hash);
280120514Sphk	} else {
281120514Sphk		FREE(dev, M_DEVT);
282149177Sphk	}
283149177Sphk}
284149177Sphk
285149177Sphkudev_t
286149177Sphkdev2udev(dev_t x)
287149177Sphk{
288149177Sphk	if (x == NODEV)
289149177Sphk		return NOUDEV;
290149177Sphk	return (x->si_udev);
291149177Sphk}
292149177Sphk
293149177Sphkdev_t
294149177Sphkudev2dev(udev_t x, int b)
295170152Skib{
296149177Sphk
297149177Sphk	if (x == NOUDEV)
298149177Sphk		return (NODEV);
299149177Sphk	switch (b) {
300149177Sphk		case 0:
301170152Skib			return makedev(umajor(x), uminor(x));
302149177Sphk		case 1:
303149177Sphk			return (NODEV);
304149177Sphk		default:
305149177Sphk			Debugger("udev2dev(...,X)");
306149177Sphk			return NODEV;
307149177Sphk	}
308149177Sphk}
309149177Sphk
310149177Sphkint
311149177Sphkuminor(udev_t dev)
312149177Sphk{
313149177Sphk	return(dev & 0xffff00ff);
314149177Sphk}
315149177Sphk
316149177Sphkint
317149177Sphkumajor(udev_t dev)
318149177Sphk{
319149177Sphk	return((dev & 0xff00) >> 8);
320149177Sphk}
321149177Sphk
322149177Sphkudev_t
323149177Sphkmakeudev(int x, int y)
324149177Sphk{
325149177Sphk        return ((x << 8) | y);
326149177Sphk}
327149177Sphk
328149177Sphkstatic void
329149177Sphkprep_cdevsw(struct cdevsw *devsw)
330149177Sphk{
331149177Sphk	int i;
332149177Sphk
333149177Sphk	if (devsw->d_open == NULL)	devsw->d_open = null_open;
334149177Sphk	if (devsw->d_close == NULL)	devsw->d_close = null_close;
335149177Sphk	if (devsw->d_read == NULL)	devsw->d_read = no_read;
336149177Sphk	if (devsw->d_write == NULL)	devsw->d_write = no_write;
337149177Sphk	if (devsw->d_ioctl == NULL)	devsw->d_ioctl = no_ioctl;
338149177Sphk	if (devsw->d_poll == NULL)	devsw->d_poll = no_poll;
339149177Sphk	if (devsw->d_mmap == NULL)	devsw->d_mmap = no_mmap;
340149177Sphk	if (devsw->d_strategy == NULL)	devsw->d_strategy = no_strategy;
341149177Sphk	if (devsw->d_dump == NULL)	devsw->d_dump = no_dump;
342149177Sphk	if (devsw->d_kqfilter == NULL)	devsw->d_kqfilter = no_kqfilter;
343149177Sphk
344149177Sphk	if (devsw->d_maj == MAJOR_AUTO) {
345149177Sphk		for (i = NUMCDEVSW - 1; i > 0; i--)
346149177Sphk			if (reserved_majors[i] != i)
347149177Sphk				break;
348149177Sphk		KASSERT(i > 0, ("Out of major numbers (%s)", devsw->d_name));
349149177Sphk		devsw->d_maj = i;
350149177Sphk		reserved_majors[i] = i;
351149177Sphk	} else {
352149177Sphk		if (devsw->d_maj == 256)	/* XXX: tty_cons.c is magic */
353149177Sphk			devsw->d_maj = 0;
354149177Sphk		KASSERT(devsw->d_maj >= 0 && devsw->d_maj < 256,
355149177Sphk		    ("Invalid major (%d) in make_dev", devsw->d_maj));
356149177Sphk		if (reserved_majors[devsw->d_maj] != devsw->d_maj) {
357149177Sphk			printf("WARNING: driver \"%s\" used %s %d\n",
358149177Sphk			    devsw->d_name, "unreserved major device number",
359149177Sphk			    devsw->d_maj);
360149177Sphk			reserved_majors[devsw->d_maj] = devsw->d_maj;
361149177Sphk		}
362149177Sphk	}
363149177Sphk}
364149177Sphk
365149177Sphkdev_t
366149177Sphkmake_dev(struct cdevsw *devsw, int minor, uid_t uid, gid_t gid, int perms, const char *fmt, ...)
367149177Sphk{
368149177Sphk	dev_t	dev;
369149177Sphk	va_list ap;
370149177Sphk	int i;
371149177Sphk
372149177Sphk	KASSERT((minor & ~0xffff00ff) == 0,
373149177Sphk	    ("Invalid minor (0x%x) in make_dev", minor));
374149177Sphk
375149177Sphk	prep_cdevsw(devsw);
376149177Sphk	dev = makedev(devsw->d_maj, minor);
377149177Sphk	if (dev->si_flags & SI_CHEAPCLONE &&
378149177Sphk	    dev->si_flags & SI_NAMED &&
379149177Sphk	    dev->si_devsw == devsw) {
380149177Sphk		/*
381149177Sphk		 * This is allowed as it removes races and generally
382149177Sphk		 * simplifies cloning devices.
383149177Sphk		 */
384149177Sphk		return (dev);
385149177Sphk	}
386149177Sphk	if (dev->si_flags & SI_NAMED) {
387149177Sphk		printf( "WARNING: Driver mistake: repeat make_dev(\"%s\")\n",
388149177Sphk		    dev->si_name);
389149177Sphk		panic("don't do that");
390149177Sphk	}
391149177Sphk	va_start(ap, fmt);
392149177Sphk	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
393149177Sphk	if (i > (sizeof dev->__si_namebuf - 1)) {
394149177Sphk		printf("WARNING: Device name truncated! (%s)",
395149177Sphk		    dev->__si_namebuf);
396149177Sphk	}
397149177Sphk	va_end(ap);
398149177Sphk	dev->si_devsw = devsw;
399149177Sphk	dev->si_uid = uid;
400149177Sphk	dev->si_gid = gid;
40147640Sphk	dev->si_mode = perms;
402130936Sle	dev->si_flags |= SI_NAMED;
40347028Sphk
40447028Sphk	devfs_create(dev);
40547028Sphk	return (dev);
406130585Sphk}
40747028Sphk
408130640Sphkint
409130640Sphkdev_named(dev_t pdev, const char *name)
410143631Sphk{
41147028Sphk	dev_t cdev;
41247028Sphk
41349826Sphk	if (strcmp(devtoname(pdev), name) == 0)
414130585Sphk		return (1);
41549826Sphk	LIST_FOREACH(cdev, &pdev->si_children, si_siblings)
41649826Sphk		if (strcmp(devtoname(cdev), name) == 0)
417130640Sphk			return (1);
418130640Sphk	return (0);
419140964Sphk}
42049826Sphk
42149826Sphkvoid
422143282Sphkdev_depends(dev_t pdev, dev_t cdev)
423143282Sphk{
424140963Sphk
425140963Sphk	cdev->si_parent = pdev;
426140969Sphk	cdev->si_flags |= SI_CHILD;
427143282Sphk	LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings);
428140963Sphk}
429140963Sphk
430140963Sphkdev_t
43166067Sphkmake_dev_alias(dev_t pdev, const char *fmt, ...)
43266067Sphk{
43366067Sphk	dev_t	dev;
43474522Sphk	va_list ap;
43566067Sphk	int i;
43666067Sphk
43766067Sphk	dev = allocdev();
438130585Sphk	dev->si_flags |= SI_ALIAS;
439144281Sphk	dev->si_flags |= SI_NAMED;
44047028Sphk	dev_depends(pdev, dev);
441140733Sphk	va_start(ap, fmt);
442130640Sphk	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
44348936Sphk	if (i > (sizeof dev->__si_namebuf - 1)) {
444140733Sphk		printf("WARNING: Device name truncated! (%s)",
445144292Sphk		    dev->__si_namebuf);
446144281Sphk	}
447143631Sphk	va_end(ap);
448170950Skib
449140733Sphk	devfs_create(dev);
450140733Sphk	return (dev);
45148936Sphk}
452143631Sphk
453150342Sphkvoid
454144281Sphkdestroy_dev(dev_t dev)
455125850Sbde{
45647028Sphk
45747028Sphk	if (!(dev->si_flags & SI_NAMED)) {
45847028Sphk		printf( "WARNING: Driver mistake: destroy_dev on %d/%d\n",
459130640Sphk		    major(dev), minor(dev));
46047028Sphk		panic("don't do that");
461140969Sphk	}
46247028Sphk
46347028Sphk	devfs_destroy(dev);
46447028Sphk	if (dev->si_flags & SI_CHILD) {
465130640Sphk		LIST_REMOVE(dev, si_siblings);
46647028Sphk		dev->si_flags &= ~SI_CHILD;
467140969Sphk	}
46847028Sphk	while (!LIST_EMPTY(&dev->si_children))
46947028Sphk		destroy_dev(LIST_FIRST(&dev->si_children));
470125846Sphk	dev->si_drv1 = 0;
471144292Sphk	dev->si_drv2 = 0;
47249535Sphk	dev->si_devsw = 0;
473149324Sphk	bzero(&dev->__si_u, sizeof(dev->__si_u));
47449535Sphk	dev->si_flags &= ~SI_NAMED;
475149324Sphk	dev->si_flags &= ~SI_ALIAS;
476149324Sphk	freedev(dev);
477149324Sphk}
478149324Sphk
479149324Sphkconst char *
480149324Sphkdevtoname(dev_t dev)
481126156Sphk{
482126082Sphk	char *p;
483126082Sphk	int mynor;
484126082Sphk
485126077Sphk	if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
486126077Sphk		p = dev->si_name;
487149177Sphk		if (devsw(dev))
488126077Sphk			sprintf(p, "#%s/", devsw(dev)->d_name);
489149177Sphk		else
490149177Sphk			sprintf(p, "#%d/", major(dev));
491149177Sphk		p += strlen(p);
492149177Sphk		mynor = minor(dev);
493135600Sphk		if (mynor < 0 || mynor > 255)
494126082Sphk			sprintf(p, "%#x", (u_int)mynor);
495143746Sphk		else
496126082Sphk			sprintf(p, "%d", mynor);
497126082Sphk	}
498154266Salfred	return (dev->si_name);
499154266Salfred}
500126082Sphk
501126082Sphkint
502126082Sphkdev_stdclone(char *name, char **namep, const char *stem, int *unit)
503126082Sphk{
504126082Sphk	int u, i;
505126082Sphk
506126082Sphk	i = strlen(stem);
507126082Sphk	if (bcmp(stem, name, i) != 0)
508126082Sphk		return (0);
509126082Sphk	if (!isdigit(name[i]))
510126082Sphk		return (0);
511126082Sphk	u = 0;
512126078Sphk	if (name[i] == '0' && isdigit(name[i+1]))
513129943Sphk		return (0);
514126078Sphk	while (isdigit(name[i])) {
515126078Sphk		u *= 10;
516126078Sphk		u += name[i++] - '0';
517126078Sphk	}
518126078Sphk	if (u > 0xffffff)
519126078Sphk		return (0);
520149177Sphk	*unit = u;
521149177Sphk	if (namep)
522149177Sphk		*namep = &name[i];
523149177Sphk	if (name[i])
524149177Sphk		return (2);
525149177Sphk	return (1);
526149177Sphk}
527149177Sphk
528149177Sphk/*
529149177Sphk * Helper sysctl for devname(3).  We're given a {u}dev_t and return
530149177Sphk * the name, if any, registered by the device driver.
531149177Sphk */
532149177Sphkstatic int
533149177Sphksysctl_devname(SYSCTL_HANDLER_ARGS)
534149177Sphk{
535149177Sphk	int error;
536149177Sphk	udev_t ud;
537149177Sphk	dev_t dev;
538149177Sphk
539149177Sphk	error = SYSCTL_IN(req, &ud, sizeof (ud));
540149177Sphk	if (error)
541149177Sphk		return (error);
542149177Sphk	if (ud == NOUDEV)
543149177Sphk		return(EINVAL);
544149177Sphk	dev = makedev(umajor(ud), uminor(ud));
545149177Sphk	if (dev->si_name[0] == '\0')
546149177Sphk		error = ENOENT;
547149177Sphk	else
548120514Sphk		error = SYSCTL_OUT(req, dev->si_name, strlen(dev->si_name) + 1);
549126082Sphk	freedev(dev);
550126082Sphk	return (error);
551126082Sphk}
552126082Sphk
553126082SphkSYSCTL_PROC(_kern, OID_AUTO, devname, CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_ANYBODY,
554135600Sphk	NULL, 0, sysctl_devname, "", "devname(3) handler");
555125846Sphk