diff --git a/src/system/kernel/device_manager/Jamfile b/src/system/kernel/device_manager/Jamfile index c2c5c4b48d..4797f31e6a 100644 --- a/src/system/kernel/device_manager/Jamfile +++ b/src/system/kernel/device_manager/Jamfile @@ -1,7 +1,7 @@ SubDir HAIKU_TOP src system kernel device_manager ; UsePrivateHeaders [ FDirName kernel boot platform $(TARGET_BOOT_PLATFORM) ] ; -UsePrivateHeaders [ FDirName kernel util ] ; +UsePrivateHeaders [ FDirName kernel util ] shared ; KernelMergeObject kernel_device_manager.o : attributes.c diff --git a/src/system/kernel/device_manager/probe.cpp b/src/system/kernel/device_manager/probe.cpp index 6f85a5d3d8..34bbed13d7 100644 --- a/src/system/kernel/device_manager/probe.cpp +++ b/src/system/kernel/device_manager/probe.cpp @@ -1,14 +1,12 @@ /* - * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * Copyright 2002-2004, Thomas Kurschel. All rights reserved. * * Distributed under the terms of the MIT License. */ /* - Part of Device Manager Probing for consumers. - Here is all the core logic how consumers are found for one node. */ @@ -17,14 +15,17 @@ #include #include -#include -#include #include +#include +#include +#include +#include #include #include #include #include +#include #include #include @@ -68,19 +69,22 @@ const char *pnp_registration_dirs[2] = { static const char *kModulePaths[] = { COMMON_MODULES_DIR, "/boot/beos/system/add-ons/kernel",//SYSTEM_MODULES_DIR, - "/boot", // ToDo: this is for the bootfs boot - to be removed NULL }; class DirectoryIterator { public: - DirectoryIterator(const char *path, const char *subPath = NULL, bool recursive = false); - DirectoryIterator(const char **paths, const char *subPath = NULL, bool recursive = false); + DirectoryIterator(const char *path, const char *subPath = NULL, + bool recursive = false); + DirectoryIterator(const char **paths, const char *subPath = NULL, + bool recursive = false); ~DirectoryIterator(); - void SetTo(const char *path, const char *subPath = NULL, bool recursive = false); - void SetTo(const char **paths, const char *subPath = NULL, bool recursive = false); + void SetTo(const char *path, const char *subPath = NULL, + bool recursive = false); + void SetTo(const char **paths, const char *subPath = NULL, + bool recursive = false); status_t GetNext(KPath &path, struct stat &stat); const char *CurrentName() const { return fCurrentName; } @@ -97,7 +101,22 @@ class DirectoryIterator { }; -DirectoryIterator::DirectoryIterator(const char *path, const char *subPath, bool recursive) +class DirectoryWatcher : public NotificationListener { + public: + DirectoryWatcher(); + virtual ~DirectoryWatcher(); + + virtual void EventOccured(NotificationService& service, + const KMessage* event); +}; + + +static DirectoryWatcher sDirectoryWatcher; +static bool sWatching; + + +DirectoryIterator::DirectoryIterator(const char *path, const char *subPath, + bool recursive) : fDirectory(NULL), fBasePath(NULL), @@ -107,7 +126,8 @@ DirectoryIterator::DirectoryIterator(const char *path, const char *subPath, bool } -DirectoryIterator::DirectoryIterator(const char **paths, const char *subPath, bool recursive) +DirectoryIterator::DirectoryIterator(const char **paths, const char *subPath, + bool recursive) : fDirectory(NULL), fBasePath(NULL) @@ -226,7 +246,52 @@ DirectoryIterator::AddPath(const char *basePath, const char *subPath) // #pragma mark - -struct path_entry * +DirectoryWatcher::DirectoryWatcher() +{ +} + + +DirectoryWatcher::~DirectoryWatcher() +{ +} + + +void +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", "")); +} + + +// #pragma mark - + + +static void +start_watching(const char *base, const char *sub) +{ + KPath path(base); + path.Append(sub); + + // TODO: create missing directories? + struct stat stat; + if (::stat(path.Path(), &stat) != 0) + return; + + add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_DIRECTORY, + sDirectoryWatcher); +} + + +static struct path_entry * new_path_entry(const char *path, dev_t device, ino_t node) { path_entry *entry = (path_entry *)malloc(sizeof(path_entry)); @@ -246,7 +311,7 @@ new_path_entry(const char *path, dev_t device, ino_t node) } -struct path_entry * +static struct path_entry * copy_path_entry(path_entry *entry) { path_entry *newEntry = (path_entry *)malloc(sizeof(struct path_entry)); @@ -264,7 +329,7 @@ copy_path_entry(path_entry *entry) } -void +static void free_module_entry(module_entry *entry) { if (entry->name != NULL && entry->driver != NULL) @@ -275,7 +340,7 @@ free_module_entry(module_entry *entry) } -struct module_entry * +static struct module_entry * new_module_entry(const char *name, driver_module_info *driver) { module_entry *entry = (module_entry *)malloc(sizeof(module_entry)); @@ -331,7 +396,8 @@ add_device_node(struct list *list, device_node_info *node) static status_t -get_next_device_node(struct list *list, uint32 *_cookie, device_node_info **_node) +get_next_device_node(struct list *list, uint32 *_cookie, + device_node_info **_node) { node_entry *entry = (node_entry *)list_get_next_item(list, (void *)*_cookie); if (entry == NULL) @@ -355,11 +421,10 @@ remove_device_nodes(struct list *list) } -/** notify a consumer that a device he might handle is added - * fileName - file name of consumer - * (moved to end of file to avoid inlining) - */ - +/*! Notify a consumer that a device he might handle is added + fileName - file name of consumer + (moved to end of file to avoid inlining) +*/ static status_t notify_probe_by_file(device_node_info *node, const char *fileName) { @@ -387,7 +452,9 @@ notify_probe_by_file(device_node_info *node, const char *fileName) TRACE(("notify_probe_by_file trying %s %s %s\n", module_name, bus, module_name + (strlen(module_name) - suffix_length + 1))); if (strlen(module_name) > suffix_length && !strncmp(module_name + (strlen(module_name) - suffix_length + 1), bus, strlen(bus)) - && !strncmp(module_name + (strlen(module_name) - sizeof(device_suffix) + 1), device_suffix, sizeof(device_suffix))) { + && !strncmp(module_name + (strlen(module_name) + - sizeof(device_suffix) + 1), device_suffix, + sizeof(device_suffix))) { // found the module res = dm_register_child_device(node, module_name, true); break; @@ -401,15 +468,14 @@ err: } -/** compose all possible names of Specific drivers; as there are - * multiple names which only differ in length, the most specific - * driver name gets stored in , whereas is a - * list of lengths of individual driver names with index 0 - * containing the length of the shortest and num_parts-1 the - * length of the longest name; is a supplied buffer of - * MAX_PATH+1 size - */ - +/*! Compose all possible names of Specific drivers; as there are + multiple names which only differ in length, the most specific + driver name gets stored in , whereas is a + list of lengths of individual driver names with index 0 + containing the length of the shortest and num_parts-1 the + length of the longest name; is a supplied buffer of + MAX_PATH+1 size +*/ static status_t compose_driver_names(device_node_info *node, const char *dir, const char *filename_pattern, int num_parts, @@ -454,12 +520,11 @@ err: } -/** notify all drivers under . If is true, notify all, - * if false, stop once a drivers notification function returned B_OK - * buffer - scratch buffer of size B_PATH_NAME_LENGTH + 1 ; destroyed on exit - * return: B_NAME_NOT_FOUND, if tell_all is false and no driver returned B_OK - */ - +/*! Notify all drivers under . If is true, notify all, + if false, stop once a drivers notification function returned B_OK + buffer - scratch buffer of size B_PATH_NAME_LENGTH + 1 ; destroyed on exit + return: B_NAME_NOT_FOUND, if tell_all is false and no driver returned B_OK +*/ static status_t try_drivers(device_node_info *node, char *directory, bool tell_all, char *buffer) @@ -511,12 +576,11 @@ try_drivers(device_node_info *node, char *directory, } -/** find normal child of node that are stored under ; first, we - * look for a specific driver; if none could be found, find a generic - * one path, buffer - used as scratch buffer (all of size B_PATH_NAME_LENGTH + 1) - * return: B_NAME_NOT_FOUND if no consumer could be found - */ - +/*! Find normal child of node that are stored under ; first, we + look for a specific driver; if none could be found, find a generic + one path, buffer - used as scratch buffer (all of size B_PATH_NAME_LENGTH + 1) + return: B_NAME_NOT_FOUND if no consumer could be found +*/ static status_t find_normal_child(device_node_info *node, const char *dir, const char *filename_pattern, int num_parts, @@ -584,16 +648,15 @@ find_normal_child(device_node_info *node, const char *dir, } -/** pre-process dynamic child name pattern. - * split into directory and pattern and count split positions; - * further, remove quotes from directory - * pattern - pattern of consumer name - * buffer - buffer to store results in - * (returned strings all point to !) - * filename_pattern - pattern of file name - * *num_parts - number of split positions - */ - +/*! Pre-process dynamic child name pattern. + split into directory and pattern and count split positions; + further, remove quotes from directory + pattern - pattern of consumer name + buffer - buffer to store results in + (returned strings all point to !) + filename_pattern - pattern of file name + *num_parts - number of split positions +*/ static status_t preprocess_child_names(const char *pattern, char *buffer, char **filename_pattern, int *const num_parts) @@ -660,11 +723,10 @@ preprocess_child_names(const char *pattern, char *buffer, } -/** find consumers for one given pattern - * has_normal_drivers - in: true - don't search for specific driver - * out: true - specific driver was found - */ - +/*! Find consumers for one given pattern + has_normal_drivers - in: true - don't search for specific driver + out: true - specific driver was found +*/ static status_t register_dynamic_child_device(device_node_info *node, const char *bus, const char *pattern, bool *has_normal_driver) @@ -738,14 +800,13 @@ find_node_ref_in_list(struct list *list, dev_t device, ino_t node) } -/** Iterates over the given list and tries to load all drivers and modules - * in that list. - * The list is emptied and freed during the traversal. - * - * ToDo: Old style drivers will be initialized as well, new style driver - * handling is not yet done. - */ +/*! Iterates over the given list and tries to load all drivers and modules + in that list. + The list is emptied and freed during the traversal. + ToDo: Old style drivers will be initialized as well, new style driver + handling is not yet done. +*/ static status_t try_drivers(struct list &list, bool tryBusDrivers) { @@ -960,7 +1021,8 @@ get_loaded_modules(struct list *modules, const char *bus, const char *device) static status_t -get_nodes_for_device_type(device_node_info *node, struct list *list, const char *type) +get_nodes_for_device_type(device_node_info *node, struct list *list, + const char *type) { bool matches = false; @@ -977,8 +1039,9 @@ get_nodes_for_device_type(device_node_info *node, struct list *list, const char if (!matches) { // we also accept any dump busses uint8 onDemand = false; - pnp_get_attr_uint8(node, B_DRIVER_FIND_DEVICES_ON_DEMAND, &onDemand, false); - + pnp_get_attr_uint8(node, B_DRIVER_FIND_DEVICES_ON_DEMAND, &onDemand, + false); + if (onDemand) matches = true; } @@ -1009,12 +1072,12 @@ get_nodes_for_device_type(device_node_info *node, struct list *list, const char // device manager private API -/** Register the device of a child for the \a node that might accept it. - * childName - module name of child/consumer - */ - +/*! Register the device of a child for the \a node that might accept it. + childName - module name of child/consumer +*/ status_t -dm_register_child_device(device_node_info *node, const char *childName, bool checkSupport) +dm_register_child_device(device_node_info *node, const char *childName, + bool checkSupport) { driver_module_info *child; status_t status; @@ -1057,11 +1120,10 @@ err: } -/** find and notify dynamic consumers that device was added - * errors returned by consumers aren't reported, only problems - * like malformed consumer patterns - */ - +/*! Find and notify dynamic consumers that device was added + errors returned by consumers aren't reported, only problems + like malformed consumer patterns +*/ status_t dm_register_dynamic_child_devices(device_node_info *node) { @@ -1143,11 +1205,10 @@ err: } -/** Register all fixed child devices of of the given \a node; in contrast - * to dynamic child devices, errors reported by fixed child devices are - * not ignored but returned, and regarded critical. - */ - +/*! Register all fixed child devices of of the given \a node; in contrast + to dynamic child devices, errors reported by fixed child devices are + not ignored but returned, and regarded critical. +*/ status_t dm_register_fixed_child_devices(device_node_info *node) { @@ -1236,9 +1297,13 @@ probe_for_driver_modules(const char *type) while (iterator.GetNext(path, stat) == B_OK) { if (S_ISDIR(stat.st_mode)) { + add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_DIRECTORY, + sDirectoryWatcher); + // 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. + size_t length = strlen("drivers/dev"); if (strncmp(type, "drivers/dev", length)) continue; @@ -1274,7 +1339,8 @@ probe_for_driver_modules(const char *type) dprintf("bus: %s\n", path.Leaf()); while (busIterator.GetNext(path, stat) == B_OK) { - path_entry *entry = find_node_ref_in_list(&drivers, stat.st_dev, stat.st_ino); + path_entry *entry = find_node_ref_in_list(&drivers, stat.st_dev, + stat.st_ino); if (entry == NULL) continue; @@ -1310,7 +1376,20 @@ probe_for_device_type(const char *type) TRACE(("probe_for_device_type(type = %s)\n", type)); char deviceType[64]; - snprintf(deviceType, sizeof(deviceType), "drivers/dev%s%s", type[0] ? "/" : "", type); + snprintf(deviceType, sizeof(deviceType), "drivers/dev%s%s", + type[0] ? "/" : "", type); + + if (!sWatching && gBootDevice > 0) { + // We're probing the actual boot volume for the first time, + // let's watch its driver directories for changes + + new(&sDirectoryWatcher) DirectoryWatcher; + for (uint32 i = 0; kModulePaths[i] != NULL; i++) { + start_watching(kModulePaths[i], "drivers/dev"); + start_watching(kModulePaths[i], "drivers/bin"); + } + sWatching = true; + } return probe_for_driver_modules(deviceType); } diff --git a/src/system/kernel/fs/devfs.cpp b/src/system/kernel/fs/devfs.cpp index 9a4388d957..6257f2f9f8 100644 --- a/src/system/kernel/fs/devfs.cpp +++ b/src/system/kernel/fs/devfs.cpp @@ -7,34 +7,36 @@ */ -#include "IOScheduler.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include -#include -#include -#include #include +#include +#include +#include #include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "IOScheduler.h" + //#define TRACE_DEVFS #ifdef TRACE_DEVFS @@ -131,6 +133,8 @@ struct driver_entry { time_t last_modified; image_id image; uint32 devices_published; + uint32 devices_used; + bool binary_updated; // driver image information int32 *api_version; @@ -140,14 +144,24 @@ struct driver_entry { status_t (*uninit_hardware)(void); }; - struct node_path_entry : DoublyLinkedListLinkImpl { char path[B_PATH_NAME_LENGTH]; }; +class DriverWatcher : public NotificationListener { + public: + DriverWatcher(); + virtual ~DriverWatcher(); + + virtual void EventOccured(NotificationService& service, + const KMessage* event); +}; + static void get_device_name(struct devfs_vnode *vnode, char *buffer, size_t size); +static status_t unpublish_node(struct devfs *fs, devfs_vnode *node, + mode_t type); static status_t publish_device(struct devfs *fs, const char *path, device_node_info *deviceNode, pnp_devfs_driver_info *info, driver_entry *driver, device_hooks *ops, int32 apiVersion); @@ -156,6 +170,7 @@ static status_t publish_device(struct devfs *fs, const char *path, /* the one and only allowed devfs instance */ static struct devfs *sDeviceFileSystem = NULL; static int32 sDefaultApiVersion = 1; +static DriverWatcher sDriverWatcher; // #pragma mark - driver private @@ -211,24 +226,25 @@ load_driver(driver_entry *driver) #error Add checks here for new vs old api version! #endif if (*driver->api_version > B_CUR_DRIVER_API_VERSION) { - dprintf("%s: api_version %ld not handled\n", driver->name, + dprintf("devfs: \"%s\" api_version %ld not handled\n", driver->name, *driver->api_version); status = B_BAD_VALUE; goto error1; } if (*driver->api_version < 1) { - dprintf("%s: api_version invalid\n", driver->name); + dprintf("devfs: \"%s\" api_version invalid\n", driver->name); status = B_BAD_VALUE; goto error1; } } else - dprintf("%s: api_version missing\n", driver->name); + dprintf("devfs: \"%s\" api_version missing\n", driver->name); if (get_image_symbol(image, "publish_devices", B_SYMBOL_TYPE_TEXT, (void **)&driver->publish_devices) != B_OK || get_image_symbol(image, "find_device", B_SYMBOL_TYPE_TEXT, (void **)&driver->find_device) != B_OK) { - dprintf("%s: mandatory driver symbol(s) missing!\n", driver->name); + dprintf("devfs: \"%s\" mandatory driver symbol(s) missing!\n", + driver->name); status = B_BAD_VALUE; goto error1; } @@ -238,8 +254,8 @@ load_driver(driver_entry *driver) if (get_image_symbol(image, "init_hardware", B_SYMBOL_TYPE_TEXT, (void **)&init_hardware) == B_OK && (status = init_hardware()) != B_OK) { - dprintf("%s: init_hardware() failed: %s\n", driver->name, - strerror(status)); + TRACE(("%s: init_hardware() failed: %s\n", driver->name, + strerror(status))); status = ENXIO; goto error1; } @@ -247,8 +263,8 @@ load_driver(driver_entry *driver) if (get_image_symbol(image, "init_driver", B_SYMBOL_TYPE_TEXT, (void **)&init_driver) == B_OK && (status = init_driver()) != B_OK) { - dprintf("%s: init_driver() failed: %s\n", driver->name, - strerror(status)); + TRACE(("%s: init_driver() failed: %s\n", driver->name, + strerror(status))); status = ENXIO; goto error2; } @@ -267,7 +283,7 @@ load_driver(driver_entry *driver) // we keep the driver loaded if it exports at least a single interface devicePaths = driver->publish_devices(); if (devicePaths == NULL) { - dprintf("%s: publish_devices() returned NULL.\n", driver->name); + TRACE(("%s: publish_devices() returned NULL.\n", driver->name)); status = ENXIO; goto error3; } @@ -327,6 +343,133 @@ unload_driver(driver_entry *driver) } +/*! Collects all devices belonging to the \a driver and unpublishs them. +*/ +static void +unpublish_driver(driver_entry *driver) +{ + RecursiveLocker locker(&sDeviceFileSystem->lock); + + // Iterate through all nodes until all devices of this driver have + // been unpublished + + while (driver->devices_published > 0) { + struct hash_iterator i; + hash_open(sDeviceFileSystem->vnode_hash, &i); + + while (true) { + devfs_vnode *vnode = (devfs_vnode *)hash_next( + sDeviceFileSystem->vnode_hash, &i); + if (vnode == NULL) + break; + + if (S_ISCHR(vnode->stream.type) + && vnode->stream.u.dev.driver == driver) + unpublish_node(sDeviceFileSystem, vnode, S_IFCHR); + } + + hash_close(sDeviceFileSystem->vnode_hash, &i, false); + } +} + + +/*! Collects all published devices of a driver, compares them to what the + driver would publish now, and then publishes/unpublishes the devices + as needed. + If the driver does not publish any devices anymore, it is unloaded. +*/ +static status_t +republish_driver(driver_entry *driver) +{ + if (driver->image < 0) { + // The driver is not yet loaded - go through the normal load procedure + return load_driver(driver); + } + + RecursiveLocker locker(&sDeviceFileSystem->lock); + + // build the list of currently present devices of this driver + // by iterating through all present nodes + struct hash_iterator i; + hash_open(sDeviceFileSystem->vnode_hash, &i); + + DoublyLinkedList currentNodes; + while (true) { + devfs_vnode *vnode = (devfs_vnode *)hash_next( + sDeviceFileSystem->vnode_hash, &i); + if (vnode == NULL) + break; + + 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; + hash_close(sDeviceFileSystem->vnode_hash, &i, false); + return B_NO_MEMORY; + } + + get_device_name(vnode, path->path, sizeof(path->path)); + currentNodes.Add(path); + } + } + hash_close(sDeviceFileSystem->vnode_hash, &i, false); + + // now ask the driver for it's currently published devices + const char **devicePaths = driver->publish_devices(); + + int32 exported = 0; + for (; devicePaths != NULL && devicePaths[0]; devicePaths++) { + bool present = false; + node_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 + currentNodes.Remove(entry); + delete entry; + exported++; + present = true; + break; + } + + entry = currentNodes.GetNext(entry); + } + + if (present) + continue; + + // the device was not present before -> publish it now + device_hooks *hooks = driver->find_device(devicePaths[0]); + if (hooks == NULL) + continue; + + TRACE(("devfs: publishing new device \"%s\"\n", devicePaths[0])); + if (publish_device(sDeviceFileSystem, devicePaths[0], NULL, NULL, + 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(); + 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); + currentNodes.Remove(entry); + delete entry; + entry = next; + } + + if (exported == 0) { + TRACE(("devfs: driver \"%s\" does not publish any more nodes and is unloaded\n", driver->path)); + unload_driver(driver); + } + + return B_OK; +} + + static const char * get_leaf(const char *path) { @@ -338,6 +481,25 @@ get_leaf(const char *path) } +static driver_entry * +find_driver(dev_t device, ino_t node) +{ + hash_iterator iterator; + hash_open(sDeviceFileSystem->driver_hash, &iterator); + driver_entry *driver; + while (true) { + driver = (driver_entry *)hash_next(sDeviceFileSystem->driver_hash, + &iterator); + if (driver == NULL + || driver->device == device && driver->node == node) + break; + } + + hash_close(sDeviceFileSystem->driver_hash, &iterator, false); + return driver; +} + + static status_t add_driver(const char *path, image_id image) { @@ -361,6 +523,8 @@ add_driver(const char *path, image_id image) sDeviceFileSystem->driver_hash, get_leaf(path)); if (driver != NULL) { // we know this driver + // TODO: check if this driver is a different one and has precendence + // (ie. common supersedes system). // TODO: test for changes here and/or via node monitoring and reload // the driver if necessary if (driver->image < B_OK) @@ -386,6 +550,9 @@ add_driver(const char *path, image_id image) driver->node = stat.st_ino; driver->image = image; driver->last_modified = stat.st_mtime; + driver->devices_published = 0; + driver->devices_used = 0; + driver->binary_updated = false; driver->api_version = NULL; driver->find_device = NULL; @@ -394,6 +561,8 @@ add_driver(const char *path, image_id image) driver->uninit_hardware = NULL; hash_insert(sDeviceFileSystem->driver_hash, driver); + if (stat.st_dev > 0) + add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_STAT, sDriverWatcher); // Even if loading the driver fails - its entry will stay with us // so that we don't have to go through it again @@ -446,6 +615,51 @@ scan_for_drivers(devfs_vnode *dir) } +// #pragma mark - DriverWatcher + + +DriverWatcher::DriverWatcher() +{ +} + + +DriverWatcher::~DriverWatcher() +{ +} + + +void +DriverWatcher::EventOccured(NotificationService& service, + const KMessage* event) +{ + if (event->GetInt32("opcode", -1) != B_STAT_CHANGED + || (event->GetInt32("fields", 0) & B_STAT_MODIFICATION_TIME) == 0) + return; + + RecursiveLocker locker(&sDeviceFileSystem->lock); + + driver_entry *driver = find_driver(event->GetInt32("device", -1), + event->GetInt64("node", 0)); + if (driver == NULL) + return; + + if (driver->devices_used == 0) { + // reload driver + dprintf("devfs: reload driver \"%s\"\n", driver->name); + //unpublish_driver(driver); + // TODO: even though we would need to unpublish the driver, this + // doesn't work right now from here (dead lock in the node monitor + // notifications) + unload_driver(driver); + load_driver(driver); + } else { + // driver is in use right now, only mark it to be updated when possible + dprintf("devfs: changed driver \"%s\" is still in use\n", driver->name); + driver->binary_updated = true; + } +} + + // #pragma mark - devfs private @@ -1028,105 +1242,6 @@ publish_device(struct devfs *fs, const char *path, device_node_info *deviceNode, } -/*! Collects all published devices of a driver, compares them to what the - driver would publish now, and then publishes/unpublishes the devices - as needed. - If the driver does not publish any devices anymore, it is unloaded. -*/ -static status_t -republish_driver(driver_entry *driver) -{ - if (driver->image < 0) { - // The driver is not yet loaded - go through the normal load procedure - return load_driver(driver); - } - - RecursiveLocker locker(&sDeviceFileSystem->lock); - - // build the list of currently present devices of this driver - // by iterating through all present nodes - struct hash_iterator i; - hash_open(sDeviceFileSystem->vnode_hash, &i); - - DoublyLinkedList currentNodes; - while (true) { - devfs_vnode *vnode = (devfs_vnode *)hash_next( - sDeviceFileSystem->vnode_hash, &i); - if (vnode == NULL) - break; - - 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; - hash_close(sDeviceFileSystem->vnode_hash, &i, false); - return B_NO_MEMORY; - } - - get_device_name(vnode, path->path, sizeof(path->path)); - currentNodes.Add(path); - } - - vnode = (devfs_vnode *)hash_next(sDeviceFileSystem->vnode_hash, &i); - } - hash_close(sDeviceFileSystem->vnode_hash, &i, false); - - // now ask the driver for it's currently published devices - const char **devicePaths = driver->publish_devices(); - - int32 exported = 0; - for (; devicePaths != NULL && devicePaths[0]; devicePaths++) { - bool present = false; - node_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 - currentNodes.Remove(entry); - delete entry; - exported++; - present = true; - break; - } - - entry = currentNodes.GetNext(entry); - } - - if (present) - continue; - - // the device was not present before -> publish it now - device_hooks *hooks = driver->find_device(devicePaths[0]); - if (hooks == NULL) - continue; - - TRACE(("devfs: publishing new device \"%s\"\n", devicePaths[0])); - if (publish_device(sDeviceFileSystem, devicePaths[0], NULL, NULL, - 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(); - 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); - currentNodes.Remove(entry); - delete entry; - entry = next; - } - - if (exported == 0) { - TRACE(("devfs: driver \"%s\" does not publish any more nodes and is unloaded\n", driver->path)); - unload_driver(driver); - } - - return B_OK; -} - - /** Construct complete device name (as used for device_open()). * This is safe to use only when the device is in use (and therefore * cannot be unpublished during the iteration). @@ -1200,7 +1315,7 @@ devfs_mount(dev_t id, const char *devfs, uint32 flags, const char *args, TRACE(("devfs_mount: entry\n")); if (sDeviceFileSystem) { - dprintf("double mount of devfs attempted\n"); + TRACE(("double mount of devfs attempted\n")); err = B_ERROR; goto err; } @@ -1233,6 +1348,8 @@ devfs_mount(dev_t id, const char *devfs, uint32 flags, const char *args, goto err3; } + new(&sDriverWatcher) DriverWatcher; + // create a vnode vnode = devfs_create_vnode(fs, NULL, ""); if (vnode == NULL) { @@ -1384,23 +1501,20 @@ static status_t devfs_get_vnode(fs_volume _fs, ino_t id, fs_vnode *_vnode, bool reenter) { struct devfs *fs = (struct devfs *)_fs; - struct devfs_vnode *vnode; TRACE(("devfs_get_vnode: asking for vnode id = %Ld, vnode = %p, r %d\n", id, _vnode, reenter)); - if (!reenter) - recursive_lock_lock(&fs->lock); - - vnode = (devfs_vnode *)hash_lookup(fs->vnode_hash, &id); - - if (!reenter) - recursive_lock_unlock(&fs->lock); - - TRACE(("devfs_get_vnode: looked it up at %p\n", *_vnode)); + RecursiveLocker _(fs->lock); + struct devfs_vnode *vnode = (devfs_vnode *)hash_lookup(fs->vnode_hash, &id); if (vnode == NULL) return B_ENTRY_NOT_FOUND; + if (S_ISCHR(vnode->stream.type) && vnode->stream.u.dev.driver != NULL) + vnode->stream.u.dev.driver->devices_used++; + + TRACE(("devfs_get_vnode: looked it up at %p\n", *_vnode)); + *_vnode = vnode; return B_OK; } @@ -1409,11 +1523,15 @@ devfs_get_vnode(fs_volume _fs, ino_t id, fs_vnode *_vnode, bool reenter) static status_t devfs_put_vnode(fs_volume _fs, fs_vnode _v, bool reenter) { -#ifdef TRACE_DEVFS + struct devfs *fs = (struct devfs *)_fs; struct devfs_vnode *vnode = (struct devfs_vnode *)_v; TRACE(("devfs_put_vnode: entry on vnode %p, id = %Ld, reenter %d\n", vnode, vnode->id, reenter)); -#endif + + RecursiveLocker _(fs->lock); + + if (S_ISCHR(vnode->stream.type) && vnode->stream.u.dev.driver != NULL) + vnode->stream.u.dev.driver->devices_used++; return B_OK; }