dbb6269f4ecfa17f17abafeccac459accd4da943
[games.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.21 2007/05/07 15:43:30 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 <machine/stdarg.h>
48
49 #define cdevsw_ALLOCSTART       (NUMCDEVSW/2)
50
51 MALLOC_DEFINE(M_DEVT, "cdev_t", "dev_t storage");
52
53 /*
54  * This is the number of hash-buckets.  Experiements with 'real-life'
55  * udev_t's show that a prime halfway between two powers of two works
56  * best.
57  */
58 #define DEVT_HASH 83
59
60 static LIST_HEAD(, cdev) dev_hash[DEVT_HASH];
61
62 static int free_devt;
63 SYSCTL_INT(_debug, OID_AUTO, free_devt, CTLFLAG_RW, &free_devt, 0, "");
64 int dev_ref_debug = 0;
65 SYSCTL_INT(_debug, OID_AUTO, dev_refs, CTLFLAG_RW, &dev_ref_debug, 0, "");
66
67 /*
68  * cdev_t and u_dev_t primitives.  Note that the major number is always
69  * extracted from si_udev, not from si_devsw, because si_devsw is replaced
70  * when a device is destroyed.
71  */
72 int
73 major(cdev_t x)
74 {
75         if (x == NOCDEV)
76                 return NOUDEV;
77         return((x->si_udev >> 8) & 0xff);
78 }
79
80 int
81 minor(cdev_t x)
82 {
83         if (x == NOCDEV)
84                 return NOUDEV;
85         return(x->si_udev & 0xffff00ff);
86 }
87
88 int
89 lminor(cdev_t x)
90 {
91         int i;
92
93         if (x == NOCDEV)
94                 return NOUDEV;
95         i = minor(x);
96         return ((i & 0xff) | (i >> 8));
97 }
98
99 /*
100  * This is a bit complex because devices are always created relative to
101  * a particular cdevsw, including 'hidden' cdevsw's (such as the raw device
102  * backing a disk subsystem overlay), so we have to compare both the
103  * devsw and udev fields to locate the correct device.
104  *
105  * The device is created if it does not already exist.  If SI_ADHOC is not
106  * set the device will be referenced (once) and SI_ADHOC will be set.
107  * The caller must explicitly add additional references to the device if
108  * the caller wishes to track additional references.
109  *
110  * NOTE: The passed ops vector must normally match the device.  This is
111  * because the kernel may create shadow devices that are INVISIBLE TO
112  * USERLAND.  For example, the block device backing a disk is created
113  * as a shadow underneath the user-visible disklabel management device.
114  * Sometimes a device ops vector can be overridden, such as by /dev/console.
115  * In this case and this case only we allow a match when the ops vector
116  * otherwise would not match.
117  */
118 static
119 cdev_t
120 hashdev(struct dev_ops *ops, int x, int y, int allow_intercept)
121 {
122         struct cdev *si;
123         udev_t  udev;
124         int hash;
125
126         udev = makeudev(x, y);
127         hash = udev % DEVT_HASH;
128         LIST_FOREACH(si, &dev_hash[hash], si_hash) {
129                 if (si->si_udev == udev) {
130                         if (si->si_ops == ops)
131                                 return (si);
132                         if (allow_intercept && (si->si_flags & SI_INTERCEPTED))
133                                 return (si);
134                 }
135         }
136         si = kmalloc(sizeof(*si), M_DEVT, M_WAITOK | M_USE_RESERVE | M_ZERO);
137         si->si_ops = ops;
138         si->si_flags |= SI_HASHED | SI_ADHOC;
139         si->si_udev = udev;
140         si->si_refs = 1;
141         LIST_INSERT_HEAD(&dev_hash[hash], si, si_hash);
142
143         dev_dclone(si);
144         if (ops != &dead_dev_ops)
145                 ++ops->head.refs;
146         if (dev_ref_debug) {
147                 kprintf("create    dev %p %s(minor=%08x) refs=%d\n", 
148                         si, devtoname(si), uminor(si->si_udev),
149                         si->si_refs);
150         }
151         return (si);
152 }
153
154 /*
155  * Convert a device pointer to a device number
156  */
157 udev_t
158 dev2udev(cdev_t x)
159 {
160         if (x == NOCDEV)
161                 return NOUDEV;
162         return (x->si_udev);
163 }
164
165 /*
166  * Convert a device number to a device pointer.  The device is referenced
167  * ad-hoc, meaning that the caller should call reference_dev() if it wishes
168  * to keep ahold of the returned structure long term.
169  *
170  * The returned device is associated with the currently installed cdevsw
171  * for the requested major number.  NOCDEV is returned if the major number
172  * has not been registered.
173  */
174 cdev_t
175 udev2dev(udev_t x, int b)
176 {
177         cdev_t dev;
178         struct dev_ops *ops;
179
180         if (x == NOUDEV || b != 0)
181                 return(NOCDEV);
182         ops = dev_ops_get(umajor(x), uminor(x));
183         if (ops == NULL)
184                 return(NOCDEV);
185         dev = hashdev(ops, umajor(x), uminor(x), TRUE);
186         return(dev);
187 }
188
189 int
190 dev_is_good(cdev_t dev)
191 {
192         if (dev != NOCDEV && dev->si_ops != &dead_dev_ops)
193                 return(1);
194         return(0);
195 }
196
197 /*
198  * Various user device number extraction and conversion routines
199  */
200 int
201 uminor(udev_t dev)
202 {
203         return(dev & 0xffff00ff);
204 }
205
206 int
207 umajor(udev_t dev)
208 {
209         return((dev & 0xff00) >> 8);
210 }
211
212 udev_t
213 makeudev(int x, int y)
214 {
215         return ((x << 8) | y);
216 }
217
218 /*
219  * Create an internal or external device.
220  *
221  * Device majors can be overloaded and used directly by the kernel without
222  * conflict, but userland will only see the particular device major that
223  * has been installed with dev_ops_add().
224  *
225  * This routine creates and returns an unreferenced ad-hoc entry for the
226  * device which will remain intact until the device is destroyed.  If the
227  * caller intends to store the device pointer it must call reference_dev()
228  * to retain a real reference to the device.
229  *
230  * If an entry already exists, this function will set (or override)
231  * its cred requirements and name (XXX DEVFS interface).
232  */
233 cdev_t
234 make_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid, 
235         int perms, const char *fmt, ...)
236 {
237         cdev_t  dev;
238         __va_list ap;
239         int i;
240
241         /*
242          * compile the cdevsw and install the device
243          */
244         compile_dev_ops(ops);
245         dev = hashdev(ops, ops->head.maj, minor, FALSE);
246
247         /*
248          * Set additional fields (XXX DEVFS interface goes here)
249          */
250         __va_start(ap, fmt);
251         i = kvcprintf(fmt, NULL, dev->si_name, 32, ap);
252         dev->si_name[i] = '\0';
253         __va_end(ap);
254
255         return (dev);
256 }
257
258 /*
259  * This function is similar to make_dev() but no cred information or name
260  * need be specified.
261  */
262 cdev_t
263 make_adhoc_dev(struct dev_ops *ops, int minor)
264 {
265         cdev_t dev;
266
267         dev = hashdev(ops, ops->head.maj, minor, FALSE);
268         return(dev);
269 }
270
271 /*
272  * This function is similar to make_dev() except the new device is created
273  * using an old device as a template.
274  */
275 cdev_t
276 make_sub_dev(cdev_t odev, int minor)
277 {
278         cdev_t  dev;
279
280         dev = hashdev(odev->si_ops, umajor(odev->si_udev), minor, FALSE);
281
282         /*
283          * Copy cred requirements and name info XXX DEVFS.
284          */
285         if (dev->si_name[0] == 0 && odev->si_name[0])
286                 bcopy(odev->si_name, dev->si_name, sizeof(dev->si_name));
287         return (dev);
288 }
289
290 /*
291  * destroy_dev() removes the adhoc association for a device and revectors
292  * its ops to &dead_dev_ops.
293  *
294  * This routine releases the reference count associated with the ADHOC
295  * entry, plus releases the reference count held by the caller.  What this
296  * means is that you should not call destroy_dev(make_dev(...)), because
297  * make_dev() does not bump the reference count (beyond what it needs to
298  * create the ad-hoc association).  Any procedure that intends to destroy
299  * a device must have its own reference to it first.
300  */
301 void
302 destroy_dev(cdev_t dev)
303 {
304         int hash;
305
306         if (dev == NOCDEV)
307                 return;
308         if ((dev->si_flags & SI_ADHOC) == 0) {
309                 release_dev(dev);
310                 return;
311         }
312         if (dev_ref_debug) {
313                 kprintf("destroy   dev %p %s(minor=%08x) refs=%d\n", 
314                         dev, devtoname(dev), uminor(dev->si_udev),
315                         dev->si_refs);
316         }
317         if (dev->si_refs < 2) {
318                 kprintf("destroy_dev(): too few references on device! "
319                         "%p %s(minor=%08x) refs=%d\n",
320                     dev, devtoname(dev), uminor(dev->si_udev),
321                     dev->si_refs);
322         }
323         dev->si_flags &= ~SI_ADHOC;
324         if (dev->si_flags & SI_HASHED) {
325                 hash = dev->si_udev % DEVT_HASH;
326                 LIST_REMOVE(dev, si_hash);
327                 dev->si_flags &= ~SI_HASHED;
328         }
329
330         /*
331          * We have to release the ops reference before we replace the
332          * device switch with dead_dev_ops.
333          */
334         if (dead_dev_ops.d_strategy == NULL)
335                 compile_dev_ops(&dead_dev_ops);
336         if (dev->si_ops && dev->si_ops != &dead_dev_ops)
337                 dev_ops_release(dev->si_ops);
338         dev->si_drv1 = NULL;
339         dev->si_drv2 = NULL;
340         dev->si_ops = &dead_dev_ops;
341         --dev->si_refs;         /* release adhoc association reference */
342         release_dev(dev);       /* release callers reference */
343 }
344
345 /*
346  * Destroy all ad-hoc device associations associated with a domain within a
347  * device switch.  Only the minor numbers are included in the mask/match
348  * values. 
349  *
350  * Unlike the ops functions whos link structures do not contain
351  * any major bits, this function scans through the dev list via si_udev
352  * which is a 32 bit field that contains both major and minor bits.
353  * Because of this, we must mask the minor bits in the passed mask variable
354  * to allow -1 to be specified generically.
355  *
356  * The caller must not include any major bits in the match value.
357  */
358 void
359 destroy_all_devs(struct dev_ops *ops, u_int mask, u_int match)
360 {
361         int i;
362         cdev_t dev;
363         cdev_t ndev;
364
365         mask = uminor(mask);
366         for (i = 0; i < DEVT_HASH; ++i) {
367                 ndev = LIST_FIRST(&dev_hash[i]);
368                 while ((dev = ndev) != NULL) {
369                     ndev = LIST_NEXT(dev, si_hash);
370                     KKASSERT(dev->si_flags & SI_ADHOC);
371                     if (dev->si_ops == ops && 
372                         (dev->si_udev & mask) == match
373                     ) {
374                         ++dev->si_refs;
375                         destroy_dev(dev);
376                     }
377                 }
378         }
379 }
380
381 /*
382  * Add a reference to a device.  Callers generally add their own references
383  * when they are going to store a device node in a variable for long periods
384  * of time, to prevent a disassociation from free()ing the node.
385  *
386  * Also note that a caller that intends to call destroy_dev() must first
387  * obtain a reference on the device.  The ad-hoc reference you get with
388  * make_dev() and friends is NOT sufficient to be able to call destroy_dev().
389  */
390 cdev_t
391 reference_dev(cdev_t dev)
392 {
393         if (dev != NOCDEV) {
394                 ++dev->si_refs;
395                 if (dev_ref_debug) {
396                         kprintf("reference dev %p %s(minor=%08x) refs=%d\n", 
397                             dev, devtoname(dev), uminor(dev->si_udev),
398                             dev->si_refs);
399                 }
400         }
401         return(dev);
402 }
403
404 /*
405  * release a reference on a device.  The device will be freed when the last
406  * reference has been released.
407  *
408  * NOTE: we must use si_udev to figure out the original (major, minor),
409  * because si_ops could already be pointing at dead_dev_ops.
410  */
411 void
412 release_dev(cdev_t dev)
413 {
414         if (dev == NOCDEV)
415                 return;
416         if (free_devt) {
417                 KKASSERT(dev->si_refs > 0);
418         } else {
419                 if (dev->si_refs <= 0) {
420                         kprintf("Warning: extra release of dev %p(%s)\n",
421                             dev, devtoname(dev));
422                         free_devt = 0;  /* prevent bad things from occuring */
423                 }
424         }
425         --dev->si_refs;
426         if (dev_ref_debug) {
427                 kprintf("release   dev %p %s(minor=%08x) refs=%d\n", 
428                         dev, devtoname(dev), uminor(dev->si_udev),
429                         dev->si_refs);
430         }
431         if (dev->si_refs == 0) {
432                 if (dev->si_flags & SI_ADHOC) {
433                         kprintf("Warning: illegal final release on ADHOC"
434                                 " device %p(%s), the device was never"
435                                 " destroyed!\n",
436                                 dev, devtoname(dev));
437                 }
438                 if (dev->si_flags & SI_HASHED) {
439                         kprintf("Warning: last release on device, no call"
440                                 " to destroy_dev() was made! dev %p(%s)\n",
441                                 dev, devtoname(dev));
442                         dev->si_refs = 3;
443                         destroy_dev(dev);
444                         dev->si_refs = 0;
445                 }
446                 if (SLIST_FIRST(&dev->si_hlist) != NULL) {
447                         kprintf("Warning: last release on device, vnode"
448                                 " associations still exist! dev %p(%s)\n",
449                                 dev, devtoname(dev));
450                         free_devt = 0;  /* prevent bad things from occuring */
451                 }
452                 if (dev->si_ops && dev->si_ops != &dead_dev_ops) {
453                         dev_ops_release(dev->si_ops);
454                         dev->si_ops = NULL;
455                 }
456                 if (free_devt) {
457                         FREE(dev, M_DEVT);
458                 }
459         }
460 }
461
462 const char *
463 devtoname(cdev_t dev)
464 {
465         int mynor;
466         int len;
467         char *p;
468         const char *dname;
469
470         if (dev == NOCDEV)
471                 return("#nodev");
472         if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
473                 p = dev->si_name;
474                 len = sizeof(dev->si_name);
475                 if ((dname = dev_dname(dev)) != NULL)
476                         ksnprintf(p, len, "#%s/", dname);
477                 else
478                         ksnprintf(p, len, "#%d/", major(dev));
479                 len -= strlen(p);
480                 p += strlen(p);
481                 mynor = minor(dev);
482                 if (mynor < 0 || mynor > 255)
483                         ksnprintf(p, len, "%#x", (u_int)mynor);
484                 else
485                         ksnprintf(p, len, "%d", mynor);
486         }
487         return (dev->si_name);
488 }
489