* Started implementing node monitoring in the devfs and device manager.

* Right now, only already known loaded drivers will be monitored for changes;
  their devices aren't republished, though, since that would cause a deadlock
  in the node notification mechanism (listeners are called synchronously);
  need to offload that the event handling to another thread.
* On changes of (known) driver directories, the device manager will now print
  some info to the syslog.
* Fixed republish_driver() I broke recently (would skip every other node), and
  moved it to the driver functions section of the devfs.cpp.
* Implemented currently unused unpublish_driver() function that would have to
  be called before reloading a driver.
* If a driver is in use when it's updated, we mark it, but we don't do anything
  with that info when we could.
* Minor cleanup.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24036 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2008-02-20 16:55:05 +00:00
parent 9448bb6e0b
commit db55a020e6
3 changed files with 429 additions and 232 deletions

View File

@ -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

View File

@ -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 <boot_device.h>
#include <elf.h>
#include <kmodule.h>
#include <fs/KPath.h>
#include <fs/devfs.h>
#include <fs/KPath.h>
#include <fs/node_monitor.h>
#include <kmodule.h>
#include <Notifications.h>
#include <util/Stack.h>
#include <util/kernel_cpp.h>
#include <image.h>
#include <KernelExport.h>
#include <NodeMonitor.h>
#include <stdlib.h>
#include <dirent.h>
@ -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", "<none>"));
}
// #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 <path>, whereas <term_array> 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; <buffer> 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 <path>, whereas <term_array> 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; <buffer> 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 <directory>. If <tell_all> 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 <directory>. If <tell_all> 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 <dir>; 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 <dir>; 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 <buffer>!)
* 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 <buffer>!)
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);
}

View File

@ -7,34 +7,36 @@
*/
#include "IOScheduler.h"
#include <KernelExport.h>
#include <Drivers.h>
#include <pnp_devfs.h>
#include <NodeMonitor.h>
#include <boot_device.h>
#include <kdevice_manager.h>
#include <KPath.h>
#include <vfs.h>
#include <debug.h>
#include <util/khash.h>
#include <util/AutoLock.h>
#include <elf.h>
#include <lock.h>
#include <vm.h>
#include <arch/cpu.h>
#include <boot/kernel_args.h>
#include <devfs.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <Drivers.h>
#include <KernelExport.h>
#include <NodeMonitor.h>
#include <pnp_devfs.h>
#include <arch/cpu.h>
#include <boot/kernel_args.h>
#include <boot_device.h>
#include <debug.h>
#include <elf.h>
#include <kdevice_manager.h>
#include <KPath.h>
#include <lock.h>
#include <node_monitor.h>
#include <Notifications.h>
#include <util/AutoLock.h>
#include <util/khash.h>
#include <vfs.h>
#include <vm.h>
#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<node_path_entry> {
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<node_path_entry> 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<node_path_entry> 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;
}