Subject: Re: Understanding foo_open, foo_read, etc.
To: None <tech-kern@netbsd.org>
From: Peter Seebach <seebs@plethora.net>
List: tech-kern
Date: 08/29/2006 15:47:19
In message <20060829201504.GV3689@gallia.cubidou.net>, Quentin Garnier writes:
>It really depends on what the file descriptor points to.

I think this may be what I'm not understanding.  Where does the file
descriptor start pointing to anything?  foo_open returns 0 on success; where
is the magic cookie (cloned softc, file *, or whatever) being stashed?

>Look closely at the defined fields of that struct fileops.  There's one
>missing:  open.  That's because the file descriptor is never opened
>through the fileops, it is instead cloned when the device node is
>opened.

Hmm.  Okay.

>There's nothing magical here, look at miscfs/specfs/specfs_vnops.c.

Ahh!

>It depends what you want to achieve.  From what you describe, I'd say
>you're in confronted to a cloning device, i.e. a device driver that
>exposes a single node that can be opened multiple times and result in
>as many instances for the userland.

Sort of:

mkdir /dev/zap
mknod /dev/zap/ctl c 196 0
mknod /dev/zap/timer c 196 253
mknod /dev/zap/channel c 196 254
mknod /dev/zap/pseudo c 196 255

It looks like each open of /dev/zap/channel opens a new device.

>Other examples of this are bpf(4) and tap(4).  I advise reading the code
>of the latter, I wrote it mainly as an example of those techniques,
>although it's a bit complicated because both aspects of the device are
>available:  you either use /dev/tap when you don't know which device you
>intend to use, but you can also use /dev/tapN to directly access one
>instance of the driver.  bpf(4) is different:  the cdevsw part only
>implements the open() method, the rest appears in the fileops to be used
>after the file descriptor was cloned.

Looking at the FreeBSD code, it looks like the key magic is:

        zt_ehtag = EVENTHANDLER_REGISTER(dev_clone, zt_clone, 0, 1000);

and the zt_clone function makes a new device when asked.

My confusion has mostly come from the observation that, in most cases,
opening seems to be a no-op, presumably because the cdevsw entries mean
that the kernel can already know what to do if an open succeeds, so there's
no need to do much of anything; the only case where there's weirdness is where
the driver needs to provide a new file descriptor, and then fdclone returns
a magic value up the chain to the caller who then swaps descriptors around.

I think I was expecting the open call to do the allocation and setup; instead,
that's already been done, and gets *undone* in the event that there's an
error.  (With a special case for EMOVEFD.)

With all that in mind... It'd be awfully convenient if I could easily
clone the device, rather than cloning file descriptors, because it would
dramatically reduce the amount of code I have to modify, since all the
existing code is written as foo_read(device, ...) rather than foo_read(struct
file *, ...).  However, I'm not sure whether this is possible; I don't see
anything corresponding to FreeBSD's make_dev(devsw, minor, ...).

So, do I just have to convert to fileops instead of cdevsw ops, or is there
some easy way to clone the device?  It looks like it's the same logic either
way, one's just fewer code changes.

-s