]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add per module/thread instantiation
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Thu, 24 Nov 2016 21:53:51 +0000 (16:53 -0500)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Thu, 24 Nov 2016 21:53:51 +0000 (16:53 -0500)
src/include/modpriv.h
src/include/modules.h
src/main/modules.c
src/main/threads.c
src/main/unit_test_module.c
src/modules/rlm_test/rlm_test.c
src/util/worker.c

index 2037711d79c1341660caa72a8a5c9769700a6f2d..30088840b5772f72e54aedf7635df7d19678833e 100644 (file)
@@ -39,7 +39,7 @@ extern "C" {
  * instance names (may NOT be the module names!), and the per-instance
  * data structures.
  */
-typedef struct module_instance {
+typedef struct {
        char const                      *name;          //!< Instance name e.g. user_database.
 
        rad_module_t const              *module;        //!< Module this is an instance of.
@@ -60,6 +60,16 @@ typedef struct module_instance {
                                                        //!< has been set to true.
 } module_instance_t;
 
+/** Per thread per instance data
+ *
+ * Stores module and thread specific data.
+ */
+typedef struct {
+       module_instance_t               *inst;          //!< Non-thread local instance of this
+
+       void                            *data;          //!< Thread specific instance data.
+} module_thread_instance_t;
+
 module_instance_t      *module_find_with_method(rlm_components_t *method,
                                                 CONF_SECTION *modules, char const *asked_name);
 module_instance_t      *module_find(CONF_SECTION *modules, char const *asked_name);
index 52fded63a2feb364120e600ee2d848fab3ed3d4c..a02a114149e9ba167cd9e8ab397113fce24b2ca1 100644 (file)
@@ -124,6 +124,20 @@ typedef int (*module_instantiate_t)(CONF_SECTION *mod_cs, void *instance);
  */
 typedef int (*module_thread_t)(CONF_SECTION *mod_cs, void *instance, void *thread);
 
+/** Module thread destruction callback
+ *
+ * Destroy a module/thread instance.
+ *
+ * @param[in] instance         data, specific to an instantiated module.
+ *                             Pre-allocated, and populated during the
+ *                             bootstrap and instantiate calls.
+ * @param[in] thread           data specific to this module instance.
+ * @return
+ *     - 0 on success.
+ *     - -1 if instantiation failed.
+ */
+typedef int (*module_thread_detach_t)(void *instance, void *thread);
+
 /** Struct exported by a rlm_* module
  *
  * Determines the capabilities of the module, and maps internal functions
@@ -136,8 +150,11 @@ typedef struct rad_module_t {
 
        module_instantiate_t    bootstrap;              //!< Callback to register dynamic attrs, xlats, etc.
        module_instantiate_t    instantiate;            //!< Callback to configure a new module instance.
-       module_thread_t         thread;                 //!< Callback to configure a module's instance for
+
+       module_thread_t         thread_instantiate;     //!< Callback to configure a module's instance for
                                                        //!< a new worker thread.
+       module_thread_detach_t  thread_detach;          //!< Destroy thread specific data.
+       size_t                  thread_inst_size;       //!< Size of data to allocate to the thread instance.
 
        module_method_t         methods[MOD_COUNT];     //!< Pointers to the various section callbacks.
 } rad_module_t;
@@ -162,8 +179,10 @@ exfile_t *module_exfile_init(TALLOC_CTX *ctx,
 /*
  *     Create free and destroy module instances
  */
-int            modules_bootstrap(CONF_SECTION *root) CC_HINT(nonnull);
+void           *module_thread_instance_find(void *inst);
+int            modules_thread_instantiate(CONF_SECTION *root) CC_HINT(nonnull);
 int            modules_instantiate(CONF_SECTION *root) CC_HINT(nonnull);
+int            modules_bootstrap(CONF_SECTION *root) CC_HINT(nonnull);
 int            modules_free(void);
 int            module_instance_read_only(TALLOC_CTX *ctx, char const *name);
 
index 7aff8e703e7c5bc844129cce7d8b0682ce4d9b3e..cea9922e3a483aaf37cbeb564d5bb144ca858393 100644 (file)
@@ -32,6 +32,8 @@ RCSID("$Id$")
 #include <freeradius-devel/interpreter.h>
 #include <freeradius-devel/parser.h>
 
+fr_thread_local_setup(rbtree_t *, module_thread_inst_tree);
+
 static TALLOC_CTX *instance_ctx = NULL;
 
 /*
@@ -381,6 +383,20 @@ module_instance_t *module_find(CONF_SECTION *modules, char const *asked_name)
        return talloc_get_type_abort(inst, module_instance_t);
 }
 
+/** Free all modules loaded by the server
+ *
+ * @return 0.
+ */
+int modules_free(void)
+{
+       /*
+        *      Free instances first, then dynamic libraries.
+        */
+       TALLOC_FREE(instance_ctx);
+
+       return 0;
+}
+
 /** Find an existing module instance and verify it implements the specified method
  *
  * Extracts the method from the module name where the format is @verbatim <module>.<method> @endverbatim
@@ -443,16 +459,137 @@ module_instance_t *module_find_with_method(rlm_components_t *method, CONF_SECTIO
        return inst;
 }
 
-/** Free all modules loaded by the server
+/** Retrieve module/thread specific instance data for a module
  *
- * @return 0.
+ * @param[in] instance to find thread specific data for.
+ * @return
+ *     - Thread specific instance data on success.
+ *     - NULL if module has no thread instance data.
  */
-int modules_free(void)
+void *module_thread_instance_find(void *instance)
 {
-       /*
-        *      Free instances first, then dynamic libraries.
-        */
-       TALLOC_FREE(instance_ctx);
+       module_instance_t               *inst = instance;
+       rbtree_t                        *tree = fr_thread_local_get(module_thread_inst_tree);
+       module_thread_instance_t        find, *found;
+
+       if (!inst->module->thread_instantiate || !inst->module->thread_inst_size) return NULL;
+
+       memset(&find, 0, sizeof(find));
+       find.inst = inst;
+
+       found = rbtree_finddata(tree, &find);
+       if (!found) return NULL;
+
+       return found->data;
+}
+
+/** Destructor for module_thread_instance_t
+ *
+ */
+static int _module_thread_instance_free(void *thread_inst_data)
+{
+       module_thread_instance_t *thread_inst = talloc_parent(thread_inst_data);
+
+       if (thread_inst->inst->module->thread_detach) {
+               (void) thread_inst->inst->module->thread_detach(thread_inst->inst, thread_inst->data);
+       }
+
+       return 0;
+}
+
+/** Frees the thread local instance free and any thread local instance data
+ *
+ * @param[in] to_free  Thread specific module instance tree to free.
+ */
+static void _module_thread_inst_tree_free(void *to_free)
+{
+       rbtree_t *thread_inst_tree = talloc_get_type_abort(to_free, rbtree_t);
+       rbtree_free(thread_inst_tree);
+}
+
+/** Compare two thread instances based on inst pointer
+ *
+ * @param[in] a                First thread specific module instance.
+ * @param[in] b                Second thread specific module instance.
+ * @return
+ *     - +1 if a > b.
+ *     - -1 if a < b.
+ *     - 0 if a == b.
+ */
+static int _module_thread_inst_tree_cmp(void const *a, void const *b)
+{
+       module_thread_instance_t const *my_a = a, *my_b = b;
+
+       if (my_a->inst > my_b->inst) return +1;
+       if (my_a->inst < my_b->inst) return -1;
+
+       return 0;
+}
+
+/** Setup thread specific instance data for a module
+ *
+ * @param[in] instance of module to perform thread instantiation for.
+ * @param[in] ctx      modules section, containing instance data.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+static int _module_thread_instantiate(void *instance, UNUSED void *ctx)
+{
+       module_instance_t               *inst = talloc_get_type_abort(instance, module_instance_t);
+       module_thread_instance_t        *thread_inst;
+       rbtree_t                        *thread_inst_tree = talloc_get_type_abort(ctx, rbtree_t);
+       int                             ret;
+
+       if (!inst->module->thread_instantiate) return 0;
+
+       MEM(thread_inst = talloc_zero(NULL, module_thread_instance_t));
+       thread_inst->inst = inst;
+
+       if (inst->module->thread_inst_size) {
+               MEM(thread_inst->data = talloc_zero_array(thread_inst, uint8_t, inst->module->thread_inst_size));
+               talloc_set_name(thread_inst->data, "%s", inst->name);
+               talloc_set_destructor(thread_inst->data, _module_thread_instance_free);
+               rbtree_insert(thread_inst_tree, thread_inst);
+       }
+
+       ret = inst->module->thread_instantiate(inst->cs, inst, thread_inst->data);
+       if (ret < 0) {
+               ERROR("Thread instantiation failed for module \"%s\"", inst->name);
+               return -1;
+       }
+
+       return 0;
+}
+
+/** Creates per-thread instance data for modules which need it
+ *
+ * Must be called by any new threads before attempting to execute unlang sections.
+ *
+ * @param[in] root     Configuration root.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+int modules_thread_instantiate(CONF_SECTION *root)
+{
+       CONF_SECTION *modules;
+       rbtree_t *thread_inst_tree;
+
+       modules = cf_section_sub_find(root, "modules");
+       if (!modules) return 0;
+
+       thread_inst_tree = fr_thread_local_init(module_thread_inst_tree, _module_thread_inst_tree_free);
+       if (!thread_inst_tree) {
+               MEM(thread_inst_tree = rbtree_create(NULL, _module_thread_inst_tree_cmp, rbtree_node_talloc_free, 0));
+               fr_thread_local_set(module_thread_inst_tree, thread_inst_tree);
+       }
+
+       if (cf_data_walk(modules, CF_DATA_TYPE_MODULE_INSTANCE, _module_thread_instantiate, thread_inst_tree) < 0) {
+               _module_thread_inst_tree_free(thread_inst_tree);        /* make re-entrant */
+               fr_thread_local_set(module_thread_inst_tree, NULL);
+               return -1;
+       }
 
        return 0;
 }
index d6771d09fad21561845446f4036068d54376a427..2b85c20bab214d81217b9659e8defc42c7cd485e 100644 (file)
@@ -28,6 +28,7 @@ USES_APPLE_DEPRECATED_API     /* OpenSSL API has been deprecated by Apple */
 #include <freeradius-devel/process.h>
 #include <freeradius-devel/heap.h>
 #include <freeradius-devel/rad_assert.h>
+#include <freeradius-devel/modules.h>
 
 #ifdef HAVE_SYS_WAIT_H
 #  include <sys/wait.h>
@@ -471,6 +472,14 @@ static void *thread_handler(void *arg)
                goto done;
        }
 
