sched/modules: Add support for dependencies between modules.

This commit is contained in:
Gregory Nutt
2017-01-27 11:43:27 -06:00
parent 8b5af8bf50
commit 68a7110195
11 changed files with 496 additions and 38 deletions
+5 -5
View File
@@ -37,13 +37,13 @@ ifeq ($(CONFIG_MODULE),y)
# OS module interfaces
CSRCS += mod_insmod.c mod_rmmod.c
CSRCS += mod_insmod.c mod_rmmod.c mod_modsym.c mod_symtab.c mod_modhandle.c
# loadable module library
# Loadable module library
CSRCS += mod_bind.c mod_init.c mod_iobuffer.c mod_load.c mod_modhandle.c
CSRCS += mod_modsym.c mod_read.c mod_registry.c mod_sections.c mod_symbols.c
CSRCS += mod_symtab.c mod_uninit.c mod_unload.c mod_verify.c
CSRCS += mod_bind.c mod_depend.c mod_init.c mod_iobuffer.c mod_load.c
CSRCS += mod_read.c mod_registry.c mod_sections.c mod_symbols.c mod_uninit.c
CSRCS += mod_unload.c mod_verify.c
# procfs support
+14 -8
View File
@@ -1,7 +1,7 @@
/****************************************************************************
* sched/module/mod_bind.c
*
* Copyright (C) 2015 Gregory Nutt. All rights reserved.
* Copyright (C) 2015, 2017 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -98,7 +98,8 @@ static inline int mod_readrel(FAR struct mod_loadinfo_s *loadinfo,
*
****************************************************************************/
static int mod_relocate(FAR struct mod_loadinfo_s *loadinfo, int relidx)
static int mod_relocate(FAR struct module_s *modp,
FAR struct mod_loadinfo_s *loadinfo, int relidx)
{
FAR Elf32_Shdr *relsec = &loadinfo->shdr[relidx];
@@ -148,7 +149,7 @@ static int mod_relocate(FAR struct mod_loadinfo_s *loadinfo, int relidx)
/* Get the value of the symbol (in sym.st_value) */
ret = mod_symvalue(loadinfo, &sym);
ret = mod_symvalue(modp, loadinfo, &sym);
if (ret < 0)
{
/* The special error -ESRCH is returned only in one condition: The
@@ -199,7 +200,8 @@ static int mod_relocate(FAR struct mod_loadinfo_s *loadinfo, int relidx)
return OK;
}
static int mod_relocateadd(FAR struct mod_loadinfo_s *loadinfo, int relidx)
static int mod_relocateadd(FAR struct module_s *modp,
FAR struct mod_loadinfo_s *loadinfo, int relidx)
{
serr("ERROR: Not implemented\n");
return -ENOSYS;
@@ -216,13 +218,17 @@ static int mod_relocateadd(FAR struct mod_loadinfo_s *loadinfo, int relidx)
* Bind the imported symbol names in the loaded module described by
* 'loadinfo' using the exported symbol values provided by mod_setsymtab().
*
* Input Parameters:
* modp - Module state information
* loadinfo - Load state information
*
* Returned Value:
* 0 (OK) is returned on success and a negated errno is returned on
* failure.
*
****************************************************************************/
int mod_bind(FAR struct mod_loadinfo_s *loadinfo)
int mod_bind(FAR struct module_s *modp, FAR struct mod_loadinfo_s *loadinfo)
{
int ret;
int i;
@@ -258,7 +264,7 @@ int mod_bind(FAR struct mod_loadinfo_s *loadinfo)
continue;
}
/* Make sure that the section is allocated. We can't relocated
/* Make sure that the section is allocated. We can't relocate
* sections that were not loaded into memory.
*/
@@ -271,11 +277,11 @@ int mod_bind(FAR struct mod_loadinfo_s *loadinfo)
if (loadinfo->shdr[i].sh_type == SHT_REL)
{
ret = mod_relocate(loadinfo, i);
ret = mod_relocate(modp, loadinfo, i);
}
else if (loadinfo->shdr[i].sh_type == SHT_RELA)
{
ret = mod_relocateadd(loadinfo, i);
ret = mod_relocateadd(modp, loadinfo, i);
}
if (ret < 0)
+200
View File
@@ -0,0 +1,200 @@
/****************************************************************************
* sched/module/mod_depend.c
*
* Copyright (C) 2017 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdlib.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/module.h>
#include "module.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: mod_depend
*
* Description:
* Set up module dependencies between the exporter and the importer of a
* symbol. The happens when the module is installed via insmod and a
* symbol is imported from another module.
*
* Returned Value:
* 0 (OK) is returned on success and a negated errno is returned on
* failure.
*
* Assumptions:
* Caller holds the registry lock.
*
****************************************************************************/
int mod_depend(FAR struct module_s *importer, FAR struct module_s *exporter)
{
#if CONFIG_MODULE_MAXDEPEND > 0
int freendx;
int i;
DEBUGASSERT(importer != NULL && exporter != NULL);
/* First checker if the exported is already in our list if dependencies.
* This would happen if we are importing multiple symbols from the same
* exporting module. In that case, the module would already be in the
* list of dependencies.
*
* The list dependency list is a a dumb, upacked array of pointers. This
* should not be too inefficient if the number of CONFIG_MODULE_MAXDEPEND
* is small. Otherwise, a more dynamic data structure would be in order.
*/
for (i = 0, freendx = -1; i < CONFIG_MODULE_MAXDEPEND; i++)
{
FAR const struct module_s *modp;
/* Check if this dependency slot is available. */
modp = importer->dependencies[i];
if (modp == NULL)
{
/* Remember this slot for use the module is NOT already in the
* list of dependencies.
*/
freendx = i;
}
else if (modp == exporter)
{
/* Yes, we are already importing symbols from this module. Nothing
* more needs to be done.
*/
return OK;
}
}
/* If we get here, then (1) this is a new exporting module that does not
* already appear in the list of dependencies, and (2) freendx is the
* index to the last free slot in the dependency list. If freendx is
* negative, then the dependency list is full.
*/
if (freendx >= 0)
{
/* Increment the count of dependencies on the exporter module */
DEBUGASSERT(exporter->dependents < UINT8_MAX);
if (exporter->dependents >= UINT8_MAX)
{
return -ENOSPC;
}
exporter->dependents++;
/* And remember the exporter so that we can decrement the count of
* dependents if the importer is removed.
*/
DEBUGASSERT(importer->dependencies[freendx] == NULL);
importer->dependencies[freendx] = exporter;
return OK;
}
/* If we get there then the list of dependencies is full. */
DEBUGPANIC();
return -ENFILE;
#else
return OK;
#endif
}
/****************************************************************************
* Name: mod_undepend
*
* Description:
* Tear down module dependencies between the exporters and the importer of
* symbols. This happens when the module is removed via rmmod (and on
* error handling cases in insmod).
*
* Returned Value:
* 0 (OK) is returned on success and a negated errno is returned on
* failure.
*
* Assumptions:
* Caller holds the registry lock.
*
****************************************************************************/
int mod_undepend(FAR struct module_s *importer)
{
#if CONFIG_MODULE_MAXDEPEND > 0
FAR struct module_s *exporter;
int i;
DEBUGASSERT(importer != NULL && importer->dependents == 0);
/* Decrement the dependency count on each of exporters of symbols used by
* this importer module. This is an upacked array of pointers. This
* should not be too inefficient if the number of CONFIG_MODULE_MAXDEPEND
* is small. Otherwise, a more dynamic data structure would be in order.
*/
for (i = 0; i < CONFIG_MODULE_MAXDEPEND; i++)
{
exporter = importer->dependencies[i];
if (exporter != NULL)
{
DEBUGASSERT(exporter->dependents > 0);
if (exporter->dependents > 0)
{
exporter->dependents--;
}
importer->dependencies[i] = NULL;
}
}
#endif
return OK;
}
+2 -1
View File
@@ -244,7 +244,7 @@ FAR void *insmod(FAR const char *filename, FAR const char *modulename)
/* Bind the program to the kernel symbol table */
ret = mod_bind(&loadinfo);
ret = mod_bind(modp, &loadinfo);
if (ret != 0)
{
sinfo("Failed to bind symbols program binary: %d\n", ret);
@@ -286,6 +286,7 @@ FAR void *insmod(FAR const char *filename, FAR const char *modulename)
errout_with_load:
mod_unload(&loadinfo);
(void)mod_undepend(modp);
errout_with_registry_entry:
kmm_free(modp);
errout_with_loadinfo:
+1
View File
@@ -89,6 +89,7 @@ FAR const void *modsym(FAR void *handle, FAR const char *name)
/* Verify that the module is in the registry */
mod_registry_lock();
ret = mod_registry_verify(modp);
if (ret < 0)
{
+75 -10
View File
@@ -48,11 +48,34 @@
#include "module.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define NO_HOLDER ((pid_t)-1)
/****************************************************************************
* Private Types
****************************************************************************/
struct mod_registrylock_s
{
sem_t lock; /* The actual registry lock */
pid_t holder; /* The PID of the current holder of the lock */
int16_t count; /* The number of nested calls to mod_registry_lock */
};
/****************************************************************************
* Private Data
****************************************************************************/
static sem_t g_mod_lock = SEM_INITIALIZER(1);
static struct mod_registrylock_s g_modlock =
{
SEM_INITIALIZER(1), /* lock */
0, /* pid */
0 /* count */
};
static FAR struct module_s *g_mod_registry;
/****************************************************************************
@@ -75,9 +98,36 @@ static FAR struct module_s *g_mod_registry;
void mod_registry_lock(void)
{
while (sem_post(&g_mod_lock) < 0)
pid_t me;
/* Do we already hold the semaphore? */
me = getpid();
if (me == g_modlock.holder)
{
DEBUGASSERT(errno == EINTR);
/* Yes... just increment the count */
g_modlock.count++;
DEBUGASSERT(g_modlock.count > 0);
}
/* Take the semaphore (perhaps waiting) */
else
{
while (sem_wait(&g_modlock.lock) != 0)
{
/* The only case that an error should occr here is if
* the wait was awakened by a signal.
*/
ASSERT(get_errno() == EINTR);
}
/* No we hold the semaphore */
g_modlock.holder = me;
g_modlock.count = 1;
}
}
@@ -97,7 +147,25 @@ void mod_registry_lock(void)
void mod_registry_unlock(void)
{
sem_post(&g_mod_lock);
DEBUGASSERT(g_modlock.holder == getpid());
/* Is this our last count on the semaphore? */
if (g_modlock.count > 1)
{
/* No.. just decrement the count */
g_modlock.count--;
}
/* Yes.. then we can really release the semaphore */
else
{
g_modlock.holder = NO_HOLDER;
g_modlock.count = 0;
sem_post(&g_modlock.lock);
}
}
/****************************************************************************
@@ -249,15 +317,12 @@ int mod_registry_verify(FAR struct module_s *modp)
* function returns a non-zero value, the traversal will be terminated and
* that non-zero value will be returned.
*
* Assumptions:
* The caller does *NOT* hold the lock on the module registry.
*
****************************************************************************/
int mod_registry_foreach(mod_callback_t callback, FAR void *arg)
{
FAR struct module_s *modp;
int ret;
int ret = OK;
/* Get exclusive access to the module registry */
@@ -272,10 +337,10 @@ int mod_registry_foreach(mod_callback_t callback, FAR void *arg)
ret = callback(modp, arg);
if (ret != 0)
{
return ret;
break;
}
}
mod_registry_unlock();
return OK;
return ret;
}
+16
View File
@@ -89,6 +89,17 @@ int rmmod(FAR void *handle)
goto errout_with_lock;
}
#if CONFIG_MODULE_MAXDEPEND > 0
/* Refuse to remove any module that other modules may depend upon. */
if (modp->dependents > 0)
{
serr("ERROR: Module has dependents: %d\n", modp->dependents);
ret = -EBUSY;
goto errout_with_lock;
}
#endif
/* Is there an uninitializer? */
if (modp->modinfo.uninitializer != NULL)
@@ -142,6 +153,11 @@ int rmmod(FAR void *handle)
goto errout_with_lock;
}
#if CONFIG_MODULE_MAXDEPEND > 0
/* Eliminate any dependencies that this module has on other modules */
(void)mod_undepend(modp);
#endif
mod_registry_unlock();
/* And free the registry entry */
+106 -10
View File
@@ -1,7 +1,7 @@
/****************************************************************************
* sched/module/mod_symbols.c
*
* Copyright (C) 2015 Gregory Nutt. All rights reserved.
* Copyright (C) 2015, 2017 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -54,10 +54,28 @@
* Pre-processor Definitions
****************************************************************************/
/* Amount to reallocate buffer when buffer is full */
#ifndef CONFIG_MODULE_BUFFERINCR
# define CONFIG_MODULE_BUFFERINCR 32
#endif
/* Return values search for exported modules */
#define SYM_NOT_FOUND 0
#define SYM_FOUND 1
/****************************************************************************
* Private Types
****************************************************************************/
struct mod_exportinfo_s
{
FAR const char *name; /* Symbol name to find */
FAR struct module_s *modp; /* The module that needs the symbol */
FAR const struct symtab_s *symbol; /* Symbol info returned (if found) */
};
/****************************************************************************
* Private Functions
****************************************************************************/
@@ -155,6 +173,56 @@ static int mod_symname(FAR struct mod_loadinfo_s *loadinfo,
return OK;
}
/****************************************************************************
* Name: mod_symcallback
*
* Description:
* mod_registry_foreach() callback function. Test if the provided module,
* modp, exports the symbol of interest. If so, return that symbol value
* and setup the module dependency relationship.
*
* Returned Value:
* 0 (OK) is returned on success and a negated errno is returned on
* failure.
*
****************************************************************************/
static int mod_symcallback(FAR struct module_s *modp, FAR void *arg)
{
FAR struct mod_exportinfo_s *exportinfo = (FAR struct mod_exportinfo_s *)arg;
int ret;
/* Check if this module exports a symbol of that name */
#ifdef CONFIG_SYMTAB_ORDEREDBYNAME
exportinfo->symbol = symtab_findorderedbyname(modp->modinfo.exports,
exportinfo->name,
modp->modinfo.nexports);
#else
exportinfo->symbol = symtab_findbyname(modp->modinfo.exports,
exportinfo->name,
modp->modinfo.nexports);
#endif
if (exportinfo->symbol != NULL)
{
/* Yes.. save the dependency relationship and return SYM_FOUND to
* stop the traversal.
*/
ret = mod_depend(exportinfo->modp, modp);
if (ret < 0)
{
serr("ERROR: mod_depend failed: %d\n", ret);
return ret;
}
return SYM_FOUND;
}
return SYM_NOT_FOUND;
}
/****************************************************************************
* Public Functions
****************************************************************************/
@@ -246,6 +314,7 @@ int mod_readsym(FAR struct mod_loadinfo_s *loadinfo, int index,
* in the st_value field of the symbol table entry.
*
* Input Parameters:
* modp - Module state information
* loadinfo - Load state information
* sym - Symbol table entry (value might be undefined)
*
@@ -261,9 +330,11 @@ int mod_readsym(FAR struct mod_loadinfo_s *loadinfo, int index,
*
****************************************************************************/
int mod_symvalue(FAR struct mod_loadinfo_s *loadinfo, FAR Elf32_Sym *sym)
int mod_symvalue(FAR struct module_s *modp,
FAR struct mod_loadinfo_s *loadinfo, FAR Elf32_Sym *sym)
{
FAR const struct symtab_s *symbol;
struct mod_exportinfo_s exportinfo;
uintptr_t secbase;
int ret;
@@ -302,18 +373,43 @@ int mod_symvalue(FAR struct mod_loadinfo_s *loadinfo, FAR Elf32_Sym *sym)
return ret;
}
/* Check if the base code exports a symbol of this name */
/* First check if the symbol is exported by an installed module.
* Newest modules are installed at the head of the list. Therefore,
* if the symbol is exported by numerous modules, then the most
* recently installed will take precedence.
*/
exportinfo.name = (FAR const char *)loadinfo->iobuffer;
exportinfo.modp = modp;
exportinfo.symbol = NULL;
ret = mod_registry_foreach(mod_symcallback, (FAR void *)&exportinfo);
if (ret < 0)
{
serr("ERROR: mod_symcallback failed: \n", ret);
return ret;
}
symbol = exportinfo.symbol;
/* If the symbol is not exported by any module, then check if the
* base code exports a symbol of this name.
*/
if (symbol == NULL)
{
#ifdef CONFIG_SYMTAB_ORDEREDBYNAME
symbol = symtab_findorderedbyname(g_mod_symtab,
(FAR char *)loadinfo->iobuffer,
g_mod_nsymbols);
symbol = symtab_findorderedbyname(g_mod_symtab, exportinfo.name,
g_mod_nsymbols);
#else
symbol = symtab_findbyname(g_mod_symtab,
(FAR char *)loadinfo->iobuffer,
g_mod_nsymbols);
symbol = symtab_findbyname(g_mod_symtab, exportinfo.name,
g_mod_nsymbols);
#endif
if (!symbol)
}
/* Was the symbol found from any exporter? */
if (symbol == NULL)
{
serr("ERROR: SHN_UNDEF: Exported symbol \"%s\" not found\n",
loadinfo->iobuffer);
+2 -2
View File
@@ -81,7 +81,7 @@ void mod_getsymtab(FAR const struct symtab_s **symtab, FAR int *nsymbols)
mod_registry_lock();
*symtab = g_mod_symtab;
*nsymbols = g_mod_nsymbols;
mod_registry_lock();
mod_registry_unlock();
}
/****************************************************************************
@@ -106,5 +106,5 @@ void mod_setsymtab(FAR const struct symtab_s *symtab, int nsymbols)
mod_registry_lock();
g_mod_symtab = symtab;
g_mod_nsymbols = nsymbols;
mod_registry_lock();
mod_registry_unlock();
}
+60 -2
View File
@@ -68,6 +68,16 @@ struct module_s
size_t textsize; /* Size of the kernel .text memory allocation */
size_t datasize; /* Size of the kernel .bss/.data memory allocation */
#endif
#if CONFIG_MODULE_MAXDEPEND > 0
uint8_t dependents; /* Number of modules that depend on this module */
/* This is an upacked array of pointers to other modules that this module
* depends upon.
*/
FAR struct module_s *dependencies[CONFIG_MODULE_MAXDEPEND];
#endif
};
/* This struct provides a description of the currently loaded instantiation
@@ -168,7 +178,7 @@ int mod_load(FAR struct mod_loadinfo_s *loadinfo);
*
****************************************************************************/
int mod_bind(FAR struct mod_loadinfo_s *loadinfo);
int mod_bind(FAR struct module_s *modp, FAR struct mod_loadinfo_s *loadinfo);
/****************************************************************************
* Name: mod_unload
@@ -178,6 +188,10 @@ int mod_bind(FAR struct mod_loadinfo_s *loadinfo);
* the actions of mod_load. It is called only under certain error
* conditions after the module has been loaded but not yet started.
*
* Input Parameters:
* modp - Module state information
* loadinfo - Load state information
*
* Returned Value:
* 0 (OK) is returned on success and a negated errno is returned on
* failure.
@@ -186,6 +200,48 @@ int mod_bind(FAR struct mod_loadinfo_s *loadinfo);
int mod_unload(struct mod_loadinfo_s *loadinfo);
/****************************************************************************
* Name: mod_depend
*
* Description:
* Set up module dependencies between the exporter and the importer of a
* symbol. The happens when the module is installed via insmod and a
* symbol is imported from another module.
*
* Returned Value:
* 0 (OK) is returned on success and a negated errno is returned on
* failure.
*
* Assumptions:
* Caller holds the registry lock.
*
****************************************************************************/
#if CONFIG_MODULE_MAXDEPEND > 0
int mod_depend(FAR struct module_s *importer, FAR struct module_s *exporter);
#endif
/****************************************************************************
* Name: mod_undepend
*
* Description:
* Tear down module dependencies between the exporters and the importer of
* symbols. This happens when the module is removed via rmmod (and on
* error handling cases in insmod).
*
* Returned Value:
* 0 (OK) is returned on success and a negated errno is returned on
* failure.
*
* Assumptions:
* Caller holds the registry lock.
*
****************************************************************************/
#if CONFIG_MODULE_MAXDEPEND > 0
int mod_undepend(FAR struct module_s *importer);
#endif
/****************************************************************************
* Name: mod_verifyheader
*
@@ -292,6 +348,7 @@ int mod_readsym(FAR struct mod_loadinfo_s *loadinfo, int index,
* in the st_value field of the symbol table entry.
*
* Input Parameters:
* modp - Module state information
* loadinfo - Load state information
* sym - Symbol table entry (value might be undefined)
*
@@ -307,7 +364,8 @@ int mod_readsym(FAR struct mod_loadinfo_s *loadinfo, int index,
*
****************************************************************************/
int mod_symvalue(FAR struct mod_loadinfo_s *loadinfo, FAR Elf32_Sym *sym);
int mod_symvalue(FAR struct module_s *modp,
FAR struct mod_loadinfo_s *loadinfo, FAR Elf32_Sym *sym);
/****************************************************************************
* Name: mod_freebuffers