xref: /haiku/src/tools/fs_shell/module.cpp (revision 589f1a9133081a871738156ebc2c95e757581aac)
190c08768SIngo Weinhold /*
2*589f1a91SAxel Dörfler  * Copyright 2002-2008, Haiku Inc. All rights reserved.
390c08768SIngo Weinhold  * Distributed under the terms of the MIT License.
490c08768SIngo Weinhold  *
590c08768SIngo Weinhold  * Copyright 2001, Thomas Kurschel. All rights reserved.
690c08768SIngo Weinhold  * Distributed under the terms of the NewOS License.
790c08768SIngo Weinhold  */
890c08768SIngo Weinhold 
990c08768SIngo Weinhold /** Manages kernel add-ons and their exported modules. */
1090c08768SIngo Weinhold 
1190c08768SIngo Weinhold #include "module.h"
1290c08768SIngo Weinhold 
1390c08768SIngo Weinhold #include <stdlib.h>
1490c08768SIngo Weinhold 
1590c08768SIngo Weinhold #include "fssh_errors.h"
1690c08768SIngo Weinhold #include "fssh_kernel_export.h"
17*589f1a91SAxel Dörfler #include "fssh_lock.h"
1890c08768SIngo Weinhold #include "fssh_module.h"
1990c08768SIngo Weinhold #include "fssh_string.h"
2090c08768SIngo Weinhold #include "hash.h"
2190c08768SIngo Weinhold 
2290c08768SIngo Weinhold 
2390c08768SIngo Weinhold //#define TRACE_MODULE
2490c08768SIngo Weinhold #ifdef TRACE_MODULE
2590c08768SIngo Weinhold #	define TRACE(x) fssh_dprintf x
2690c08768SIngo Weinhold #else
2790c08768SIngo Weinhold #	define TRACE(x) ;
2890c08768SIngo Weinhold #endif
2990c08768SIngo Weinhold #define FATAL(x) fssh_dprintf x
3090c08768SIngo Weinhold 
3190c08768SIngo Weinhold 
3290c08768SIngo Weinhold namespace FSShell {
3390c08768SIngo Weinhold 
3490c08768SIngo Weinhold 
3590c08768SIngo Weinhold #define MODULE_HASH_SIZE 16
3690c08768SIngo Weinhold 
3790c08768SIngo Weinhold enum module_state {
3890c08768SIngo Weinhold 	MODULE_QUERIED = 0,
3990c08768SIngo Weinhold 	MODULE_LOADED,
4090c08768SIngo Weinhold 	MODULE_INIT,
4190c08768SIngo Weinhold 	MODULE_READY,
4290c08768SIngo Weinhold 	MODULE_UNINIT,
4390c08768SIngo Weinhold 	MODULE_ERROR
4490c08768SIngo Weinhold };
4590c08768SIngo Weinhold 
4690c08768SIngo Weinhold 
4790c08768SIngo Weinhold /* Each known module will have this structure which is put in the
4890c08768SIngo Weinhold  * gModulesHash, and looked up by name.
4990c08768SIngo Weinhold  */
5090c08768SIngo Weinhold 
5190c08768SIngo Weinhold struct module {
5290c08768SIngo Weinhold 	struct module		*next;
5390c08768SIngo Weinhold 	char				*name;
5490c08768SIngo Weinhold 	char				*file;
5590c08768SIngo Weinhold 	int32_t				ref_count;
5690c08768SIngo Weinhold 	fssh_module_info	*info;		/* will only be valid if ref_count > 0 */
5790c08768SIngo Weinhold 	int32_t				offset;		/* this is the offset in the headers */
5890c08768SIngo Weinhold 	module_state		state;		/* state of module */
5990c08768SIngo Weinhold 	uint32_t			flags;
6090c08768SIngo Weinhold };
6190c08768SIngo Weinhold 
6290c08768SIngo Weinhold #define FSSH_B_BUILT_IN_MODULE	2
6390c08768SIngo Weinhold 
6490c08768SIngo Weinhold 
6590c08768SIngo Weinhold /* locking scheme: there is a global lock only; having several locks
6690c08768SIngo Weinhold  * makes trouble if dependent modules get loaded concurrently ->
6790c08768SIngo Weinhold  * they have to wait for each other, i.e. we need one lock per module;
6890c08768SIngo Weinhold  * also we must detect circular references during init and not dead-lock
6990c08768SIngo Weinhold  */
70*589f1a91SAxel Dörfler static fssh_recursive_lock sModulesLock;
7190c08768SIngo Weinhold 
7290c08768SIngo Weinhold /* we store the loaded modules by directory path, and all known modules by module name
7390c08768SIngo Weinhold  * in a hash table for quick access
7490c08768SIngo Weinhold  */
7590c08768SIngo Weinhold static hash_table *sModulesHash;
7690c08768SIngo Weinhold 
7790c08768SIngo Weinhold 
7890c08768SIngo Weinhold /** calculates hash for a module using its name */
7990c08768SIngo Weinhold 
8090c08768SIngo Weinhold static uint32_t
module_hash(void * _module,const void * _key,uint32_t range)8190c08768SIngo Weinhold module_hash(void *_module, const void *_key, uint32_t range)
8290c08768SIngo Weinhold {
8390c08768SIngo Weinhold 	module *module = (struct module *)_module;
8490c08768SIngo Weinhold 	const char *name = (const char *)_key;
8590c08768SIngo Weinhold 
8690c08768SIngo Weinhold 	if (module != NULL)
8790c08768SIngo Weinhold 		return hash_hash_string(module->name) % range;
8890c08768SIngo Weinhold 
8990c08768SIngo Weinhold 	if (name != NULL)
9090c08768SIngo Weinhold 		return hash_hash_string(name) % range;
9190c08768SIngo Weinhold 
9290c08768SIngo Weinhold 	return 0;
9390c08768SIngo Weinhold }
9490c08768SIngo Weinhold 
9590c08768SIngo Weinhold 
9690c08768SIngo Weinhold /** compares a module to a given name */
9790c08768SIngo Weinhold 
9890c08768SIngo Weinhold static int
module_compare(void * _module,const void * _key)9990c08768SIngo Weinhold module_compare(void *_module, const void *_key)
10090c08768SIngo Weinhold {
10190c08768SIngo Weinhold 	module *module = (struct module *)_module;
10290c08768SIngo Weinhold 	const char *name = (const char *)_key;
10390c08768SIngo Weinhold 	if (name == NULL)
10490c08768SIngo Weinhold 		return -1;
10590c08768SIngo Weinhold 
10690c08768SIngo Weinhold 	return fssh_strcmp(module->name, name);
10790c08768SIngo Weinhold }
10890c08768SIngo Weinhold 
10990c08768SIngo Weinhold 
11090c08768SIngo Weinhold static inline void
inc_module_ref_count(struct module * module)11190c08768SIngo Weinhold inc_module_ref_count(struct module *module)
11290c08768SIngo Weinhold {
11390c08768SIngo Weinhold 	module->ref_count++;
11490c08768SIngo Weinhold }
11590c08768SIngo Weinhold 
11690c08768SIngo Weinhold 
11790c08768SIngo Weinhold static inline void
dec_module_ref_count(struct module * module)11890c08768SIngo Weinhold dec_module_ref_count(struct module *module)
11990c08768SIngo Weinhold {
12090c08768SIngo Weinhold 	module->ref_count--;
12190c08768SIngo Weinhold }
12290c08768SIngo Weinhold 
12390c08768SIngo Weinhold 
12490c08768SIngo Weinhold /** Extract the information from the module_info structure pointed at
12590c08768SIngo Weinhold  *	by "info" and create the entries required for access to it's details.
12690c08768SIngo Weinhold  */
12790c08768SIngo Weinhold 
12890c08768SIngo Weinhold static fssh_status_t
create_module(fssh_module_info * info,const char * file,int offset,module ** _module)12990c08768SIngo Weinhold create_module(fssh_module_info *info, const char *file, int offset, module **_module)
13090c08768SIngo Weinhold {
13190c08768SIngo Weinhold 	module *module;
13290c08768SIngo Weinhold 
13390c08768SIngo Weinhold 	TRACE(("create_module(info = %p, file = \"%s\", offset = %d, _module = %p)\n",
13490c08768SIngo Weinhold 		info, file, offset, _module));
13590c08768SIngo Weinhold 
13690c08768SIngo Weinhold 	if (!info->name)
13790c08768SIngo Weinhold 		return FSSH_B_BAD_VALUE;
13890c08768SIngo Weinhold 
13990c08768SIngo Weinhold 	module = (struct module *)hash_lookup(sModulesHash, info->name);
14090c08768SIngo Weinhold 	if (module) {
14190c08768SIngo Weinhold 		FATAL(("Duplicate module name (%s) detected... ignoring new one\n", info->name));
14290c08768SIngo Weinhold 		return FSSH_B_FILE_EXISTS;
14390c08768SIngo Weinhold 	}
14490c08768SIngo Weinhold 
14590c08768SIngo Weinhold 	if ((module = (struct module *)malloc(sizeof(struct module))) == NULL)
14690c08768SIngo Weinhold 		return FSSH_B_NO_MEMORY;
14790c08768SIngo Weinhold 
14890c08768SIngo Weinhold 	TRACE(("create_module: name = \"%s\", file = \"%s\"\n", info->name, file));
14990c08768SIngo Weinhold 
15090c08768SIngo Weinhold 	module->name = fssh_strdup(info->name);
15190c08768SIngo Weinhold 	if (module->name == NULL) {
15290c08768SIngo Weinhold 		free(module);
15390c08768SIngo Weinhold 		return FSSH_B_NO_MEMORY;
15490c08768SIngo Weinhold 	}
15590c08768SIngo Weinhold 
15690c08768SIngo Weinhold 	module->file = fssh_strdup(file);
15790c08768SIngo Weinhold 	if (module->file == NULL) {
15890c08768SIngo Weinhold 		free(module->name);
15990c08768SIngo Weinhold 		free(module);
16090c08768SIngo Weinhold 		return FSSH_B_NO_MEMORY;
16190c08768SIngo Weinhold 	}
16290c08768SIngo Weinhold 
16390c08768SIngo Weinhold 	module->state = MODULE_QUERIED;
16490c08768SIngo Weinhold 	module->info = info;
16590c08768SIngo Weinhold 	module->offset = offset;
16690c08768SIngo Weinhold 		// record where the module_info can be found in the module_info array
16790c08768SIngo Weinhold 	module->ref_count = 0;
16890c08768SIngo Weinhold 	module->flags = info->flags;
16990c08768SIngo Weinhold 
170*589f1a91SAxel Dörfler 	fssh_recursive_lock_lock(&sModulesLock);
17190c08768SIngo Weinhold 	hash_insert(sModulesHash, module);
172*589f1a91SAxel Dörfler 	fssh_recursive_lock_unlock(&sModulesLock);
17390c08768SIngo Weinhold 
17490c08768SIngo Weinhold 	if (_module)
17590c08768SIngo Weinhold 		*_module = module;
17690c08768SIngo Weinhold 
17790c08768SIngo Weinhold 	return FSSH_B_OK;
17890c08768SIngo Weinhold }
17990c08768SIngo Weinhold 
18090c08768SIngo Weinhold 
18190c08768SIngo Weinhold /** Initializes a loaded module depending on its state */
18290c08768SIngo Weinhold 
18390c08768SIngo Weinhold static inline fssh_status_t
init_module(module * module)18490c08768SIngo Weinhold init_module(module *module)
18590c08768SIngo Weinhold {
18690c08768SIngo Weinhold 	switch (module->state) {
18790c08768SIngo Weinhold 		case MODULE_QUERIED:
18890c08768SIngo Weinhold 		case MODULE_LOADED:
18990c08768SIngo Weinhold 		{
19090c08768SIngo Weinhold 			fssh_status_t status;
19190c08768SIngo Weinhold 			module->state = MODULE_INIT;
19290c08768SIngo Weinhold 
19390c08768SIngo Weinhold 			// init module
19490c08768SIngo Weinhold 
19590c08768SIngo Weinhold 			TRACE(("initializing module %s (at %p)... \n", module->name, module->info->std_ops));
19690c08768SIngo Weinhold 			status = module->info->std_ops(FSSH_B_MODULE_INIT);
19790c08768SIngo Weinhold 			TRACE(("...done (%s)\n", strerror(status)));
19890c08768SIngo Weinhold 
19990c08768SIngo Weinhold 			if (status >= FSSH_B_OK)
20090c08768SIngo Weinhold 				module->state = MODULE_READY;
20190c08768SIngo Weinhold 			else {
20290c08768SIngo Weinhold 				module->state = MODULE_LOADED;
20390c08768SIngo Weinhold 			}
20490c08768SIngo Weinhold 
20590c08768SIngo Weinhold 			return status;
20690c08768SIngo Weinhold 		}
20790c08768SIngo Weinhold 
20890c08768SIngo Weinhold 		case MODULE_READY:
20990c08768SIngo Weinhold 			return FSSH_B_OK;
21090c08768SIngo Weinhold 
21190c08768SIngo Weinhold 		case MODULE_INIT:
21290c08768SIngo Weinhold 			FATAL(("circular reference to %s\n", module->name));
21390c08768SIngo Weinhold 			return FSSH_B_ERROR;
21490c08768SIngo Weinhold 
21590c08768SIngo Weinhold 		case MODULE_UNINIT:
21690c08768SIngo Weinhold 			FATAL(("tried to load module %s which is currently unloading\n", module->name));
21790c08768SIngo Weinhold 			return FSSH_B_ERROR;
21890c08768SIngo Weinhold 
21990c08768SIngo Weinhold 		case MODULE_ERROR:
22090c08768SIngo Weinhold 			FATAL(("cannot load module %s because its earlier unloading failed\n", module->name));
22190c08768SIngo Weinhold 			return FSSH_B_ERROR;
22290c08768SIngo Weinhold 
22390c08768SIngo Weinhold 		default:
22490c08768SIngo Weinhold 			return FSSH_B_ERROR;
22590c08768SIngo Weinhold 	}
22690c08768SIngo Weinhold 	// never trespasses here
22790c08768SIngo Weinhold }
22890c08768SIngo Weinhold 
22990c08768SIngo Weinhold 
23090c08768SIngo Weinhold /** Uninitializes a module depeding on its state */
23190c08768SIngo Weinhold 
23290c08768SIngo Weinhold static inline int
uninit_module(module * module)23390c08768SIngo Weinhold uninit_module(module *module)
23490c08768SIngo Weinhold {
23590c08768SIngo Weinhold 	TRACE(("uninit_module(%s)\n", module->name));
23690c08768SIngo Weinhold 
23790c08768SIngo Weinhold 	switch (module->state) {
23890c08768SIngo Weinhold 		case MODULE_QUERIED:
23990c08768SIngo Weinhold 		case MODULE_LOADED:
24090c08768SIngo Weinhold 			return FSSH_B_NO_ERROR;
24190c08768SIngo Weinhold 
24290c08768SIngo Weinhold 		case MODULE_INIT:
24390c08768SIngo Weinhold 			fssh_panic("Trying to unload module %s which is initializing\n", module->name);
24490c08768SIngo Weinhold 			return FSSH_B_ERROR;
24590c08768SIngo Weinhold 
24690c08768SIngo Weinhold 		case MODULE_UNINIT:
24790c08768SIngo Weinhold 			fssh_panic("Trying to unload module %s which is un-initializing\n", module->name);
24890c08768SIngo Weinhold 			return FSSH_B_ERROR;
24990c08768SIngo Weinhold 
25090c08768SIngo Weinhold 		case MODULE_READY:
25190c08768SIngo Weinhold 		{
25290c08768SIngo Weinhold 			fssh_status_t status;
25390c08768SIngo Weinhold 
25490c08768SIngo Weinhold 			module->state = MODULE_UNINIT;
25590c08768SIngo Weinhold 
25690c08768SIngo Weinhold 			TRACE(("uninitializing module %s...\n", module->name));
25790c08768SIngo Weinhold 			status = module->info->std_ops(FSSH_B_MODULE_UNINIT);
25890c08768SIngo Weinhold 			TRACE(("...done (%s)\n", strerror(status)));
25990c08768SIngo Weinhold 
26090c08768SIngo Weinhold 			if (status == FSSH_B_NO_ERROR) {
26190c08768SIngo Weinhold 				module->state = MODULE_LOADED;
26290c08768SIngo Weinhold 				return 0;
26390c08768SIngo Weinhold 			}
26490c08768SIngo Weinhold 
26590c08768SIngo Weinhold 			FATAL(("Error unloading module %s (%s)\n", module->name, fssh_strerror(status)));
26690c08768SIngo Weinhold 
26790c08768SIngo Weinhold 			module->state = MODULE_ERROR;
26890c08768SIngo Weinhold 			module->flags |= FSSH_B_KEEP_LOADED;
26990c08768SIngo Weinhold 
27090c08768SIngo Weinhold 			return status;
27190c08768SIngo Weinhold 		}
27290c08768SIngo Weinhold 		default:
27390c08768SIngo Weinhold 			return FSSH_B_ERROR;
27490c08768SIngo Weinhold 	}
27590c08768SIngo Weinhold 	// never trespasses here
27690c08768SIngo Weinhold }
27790c08768SIngo Weinhold 
27890c08768SIngo Weinhold 
27990c08768SIngo Weinhold void
register_builtin_module(struct fssh_module_info * info)28090c08768SIngo Weinhold register_builtin_module(struct fssh_module_info *info)
28190c08768SIngo Weinhold {
28290c08768SIngo Weinhold 	info->flags |= FSSH_B_BUILT_IN_MODULE;
28390c08768SIngo Weinhold 		// this is an internal flag, it doesn't have to be set by modules itself
28490c08768SIngo Weinhold 
28590c08768SIngo Weinhold 	if (create_module(info, "", -1, NULL) != FSSH_B_OK)
28690c08768SIngo Weinhold 		fssh_dprintf("creation of built-in module \"%s\" failed!\n", info->name);
28790c08768SIngo Weinhold }
28890c08768SIngo Weinhold 
28990c08768SIngo Weinhold 
29090c08768SIngo Weinhold static int
dump_modules(int argc,char ** argv)29190c08768SIngo Weinhold dump_modules(int argc, char **argv)
29290c08768SIngo Weinhold {
29390c08768SIngo Weinhold 	hash_iterator iterator;
29490c08768SIngo Weinhold 	struct module *module;
29590c08768SIngo Weinhold 
29690c08768SIngo Weinhold 	hash_rewind(sModulesHash, &iterator);
29790c08768SIngo Weinhold 	fssh_dprintf("-- known modules:\n");
29890c08768SIngo Weinhold 
29990c08768SIngo Weinhold 	while ((module = (struct module *)hash_next(sModulesHash, &iterator)) != NULL) {
30090c08768SIngo Weinhold 		fssh_dprintf("%p: \"%s\", \"%s\" (%d), refcount = %d, state = %d\n",
30190c08768SIngo Weinhold 			module, module->name, module->file, (int)module->offset, (int)module->ref_count,
30290c08768SIngo Weinhold 			module->state);
30390c08768SIngo Weinhold 	}
30490c08768SIngo Weinhold 
30590c08768SIngo Weinhold 	return 0;
30690c08768SIngo Weinhold }
30790c08768SIngo Weinhold 
30890c08768SIngo Weinhold 
30990c08768SIngo Weinhold //	#pragma mark -
31090c08768SIngo Weinhold //	Exported Kernel API (private part)
31190c08768SIngo Weinhold 
31290c08768SIngo Weinhold 
31390c08768SIngo Weinhold /** Setup the module structures and data for use - must be called
31490c08768SIngo Weinhold  *	before any other module call.
31590c08768SIngo Weinhold  */
31690c08768SIngo Weinhold 
31790c08768SIngo Weinhold fssh_status_t
module_init(kernel_args * args)31890c08768SIngo Weinhold module_init(kernel_args *args)
31990c08768SIngo Weinhold {
320*589f1a91SAxel Dörfler 	fssh_recursive_lock_init(&sModulesLock, "modules rlock");
32190c08768SIngo Weinhold 
32290c08768SIngo Weinhold 	sModulesHash = hash_init(MODULE_HASH_SIZE, 0, module_compare, module_hash);
32390c08768SIngo Weinhold 	if (sModulesHash == NULL)
32490c08768SIngo Weinhold 		return FSSH_B_NO_MEMORY;
32590c08768SIngo Weinhold 
32690c08768SIngo Weinhold 	fssh_add_debugger_command("modules", &dump_modules, "list all known & loaded modules");
32790c08768SIngo Weinhold 
32890c08768SIngo Weinhold 	return FSSH_B_OK;
32990c08768SIngo Weinhold }
33090c08768SIngo Weinhold 
33190c08768SIngo Weinhold 
33290c08768SIngo Weinhold }	// namespace FSShell
33390c08768SIngo Weinhold 
33490c08768SIngo Weinhold 
33590c08768SIngo Weinhold //	#pragma mark -
33690c08768SIngo Weinhold //	Exported Kernel API (public part)
33790c08768SIngo Weinhold 
33890c08768SIngo Weinhold 
33990c08768SIngo Weinhold using namespace FSShell;
34090c08768SIngo Weinhold 
34190c08768SIngo Weinhold 
34290c08768SIngo Weinhold fssh_status_t
fssh_get_module(const char * path,fssh_module_info ** _info)34390c08768SIngo Weinhold fssh_get_module(const char *path, fssh_module_info **_info)
34490c08768SIngo Weinhold {
34590c08768SIngo Weinhold 	module *module;
34690c08768SIngo Weinhold 	fssh_status_t status;
34790c08768SIngo Weinhold 
34890c08768SIngo Weinhold 	TRACE(("get_module(%s)\n", path));
34990c08768SIngo Weinhold 
35090c08768SIngo Weinhold 	if (path == NULL)
35190c08768SIngo Weinhold 		return FSSH_B_BAD_VALUE;
35290c08768SIngo Weinhold 
353*589f1a91SAxel Dörfler 	fssh_recursive_lock_lock(&sModulesLock);
35490c08768SIngo Weinhold 
35590c08768SIngo Weinhold 	module = (struct module *)hash_lookup(sModulesHash, path);
35690c08768SIngo Weinhold 	if (module == NULL)
35790c08768SIngo Weinhold 		goto err;
35890c08768SIngo Weinhold 
35990c08768SIngo Weinhold 	// The state will be adjusted by the call to init_module
36090c08768SIngo Weinhold 	// if we have just loaded the file
36190c08768SIngo Weinhold 	if (module->ref_count == 0)
36290c08768SIngo Weinhold 		status = init_module(module);
36390c08768SIngo Weinhold 	else
36490c08768SIngo Weinhold 		status = FSSH_B_OK;
36590c08768SIngo Weinhold 
36690c08768SIngo Weinhold 	if (status == FSSH_B_OK) {
36790c08768SIngo Weinhold 		inc_module_ref_count(module);
36890c08768SIngo Weinhold 		*_info = module->info;
36990c08768SIngo Weinhold 	}
37090c08768SIngo Weinhold 
371*589f1a91SAxel Dörfler 	fssh_recursive_lock_unlock(&sModulesLock);
37290c08768SIngo Weinhold 	return status;
37390c08768SIngo Weinhold 
37490c08768SIngo Weinhold err:
375*589f1a91SAxel Dörfler 	fssh_recursive_lock_unlock(&sModulesLock);
37690c08768SIngo Weinhold 	return FSSH_B_ENTRY_NOT_FOUND;
37790c08768SIngo Weinhold }
37890c08768SIngo Weinhold 
37990c08768SIngo Weinhold 
38090c08768SIngo Weinhold fssh_status_t
fssh_put_module(const char * path)38190c08768SIngo Weinhold fssh_put_module(const char *path)
38290c08768SIngo Weinhold {
38390c08768SIngo Weinhold 	module *module;
38490c08768SIngo Weinhold 
38590c08768SIngo Weinhold 	TRACE(("put_module(path = %s)\n", path));
38690c08768SIngo Weinhold 
387*589f1a91SAxel Dörfler 	fssh_recursive_lock_lock(&sModulesLock);
38890c08768SIngo Weinhold 
38990c08768SIngo Weinhold 	module = (struct module *)hash_lookup(sModulesHash, path);
39090c08768SIngo Weinhold 	if (module == NULL) {
39190c08768SIngo Weinhold 		FATAL(("module: We don't seem to have a reference to module %s\n", path));
392*589f1a91SAxel Dörfler 		fssh_recursive_lock_unlock(&sModulesLock);
39390c08768SIngo Weinhold 		return FSSH_B_BAD_VALUE;
39490c08768SIngo Weinhold 	}
39590c08768SIngo Weinhold 
39690c08768SIngo Weinhold 	if ((module->flags & FSSH_B_KEEP_LOADED) == 0) {
39790c08768SIngo Weinhold 		dec_module_ref_count(module);
39890c08768SIngo Weinhold 
39990c08768SIngo Weinhold 		if (module->ref_count == 0)
40090c08768SIngo Weinhold 			uninit_module(module);
40190c08768SIngo Weinhold 	}
40290c08768SIngo Weinhold 
403*589f1a91SAxel Dörfler 	fssh_recursive_lock_unlock(&sModulesLock);
40490c08768SIngo Weinhold 	return FSSH_B_OK;
40590c08768SIngo Weinhold }
406