Re: [RFC][PATCH] x86 BIOS Enhanced Disk Device (EDD) polling

Patrick Mochel
2002-09-08T00:23:44Z

[-- Attachment #1: Type: TEXT/PLAIN, Size: 5274 bytes --]

> Sweet, we're an axis now! That's much better than the other terms Pat
> and I have been called in the past... And everyone knows what's the
> next size jump up from axis :)

Yes, this is definitely cause for celebration. I've always believed that I
was a straight line around which objects rotated. Thanks for the
reassurance ;)

> So here's how driverfs fits into the big picture in one sentance:
>
> Anything that does not have to do with processes
> will go into driverfs.

This is true, as per discussions in Ottawa in June. This will take a long
time, and is not as clear cut as we make it out to sound. For example,
porting things like meminfo and cpuinfo will likely never happen. Though,
we intend to have equivalent information in driverfs. Things like stats
will also take a long, if not indefinite amount of, time to convert
because of performance concerns.

But, the obvious candidates for transition are things like /proc/bus/*,
/proc/ide, /proc/scsi, and at least the /proc/sys/cpu/ part of /proc/sys/.

We've been working on solidifying the various top-level infrastructural
pieces, like device classes, so that the organization of, and correlation
between objects in, driverfs makes sense to at least developers. As
always, mo' better stuff is coming RSN..

> So in this example, we are exporting a number of boot devices as the
> bios told us, so apply the rule stated above, and determine if it should
> go into /proc or not[1].

This is interesting, and I look forward to delving into the code. ACPI is
doing something very similar. One thing I would like to do is create a
'platform' or 'firmware' top-level directory in driverfs in which all the
various firmware drivers can display the data they ascertain from the
firmware. Stay tuned..

> [1] Yes, it's still a bit difficult to figure how to add files to
> driverfs, if you aren't starting with a "struct device" or "struct
> device_driver", but I have seen some very nice documentation on how to
> do it properly written by Pat involving a wonderful example of beer, and
> I'm sure that once he gets back into internet connectivity range, he'll
> be updating it and adding it to the Documentation directory. That
> should be by the end of the week or so.

Two examples are attached:

pub.c: A subsystem that maintains a list of beers that are dyanamically
registered and unregistered. This example is very similar to what is
already there wrt devices and drivers.

When the module is loaded, a top-level directory is created called 'pub',
and a subdirectory is created in that: 'beer'.

A struct beer describes a beer type. A couple of example beers are defined
and are registered with the pub subsystem (pubsystem?). A directory is
created for each, and a number of attributes are exported on behalf of the
beer by the pubsystem. All are read-only for now. (Wouldn't you love to
change the attributes of a beer? ;)

The things to note are the following:

- struct beer_attribute.

This describes an attribute and includes type-safe callbacks to read and
write the attributes.

- BEER_ATTR() macro

This helps you define an attribute for a beer. It creates a structure
called beer_attr_<name>, where <name> is the name of the attribute, and
initializes it properly.

- struct driverfs_ops

This defines the callbacks that driverfs actually calls. These are
responsible for converting between the generic structures to the
pubsystem-specific structures (see to_beer() and to_beer_attr() macros).

This code is pretty much identical to what already exists. I'm not
claiming its perfect, and not many eyes have really looked at what's going
on. I'm sure I'll hear if I'm doing something completely retarded, and am
looking forward to it.

If anyone is really that bored (and sick), extend the pub to include
objects of struct wine, etc. :)

pat.c: An in-kernel representation of myself.

This example exports attributes concerning my current state. I've been
threatening for some time to write patfs, but I decided to port patfs to
driverfs and eat my own dog food. It's pretty shoddy, and doesn't include
a decent way to update my current attributes. But, it's good for about 3
seconds of entertainment.

There is no registration of objects and no dynamic directory or file
creation. I am singular and may not be replicated.

When the module loads, a top-level directory is created named 'pat'. An
array of files are then created, each one for a particular attribute of my
self on this dreary Oregon Saturday.

For defining attributes, I defined struct simple_attribute, which has no
type-specific callbacks; they pass only the buffer, count, and offset.
Because of this, the driverfs_ops for the patsystem are simple.

[ struct simple_attribute can, and likely will be, defined in a common
header for others to use. There is a lot more work that I've done in this
area and I'm currently trying to figure out what to do with it all..]

Everything else in it is pretty self-explanatory (I hope). The patsystem
is pretty much as simple as it gets for exporting attributes of a
subsystem.

The documentation in Documentation/filesystems/driverfs.txt is up to date.
If anyone has any questions, please ask. Other than that, go wild.

-pat

[-- Attachment #2: pub.c --]
[-- Type: TEXT/PLAIN, Size: 7183 bytes --]

/*
* pub.c - exploting driverfs for fun and profit
*
* Copyright (c) 2002 Patrick Mochel
* Open Source Development Lab
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* This is a sample subsystem to illustrate how easy it is to latch on to
* driverfs and exploit it. It is the 'pub' subsystem. Objects of type 'beer'
* can be registered and unregistered with the pub via beer_register and
* beer_unregister.
*
* Every time a beer is registered with the pub subsystem, a directory is
* created for it. The pub subsystem could then create files on behalf of
* the beer, in the beer's directory. Once the beer has registered, it
* can create files for itself in its directory. A second, more robust
* example will surely follow.
*
* Compiled with

CFLAGS = -Wall -O2 -fomit-frame-pointer -DMODULE -D__KERNEL__
IDIR = /home/mochel/src/kernel/devel/linux-2.5/include

pub.o::pub.c
$(CC) $(CFLAGS) -I$(IDIR) -c -o $@ $<
*/