+       /*
+        *      Perform thread specific module instantiation
+        */
+       if (modules_thread_instantiate(main_config.config) < 0) {
+               ERROR("Thread instantiation failed");
+               goto done;
+       }
+
        thread->status = THREAD_ACTIVE;
 
        /*
index 0b1a872cbc01e98c55b4d4507b6a15534ffd6812..6f83784b3a2f56cba6aaf51b5100504b03fcc8e3 100644 (file)
@@ -812,10 +812,15 @@ int main(int argc, char *argv[])
        if (modules_bootstrap(main_config.config) < 0) goto exit_failure;
 
        /*
-        *      Load the modules
+        *      Instantiate the modules
         */
        if (modules_instantiate(main_config.config) < 0) goto exit_failure;
 
+       /*
+        *      Perform any thread specific instantiation
+        */
+       if (modules_thread_instantiate(main_config.config) < 0) goto exit_failure;
+
        /*
         *      And then load the virtual servers.
         */
index 43bf7f7126e9255ef0b363288746f95501094ee8..6635d16e075a5f95fa890eebd1fe775a6f8204de 100644 (file)
@@ -98,6 +98,10 @@ typedef struct rlm_test_t {
        _timeval_t      *timeval_m;
 } rlm_test_t;
 
+typedef struct {
+       pthread_t       value;
+} rlm_test_thread_t;
+
 /*
  *     A mapping of configuration file names to internal variables.
  */
