* Restructured the notification driven addition/removal of drivers to
driver_events (ie. there is now only a single list to walk). * Also, the DriverWatcher is now maintained using the driver_events. This fixes bug #5005. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@35463 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2009, Axel Dörfler, [email protected].
|
* Copyright 2002-2010, Axel Dörfler, [email protected].
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -104,21 +104,48 @@ struct legacy_driver {
|
|||||||
status_t (*uninit_hardware)(void);
|
status_t (*uninit_hardware)(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct path_entry : DoublyLinkedListLinkImpl<path_entry> {
|
|
||||||
char path[B_PATH_NAME_LENGTH];
|
enum driver_event_type {
|
||||||
|
kAddDriver,
|
||||||
|
kRemoveDriver,
|
||||||
|
kAddWatcher,
|
||||||
|
kRemoveWatcher
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef DoublyLinkedList<path_entry> EntryList;
|
struct driver_event : DoublyLinkedListLinkImpl<driver_event> {
|
||||||
|
driver_event(driver_event_type _type) : type(_type) {}
|
||||||
|
|
||||||
struct driver_entry : public DoublyLinkedListLinkImpl<driver_entry> {
|
struct ref {
|
||||||
char* path;
|
dev_t device;
|
||||||
dev_t device;
|
ino_t node;
|
||||||
ino_t node;
|
};
|
||||||
int32 busses;
|
|
||||||
|
driver_event_type type;
|
||||||
|
union {
|
||||||
|
char path[B_PATH_NAME_LENGTH];
|
||||||
|
ref node;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef DoublyLinkedList<driver_event> DriverEventList;
|
||||||
|
|
||||||
|
|
||||||
|
struct driver_entry : DoublyLinkedListLinkImpl<driver_entry> {
|
||||||
|
char* path;
|
||||||
|
dev_t device;
|
||||||
|
ino_t node;
|
||||||
|
int32 busses;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef DoublyLinkedList<driver_entry> DriverEntryList;
|
typedef DoublyLinkedList<driver_entry> DriverEntryList;
|
||||||
|
|
||||||
|
|
||||||
|
struct node_entry : DoublyLinkedListLinkImpl<node_entry> {
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef DoublyLinkedList<node_entry> NodeList;
|
||||||
|
|
||||||
|
|
||||||
struct directory_node_entry {
|
struct directory_node_entry {
|
||||||
directory_node_entry* hash_link;
|
directory_node_entry* hash_link;
|
||||||
ino_t node;
|
ino_t node;
|
||||||
@@ -192,11 +219,10 @@ static status_t load_driver(legacy_driver *driver);
|
|||||||
|
|
||||||
static hash_table* sDriverHash;
|
static hash_table* sDriverHash;
|
||||||
static DriverWatcher sDriverWatcher;
|
static DriverWatcher sDriverWatcher;
|
||||||
static int32 sDriverEvents;
|
static int32 sDriverEventsPending;
|
||||||
static EntryList sDriversToAdd;
|
static DriverEventList sDriverEvents;
|
||||||
static EntryList sDriversToRemove;
|
static mutex sDriverEventsLock = MUTEX_INITIALIZER("driver events");
|
||||||
static mutex sDriversListLock = MUTEX_INITIALIZER("driversList");
|
// inner lock, protects the sDriverEvents list only
|
||||||
// inner lock, protects the sDriversToAdd/sDriversToRemove lists only
|
|
||||||
static DirectoryWatcher sDirectoryWatcher;
|
static DirectoryWatcher sDirectoryWatcher;
|
||||||
static DirectoryNodeHash sDirectoryNodeHash;
|
static DirectoryNodeHash sDirectoryNodeHash;
|
||||||
static recursive_lock sLock;
|
static recursive_lock sLock;
|
||||||
@@ -446,6 +472,27 @@ unpublish_driver(legacy_driver *driver)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
change_driver_watcher(dev_t device, ino_t node, bool add)
|
||||||
|
{
|
||||||
|
if (device == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
driver_event* event = new (std::nothrow) driver_event(
|
||||||
|
add ? kAddWatcher : kRemoveWatcher);
|
||||||
|
if (event == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
event->node.device = device;
|
||||||
|
event->node.node = node;
|
||||||
|
|
||||||
|
MutexLocker _(sDriverEventsLock);
|
||||||
|
sDriverEvents.Add(event);
|
||||||
|
|
||||||
|
atomic_add(&sDriverEventsPending, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int32
|
static int32
|
||||||
get_priority(const char* path)
|
get_priority(const char* path)
|
||||||
{
|
{
|
||||||
@@ -505,7 +552,7 @@ find_driver(dev_t device, ino_t node)
|
|||||||
static status_t
|
static status_t
|
||||||
add_driver(const char *path, image_id image)
|
add_driver(const char *path, image_id image)
|
||||||
{
|
{
|
||||||
// see if we already know this driver
|
// Check if we already know this driver
|
||||||
|
|
||||||
struct stat stat;
|
struct stat stat;
|
||||||
if (image >= 0) {
|
if (image >= 0) {
|
||||||
@@ -583,10 +630,8 @@ add_driver(const char *path, image_id image)
|
|||||||
new(&driver->devices) DeviceList;
|
new(&driver->devices) DeviceList;
|
||||||
|
|
||||||
hash_insert(sDriverHash, driver);
|
hash_insert(sDriverHash, driver);
|
||||||
if (stat.st_dev > 0) {
|
if (stat.st_dev > 0)
|
||||||
add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_STAT | B_WATCH_NAME,
|
change_driver_watcher(stat.st_dev, stat.st_ino, true);
|
||||||
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
|
||||||
@@ -611,7 +656,8 @@ load_driver_symbols(const char *driverName)
|
|||||||
static status_t
|
static status_t
|
||||||
reload_driver(legacy_driver *driver)
|
reload_driver(legacy_driver *driver)
|
||||||
{
|
{
|
||||||
dprintf("devfs: reload driver \"%s\"\n", driver->name);
|
dprintf("devfs: reload driver \"%s\" (%ld, %lld)\n", driver->name,
|
||||||
|
driver->device, driver->node);
|
||||||
|
|
||||||
unload_driver(driver);
|
unload_driver(driver);
|
||||||
|
|
||||||
@@ -619,11 +665,12 @@ reload_driver(legacy_driver *driver)
|
|||||||
if (::stat(driver->path, &stat) == 0
|
if (::stat(driver->path, &stat) == 0
|
||||||
&& (stat.st_dev != driver->device || stat.st_ino != driver->node)) {
|
&& (stat.st_dev != driver->device || stat.st_ino != driver->node)) {
|
||||||
// The driver file has been changed, so we need to update its listener
|
// The driver file has been changed, so we need to update its listener
|
||||||
if (driver->device != -1)
|
change_driver_watcher(driver->device, driver->node, false);
|
||||||
remove_node_listener(driver->device, driver->node, sDriverWatcher);
|
|
||||||
|
|
||||||
add_node_listener(driver->device, driver->node,
|
driver->device = stat.st_dev;
|
||||||
B_WATCH_STAT | B_WATCH_NAME, sDriverWatcher);
|
driver->node = stat.st_ino;
|
||||||
|
|
||||||
|
change_driver_watcher(driver->device, driver->node, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t status = load_driver(driver);
|
status_t status = load_driver(driver);
|
||||||
@@ -637,53 +684,73 @@ reload_driver(legacy_driver *driver)
|
|||||||
static void
|
static void
|
||||||
handle_driver_events(void */*_fs*/, int /*iteration*/)
|
handle_driver_events(void */*_fs*/, int /*iteration*/)
|
||||||
{
|
{
|
||||||
if (atomic_and(&sDriverEvents, 0) == 0)
|
if (atomic_and(&sDriverEventsPending, 0) == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// something happened, let's see what it was
|
// something happened, let's see what it was
|
||||||
|
|
||||||
RecursiveLocker _(sLock);
|
|
||||||
|
|
||||||
// Add new drivers
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
MutexLocker listLocker(sDriversListLock);
|
MutexLocker eventLocker(sDriverEventsLock);
|
||||||
|
|
||||||
path_entry* path = sDriversToAdd.RemoveHead();
|
driver_event* event = sDriverEvents.RemoveHead();
|
||||||
if (path == NULL)
|
if (event == NULL)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
listLocker.Unlock();
|
eventLocker.Unlock();
|
||||||
|
TRACE(("driver event %p, type %d\n", event, event->type));
|
||||||
|
|
||||||
legacy_driver* driver = (legacy_driver*)hash_lookup(sDriverHash,
|
switch (event->type) {
|
||||||
get_leaf(path->path));
|
case kAddDriver:
|
||||||
if (driver == NULL)
|
{
|
||||||
legacy_driver_add(path->path);
|
// Add new drivers
|
||||||
else if (get_priority(path->path) >= driver->priority)
|
RecursiveLocker locker(sLock);
|
||||||
driver->binary_updated = true;
|
TRACE((" add driver %p\n", event->path));
|
||||||
delete path;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark removed drivers as updated
|
legacy_driver* driver = (legacy_driver*)hash_lookup(sDriverHash,
|
||||||
|
get_leaf(event->path));
|
||||||
|
if (driver == NULL)
|
||||||
|
legacy_driver_add(event->path);
|
||||||
|
else if (get_priority(event->path) >= driver->priority)
|
||||||
|
driver->binary_updated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case kRemoveDriver:
|
||||||
|
{
|
||||||
|
// Mark removed drivers as updated
|
||||||
|
RecursiveLocker locker(sLock);
|
||||||
|
TRACE((" remove driver %p\n", event->path));
|
||||||
|
|
||||||
while (true) {
|
legacy_driver* driver = (legacy_driver*)hash_lookup(sDriverHash,
|
||||||
MutexLocker listLocker(sDriversListLock);
|
get_leaf(event->path));
|
||||||
|
if (driver != NULL
|
||||||
|
&& get_priority(event->path) >= driver->priority)
|
||||||
|
driver->binary_updated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
path_entry* path = sDriversToRemove.RemoveHead();
|
case kAddWatcher:
|
||||||
if (path == NULL)
|
TRACE((" add watcher %ld:%lld\n", event->node.device,
|
||||||
break;
|
event->node.node));
|
||||||
|
add_node_listener(event->node.device, event->node.node,
|
||||||
|
B_WATCH_STAT | B_WATCH_NAME, sDriverWatcher);
|
||||||
|
break;
|
||||||
|
|
||||||
listLocker.Unlock();
|
case kRemoveWatcher:
|
||||||
|
TRACE((" remove watcher %ld:%lld\n", event->node.device,
|
||||||
|
event->node.node));
|
||||||
|
remove_node_listener(event->node.device, event->node.node,
|
||||||
|
sDriverWatcher);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
legacy_driver* driver = (legacy_driver*)hash_lookup(sDriverHash,
|
delete event;
|
||||||
get_leaf(path->path));
|
|
||||||
if (driver != NULL && get_priority(path->path) >= driver->priority)
|
|
||||||
driver->binary_updated = true;
|
|
||||||
delete path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reload updated drivers
|
// Reload updated drivers
|
||||||
|
|
||||||
|
RecursiveLocker locker(sLock);
|
||||||
|
|
||||||
hash_iterator iterator;
|
hash_iterator iterator;
|
||||||
hash_open(sDriverHash, &iterator);
|
hash_open(sDriverHash, &iterator);
|
||||||
legacy_driver *driver;
|
legacy_driver *driver;
|
||||||
@@ -699,6 +766,7 @@ handle_driver_events(void */*_fs*/, int /*iteration*/)
|
|||||||
}
|
}
|
||||||
|
|
||||||
hash_close(sDriverHash, &iterator, false);
|
hash_close(sDriverHash, &iterator, false);
|
||||||
|
locker.Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -720,9 +788,8 @@ DriverWatcher::EventOccurred(NotificationService& service,
|
|||||||
const KMessage* event)
|
const KMessage* event)
|
||||||
{
|
{
|
||||||
int32 opcode = event->GetInt32("opcode", -1);
|
int32 opcode = event->GetInt32("opcode", -1);
|
||||||
if ((opcode == B_STAT_CHANGED
|
if (opcode != B_STAT_CHANGED
|
||||||
&& (event->GetInt32("fields", 0) & B_STAT_MODIFICATION_TIME) == 0)
|
|| (event->GetInt32("fields", 0) & B_STAT_MODIFICATION_TIME) == 0)
|
||||||
|| opcode != B_ENTRY_REMOVED)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
RecursiveLocker locker(sLock);
|
RecursiveLocker locker(sLock);
|
||||||
@@ -736,7 +803,7 @@ DriverWatcher::EventOccurred(NotificationService& service,
|
|||||||
|
|
||||||
if (driver->devices_used == 0) {
|
if (driver->devices_used == 0) {
|
||||||
// trigger a reload of the driver
|
// trigger a reload of the driver
|
||||||
atomic_add(&sDriverEvents, 1);
|
atomic_add(&sDriverEventsPending, 1);
|
||||||
} else {
|
} else {
|
||||||
// driver is in use right now
|
// driver is in use right now
|
||||||
dprintf("devfs: changed driver \"%s\" is still in use\n", driver->name);
|
dprintf("devfs: changed driver \"%s\" is still in use\n", driver->name);
|
||||||
@@ -986,22 +1053,16 @@ DirectoryWatcher::EventOccurred(NotificationService& service,
|
|||||||
dprintf("driver \"%s\" %s\n", path.Leaf(),
|
dprintf("driver \"%s\" %s\n", path.Leaf(),
|
||||||
opcode == B_ENTRY_CREATED ? "added" : "removed");
|
opcode == B_ENTRY_CREATED ? "added" : "removed");
|
||||||
|
|
||||||
path_entry* entry = new(std::nothrow) path_entry;
|
driver_event* driverEvent = new(std::nothrow) driver_event(
|
||||||
if (entry == NULL)
|
opcode == B_ENTRY_CREATED ? kAddDriver : kRemoveDriver);
|
||||||
|
if (driverEvent == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
strlcpy(entry->path, path.Path(), sizeof(entry->path));
|
strlcpy(driverEvent->path, path.Path(), sizeof(driverEvent->path));
|
||||||
MutexLocker _(sDriversListLock);
|
|
||||||
|
|
||||||
switch (opcode) {
|
MutexLocker _(sDriverEventsLock);
|
||||||
case B_ENTRY_CREATED:
|
sDriverEvents.Add(driverEvent);
|
||||||
sDriversToAdd.Add(entry);
|
atomic_add(&sDriverEventsPending, 1);
|
||||||
break;
|
|
||||||
case B_ENTRY_REMOVED:
|
|
||||||
sDriversToRemove.Add(entry);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
atomic_add(&sDriverEvents, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1419,7 +1480,7 @@ legacy_driver_init(void)
|
|||||||
recursive_lock_init(&sLock, "legacy driver");
|
recursive_lock_init(&sLock, "legacy driver");
|
||||||
|
|
||||||
new(&sDriverWatcher) DriverWatcher;
|
new(&sDriverWatcher) DriverWatcher;
|
||||||
new(&sDriversToAdd) DoublyLinkedList<path_entry>;
|
new(&sDriverEvents) DriverEventList;
|
||||||
|
|
||||||
register_kernel_daemon(&handle_driver_events, NULL, 10);
|
register_kernel_daemon(&handle_driver_events, NULL, 10);
|
||||||
// once every second
|
// once every second
|
||||||
|
|||||||
Reference in New Issue
Block a user