From 8cc50b1ebc3fe6eacd2a03a8c8bc4800bdddfbe9 Mon Sep 17 00:00:00 2001
From: Corey Farrell <git@cfware.com>
Date: Wed, 11 Feb 2015 17:03:04 +0000
Subject: [PATCH] Enable REF_DEBUG for ast_module_ref / ast_module_unref.

Add ast_module_shutdown_ref for use by modules that can
only be unloaded during graceful shutdown.

When REF_DEBUG is enabled:
* Add an empty ao2 object to struct ast_module.
* Allocate ao2 object when the module is loaded.
* Perform an ao2_ref in each place where mod->usecount is manipulated.
* ao2_cleanup on module unload.

ASTERISK-24479 #close
Reported by: Corey Farrell
Review: https://reviewboard.asterisk.org/r/4141/
........

Merged revisions 431662 from http://svn.asterisk.org/svn/asterisk/branches/11
........

Merged revisions 431663 from http://svn.asterisk.org/svn/asterisk/branches/13


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@431672 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 bridges/bridge_builtin_features.c |  8 +++--
 include/asterisk/module.h         | 27 +++++++++++++--
 main/loader.c                     | 56 +++++++++++++++++++++++++++++--
 3 files changed, 85 insertions(+), 6 deletions(-)

diff --git a/bridges/bridge_builtin_features.c b/bridges/bridge_builtin_features.c
index 8ba9a693da..d09a7a2ec9 100644
--- a/bridges/bridge_builtin_features.c
+++ b/bridges/bridge_builtin_features.c
@@ -499,6 +499,10 @@ static int feature_hangup(struct ast_bridge_channel *bridge_channel, void *hook_
 
 static int unload_module(void)
 {
+	ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_HANGUP);
+	ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_AUTOMON);
+	ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_AUTOMIXMON);
+
 	return 0;
 }
 
@@ -508,8 +512,8 @@ static int load_module(void)
 	ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMON, feature_automonitor, NULL);
 	ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMIXMON, feature_automixmonitor, NULL);
 
-	/* Bump up our reference count so we can't be unloaded */
-	ast_module_ref(ast_module_info->self);
+	/* This module cannot be unloaded until shutdown */
+	ast_module_shutdown_ref(ast_module_info->self);
 
 	return AST_MODULE_LOAD_SUCCESS;
 }
diff --git a/include/asterisk/module.h b/include/asterisk/module.h
index d6e1d90399..6d5e04dfc1 100644
--- a/include/asterisk/module.h
+++ b/include/asterisk/module.h
@@ -316,8 +316,31 @@ void __ast_module_user_hangup_all(struct ast_module *);
 #define ast_module_user_remove(user) __ast_module_user_remove(ast_module_info->self, user)
 #define ast_module_user_hangup_all() __ast_module_user_hangup_all(ast_module_info->self)
 
-struct ast_module *ast_module_ref(struct ast_module *);
-void ast_module_unref(struct ast_module *);
+struct ast_module *__ast_module_ref(struct ast_module *mod, const char *file, int line, const char *func);
+void __ast_module_shutdown_ref(struct ast_module *mod, const char *file, int line, const char *func);
+void __ast_module_unref(struct ast_module *mod, const char *file, int line, const char *func);
+
+/*!
+ * \brief Hold a reference to the module
+ * \param mod Module to reference
+ * \return mod
+ *
+ * \note A module reference will prevent the module
+ * from being unloaded.
+ */
+#define ast_module_ref(mod)           __ast_module_ref(mod, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+/*!
+ * \brief Prevent unload of the module before shutdown
+ * \param mod Module to hold
+ *
+ * \note This should not be balanced by a call to ast_module_unref.
+ */
+#define ast_module_shutdown_ref(mod)  __ast_module_shutdown_ref(mod, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+/*!
+ * \brief Release a reference to the module
+ * \param mod Module to release
+ */
+#define ast_module_unref(mod)         __ast_module_unref(mod, __FILE__, __LINE__, __PRETTY_FUNCTION__)
 
 #if defined(__cplusplus) || defined(c_plusplus)
 #define AST_MODULE_INFO(keystr, flags_to_set, desc, load_func, unload_func, reload_func, load_pri, support_level)	\
diff --git a/main/loader.c b/main/loader.c
index f80e726340..5d644ed6bc 100644
--- a/main/loader.c
+++ b/main/loader.c
@@ -117,12 +117,17 @@ static unsigned int embedding = 1; /* we always start out by registering embedde
 
 struct ast_module {
 	const struct ast_module_info *info;
+#ifdef REF_DEBUG
+	/* Used to get module references into REF_DEBUG logs */
+	void *ref_debug;
+#endif
 	void *lib;					/* the shared lib, or NULL if embedded */
 	int usecount;					/* the number of 'users' currently in this module */
 	struct module_user_list users;			/* the list of users in the module */
 	struct {
 		unsigned int running:1;
 		unsigned int declined:1;
+		unsigned int keepuntilshutdown:1;
 	} flags;
 	AST_LIST_ENTRY(ast_module) list_entry;
 	AST_DLLIST_ENTRY(ast_module) entry;
