schema mount UPDATE reuse inline mp context when possible
Refs sysrepo/sysrepo#2945
diff --git a/src/plugins_exts/schema_mount.c b/src/plugins_exts/schema_mount.c
index c768b5f..1d28268 100644
--- a/src/plugins_exts/schema_mount.c
+++ b/src/plugins_exts/schema_mount.c
@@ -35,9 +35,9 @@
* @brief Internal schema mount data structure for holding all the contexts of parsed data.
*/
struct lyplg_ext_sm {
- struct lyplg_ext_sm_shared {
- pthread_mutex_t lock; /**< lock for accessing this shared structure */
+ pthread_mutex_t lock; /**< lock for accessing this shared structure */
+ struct lyplg_ext_sm_shared {
struct {
struct ly_ctx *ctx; /**< context shared between all data of this mount point */
const char *mount_point; /**< mount point name */
@@ -51,7 +51,7 @@
struct lyplg_ext_sm_inln {
struct {
- struct ly_ctx *ctx; /**< context created for single inline schema data */
+ struct ly_ctx *ctx; /**< context created for inline schema data, may be reused if possible */
} *schemas; /**< array of inline schemas */
uint32_t schema_count; /**< length of schemas array */
} inln; /**< inline mount points */
@@ -207,6 +207,7 @@
if (!sm_data) {
EXT_LOGERR_MEM_RET(cctx, ext);
}
+ pthread_mutex_init(&sm_data->lock, NULL);
ext->compiled = sm_data;
/* find the owner module */
@@ -225,7 +226,6 @@
free(sm_data);
EXT_LOGERR_MEM_RET(cctx, ext);
}
- pthread_mutex_init(&sm_data->shared->lock, NULL);
sm_data->shared->ref_count = 1;
}
@@ -348,6 +348,40 @@
}
/**
+ * @brief Get ietf-yang-library context-id from its data.
+ *
+ * @param[in] ext Compiled extension instance for logging.
+ * @param[in] ext_data Extension data retrieved by the callback with the yang-library data.
+ * @param[out] content_id Content ID in @p ext_data.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+schema_mount_get_content_id(struct lysc_ext_instance *ext, const struct lyd_node *ext_data, const char **content_id)
+{
+ struct lyd_node *node = NULL;
+
+ *content_id = NULL;
+
+ /* get yang-library content-id or module-set-id */
+ if (ext_data) {
+ lyd_find_path(ext_data, "/ietf-yang-library:yang-library/content-id", 0, &node);
+ if (!node) {
+ lyd_find_path(ext_data, "/ietf-yang-library:modules-state/module-set-id", 0, &node);
+ }
+ if (node) {
+ *content_id = lyd_get_value(node);
+ }
+ }
+
+ if (!*content_id) {
+ lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EVALID,
+ "Missing \"content-id\" or \"module-set-id\" in ietf-yang-library data.");
+ return LY_EVALID;
+ }
+ return LY_SUCCESS;
+}
+
+/**
* @brief Get schema (context) for a shared-schema mount point.
*
* @param[in] ext Compiled extension instance.
@@ -361,33 +395,21 @@
const struct ly_ctx **ext_ctx)
{
struct lyplg_ext_sm *sm_data = ext->compiled;
- LY_ERR ret = LY_SUCCESS, r;
- struct lyd_node *node = NULL;
+ LY_ERR rc = LY_SUCCESS, r;
struct ly_ctx *new_ctx = NULL;
uint32_t i;
- const char *content_id = NULL;
+ const char *content_id;
void *mem;
assert(sm_data && sm_data->shared);
/* get yang-library content-id or module-set-id */
- if (ext_data) {
- lyd_find_path(ext_data, "/ietf-yang-library:yang-library/content-id", 0, &node);
- if (!node) {
- lyd_find_path(ext_data, "/ietf-yang-library:modules-state/module-set-id", 0, &node);
- }
- if (node) {
- content_id = lyd_get_value(node);
- }
- }
- if (!content_id) {
- lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EVALID,
- "Missing \"content-id\" or \"module-set-id\" in ietf-yang-library data.");
- return LY_EVALID;
+ if ((r = schema_mount_get_content_id(ext, ext_data, &content_id))) {
+ return r;
}
/* LOCK */
- if ((r = pthread_mutex_lock(&sm_data->shared->lock))) {
+ if ((r = pthread_mutex_lock(&sm_data->lock))) {
lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_ESYS, "Mutex lock failed (%s).", strerror(r));
return LY_ESYS;
}
@@ -405,13 +427,13 @@
lyplg_ext_compile_log_path("/ietf-yang-library:yang-library/content-id", ext, LY_LLERR, LY_EVALID,
"Shared-schema yang-library content-id \"%s\" differs from \"%s\" used previously.",
content_id, sm_data->shared->schemas[i].content_id);
- ret = LY_EVALID;
+ rc = LY_EVALID;
goto cleanup;
}
} else {
/* no schema found, create it */
if ((r = schema_mount_create_ctx(ext, ext_data, config, &new_ctx))) {
- ret = r;
+ rc = r;
goto cleanup;
}
@@ -419,7 +441,7 @@
mem = realloc(sm_data->shared->schemas, (i + 1) * sizeof *sm_data->shared->schemas);
if (!mem) {
ly_ctx_destroy(new_ctx);
- EXT_LOGERR_MEM_GOTO(NULL, ext, ret, cleanup);
+ EXT_LOGERR_MEM_GOTO(NULL, ext, rc, cleanup);
}
sm_data->shared->schemas = mem;
++sm_data->shared->schema_count;
@@ -435,9 +457,102 @@
cleanup:
/* UNLOCK */
- pthread_mutex_unlock(&sm_data->shared->lock);
+ pthread_mutex_unlock(&sm_data->lock);
- return ret;
+ return rc;
+}
+
+/**
+ * @brief Check whether ietf-yang-library data describe an existing context meaning whether it includes
+ * at least exactly all the mentioned modules.
+ *
+ * @param[in] ext Compiled extension instance for logging.
+ * @param[in] ext_data Extension data retrieved by the callback with the yang-library data.
+ * @param[in] ctx Context to consider.
+ * @return LY_SUCCESS if the context matches.
+ * @return LY_ENOT if the context differs.
+ * @return LY_ERR on error.
+ */
+static LY_ERR
+schema_mount_ctx_match(struct lysc_ext_instance *ext, const struct lyd_node *ext_data, const struct ly_ctx *ctx)
+{
+ struct ly_set *impl_mods = NULL, *imp_mods = NULL;
+ struct lyd_node *node;
+ const struct lys_module *mod;
+ const char *name, *revision;
+ LY_ERR rc = LY_ENOT, r;
+ uint32_t i;
+
+ /* collect all the implemented and imported modules, we do not really care about content-id */
+ if (!lyd_find_path(ext_data, "/ietf-yang-library:yang-library/content-id", 0, NULL)) {
+ if ((r = lyd_find_xpath(ext_data, "/ietf-yang-library:yang-library/module-set[1]/module", &impl_mods))) {
+ rc = r;
+ goto cleanup;
+ }
+ if ((r = lyd_find_xpath(ext_data, "/ietf-yang-library:yang-library/module-set[1]/import-only-module", &imp_mods))) {
+ rc = r;
+ goto cleanup;
+ }
+ } else {
+ if ((r = lyd_find_xpath(ext_data, "/ietf-yang-library:modules-state/module[conformance-type='implement']", &impl_mods))) {
+ rc = r;
+ goto cleanup;
+ }
+ if ((r = lyd_find_xpath(ext_data, "/ietf-yang-library:modules-state/module[conformance-type='import']", &imp_mods))) {
+ rc = r;
+ goto cleanup;
+ }
+ }
+
+ if (!impl_mods->count) {
+ lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EVALID, "No implemented modules included in ietf-yang-library data.");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* check all the implemented modules */
+ for (i = 0; i < impl_mods->count; ++i) {
+ lyd_find_path(impl_mods->dnodes[i], "name", 0, &node);
+ name = lyd_get_value(node);
+
+ lyd_find_path(impl_mods->dnodes[i], "revision", 0, &node);
+ if (node && strlen(lyd_get_value(node))) {
+ revision = lyd_get_value(node);
+ } else {
+ revision = NULL;
+ }
+
+ if (!(mod = ly_ctx_get_module(ctx, name, revision)) || !mod->implemented) {
+ /* unsuitable module */
+ goto cleanup;
+ }
+ }
+
+ /* check all the imported modules */
+ for (i = 0; i < imp_mods->count; ++i) {
+ lyd_find_path(imp_mods->dnodes[i], "name", 0, &node);
+ name = lyd_get_value(node);
+
+ lyd_find_path(imp_mods->dnodes[i], "revision", 0, &node);
+ if (node && strlen(lyd_get_value(node))) {
+ revision = lyd_get_value(node);
+ } else {
+ revision = NULL;
+ }
+
+ if (!ly_ctx_get_module(ctx, name, revision)) {
+ /* unsuitable module */
+ goto cleanup;
+ }
+ }
+
+ /* context matches and can be reused */
+ rc = LY_SUCCESS;
+
+cleanup:
+ ly_set_free(impl_mods, NULL);
+ ly_set_free(imp_mods, NULL);
+ return rc;
}
/**
@@ -454,25 +569,44 @@
const struct ly_ctx **ext_ctx)
{
struct lyplg_ext_sm *sm_data = ext->compiled;
- LY_ERR r;
struct ly_ctx *new_ctx = NULL;
uint32_t i;
void *mem;
+ LY_ERR rc = LY_SUCCESS, r;
assert(sm_data && sm_data->shared);
- i = sm_data->inln.schema_count;
+ /* LOCK */
+ if ((r = pthread_mutex_lock(&sm_data->lock))) {
+ lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_ESYS, "Mutex lock failed (%s).", strerror(r));
+ return LY_ESYS;
+ }
- /* always new schema required, create context */
+ /* try to find a context we can reuse */
+ for (i = 0; i < sm_data->inln.schema_count; ++i) {
+ r = schema_mount_ctx_match(ext, ext_data, sm_data->inln.schemas[i].ctx);
+ if (!r) {
+ /* match */
+ *ext_ctx = sm_data->inln.schemas[i].ctx;
+ goto cleanup;
+ } else if (r != LY_ENOT) {
+ /* error */
+ rc = r;
+ goto cleanup;
+ }
+ }
+
+ /* new schema required, create context */
if ((r = schema_mount_create_ctx(ext, ext_data, config, &new_ctx))) {
- return r;
+ rc = r;
+ goto cleanup;
}
/* new entry */
mem = realloc(sm_data->inln.schemas, (i + 1) * sizeof *sm_data->inln.schemas);
if (!mem) {
ly_ctx_destroy(new_ctx);
- EXT_LOGERR_MEM_RET(NULL, ext);
+ EXT_LOGERR_MEM_GOTO(NULL, ext, rc, cleanup);
}
sm_data->inln.schemas = mem;
++sm_data->inln.schema_count;
@@ -482,7 +616,12 @@
/* use the context */
*ext_ctx = sm_data->inln.schemas[i].ctx;
- return LY_SUCCESS;
+
+cleanup:
+ /* UNLOCK */
+ pthread_mutex_unlock(&sm_data->lock);
+
+ return rc;
}
/**
@@ -910,7 +1049,6 @@
lydict_remove(ctx, sm_data->shared->schemas[i].content_id);
}
free(sm_data->shared->schemas);
- pthread_mutex_destroy(&sm_data->shared->lock);
free(sm_data->shared);
}
@@ -918,6 +1056,8 @@
ly_ctx_destroy(sm_data->inln.schemas[i].ctx);
}
free(sm_data->inln.schemas);
+
+ pthread_mutex_destroy(&sm_data->lock);
free(sm_data);
}