* Removed obsolete "ident" parameter from devfs_publish_device().

* driver_entry::api_version now stores the actual version instead of a
  pointer to it.
* Renamed node_path_entry to path_entry and reused it for driver 
  reloading: handle_driver_events() will now also check for drivers to
  add in the sDriversToAdd list.
* Added new devfs_driver_added(), and devfs_driver_removed() functions
  that trigger certain driver actions.
* Implemented notifying devfs on B_ENTRY_CREATED, B_ENTRY_REMOVED, and
  B_ENTRY_MOVED events in probe.cpp. The watched directory inode numbers
  are now stored in a hash for B_ENTRY_MOVED.
* unpublish_driver() did not actually delete the node, it only marked
  it removable since we never get/put the node. We now do, and so the
  node is actually removed as intended.
* Added "devfs_driver" KDL command.
* Minor cleanup.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24152 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2008-02-27 16:55:59 +00:00
parent 4f26630d82
commit a77ed12b25
4 changed files with 278 additions and 62 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
@ -22,6 +22,9 @@ void devfs_add_preloaded_drivers(struct kernel_args* args);
status_t devfs_add_driver(const char *path);
void devfs_driver_added(const char *path);
void devfs_driver_removed(const char *path);
status_t devfs_unpublish_file_device(const char *path);
status_t devfs_publish_file_device(const char *path, const char *filePath);
@ -29,7 +32,7 @@ status_t devfs_unpublish_partition(const char *path);
status_t devfs_publish_partition(const char *path, const partition_info *info);
status_t devfs_unpublish_device(const char *path, bool disconnect);
status_t devfs_publish_device(const char *path, void *ident, device_hooks *calls);
status_t devfs_publish_device(const char *path, device_hooks *calls);
status_t devfs_publish_directory(const char *path);
status_t devfs_rescan_driver(const char *driverName);

View File

@ -68,8 +68,8 @@ ps2_dev_publish(ps2_dev *dev)
dev->active = true;
status = devfs_publish_device(dev->name, NULL,
(atomic_get(&dev->flags) & PS2_FLAG_KEYB) ? &gKeyboardDeviceHooks : &gMouseDeviceHooks);
status = devfs_publish_device(dev->name, (atomic_get(&dev->flags)
& PS2_FLAG_KEYB) ? &gKeyboardDeviceHooks : &gMouseDeviceHooks);
INFO("ps2: devfs_publish_device %s, status = 0x%08lx\n", dev->name, status);
}

View File

@ -20,8 +20,10 @@
#include <fs/node_monitor.h>
#include <kmodule.h>
#include <Notifications.h>
#include <util/Stack.h>
#include <util/kernel_cpp.h>
#include <util/OpenHashTable.h>
#include <util/Stack.h>
#include <vfs.h>
#include <image.h>
#include <KernelExport.h>
@ -58,6 +60,30 @@ struct module_entry {
bool no_connection;
};
struct directory_node_entry {
HashTableLink<directory_node_entry> link;
ino_t node;
};
struct DirectoryNodeHashDefinition {
typedef ino_t *KeyType;
typedef directory_node_entry ValueType;
size_t HashKey(ino_t *key) const
{ return _Hash(*key); }
size_t Hash(directory_node_entry *entry) const
{ return _Hash(entry->node); }
bool Compare(ino_t *key, directory_node_entry *entry) const
{ return *key == entry->node; }
HashTableLink<directory_node_entry>* GetLink(directory_node_entry *entry) const
{ return &entry->link; }
uint32 _Hash(ino_t node) const
{ return (uint32)(node >> 32) + (uint32)node; }
};
typedef OpenHashTable<DirectoryNodeHashDefinition> DirectoryNodeHash;
// list of driver registration directories
const char *pnp_registration_dirs[2] = {
COMMON_DRIVER_REGISTRATION,
@ -112,6 +138,7 @@ class DirectoryWatcher : public NotificationListener {
static DirectoryWatcher sDirectoryWatcher;
static DirectoryNodeHash sDirectoryNodeHash;
static bool sWatching;
@ -153,7 +180,8 @@ DirectoryIterator::SetTo(const char *path, const char *subPath, bool recursive)
void
DirectoryIterator::SetTo(const char **paths, const char *subPath, bool recursive)
DirectoryIterator::SetTo(const char **paths, const char *subPath,
bool recursive)
{
Unset();
fRecursive = recursive;
@ -261,14 +289,46 @@ DirectoryWatcher::EventOccured(NotificationService& service,
const KMessage* event)
{
int32 opcode = event->GetInt32("opcode", -1);
/*
if (opcode == B_ENTRY_CREATED)
devfs_publish_directory();
*/
dprintf("DRIVER DIRECTORY EVENT: %ld\n", opcode);
dprintf(" device %ld\n", event->GetInt32("device", -1));
dprintf(" node %Ld\n", event->GetInt64("node", -1));
dprintf(" name %s\n", event->GetString("name", "<none>"));
dev_t device = event->GetInt32("device", -1);
ino_t directory = event->GetInt64("directory", -1);
const char *name = event->GetString("name", NULL);
if (opcode == B_ENTRY_MOVED) {
// Determine wether it's a move within, out of, or into one
// of our watched directories.
ino_t from = event->GetInt64("from directory", -1);
ino_t to = event->GetInt64("to directory", -1);
if (sDirectoryNodeHash.Lookup(&from) == NULL) {
directory = to;
opcode = B_ENTRY_CREATED;
} else if (sDirectoryNodeHash.Lookup(&to) == NULL) {
directory = from;
opcode = B_ENTRY_REMOVED;
} else {
// Move within, don't do anything for now
// TODO: adjust driver priority if necessary
return;
}
}
KPath path(B_PATH_NAME_LENGTH + 1);
if (path.InitCheck() != B_OK || vfs_entry_ref_to_path(device, directory,
name, path.LockBuffer(), path.BufferSize()) != B_OK)
return;
path.UnlockBuffer();
dprintf("driver \"%s\" %s\n", path.Leaf(),
opcode == B_ENTRY_CREATED ? "added" : "removed");
switch (opcode) {
case B_ENTRY_CREATED:
devfs_driver_added(path.Path());
break;
case B_ENTRY_REMOVED:
devfs_driver_removed(path.Path());
break;
}
}
@ -288,6 +348,12 @@ start_watching(const char *base, const char *sub)
add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_DIRECTORY,
sDirectoryWatcher);
directory_node_entry *entry = new(std::nothrow) directory_node_entry;
if (entry != NULL) {
entry->node = stat.st_ino;
sDirectoryNodeHash.Insert(entry);
}
}
@ -1300,6 +1366,13 @@ probe_for_driver_modules(const char *type)
add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_DIRECTORY,
sDirectoryWatcher);
directory_node_entry *entry
= new(std::nothrow) directory_node_entry;
if (entry != NULL) {
entry->node = stat.st_ino;
sDirectoryNodeHash.Insert(entry);
}
// We need to make sure that drivers in ie. "audio/raw/" can
// be found as well - therefore, we must make sure that "audio"
// exists on /dev.

View File

