Subject: Re: CFR: The Auto-Generation Block/Character Device Switch Tables
To: None <>
From: David Laight <>
List: tech-kern
Date: 05/09/2002 19:09:13
> >
> > The interface functions are not global but local in their source. They
> > should be always called via the device switch. It's a bad idea to call
> > them directly from outside of the driver. To get the device switch entry
> > corresponding to a specific device, devsw_lookup(9) function is introduced.
> > Similarly, newly added devsw_lookup_major(9) can be used to get the major
> > number of a specific device.
> Why? We've always let the routines be global before. Why should we stop
> that now? Also, with the above change, you're doing more than just change
> how we handle b/cdevsw, you're forcing anything which calls routines
> directly to now have to know about major numbers. Also, you're forcing
> major numbers (for these rare cases) to have to be made global. :-)
> That strikes me as a bad thing.

IMHO it means that the symobols don't HAVE to be global.
In some drivers they will be anyway because the routines will
be in different source files (unless you do clever things with ld).

If you are going to look up the devsw entries, would it be better to
do it by name?  At least names have a change in hell of being
globally unique (esp. if you make them more than 2 characters long).

> Just to make sure I understand, compiled-in drivers won't need
> devsw_attach(), correct?

My reading was that this would have to be done somewhere.....
OTOH it could be magiced up by the build process.

> I would caution you that in places where drivers are making explicit calls
> to open/close/ioctl routines (i.e. where it's specifically foo_open, not
> jumping though the devsw), because chances are it REALLY wants that
> routine. I don't see what we gain by making such code have to now know
> what major number to use to find said driver.

Agreed.  I've written drivers in the past where part of one drivers
open routine is the entire contents of that of another driver.....

> > const void *devsw_lookup(dev_t dev, enum devswtype type);
> >
> > Get a device switch associated with the dev_t 'dev' and the device switch
> > type 'type'. The 'type' determines which device switches to be looked up.
> > In the internal of this function, get the major number from 'dev' by using
> > major() macro. Return the device switch on success. Otherwise, return NULL.

How many devsw tables do you think there might be?
In how many places will the 'type' paramter be anything other than
a constant?
Seems to me 2 routines would always be faster!
I presume you have some good reason for not leaving an externally
visible b/cdevsw[] table (or pointer to table)?

> >
> >
> > int devsw_lookup_major(const void *devsw, enum devswtype type);
> >
> > Get a device major number associated with the device switch 'devsw' and
> > the device switch type 'type'. The 'type' determines which device switches
> > to be looked up. Return the device switch on success. Otherwise, return NODEV.


> > dev_t devsw_chr2blk_dev(dev_t chrdev);
> >
> > Convert from character dev_t to block dev_t.
> > Return the valid dev_t (!= NODEV) on success. Otherwise return NODEV.
> >
> >
> > dev_t devsw_blk2chr_dev(dev_t blkdev);
> >
> > Convert from block dev_t to character dev_t.
> > Return the valid dev_t (!= NODEV) on success. Otherwise return NODEV.

Are these routines possible?
Esp. if the cdevsw and bdevsw entries are added separately.

> > int devsw_attach(const void *devsw, int *maj, enum devswtype type);
> >
> > Attach a device switch 'devsw' associated with the major number '*maj'
> > and the device switch type 'type'. If '*maj' is -1, allocate a major number
> > dynamically and stored allocated number in '*maj'. Return 0 on success or
> > an error value.
> This interface I don't like. You should pass in both the character & block
> devsws and maj int pointers at once; once call does it all. If you do
> devsw_attach as two steps (one for character, one for block) how do you
> know you should assosciate the character & block devices together?
> I think a better signature would be:
> int devsw_attach(const cdevsw *cdevsw, int *cret, const bdevsw *bdevsw,
> 	int *bret)

I tend to agree.
However do you want to use 'struct c/bdevsw *' as the argument?
Would it be better to define a separate (version independant)
structure that contains the required fields and (esp. for LKM)
a version number?

This would allow yo to (say) include the information for an
kernel based /dev at a later time.

> Don't worry about that. Break them. LKMs are only good for the specific
> kernel version they were built against. Far less invasive changes have
> broken lkm compatability in the past. :-)

Mmmm,,,, Solaris and SVR4 manage to have (resonably) version independant
LKMs - especially if the guidlines are adhered to properly.
Makes commercial device drivers much easier.

> I think dynamic major number support is well worth the code rototillage
> this would entail.

> One other aspect of lkms now is that they will only load in lkm device
> slots, bdev_lkm_dummy or cdev_lkm_dummy. It would be nice to retain that
> behavior. Or at least on a port with N lkm slots, it would be nice for the
> first N lkms to use them. Otherwise we have problems with adding devices
> at major numbers for which we don't have device nodes.

You certainly need to do something here.
There are 3 sorts of drivers:
- ones linked into the kernel that MUST have fixed numbers (boot disk etc)
- ones linked into teh kernel that can have dynamic numbers.
- dynamically added LKMs that have numbers assigned when installed.

Since it is generally undesirable to change the number of an installed
driver - but a kernel rebuild might include a different set of fixed
drivers - you need to ensure that there is enough space left so that
dynamically allocated major/minor are in a different part of the
number space.

(don't do what Unixware 2/7 (actually SVR4 for x86) does and have a
script that regenerates all of /dev every time you build a new kernel
- it is far too painfull!)

I would suggest trying to save the device-name to device number
map somewhere in the root filesystem.  This could be loaded into
the kernel (probably) before any dynamic assignments are done
(at least any for optional drivers).  That way /dev wont need

It would also allow the system to verify that the /dev matches the


David Laight: