halcc: Add support for hal-api dependency checking 71/304171/12 accepted/tizen_unified_riscv accepted/tizen/unified/20240116.155507 accepted/tizen/unified/riscv/20240117.041616
authorYoungjae Cho <y0.cho@samsung.com>
Thu, 11 Jan 2024 08:50:58 +0000 (17:50 +0900)
committerYoungjae Cho <y0.cho@samsung.com>
Mon, 15 Jan 2024 09:11:58 +0000 (18:11 +0900)
Optionally, hal-api can specify dependency, name and version of another
hal-api, that it must work with.

  | <!--hal-api-xxx.xml-->
  |
  | <manifest version="1.0" type="platform" level="2">
  |     <hal>
  |         <name>AAA</name>
  |         <version>1.0</version>
  |         <dependency>
  |             <hal>
  |                 <name>BBB</name>
  |                 <version>1.0</version>
  |             </hal>
  |             ...
  |         </dependency>
  |     </hal>
  |     <hal>
  |         <name>BBB</name>
  |         <version>1.5</version>
  |     </hal>
  |     ...
  | </manifest>

The halcc-object.c now provides
 - halcc_manifest_validate_hal_dependency()

This validates dependencies between hals within manifest. It try to
find compatible hal that specified by hal-api dependency. If it
couldn't find compatible one for the dependency, the resolution fails
and the hal is regarded as not supported.

Let's say a manifest contains below specification about 4 hals
 - AAA@1.5  : success
 - BBB@2.3  : success
 - CCC@3.3  : success
 - DDD@1.0  : success
If there are no dependencies between each other, no problem.

Case1] If AAA@1.5 depends on YYY@1.0.
 - AAA@1.5 -> YYY@1.0  : fail, YYY@1.0 is not specified
 - BBB@2.3  : success
 - CCC@3.3  : success
 - DDD@1.0  : success

Case2] If AAA@1.5 depends on BBB@1.0.
 - AAA@1.5 -> BBB@1.0  : fail, BBB@1.0 isn't compatible with
                         the specified BBB@2.3
 - BBB@2.3  : success
 - CCC@3.3  : success
 - DDD@1.0  : success

Caser3] If AAA@1.5 depends on BBB@2.0, CCC@4.0
 - AAA@1.5 -> BBB@2.0, CCC@4.0 : fail, BBB@2.0 is backward compatible
                         with the specified BBB@2.3, but CCC@4.0 isn't
                         compatible with the specified CCC@3.3.
 - BBB@2.3  : success
 - CCC@3.3  : success
 - DDD@1.0  : success
 If multiple dependencies are specified on a hal, it success only when
 all the dependencies are satisfied.

Case4] If AAA@1.5 depends on BBB@2.0
          and BBB@2.3 depends on CCC@3.0
          and CCC@3.3 depends on DDD@1.5
 - AAA@1.5 -> BBB@2.0  : fail, BBB@2.0 is backward compatible with the
                         specified BBB@2.3, but BBB@2.3 has failed to
                         validate dependency
 - BBB@2.3 -> CCC@3.0  : fail, CCC@3.0 is backward compatible with the
                         specified CCC@3.3, but CCC@3.3 has failed to
                         validate dependency
 - CCC@3.3 -> DDD@1.5  : fail, DDD@1.5 isn't compatible with the
                         specified DDD@1.0
 - DDD@1.0  : success
 The failure is propagated to all hals that have dependency on that
 failed hal.

Case5] If AAA@1.5 depends on BBB@2.1
          and BBB@2.3 depends on CCC@3.0
          and CCC@3.3 depends on AAA@1.0
 - AAA@1.5 -> BBB@2.1  : fail, cyclic dependency
 - BBB@2.3 -> CCC@3.0  : fail, cyclic dependency
 - CCC@3.3 -> AAA@1.0  : fail, cyclic dependency
 - DDD@1.0  : success
 All hals that make dependency cycle are failure.

Case6] If AAA@1.5 depends on BBB@2.0
          and BBB@2.3 depends on CCC@3.0
 - AAA@1.5 -> BBB@2.0  : success
 - BBB@2.3 -> CCC@3.0  : success
 - CCC@3.3  : success
 - DDD@1.0  : success