@@ -189,6 +194,9 @@ void ast_module_register(const struct ast_module_info *info)
 	ast_debug(5, "Registering module %s\n", info->name);
 
 	mod->info = info;
+#ifdef REF_DEBUG
+	mod->ref_debug = ao2_t_alloc(0, NULL, info->name);
+#endif
 	AST_LIST_HEAD_INIT(&mod->users);
 
 	/* during startup, before the loader has been initialized,
@@ -235,6 +243,9 @@ void ast_module_unregister(const struct ast_module_info *info)
 	if (mod) {
 		ast_debug(5, "Unregistering module %s\n", info->name);
 		AST_LIST_HEAD_DESTROY(&mod->users);
+#ifdef REF_DEBUG
+		ao2_cleanup(mod->ref_debug);
+#endif
 		ast_free(mod);
 	}
 }
@@ -254,6 +265,10 @@ struct ast_module_user *__ast_module_user_add(struct ast_module *mod, struct ast
 	AST_LIST_INSERT_HEAD(&mod->users, u, entry);
 	AST_LIST_UNLOCK(&mod->users);
 
+#ifdef REF_DEBUG
+	ao2_ref(mod->ref_debug, +1);
+#endif
+
 	ast_atomic_fetchadd_int(&mod->usecount, +1);
 
 	ast_update_use_count();
@@ -278,6 +293,10 @@ void __ast_module_user_remove(struct ast_module *mod, struct ast_module_user *u)
 		return;
 	}
 
+#ifdef REF_DEBUG
+	ao2_ref(mod->ref_debug, -1);
+#endif
+
 	ast_atomic_fetchadd_int(&mod->usecount, -1);
 	ast_free(u);
 
@@ -293,6 +312,11 @@ void __ast_module_user_hangup_all(struct ast_module *mod)
 		if (u->chan) {
 			ast_softhangup(u->chan, AST_SOFTHANGUP_APPUNLOAD);
 		}
+
+#ifdef REF_DEBUG
+		ao2_ref(mod->ref_debug, -1);
+#endif
+
 		ast_atomic_fetchadd_int(&mod->usecount, -1);
 		ast_free(u);
 	}
@@ -610,10 +634,22 @@ void ast_module_shutdown(void)
 				mod->info->unload();
 			}
 			AST_LIST_HEAD_DESTROY(&mod->users);
+#ifdef REF_DEBUG
+			ao2_cleanup(mod->ref_debug);
+#endif
 			ast_free(mod);
 			somethingchanged = 1;
 		}
 		AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_END;
+		if (!somethingchanged) {
+			AST_DLLIST_TRAVERSE(&module_list, mod, entry) {
+				if (mod->flags.keepuntilshutdown) {
+					ast_module_unref(mod);
+					mod->flags.keepuntilshutdown = 0;
+					somethingchanged = 1;
+				}
+			}
+		}
 	} while (somethingchanged && !final);
 
 	AST_DLLIST_UNLOCK(&module_list);
@@ -1430,24 +1466,40 @@ int ast_loader_unregister(int (*v)(void))
 	return cur ? 0 : -1;
 }
 
-struct ast_module *ast_module_ref(struct ast_module *mod)
+struct ast_module *__ast_module_ref(struct ast_module *mod, const char *file, int line, const char *func)
 {
 	if (!mod) {
 		return NULL;
 	}
 
+#ifdef REF_DEBUG
+	__ao2_ref_debug(mod->ref_debug, +1, "", file, line, func);
+#endif
+
 	ast_atomic_fetchadd_int(&mod->usecount, +1);
 	ast_update_use_count();
 
 	return mod;
 }
 
-void ast_module_unref(struct ast_module *mod)
+void __ast_module_shutdown_ref(struct ast_module *mod, const char *file, int line, const char *func)
+{
+	if (!mod->flags.keepuntilshutdown) {
+		__ast_module_ref(mod, file, line, func);
+		mod->flags.keepuntilshutdown = 1;
+	}
+}
+
+void __ast_module_unref(struct ast_module *mod, const char *file, int line, const char *func)
 {
 	if (!mod) {
 		return;
 	}
 
+#ifdef REF_DEBUG
+	__ao2_ref_debug(mod->ref_debug, -1, "", file, line, func);
+#endif
+
 	ast_atomic_fetchadd_int(&mod->usecount, -1);
 	ast_update_use_count();
 }
-- 
GitLab