971bfba6354a8569c545b121a2f6ae1ff65f83a5
[dragonfly.git] / sys / kern / kern_conf.c
1 /*-
2  * Parts Copyright (c) 1995 Terrence R. Lambert
3  * Copyright (c) 1995 Julian R. Elischer
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *      This product includes software developed by Terrence R. Lambert.
17  * 4. The name Terrence R. Lambert may not be used to endorse or promote
18  *    products derived from this software without specific prior written
19  *    permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY Julian R. Elischer ``AS IS'' AND ANY
22  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * $FreeBSD: src/sys/kern/kern_conf.c,v 1.73.2.3 2003/03/10 02:18:25 imp Exp $
34  * $DragonFly: src/sys/kern/kern_conf.c,v 1.23 2007/05/09 00:53:34 dillon Exp $
35  */
36
37 #include <sys/param.h>
38 #include <sys/kernel.h>
39 #include <sys/sysctl.h>
40 #include <sys/systm.h>
41 #include <sys/module.h>
42 #include <sys/malloc.h>
43 #include <sys/conf.h>
44 #include <sys/vnode.h>
45 #include <sys/queue.h>
46 #include <sys/device.h>
47 #include <sys/disk.h>
48 #include <machine/stdarg.h>
49
50 #include <sys/sysref2.h>
51
52 #include <vfs/devfs/devfs.h>
53
54 MALLOC_DEFINE(M_DEVT, "cdev_t", "dev_t storage");
55
56 static int free_devt;
57 SYSCTL_INT(_debug, OID_AUTO, free_devt, CTLFLAG_RW, &free_devt, 0, "");
58 int dev_ref_debug = 0;
59 SYSCTL_INT(_debug, OID_AUTO, dev_refs, CTLFLAG_RW, &dev_ref_debug, 0, "");
60
61 /*
62  * cdev_t and u_dev_t primitives.  Note that the major number is always
63  * extracted from si_umajor, not from si_devsw, because si_devsw is replaced
64  * when a device is destroyed.
65  */
66 int
67 major(cdev_t dev)
68 {
69         if (dev == NULL)
70                 return NOUDEV;
71         return(dev->si_umajor);
72 }
73
74 int
75 minor(cdev_t dev)
76 {
77         if (dev == NULL)
78                 return NOUDEV;
79         return(dev->si_uminor);
80 }
81
82 /*
83  * Compatibility function with old udev_t format to convert the
84  * non-consecutive minor space into a consecutive minor space.
85  */
86 int
87 lminor(cdev_t dev)
88 {
89         int y;
90
91         if (dev == NULL)
92                 return NOUDEV;
93         y = dev->si_uminor;
94         if (y & 0x0000ff00)
95                 return NOUDEV;
96         return ((y & 0xff) | (y >> 8));
97 }
98
99 /*
100  * Convert a device pointer to an old style device number.  Return NOUDEV
101  * if the device is invalid or if the device (maj,min) cannot be converted
102  * to an old style udev_t.
103  */
104 udev_t
105 dev2udev(cdev_t dev)
106 {
107         if (dev == NULL)
108                 return NOUDEV;
109
110         return (udev_t)dev->si_inode;
111 }
112
113 /*
114  * Convert a device number to a device pointer.  The device is referenced
115  * ad-hoc, meaning that the caller should call reference_dev() if it wishes
116  * to keep ahold of the returned structure long term.
117  *
118  * The returned device is associated with the currently installed cdevsw
119  * for the requested major number.  NULL is returned if the major number
120  * has not been registered.
121  */
122 cdev_t
123 udev2dev(udev_t x, int b)
124 {
125         if (x == NOUDEV || b != 0)
126                 return(NULL);
127
128         return devfs_find_device_by_udev(x);
129 }
130
131 int
132 dev_is_good(cdev_t dev)
133 {
134         if (dev != NULL && dev->si_ops != &dead_dev_ops)
135                 return(1);
136         return(0);
137 }
138
139 /*
140  * Various user device number extraction and conversion routines
141  */
142 int
143 uminor(udev_t dev)
144 {
145         if (dev == NOUDEV)
146                 return(-1);
147         return(dev & 0xffff00ff);
148 }
149
150 int
151 umajor(udev_t dev)
152 {
153         if (dev == NOUDEV)
154                 return(-1);
155         return((dev & 0xff00) >> 8);
156 }
157
158 udev_t
159 makeudev(int x, int y)
160 {
161         if ((x & 0xffffff00) || (y & 0x0000ff00))
162                 return NOUDEV;
163         return ((x << 8) | y);
164 }
165
166 /*
167  * Create an internal or external device.
168  *
169  * This routine creates and returns an unreferenced ad-hoc entry for the
170  * device which will remain intact until the device is destroyed.  If the
171  * caller intends to store the device pointer it must call reference_dev()
172  * to retain a real reference to the device.
173  *
174  * If an entry already exists, this function will set (or override)
175  * its cred requirements and name (XXX DEVFS interface).
176  */
177 cdev_t
178 make_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid,
179         int perms, const char *fmt, ...)
180 {
181         cdev_t  devfs_dev;
182         __va_list ap;
183         int i;
184         char dev_name[PATH_MAX+1];
185
186         /*
187          * compile the cdevsw and install the device
188          */
189         compile_dev_ops(ops);
190
191         /*
192          * Set additional fields (XXX DEVFS interface goes here)
193          */
194         __va_start(ap, fmt);
195         i = kvcprintf(fmt, NULL, dev_name, 32, ap);
196         dev_name[i] = '\0';
197         __va_end(ap);
198
199 /*
200         if ((devfs_dev = devfs_find_device_by_name(dev_name)) != NULL) {
201                 kprintf("make_dev: Device %s already exists, returning old dev without creating new node\n", dev_name);
202                 return devfs_dev;
203         }
204 */
205
206         devfs_dev = devfs_new_cdev(ops, minor);
207         memcpy(devfs_dev->si_name, dev_name, i+1);
208
209         devfs_debug(DEVFS_DEBUG_INFO,
210                     "make_dev called for %s\n",
211                     devfs_dev->si_name);
212         devfs_create_dev(devfs_dev, uid, gid, perms);
213
214         return (devfs_dev);
215 }
216
217
218 cdev_t
219 make_only_devfs_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid,
220         int perms, const char *fmt, ...)
221 {
222         cdev_t  devfs_dev;
223         __va_list ap;
224         int i;
225         //char *dev_name;
226
227         /*
228          * compile the cdevsw and install the device
229          */
230         compile_dev_ops(ops);
231         devfs_dev = devfs_new_cdev(ops, minor);
232
233         /*
234          * Set additional fields (XXX DEVFS interface goes here)
235          */
236         __va_start(ap, fmt);
237         i = kvcprintf(fmt, NULL, devfs_dev->si_name, 32, ap);
238         devfs_dev->si_name[i] = '\0';
239         __va_end(ap);
240
241
242         devfs_create_dev(devfs_dev, uid, gid, perms);
243
244         return (devfs_dev);
245 }
246
247
248 cdev_t
249 make_only_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid,
250         int perms, const char *fmt, ...)
251 {
252         cdev_t  devfs_dev;
253         __va_list ap;
254         int i;
255         //char *dev_name;
256
257         /*
258          * compile the cdevsw and install the device
259          */
260         compile_dev_ops(ops);
261         devfs_dev = devfs_new_cdev(ops, minor);
262         devfs_dev->si_perms = perms;
263         devfs_dev->si_uid = uid;
264         devfs_dev->si_gid = gid;
265
266         /*
267          * Set additional fields (XXX DEVFS interface goes here)
268          */
269         __va_start(ap, fmt);
270         i = kvcprintf(fmt, NULL, devfs_dev->si_name, 32, ap);
271         devfs_dev->si_name[i] = '\0';
272         __va_end(ap);
273
274         reference_dev(devfs_dev);
275
276         return (devfs_dev);
277 }
278
279 void
280 destroy_only_dev(cdev_t dev)
281 {
282         release_dev(dev);
283         release_dev(dev);
284         release_dev(dev);
285 }
286
287 /*
288  * destroy_dev() removes the adhoc association for a device and revectors
289  * its ops to &dead_dev_ops.
290  *
291  * This routine releases the reference count associated with the ADHOC
292  * entry, plus releases the reference count held by the caller.  What this
293  * means is that you should not call destroy_dev(make_dev(...)), because
294  * make_dev() does not bump the reference count (beyond what it needs to
295  * create the ad-hoc association).  Any procedure that intends to destroy
296  * a device must have its own reference to it first.
297  */
298 void
299 destroy_dev(cdev_t dev)
300 {
301         if (dev) {
302                 devfs_debug(DEVFS_DEBUG_DEBUG,
303                             "destroy_dev called for %s\n",
304                             dev->si_name);
305                 devfs_destroy_dev(dev);
306         }
307 }
308
309 /*
310  * Make sure all asynchronous disk and devfs related operations have
311  * completed.
312  *
313  * Typically called prior to mountroot to ensure that all disks have
314  * been completely probed and on module unload to ensure that ops
315  * structures have been dereferenced.
316  */
317 void
318 sync_devs(void)
319 {
320         disk_config(NULL);
321         devfs_config();
322         disk_config(NULL);
323         devfs_config();
324 }
325
326 int
327 make_dev_alias(cdev_t target, const char *fmt, ...)
328 {
329         char name[PATH_MAX + 1];
330         __va_list ap;
331         int i;
332
333         __va_start(ap, fmt);
334         i = kvcprintf(fmt, NULL, name, 32, ap);
335         name[i] = '\0';
336         __va_end(ap);
337
338         devfs_make_alias(name, target);
339
340         return 0;
341 }
342
343 extern struct dev_ops default_dev_ops;
344
345 cdev_t
346 make_autoclone_dev(struct dev_ops *ops, struct devfs_bitmap *bitmap,
347                 d_clone_t *nhandler, uid_t uid, gid_t gid, int perms, const char *fmt, ...)
348 {
349         cdev_t dev;
350         char name[PATH_MAX + 1];
351         __va_list ap;
352         int i;
353
354         __va_start(ap, fmt);
355         i = kvcprintf(fmt, NULL, name, 32, ap);
356         name[i] = '\0';
357         __va_end(ap);
358
359         if (bitmap != NULL)
360                 devfs_clone_bitmap_init(bitmap);
361
362         devfs_clone_handler_add(name, nhandler);
363         dev = make_dev(&default_dev_ops, 0xffff00ff, uid, gid, perms, name);
364
365         return dev;
366 }
367
368 void
369 destroy_autoclone_dev(cdev_t dev, struct devfs_bitmap *bitmap)
370 {
371         if (dev == NULL)
372                 return;
373
374         devfs_clone_handler_del(dev->si_name);
375
376         if (bitmap != NULL)
377                 devfs_clone_bitmap_uninit(bitmap);
378
379         destroy_dev(dev);
380 }
381
382
383 /*
384  * Add a reference to a device.  Callers generally add their own references
385  * when they are going to store a device node in a variable for long periods
386  * of time, to prevent a disassociation from free()ing the node.
387  *
388  * Also note that a caller that intends to call destroy_dev() must first
389  * obtain a reference on the device.  The ad-hoc reference you get with
390  * make_dev() and friends is NOT sufficient to be able to call destroy_dev().
391  */
392 cdev_t
393 reference_dev(cdev_t dev)
394 {
395         //kprintf("reference_dev\n");
396
397         if (dev != NULL) {
398                 sysref_get(&dev->si_sysref);
399                 if (dev_ref_debug & 2) {
400                         kprintf("reference dev %p %s(minor=%08x) refs=%d\n",
401                             dev, devtoname(dev), dev->si_uminor,
402                             dev->si_sysref.refcnt);
403                 }
404         }
405         return(dev);
406 }
407
408 /*
409  * release a reference on a device.  The device will be terminated when the
410  * last reference has been released.
411  *
412  * NOTE: we must use si_umajor to figure out the original major number,
413  * because si_ops could already be pointing at dead_dev_ops.
414  */
415 void
416 release_dev(cdev_t dev)
417 {
418         //kprintf("release_dev\n");
419
420         if (dev == NULL)
421                 return;
422         sysref_put(&dev->si_sysref);
423 }
424
425 const char *
426 devtoname(cdev_t dev)
427 {
428         int mynor;
429         int len;
430         char *p;
431         const char *dname;
432
433         if (dev == NULL)
434                 return("#nodev");
435         if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
436                 p = dev->si_name;
437                 len = sizeof(dev->si_name);
438                 if ((dname = dev_dname(dev)) != NULL)
439                         ksnprintf(p, len, "#%s/", dname);
440                 else
441                         ksnprintf(p, len, "#%d/", major(dev));
442                 len -= strlen(p);
443                 p += strlen(p);
444                 mynor = minor(dev);
445                 if (mynor < 0 || mynor > 255)
446                         ksnprintf(p, len, "%#x", (u_int)mynor);
447                 else
448                         ksnprintf(p, len, "%d", mynor);
449         }
450         return (dev->si_name);
451 }
452