kern_conf.c revision 201145
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 201145 2009-12-28 22:56:30Z antoine $");
29116182Sobrien
3011126Sjulian#include <sys/param.h>
3148936Sphk#include <sys/kernel.h>
3283366Sjulian#include <sys/systm.h>
33178991Skib#include <sys/bus.h>
34111179Sphk#include <sys/bio.h>
3590737Sgreen#include <sys/lock.h>
3690737Sgreen#include <sys/mutex.h>
3736735Sdfr#include <sys/module.h>
3848936Sphk#include <sys/malloc.h>
3911126Sjulian#include <sys/conf.h>
4012954Sjulian#include <sys/vnode.h>
4148936Sphk#include <sys/queue.h>
42120514Sphk#include <sys/poll.h>
43171181Skib#include <sys/sx.h>
4465374Sphk#include <sys/ctype.h>
45147982Srwatson#include <sys/ucred.h>
46171181Skib#include <sys/taskqueue.h>
4749535Sphk#include <machine/stdarg.h>
4811126Sjulian
49149144Sphk#include <fs/devfs/devfs_int.h>
50193275Sjhb#include <vm/vm.h>
51149144Sphk
52131996Sphkstatic MALLOC_DEFINE(M_DEVT, "cdev", "cdev storage");
5348936Sphk
54150342Sphkstruct mtx devmtx;
55142242Sphkstatic void destroy_devl(struct cdev *dev);
56171188Skibstatic int destroy_dev_sched_cbl(struct cdev *dev,
57171188Skib    void (*cb)(void *), void *arg);
58171181Skibstatic struct cdev *make_dev_credv(int flags,
59183382Sed    struct cdevsw *devsw, int unit,
60171181Skib    struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt,
61171181Skib    va_list ap);
62171181Skib
63170950Skibstatic struct cdev_priv_list cdevp_free_list =
64170950Skib    TAILQ_HEAD_INITIALIZER(cdevp_free_list);
65177301Skibstatic SLIST_HEAD(free_cdevsw, cdevsw) cdevsw_gt_post_list =
66201145Santoine    SLIST_HEAD_INITIALIZER(cdevsw_gt_post_list);
67170950Skib
68135600Sphkvoid
69135600Sphkdev_lock(void)
70126082Sphk{
71151450Sjhb
72126082Sphk	mtx_lock(&devmtx);
73126082Sphk}
74126082Sphk
75177301Skib/*
76177301Skib * Free all the memory collected while the cdev mutex was
77177301Skib * locked. Since devmtx is after the system map mutex, free() cannot
78177301Skib * be called immediately and is postponed until cdev mutex can be
79177301Skib * dropped.
80177301Skib */
81170950Skibstatic void
82170950Skibdev_unlock_and_free(void)
83170950Skib{
84177301Skib	struct cdev_priv_list cdp_free;
85177301Skib	struct free_cdevsw csw_free;
86170950Skib	struct cdev_priv *cdp;
87177301Skib	struct cdevsw *csw;
88170950Skib
89170950Skib	mtx_assert(&devmtx, MA_OWNED);
90177301Skib
91177301Skib	/*
92177301Skib	 * Make the local copy of the list heads while the dev_mtx is
93177301Skib	 * held. Free it later.
94177301Skib	 */
95177301Skib	TAILQ_INIT(&cdp_free);
96177301Skib	TAILQ_CONCAT(&cdp_free, &cdevp_free_list, cdp_list);
97177301Skib	csw_free = cdevsw_gt_post_list;
98177301Skib	SLIST_INIT(&cdevsw_gt_post_list);
99177301Skib
100177301Skib	mtx_unlock(&devmtx);
101177301Skib
102177301Skib	while ((cdp = TAILQ_FIRST(&cdp_free)) != NULL) {
103177301Skib		TAILQ_REMOVE(&cdp_free, cdp, cdp_list);
104170950Skib		devfs_free(&cdp->cdp_c);
105170950Skib	}
106177301Skib	while ((csw = SLIST_FIRST(&csw_free)) != NULL) {
107177301Skib		SLIST_REMOVE_HEAD(&csw_free, d_postfree_list);
108177301Skib		free(csw, M_DEVT);
109177301Skib	}
110170950Skib}
111170950Skib
112170950Skibstatic void
113170950Skibdev_free_devlocked(struct cdev *cdev)
114170950Skib{
115170950Skib	struct cdev_priv *cdp;
116170950Skib
117170950Skib	mtx_assert(&devmtx, MA_OWNED);
118179828Skib	cdp = cdev2priv(cdev);
119170950Skib	TAILQ_INSERT_HEAD(&cdevp_free_list, cdp, cdp_list);
120170950Skib}
121170950Skib
122177301Skibstatic void
123177301Skibcdevsw_free_devlocked(struct cdevsw *csw)
124177301Skib{
125177301Skib
126177301Skib	mtx_assert(&devmtx, MA_OWNED);
127177301Skib	SLIST_INSERT_HEAD(&cdevsw_gt_post_list, csw, d_postfree_list);
128177301Skib}
129177301Skib
130135600Sphkvoid
131135600Sphkdev_unlock(void)
132126082Sphk{
133135704Sphk
134126082Sphk	mtx_unlock(&devmtx);
135126082Sphk}
136126082Sphk
137126082Sphkvoid
138144385Sphkdev_ref(struct cdev *dev)
139144385Sphk{
140144385Sphk
141144385Sphk	mtx_assert(&devmtx, MA_NOTOWNED);
142144385Sphk	mtx_lock(&devmtx);
143144385Sphk	dev->si_refcount++;
144144385Sphk	mtx_unlock(&devmtx);
145144385Sphk}
146144385Sphk
147144385Sphkvoid
148144384Sphkdev_refl(struct cdev *dev)
149126082Sphk{
150135704Sphk
151142232Sphk	mtx_assert(&devmtx, MA_OWNED);
152126082Sphk	dev->si_refcount++;
153126082Sphk}
154126082Sphk
155126082Sphkvoid
156142242Sphkdev_rel(struct cdev *dev)
157126082Sphk{
158142242Sphk	int flag = 0;
159135600Sphk
160136014Sphk	mtx_assert(&devmtx, MA_NOTOWNED);
161136014Sphk	dev_lock();
162126082Sphk	dev->si_refcount--;
163126082Sphk	KASSERT(dev->si_refcount >= 0,
164126082Sphk	    ("dev_rel(%s) gave negative count", devtoname(dev)));
165150342Sphk#if 0
166142242Sphk	if (dev->si_usecount == 0 &&
167142242Sphk	    (dev->si_flags & SI_CHEAPCLONE) && (dev->si_flags & SI_NAMED))
168150342Sphk		;
169150342Sphk	else
170150342Sphk#endif
171154029Sbz	if (dev->si_devsw == NULL && dev->si_refcount == 0) {
172126082Sphk		LIST_REMOVE(dev, si_list);
173136014Sphk		flag = 1;
174136014Sphk	}
175136014Sphk	dev_unlock();
176136014Sphk	if (flag)
177150342Sphk		devfs_free(dev);
178126082Sphk}
179136014Sphk
180135704Sphkstruct cdevsw *
181135704Sphkdev_refthread(struct cdev *dev)
182135704Sphk{
183135704Sphk	struct cdevsw *csw;
184171181Skib	struct cdev_priv *cdp;
185126082Sphk
186135704Sphk	mtx_assert(&devmtx, MA_NOTOWNED);
187135704Sphk	dev_lock();
188135704Sphk	csw = dev->si_devsw;
189171181Skib	if (csw != NULL) {
190179828Skib		cdp = cdev2priv(dev);
191171181Skib		if ((cdp->cdp_flags & CDP_SCHED_DTR) == 0)
192171181Skib			dev->si_threadcount++;
193171181Skib		else
194171181Skib			csw = NULL;
195171181Skib	}
196135704Sphk	dev_unlock();
197135704Sphk	return (csw);
198135704Sphk}
199135704Sphk
200163529Skibstruct cdevsw *
201163529Skibdevvn_refthread(struct vnode *vp, struct cdev **devp)
202163529Skib{
203163529Skib	struct cdevsw *csw;
204171181Skib	struct cdev_priv *cdp;
205163529Skib
206163529Skib	mtx_assert(&devmtx, MA_NOTOWNED);
207163529Skib	csw = NULL;
208163529Skib	dev_lock();
209163529Skib	*devp = vp->v_rdev;
210163529Skib	if (*devp != NULL) {
211179828Skib		cdp = cdev2priv(*devp);
212171181Skib		if ((cdp->cdp_flags & CDP_SCHED_DTR) == 0) {
213171181Skib			csw = (*devp)->si_devsw;
214171181Skib			if (csw != NULL)
215171181Skib				(*devp)->si_threadcount++;
216171181Skib		}
217163529Skib	}
218163529Skib	dev_unlock();
219163529Skib	return (csw);
220163529Skib}
221163529Skib
222135704Sphkvoid
223135704Sphkdev_relthread(struct cdev *dev)
224135704Sphk{
225135704Sphk
226135704Sphk	mtx_assert(&devmtx, MA_NOTOWNED);
227135704Sphk	dev_lock();
228179248Skib	KASSERT(dev->si_threadcount > 0,
229179248Skib	    ("%s threadcount is wrong", dev->si_name));
230135704Sphk	dev->si_threadcount--;
231135704Sphk	dev_unlock();
232135704Sphk}
233135704Sphk
234120514Sphkint
235120514Sphknullop(void)
236120514Sphk{
23785603Sphk
238120514Sphk	return (0);
239120514Sphk}
240120514Sphk
241120514Sphkint
242120514Sphkeopnotsupp(void)
243120514Sphk{
244120514Sphk
245120514Sphk	return (EOPNOTSUPP);
246120514Sphk}
247120514Sphk
248111179Sphkstatic int
249111179Sphkenxio(void)
250111179Sphk{
251111179Sphk	return (ENXIO);
252111179Sphk}
253111179Sphk
254120514Sphkstatic int
255120514Sphkenodev(void)
256120514Sphk{
257120514Sphk	return (ENODEV);
258120514Sphk}
259120514Sphk
260120514Sphk/* Define a dead_cdevsw for use when devices leave unexpectedly. */
261120514Sphk
262111179Sphk#define dead_open	(d_open_t *)enxio
263111179Sphk#define dead_close	(d_close_t *)enxio
264111179Sphk#define dead_read	(d_read_t *)enxio
265111179Sphk#define dead_write	(d_write_t *)enxio
266111179Sphk#define dead_ioctl	(d_ioctl_t *)enxio
267120514Sphk#define dead_poll	(d_poll_t *)enodev
268120514Sphk#define dead_mmap	(d_mmap_t *)enodev
269111179Sphk
270111179Sphkstatic void
271111179Sphkdead_strategy(struct bio *bp)
272111179Sphk{
273111179Sphk
274111179Sphk	biofinish(bp, NULL, ENXIO);
275111179Sphk}
276111179Sphk
277111220Sphk#define dead_dump	(dumper_t *)enxio
278111179Sphk#define dead_kqfilter	(d_kqfilter_t *)enxio
279193275Sjhb#define dead_mmap_single (d_mmap_single_t *)enodev
280111179Sphk
281111179Sphkstatic struct cdevsw dead_cdevsw = {
282126080Sphk	.d_version =	D_VERSION,
283126080Sphk	.d_flags =	D_NEEDGIANT, /* XXX: does dead_strategy need this ? */
284111815Sphk	.d_open =	dead_open,
285111815Sphk	.d_close =	dead_close,
286111815Sphk	.d_read =	dead_read,
287111815Sphk	.d_write =	dead_write,
288111815Sphk	.d_ioctl =	dead_ioctl,
289111815Sphk	.d_poll =	dead_poll,
290111815Sphk	.d_mmap =	dead_mmap,
291111815Sphk	.d_strategy =	dead_strategy,
292111815Sphk	.d_name =	"dead",
293111815Sphk	.d_dump =	dead_dump,
294193275Sjhb	.d_kqfilter =	dead_kqfilter,
295193275Sjhb	.d_mmap_single = dead_mmap_single
296111179Sphk};
297111179Sphk
298120514Sphk/* Default methods if driver does not specify method */
299111179Sphk
300120514Sphk#define null_open	(d_open_t *)nullop
301120514Sphk#define null_close	(d_close_t *)nullop
302120514Sphk#define no_read		(d_read_t *)enodev
303120514Sphk#define no_write	(d_write_t *)enodev
304120514Sphk#define no_ioctl	(d_ioctl_t *)enodev
305196615Sjhb#define no_mmap		(d_mmap2_t *)enodev
306133741Sjmg#define no_kqfilter	(d_kqfilter_t *)enodev
307193275Sjhb#define no_mmap_single	(d_mmap_single_t *)enodev
308120514Sphk
309120514Sphkstatic void
310120514Sphkno_strategy(struct bio *bp)
311120514Sphk{
312120514Sphk
313120514Sphk	biofinish(bp, NULL, ENODEV);
314120514Sphk}
315120514Sphk
316120514Sphkstatic int
317130585Sphkno_poll(struct cdev *dev __unused, int events, struct thread *td __unused)
318120514Sphk{
319120514Sphk
320189450Skib	return (poll_no_poll(events));
321120514Sphk}
322120514Sphk
323120514Sphk#define no_dump		(dumper_t *)enodev
324120514Sphk
325149177Sphkstatic int
326149177Sphkgiant_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
327149177Sphk{
328177301Skib	struct cdevsw *dsw;
329149177Sphk	int retval;
330149177Sphk
331177301Skib	dsw = dev_refthread(dev);
332177301Skib	if (dsw == NULL)
333177301Skib		return (ENXIO);
334149177Sphk	mtx_lock(&Giant);
335177301Skib	retval = dsw->d_gianttrick->d_open(dev, oflags, devtype, td);
336149177Sphk	mtx_unlock(&Giant);
337177301Skib	dev_relthread(dev);
338149177Sphk	return (retval);
339149177Sphk}
340149177Sphk
341149177Sphkstatic int
342170152Skibgiant_fdopen(struct cdev *dev, int oflags, struct thread *td, struct file *fp)
343149177Sphk{
344177301Skib	struct cdevsw *dsw;
345149177Sphk	int retval;
346149177Sphk
347177301Skib	dsw = dev_refthread(dev);
348177301Skib	if (dsw == NULL)
349177301Skib		return (ENXIO);
350149177Sphk	mtx_lock(&Giant);
351177301Skib	retval = dsw->d_gianttrick->d_fdopen(dev, oflags, td, fp);
352149177Sphk	mtx_unlock(&Giant);
353177301Skib	dev_relthread(dev);
354149177Sphk	return (retval);
355149177Sphk}
356149177Sphk
357149177Sphkstatic int
358149177Sphkgiant_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
359149177Sphk{
360177301Skib	struct cdevsw *dsw;
361149177Sphk	int retval;
362149177Sphk
363177301Skib	dsw = dev_refthread(dev);
364177301Skib	if (dsw == NULL)
365177301Skib		return (ENXIO);
366149177Sphk	mtx_lock(&Giant);
367177301Skib	retval = dsw->d_gianttrick->d_close(dev, fflag, devtype, td);
368149177Sphk	mtx_unlock(&Giant);
369177301Skib	dev_relthread(dev);
370149177Sphk	return (retval);
371149177Sphk}
372149177Sphk
373149177Sphkstatic void
374149177Sphkgiant_strategy(struct bio *bp)
375149177Sphk{
376177301Skib	struct cdevsw *dsw;
377177301Skib	struct cdev *dev;
378149177Sphk
379177301Skib	dev = bp->bio_dev;
380177301Skib	dsw = dev_refthread(dev);
381177301Skib	if (dsw == NULL) {
382177301Skib		biofinish(bp, NULL, ENXIO);
383177301Skib		return;
384177301Skib	}
385149177Sphk	mtx_lock(&Giant);
386177301Skib	dsw->d_gianttrick->d_strategy(bp);
387149177Sphk	mtx_unlock(&Giant);
388177301Skib	dev_relthread(dev);
389149177Sphk}
390149177Sphk
391149177Sphkstatic int
392149177Sphkgiant_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
393149177Sphk{
394177301Skib	struct cdevsw *dsw;
395149177Sphk	int retval;
396149177Sphk
397177301Skib	dsw = dev_refthread(dev);
398177301Skib	if (dsw == NULL)
399177301Skib		return (ENXIO);
400149177Sphk	mtx_lock(&Giant);
401177858Skib	retval = dsw->d_gianttrick->d_ioctl(dev, cmd, data, fflag, td);
402149177Sphk	mtx_unlock(&Giant);
403177301Skib	dev_relthread(dev);
404149177Sphk	return (retval);
405149177Sphk}
406149177Sphk
407149177Sphkstatic int
408149177Sphkgiant_read(struct cdev *dev, struct uio *uio, int ioflag)
409149177Sphk{
410177301Skib	struct cdevsw *dsw;
411149177Sphk	int retval;
412149177Sphk
413177301Skib	dsw = dev_refthread(dev);
414177301Skib	if (dsw == NULL)
415177301Skib		return (ENXIO);
416149177Sphk	mtx_lock(&Giant);
417177858Skib	retval = dsw->d_gianttrick->d_read(dev, uio, ioflag);
418149177Sphk	mtx_unlock(&Giant);
419177301Skib	dev_relthread(dev);
420149177Sphk	return (retval);
421149177Sphk}
422149177Sphk
423149177Sphkstatic int
424149177Sphkgiant_write(struct cdev *dev, struct uio *uio, int ioflag)
425149177Sphk{
426177301Skib	struct cdevsw *dsw;
427149177Sphk	int retval;
428149177Sphk
429177301Skib	dsw = dev_refthread(dev);
430177301Skib	if (dsw == NULL)
431177301Skib		return (ENXIO);
432149177Sphk	mtx_lock(&Giant);
433177301Skib	retval = dsw->d_gianttrick->d_write(dev, uio, ioflag);
434149177Sphk	mtx_unlock(&Giant);
435177301Skib	dev_relthread(dev);
436149177Sphk	return (retval);
437149177Sphk}
438149177Sphk
439149177Sphkstatic int
440149177Sphkgiant_poll(struct cdev *dev, int events, struct thread *td)
441149177Sphk{
442177301Skib	struct cdevsw *dsw;
443149177Sphk	int retval;
444149177Sphk
445177301Skib	dsw = dev_refthread(dev);
446177301Skib	if (dsw == NULL)
447177301Skib		return (ENXIO);
448149177Sphk	mtx_lock(&Giant);
449177301Skib	retval = dsw->d_gianttrick->d_poll(dev, events, td);
450149177Sphk	mtx_unlock(&Giant);
451177301Skib	dev_relthread(dev);
452149177Sphk	return (retval);
453149177Sphk}
454149177Sphk
455149177Sphkstatic int
456149177Sphkgiant_kqfilter(struct cdev *dev, struct knote *kn)
457149177Sphk{
458177301Skib	struct cdevsw *dsw;
459149177Sphk	int retval;
460149177Sphk
461177301Skib	dsw = dev_refthread(dev);
462177301Skib	if (dsw == NULL)
463177301Skib		return (ENXIO);
464149177Sphk	mtx_lock(&Giant);
465177301Skib	retval = dsw->d_gianttrick->d_kqfilter(dev, kn);
466149177Sphk	mtx_unlock(&Giant);
467177301Skib	dev_relthread(dev);
468149177Sphk	return (retval);
469149177Sphk}
470149177Sphk
471149177Sphkstatic int
472196615Sjhbgiant_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot,
473196615Sjhb    vm_memattr_t *memattr)
474149177Sphk{
475177301Skib	struct cdevsw *dsw;
476149177Sphk	int retval;
477149177Sphk
478177301Skib	dsw = dev_refthread(dev);
479177301Skib	if (dsw == NULL)
480177301Skib		return (ENXIO);
481149177Sphk	mtx_lock(&Giant);
482196615Sjhb	if (dsw->d_gianttrick->d_flags & D_MMAP2)
483196615Sjhb		retval = dsw->d_gianttrick->d_mmap2(dev, offset, paddr, nprot,
484196615Sjhb		    memattr);
485196615Sjhb	else
486196615Sjhb		retval = dsw->d_gianttrick->d_mmap(dev, offset, paddr, nprot);
487149177Sphk	mtx_unlock(&Giant);
488177301Skib	dev_relthread(dev);
489149177Sphk	return (retval);
490149177Sphk}
491149177Sphk
492193275Sjhbstatic int
493193275Sjhbgiant_mmap_single(struct cdev *dev, vm_ooffset_t *offset, vm_size_t size,
494193275Sjhb    vm_object_t *object, int nprot)
495193275Sjhb{
496193275Sjhb	struct cdevsw *dsw;
497193275Sjhb	int retval;
498149177Sphk
499193275Sjhb	dsw = dev_refthread(dev);
500193275Sjhb	if (dsw == NULL)
501193275Sjhb		return (ENXIO);
502193275Sjhb	mtx_lock(&Giant);
503193275Sjhb	retval = dsw->d_gianttrick->d_mmap_single(dev, offset, size, object,
504193275Sjhb	    nprot);
505193275Sjhb	mtx_unlock(&Giant);
506193275Sjhb	dev_relthread(dev);
507193275Sjhb	return (retval);
508193275Sjhb}
509193275Sjhb
510178991Skibstatic void
511178991Skibnotify(struct cdev *dev, const char *ev)
512178991Skib{
513178991Skib	static const char prefix[] = "cdev=";
514178991Skib	char *data;
515178991Skib	int namelen;
516178991Skib
517178991Skib	if (cold)
518178991Skib		return;
519178991Skib	namelen = strlen(dev->si_name);
520192535Sattilio	data = malloc(namelen + sizeof(prefix), M_TEMP, M_NOWAIT);
521192535Sattilio	if (data == NULL)
522192535Sattilio		return;
523178991Skib	memcpy(data, prefix, sizeof(prefix) - 1);
524178991Skib	memcpy(data + sizeof(prefix) - 1, dev->si_name, namelen + 1);
525178991Skib	devctl_notify("DEVFS", "CDEV", ev, data);
526178991Skib	free(data, M_TEMP);
527178991Skib}
528178991Skib
529178991Skibstatic void
530178991Skibnotify_create(struct cdev *dev)
531178991Skib{
532178991Skib
533178991Skib	notify(dev, "CREATE");
534178991Skib}
535178991Skib
536178991Skibstatic void
537178991Skibnotify_destroy(struct cdev *dev)
538178991Skib{
539178991Skib
540178991Skib	notify(dev, "DESTROY");
541178991Skib}
542178991Skib
543130585Sphkstatic struct cdev *
544191116Sednewdev(struct cdevsw *csw, int unit, struct cdev *si)
54547028Sphk{
546140733Sphk	struct cdev *si2;
54748936Sphk
548140733Sphk	mtx_assert(&devmtx, MA_OWNED);
549179726Sed	if (csw->d_flags & D_NEEDMINOR) {
550179726Sed		/* We may want to return an existing device */
551179726Sed		LIST_FOREACH(si2, &csw->d_devs, si_list) {
552191116Sed			if (dev2unit(si2) == unit) {
553179726Sed				dev_free_devlocked(si);
554179726Sed				return (si2);
555179726Sed			}
556140733Sphk		}
55748936Sphk	}
558191116Sed	si->si_drv0 = unit;
559150342Sphk	si->si_devsw = csw;
560144281Sphk	LIST_INSERT_HEAD(&csw->d_devs, si, si_list);
561125850Sbde	return (si);
56247028Sphk}
56347028Sphk
564125846Sphkstatic void
565144292Sphkfini_cdevsw(struct cdevsw *devsw)
56649535Sphk{
567149324Sphk	struct cdevsw *gt;
56849535Sphk
569149324Sphk	if (devsw->d_gianttrick != NULL) {
570149324Sphk		gt = devsw->d_gianttrick;
571149324Sphk		memcpy(devsw, gt, sizeof *devsw);
572177301Skib		cdevsw_free_devlocked(gt);
573149324Sphk		devsw->d_gianttrick = NULL;
574149324Sphk	}
575126156Sphk	devsw->d_flags &= ~D_INIT;
576126082Sphk}
577126082Sphk
578126082Sphkstatic void
579126077Sphkprep_cdevsw(struct cdevsw *devsw)
580126077Sphk{
581149177Sphk	struct cdevsw *dsw2;
582126077Sphk
583177301Skib	mtx_assert(&devmtx, MA_OWNED);
584177301Skib	if (devsw->d_flags & D_INIT)
585177301Skib		return;
586177301Skib	if (devsw->d_flags & D_NEEDGIANT) {
587177301Skib		dev_unlock();
588149177Sphk		dsw2 = malloc(sizeof *dsw2, M_DEVT, M_WAITOK);
589177301Skib		dev_lock();
590177301Skib	} else
591149177Sphk		dsw2 = NULL;
592177301Skib	if (devsw->d_flags & D_INIT) {
593177301Skib		if (dsw2 != NULL)
594177301Skib			cdevsw_free_devlocked(dsw2);
595177301Skib		return;
596177301Skib	}
597126082Sphk
598193275Sjhb	if (devsw->d_version != D_VERSION_01 &&
599193275Sjhb	    devsw->d_version != D_VERSION_02) {
600126082Sphk		printf(
601126082Sphk		    "WARNING: Device driver \"%s\" has wrong version %s\n",
602154266Salfred		    devsw->d_name == NULL ? "???" : devsw->d_name,
603154266Salfred		    "and is disabled.  Recompile KLD module.");
604126082Sphk		devsw->d_open = dead_open;
605126082Sphk		devsw->d_close = dead_close;
606126082Sphk		devsw->d_read = dead_read;
607126082Sphk		devsw->d_write = dead_write;
608126082Sphk		devsw->d_ioctl = dead_ioctl;
609126082Sphk		devsw->d_poll = dead_poll;
610126082Sphk		devsw->d_mmap = dead_mmap;
611126082Sphk		devsw->d_strategy = dead_strategy;
612126082Sphk		devsw->d_dump = dead_dump;
613126082Sphk		devsw->d_kqfilter = dead_kqfilter;
614126082Sphk	}
615193275Sjhb	if (devsw->d_version == D_VERSION_01)
616193275Sjhb		devsw->d_mmap_single = NULL;
617126082Sphk
618149177Sphk	if (devsw->d_flags & D_NEEDGIANT) {
619149177Sphk		if (devsw->d_gianttrick == NULL) {
620149177Sphk			memcpy(dsw2, devsw, sizeof *dsw2);
621149177Sphk			devsw->d_gianttrick = dsw2;
622196615Sjhb			devsw->d_flags |= D_MMAP2;
623177301Skib			dsw2 = NULL;
624177301Skib		}
625149177Sphk	}
626149177Sphk
627149177Sphk#define FIXUP(member, noop, giant) 				\
628149177Sphk	do {							\
629149177Sphk		if (devsw->member == NULL) {			\
630149177Sphk			devsw->member = noop;			\
631149177Sphk		} else if (devsw->d_flags & D_NEEDGIANT)	\
632149177Sphk			devsw->member = giant;			\
633149177Sphk		}						\
634149177Sphk	while (0)
635149177Sphk
636149177Sphk	FIXUP(d_open,		null_open,	giant_open);
637149177Sphk	FIXUP(d_fdopen,		NULL,		giant_fdopen);
638149177Sphk	FIXUP(d_close,		null_close,	giant_close);
639149177Sphk	FIXUP(d_read,		no_read,	giant_read);
640149177Sphk	FIXUP(d_write,		no_write,	giant_write);
641149177Sphk	FIXUP(d_ioctl,		no_ioctl,	giant_ioctl);
642149177Sphk	FIXUP(d_poll,		no_poll,	giant_poll);
643196615Sjhb	FIXUP(d_mmap2,		no_mmap,	giant_mmap);
644149177Sphk	FIXUP(d_strategy,	no_strategy,	giant_strategy);
645149177Sphk	FIXUP(d_kqfilter,	no_kqfilter,	giant_kqfilter);
646193275Sjhb	FIXUP(d_mmap_single,	no_mmap_single,	giant_mmap_single);
647149177Sphk
648120514Sphk	if (devsw->d_dump == NULL)	devsw->d_dump = no_dump;
649126082Sphk
650126082Sphk	LIST_INIT(&devsw->d_devs);
651126082Sphk
652126082Sphk	devsw->d_flags |= D_INIT;
653126082Sphk
654177301Skib	if (dsw2 != NULL)
655177301Skib		cdevsw_free_devlocked(dsw2);
656125846Sphk}
657111622Sphk
658171181Skibstruct cdev *
659183382Sedmake_dev_credv(int flags, struct cdevsw *devsw, int unit,
660171181Skib    struct ucred *cr, uid_t uid,
661147982Srwatson    gid_t gid, int mode, const char *fmt, va_list ap)
662125846Sphk{
663130585Sphk	struct cdev *dev;
664125846Sphk	int i;
665125846Sphk
666150342Sphk	dev = devfs_alloc();
667140733Sphk	dev_lock();
668177301Skib	prep_cdevsw(devsw);
669183382Sed	dev = newdev(devsw, unit, dev);
670171181Skib	if (flags & MAKEDEV_REF)
671171181Skib		dev_refl(dev);
672120529Sphk	if (dev->si_flags & SI_CHEAPCLONE &&
673150342Sphk	    dev->si_flags & SI_NAMED) {
674120529Sphk		/*
675120529Sphk		 * This is allowed as it removes races and generally
676120529Sphk		 * simplifies cloning devices.
677126082Sphk		 * XXX: still ??
678120529Sphk		 */
679170950Skib		dev_unlock_and_free();
680120529Sphk		return (dev);
681120529Sphk	}
682126082Sphk	KASSERT(!(dev->si_flags & SI_NAMED),
683144281Sphk	    ("make_dev() by driver %s on pre-existing device (min=%x, name=%s)",
684183397Sed	    devsw->d_name, dev2unit(dev), devtoname(dev)));
685126082Sphk
686110318Sphk	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
687110318Sphk	if (i > (sizeof dev->__si_namebuf - 1)) {
688134501Spjd		printf("WARNING: Device name truncated! (%s)\n",
689110318Sphk		    dev->__si_namebuf);
690110318Sphk	}
691136947Sphk
69265747Sphk	dev->si_flags |= SI_NAMED;
693147982Srwatson	if (cr != NULL)
694147982Srwatson		dev->si_cred = crhold(cr);
695147982Srwatson	else
696147982Srwatson		dev->si_cred = NULL;
697144385Sphk	dev->si_uid = uid;
698144385Sphk	dev->si_gid = gid;
699144385Sphk	dev->si_mode = mode;
70050092Sjulian
701111730Sphk	devfs_create(dev);
702171202Skib	clean_unrhdrl(devfs_inos);
703177301Skib	dev_unlock_and_free();
704178991Skib
705178991Skib	notify_create(dev);
706178991Skib
70749535Sphk	return (dev);
70849535Sphk}
70949535Sphk
710147982Srwatsonstruct cdev *
711183382Sedmake_dev(struct cdevsw *devsw, int unit, uid_t uid, gid_t gid, int mode,
712147982Srwatson    const char *fmt, ...)
713147982Srwatson{
714147982Srwatson	struct cdev *dev;
715147982Srwatson	va_list ap;
716147982Srwatson
717147982Srwatson	va_start(ap, fmt);
718183382Sed	dev = make_dev_credv(0, devsw, unit, NULL, uid, gid, mode, fmt, ap);
719147982Srwatson	va_end(ap);
720147982Srwatson	return (dev);
721147982Srwatson}
722147982Srwatson
723147982Srwatsonstruct cdev *
724183382Sedmake_dev_cred(struct cdevsw *devsw, int unit, struct ucred *cr, uid_t uid,
725147982Srwatson    gid_t gid, int mode, const char *fmt, ...)
726147982Srwatson{
727147982Srwatson	struct cdev *dev;
728147982Srwatson	va_list ap;
729147982Srwatson
730147982Srwatson	va_start(ap, fmt);
731183382Sed	dev = make_dev_credv(0, devsw, unit, cr, uid, gid, mode, fmt, ap);
732147982Srwatson	va_end(ap);
733147982Srwatson
734147982Srwatson	return (dev);
735147982Srwatson}
736147982Srwatson
737171181Skibstruct cdev *
738183382Sedmake_dev_credf(int flags, struct cdevsw *devsw, int unit,
739171181Skib    struct ucred *cr, uid_t uid,
740171181Skib    gid_t gid, int mode, const char *fmt, ...)
741171181Skib{
742171181Skib	struct cdev *dev;
743171181Skib	va_list ap;
744171181Skib
745171181Skib	va_start(ap, fmt);
746183382Sed	dev = make_dev_credv(flags, devsw, unit, cr, uid, gid, mode,
747171181Skib	    fmt, ap);
748171181Skib	va_end(ap);
749171181Skib
750171181Skib	return (dev);
751171181Skib}
752171181Skib
753150342Sphkstatic void
754150342Sphkdev_dependsl(struct cdev *pdev, struct cdev *cdev)
755150342Sphk{
756150342Sphk
757150342Sphk	cdev->si_parent = pdev;
758150342Sphk	cdev->si_flags |= SI_CHILD;
759150342Sphk	LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings);
760150342Sphk}
761150342Sphk
762150342Sphk
76377215Sphkvoid
764130585Sphkdev_depends(struct cdev *pdev, struct cdev *cdev)
76577215Sphk{
76677215Sphk
767135600Sphk	dev_lock();
768150342Sphk	dev_dependsl(pdev, cdev);
769135600Sphk	dev_unlock();
77077215Sphk}
77177215Sphk
772130585Sphkstruct cdev *
773130585Sphkmake_dev_alias(struct cdev *pdev, const char *fmt, ...)
77464880Sphk{
775130585Sphk	struct cdev *dev;
77664880Sphk	va_list ap;
77764880Sphk	int i;
77864880Sphk
779180445Skib	KASSERT(pdev != NULL, ("NULL pdev"));
780150342Sphk	dev = devfs_alloc();
781135600Sphk	dev_lock();
78264880Sphk	dev->si_flags |= SI_ALIAS;
78365747Sphk	dev->si_flags |= SI_NAMED;
78464880Sphk	va_start(ap, fmt);
785110318Sphk	i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
786110318Sphk	if (i > (sizeof dev->__si_namebuf - 1)) {
787134501Spjd		printf("WARNING: Device name truncated! (%s)\n",
788110318Sphk		    dev->__si_namebuf);
789110318Sphk	}
79064880Sphk	va_end(ap);
79164880Sphk
792111730Sphk	devfs_create(dev);
793180445Skib	dev_dependsl(pdev, dev);
794171202Skib	clean_unrhdrl(devfs_inos);
795135600Sphk	dev_unlock();
796178991Skib
797178991Skib	notify_create(dev);
798178991Skib
79964880Sphk	return (dev);
80064880Sphk}
80164880Sphk
802126082Sphkstatic void
803142242Sphkdestroy_devl(struct cdev *dev)
80450549Sphk{
805135843Sphk	struct cdevsw *csw;
806179175Skib	struct cdev_privdata *p, *p1;
807135843Sphk
808142242Sphk	mtx_assert(&devmtx, MA_OWNED);
809135843Sphk	KASSERT(dev->si_flags & SI_NAMED,
810183397Sed	    ("WARNING: Driver mistake: destroy_dev on %d\n", dev2unit(dev)));
811154029Sbz
812111730Sphk	devfs_destroy(dev);
813126082Sphk
814126082Sphk	/* Remove name marking */
815126077Sphk	dev->si_flags &= ~SI_NAMED;
816126077Sphk
817126082Sphk	/* If we are a child, remove us from the parents list */
81877215Sphk	if (dev->si_flags & SI_CHILD) {
81977215Sphk		LIST_REMOVE(dev, si_siblings);
82077215Sphk		dev->si_flags &= ~SI_CHILD;
82177215Sphk	}
822126082Sphk
823126082Sphk	/* Kill our children */
82477215Sphk	while (!LIST_EMPTY(&dev->si_children))
825142242Sphk		destroy_devl(LIST_FIRST(&dev->si_children));
826126082Sphk
827126082Sphk	/* Remove from clone list */
828126077Sphk	if (dev->si_flags & SI_CLONELIST) {
829126077Sphk		LIST_REMOVE(dev, si_clone);
830126077Sphk		dev->si_flags &= ~SI_CLONELIST;
831126077Sphk	}
832126082Sphk
833163328Stegge	dev->si_refcount++;	/* Avoid race with dev_rel() */
834135843Sphk	csw = dev->si_devsw;
835135934Sgreen	dev->si_devsw = NULL;	/* already NULL for SI_ALIAS */
836135934Sgreen	while (csw != NULL && csw->d_purge != NULL && dev->si_threadcount) {
837135843Sphk		csw->d_purge(dev);
838135843Sphk		msleep(csw, &devmtx, PRIBIO, "devprg", hz/10);
839158684Sphk		if (dev->si_threadcount)
840158684Sphk			printf("Still %lu threads in %s\n",
841158684Sphk			    dev->si_threadcount, devtoname(dev));
842135843Sphk	}
843163328Stegge	while (dev->si_threadcount != 0) {
844163328Stegge		/* Use unique dummy wait ident */
845163328Stegge		msleep(&csw, &devmtx, PRIBIO, "devdrn", hz / 10);
846163328Stegge	}
847135843Sphk
848179175Skib	dev_unlock();
849178991Skib	notify_destroy(dev);
850179175Skib	mtx_lock(&cdevpriv_mtx);
851179828Skib	LIST_FOREACH_SAFE(p, &cdev2priv(dev)->cdp_fdpriv, cdpd_list, p1) {
852179175Skib		devfs_destroy_cdevpriv(p);
853179175Skib		mtx_lock(&cdevpriv_mtx);
854179175Skib	}
855179175Skib	mtx_unlock(&cdevpriv_mtx);
856179175Skib	dev_lock();
857178991Skib
858135843Sphk	dev->si_drv1 = 0;
859135843Sphk	dev->si_drv2 = 0;
860135843Sphk	bzero(&dev->__si_u, sizeof(dev->__si_u));
861135843Sphk
862126082Sphk	if (!(dev->si_flags & SI_ALIAS)) {
863126082Sphk		/* Remove from cdevsw list */
864126082Sphk		LIST_REMOVE(dev, si_list);
865126082Sphk
866150342Sphk		/* If cdevsw has no more struct cdev *'s, clean it */
867171181Skib		if (LIST_EMPTY(&csw->d_devs)) {
868135844Sphk			fini_cdevsw(csw);
869171181Skib			wakeup(&csw->d_devs);
870171181Skib		}
871126082Sphk	}
87265747Sphk	dev->si_flags &= ~SI_ALIAS;
873163328Stegge	dev->si_refcount--;	/* Avoid race with dev_rel() */
874135843Sphk
875126082Sphk	if (dev->si_refcount > 0) {
876126082Sphk		LIST_INSERT_HEAD(&dead_cdevsw.d_devs, dev, si_list);
877126082Sphk	} else {
878170950Skib		dev_free_devlocked(dev);
879126082Sphk	}
88050549Sphk}
88150549Sphk
882126082Sphkvoid
883130585Sphkdestroy_dev(struct cdev *dev)
884126082Sphk{
885126082Sphk
886185373Skib	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "destroy_dev");
887135600Sphk	dev_lock();
888171251Skib	destroy_devl(dev);
889171251Skib	dev_unlock_and_free();
890126082Sphk}
891126082Sphk
89251225Sbdeconst char *
893130585Sphkdevtoname(struct cdev *dev)
89449982Sbillf{
89549982Sbillf
89649982Sbillf	return (dev->si_name);
89749982Sbillf}
89865374Sphk
89965374Sphkint
90091998Sphkdev_stdclone(char *name, char **namep, const char *stem, int *unit)
90165374Sphk{
90265374Sphk	int u, i;
90365374Sphk
90475519Sbrian	i = strlen(stem);
90575519Sbrian	if (bcmp(stem, name, i) != 0)
90665374Sphk		return (0);
90765374Sphk	if (!isdigit(name[i]))
90865374Sphk		return (0);
90965374Sphk	u = 0;
91086461Sphk	if (name[i] == '0' && isdigit(name[i+1]))
91186461Sphk		return (0);
91265374Sphk	while (isdigit(name[i])) {
91365374Sphk		u *= 10;
91465374Sphk		u += name[i++] - '0';
91565374Sphk	}
916104523Sgreen	if (u > 0xffffff)
917104523Sgreen		return (0);
91865374Sphk	*unit = u;
91965374Sphk	if (namep)
92065374Sphk		*namep = &name[i];
92165374Sphk	if (name[i])
92265374Sphk		return (2);
92365374Sphk	return (1);
92465374Sphk}
92565632Sphk
92665632Sphk/*
927126077Sphk * Helper functions for cloning device drivers.
928126077Sphk *
929126077Sphk * The objective here is to make it unnecessary for the device drivers to
930126077Sphk * use rman or similar to manage their unit number space.  Due to the way
931126077Sphk * we do "on-demand" devices, using rman or other "private" methods
932126077Sphk * will be very tricky to lock down properly once we lock down this file.
933126077Sphk *
934130936Sle * Instead we give the drivers these routines which puts the struct cdev *'s
935130936Sle * that are to be managed on their own list, and gives the driver the ability
936126077Sphk * to ask for the first free unit number or a given specified unit number.
937126077Sphk *
938126077Sphk * In addition these routines support paired devices (pty, nmdm and similar)
939126077Sphk * by respecting a number of "flag" bits in the minor number.
940126077Sphk *
941126077Sphk */
942126077Sphk
943126077Sphkstruct clonedevs {
944126077Sphk	LIST_HEAD(,cdev)	head;
945126077Sphk};
946126077Sphk
947126845Sphkvoid
948126845Sphkclone_setup(struct clonedevs **cdp)
949126845Sphk{
950126845Sphk
951126845Sphk	*cdp = malloc(sizeof **cdp, M_DEVBUF, M_WAITOK | M_ZERO);
952126845Sphk	LIST_INIT(&(*cdp)->head);
953126845Sphk}
954126845Sphk
955126077Sphkint
956166438Sbmsclone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev **dp, int extra)
957126077Sphk{
958126077Sphk	struct clonedevs *cd;
959140733Sphk	struct cdev *dev, *ndev, *dl, *de;
960126077Sphk	int unit, low, u;
961126077Sphk
962126845Sphk	KASSERT(*cdp != NULL,
963126845Sphk	    ("clone_setup() not called in driver \"%s\"", csw->d_name));
964126077Sphk	KASSERT(!(extra & CLONE_UNITMASK),
965126845Sphk	    ("Illegal extra bits (0x%x) in clone_create", extra));
966126077Sphk	KASSERT(*up <= CLONE_UNITMASK,
967126845Sphk	    ("Too high unit (0x%x) in clone_create", *up));
968179726Sed	KASSERT(csw->d_flags & D_NEEDMINOR,
969179726Sed	    ("clone_create() on cdevsw without minor numbers"));
970126077Sphk
971126077Sphk
972126077Sphk	/*
973126077Sphk	 * Search the list for a lot of things in one go:
974126077Sphk	 *   A preexisting match is returned immediately.
975126077Sphk	 *   The lowest free unit number if we are passed -1, and the place
976126077Sphk	 *	 in the list where we should insert that new element.
977126077Sphk	 *   The place to insert a specified unit number, if applicable
978126077Sphk	 *       the end of the list.
979126077Sphk	 */
980126077Sphk	unit = *up;
981150342Sphk	ndev = devfs_alloc();
982140733Sphk	dev_lock();
983177301Skib	prep_cdevsw(csw);
984126849Sphk	low = extra;
985126077Sphk	de = dl = NULL;
986126845Sphk	cd = *cdp;
987126077Sphk	LIST_FOREACH(dev, &cd->head, si_clone) {
988140733Sphk		KASSERT(dev->si_flags & SI_CLONELIST,
989140733Sphk		    ("Dev %p(%s) should be on clonelist", dev, dev->si_name));
990126077Sphk		u = dev2unit(dev);
991126077Sphk		if (u == (unit | extra)) {
992126077Sphk			*dp = dev;
993170950Skib			dev_unlock();
994150342Sphk			devfs_free(ndev);
995126077Sphk			return (0);
996126077Sphk		}
997126077Sphk		if (unit == -1 && u == low) {
998126077Sphk			low++;
999126077Sphk			de = dev;
1000126077Sphk			continue;
1001150793Sphk		} else if (u < (unit | extra)) {
1002150793Sphk			de = dev;
1003150793Sphk			continue;
1004150793Sphk		} else if (u > (unit | extra)) {
1005126077Sphk			dl = dev;
1006126077Sphk			break;
1007126077Sphk		}
1008126077Sphk	}
1009126077Sphk	if (unit == -1)
1010126849Sphk		unit = low & CLONE_UNITMASK;
1011183381Sed	dev = newdev(csw, unit | extra, ndev);
1012140733Sphk	if (dev->si_flags & SI_CLONELIST) {
1013140733Sphk		printf("dev %p (%s) is on clonelist\n", dev, dev->si_name);
1014150793Sphk		printf("unit=%d, low=%d, extra=0x%x\n", unit, low, extra);
1015140733Sphk		LIST_FOREACH(dev, &cd->head, si_clone) {
1016140733Sphk			printf("\t%p %s\n", dev, dev->si_name);
1017140733Sphk		}
1018140733Sphk		panic("foo");
1019140733Sphk	}
1020126077Sphk	KASSERT(!(dev->si_flags & SI_CLONELIST),
1021140733Sphk	    ("Dev %p(%s) should not be on clonelist", dev, dev->si_name));
1022126077Sphk	if (dl != NULL)
1023126077Sphk		LIST_INSERT_BEFORE(dl, dev, si_clone);
1024126077Sphk	else if (de != NULL)
1025126077Sphk		LIST_INSERT_AFTER(de, dev, si_clone);
1026126077Sphk	else
1027126077Sphk		LIST_INSERT_HEAD(&cd->head, dev, si_clone);
1028126077Sphk	dev->si_flags |= SI_CLONELIST;
1029126077Sphk	*up = unit;
1030170950Skib	dev_unlock_and_free();
1031126077Sphk	return (1);
1032126077Sphk}
1033126077Sphk
1034126077Sphk/*
1035126077Sphk * Kill everything still on the list.  The driver should already have
1036130585Sphk * disposed of any softc hung of the struct cdev *'s at this time.
1037126077Sphk */
1038126077Sphkvoid
1039126077Sphkclone_cleanup(struct clonedevs **cdp)
1040126077Sphk{
1041171181Skib	struct cdev *dev;
1042171181Skib	struct cdev_priv *cp;
1043126077Sphk	struct clonedevs *cd;
1044126077Sphk
1045126077Sphk	cd = *cdp;
1046126077Sphk	if (cd == NULL)
1047126077Sphk		return;
1048140733Sphk	dev_lock();
1049171181Skib	while (!LIST_EMPTY(&cd->head)) {
1050171181Skib		dev = LIST_FIRST(&cd->head);
1051171181Skib		LIST_REMOVE(dev, si_clone);
1052140733Sphk		KASSERT(dev->si_flags & SI_CLONELIST,
1053140733Sphk		    ("Dev %p(%s) should be on clonelist", dev, dev->si_name));
1054171181Skib		dev->si_flags &= ~SI_CLONELIST;
1055179828Skib		cp = cdev2priv(dev);
1056171181Skib		if (!(cp->cdp_flags & CDP_SCHED_DTR)) {
1057171181Skib			cp->cdp_flags |= CDP_SCHED_DTR;
1058171181Skib			KASSERT(dev->si_flags & SI_NAMED,
1059191115Sed				("Driver has goofed in cloning underways udev %x unit %x", dev2udev(dev), dev2unit(dev)));
1060171181Skib			destroy_devl(dev);
1061171181Skib		}
1062126077Sphk	}
1063177301Skib	dev_unlock_and_free();
1064126077Sphk	free(cd, M_DEVBUF);
1065126077Sphk	*cdp = NULL;
1066126077Sphk}
1067171181Skib
1068171181Skibstatic TAILQ_HEAD(, cdev_priv) dev_ddtr =
1069171181Skib	TAILQ_HEAD_INITIALIZER(dev_ddtr);
1070171181Skibstatic struct task dev_dtr_task;
1071171181Skib
1072171181Skibstatic void
1073171181Skibdestroy_dev_tq(void *ctx, int pending)
1074171181Skib{
1075171181Skib	struct cdev_priv *cp;
1076171181Skib	struct cdev *dev;
1077171181Skib	void (*cb)(void *);
1078171181Skib	void *cb_arg;
1079171181Skib
1080171181Skib	dev_lock();
1081171181Skib	while (!TAILQ_EMPTY(&dev_ddtr)) {
1082171181Skib		cp = TAILQ_FIRST(&dev_ddtr);
1083171181Skib		dev = &cp->cdp_c;
1084171181Skib		KASSERT(cp->cdp_flags & CDP_SCHED_DTR,
1085171181Skib		    ("cdev %p in dev_destroy_tq without CDP_SCHED_DTR", cp));
1086171181Skib		TAILQ_REMOVE(&dev_ddtr, cp, cdp_dtr_list);
1087171181Skib		cb = cp->cdp_dtr_cb;
1088171181Skib		cb_arg = cp->cdp_dtr_cb_arg;
1089171181Skib		destroy_devl(dev);
1090177301Skib		dev_unlock_and_free();
1091171181Skib		dev_rel(dev);
1092171181Skib		if (cb != NULL)
1093171181Skib			cb(cb_arg);
1094171181Skib		dev_lock();
1095171181Skib	}
1096171181Skib	dev_unlock();
1097171181Skib}
1098171181Skib
1099171188Skib/*
1100171188Skib * devmtx shall be locked on entry. devmtx will be unlocked after
1101171188Skib * function return.
1102171188Skib */
1103171188Skibstatic int
1104171188Skibdestroy_dev_sched_cbl(struct cdev *dev, void (*cb)(void *), void *arg)
1105171181Skib{
1106171181Skib	struct cdev_priv *cp;
1107171188Skib
1108171188Skib	mtx_assert(&devmtx, MA_OWNED);
1109179828Skib	cp = cdev2priv(dev);
1110171181Skib	if (cp->cdp_flags & CDP_SCHED_DTR) {
1111171181Skib		dev_unlock();
1112171181Skib		return (0);
1113171181Skib	}
1114171181Skib	dev_refl(dev);
1115171181Skib	cp->cdp_flags |= CDP_SCHED_DTR;
1116171181Skib	cp->cdp_dtr_cb = cb;
1117171181Skib	cp->cdp_dtr_cb_arg = arg;
1118171181Skib	TAILQ_INSERT_TAIL(&dev_ddtr, cp, cdp_dtr_list);
1119171181Skib	dev_unlock();
1120171181Skib	taskqueue_enqueue(taskqueue_swi_giant, &dev_dtr_task);
1121171181Skib	return (1);
1122171181Skib}
1123171181Skib
1124171181Skibint
1125171188Skibdestroy_dev_sched_cb(struct cdev *dev, void (*cb)(void *), void *arg)
1126171188Skib{
1127171188Skib	dev_lock();
1128171188Skib	return (destroy_dev_sched_cbl(dev, cb, arg));
1129171188Skib}
1130171188Skib
1131171188Skibint
1132171181Skibdestroy_dev_sched(struct cdev *dev)
1133171181Skib{
1134171181Skib	return (destroy_dev_sched_cb(dev, NULL, NULL));
1135171181Skib}
1136171181Skib
1137171181Skibvoid
1138171181Skibdestroy_dev_drain(struct cdevsw *csw)
1139171181Skib{
1140171181Skib
1141171181Skib	dev_lock();
1142171181Skib	while (!LIST_EMPTY(&csw->d_devs)) {
1143171181Skib		msleep(&csw->d_devs, &devmtx, PRIBIO, "devscd", hz/10);
1144171181Skib	}
1145171181Skib	dev_unlock();
1146171181Skib}
1147171181Skib
1148171181Skibvoid
1149171181Skibdrain_dev_clone_events(void)
1150171181Skib{
1151171181Skib
1152171181Skib	sx_xlock(&clone_drain_lock);
1153171181Skib	sx_xunlock(&clone_drain_lock);
1154171181Skib}
1155171181Skib
1156171181Skibstatic void
1157171181Skibdevdtr_init(void *dummy __unused)
1158171181Skib{
1159171181Skib
1160171181Skib	TASK_INIT(&dev_dtr_task, 0, destroy_dev_tq, NULL);
1161171181Skib}
1162171181Skib
1163171181SkibSYSINIT(devdtr, SI_SUB_DEVFS, SI_ORDER_SECOND, devdtr_init, NULL);
1164