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

View File

@ -1128,6 +1128,21 @@ menuconfig MODULE
if MODULE
config MODULE_MAXDEPEND
int "Max dependencies"
default 2
---help---
This setting controls the number of other modules that a new module
may depend on. That is, when a new module in inserted via 'insmod'
it may depend on symbols exported by other, already installed
modules. This is the maximum number of modules that export symbols
to the new module. This maximum is artificial; it is used in the
current design because it uses some very simple, pre-allocated data
structures.
All dependencies logic my be eliminated by sett CONFIG_MODULE_MAXDEPEND
to zero.
config MODULE_ALIGN_LOG2
int "Log2 Section Alignment"
default 2

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

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
sched/module/mod_depend.c Normal file
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;
}

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:

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)
{

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;
}

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 */

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);

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();
}

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