Cc: xe-linux-exter...@cisco.com
Cc: Valerii Chernous <vcher...@cisco.com>
Signed-off-by: Valerii Chernous <vcher...@cisco.com>
---
 libkmod/libkmod-internal.h |   2 +
 libkmod/libkmod-module.c   |  74 ++++++++++++++++++++++-
 libkmod/libkmod.c          | 118 ++++++++++++++++++++++++++++++++++++-
 libkmod/libkmod.h          |   6 ++
 4 files changed, 196 insertions(+), 4 deletions(-)

diff --git a/libkmod/libkmod-internal.h b/libkmod/libkmod-internal.h
index 0a274e7..44acfb1 100644
--- a/libkmod/libkmod-internal.h
+++ b/libkmod/libkmod-internal.h
@@ -107,6 +107,8 @@ int kmod_lookup_alias_from_kernel_builtin_file(struct 
kmod_ctx *ctx, const char
 int kmod_lookup_alias_from_builtin_file(struct kmod_ctx *ctx, const char 
*name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
 bool kmod_lookup_alias_is_builtin(struct kmod_ctx *ctx, const char *name) 
__attribute__((nonnull(1, 2)));
 int kmod_lookup_alias_from_commands(struct kmod_ctx *ctx, const char *name, 
struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+int kmod_lookup_alternatives_from_modalt_file(struct kmod_ctx *ctx, const char 
*name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+char *kmod_search_modalternatives(struct kmod_ctx *ctx, const char *name) 
__attribute__((nonnull(1, 2)));
 void kmod_set_modules_visited(struct kmod_ctx *ctx, bool visited) 
__attribute__((nonnull((1))));
 void kmod_set_modules_required(struct kmod_ctx *ctx, bool required) 
__attribute__((nonnull((1))));
 
diff --git a/libkmod/libkmod-module.c b/libkmod/libkmod-module.c
index 2cdec34..5f4a877 100644
--- a/libkmod/libkmod-module.c
+++ b/libkmod/libkmod-module.c
@@ -107,7 +107,7 @@ struct kmod_module {
        bool required : 1;
 };
 
-static inline const char *path_join(const char *path, size_t prefixlen,
+const char *path_join(const char *path, size_t prefixlen,
                                                        char buf[PATH_MAX])
 {
        size_t pathlen;
@@ -700,11 +700,83 @@ static const struct kmod_list 
*module_get_dependencies_noref(const struct kmod_m
 
                if (!mod->init.dep)
                        return NULL;
+
+               update_mod_deps_correspond_to_alternatives((struct kmod_module 
*)mod);
        }
 
        return mod->dep;
 }
 
+int update_mod_deps_correspond_to_alternatives(struct kmod_module *mod)
+{
+       if (is_index_available(mod->ctx, KMOD_INDEX_MODULES_ALTERNATIVES)) {
+               struct kmod_list *l;
+               struct kmod_list *preferred_l = NULL;
+               struct kmod_list *final_l = NULL;
+               int n;
+
+               DBG(mod->ctx, "Update deps alternatives for mod: %s\n",
+                   mod->name != NULL ? mod->name : "undef");
+               kmod_list_foreach(l, mod->dep) {
+                       struct kmod_list *l_alt;
+                       char buf[PATH_MAX*2];
+                       struct kmod_module *dep = (struct kmod_module *)l->data;
+                       struct kmod_module *preferred;
+
+                       snprintf(buf, sizeof(buf), "%s#_#%s", mod->name, 
dep->name);
+                       preferred = NULL;
+                       n = kmod_lookup_alternatives_from_modalt_file(mod->ctx, 
buf, &l_alt);
+                       if (n == 0 || n == -ENOENT) {
+                               preferred = dep;
+                               kmod_module_ref(preferred);
+                       } else if ( n < 0) {
+                               kmod_module_unref_list(preferred_l);
+                               return n;
+                       } else {
+                               struct kmod_list *l2;
+                               kmod_list_foreach(l2, l_alt) {
+                                       struct stat st;
+                                       struct kmod_module *alt = (struct 
kmod_module *)l2->data;
+                                       // if no preferrable alternative use 
first as preferred //
+                                       if (preferred == NULL)
+                                               preferred = alt;
+                                       snprintf(buf, sizeof(buf), 
"/sys/module/%s", alt->name);
+                                       if (stat(buf, &st) == 0 && 
S_ISDIR(st.st_mode)) {
+                                               // if one from alternative 
providers loaded use it as alternative //
+                                               preferred = alt;
+                                               break;
+                                       }
+                               }
+                               kmod_module_ref(preferred);
+                               kmod_module_unref_list(l_alt);
+                       }
+                       preferred_l = kmod_list_append(preferred_l, preferred);
+               }
+               kmod_list_foreach_reverse(l, preferred_l) {
+                       struct kmod_list *l2 = NULL;
+                       struct kmod_module *dep = (struct kmod_module *)l->data;
+                       final_l = kmod_list_insert_before(final_l, dep);
+                       if (dep->dep == NULL) {
+                               n = 
kmod_lookup_alias_from_moddep_file(mod->ctx, dep->name, &l2);
+                               if (n < 0) {
+                                       DBG(mod->ctx, "Loading subdeps for %s 
failed\n", dep->name);
+                                       kmod_module_unref_list(preferred_l);
+                                       return n;
+                               }
+                       }
+                       // remove duplicates from final dependencies and add 
all sub deps to current deps
+                       kmod_list_foreach_reverse(l2, dep->dep) {
+                               final_l = kmod_list_remove_data(final_l, 
l2->data);
+                               final_l = kmod_list_insert_before(final_l, 
l2->data);
+                       }
+               }
+               kmod_module_unref_list(mod->dep);
+               mod->dep = final_l;
+       } else
+               DBG(mod->ctx, "Alternatives indexes[%d]) didn't load\n", 
(int)KMOD_INDEX_MODULES_ALTERNATIVES);
+       return 0;
+}
+
 /**
  * kmod_module_get_dependencies:
  * @mod: kmod module
diff --git a/libkmod/libkmod.c b/libkmod/libkmod.c
index 213b424..e5646b2 100644
--- a/libkmod/libkmod.c
+++ b/libkmod/libkmod.c
@@ -40,7 +40,7 @@
 
 #define KMOD_HASH_SIZE (256)
 #define KMOD_LRU_MAX (128)
-#define _KMOD_INDEX_MODULES_SIZE KMOD_INDEX_MODULES_BUILTIN + 1
+#define _KMOD_INDEX_MODULES_SIZE KMOD_INDEX_MODULES_ALTERNATIVES + 1
 
 /**
  * SECTION:libkmod
@@ -59,6 +59,7 @@ static const struct {
        [KMOD_INDEX_MODULES_SYMBOL] = { .fn = "modules.symbols", .prefix = 
"alias "},
        [KMOD_INDEX_MODULES_BUILTIN_ALIAS] = { .fn = "modules.builtin.alias", 
.prefix = "" },
        [KMOD_INDEX_MODULES_BUILTIN] = { .fn = "modules.builtin", .prefix = ""},
+       [KMOD_INDEX_MODULES_ALTERNATIVES] = { .fn = "modules.alternatives", 
.prefix = ""},
 };
 
 static const char *const default_config_paths[] = {
@@ -655,6 +656,105 @@ char *kmod_search_moddep(struct kmod_ctx *ctx, const char 
*name)
        return line;
 }
 
+char *kmod_search_modalternatives(struct kmod_ctx *ctx, const char *name)
+{
+       struct index_file *idx;
+       char fn[PATH_MAX];
+       char *line;
+
+       if (ctx->indexes[KMOD_INDEX_MODULES_ALTERNATIVES]) {
+               DBG(ctx, "use mmaped index '%s' mod alternative=%s\n",
+                               
index_files[KMOD_INDEX_MODULES_ALTERNATIVES].fn, name);
+               return 
index_mm_search(ctx->indexes[KMOD_INDEX_MODULES_ALTERNATIVES],
+                                                                       name);
+       }
+
+       snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname,
+                                       
index_files[KMOD_INDEX_MODULES_ALTERNATIVES].fn);
+
+       DBG(ctx, "file=%s mod alternative name=%s\n", fn, name);
+
+       idx = index_file_open(fn);
+       if (idx == NULL) {
+               DBG(ctx, "could not open mod alternatives file '%s'\n", fn);
+               return NULL;
+       }
+
+       line = index_search(idx, name);
+       index_file_close(idx);
+
+       return line;
+}
+
+int kmod_lookup_alternatives_from_modalt_file(struct kmod_ctx *ctx, const char 
*name,
+                                               struct kmod_list **list)
+{
+       char *line;
+       int n = 0, err = 0;
+       const char *dirname;
+       size_t dirnamelen;
+       char buf[PATH_MAX];
+       char *p, *saveptr;
+
+       *list = NULL;
+       /*
+        * Module alternatives names do not contain ':'. Return early if we 
know it will
+        * not be found.
+        */
+       if (strchr(name, ':'))
+               return 0;
+
+       line = kmod_search_modalternatives(ctx, name);
+       if (line == NULL)
+               return 0;
+
+       p = strchr(line, ':');
+       if (p == NULL) {
+               err = -ENOENT;
+               goto fail;
+       }
+       *p = '\0';
+       p++;
+
+       dirname = kmod_get_dirname(ctx);
+       dirnamelen = strlen(dirname);
+       if (dirnamelen + 2 >= PATH_MAX)
+               return 0;
+       memcpy(buf, dirname, dirnamelen);
+       buf[dirnamelen] = '/';
+       dirnamelen++;
+       buf[dirnamelen] = '\0';
+
+       for (p = strtok_r(p, " \t", &saveptr); p != NULL;
+                                p = strtok_r(NULL, " \t", &saveptr)) {
+               struct kmod_module *mod;
+               const char *path;
+               path = path_join(p, dirnamelen, buf);
+               if (path == NULL) {
+                       ERR(ctx, "could not join path '%s' and '%s'.\n", 
dirname, p);
+                       err = -ENOENT;
+                       goto fail;
+               }
+               err = kmod_module_new_from_path(ctx, path, &mod);
+               if (err < 0) {
+                       ERR(ctx, "ctx=%p path=%s error=%s\n", ctx, path, 
strerror(-err));
+                       goto fail;
+               }
+               *list = kmod_list_append(*list, mod);
+               n++;
+       }
+
+       free(line);
+       return n;
+
+fail:
+       kmod_module_unref_list(*list);
+       *list = NULL;
+       free(line);
+       return err;
+}
+
+
 int kmod_lookup_alias_from_moddep_file(struct kmod_ctx *ctx, const char *name,
                                                struct kmod_list **list)
 {
@@ -681,6 +781,7 @@ int kmod_lookup_alias_from_moddep_file(struct kmod_ctx 
*ctx, const char *name,
 
                *list = kmod_list_append(*list, mod);
                kmod_module_parse_depline(mod, line);
+               update_mod_deps_correspond_to_alternatives(mod);
        }
 
 finish:
@@ -916,12 +1017,12 @@ KMOD_EXPORT int kmod_load_resources(struct kmod_ctx *ctx)
                                    &ctx->indexes[i]);
 
                /*
-                * modules.builtin.alias are considered optional since it's
+                * modules.builtin.alias and modules.alternatives are 
considered optional since it's
                 * recently added and older installations may not have it;
                 * we allow failing for any reason
                 */
                if (ret) {
-                       if (i != KMOD_INDEX_MODULES_BUILTIN_ALIAS)
+                       if (i != KMOD_INDEX_MODULES_BUILTIN_ALIAS && i != 
KMOD_INDEX_MODULES_ALTERNATIVES)
                                break;
                        ret = 0;
                }
@@ -1022,3 +1123,14 @@ enum kmod_file_compression_type 
kmod_get_kernel_compression(const struct kmod_ct
 {
        return ctx->kernel_compression;
 }
+
+bool is_index_available(const struct kmod_ctx *ctx, enum kmod_index index)
+{
+       if (ctx == NULL)
+               return false;
+       if (index >= _KMOD_INDEX_MODULES_SIZE)
+               return false;
+       if (ctx->indexes[index] == NULL)
+               return false;
+       return true;
+}
diff --git a/libkmod/libkmod.h b/libkmod/libkmod.h
index 7251aa7..f3bd3d7 100644
--- a/libkmod/libkmod.h
+++ b/libkmod/libkmod.h
@@ -25,6 +25,7 @@
 #include <stdarg.h>
 #include <stdbool.h>
 #include <inttypes.h>
+#include <limits.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -72,6 +73,7 @@ enum kmod_index {
        KMOD_INDEX_MODULES_SYMBOL,
        KMOD_INDEX_MODULES_BUILTIN_ALIAS,
        KMOD_INDEX_MODULES_BUILTIN,
+       KMOD_INDEX_MODULES_ALTERNATIVES,
        /* Padding to make sure enum is not mapped to char */
        _KMOD_INDEX_PAD = 1U << 31,
 };
@@ -264,6 +266,10 @@ int kmod_module_dependency_symbol_get_bind(const struct 
kmod_list *entry);
 uint64_t kmod_module_dependency_symbol_get_crc(const struct kmod_list *entry);
 void kmod_module_dependency_symbols_free_list(struct kmod_list *list);
 
+bool is_index_available(const struct kmod_ctx *ctx, enum kmod_index index);
+int update_mod_deps_correspond_to_alternatives(struct kmod_module *mod);
+const char *path_join(const char *path, size_t prefixlen, char buf[PATH_MAX]);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
-- 
2.35.6


Reply via email to