* 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
+1 -1
View File
@@ -1,7 +1,7 @@
SubDir HAIKU_TOP src system kernel device_manager ; SubDir HAIKU_TOP src system kernel device_manager ;
UsePrivateHeaders [ FDirName kernel boot platform $(TARGET_BOOT_PLATFORM) ] ; UsePrivateHeaders [ FDirName kernel boot platform $(TARGET_BOOT_PLATFORM) ] ;
UsePrivateHeaders [ FDirName kernel util ] ; UsePrivateHeaders [ FDirName kernel util ] shared ;
KernelMergeObject kernel_device_manager.o : KernelMergeObject kernel_device_manager.o :
attributes.c attributes.c
+164 -85
View File
@@ -1,14 +1,12 @@
/* /*
* Copyright 2004-2005, Axel Dörfler, [email protected]. All rights reserved. * Copyright 2004-2008, Axel Dörfler, [email protected]. All rights reserved.
* Copyright 2002-2004, Thomas Kurschel. All rights reserved. * Copyright 2002-2004, Thomas Kurschel. All rights reserved.
* *
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
/* /*
Part of Device Manager
Probing for consumers. Probing for consumers.
Here is all the core logic how consumers are found for one node. Here is all the core logic how consumers are found for one node.
*/ */
@@ -17,14 +15,17 @@
#include <boot_device.h> #include <boot_device.h>
#include <elf.h> #include <elf.h>
#include <kmodule.h>
#include <fs/KPath.h>
#include <fs/devfs.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/Stack.h>
#include <util/kernel_cpp.h> #include <util/kernel_cpp.h>
#include <image.h> #include <image.h>
#include <KernelExport.h> #include <KernelExport.h>
#include <NodeMonitor.h>
#include <stdlib.h> #include <stdlib.h>
#include <dirent.h> #include <dirent.h>
@@ -68,19 +69,22 @@ const char *pnp_registration_dirs[2] = {
static const char *kModulePaths[] = { static const char *kModulePaths[] = {
COMMON_MODULES_DIR, COMMON_MODULES_DIR,
"/boot/beos/system/add-ons/kernel",//SYSTEM_MODULES_DIR, "/boot/beos/system/add-ons/kernel",//SYSTEM_MODULES_DIR,
"/boot", // ToDo: this is for the bootfs boot - to be removed
NULL NULL
}; };
class DirectoryIterator { class DirectoryIterator {
public: public:
DirectoryIterator(const char *path, const char *subPath = NULL, bool recursive = false); DirectoryIterator(const char *path, const char *subPath = NULL,
DirectoryIterator(const char **paths, const char *subPath = NULL, bool recursive = false); bool recursive = false);
DirectoryIterator(const char **paths, const char *subPath = NULL,
bool recursive = false);
~DirectoryIterator(); ~DirectoryIterator();
void SetTo(const char *path, const char *subPath = NULL, bool recursive = false); void SetTo(const char *path, const char *subPath = NULL,
void SetTo(const char **paths, const char *subPath = NULL, bool recursive = false); bool recursive = false);
void SetTo(const char **paths, const char *subPath = NULL,
bool recursive = false);
status_t GetNext(KPath &path, struct stat &stat); status_t GetNext(KPath &path, struct stat &stat);
const char *CurrentName() const { return fCurrentName; } 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), fDirectory(NULL),
fBasePath(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), fDirectory(NULL),
fBasePath(NULL) fBasePath(NULL)
@@ -226,7 +246,52 @@ DirectoryIterator::AddPath(const char *basePath, const char *subPath)
// #pragma mark - // #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) new_path_entry(const char *path, dev_t device, ino_t node)
{ {
path_entry *entry = (path_entry *)malloc(sizeof(path_entry)); 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) copy_path_entry(path_entry *entry)
{ {
path_entry *newEntry = (path_entry *)malloc(sizeof(struct path_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) free_module_entry(module_entry *entry)
{ {
if (entry->name != NULL && entry->driver != NULL) 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) new_module_entry(const char *name, driver_module_info *driver)
{ {
module_entry *entry = (module_entry *)malloc(sizeof(module_entry)); 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 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); node_entry *entry = (node_entry *)list_get_next_item(list, (void *)*_cookie);
if (entry == NULL) if (entry == NULL)
@@ -355,11 +421,10 @@ remove_device_nodes(struct list *list)
} }
/** notify a consumer that a device he might handle is added /*! Notify a consumer that a device he might handle is added
* fileName - file name of consumer fileName - file name of consumer
* (moved to end of file to avoid inlining) (moved to end of file to avoid inlining)
*/ */
static status_t static status_t
notify_probe_by_file(device_node_info *node, const char *fileName) 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))); 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 if (strlen(module_name) > suffix_length
&& !strncmp(module_name + (strlen(module_name) - suffix_length + 1), bus, strlen(bus)) && !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 // found the module
res = dm_register_child_device(node, module_name, true); res = dm_register_child_device(node, module_name, true);
break; break;
@@ -401,15 +468,14 @@ err:
} }
/** compose all possible names of Specific drivers; as there are /*! Compose all possible names of Specific drivers; as there are
* multiple names which only differ in length, the most specific multiple names which only differ in length, the most specific
* driver name gets stored in <path>, whereas <term_array> is a driver name gets stored in <path>, whereas <term_array> is a
* list of lengths of individual driver names with index 0 list of lengths of individual driver names with index 0
* containing the length of the shortest and num_parts-1 the containing the length of the shortest and num_parts-1 the
* length of the longest name; <buffer> is a supplied buffer of length of the longest name; <buffer> is a supplied buffer of
* MAX_PATH+1 size MAX_PATH+1 size
*/ */
static status_t static status_t
compose_driver_names(device_node_info *node, const char *dir, compose_driver_names(device_node_info *node, const char *dir,
const char *filename_pattern, int num_parts, const char *filename_pattern, int num_parts,
@@ -454,12 +520,11 @@ err:
} }
/** notify all drivers under <directory>. If <tell_all> is true, notify all, /*! Notify all drivers under <directory>. If <tell_all> is true, notify all,
* if false, stop once a drivers notification function returned B_OK if false, stop once a drivers notification function returned B_OK
* buffer - scratch buffer of size B_PATH_NAME_LENGTH + 1 ; destroyed on exit 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 return: B_NAME_NOT_FOUND, if tell_all is false and no driver returned B_OK
*/ */
static status_t static status_t
try_drivers(device_node_info *node, char *directory, try_drivers(device_node_info *node, char *directory,
bool tell_all, char *buffer) 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 /*! 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 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) 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 return: B_NAME_NOT_FOUND if no consumer could be found
*/ */
static status_t static status_t
find_normal_child(device_node_info *node, const char *dir, find_normal_child(device_node_info *node, const char *dir,
const char *filename_pattern, int num_parts, 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. /*! Pre-process dynamic child name pattern.
* split into directory and pattern and count split positions; split into directory and pattern and count split positions;
* further, remove quotes from directory further, remove quotes from directory
* pattern - pattern of consumer name pattern - pattern of consumer name
* buffer - buffer to store results in buffer - buffer to store results in
* (returned strings all point to <buffer>!) (returned strings all point to <buffer>!)
* filename_pattern - pattern of file name filename_pattern - pattern of file name
* *num_parts - number of split positions *num_parts - number of split positions
*/ */
static status_t static status_t
preprocess_child_names(const char *pattern, char *buffer, preprocess_child_names(const char *pattern, char *buffer,
char **filename_pattern, int *const num_parts) 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 /*! Find consumers for one given pattern
* has_normal_drivers - in: true - don't search for specific driver has_normal_drivers - in: true - don't search for specific driver
* out: true - specific driver was found out: true - specific driver was found
*/ */
static status_t static status_t
register_dynamic_child_device(device_node_info *node, const char *bus, register_dynamic_child_device(device_node_info *node, const char *bus,
const char *pattern, bool *has_normal_driver) 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 /*! Iterates over the given list and tries to load all drivers and modules
* in that list. in that list.
* The list is emptied and freed during the traversal. 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.
*/
ToDo: Old style drivers will be initialized as well, new style driver
handling is not yet done.
*/
static status_t static status_t
try_drivers(struct list &list, bool tryBusDrivers) 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 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; bool matches = false;
@@ -977,7 +1039,8 @@ get_nodes_for_device_type(device_node_info *node, struct list *list, const char
if (!matches) { if (!matches) {
// we also accept any dump busses // we also accept any dump busses
uint8 onDemand = false; 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) if (onDemand)
matches = true; matches = true;
@@ -1009,12 +1072,12 @@ get_nodes_for_device_type(device_node_info *node, struct list *list, const char
// device manager private API // device manager private API
/** Register the device of a child for the \a node that might accept it. /*! Register the device of a child for the \a node that might accept it.
* childName - module name of child/consumer childName - module name of child/consumer
*/ */
status_t 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; driver_module_info *child;
status_t status; status_t status;
@@ -1057,11 +1120,10 @@ err:
} }
/** find and notify dynamic consumers that device was added /*! Find and notify dynamic consumers that device was added
* errors returned by consumers aren't reported, only problems errors returned by consumers aren't reported, only problems
* like malformed consumer patterns like malformed consumer patterns
*/ */
status_t status_t
dm_register_dynamic_child_devices(device_node_info *node) 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 /*! Register all fixed child devices of of the given \a node; in contrast
* to dynamic child devices, errors reported by fixed child devices are to dynamic child devices, errors reported by fixed child devices are
* not ignored but returned, and regarded critical. not ignored but returned, and regarded critical.
*/ */
status_t status_t
dm_register_fixed_child_devices(device_node_info *node) 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) { while (iterator.GetNext(path, stat) == B_OK) {
if (S_ISDIR(stat.st_mode)) { 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 // We need to make sure that drivers in ie. "audio/raw/" can
// be found as well - therefore, we must make sure that "audio" // be found as well - therefore, we must make sure that "audio"
// exists on /dev. // exists on /dev.
size_t length = strlen("drivers/dev"); size_t length = strlen("drivers/dev");
if (strncmp(type, "drivers/dev", length)) if (strncmp(type, "drivers/dev", length))
continue; continue;
@@ -1274,7 +1339,8 @@ probe_for_driver_modules(const char *type)
dprintf("bus: %s\n", path.Leaf()); dprintf("bus: %s\n", path.Leaf());
while (busIterator.GetNext(path, stat) == B_OK) { 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) if (entry == NULL)
continue; continue;
@@ -1310,7 +1376,20 @@ probe_for_device_type(const char *type)
TRACE(("probe_for_device_type(type = %s)\n", type)); TRACE(("probe_for_device_type(type = %s)\n", type));
char deviceType[64]; 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); return probe_for_driver_modules(deviceType);
} }
+263 -145
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 <devfs.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h> #include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.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 //#define TRACE_DEVFS
#ifdef TRACE_DEVFS #ifdef TRACE_DEVFS
@@ -131,6 +133,8 @@ struct driver_entry {
time_t last_modified; time_t last_modified;
image_id image; image_id image;
uint32 devices_published; uint32 devices_published;
uint32 devices_used;
bool binary_updated;
// driver image information // driver image information
int32 *api_version; int32 *api_version;
@@ -140,14 +144,24 @@ struct driver_entry {
status_t (*uninit_hardware)(void); status_t (*uninit_hardware)(void);
}; };
struct node_path_entry : DoublyLinkedListLinkImpl<node_path_entry> { struct node_path_entry : DoublyLinkedListLinkImpl<node_path_entry> {
char path[B_PATH_NAME_LENGTH]; 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, static void get_device_name(struct devfs_vnode *vnode, char *buffer,
size_t size); 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, static status_t publish_device(struct devfs *fs, const char *path,
device_node_info *deviceNode, pnp_devfs_driver_info *info, device_node_info *deviceNode, pnp_devfs_driver_info *info,
driver_entry *driver, device_hooks *ops, int32 apiVersion); 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 */ /* the one and only allowed devfs instance */
static struct devfs *sDeviceFileSystem = NULL; static struct devfs *sDeviceFileSystem = NULL;
static int32 sDefaultApiVersion = 1; static int32 sDefaultApiVersion = 1;
static DriverWatcher sDriverWatcher;
// #pragma mark - driver private // #pragma mark - driver private
@@ -211,24 +226,25 @@ load_driver(driver_entry *driver)
#error Add checks here for new vs old api version! #error Add checks here for new vs old api version!
#endif #endif
if (*driver->api_version > B_CUR_DRIVER_API_VERSION) { 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); *driver->api_version);
status = B_BAD_VALUE; status = B_BAD_VALUE;
goto error1; goto error1;
} }
if (*driver->api_version < 1) { 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; status = B_BAD_VALUE;
goto error1; goto error1;
} }
} else } 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, if (get_image_symbol(image, "publish_devices", B_SYMBOL_TYPE_TEXT,
(void **)&driver->publish_devices) != B_OK (void **)&driver->publish_devices) != B_OK
|| get_image_symbol(image, "find_device", B_SYMBOL_TYPE_TEXT, || get_image_symbol(image, "find_device", B_SYMBOL_TYPE_TEXT,
(void **)&driver->find_device) != B_OK) { (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; status = B_BAD_VALUE;
goto error1; goto error1;
} }
@@ -238,8 +254,8 @@ load_driver(driver_entry *driver)
if (get_image_symbol(image, "init_hardware", B_SYMBOL_TYPE_TEXT, if (get_image_symbol(image, "init_hardware", B_SYMBOL_TYPE_TEXT,
(void **)&init_hardware) == B_OK (void **)&init_hardware) == B_OK
&& (status = init_hardware()) != B_OK) { && (status = init_hardware()) != B_OK) {
dprintf("%s: init_hardware() failed: %s\n", driver->name, TRACE(("%s: init_hardware() failed: %s\n", driver->name,
strerror(status)); strerror(status)));
status = ENXIO; status = ENXIO;
goto error1; goto error1;
} }
@@ -247,8 +263,8 @@ load_driver(driver_entry *driver)
if (get_image_symbol(image, "init_driver", B_SYMBOL_TYPE_TEXT, if (get_image_symbol(image, "init_driver", B_SYMBOL_TYPE_TEXT,
(void **)&init_driver) == B_OK (void **)&init_driver) == B_OK
&& (status = init_driver()) != B_OK) { && (status = init_driver()) != B_OK) {
dprintf("%s: init_driver() failed: %s\n", driver->name, TRACE(("%s: init_driver() failed: %s\n", driver->name,
strerror(status)); strerror(status)));
status = ENXIO; status = ENXIO;
goto error2; goto error2;
} }
@@ -267,7 +283,7 @@ load_driver(driver_entry *driver)
// we keep the driver loaded if it exports at least a single interface // we keep the driver loaded if it exports at least a single interface
devicePaths = driver->publish_devices(); devicePaths = driver->publish_devices();
if (devicePaths == NULL) { if (devicePaths == NULL) {
dprintf("%s: publish_devices() returned NULL.\n", driver->name); TRACE(("%s: publish_devices() returned NULL.\n", driver->name));
status = ENXIO; status = ENXIO;
goto error3; 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 * static const char *
get_leaf(const char *path) 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 static status_t
add_driver(const char *path, image_id image) 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)); sDeviceFileSystem->driver_hash, get_leaf(path));
if (driver != NULL) { if (driver != NULL) {
// we know this driver // 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 // TODO: test for changes here and/or via node monitoring and reload
// the driver if necessary // the driver if necessary
if (driver->image < B_OK) if (driver->image < B_OK)
@@ -386,6 +550,9 @@ add_driver(const char *path, image_id image)
driver->node = stat.st_ino; driver->node = stat.st_ino;
driver->image = image; driver->image = image;
driver->last_modified = stat.st_mtime; driver->last_modified = stat.st_mtime;
driver->devices_published = 0;
driver->devices_used = 0;
driver->binary_updated = false;
driver->api_version = NULL; driver->api_version = NULL;
driver->find_device = NULL; driver->find_device = NULL;
@@ -394,6 +561,8 @@ add_driver(const char *path, image_id image)
driver->uninit_hardware = NULL; driver->uninit_hardware = NULL;
hash_insert(sDeviceFileSystem->driver_hash, driver); 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 // Even if loading the driver fails - its entry will stay with us
// so that we don't have to go through it again // 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 // #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()). /** Construct complete device name (as used for device_open()).
* This is safe to use only when the device is in use (and therefore * This is safe to use only when the device is in use (and therefore
* cannot be unpublished during the iteration). * 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")); TRACE(("devfs_mount: entry\n"));
if (sDeviceFileSystem) { if (sDeviceFileSystem) {
dprintf("double mount of devfs attempted\n"); TRACE(("double mount of devfs attempted\n"));
err = B_ERROR; err = B_ERROR;
goto err; goto err;
} }
@@ -1233,6 +1348,8 @@ devfs_mount(dev_t id, const char *devfs, uint32 flags, const char *args,
goto err3; goto err3;
} }
new(&sDriverWatcher) DriverWatcher;
// create a vnode // create a vnode
vnode = devfs_create_vnode(fs, NULL, ""); vnode = devfs_create_vnode(fs, NULL, "");
if (vnode == 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) devfs_get_vnode(fs_volume _fs, ino_t id, fs_vnode *_vnode, bool reenter)
{ {
struct devfs *fs = (struct devfs *)_fs; 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)); TRACE(("devfs_get_vnode: asking for vnode id = %Ld, vnode = %p, r %d\n", id, _vnode, reenter));
if (!reenter) RecursiveLocker _(fs->lock);
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));
struct devfs_vnode *vnode = (devfs_vnode *)hash_lookup(fs->vnode_hash, &id);
if (vnode == NULL) if (vnode == NULL)
return B_ENTRY_NOT_FOUND; 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; *_vnode = vnode;
return B_OK; return B_OK;
} }
@@ -1409,11 +1523,15 @@ devfs_get_vnode(fs_volume _fs, ino_t id, fs_vnode *_vnode, bool reenter)
static status_t static status_t
devfs_put_vnode(fs_volume _fs, fs_vnode _v, bool reenter) 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; 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)); 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; return B_OK;
} }