Subject: Re: 32 bit dev_t
To: Todd Vierling <tv@NetBSD.ORG>
From: Charles M. Hannum <mycroft@mit.edu>
List: tech-kern
Date: 01/13/1998 17:25:25
Todd Vierling <tv@NetBSD.ORG> writes:

> 
> On Tue, 13 Jan 1998, Charles M. Hannum wrote:
> 
> : * As an alternative to renumbering, you could instead make the minor
> : number non-contiguous; i.e.:
> : 
> :     3322222222221111111111
> :     10987654321098765432109876543210
> :     |--minor---||--major---||minor-|    new
> :                     |major-||minor-|    old
> : 
> : * Given the above scheme, should we decide to renumber everything at
> : some point (hopefully only once!!!), we can simply choose some portion
> : of the major numbers (say, the first 256) and use them for
> : `compatibility'.  There's no need to waste half the number space.
> 
> But there's a
> big problem with splitting the numbers this way:  kernel overhead. 
> Disassembling and reassembling pieces of ints turns a bitand-and-shiftright
> into a (bitand)-bitor-(bitand-and-shiftright).  Not that this is too bad,
> but it's more than it is worth, particularly if you need to look at a
> hexadecimal dev_t in kernel data dumps and figure out what it is.  Why
> bother, if the dev_t's will be renumbered anyway?

Uh, IF YOU DO WHAT I SAID BELOW, then this occurs exactly once each
time the spec node is creating.  I'm sure this is much more overhead
than calling a complicated function several times every time you
reference the device node.  (NOT!)

> : * If you're going to change all the existing device numbers, rather
> : than turning major() and minor() into functions, you should do any
> : old->new conversion (if necessary) in checkalias(), before it's put in
> : v_specinfo.  This value is *only* used internally to the kernel.
> 
> Well, only internally to the kernel, but not necessarily in just one place. 
> And it's not the only place that cares about the value of a dev_t.

You're missing the point, clearly.

All internal uses of dev_t use the value in v_specinfo.  All cases
where the data is returned to the user get it directly from the file
system.  Ergo, you only ever need to do conversion when filling in
v_specinfo, and it's totally transparent to the user.  There's *no*
need to make the major() and minor() macros complex; you already have
a convenient single point to do the conversion, so USE it.

> Problem
> is, as I see it, if I were to convert the value before it gets put into an
> info structure, then __stat13() would break--it would get the (converted)
> value of a dev_t, which is *not* what we want.

If you believe that, then you clearly didn't read what I wrote before.
To quote myself: `This value is *only* used internally to the kernel.
(You'll note that the device number returned by *stat(2) is taken
directly from the file system through *_getattr().)'

> : (Think about what you're proposing doing to your serial port read()
> : path, for example.  A couple of calls to major(), one to minor(), etc.
> : How many more layers of indirection before you make it slow enough
> : that nobody wants to use it any more?)
> 
> Let me quote my /sys/sys/types.h:
> 
> typedef u_int32_t       dev_t;          /* device number */
> [...]
> #if     defined(_KERNEL) && defined(COMPAT_13)
> extern  const dev_t __devcvt32 __P((dev_t));    /* convert major _and_ minor */
> extern  const u_int __devcvt32maj __P((dev_t)); /* convert major (faster) */
> #define devcvt32(x)  ((dev_t)(x) & 0xfff00000 ? (dev_t)(x) : __devcvt32(x))
> #define major(x)     ((u_int)(x) & 0xfff00000 ? \
>                       ((u_int)(x) >> 20) & 0xfff : __devcvt32maj(x))
> #define minor(x)     ((u_int)(devcvt32(x) & 0xfffff))
> #define makedev(x,y) (((((dev_t)(x) & 0xfff) << 20) | ((dev_t)(y) & 0xfffff)))
> #define devcmp(x,y)  (devcvt32(x) != devcvt32(y))
> [...]
> 
> You'll note that I added inline checks for `new' and `old' devices in
> major(), minor(), devcvt32(), and devcmp().  If a device is `new', a
> function call is never made.  Better yet, these all drop out if COMPAT_13 is
> undefined.

Uh, `that's nice'.  My proposal avoided doing the function calls *in
any case* in critical paths.  Virtually 0 overhead for either new or
old device numbers.

> : * We can't have stat(2) or mknod(2) magically change device numbers
> : behind our back.  For example, consider using pax(1) to pack or unpack
> : a file system used by another operating system.  Total lossage.
> 
> Well, that will automagically happen if the specinfo struct gets changed,
> won't it?

`See above.'

> But, if you're unpacking device nodes for a different system, and you're
> going to use them locally instead of exported, you're going to lose.  :>

That was, obviously, *not* the point.