xref: /dflybsd-src/sys/kern/kern_udev.c (revision 5844bad4362a5003b37475c888ad60161aad744d)
1 /*
2  * Copyright (c) 2010 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/proc.h>
38 #include <sys/buf.h>
39 #include <sys/conf.h>
40 #include <sys/poll.h>
41 #include <sys/ioccom.h>
42 #include <sys/malloc.h>
43 #include <sys/ctype.h>
44 #include <sys/syslog.h>
45 #include <sys/udev.h>
46 #include <sys/devfs.h>
47 #include <libprop/proplib.h>
48 
49 MALLOC_DEFINE(M_UDEV, "udev", "udev allocs");
50 
51 /* XXX: use UUIDs for identification; would need help from devfs */
52 
53 static cdev_t		udev_dev;
54 static d_open_t		udev_dev_open;
55 static d_close_t	udev_dev_close;
56 static d_read_t		udev_dev_read;
57 static d_poll_t		udev_dev_poll;
58 static d_ioctl_t	udev_dev_ioctl;
59 
60 static int _udev_dict_set_cstr(prop_dictionary_t, const char *, char *);
61 static int _udev_dict_set_int(prop_dictionary_t, const char *, int64_t);
62 static int _udev_dict_set_uint(prop_dictionary_t, const char *, uint64_t);
63 static int _udev_dict_delete_key(prop_dictionary_t, const char *);
64 static prop_dictionary_t udev_init_dict_event(cdev_t, const char *);
65 static int udev_init_dict(cdev_t);
66 static int udev_destroy_dict(cdev_t);
67 static void udev_event_insert(int, prop_dictionary_t);
68 static struct udev_event_kernel *udev_event_remove(void);
69 static void udev_event_free(struct udev_event_kernel *);
70 static char *udev_event_externalize(struct udev_event_kernel *);
71 static void udev_getdevs_scan_callback(cdev_t, void *);
72 static int udev_getdevs_ioctl(struct plistref *, u_long, prop_dictionary_t);
73 
74 struct cmd_function {
75 	const char *cmd;
76 	int  (*fn)(struct plistref *, u_long, prop_dictionary_t);
77 };
78 
79 struct udev_prop_ctx {
80 	prop_array_t cdevs;
81 	int error;
82 };
83 
84 struct udev_event_kernel {
85 	struct udev_event ev;
86 	TAILQ_ENTRY(udev_event_kernel)	link;
87 };
88 
89 struct udev_softc {
90 	int opened;
91 	int initiated;
92 
93 	struct selinfo sel;
94 
95 	int qlen;
96 	struct lock lock;
97 	TAILQ_HEAD(, udev_event_kernel) ev_queue;	/* list of thread_io */
98 } udevctx;
99 
100 static struct dev_ops udev_dev_ops = {
101 	{ "udev", 0, 0 },
102 	.d_open = udev_dev_open,
103 	.d_close = udev_dev_close,
104 	.d_read = udev_dev_read,
105 	.d_poll = udev_dev_poll,
106 	.d_ioctl = udev_dev_ioctl
107 };
108 
109 struct cmd_function cmd_fn[] = {
110 		{ .cmd = "getdevs", .fn = udev_getdevs_ioctl},
111 		{NULL, NULL}
112 };
113 
114 static int
115 _udev_dict_set_cstr(prop_dictionary_t dict, const char *key, char *str)
116 {
117 	prop_string_t	ps;
118 
119 	KKASSERT(dict != NULL);
120 
121 	ps = prop_string_create_cstring(str);
122 	if (ps == NULL)
123 		return ENOMEM;
124 
125 	if (prop_dictionary_set(dict, key, ps) == false) {
126 		prop_object_release(ps);
127 		return ENOMEM;
128 	}
129 
130 	prop_object_release(ps);
131 	return 0;
132 }
133 
134 static int
135 _udev_dict_set_int(prop_dictionary_t dict, const char *key, int64_t val)
136 {
137 	prop_number_t	pn;
138 
139 	KKASSERT(dict != NULL);
140 
141 	pn = prop_number_create_integer(val);
142 	if (pn == NULL)
143 		return ENOMEM;
144 
145 	if (prop_dictionary_set(dict, key, pn) == false) {
146 		prop_object_release(pn);
147 		return ENOMEM;
148 	}
149 
150 	prop_object_release(pn);
151 	return 0;
152 }
153 
154 static int
155 _udev_dict_set_uint(prop_dictionary_t dict, const char *key, uint64_t val)
156 {
157 	prop_number_t	pn;
158 
159 	KKASSERT(dict != NULL);
160 
161 	pn = prop_number_create_unsigned_integer(val);
162 	if (pn == NULL)
163 		return ENOMEM;
164 
165 	if (prop_dictionary_set(dict, key, pn) == false) {
166 		prop_object_release(pn);
167 		return ENOMEM;
168 	}
169 
170 	prop_object_release(pn);
171 	return 0;
172 }
173 
174 static int
175 _udev_dict_delete_key(prop_dictionary_t dict, const char *key)
176 {
177 	KKASSERT(dict != NULL);
178 
179 	prop_dictionary_remove(dict, key);
180 
181 	return 0;
182 }
183 
184 /*
185  * Initialize an event dictionary, which contains three parameters to
186  * identify the device referred to (name, devnum, kptr) and the affected key.
187  */
188 static prop_dictionary_t
189 udev_init_dict_event(cdev_t dev, const char *key)
190 {
191 	prop_dictionary_t	dict;
192 	uint64_t	kptr;
193 	int error;
194 
195 	kptr = (uint64_t)(uintptr_t)dev;
196 	KKASSERT(dev != NULL);
197 
198 	dict = prop_dictionary_create();
199 	if (dict == NULL) {
200 		log(LOG_DEBUG, "udev_init_dict_event: prop_dictionary_create() failed\n");
201 		return NULL;
202 	}
203 
204 	if ((error = _udev_dict_set_cstr(dict, "name", dev->si_name)))
205 		goto error_out;
206 	if ((error = _udev_dict_set_uint(dict, "devnum", dev->si_inode)))
207 		goto error_out;
208 	if ((error = _udev_dict_set_uint(dict, "kptr", kptr)))
209 		goto error_out;
210 	if ((error = _udev_dict_set_cstr(dict, "key", __DECONST(char *, key))))
211 		goto error_out;
212 
213 error_out:
214 	prop_object_release(dict);
215 	return NULL;
216 }
217 
218 int
219 udev_dict_set_cstr(cdev_t dev, const char *key, char *str)
220 {
221 	prop_dictionary_t	dict;
222 	int error;
223 
224 	KKASSERT(dev != NULL);
225 
226 	/* Queue a key update event */
227 	dict = udev_init_dict_event(dev, key);
228 	if (dict == NULL)
229 		return ENOMEM;
230 	if ((error = _udev_dict_set_cstr(dict, "value", str))) {
231 		prop_object_release(dict);
232 		return error;
233 	}
234 	udev_event_insert(UDEV_EV_KEY_UPDATE, dict);
235 	prop_object_release(dict);
236 
237 	return _udev_dict_set_cstr(dev->si_dict, key, str);
238 }
239 
240 int
241 udev_dict_set_int(cdev_t dev, const char *key, int64_t val)
242 {
243 	prop_dictionary_t	dict;
244 	int error;
245 
246 	KKASSERT(dev != NULL);
247 
248 	/* Queue a key update event */
249 	dict = udev_init_dict_event(dev, key);
250 	if (dict == NULL)
251 		return ENOMEM;
252 	if ((error = _udev_dict_set_int(dict, "value", val))) {
253 		prop_object_release(dict);
254 		return error;
255 	}
256 	udev_event_insert(UDEV_EV_KEY_UPDATE, dict);
257 	prop_object_release(dict);
258 
259 	return _udev_dict_set_int(dev->si_dict, key, val);
260 }
261 
262 int
263 udev_dict_set_uint(cdev_t dev, const char *key, uint64_t val)
264 {
265 	prop_dictionary_t	dict;
266 	int error;
267 
268 	KKASSERT(dev != NULL);
269 
270 	/* Queue a key update event */
271 	dict = udev_init_dict_event(dev, key);
272 	if (dict == NULL)
273 		return ENOMEM;
274 	if ((error = _udev_dict_set_uint(dict, "value", val))) {
275 		prop_object_release(dict);
276 		return error;
277 	}
278 	udev_event_insert(UDEV_EV_KEY_UPDATE, dict);
279 	prop_object_release(dict);
280 
281 	return _udev_dict_set_uint(dev->si_dict, key, val);
282 }
283 
284 int
285 udev_dict_delete_key(cdev_t dev, const char *key)
286 {
287 	prop_dictionary_t	dict;
288 
289 	KKASSERT(dev != NULL);
290 
291 	/* Queue a key removal event */
292 	dict = udev_init_dict_event(dev, key);
293 	if (dict == NULL)
294 		return ENOMEM;
295 	udev_event_insert(UDEV_EV_KEY_REMOVE, dict);
296 	prop_object_release(dict);
297 
298 	return _udev_dict_delete_key(dev->si_dict, key);
299 }
300 
301 static int
302 udev_init_dict(cdev_t dev)
303 {
304 	prop_dictionary_t dict;
305 	uint64_t	kptr;
306 	int error;
307 
308 	kptr = (uint64_t)(uintptr_t)dev;
309 
310 	KKASSERT(dev != NULL);
311 	dict = prop_dictionary_create();
312 	if (dict == NULL) {
313 		log(LOG_DEBUG, "udev_init_dict: prop_dictionary_create() failed\n");
314 		return ENOMEM;
315 	}
316 
317 	if ((error = _udev_dict_set_cstr(dict, "name", dev->si_name)))
318 		goto error_out;
319 	if ((error = _udev_dict_set_uint(dict, "devnum", dev->si_inode)))
320 		goto error_out;
321 	if ((error = _udev_dict_set_uint(dict, "kptr", kptr)))
322 		goto error_out;
323 
324 	/* XXX: The next 3 are marginallly useful, if at all */
325 	if ((error = _udev_dict_set_uint(dict, "uid", dev->si_uid)))
326 		goto error_out;
327 	if ((error = _udev_dict_set_uint(dict, "gid", dev->si_gid)))
328 		goto error_out;
329 	if ((error = _udev_dict_set_int(dict, "mode", dev->si_perms)))
330 		goto error_out;
331 
332 	if ((error = _udev_dict_set_int(dict, "major", umajor(dev->si_inode))))
333 		goto error_out;
334 	if ((error = _udev_dict_set_int(dict, "minor", dev->si_uminor)))
335 		goto error_out;
336 
337 	dev->si_dict = dict;
338 	return 0;
339 
340 error_out:
341 	dev->si_dict = NULL;
342 	prop_object_release(dict);
343 	return error;
344 }
345 
346 static int
347 udev_destroy_dict(cdev_t dev)
348 {
349 	KKASSERT(dev != NULL);
350 
351 	if (dev->si_dict != NULL) {
352 		prop_object_release(dev->si_dict);
353 		dev->si_dict = NULL;
354 	}
355 
356 	return 0;
357 }
358 
359 static void
360 udev_event_insert(int ev_type, prop_dictionary_t dict)
361 {
362 	struct udev_event_kernel *ev;
363 
364 	/* Only start queing events after client has initiated properly */
365 	if (!udevctx.initiated)
366 		return;
367 
368 	/* XXX: use objcache eventually */
369 	ev = kmalloc(sizeof(*ev), M_UDEV, M_WAITOK);
370 	ev->ev.ev_dict = prop_dictionary_copy(dict);
371 	if (ev->ev.ev_dict == NULL) {
372 		kfree(ev, M_UDEV);
373 		return;
374 	}
375 	ev->ev.ev_type = ev_type;
376 
377 	lockmgr(&udevctx.lock, LK_EXCLUSIVE);
378 	TAILQ_INSERT_TAIL(&udevctx.ev_queue, ev, link);
379 	++udevctx.qlen;
380 	lockmgr(&udevctx.lock, LK_RELEASE);
381 
382 	wakeup(&udevctx);
383 	selwakeup(&udevctx.sel);
384 }
385 
386 static struct udev_event_kernel *
387 udev_event_remove(void)
388 {
389 	struct udev_event_kernel *ev;
390 
391 	lockmgr(&udevctx.lock, LK_EXCLUSIVE);
392 	if (TAILQ_EMPTY(&udevctx.ev_queue)) {
393 		lockmgr(&udevctx.lock, LK_RELEASE);
394 		return NULL;
395 	}
396 
397 	ev = TAILQ_FIRST(&udevctx.ev_queue);
398 	TAILQ_REMOVE(&udevctx.ev_queue, ev, link);
399 	--udevctx.qlen;
400 	lockmgr(&udevctx.lock, LK_RELEASE);
401 
402 	return ev;
403 }
404 
405 static void
406 udev_event_free(struct udev_event_kernel *ev)
407 {
408 	/* XXX: use objcache eventually */
409 	kfree(ev, M_UDEV);
410 }
411 
412 static char *
413 udev_event_externalize(struct udev_event_kernel *ev)
414 {
415 	prop_dictionary_t	dict;
416 	char *xml;
417 	int error;
418 
419 
420 	dict = prop_dictionary_create();
421 	if (dict == NULL) {
422 		log(LOG_DEBUG, "udev_event_externalize: prop_dictionary_create() failed\n");
423 		return NULL;
424 	}
425 
426 	if ((error = _udev_dict_set_int(dict, "evtype", ev->ev.ev_type))) {
427 		prop_object_release(dict);
428 		return NULL;
429 	}
430 
431 	if (prop_dictionary_set(dict, "evdict", ev->ev.ev_dict) == false) {
432 		prop_object_release(dict);
433 		return NULL;
434 	}
435 
436 	prop_object_release(ev->ev.ev_dict);
437 
438 	xml = prop_dictionary_externalize(dict);
439 
440 	prop_object_release(dict);
441 
442 	return xml;
443 }
444 
445 int
446 udev_event_attach(cdev_t dev, char *name, int alias)
447 {
448 	prop_dictionary_t	dict;
449 	int error;
450 
451 	KKASSERT(dev != NULL);
452 
453 	reference_dev(dev);
454 
455 	error = udev_init_dict(dev);
456 	if (error)
457 		goto error_out;
458 
459 	if (alias) {
460 		dict = prop_dictionary_copy(dev->si_dict);
461 		if (dict == NULL)
462 			goto error_out;
463 
464 		if ((error = _udev_dict_set_cstr(dict, "name", name))) {
465 			prop_object_release(dict);
466 			goto error_out;
467 		}
468 
469 		_udev_dict_set_int(dict, "alias", 1);
470 
471 		udev_event_insert(UDEV_EVENT_ATTACH, dict);
472 		prop_object_release(dict);
473 	} else {
474 		_udev_dict_set_int(dev->si_dict, "alias", 0);
475 		udev_event_insert(UDEV_EVENT_ATTACH, dev->si_dict);
476 	}
477 
478 error_out:
479 	release_dev(dev);
480 	return error;
481 }
482 
483 int
484 udev_event_detach(cdev_t dev, char *name, int alias)
485 {
486 	prop_dictionary_t	dict;
487 
488 	KKASSERT(dev != NULL);
489 
490 	reference_dev(dev);
491 
492 	if (alias) {
493 		dict = prop_dictionary_copy(dev->si_dict);
494 		if (dict == NULL)
495 			goto error_out;
496 
497 		if (_udev_dict_set_cstr(dict, "name", name)) {
498 			prop_object_release(dict);
499 			goto error_out;
500 		}
501 
502 		_udev_dict_set_int(dict, "alias", 1);
503 
504 		udev_event_insert(UDEV_EVENT_DETACH, dict);
505 		prop_object_release(dict);
506 	} else {
507 		udev_event_insert(UDEV_EVENT_DETACH, dev->si_dict);
508 	}
509 
510 error_out:
511 	udev_destroy_dict(dev);
512 
513 	release_dev(dev);
514 
515 	return 0;
516 }
517 
518 /*
519  * dev stuff
520  */
521 static int
522 udev_dev_open(struct dev_open_args *ap)
523 {
524 	if (udevctx.opened)
525 		return EBUSY;
526 
527 	udevctx.opened = 1;
528 
529 	return 0;
530 }
531 
532 static int
533 udev_dev_close(struct dev_close_args *ap)
534 {
535 	udevctx.opened = 0;
536 	udevctx.initiated = 0;
537 	wakeup(&udevctx);
538 
539 	return 0;
540 }
541 
542 static int
543 udev_dev_poll(struct dev_poll_args *ap)
544 {
545 	int revents = 0;
546 
547         lockmgr(&udevctx.lock, LK_EXCLUSIVE);
548         if (ap->a_events & (POLLIN | POLLRDNORM)) {
549                 if (!TAILQ_EMPTY(&udevctx.ev_queue))
550                         revents = ap->a_events & (POLLIN | POLLRDNORM);
551                 else
552                         selrecord(curthread, &udevctx.sel);
553         }
554         lockmgr(&udevctx.lock, LK_RELEASE);
555 
556         ap->a_events = revents;
557         return 0;
558 }
559 
560 static int
561 udev_dev_read(struct dev_read_args *ap)
562 {
563 	struct udev_event_kernel *ev;
564 	struct uio *uio = ap->a_uio;
565 	char	*xml;
566 	size_t	len;
567 	int	error;
568 
569 
570 	lockmgr(&udevctx.lock, LK_EXCLUSIVE);
571 
572 	for (;;) {
573 		if ((ev = udev_event_remove()) != NULL) {
574 			if ((xml = udev_event_externalize(ev)) == NULL) {
575 				lockmgr(&udevctx.lock, LK_RELEASE);
576 				return ENOMEM;
577 			}
578 
579 			len = strlen(xml) + 1; /* account for NULL-termination */
580 			if (uio->uio_resid < len) {
581 				error = ENOMEM;
582 			} else {
583 				error = uiomove((caddr_t)xml, len, uio);
584 			}
585 
586 			kfree(xml, M_TEMP);
587 			udev_event_free(ev);
588 			lockmgr(&udevctx.lock, LK_RELEASE);
589 			return error;
590 		}
591 
592 		if ((error = lksleep(&udevctx, &udevctx.lock, 0, "udevq", 0))) {
593 			lockmgr(&udevctx.lock, LK_RELEASE);
594 			return error;
595 		}
596 	}
597 
598 	lockmgr(&udevctx.lock, LK_RELEASE);
599 
600 }
601 
602 static int
603 udev_dev_ioctl(struct dev_ioctl_args *ap)
604 {
605 	prop_dictionary_t dict;
606 	prop_object_t	po;
607 	prop_string_t	ps;
608 	struct plistref *pref;
609 	int i, error;
610 
611 	error = 0;
612 
613 	switch(ap->a_cmd) {
614 	case UDEVPROP:
615 		/* Use proplib(3) for userspace/kernel communication */
616 		pref = (struct plistref *)ap->a_data;
617 		error = prop_dictionary_copyin_ioctl(pref, ap->a_cmd, &dict);
618 		if (error)
619 			return error;
620 
621 		po = prop_dictionary_get(dict, "command");
622 		if (po == NULL || prop_object_type(po) != PROP_TYPE_STRING) {
623 			log(LOG_DEBUG, "udev: prop_dictionary_get() failed\n");
624 			prop_object_release(dict);
625 			return EINVAL;
626 		}
627 
628 		ps = po;
629 		/* Handle cmd */
630 		for(i = 0; cmd_fn[i].cmd != NULL; i++) {
631 			if (prop_string_equals_cstring(ps, cmd_fn[i].cmd))
632 				break;
633 		}
634 
635 		if (cmd_fn[i].cmd != NULL) {
636 			log(LOG_DEBUG, "udev: ioctl %s called\n", cmd_fn[i].cmd);
637 			error = cmd_fn[i].fn(pref, ap->a_cmd, dict);
638 		} else {
639 			error = EINVAL;
640 		}
641 
642 		//prop_object_release(po);
643 		kprintf("foo\n");
644 		prop_object_release(dict);
645 		break;
646 	default:
647 		error = ENOTTY; /* Inappropriate ioctl for device */
648 		break;
649 	}
650 
651 	return(error);
652 }
653 
654 static void
655 udev_getdevs_scan_callback(cdev_t cdev, void *arg)
656 {
657 	struct udev_prop_ctx *ctx = arg;
658 
659 	KKASSERT(arg != NULL);
660 
661 	if (cdev->si_dict == NULL)
662 		return;
663 
664 	if (prop_array_add(ctx->cdevs, cdev->si_dict) == false) {
665 		ctx->error = EINVAL;
666 		return;
667 	}
668 }
669 
670 static int
671 udev_getdevs_ioctl(struct plistref *pref, u_long cmd, prop_dictionary_t dict)
672 {
673 	prop_dictionary_t odict;
674 	struct udev_prop_ctx ctx;
675 	int error;
676 
677 	ctx.error = 0;
678 	ctx.cdevs = prop_array_create();
679 	if (ctx.cdevs == NULL) {
680 		log(LOG_DEBUG, "udev_getdevs_ioctl: prop_array_create() failed\n");
681 		return EINVAL;
682 	}
683 
684 	/* XXX: need devfs_scan_alias_callback() */
685 	devfs_scan_callback(udev_getdevs_scan_callback, &ctx);
686 
687 	if (ctx.error != 0) {
688 		prop_object_release(ctx.cdevs);
689 		return (ctx.error);
690 	}
691 	udevctx.initiated = 1;
692 
693 	odict = prop_dictionary_create();
694 	if (odict == NULL) {
695 		return ENOMEM;
696 	}
697 
698 	if ((prop_dictionary_set(odict, "array", ctx.cdevs)) == 0) {
699 		log(LOG_DEBUG, "udev_getdevs_ioctl: prop_dictionary_set failed\n");
700 		prop_object_release(odict);
701 		return ENOMEM;
702 	}
703 
704 	error = prop_dictionary_copyout_ioctl(pref, cmd, odict);
705 
706 	/* XXX: need to release ctx.cdevs? */
707 	prop_object_release(odict);
708 	return error;
709 }
710 
711 
712 /*
713  * SYSINIT stuff
714  */
715 static void
716 udev_init(void)
717 {
718 	lockinit(&udevctx.lock, "udevevq", 0, LK_CANRECURSE);
719 	TAILQ_INIT(&udevctx.ev_queue);
720 }
721 
722 static void
723 udev_uninit(void)
724 {
725 }
726 
727 static void
728 udev_dev_init(void)
729 {
730 	udev_dev = make_dev(&udev_dev_ops,
731             0,
732             UID_ROOT,
733             GID_WHEEL,
734             0600,
735             "udev");
736 }
737 
738 static void
739 udev_dev_uninit(void)
740 {
741 	destroy_dev(udev_dev);
742 }
743 
744 SYSINIT(subr_udev_register, SI_SUB_CREATE_INIT, SI_ORDER_ANY, udev_init, NULL);
745 SYSUNINIT(subr_udev_register, SI_SUB_CREATE_INIT, SI_ORDER_ANY, udev_uninit, NULL);
746 SYSINIT(subr_udev_dev_register, SI_SUB_DRIVERS, SI_ORDER_ANY, udev_dev_init, NULL);
747 SYSUNINIT(subr_udev_dev_register, SI_SUB_DRIVERS, SI_ORDER_ANY, udev_dev_uninit, NULL);
748