Messages in this thread | | | Date | Mon, 12 Jan 1998 21:31:44 +1100 | From | Richard Gooch <> | Subject | Re: disk naming proposal & devfs |
| |
Larry McVoy writes: > [...] > : OK, well, I think this is not a problem for devfs. Once you have your > : fs_mountpoint() you can simply call devfs_register(). So, submit a > : patch and wait for it to be accepted (or shot down:-). > > OK, perhaps I misunderstood the point of devfs. Can you please tell me > (perhaps again) what is the problem that devfs is solving?
It's solving not one but many problems. Perhaps none are fatal in themselves, but in totality the old scheme is inadequate, IMHO. I've attached the README which tells you why.
Regards,
Richard.... =============================================================================== Device File System (devfs) Overview
Richard Gooch <rgooch@atnf.csiro.au>
12-JAN-1998
Conventions used in this document <section> =================================
Each section in this document will have the string "<section>" at the right-hand side of the section title. Each subsection will have "<subsection>" at the right-hand side. These strings are meant to make it easier to search through the document.
What is it? <section> ===========
Devfs is an alternative to "real" character and block special devices on your root filesystem. Kernel device drivers can register devices by name rather than major and minor numbers. These devices will appear in the devfs automatically, with whatever default ownership and protection the driver specified.
Why do it? <section> ==========
There are several problems that devfs addresses. Some of these problems are more serious than others (depending on your point of view), and some can be solved without devfs. However, the totality of these problems really calls out for devfs.
Major&minor allocation <subsection> ---------------------- The existing scheme requires the allocation of major and minor device numbers for each and every device. This means that a central co-ordinating authority is required to issue these device numbers (unless you're developing a "private" device driver), in order to preserve uniqueness. Devfs shifts the burden to a namespace. This may not seem like a huge benefit, but actually it is. Since driver authors will naturally choose a device name which reflects the functionality of the device, there is far less potential for namespace conflict. Solving this requires a kernel change.
/dev management <subsection> --------------- Because you currently access devices through device nodes, these must be created by the system administrator. For standard devices you can usually find a MAKEDEV programme which creates all these (hundreds!) of nodes. This means that changes in the kernel must be reflected by changes in the MAKEDEV programme, or else the system administrator creates device nodes by hand. The basic problem is that there are two separate databases of major and minor numbers. One is in the kernel and one is in /dev (or in a MAKEDEV programme, if you want to look at it that way). Solving this requires a kernel change.
/dev growth <subsection> ----------- I maintain a subset of the common /dev nodes, and I have nearly 600! Others have twice this number. Most of these devices simply don't exist because the hardware is not available. A huge /dev increases the time to access devices (I'm just referring to the dentry lookup times and the time taken to read inodes off disc: the next section shows some more horrors). An example of how big /dev can grow is if we consider SCSI devices: host 6 bits (say up to 64 hosts on a really big machine) channel 4 bits (say up to 16 SCSI buses per host) id 4 bits lun 3 bits partition 6 bits TOTAL 23 bits This requires 8 Mega (1024*1024) inodes if we want to store all possible device nodes. Even if we scrap everything but id,partition and assume a single host adapter with a single SCSI bus and only one logical unit per SCSI target (id), that's still 10 bits or 1024 inodes. Each VFS inode takes around 256 bytes (kernel 2.1.78), so that's 256 kBytes of inode storage on disc (assuming real inodes take a similar amount of space as VFS inodes). This is actually not so bad, because disc is cheap these days. Embedded systems would care about 256 kBytes of /dev inodes, but you could argue that embedded systems would have hand-tuned /dev directories. I've had to do just that on my embedded systems, but I would rather just leave it to devfs. Another issue is the time taken to lookup an inode when first referenced. Not only does this take time in scanning through a list in memory, but also the seek times to read the inodes off disc. This could be solved in user-space using a clever programme which scanned the kernel logs and deleted /dev entries which are not available and created them when they were available. This programme would need to be run every time a new module was loaded, which would slow things down a lot. Devfs is much cleaner.
Node to driver file_operations translation <subsection> ------------------------------------------ There is an important difference between the way disc-based c&b nodes and devfs make the connection between an entry in /dev and the actual device driver.
With the current 8 bit major and minor numbers the connection between disc-based c&b nodes and per-major drivers is done through a fixed-length table of 128 entries. The various filesystem types set the inode operations for c&b nodes to {chr,blk}dev_inode_operations, so when a device is opened a few quick levels of indirection bring us to the driver file_operations.
For miscellaneous character devices a second step is required: there is a scan for the driver entry with the same minor number as the file that was opened, and the appropriate minor open method is called. This scanning is done *every time* you open a device node. Potentially, you may be searching through dozens of misc. entries before you find your open method.
Linux *must* move beyond the 8 bit major and minor barrier, somehow. If we simply increase each to 16 bits, then the indexing scheme used for major driver lookup becomes untenable, because the major tables (one each for character and block devices) would need to be 64 k entries long (512 kBytes on x86, 1 Mbyte for 64 bit systems). So we would have to use a scheme like that used for miscellaneous character devices, which means the search time goes up linearly with the average number of major device drivers on your system.
Note that the devfs doesn't use the major&minor system. For devfs entries, the connection is done when you lookup the /dev entry. When dev_register() is called, an internal table is appended which has the entry name and the file_operations. If the dentry cache doesn't have the /dev entry already, this internal table is scanned to get the file_operations, and an inode is created. If the dentry cache already has the entry, there is *no lookup time* (other than the dentry scan itself, but we can't avoid that anyway, and besides Linux dentries cream other OS'es which don't have them:-). Furthermore, the number of node entries in a devfs is only the number of available device entries, not the number of *conceivable* entries. Even if you remove unnecessary entries in a disc-based /dev, the number of conceivable entries remains the same. Devfs provides a fast connection between a VFS node and the device driver, in a scalable way.
/dev as a system administration tool <subsection> ------------------------------------ Right now /dev contains a list of conceivable devices, most of which I don't have. A devfs would only show those devices available on my system. This means that listing /dev would be a handy way of checking what devices were available.
Major&minor size <subsection> ---------------- Existing major and minor numbers are limited to 8 bits each. This is now a limiting factor for some drivers, particularly the SCSI disc driver, which consumes a single major number. Only 16 discs are supported, and each disc may have only 15 partitions. Maybe this isn't a problem for you, but some of us are building huge Linux systems with disc arrays. Solving this requires a kernel change.
Readonly root filesystem <subsection> ------------------------ Having your device nodes on the root filesystem means that you can't operate properly with a read-only root filesystem. This is because you want to change ownerships and protections of tty devices. Existing practice prevents you using a CD-ROM as your root filesystem for a *real* system. Sure, you can boot off a CD-ROM, but you can't change tty ownerships, so it's only good for installing. Also, you can't use a shared NFS root filesystem for a cluster of discless Linux machines (having tty ownerships changed on a common /dev is not good). Nor can you embed your root filesystem in a ROM-FS. You can get around this by creating a RAMDISC at boot time, making an ext2 filesystem in it, mounting it somewhere and copying the contents of /dev into it, then unmounting it and mounting it over /dev. A devfs is a cleaner way of solving this.
Non-Unix root filesystem <subsection> ------------------------ Non-Unix filesystems (such as NTFS) can't be used for a root filesystem because they variously don't support character and block special files or symbolic links. You can't have a separate disc-based or RAMDISC-based filesystem mounted on /dev because you need device nodes before you can mount these. Devfs can be mounted without any device nodes. Devlinks won't work because symlinks aren't supported. Solving this requires devfs.
PTY security <subsection> ------------ Current pseudo-tty (pty) devices are owned by root and read-writable by everyone. The user of a pty-pair cannot change ownership/protections without being suid-root. This could be solved with a secure user-space daemon which runs as root and does the actual creation of pty-pairs. Such a daemon would require modification to *every* programme that wants to use this new mechanism. It also slows down creation of pty-pairs. An alternative is to create a new open_pty() syscall which does much the same thing as the user-space daemon. Once again, this requires modifications to pty-handling programmes. The devfs solution would allow a device driver to "tag" certain device files so that when an unopened device is opened, the ownerships are changed to the current euid and egid of the opening process, and the protections are changed to the default registered by the driver. When the device is closed ownership is set back to root and protections are set back to read-write for everybody. No programme need be changed.
How it works <section> ============
Registering device entries <subsection> -------------------------- For every entry (device node) in a devfs-based /dev a driver must call dev_register(). This adds the name of the device entry, the file_operations structure pointer and a few other things to an internal table. Device entries may be added and removed at any time. When a device entry is registered, it automagically appears in any mounted devfs'.
Inode lookup <subsection> ------------ When a lookup operation on an entry is performed and if there is no driver information for that entry devfs will attempt to call kerneld. If still no driver information can be found then a negative dentry is yielded and the next stage operation will be called by the VFS (such as create() or mknod() inode methods). If driver information can be found, an inode is created (if one does not exist already) and all is well.
Manually creating device nodes <subsection> ------------------------------ The mknod() method allows you to create an ordinary named pipe in the devfs, or you can create a character or block special inode if one does not already exist. You may wish to create a character or block special inode so that you can set permissions and ownership. Later, if a device driver registers an entry with the same name, the permissions, ownership and times are retained. This is how you can set the protections on a device even before the driver is loaded. Once you create an inode it appears in the directory listing.
Unregistering device entries <subsection> ---------------------------- A device driver calls dev_unregister() to unregister an entry. This does not remove the inode. Once an inode is created it remains until the devfs is unmounted.
Chroot() gaols <subsection> -------------- The semantics of inode creation are different when the devfs is mounted with the "explicit" option. Now, when a device entry is registered, it will not appear until you use mknod() to create the device. It doesn't matter if you mknod() before or after the device is registered with dev_register(). The purpose of this behaviour is to support chroot(2) gaols, where you want to mount a minimal devfs inside the gaol. Only the devices you specifically want to be available (through your mknod() setup) will be accessible.
Persistence of ownership/permissions across reboots <section> ===================================================
If you don't use mknod(2) to create a device file, nor use chmod(2) or chown(2) to change the ownerships/permissions, the inode ctime will remain at 0 (the epoch, 1-JAN-1970). Anything with a ctime later than this has had it's ownership/permissions changed. Hence, a simple script or programme may be used to tar up all changed inodes, prior to shutdown.
Upon bootup, simply untar the previously created tarfile, and all your ownerships/permissions will be retained. For the paranoid, you can save your permissions periodically using a cron job.
I've provided a script called "rc.devfs" in this directory which you can use to save and restore permissions.
Installation during the transition phase <section> ========================================
Currently, not all device drivers in the kernel have been modified to use devfs. To allow booting of kernels with and without devfs support, you will want to copy the contents of /dev to /devfs. Then, remove entries in /dev which are now available in devfs and make them symbolic links to the entries in /devfs. Finally, edit your /etc/fstab or boot scripts so that devfs is mounted over /devfs on bootup. If devfs is supported, accessing devices supported by devfs will follow the symlinks to devfs. If devfs is not supported, accessing those same devices will follow the symlinks to /devfs which contains only old-style device nodes. Devices not supported by devfs will be found directly on /dev. Simple! You can also follow this principle for chroot gaols.
I've provided a script called "mk-sdlinks" in this directory which you can use to create symlinks in /dev and /devfs for SCSI discs.
Device drivers currently ported <section> ===============================
- All miscellaneous character devices support devfs (this is done transparently through misc_register()).
- SCSI discs and generic hard discs Note that CONFIG_DEVFS_ONLY is ignored, otherwise you couldn't boot. This is because the current root-mounting code only supports a major:minor format
| |