Author: axeld Date: 2010-02-14 23:57:47 +0100 (Sun, 14 Feb 2010) New Revision: 35463 Changeset: http://dev.haiku-os.org/changeset/35463/haiku Ticket: http://dev.haiku-os.org/ticket/5005 Modified: haiku/trunk/src/system/kernel/device_manager/legacy_drivers.cpp Log: * 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. Modified: haiku/trunk/src/system/kernel/device_manager/legacy_drivers.cpp =================================================================== --- haiku/trunk/src/system/kernel/device_manager/legacy_drivers.cpp 2010-02-14 22:29:29 UTC (rev 35462) +++ haiku/trunk/src/system/kernel/device_manager/legacy_drivers.cpp 2010-02-14 22:57:47 UTC (rev 35463) @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009, Axel Dörfler, axeld@xxxxxxxxxxxxxxxxx + * Copyright 2002-2010, Axel Dörfler, axeld@xxxxxxxxxxxxxxxxx * Distributed under the terms of the MIT License. */ @@ -104,21 +104,48 @@ 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> { - char* path; - dev_t device; - ino_t node; - int32 busses; + struct ref { + dev_t device; + ino_t node; + }; + + 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; + +struct node_entry : DoublyLinkedListLinkImpl<node_entry> { +}; + +typedef DoublyLinkedList<node_entry> NodeList; + + struct directory_node_entry { directory_node_entry* hash_link; ino_t node; @@ -192,11 +219,10 @@ static hash_table* sDriverHash; static DriverWatcher sDriverWatcher; -static int32 sDriverEvents; -static EntryList sDriversToAdd; -static EntryList sDriversToRemove; -static mutex sDriversListLock = MUTEX_INITIALIZER("driversList"); - // inner lock, protects the sDriversToAdd/sDriversToRemove lists only +static int32 sDriverEventsPending; +static DriverEventList sDriverEvents; +static mutex sDriverEventsLock = MUTEX_INITIALIZER("driver events"); + // inner lock, protects the sDriverEvents list only static DirectoryWatcher sDirectoryWatcher; static DirectoryNodeHash sDirectoryNodeHash; static recursive_lock sLock; @@ -446,6 +472,27 @@ } +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 get_priority(const char* path) { @@ -505,7 +552,7 @@ static status_t 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; if (image >= 0) { @@ -583,10 +630,8 @@ new(&driver->devices) DeviceList; hash_insert(sDriverHash, driver); - if (stat.st_dev > 0) { - add_node_listener(stat.st_dev, stat.st_ino, B_WATCH_STAT | B_WATCH_NAME, - sDriverWatcher); - } + if (stat.st_dev > 0) + change_driver_watcher(stat.st_dev, stat.st_ino, true); // Even if loading the driver fails - its entry will stay with us // so that we don't have to go through it again @@ -611,7 +656,8 @@ static status_t 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); @@ -619,11 +665,12 @@ if (::stat(driver->path, &stat) == 0 && (stat.st_dev != driver->device || stat.st_ino != driver->node)) { // The driver file has been changed, so we need to update its listener - if (driver->device != -1) - remove_node_listener(driver->device, driver->node, sDriverWatcher); + change_driver_watcher(driver->device, driver->node, false); - add_node_listener(driver->device, driver->node, - B_WATCH_STAT | B_WATCH_NAME, sDriverWatcher); + driver->device = stat.st_dev; + driver->node = stat.st_ino; + + change_driver_watcher(driver->device, driver->node, true); } status_t status = load_driver(driver); @@ -637,53 +684,73 @@ static void handle_driver_events(void */*_fs*/, int /*iteration*/) { - if (atomic_and(&sDriverEvents, 0) == 0) + if (atomic_and(&sDriverEventsPending, 0) == 0) return; // something happened, let's see what it was - RecursiveLocker _(sLock); - - // Add new drivers - while (true) { - MutexLocker listLocker(sDriversListLock); + MutexLocker eventLocker(sDriverEventsLock); - path_entry* path = sDriversToAdd.RemoveHead(); - if (path == NULL) + driver_event* event = sDriverEvents.RemoveHead(); + if (event == NULL) break; - listLocker.Unlock(); + eventLocker.Unlock(); + TRACE(("driver event %p, type %d\n", event, event->type)); - legacy_driver* driver = (legacy_driver*)hash_lookup(sDriverHash, - get_leaf(path->path)); - if (driver == NULL) - legacy_driver_add(path->path); - else if (get_priority(path->path) >= driver->priority) - driver->binary_updated = true; - delete path; - } + switch (event->type) { + case kAddDriver: + { + // Add new drivers + RecursiveLocker locker(sLock); + TRACE((" add driver %p\n", event->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) { - MutexLocker listLocker(sDriversListLock); + legacy_driver* driver = (legacy_driver*)hash_lookup(sDriverHash, + get_leaf(event->path)); + if (driver != NULL + && get_priority(event->path) >= driver->priority) + driver->binary_updated = true; + break; + } - path_entry* path = sDriversToRemove.RemoveHead(); - if (path == NULL) - break; + case kAddWatcher: + TRACE((" add watcher %ld:%lld\n", event->node.device, + 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, - get_leaf(path->path)); - if (driver != NULL && get_priority(path->path) >= driver->priority) - driver->binary_updated = true; - delete path; + delete event; } // Reload updated drivers + RecursiveLocker locker(sLock); + hash_iterator iterator; hash_open(sDriverHash, &iterator); legacy_driver *driver; @@ -699,6 +766,7 @@ } hash_close(sDriverHash, &iterator, false); + locker.Unlock(); } @@ -720,9 +788,8 @@ const KMessage* event) { int32 opcode = event->GetInt32("opcode", -1); - if ((opcode == B_STAT_CHANGED - && (event->GetInt32("fields", 0) & B_STAT_MODIFICATION_TIME) == 0) - || opcode != B_ENTRY_REMOVED) + if (opcode != B_STAT_CHANGED + || (event->GetInt32("fields", 0) & B_STAT_MODIFICATION_TIME) == 0) return; RecursiveLocker locker(sLock); @@ -736,7 +803,7 @@ if (driver->devices_used == 0) { // trigger a reload of the driver - atomic_add(&sDriverEvents, 1); + atomic_add(&sDriverEventsPending, 1); } else { // driver is in use right now dprintf("devfs: changed driver \"%s\" is still in use\n", driver->name); @@ -986,22 +1053,16 @@ dprintf("driver \"%s\" %s\n", path.Leaf(), opcode == B_ENTRY_CREATED ? "added" : "removed"); - path_entry* entry = new(std::nothrow) path_entry; - if (entry == NULL) + driver_event* driverEvent = new(std::nothrow) driver_event( + opcode == B_ENTRY_CREATED ? kAddDriver : kRemoveDriver); + if (driverEvent == NULL) return; - strlcpy(entry->path, path.Path(), sizeof(entry->path)); - MutexLocker _(sDriversListLock); + strlcpy(driverEvent->path, path.Path(), sizeof(driverEvent->path)); - switch (opcode) { - case B_ENTRY_CREATED: - sDriversToAdd.Add(entry); - break; - case B_ENTRY_REMOVED: - sDriversToRemove.Add(entry); - break; - } - atomic_add(&sDriverEvents, 1); + MutexLocker _(sDriverEventsLock); + sDriverEvents.Add(driverEvent); + atomic_add(&sDriverEventsPending, 1); } @@ -1419,7 +1480,7 @@ recursive_lock_init(&sLock, "legacy driver"); new(&sDriverWatcher) DriverWatcher; - new(&sDriversToAdd) DoublyLinkedList<path_entry>; + new(&sDriverEvents) DriverEventList; register_kernel_daemon(&handle_driver_events, NULL, 10); // once every second