In addition, the tool hal-compatibility-checker has changed to validate
dependency, by default, before it checks compatibility.

Change-Id: I34ce0f78a9ae9c05f65c77b20199353c93072851
Signed-off-by: Youngjae Cho <y0.cho@samsung.com>
halcc/src/hal-compatibility-checker.c
halcc/src/halcc-object.c
halcc/src/halcc-object.h

index 1ee3f64..38272f6 100644 (file)
@@ -94,6 +94,7 @@ static void foreach_hal(void *data, void *user_data)
        halcc_hal *hal;
        const char *hal_name = NULL;
        int major, minor;
+       halcc_dependency_state_e state;
        halcc_compatibility_e compatible = HALCC_INCOMPATIBLE;
 
        hal = (halcc_hal *) data;
@@ -109,6 +110,12 @@ static void foreach_hal(void *data, void *user_data)
        if (halcc_hal_get_version(hal, &major, &minor, NULL) < 0)
                return;
 
+       if (halcc_hal_get_dependency_state(hal, &state) != 0)
+               return;
+
+       if (state != HALCC_DEPENDENCY_STATE_SUCCESS)
+               return;
+
        for (module = HAL_MODULE_UNKNOWN; module < HAL_MODULE_END; ++module) {
                char module_name[128] = { 0 , };
                unsigned int abi_version;
@@ -162,6 +169,8 @@ int halcc_check_compatibility(const char *hal_api_manifest_dir,
        if (ret < 0)
                goto out;
 
+       halcc_manifest_validate_hal_dependency(manifest);
+
        callback_data = (struct callback_data) { callback, user_data };
 
        halcc_manifest_foreach_hal(manifest, foreach_hal, &callback_data);
index 96698db..d9de779 100644 (file)
@@ -532,6 +532,192 @@ void halcc_manifest_foreach_hal(halcc_manifest *manifest,
        hashtable_foreach(manifest->hals, cb, user_data);
 }
 
+static void reset_hal_dependency_state(void *data, void *user_data)
+{
+       halcc_hal *hal = (halcc_hal *) data;
+
+       assert(hal);
+
+       halcc_hal_set_dependency_state(hal, HALCC_DEPENDENCY_STATE_NONE);
+}
+
+/**
+ * Check whether the dependencies specified by hal->dependencies are satisfied.
+ * It sets hal->dependency_state and returns the value.
+ */
+static halcc_dependency_state_e
+__validate_hal_dependency_state(halcc_manifest *manifest, halcc_hal *hal)
+{
+       halcc_hal *dependency;
+       halcc_dependency_state_e state = HALCC_DEPENDENCY_STATE_FAIL;
+       GHashTableIter iter;
+
+       assert(manifest);
+       assert(hal);
+
+       halcc_hal_get_dependency_state(hal, &state);
+
+       /* It has been checked already, return immediately. */
+       if (state != HALCC_DEPENDENCY_STATE_NONE)
+               return state;
+
+       /**
+        * Mark it as validating is underway.
+        *
+        * The dependency_state will eventually be changed into
+        * HALCC_DEPENDENCY_STATE_SUCCESS or HALCC_DEPENDENCY_STATE_FAIL
+        * before going out of this function.
+        */
+       halcc_hal_set_dependency_state(hal, HALCC_DEPENDENCY_STATE_VALIDATING);
+
+       /**
+        * As the hashtable_foreach() only accepts a single parameter, it is
+        * annoying that passing multiple parameter to iteration callback.
+        * So exceptionally iterate the hashtable directly instead of using
+        * hashtable_foreach().
+        *
+        * Iterate over the dependencies of the given hal. Check if there is compatible
+        * hal with that dependency in the manifest. It will success only when all of the given
+        * dependencies is compatible with one of a hal from the manifest. Otherwise, resolution
+        * fails, setting dependency_state to HALCC_DEPENDENCY_STATE_FAIL.
+        */
+       g_hash_table_iter_init(&iter, hal->dependencies);
+       while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &dependency)) {
+               const char *dependency_hal_name = NULL;
+               int dependency_hal_major = 0;
+               int dependency_hal_minor = 0;
+               halcc_hal *found = NULL;
+               int ret;
+
+               halcc_hal_get_name(dependency, &dependency_hal_name);
+               halcc_hal_get_version(dependency, &dependency_hal_major, &dependency_hal_minor, NULL);
+
+               ret = halcc_manifest_find_hal_forward_compatible(manifest,
+                       dependency_hal_name, dependency_hal_major, dependency_hal_minor,
+                       &found);
+               if (ret != 0) { /* There is no hal that meets dependency. */
+                       halcc_hal_set_dependency_state(hal, HALCC_DEPENDENCY_STATE_FAIL);
+                       return HALCC_DEPENDENCY_STATE_FAIL;
+               }
+
+               /* Recursively validate dependency of the found hal. */
+               state = __validate_hal_dependency_state(manifest, found);
+
+               assert(state != HALCC_DEPENDENCY_STATE_NONE);
+
+               if (state == HALCC_DEPENDENCY_STATE_SUCCESS)
+                       continue;
+
+               /**
+                * The below two cases are possible and they are all regarded as failure.
+                *
+                * 1) state == HALCC_DEPENDENCY_STATE_FAIL
+                *     : The found hal has already failed to validate its dependency,
+                *       thus the current hal is also set to fail.
+                *
+                * 2) state == HALCC_DEPENDENCY_STATE_VALIDATING
+                *     : Cyclic dependency, fail.
+                */
+               halcc_hal_set_dependency_state(hal, HALCC_DEPENDENCY_STATE_FAIL);
+
+               return HALCC_DEPENDENCY_STATE_FAIL;
+       }
+
+       halcc_hal_set_dependency_state(hal, HALCC_DEPENDENCY_STATE_SUCCESS);
+
+       return HALCC_DEPENDENCY_STATE_SUCCESS;
+}
+
+static void validate_hal_dependency_state(void *data, void *user_data)
+{
+       halcc_manifest *manifest;
+       halcc_hal *hal;
+
+       manifest = (halcc_manifest *) user_data;
+       hal = (halcc_hal *) data;
+
+       assert(manifest);
+       assert(hal);
+
+       __validate_hal_dependency_state(manifest, hal);
+}
+
+/**
+ * Validate dependencies between hals within manifest.
+ *
+ * Let's say a manifest contains below specification about 4 hals
+ *  - AAA@1.5  : success
+ *  - BBB@2.3  : success
+ *  - CCC@3.3  : success
+ *  - DDD@1.0  : success
+ * If there are no dependencies between each other, no problem.
+ *
+ * Case1] If AAA@1.5 depends on YYY@1.0.
+ *  - AAA@1.5 -> YYY@1.0  : fail, YYY@1.0 is not specified
+ *  - BBB@2.3  : success
+ *  - CCC@3.3  : success
+ *  - DDD@1.0  : success
+ *
+ * Case2] If AAA@1.5 depends on BBB@1.0.
+ *  - AAA@1.5 -> BBB@1.0  : fail, BBB@1.0 isn't compatible with
+ *                          the specified BBB@2.3
+ *  - BBB@2.3  : success
+ *  - CCC@3.3  : success
+ *  - DDD@1.0  : success
+ *
+ * Caser3] If AAA@1.5 depends on BBB@2.0, CCC@4.0
+ *  - AAA@1.5 -> BBB@2.0, CCC@4.0 : fail, BBB@2.0 is backward compatible
+ *                          with the specified BBB@2.3, but CCC@4.0 isn't
+ *                          compatible with the specified CCC@3.3.
+ *  - BBB@2.3  : success
+ *  - CCC@3.3  : success
+ *  - DDD@1.0  : success
+ *  If multiple dependencies are specified, it success only when all the
+ *  dependencies are satisfied.
+ *
+ * Case4] If AAA@1.5 depends on BBB@2.0
+ *           and BBB@2.3 depends on CCC@3.0
+ *           and CCC@3.3 depends on DDD@1.5
+ *  - AAA@1.5 -> BBB@2.0  : fail, BBB@2.0 is backward compatible with the
+ *                          specified BBB@2.3, but BBB@2.3 has failed to
+ *                          validate dependency
+ *  - BBB@2.3 -> CCC@3.0  : fail, CCC@3.0 is backward compatible with the
+ *                          specified CCC@3.3, but CCC@3.3 has failed to
+ *                          validate dependency
+ *  - CCC@3.3 -> DDD@1.5  : fail, DDD@1.5 isn't compatible with the
+ *                          specified DDD@1.0
+ *  - DDD@1.0  : success
+ *  The failure is propagated to all hals that have dependency on that
+ *  failed hal.
+ *
+ * Case5] If AAA@1.5 depends on BBB@2.1
+ *           and BBB@2.3 depends on CCC@3.0
+ *           and CCC@3.3 depends on AAA@1.0
+ *  - AAA@1.5 -> BBB@2.1  : fail, cyclic dependency
+ *  - BBB@2.3 -> CCC@3.0  : fail, cyclic dependency
+ *  - CCC@3.3 -> AAA@1.0  : fail, cyclic dependency
+ *  - DDD@1.0  : success
+ *  All hals that make dependency cycle are failure.
+ *
+ * Case6] If AAA@1.5 depends on BBB@2.0
+ *           and BBB@2.3 depends on CCC@3.0
+ *  - AAA@1.5 -> BBB@2.0  : success
+ *  - BBB@2.3 -> CCC@3.0  : success
+ *  - CCC@3.3  : success
+ *  - DDD@1.0  : success
+ */
+void halcc_manifest_validate_hal_dependency(halcc_manifest *manifest)
+{
+       if (!manifest)
+               return;
+
+       /* Reset all */
+       halcc_manifest_foreach_hal(manifest, reset_hal_dependency_state, NULL);
+
+       /* Validate dependency */
+       halcc_manifest_foreach_hal(manifest, validate_hal_dependency_state, manifest);
+}
+
 int halcc_hal_new(halcc_hal **hal)
 {
        halcc_hal *h;
@@ -694,6 +880,26 @@ void halcc_hal_foreach_interface(halcc_hal *hal, halcc_iter_cb cb, void *user_da
        hashtable_foreach(hal->interfaces, cb, user_data);
 }
 
+int halcc_hal_set_dependency_state(halcc_hal *hal, halcc_dependency_state_e dependency_state)
+{
+       if (!hal)
+               return -EINVAL;
+
+       hal->dependency_state = dependency_state;
+
+       return 0;
+}
+
+int halcc_hal_get_dependency_state(halcc_hal *hal, halcc_dependency_state_e *dependency_state)
+{
+       if (!hal || !dependency_state)
+               return -EINVAL;
+
+       *dependency_state = hal->dependency_state;
+
+       return 0;
+}
+
 int halcc_interface_new(halcc_interface **interface)
 {
        halcc_interface *iface;
index 799b984..f657617 100644 (file)
@@ -76,6 +76,7 @@ void halcc_manifest_remove_hal(halcc_manifest *manifest,
        const char *hal_name, int major, int minor);
 void halcc_manifest_foreach_hal(halcc_manifest *manifest,
        halcc_iter_cb cb, void *user_data);
+void halcc_manifest_validate_hal_dependency(halcc_manifest *manifest);
 
 int halcc_hal_new(halcc_hal **hal);
 void halcc_hal_free(halcc_hal *hal);
@@ -93,6 +94,8 @@ int halcc_hal_add_interface(halcc_hal *hal, halcc_interface *interface);
 void halcc_hal_remove_interface(halcc_hal *hal,
        const char *interface_name, const char *instance_id);
 void halcc_hal_foreach_interface(halcc_hal *hal, halcc_iter_cb cb, void *user_data);
+int halcc_hal_set_dependency_state(halcc_hal *hal, halcc_dependency_state_e dependency_state);
+int halcc_hal_get_dependency_state(halcc_hal *hal, halcc_dependency_state_e *dependency_state);
 
 int halcc_interface_new(halcc_interface **interface);
 void halcc_interface_free(halcc_interface *interface);