#include <linux/module.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/stat.h>
#include <linux/err.h>
#include <linux/driverfs_fs.h>

struct beer {
char * name;
char * style;
char * nation;
u32 founded;
u32 alcohol;
u32 calories;
struct list_head node;
struct driver_dir_entry dir;
};

struct beer_attribute {
struct attribute attr;
ssize_t (*show)(struct beer * beer, char * buf, size_t count, loff_t off);
ssize_t (*store)(struct beer * beer, const char * buf, size_t count, loff_t off);
};

#define BEER_ATTR(_name,_mode,_show,_store) \
struct beer_attribute beer_attr_##_name = { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
};

#define to_beer_attr(_attr) container_of(_attr,struct beer_attribute,attr)

#define to_beer(_dir) container_of(_dir,struct beer,dir)

static LIST_HEAD(beer_list);

static struct driver_dir_entry pub_dir = {
.name = "pub",
.mode = (S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO),
};

static struct driver_dir_entry pub_beer_dir = {
.name = "beer",
.mode = (S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO),
};

/* driverfs ops for beer attribute files */

static int empty_attr_open(struct driver_dir_entry * dir)
{
/* nothing to do */
return 0;
}

static int empty_attr_close(struct driver_dir_entry * dir)
{
/* nothing to do */
return 0;
}

static ssize_t
beer_attr_show(struct driver_dir_entry * dir, struct attribute * attr,
char * buf, size_t count, loff_t off)
{
struct beer * beer = to_beer(dir);
struct beer_attribute * beer_attr = to_beer_attr(attr);
ssize_t ret = 0;

if (beer_attr->show)
ret = beer_attr->show(beer,buf,count,off);
return ret;
}

static ssize_t
beer_attr_store(struct driver_dir_entry * dir, struct attribute * attr,
const char * buf, size_t count, loff_t off)
{
struct beer * beer = to_beer(dir);
struct beer_attribute * beer_attr = to_beer_attr(attr);
ssize_t ret = 0;

if (beer_attr->store)
ret = beer_attr->store(beer,buf,count,off);
return ret;
}

static struct driverfs_ops beer_attr_ops = {
.open = empty_attr_open,
.close = empty_attr_close,
.show = beer_attr_show,
.store = beer_attr_store,
};

/* default attributes for beer */

static ssize_t style_show(struct beer * beer, char * buf, size_t count, loff_t off)
{
return off ? 0 : snprintf(buf,50,"%s\n",beer->style);
}