@@ -173,6 +177,27 @@ static int rlm_test_cmp(UNUSED void *instance, REQUEST *request, UNUSED VALUE_PA
        return 1;
 }
 
+static int mod_thread_instantiate(UNUSED CONF_SECTION *conf, UNUSED void *instance, void *thread)
+{
+       rlm_test_thread_t *t = thread;
+
+       t->value = pthread_self();
+       INFO("Performing instantiation for thread %p", t->value);
+
+       return 0;
+}
+
+static int mod_thread_detach(UNUSED void *instance, void *thread)
+{
+       rlm_test_thread_t *t = thread;
+
+       INFO("Performing detach for thread %p", t->value);
+
+       if (!rad_cond_assert(t->value == pthread_self())) return RLM_MODULE_FAIL;
+
+       return 0;
+}
+
 /*
  *     Do any per-module initialization that is separate to each
  *     configured instance of the module.  e.g. set up connections
@@ -215,6 +240,8 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
  */
 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, UNUSED void *thread, REQUEST *request)
 {
+       rlm_test_thread_t *t = thread;
+
        RINFO("RINFO message");
        RDEBUG("RDEBUG message");
        RDEBUG2("RDEBUG2 message");
@@ -240,6 +267,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, UNUSED
        REXDENT();
        REDEBUG4("RDEBUG4 error message");
 
+       if (!rad_cond_assert(t->value == pthread_self())) return RLM_MODULE_FAIL;
+
        return RLM_MODULE_OK;
 }
 
