Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
75fc4b98
Commit
75fc4b98
authored
Aug 02, 2002
by
Patrick Mochel
Browse files
Options
Browse Files
Download
Plain Diff
Merge
bk://ldm@bkbits.net/linux-2.5-driverfs
into osdl.org:/home/mochel/src/kernel/devel/linux-2.5-driverfs
parents
aaec33fa
fbbabb69
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
345 additions
and
120 deletions
+345
-120
Documentation/filesystems/driverfs.txt
Documentation/filesystems/driverfs.txt
+245
-120
drivers/base/fs/driver.c
drivers/base/fs/driver.c
+82
-0
include/linux/device.h
include/linux/device.h
+18
-0
No files found.
Documentation/filesystems/driverfs.txt
View file @
75fc4b98
...
...
@@ -3,175 +3,294 @@ driverfs - The Device Driver Filesystem
Patrick Mochel <mochel@osdl.org>
3 December 2001
2 August 2002
What it is:
~~~~~~~~~~~
driverfs is a
unified means for device drivers to export interfaces to
userspace.
driverfs is a
ram-based filesystem. It was created by copying
ramfs/inode.c to driverfs/inode.c and doing a little search-and-replace.
Some drivers have a need for exporting interfaces for things like
setting device-specific parameters, or tuning the device performance.
For example, wireless networking cards export a file in procfs to set
their SSID.
driverfs is a means to export kernel data structures, their
attributes, and the linkages between them to userspace.
Other times, the bus on which a device resides may export other
information about the device. For example, PCI and USB both export
device information via procfs or usbdevfs.
driverfs provides a unified interface for exporting attributes to
userspace. Currently, this interface is available only to device and
bus drivers.
In these cases, the files or directories are in nearly random places
in /proc. One benefit of driverfs is that it can consolidate all of
these interfaces to one standard location.
Using driverfs
~~~~~~~~~~~~~~
driverfs is always compiled in. You can access it by doing something like:
mount -t driverfs driverfs /devices
Top Level Directory Layout
~~~~~~~~~~~~~~~~~~~~~~~~~~
The driverfs directory arrangement exposes the relationship of kernel
data structures.
The top level driverfs diretory looks like:
bus/
root/
root/ contains a filesystem representation of the device tree. It maps
directly to the internal kernel device tree, which is a hierarchy of
struct device.
bus/ contains flat directory layout of the various bus types in the
kernel. Each bus's directory contains two subdirectories:
Why it's better than procfs:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This of course can't happen without changing every single driver that
exports a procfs interface, and having some coordination between all
of them as to what the proper place for their files is. Or can it?
devices/
drivers/
devices/ contains symlinks for each device discovered in the system
that point to the device's directory under root/.
driverfs was developed in conjunction with the new driver model for
the 2.5 kernel. In that model, the system has one unified tree of all
the devices that are present in the system. It follows naturally that
this tree can be exported to userspace in the same order.
drivers/ contains a directory for each device driver that is loaded
for devices on that particular bus (this assmumes that drivers do not
span multiple bus types).
So, every bus and every device gets a directory in the filesystem.
This directory is created when the device is registered in the tree;
before the driver actually gets a initialised. The dentry for this
directory is stored in the struct device for this driver, so the
driver has access to it.
Now, every driver has one standard place to export its files.
More information can device-model specific features can be found in
Documentation/device-model/.
Granted, the location of the file is not as intuitive as it may have
been under procfs. But, I argue that with the exception of
/proc/bus/pci, none of the files had intuitive locations. I also argue
that the development of userspace tools can help cope with these
changes and inconsistencies in locations.
Directory Contents
~~~~~~~~~~~~~~~~~~
Each object that is represented in driverfs gets a directory, rather
than a file, to make it simple to export attributes of that object.
Attributes are exported via ASCII text files. The programming
interface is discussed below.
Why we're not just using procfs:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When developing the new driver model, it was initially implemented
with a procfs tree. In explaining the concept to Linus, he said "Don't
use proc."
Instead of having monolithic files that are difficult to parse, all
files are intended to export one attribute. The name of the attribute
is the name of the file. The value of the attribute are the contents
of the file.
I was a little shocked (especially considering I had already
implemented it using procfs). "What do you mean 'don't use proc'?"
There should be few, if any, exceptions to this rule. You should not
violate it, for fear of public humilation.
His argument was that too many things use proc that shouldn't. And
even more things misuse proc that shouldn't. On top of that, procfs
was written before the VFS layer was written, so it doesn't use the
dcache. It reimplements many of the same features that the dcache
does, and is in general, crufty.
So, he told me to write my own. Soon after, he pointed me at ramfs,
the simplest filesystem known to man.
The Two-Tier Model
~~~~~~~~~~~~~~~~~~
Consequently, we have a virtual fileystem based heavily on ramfs, and
borrowing some conceptual functionality from procfs.
driverfs is a very simple, low-level interface. In order for kernel
objects to use it, there must be an intermediate layer in place for
each object type.
It may suck, but it does what it was designed to. At least so far.
All calls in driverfs are intended to be as type-safe as possible.
In order to extend driverfs to support multiple data types, a layer of
abstraction was required. This intermediate layer converts between the
generic calls and data structures of the driverfs core to the
subsystem-specific objects and calls.
How it works:
~~~~~~~~~~~~~
The Subsystem Interface
~~~~~~~~~~~~~
~~~~~~~~~~
Directories are encapsulated like this:
The subsystems bear the responsibility of implementing driverfs
extensions for the objects they control. Fortunately, it's intended to
be really easy to do so.
It's divided into three sections: directories, files, and operations.
Directories
~~~~~~~~~~~
struct driver_dir_entry {
char * name;
struct dentry * dentry;
mode_t mode;
struct list_head file
s;
char * name;
struct dentry * dentry;
mode_t mode;
struct driverfs_ops * op
s;
};
name:
Name of the directory.
dentry:
Dentry for the directory.
mode:
Permissions of the directory.
files:
Linked list of driver_file_entry's that are in the directory.
int
driverfs_create_dir(struct driver_dir_entry *, struct driver_dir_entry *);
To create a directory, one first calls
void
driverfs_remove_dir(struct driver_dir_entry * entry);
struct driver_dir_entry *
driverfs_create_dir_entry(const char * name, mode_t mode);
The directory structure should be statically allocated, and reside in
a subsystem-specific data structure:
which allocates and initialises a struct driver_dir_entry. Then to actually
create the directory:
struct device {
...
struct driver_dir_entry dir;
};
The subsystem is responsible for initializing the name, mode, and ops
fields of the directory entry. (More on struct driverfs_ops later)
int driverfs_create_dir(struct driver_dir_entry *, struct driver_dir_entry *);
To remove a directory:
Files
~~~~~
void driverfs_remove_dir(struct driver_dir_entry * entry);
struct attribute {
char * name;
mode_t mode;
};
Files are encapsulated like this:
int
driverfs_create_file(struct attribute * attr, struct driver_dir_entry * parent);
struct driver_file_entry {
struct driver_dir_entry * parent;
struct list_head node;
char * name;
mode_t mode;
struct dentry * dentry;
void * data;
struct driverfs_operations * ops;
void
driverfs_remove_file(struct driver_dir_entry *, const char * name);
The attribute structure is a simple, common token that the driverfs
core handles. It has little use on its own outside of the
core. Objects cannot use a plain struct attribute to export
attributes, since there are no callbacks for reading and writing data.
Therefore, the subsystem is required to define a data structure that
encapsulates the attribute structure, and provides type-safe callbacks
for reading and writing data.
An example looks like this:
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device * dev, char * buf, size_t count, loff_t off);
ssize_t (*store)(struct device * dev, const char * buf, size_t count, loff_t off);
};
struct driverfs_operations {
ssize_t (*read) (char *, size_t, loff_t, void *);
ssize_t (*write)(const char *, size_t, loff_t, void*);
Note that there is a struct attribute embedded in the structure. In
order to relieve pain in declaring attributes, the subsystem should
also define a macro, like:
#define DEVICE_ATTR(_name,_str,_mode,_show,_store) \
struct device_attribute dev_attr_##_name = { \
.attr = {.name = _str, .mode = _mode }, \
.show = _show, \
.store = _store, \
};
node:
Node in its parent directory's list of files.
This hides the initialization of the embedded struct, and in general,
the internals of each structure. It yields a structure by the name of
dev_attr_<name>.
name:
The name of the file.
In order for objects to create files, the subsystem should create
wrapper functions, like this:
dentry:
The dentry for the file.
int device_create_file(struct device *device, struct device_attribute * entry);
void device_remove_file(struct device * dev, struct device_attribute * attr);
data:
Caller specific data that is passed to the callbacks when they
are called.
..and forward the call on to the driverfs functions.
ops:
Operations for the file. Currently, this only contains read() and write()
callbacks for the file.
Note that there is no unique information in the attribute structures,
so the same structure can be used to describe files of several
different object instances.
To create a file, one first calls
struct driver_file_entry *
driverfs_create_entry (const char * name, mode_t mode,
struct driverfs_operations * ops, void * data);
Operations
~~~~~~~~~~
That allocates and initialises a struct driver_file_entry. Then, to actually
create a file, one calls
struct driverfs_ops {
int (*open)(struct driver_dir_entry *);
int (*close)(struct driver_dir_entry *);
ssize_t (*show)(struct driver_dir_entry *, struct attribute *,char *, size_t, loff_t);
ssize_t (*store)(struct driver_dir_entry *,struct attribute *,const char *, size_t, loff_t);
};
Subsystems are required to implement this set of callbacks. Their
purpose is to translate the generic data structures into the specific
objects, and operate on them. This can be done by defining macros like
this:
#define to_dev_attr(_attr) container_of(_attr,struct device_attribute,attr)
int driverfs_create_file(struct driver_file_entry * entry,
struct driver_dir_entry * parent);
#define to_device(d) container_of(d, struct device, dir)
To remove a file, one calls
Since the directories are statically allocated in the object, you can
derive the pointer to the object that owns the file. Ditto for the
attribute structures.
void driverfs_remove_file(struct driver_dir_entry *, const char * name);
Current Interfaces
~~~~~~~~~~~~~~~~~~
The following interface layers currently exist in driverfs:
- devices (include/linux/device.h)
----------------------------------
Structure:
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device * dev, char * buf, size_t count, loff_t off);
ssize_t (*store)(struct device * dev, const char * buf, size_t count, loff_t off);
};
Declaring:
DEVICE_ATTR(_name,_str,_mode,_show,_store);
Creation/Removal:
int device_create_file(struct device *device, struct device_attribute * entry);
void device_remove_file(struct device * dev, struct device_attribute * attr);
- bus drivers (include/linux/device.h)
--------------------------------------
Structure:
struct bus_attribute {
struct attribute attr;
ssize_t (*show)(struct bus_type *, char * buf, size_t count, loff_t off);
ssize_t (*store)(struct bus_type *, const char * buf, size_t count, loff_t off);
};
Declaring:
BUS_ATTR(_name,_str,_mode,_show,_store)
Creation/Removal:
int bus_create_file(struct bus_type *, struct bus_attribute *);
void bus_remove_file(struct bus_type *, struct bus_attribute *);
- device drivers (include/linux/device.h)
-----------------------------------------
Structure:
struct driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *, char * buf, size_t count, loff_t off);
ssize_t (*store)(struct device_driver *, const char * buf, size_t count, loff_t off);
};
Declaring:
DRIVER_ATTR(_name,_str,_mode,_show,_store)
Creation/Removal:
int driver_create_file(struct device_driver *, struct driver_attribute *);
void driver_remove_file(struct device_driver *, struct driver_attribute *);
Reading/Writing Data
~~~~~~~~~~~~~~~~~~~~
The callback functionality is similar to the way procfs works. When a
user performs a read(2) or write(2) on the file, it first calls a
driverfs function. This function then checks for a non-NULL pointer in
the file->private_data field, which it assumes to be a pointer to a
struct driver_file_entry.
driverfs function. This calls to the subsystem, which then calls to
the object's show() or store() function.
It then checks for the appropriate callback and calls it.
The buffer pointer, offset, and length should be passed to each
function. The downstream callback should fill the buffer and return
the number of bytes read/written.
What driverfs is not:
...
...
@@ -194,18 +313,24 @@ Limitations:
The driverfs functions assume that at most a page is being either read
or written each time.
There is a race condition that is really, really hard to fix; if not
impossible. There exists a race between a driverfs file being opened
and the object that owns the file going away. During the driverfs
open() callback, the reference count for the owning object needs to be
incremented.
For drivers, we can put a struct module * owner in struct driver_dir_entry
and do try_inc_mod_count() when we open a file. However, this won't
work for devices, that aren't tied to a module. And, it is still not
guaranteed to solve the race.
I'm looking into fixing this, but it may not be doable without making
a separate filesystem instance for each object. It's fun stuff. Please
mail me with creative ideas that you know will work.
Possible bugs:
~~~~~~~~~~~~~~
It may not deal with offsets and/or seeks very well, especially if
they cross a page boundary.
There may be locking issues when dynamically adding/removing files and
directories rapidly (like if you have a hot plug device).
There are some people that believe that filesystems which add
files/directories dynamically based on the presence of devices are
inherently flawed. Though not as technically versed in this area as
some of those people, I like to believe that they can be made to work,
with the right guidance.
drivers/base/fs/driver.c
View file @
75fc4b98
#include <linux/device.h>
#include <linux/module.h>
#include <linux/stat.h>
#include <linux/err.h>
#include "fs.h"
#define to_drv_attr(_attr) container_of(_attr,struct driver_attribute,attr)
#define to_drv(d) container_of(d, struct device_driver, dir)
/* driverfs ops for device attribute files */
static
int
drv_attr_open
(
struct
driver_dir_entry
*
dir
)
{
struct
device_driver
*
drv
=
to_drv
(
dir
);
get_driver
(
drv
);
return
0
;
}
static
int
drv_attr_close
(
struct
driver_dir_entry
*
dir
)
{
struct
device_driver
*
drv
=
to_drv
(
dir
);
put_driver
(
drv
);
return
0
;
}
static
ssize_t
drv_attr_show
(
struct
driver_dir_entry
*
dir
,
struct
attribute
*
attr
,
char
*
buf
,
size_t
count
,
loff_t
off
)
{
struct
driver_attribute
*
drv_attr
=
to_drv_attr
(
attr
);
struct
device_driver
*
drv
=
to_drv
(
dir
);
ssize_t
ret
=
0
;
if
(
drv_attr
->
show
)
ret
=
drv_attr
->
show
(
drv
,
buf
,
count
,
off
);
return
ret
;
}
static
ssize_t
drv_attr_store
(
struct
driver_dir_entry
*
dir
,
struct
attribute
*
attr
,
const
char
*
buf
,
size_t
count
,
loff_t
off
)
{
struct
driver_attribute
*
drv_attr
=
to_drv_attr
(
attr
);
struct
device_driver
*
drv
=
to_drv
(
dir
);
ssize_t
ret
=
0
;
if
(
drv_attr
->
store
)
ret
=
drv_attr
->
store
(
drv
,
buf
,
count
,
off
);
return
ret
;
}
static
struct
driverfs_ops
drv_attr_ops
=
{
open:
drv_attr_open
,
close:
drv_attr_close
,
show:
drv_attr_show
,
store:
drv_attr_store
,
};
int
driver_create_file
(
struct
device_driver
*
drv
,
struct
driver_attribute
*
attr
)
{
int
error
;
if
(
get_driver
(
drv
))
{
error
=
driverfs_create_file
(
&
attr
->
attr
,
&
drv
->
dir
);
put_driver
(
drv
);
}
else
error
=
-
EINVAL
;
return
error
;
}
void
driver_remove_file
(
struct
device_driver
*
drv
,
struct
driver_attribute
*
attr
)
{
if
(
get_driver
(
drv
))
{
driverfs_remove_file
(
&
drv
->
dir
,
attr
->
attr
.
name
);
put_driver
(
drv
);
}
}
/**
* driver_make_dir - create a driverfs directory for a driver
* @drv: driver in question
...
...
@@ -8,6 +86,8 @@
int
driver_make_dir
(
struct
device_driver
*
drv
)
{
drv
->
dir
.
name
=
drv
->
name
;
drv
->
dir
.
ops
=
&
drv_attr_ops
;
return
device_create_dir
(
&
drv
->
dir
,
&
drv
->
bus
->
driver_dir
);
}
...
...
@@ -16,3 +96,5 @@ void driver_remove_dir(struct device_driver * drv)
driverfs_remove_dir
(
&
drv
->
dir
);
}
EXPORT_SYMBOL
(
driver_create_file
);
EXPORT_SYMBOL
(
driver_remove_file
);
include/linux/device.h
View file @
75fc4b98
...
...
@@ -142,6 +142,24 @@ extern int driver_for_each_dev(struct device_driver * drv, void * data,
int
(
*
callback
)(
struct
device
*
dev
,
void
*
data
));
/* driverfs interface for exporting driver attributes */
struct
driver_attribute
{
struct
attribute
attr
;
ssize_t
(
*
show
)(
struct
device_driver
*
,
char
*
buf
,
size_t
count
,
loff_t
off
);
ssize_t
(
*
store
)(
struct
device_driver
*
,
const
char
*
buf
,
size_t
count
,
loff_t
off
);
};
#define DRIVER_ATTR(_name,_str,_mode,_show,_store) \
struct driver_attribute driver_attr_##_name = { \
.attr = {.name = _str, .mode = _mode }, \
.show = _show, \
.store = _store, \
};
extern
int
driver_create_file
(
struct
device_driver
*
,
struct
driver_attribute
*
);
extern
void
driver_remove_file
(
struct
device_driver
*
,
struct
driver_attribute
*
);
struct
device
{
struct
list_head
g_list
;
/* node in depth-first order list */
struct
list_head
node
;
/* node in sibling list */
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment