diff --git a/headers/private/kernel/fs/devfs.h b/headers/private/kernel/fs/devfs.h index ab9f6486f9..fd8f2e7e0f 100644 --- a/headers/private/kernel/fs/devfs.h +++ b/headers/private/kernel/fs/devfs.h @@ -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); diff --git a/src/add-ons/kernel/bus_managers/ps2/ps2_dev.c b/src/add-ons/kernel/bus_managers/ps2/ps2_dev.c index 5d9f951090..b3d5e59372 100644 --- a/src/add-ons/kernel/bus_managers/ps2/ps2_dev.c +++ b/src/add-ons/kernel/bus_managers/ps2/ps2_dev.c @@ -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); } diff --git a/src/system/kernel/device_manager/probe.cpp b/src/system/kernel/device_manager/probe.cpp index 34bbed13d7..5b7c8fd5fe 100644 --- a/src/system/kernel/device_manager/probe.cpp +++ b/src/system/kernel/device_manager/probe.cpp @@ -20,8 +20,10 @@ #include #include #include -#include #include +#include +#include +#include #include #include @@ -58,6 +60,30 @@ struct module_entry { bool no_connection; }; +struct directory_node_entry { + HashTableLink 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* GetLink(directory_node_entry *entry) const + { return &entry->link; } + + uint32 _Hash(ino_t node) const + { return (uint32)(node >> 32) + (uint32)node; } +}; + +typedef OpenHashTable 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", "")); + 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. diff --git a/src/system/kernel/fs/devfs.cpp b/src/system/kernel/fs/devfs.cpp index e6cf12e822..85292d9382 100644 --- a/src/system/kernel/fs/devfs.cpp +++ b/src/system/kernel/fs/devfs.cpp @@ -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 { +struct path_entry : DoublyLinkedListLinkImpl { 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 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 currentNodes; + DoublyLinkedList 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; + 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);