@@ -248,6 +277,10 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, UNUSED
  */
 static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, UNUSED void *thread, UNUSED REQUEST *request)
 {
+       rlm_test_thread_t *t = thread;
+
+       if (!rad_cond_assert(t->value == pthread_self())) return RLM_MODULE_FAIL;
+
        return RLM_MODULE_OK;
 }
 
@@ -257,6 +290,10 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, UNUS
  */
 static rlm_rcode_t CC_HINT(nonnull) mod_preacct(UNUSED void *instance, UNUSED void *thread, UNUSED REQUEST *request)
 {
+       rlm_test_thread_t *t = thread;
+
+       if (!rad_cond_assert(t->value == pthread_self())) return RLM_MODULE_FAIL;
+
        return RLM_MODULE_OK;
 }
 
@@ -265,6 +302,10 @@ static rlm_rcode_t CC_HINT(nonnull) mod_preacct(UNUSED void *instance, UNUSED vo
  */
 static rlm_rcode_t CC_HINT(nonnull) mod_accounting(UNUSED void *instance, UNUSED void *thread, UNUSED REQUEST *request)
 {
+       rlm_test_thread_t *t = thread;
+
+       if (!rad_cond_assert(t->value == pthread_self())) return RLM_MODULE_FAIL;
+
        return RLM_MODULE_OK;
 }
 
@@ -280,7 +321,11 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(UNUSED void *instance, UNUSED
  */
 static rlm_rcode_t CC_HINT(nonnull) mod_checksimul(UNUSED void *instance, UNUSED void *thread, REQUEST *request)
 {
-       request->simul_count=0;
+       rlm_test_thread_t *t = thread;
+
+       request->simul_count = 0;
+
+       if (!rad_cond_assert(t->value == pthread_self())) return RLM_MODULE_FAIL;
 
        return RLM_MODULE_OK;
 }
@@ -308,13 +353,16 @@ static int mod_detach(UNUSED void *instance)
  */
 extern rad_module_t rlm_test;
 rad_module_t rlm_test = {
-       .magic          = RLM_MODULE_INIT,
-       .name           = "test",
-       .type           = RLM_TYPE_THREAD_SAFE,
-       .inst_size      = sizeof(rlm_test_t),
-       .config         = module_config,
-       .instantiate    = mod_instantiate,
-       .detach         = mod_detach,
+       .magic                  = RLM_MODULE_INIT,
+       .name                   = "test",
+       .type                   = RLM_TYPE_THREAD_SAFE,
+       .inst_size              = sizeof(rlm_test_t),
+       .thread_inst_size       = sizeof(rlm_test_thread_t),
+       .config                 = module_config,
+       .instantiate            = mod_instantiate,
+       .thread_instantiate     = mod_thread_instantiate,
+       .thread_detach          = mod_thread_detach,
+       .detach                 = mod_detach,
        .methods = {
                [MOD_AUTHENTICATE]      = mod_authenticate,
                [MOD_AUTHORIZE]         = mod_authorize,
index 39682429d45cb014678ba6d8ed6d2aa11e246941..7e9eab517a9f616d1dc68fa1449162bfca008976 100644 (file)
@@ -511,6 +511,17 @@ void *fr_worker(UNUSED void *arg)
                return NULL;
        }
 
+#if 0
+       /*
+        *      Perform thread specific module instantiation
+        */
+       if (modules_thread_instantiate(main_config.config) < 0) {
+               ERROR("Thread instantiation failed");
+               talloc_free(ctx);
+               return NULL;
+       }
+#endif
+
        while (true) {
                bool wait_for_event;
                int num_events;