@ -135,16 +135,17 @@ struct driver_entry {
uint32 devices_published;
uint32 devices_used;
bool binary_updated;
int32 priority;
// driver image information
int32 *api_version;
int32 api_version;
device_hooks *(*find_device)(const char *);
const char **(*publish_devices)(void);
status_t (*uninit_driver)(void);
status_t (*uninit_hardware)(void);
};
struct node_path_entry : DoublyLinkedListLinkImpl<node_path_entry> {
struct path_entry : DoublyLinkedListLinkImpl<path_entry> {
char path[B_PATH_NAME_LENGTH];
};
@ -174,6 +175,7 @@ static struct devfs *sDeviceFileSystem = NULL;
static int32 sDefaultApiVersion = 1;
static DriverWatcher sDriverWatcher;
static int32 sDriverEvents;
static DoublyLinkedList<path_entry> sDriversToAdd;
// #pragma mark - driver private
@ -222,7 +224,7 @@ republish_driver(driver_entry *driver)
struct hash_iterator i;
hash_open(sDeviceFileSystem->vnode_hash, &i);
DoublyLinkedList<node_path_entry> currentNodes;
DoublyLinkedList<path_entry> currentNodes;
while (true) {
devfs_vnode *vnode = (devfs_vnode *)hash_next(
sDeviceFileSystem->vnode_hash, &i);
@ -231,16 +233,16 @@ republish_driver(driver_entry *driver)
if (S_ISCHR(vnode->stream.type)
&& vnode->stream.u.dev.driver == driver) {
node_path_entry *path = new(std::nothrow) node_path_entry;
if (!path) {
while ((path = currentNodes.RemoveHead()))
delete path;
path_entry *entry = new(std::nothrow) path_entry;
if (entry == NULL) {
while ((entry = currentNodes.RemoveHead()))
delete entry;
hash_close(sDeviceFileSystem->vnode_hash, &i, false);
return B_NO_MEMORY;
}
get_device_name(vnode, path->path, sizeof(path->path));
currentNodes.Add(path);
get_device_name(vnode, entry->path, sizeof(entry->path));
currentNodes.Add(entry);
}
}
hash_close(sDeviceFileSystem->vnode_hash, &i, false);
@ -251,7 +253,7 @@ republish_driver(driver_entry *driver)
int32 exported = 0;
for (; devicePaths != NULL && devicePaths[0]; devicePaths++) {
bool present = false;
node_path_entry *entry = currentNodes.Head();
path_entry *entry = currentNodes.Head();
while (entry) {
if (strncmp(entry->path, devicePaths[0], B_PATH_NAME_LENGTH) == 0) {
// this device was present before and still is -> no republish
@ -275,16 +277,16 @@ republish_driver(driver_entry *driver)
TRACE(("devfs: publishing new device \"%s\"\n", devicePaths[0]));
if (publish_device(sDeviceFileSystem, devicePaths[0], NULL, NULL,
driver, hooks, *driver->api_version) == B_OK)
driver, hooks, driver->api_version) == B_OK)
exported++;
}
// what's left in currentNodes was present but is not anymore -> unpublish
node_path_entry *entry = currentNodes.Head();
path_entry *entry = currentNodes.Head();
while (entry) {
TRACE(("devfs: unpublishing no more present \"%s\"\n", entry->path));
devfs_unpublish_device(entry->path, true);
node_path_entry *next = currentNodes.GetNext(entry);
path_entry *next = currentNodes.GetNext(entry);
currentNodes.Remove(entry);
delete entry;
entry = next;
@ -308,6 +310,8 @@ load_driver(driver_entry *driver)
int32 exported = 0;
status_t status;
driver->binary_updated = false;
// load the module
image_id image = driver->image;
if (image < 0) {
@ -318,24 +322,26 @@ load_driver(driver_entry *driver)
// For a valid device driver the following exports are required
driver->api_version = &sDefaultApiVersion;
int32 *apiVersion;
if (get_image_symbol(image, "api_version", B_SYMBOL_TYPE_DATA,
(void **)&driver->api_version) == B_OK) {
(void **)&apiVersion) == B_OK) {
#if B_CUR_DRIVER_API_VERSION != 2
// just in case someone decides to bump up the api version
#error Add checks here for new vs old api version!
#endif
if (*driver->api_version > B_CUR_DRIVER_API_VERSION) {
if (*apiVersion > B_CUR_DRIVER_API_VERSION) {
dprintf("devfs: \"%s\" api_version %ld not handled\n", driver->name,
*driver->api_version);
*apiVersion);
status = B_BAD_VALUE;
goto error1;
}
if (*driver->api_version < 1) {
if (*apiVersion < 1) {
dprintf("devfs: \"%s\" api_version invalid\n", driver->name);
status = B_BAD_VALUE;
goto error1;
}
driver->api_version = *apiVersion;
} else
dprintf("devfs: \"%s\" api_version missing\n", driver->name);
@ -418,6 +424,11 @@ unload_driver(driver_entry *driver)
unload_kernel_add_on(driver->image);
driver->image = -1;
driver->binary_updated = false;
driver->find_device = NULL;
driver->publish_devices = NULL;
driver->uninit_driver = NULL;
driver->uninit_hardware = NULL;
return B_OK;
}
@ -444,7 +455,13 @@ unpublish_driver(driver_entry *driver)
if (S_ISCHR(vnode->stream.type)
&& vnode->stream.u.dev.driver == driver) {
void *dummy;
get_vnode(sDeviceFileSystem->id, vnode->id, &dummy);
// We need to get/put the node, so that it is
// actually removed
unpublish_node(sDeviceFileSystem, vnode, S_IFCHR);
put_vnode(sDeviceFileSystem->id, vnode->id);
break;
}
}
@ -454,6 +471,20 @@ unpublish_driver(driver_entry *driver)
}
static int32
get_priority(const char *path)
{
// TODO: use find_directory()
const char *kPaths[] = {"/boot/beos", "/boot/common", "/boot/home", NULL};
for (int32 i = 0; kPaths[i] != NULL; i++) {
if (!strncmp(kPaths[i], path, strlen(kPaths[i])))
return i;
}
return -1;
}
static const char *
get_leaf(const char *path)
@ -502,6 +533,8 @@ add_driver(const char *path, image_id image)
return errno;
}
int32 priority = get_priority(path);
RecursiveLocker locker(&sDeviceFileSystem->lock);
driver_entry *driver = (driver_entry *)hash_lookup(
@ -510,6 +543,12 @@ add_driver(const char *path, image_id image)
// we know this driver
// TODO: check if this driver is a different one and has precendence
// (ie. common supersedes system).
//dprintf("new driver has priority %ld, old %ld\n", priority, driver->priority);
if (priority >= driver->priority) {
driver->binary_updated = true;
return B_OK;
}
// TODO: test for changes here and/or via node monitoring and reload
// the driver if necessary
if (driver->image < B_OK)
@ -538,8 +577,9 @@ add_driver(const char *path, image_id image)
driver->devices_published = 0;
driver->devices_used = 0;
driver->binary_updated = false;
driver->priority = priority;
driver->api_version = NULL;
driver->api_version = 1;
driver->find_device = NULL;
driver->publish_devices = NULL;
driver->uninit_driver = NULL;
@ -627,6 +667,15 @@ handle_driver_events(void *_fs, int /*iteration*/)
RecursiveLocker locker(fs->lock);
while (true) {
path_entry *path = sDriversToAdd.RemoveHead();
if (path == NULL)
break;
devfs_add_driver(path->path);
delete path;
}
hash_iterator iterator;
hash_open(sDeviceFileSystem->driver_hash, &iterator);
driver_entry *driver;
@ -675,7 +724,7 @@ DriverWatcher::EventOccured(NotificationService& service,
return;
driver->binary_updated = true;
dprintf("%s: devices published %ld, used %ld\n", driver->name, driver->devices_published, driver->devices_used);
//dprintf("%s: devices published %ld, used %ld\n", driver->name, driver->devices_published, driver->devices_used);
if (driver->devices_used == 0) {
// trigger a reload of the driver
atomic_add(&sDriverEvents, 1);
@ -1325,6 +1374,59 @@ dump_node(int argc, char **argv)
}
static int
dump_driver(int argc, char **argv)
{
if (argc < 2) {
// print list of all drivers
kprintf("address image used publ. pri name\n");
hash_iterator iterator;
hash_open(sDeviceFileSystem->driver_hash, &iterator);
while (true) {
driver_entry *driver = (driver_entry *)hash_next(
sDeviceFileSystem->driver_hash, &iterator);
if (driver == NULL)
break;
kprintf("%p %5ld %3ld %5ld %c %3ld %s\n", driver,
driver->image < 0 ? -1 : driver->image,
driver->devices_used, driver->devices_published,
driver->binary_updated ? 'U' : ' ', driver->priority,
driver->name);
}
hash_close(sDeviceFileSystem->driver_hash, &iterator, false);
return 0;
}
driver_entry *driver = (driver_entry *)hash_lookup(
sDeviceFileSystem->driver_hash, argv[1]);
if (driver == NULL) {
kprintf("Driver named \"%s\" not found.\n", argv[1]);
return 0;
}
kprintf("DEVFS DRIVER: %p\n", driver);
kprintf(" name: %s\n", driver->name);
kprintf(" path: %s\n", driver->path);
kprintf(" image: %ld\n", driver->image);
kprintf(" device: %ld\n", driver->device);
kprintf(" node: %Ld\n", driver->node);
kprintf(" last modified: %ld\n", driver->last_modified);
kprintf(" devs used: %ld\n", driver->devices_used);
kprintf(" devs published: %ld\n", driver->devices_published);
kprintf(" binary updated: %d\n", driver->binary_updated);
kprintf(" priority: %ld\n", driver->priority);
kprintf(" api version: %ld\n", driver->api_version);
kprintf(" hooks: find_device %p, publish_devices %p\n"
" uninit_driver %p, uninit_hardware %p\n",
driver->find_device, driver->publish_devices, driver->uninit_driver,
driver->uninit_hardware);
return 0;
}
// #pragma mark - file system interface
@ -1373,6 +1475,8 @@ devfs_mount(dev_t id, const char *devfs, uint32 flags, const char *args,
}
new(&sDriverWatcher) DriverWatcher;
new(&sDriversToAdd) DoublyLinkedList<path_entry>;
register_kernel_daemon(&handle_driver_events, fs, 10);
// once every second
@ -1424,6 +1528,16 @@ devfs_unmount(fs_volume _fs)
unregister_kernel_daemon(&handle_driver_events, fs);
recursive_lock_lock(&fs->lock);
while (true) {
path_entry *entry = sDriversToAdd.RemoveHead();
if (entry == NULL)
break;
delete entry;
}
// release the reference to the root
put_vnode(fs->id, fs->root_vnode->id);
@ -1454,7 +1568,8 @@ devfs_sync(fs_volume fs)
static status_t
devfs_lookup(fs_volume _fs, fs_vnode _dir, const char *name, ino_t *_id, int *_type)
devfs_lookup(fs_volume _fs, fs_vnode _dir, const char *name, ino_t *_id,
int *_type)
{
struct devfs *fs = (struct devfs *)_fs;
struct devfs_vnode *dir = (struct devfs_vnode *)_dir;
@ -1474,32 +1589,9 @@ devfs_lookup(fs_volume _fs, fs_vnode _dir, const char *name, ino_t *_id, int *_t
// look it up
vnode = devfs_find_in_dir(dir, name);
if (vnode == NULL) {
#if 0
// ToDo: with node monitoring in place, we *know* here that the file does not exist
// and don't have to scan for it again.
// scan for drivers in the given directory (that indicates the type of the driver)
KPath path;
if (path.InitCheck() != B_OK)
return B_NO_MEMORY;
get_device_name(dir, path.LockBuffer(), path.BufferSize());
path.UnlockBuffer();
path.Append(name);
dprintf("lookup: \"%s\"\n", path.Path());
// scan for drivers of this type
probe_for_device_type(path.Path());
vnode = devfs_find_in_dir(dir, name);
if (vnode == NULL) {
#endif
return B_ENTRY_NOT_FOUND;
#if 0
}
if (S_ISDIR(vnode->stream.type) && gBootDevice >= 0)
vnode->stream.u.dir.scanned = true;
#endif
// We don't have to rescan here, because thanks to node monitoring
// we already know it does not exist
return B_ENTRY_NOT_FOUND;
}
status = get_vnode(fs->id, vnode->id, (fs_vnode *)&vdummy);
@ -2403,10 +2495,13 @@ devfs_std_ops(int32 op, ...)
case B_MODULE_INIT:
add_debugger_command("devfs_node", &dump_node,
"info about a private devfs node");
add_debugger_command("devfs_driver", &dump_driver,
"info about a devfs driver entry");
return B_OK;
case B_MODULE_UNINIT:
remove_debugger_command("devfs_node", &dump_node);
remove_debugger_command("devfs_driver", &dump_driver);
return B_OK;
default:
@ -2683,6 +2778,51 @@ devfs_add_driver(const char *path)
}
extern "C" void
devfs_driver_added(const char *path)
{
int32 priority = get_priority(path);
RecursiveLocker locker(&sDeviceFileSystem->lock);
driver_entry *driver = (driver_entry *)hash_lookup(
sDeviceFileSystem->driver_hash, get_leaf(path));
if (driver == NULL) {
// Add the driver to our list
path_entry *entry = new(std::nothrow) path_entry;
if (entry == NULL)
return;
strlcpy(entry->path, path, sizeof(entry->path));
sDriversToAdd.Add(entry);
} else {
// Update the driver if it is affected by the new entry
if (priority < driver->priority)
return;
driver->binary_updated = true;
}
atomic_add(&sDriverEvents, 1);
}
extern "C" void
devfs_driver_removed(const char *path)
{
int32 priority = get_priority(path);
RecursiveLocker locker(&sDeviceFileSystem->lock);
driver_entry *driver = (driver_entry *)hash_lookup(
sDeviceFileSystem->driver_hash, get_leaf(path));
if (driver == NULL || priority < driver->priority)
return;
driver->binary_updated = true;
atomic_add(&sDriverEvents, 1);
}
extern "C" status_t
devfs_unpublish_file_device(const char *path)
{
@ -2773,7 +2913,7 @@ devfs_unpublish_device(const char *path, bool disconnect)
extern "C" status_t
devfs_publish_device(const char *path, void *obsolete, device_hooks *ops)
devfs_publish_device(const char *path, device_hooks *ops)
{
// post R5: assume version 2
return publish_device(sDeviceFileSystem, path, NULL, NULL, NULL, ops, 2);