static BEER_ATTR(style,0444,style_show,NULL);

static ssize_t nation_show(struct beer * beer, char * buf, size_t count, loff_t off)
{
return off ? 0 : snprintf(buf,50,"%s\n",beer->nation);
}

static BEER_ATTR(nation,0444,nation_show,NULL);

static ssize_t founded_show(struct beer * beer, char * buf, size_t count, loff_t off)
{
return off ? 0 : snprintf(buf,50,"%u\n",beer->founded);
}

static BEER_ATTR(founded,0444,founded_show,NULL);

static ssize_t alcohol_show(struct beer * beer, char * buf, size_t count, loff_t off)
{
return off ? 0 : snprintf(buf,50,"%u\n",beer->alcohol);
}

static BEER_ATTR(alcohol,0444,alcohol_show,NULL);

static ssize_t calories_show(struct beer * beer, char * buf, size_t count, loff_t off)
{
return off ? 0 : snprintf(buf,50,"%u\n",beer->calories);
}

static BEER_ATTR(calories,0444,calories_show,NULL);

static struct beer_attribute * def_attrs[] = {
&beer_attr_style,
&beer_attr_nation,
&beer_attr_founded,
&beer_attr_alcohol,
&beer_attr_calories,
NULL,
};

int beer_create_file(struct beer * beer, struct beer_attribute * attr)
{
int error = -EINVAL;
if (beer)
error = driverfs_create_file(&attr->attr,&beer->dir);
return error;
}

void beer_remove_file(struct beer * beer, struct beer_attribute * attr)
{
if (beer)
driverfs_remove_file(&beer->dir,attr->attr.name);
}

static void beer_remove_dir(struct beer * beer)
{
driverfs_remove_dir(&beer->dir);
}

static int populate_dir(struct beer * beer)
{
struct beer_attribute * attr;
int i;
int error = 0;

for (i = 0; (attr = def_attrs[i]); i++) {
if ((error = beer_create_file(beer,attr))) {
beer_remove_dir(beer);
break;
}
}
return error;
}

static int beer_make_dir(struct beer * beer)
{
int error;

beer->dir.name = beer->name;
beer->dir.ops = &beer_attr_ops;

error = driverfs_create_dir(&beer->dir,&pub_beer_dir);
if (!error)
error = populate_dir(beer);
return error;
}

int beer_register(struct beer * beer)
{
int error;
error = beer_make_dir(beer);
if (!error)
list_add_tail(&beer->node,&beer_list);
return error;
}

void beer_unregister(struct beer * beer)
{
beer_remove_dir(beer);
list_del_init(&beer->node);
}

static struct beer guinness_beer = {
.name = "Guinness",
.style = "stout",
.nation = "Ireland",
.founded = 1759,
.alcohol = 473,
.calories = 43,
};

static struct beer pabst_beer = {
.name = "Pabst",
.style = "pilsner",
.nation = "USA",
.founded = 1844,
.alcohol = 578,
.calories = 45,
};

static int __init pub_init(void)
{
driverfs_create_dir(&pub_dir,NULL);
driverfs_create_dir(&pub_beer_dir,&pub_dir);
beer_register(&guinness_beer);
beer_register(&pabst_beer);
return 0;
}

static void __exit pub_exit(void)
{
beer_unregister(&guinness_beer);
beer_unregister(&pabst_beer);
driverfs_remove_dir(&pub_beer_dir);
driverfs_remove_dir(&pub_dir);
}

subsys_initcall(pub_init);
module_exit(pub_exit);

[-- Attachment #3: pat.c --]
[-- Type: TEXT/PLAIN, Size: 4777 bytes --]

/*
* pat.c - an in-kernel representation of myself.
*
* Copyright (c) 2002 Patrick Mochel
* Open Source Development Lab
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Compiled with

CFLAGS = -Wall -O2 -fomit-frame-pointer -DMODULE -D__KERNEL__
IDIR = /home/mochel/src/kernel/devel/linux-2.5/include

pat.o::pat.c
$(CC) $(CFLAGS) -I$(IDIR) -c -o $@ $<
*/

#include <linux/module.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/stat.h>
#include <linux/err.h>
#include <linux/driverfs_fs.h>

struct simple_attribute {
struct attribute attr;
ssize_t (*show)(char * buf, size_t count, loff_t off);
};

#define SIMPLE_ATTR(_name,_mode,_show) \
struct simple_attribute sattr_##_name = { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
};

