From e6fdb84b92adb2be69630d2a8fa4b2658dccb461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Tue, 26 Feb 2008 14:54:41 +0000 Subject: [PATCH] * If path equals "" it won't be published anymore. * load_driver() now uses republish_driver() instead of publishing all devices every time. This allows published nodes to remain stable, no matter if the driver has been unloaded in the mean time or not. * Offloaded driver reloading to a kernel daemon. Since this is regarded to happen rarely, a simple event variable will be set when something happened, and the daemon will then check all known drivers. * devfs_open() will now check if the driver has to be reloaded, and will do so before opening the device. * Moved republish_driver() around again. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24141 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/system/kernel/fs/devfs.cpp | 435 ++++++++++++++++++--------------- 1 file changed, 238 insertions(+), 197 deletions(-) diff --git a/src/system/kernel/fs/devfs.cpp b/src/system/kernel/fs/devfs.cpp index 65c4e809a7..e6cf12e822 100644 --- a/src/system/kernel/fs/devfs.cpp +++ b/src/system/kernel/fs/devfs.cpp @@ -165,12 +165,15 @@ static status_t unpublish_node(struct devfs *fs, devfs_vnode *node, 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); +static status_t unload_driver(driver_entry *driver); +static status_t load_driver(driver_entry *driver); /* the one and only allowed devfs instance */ static struct devfs *sDeviceFileSystem = NULL; static int32 sDefaultApiVersion = 1; static DriverWatcher sDriverWatcher; +static int32 sDriverEvents; // #pragma mark - driver private @@ -199,180 +202,6 @@ driver_entry_compare(void *_driver, const void *_key) } -static status_t -load_driver(driver_entry *driver) -{ - status_t (*init_hardware)(void); - status_t (*init_driver)(void); - const char **devicePaths; - int32 exported = 0; - status_t status; - - // load the module - image_id image = driver->image; - if (image < 0) { - image = load_kernel_add_on(driver->path); - if (image < 0) - return image; - } - - // For a valid device driver the following exports are required - - driver->api_version = &sDefaultApiVersion; - if (get_image_symbol(image, "api_version", B_SYMBOL_TYPE_DATA, - (void **)&driver->api_version) == B_OK) { -#if B_CUR_DRIVER_API_VERSION != 2 - // just in case someone decides to bump up the api version -#error Add checks here for new vs old api version! -#endif - if (*driver->api_version > B_CUR_DRIVER_API_VERSION) { - 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("devfs: \"%s\" api_version invalid\n", driver->name); - status = B_BAD_VALUE; - goto error1; - } - } else - 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("devfs: \"%s\" mandatory driver symbol(s) missing!\n", - driver->name); - status = B_BAD_VALUE; - goto error1; - } - - // Init the driver - - if (get_image_symbol(image, "init_hardware", B_SYMBOL_TYPE_TEXT, - (void **)&init_hardware) == B_OK - && (status = init_hardware()) != B_OK) { - TRACE(("%s: init_hardware() failed: %s\n", driver->name, - strerror(status))); - status = ENXIO; - goto error1; - } - - if (get_image_symbol(image, "init_driver", B_SYMBOL_TYPE_TEXT, - (void **)&init_driver) == B_OK - && (status = init_driver()) != B_OK) { - TRACE(("%s: init_driver() failed: %s\n", driver->name, - strerror(status))); - status = ENXIO; - goto error2; - } - - // resolve and cache those for the driver unload code - if (get_image_symbol(image, "uninit_driver", B_SYMBOL_TYPE_TEXT, - (void **)&driver->uninit_driver) != B_OK) - driver->uninit_driver = NULL; - if (get_image_symbol(image, "uninit_hardware", B_SYMBOL_TYPE_TEXT, - (void **)&driver->uninit_hardware) != B_OK) - driver->uninit_hardware = NULL; - - // The driver has successfully been initialized, now we can - // finally publish its device entries - - // we keep the driver loaded if it exports at least a single interface - devicePaths = driver->publish_devices(); - if (devicePaths == NULL) { - TRACE(("%s: publish_devices() returned NULL.\n", driver->name)); - status = ENXIO; - goto error3; - } - - for (; devicePaths[0]; devicePaths++) { - device_hooks *hooks = driver->find_device(devicePaths[0]); - - if (hooks != NULL - && publish_device(sDeviceFileSystem, devicePaths[0], - NULL, NULL, driver, hooks, *driver->api_version) == B_OK) - exported++; - } - - // we're all done, driver will be kept loaded - if (exported > 0) { - driver->image = image; - return B_OK; - } - - status = B_ERROR; // whatever... - -error3: - if (driver->uninit_driver) - driver->uninit_driver(); - -error2: - if (driver->uninit_hardware) - driver->uninit_hardware(); - -error1: - if (driver->image < 0) { - unload_kernel_add_on(image); - driver->image = status; - } - - return status; -} - - -static status_t -unload_driver(driver_entry *driver) -{ - if (driver->image < 0) { - // driver is not currently loaded - return B_NO_INIT; - } - - if (driver->uninit_driver) - driver->uninit_driver(); - - if (driver->uninit_hardware) - driver->uninit_hardware(); - - unload_kernel_add_on(driver->image); - driver->image = -1; - return B_OK; -} - - -/*! 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. @@ -470,6 +299,162 @@ republish_driver(driver_entry *driver) } +static status_t +load_driver(driver_entry *driver) +{ + status_t (*init_hardware)(void); + status_t (*init_driver)(void); + const char **devicePaths; + int32 exported = 0; + status_t status; + + // load the module + image_id image = driver->image; + if (image < 0) { + image = load_kernel_add_on(driver->path); + if (image < 0) + return image; + } + + // For a valid device driver the following exports are required + + driver->api_version = &sDefaultApiVersion; + if (get_image_symbol(image, "api_version", B_SYMBOL_TYPE_DATA, + (void **)&driver->api_version) == B_OK) { +#if B_CUR_DRIVER_API_VERSION != 2 + // just in case someone decides to bump up the api version +#error Add checks here for new vs old api version! +#endif + if (*driver->api_version > B_CUR_DRIVER_API_VERSION) { + 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("devfs: \"%s\" api_version invalid\n", driver->name); + status = B_BAD_VALUE; + goto error1; + } + } else + 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("devfs: \"%s\" mandatory driver symbol(s) missing!\n", + driver->name); + status = B_BAD_VALUE; + goto error1; + } + + // Init the driver + + if (get_image_symbol(image, "init_hardware", B_SYMBOL_TYPE_TEXT, + (void **)&init_hardware) == B_OK + && (status = init_hardware()) != B_OK) { + TRACE(("%s: init_hardware() failed: %s\n", driver->name, + strerror(status))); + status = ENXIO; + goto error1; + } + + if (get_image_symbol(image, "init_driver", B_SYMBOL_TYPE_TEXT, + (void **)&init_driver) == B_OK + && (status = init_driver()) != B_OK) { + TRACE(("%s: init_driver() failed: %s\n", driver->name, + strerror(status))); + status = ENXIO; + goto error2; + } + + // resolve and cache those for the driver unload code + if (get_image_symbol(image, "uninit_driver", B_SYMBOL_TYPE_TEXT, + (void **)&driver->uninit_driver) != B_OK) + driver->uninit_driver = NULL; + if (get_image_symbol(image, "uninit_hardware", B_SYMBOL_TYPE_TEXT, + (void **)&driver->uninit_hardware) != B_OK) + driver->uninit_hardware = NULL; + + // The driver has successfully been initialized, now we can + // finally publish its device entries + + driver->image = image; + return republish_driver(driver); + +error3: + if (driver->uninit_driver) + driver->uninit_driver(); + +error2: + if (driver->uninit_hardware) + driver->uninit_hardware(); + +error1: + if (driver->image < 0) { + unload_kernel_add_on(image); + driver->image = status; + } + + return status; +} + + +static status_t +unload_driver(driver_entry *driver) +{ + if (driver->image < 0) { + // driver is not currently loaded + return B_NO_INIT; + } + + if (driver->uninit_driver) + driver->uninit_driver(); + + if (driver->uninit_hardware) + driver->uninit_hardware(); + + unload_kernel_add_on(driver->image); + driver->image = -1; + driver->binary_updated = false; + return B_OK; +} + + +/*! 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); + break; + } + } + + hash_close(sDeviceFileSystem->vnode_hash, &i, false); + } +} + + + static const char * get_leaf(const char *path) { @@ -615,6 +600,52 @@ scan_for_drivers(devfs_vnode *dir) } +static status_t +reload_driver(driver_entry *driver) +{ + dprintf("devfs: reload driver \"%s\"\n", driver->name); + + unload_driver(driver); + + status_t status = load_driver(driver); + if (status < B_OK) + unpublish_driver(driver); + + return status; +} + + +static void +handle_driver_events(void *_fs, int /*iteration*/) +{ + struct devfs *fs = (devfs *)_fs; + + if (atomic_and(&sDriverEvents, 0) == 0) + return; + + // something happened, let's see what it was + + RecursiveLocker locker(fs->lock); + + 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) + break; + if (!driver->binary_updated || driver->devices_used != 0) + continue; + + // try to reload the driver + reload_driver(driver); + } + + hash_close(sDeviceFileSystem->driver_hash, &iterator, false); +} + + // #pragma mark - DriverWatcher @@ -643,19 +674,14 @@ DriverWatcher::EventOccured(NotificationService& service, if (driver == NULL) return; + driver->binary_updated = true; +dprintf("%s: devices published %ld, used %ld\n", driver->name, driver->devices_published, driver->devices_used); 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); + // trigger a reload of the driver + atomic_add(&sDriverEvents, 1); } else { - // driver is in use right now, only mark it to be updated when possible + // driver is in use right now dprintf("devfs: changed driver \"%s\" is still in use\n", driver->name); - driver->binary_updated = true; } } @@ -749,8 +775,7 @@ devfs_delete_vnode(struct devfs *fs, struct devfs_vnode *vnode, } -/** makes sure none of the dircookies point to the vnode passed in */ - +/*! Makes sure none of the dircookies point to the vnode passed in */ static void update_dir_cookies(struct devfs_vnode *dir, struct devfs_vnode *vnode) { @@ -1189,7 +1214,7 @@ publish_device(struct devfs *fs, const char *path, device_node_info *deviceNode, } if ((ops == NULL && (deviceNode == NULL || info == NULL)) - || path == NULL || path[0] == '/') + || path == NULL || path[0] == '\0' || path[0] == '/') return B_BAD_VALUE; // are the provided device hooks okay? @@ -1242,11 +1267,10 @@ publish_device(struct devfs *fs, const char *path, device_node_info *deviceNode, } -/** 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). - */ - +/*! 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). +*/ static void get_device_name(struct devfs_vnode *vnode, char *buffer, size_t size) { @@ -1349,6 +1373,8 @@ devfs_mount(dev_t id, const char *devfs, uint32 flags, const char *args, } new(&sDriverWatcher) DriverWatcher; + register_kernel_daemon(&handle_driver_events, fs, 10); + // once every second // create a vnode vnode = devfs_create_vnode(fs, NULL, ""); @@ -1396,6 +1422,8 @@ devfs_unmount(fs_volume _fs) TRACE(("devfs_unmount: entry fs = %p\n", fs)); + unregister_kernel_daemon(&handle_driver_events, fs); + // release the reference to the root put_vnode(fs->id, fs->root_vnode->id); @@ -1631,6 +1659,20 @@ devfs_open(fs_volume _fs, fs_vnode _vnode, int openMode, fs_cookie *_cookie) return B_NO_MEMORY; if (S_ISCHR(vnode->stream.type)) { + RecursiveLocker locker(fs->lock); + + driver_entry *driver = vnode->stream.u.dev.driver; + // TODO: we might want to check if the current node does still exist + // (it should fail in the driver's open(), though, if it doesn't) + if (driver != NULL + && (driver->image < 0 || driver->binary_updated)) { + status = reload_driver(driver); + if (status < B_OK) + return status; + } + + locker.Unlock(); + if (vnode->stream.u.dev.node != NULL) { status = vnode->stream.u.dev.info->open( vnode->stream.u.dev.node->parent->cookie, openMode, @@ -1643,10 +1685,10 @@ devfs_open(fs_volume _fs, fs_vnode _vnode, int openMode, fs_cookie *_cookie) &cookie->device_cookie); } - RecursiveLocker _(fs->lock); + locker.Lock(); - if (status == B_OK && vnode->stream.u.dev.driver != NULL) - vnode->stream.u.dev.driver->devices_used++; + if (status == B_OK && driver != NULL) + driver->devices_used++; } if (status < B_OK) free(cookie); @@ -2460,8 +2502,7 @@ static const device_attr pnp_devfs_attrs[] = { }; -/** someone registered a device */ - +/*! Someone registered a device */ static status_t pnp_devfs_register_device(device_node_handle parent) {