Devices with Per Open Data

M. Warner Losh imp at bsdimp.com
Sat Nov 25 12:00:04 PST 2006


In message: <440b3e930611251127k2f94cb39ge198c538aaee68b0 at mail.gmail.com>
            "Ali Mashtizadeh" <mashtizadeh at gmail.com> writes:
: I had a question what is the proper way to hold per open data?

Generally, you can't.  dup2(2) and the internal structure of the
kernel makes this impossible to implement directly.

Typically, drivers needing this functionality use the cloning
interface.  This allows them to create a new minor for each open and
give them the 'per instance' data that they desire.

While this sounds complicated, in practice it turns out to be easy to
accomplish.  Once you've create the cloned device, you can use the
si_drv[012] fields of the struct cdev * to store this data.

Cloning it a bit underdocumented right now, as is most of the cdevsw
interaction.  But here's a sketch.

First, you need to register a clone event handler.  There is one of
these for your driver, even if it has multiple instances.  The method
I recommend is doing it in your attach routine if you haven't done so
yet, and removing it in your detach routine:

static eventhandler_tag ehtag;
static int ehtagref;

int
foo_attach(device_t)
{
...	
	if (ehtag == NULL)
		ehtag = EVENTHANDLER_REGISTER(dev_clone, foo_clone, 0, 1000);
	ehtagref++;
...
}

int
foo_detach(device_t)
{
	if (--ehtagref == 0 && ehtag != NULL)
		EVENTHANDLER_DEREGISTER(dev_clone, ehtag);
}

You are then setup with a clone handler that gets called in response
to all /dev/ opens that don't already have a static entry in /dev.
This is a big magical, but apart from files not appearing in ls it
works out well.

static struct clonedevs	*foo_dev_clones = NULL;

/* Clone device */
static void
foo_dev_clone(void *arg, struct ucred *cred, char *name, int namelen,
    struct cdev **dev)
{
	if (*dev != NULL)
		return;
	/* If you want a /dev/foo control device */
	if (strcmp(name, "foo") == 0)
		unit = -1;
	else if (dev_stdclone(name, NULL, "foo", &unit) != 1)
		return;	/* Bail on names we don't know */

	/* find any existing device, or allocate new unit number */
	if (clone_create(&foo_dev_clones, &foo_dev_cdevsw, &unit, dev, 0)) {
		*dev = make_dev(&foo_dev_cdevsw, unit2minor(unit),
			UID_ROOT, GID_WHEEL, 0600, "foo%d", unit);
		if (*dev != NULL) {
			dev_ref(*dev);
			(*dev)->si_flags |= SI_CHEAPCLONE;
		}
	}
}

Your open routine will then get the new dev and you can save stuff off
in it.

I think a man page needs to be written to document this stuff, because
I couldn't find one in my quick search.

: It seems
: there were some discussions in the past and they were all hacks. The API
: seems to have changed hiding the interfaces these hacks used (Between 6 and
: 6.1 i believe). 

I very much doubt that the API changed on the RELENG_6 branch in this
area.  Maybe between 5 and 6.

: It also seems to be something that NVidia has on its
: requested kernel features. So I'm wondering if this has been addressed yet
: or not? If so can someone point me to something to read either an example
: driver.

This is an oft-requrested feature.  The higher levels in FreeBSD make
it difficult to implement directly.  All device vnodes in the FreeBDS
(or any BSD) kernel are fungible.  This means it isn't possible to
tell them apart at the higher levels, and the driver API is such that
you can't tell them apart there unless you have different struct cdev
*.

Hope this helps.

Warner


More information about the freebsd-drivers mailing list