static char * who = "myself";
static char * what = "working";
static char * when = "not for long";
static char * where = "office";
static char * why = "catching up";
static char * how = "hungover";
static u32 dollars = 6;
static u32 cents = 89;
static char * beverage = "coffee";
static int bev_size = 12;

#define to_simple_attr(_attr) container_of(_attr,struct simple_attribute,attr)

/* driverfs ops for displaying my attributes */

static int pat_attr_show(struct driver_dir_entry * dir, struct attribute * attr,
char * buf, size_t count, loff_t off)
{
struct simple_attribute * sattr = to_simple_attr(attr);
if (sattr->show)
return sattr->show(buf,count,off);
return 0;
}

static struct driverfs_ops pat_attr_ops = {
.show pat_attr_show,
};

static struct driver_dir_entry pat_dir = {
.name = "pat",
.mode = (S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO),
.ops = &pat_attr_ops,
};

/* exporting my attributes */

static ssize_t show_who(char * buf, size_t count, loff_t off)
{
return off ? 0 : snprintf(buf,50,"%s\n",who);
}

static SIMPLE_ATTR(who,0444,show_who);

static ssize_t show_what(char * buf, size_t count, loff_t off)
{
return off ? 0 : snprintf(buf,50,"%s\n",what);
}

static SIMPLE_ATTR(what,0444,show_what);

static ssize_t show_when(char * buf, size_t count, loff_t off)
{
return off ? 0 : snprintf(buf,50,"%s\n",when);
}

static SIMPLE_ATTR(when,0444,show_when);

static ssize_t show_where(char * buf, size_t count, loff_t off)
{
return off ? 0 : snprintf(buf,50,"%s\n",where);
}

static SIMPLE_ATTR(where,0444,show_where);

static ssize_t show_why(char * buf, size_t count, loff_t off)
{
return off ? 0 : snprintf(buf,50,"%s\n",why);
}

static SIMPLE_ATTR(why,0444,show_why);

static ssize_t show_how(char * buf, size_t count, loff_t off)
{
return off ? 0 : snprintf(buf,50,"%s\n",how);
}

static SIMPLE_ATTR(how,0444,show_how);

static ssize_t show_cash(char * buf, size_t count, loff_t off)
{
return off ? 0 : snprintf(buf,50,"%u.%u\n",dollars,cents);
}

static SIMPLE_ATTR(cash,0444,show_cash);

static ssize_t show_beverage(char * buf, size_t count, loff_t off)
{
return off ? 0 : snprintf(buf,50,"%s\n",beverage);
}

static SIMPLE_ATTR(beverage,0444,show_beverage);

static ssize_t show_bev_size(char * buf, size_t count, loff_t off)
{
return off ? 0 : snprintf(buf,50,"%u\n",bev_size);
}

static SIMPLE_ATTR(bev_size,0444,show_bev_size);

static struct simple_attribute * pat_attrs[] = {
&sattr_who,
&sattr_what,
&sattr_when,
&sattr_where,
&sattr_why,
&sattr_how,
&sattr_cash,
&sattr_beverage,
&sattr_bev_size,
NULL,
};

static int populate_dir(void)
{
struct simple_attribute * attr;
int i;
int error = 0;

for (i = 0; (attr = pat_attrs[i]); i++) {
if ((error = driverfs_create_file(&attr->attr,&pat_dir)))
break;
}
return error;
}

static int __init pat_init(void)
{
driverfs_create_dir(&pat_dir,NULL);
return populate_dir();
}

static void __exit pat_exit(void)
{
driverfs_remove_dir(&pat_dir);
}

subsys_initcall(pat_init);
module_exit(pat_exit);