* 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:
Axel Dörfler
2010-02-14 22:57:47 +00:00
parent 15fd71bfff
commit fb40740174
@@ -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