ADHD: Facilitate sorting of linker sets

Details

  It is desirable to have an ordering applied to the threads in gavd.
  One reason for this might be to ensure that the audio system is set
  to the factory default at power-on, or to ensure that a 'work list'
  management system is ready-to-use before any other threads are
  instantiated.

  To be able to provide priority to threads during startup, we need to
  be able to order the set of thread descriptors, which is a linker
  set.

  This change adds better comments to the linker set interface, and
  adds 'LINKERSET_SORT'.

  To add this functionality, LINKERSET_ADD_ITEM had to be changed to
  remove the const-ness on the data; without this removal, the
  linker set is put into R/O memory.

Testing

  Built chromiumos for tegra2_aebl
  Built chromiumos for x86-mario (compilation only, gavd is a nop on x86)

  Booted on tegra2_aebl, observed that the daemon runs correctly.

BUG=chromium-os:19558
TEST=See above.

Change-Id: Id119e4ad9392a1db7e1f943e341c5b0558cb8f77
Signed-off-by: Taylor Hutt <thutt@chromium.org>
diff --git a/gavd/linkerset.h b/gavd/linkerset.h
index 18d6bac..47ff6cd 100644
--- a/gavd/linkerset.h
+++ b/gavd/linkerset.h
@@ -4,6 +4,8 @@
  */
 #if !defined(_LINKERSET_H_)
 #define _LINKERSET_H_
+#include <assert.h>
+#include <stdlib.h>
 
 #define WEAK __attribute__((weak))
 
@@ -46,14 +48,6 @@
  *
  *     Data is inserted into the linker set using LINKERSET_ADD_ITEM.
  *
- *     The '_name' argument is the name of the linker set, as
- *     described above.
- *
- *     The '_desc_name' argument is the name of the variable which
- *     should be referenced by the linker set entry.  This variable
- *     should be typed as the 'linker set data type', as described
- *     above.  Generally this variable is statically initialized.
- *
  *   o Declare the linker set
  *
  *     Somewhere prior to iterating over the linker set, the set must
@@ -68,18 +62,6 @@
  *     To process the elements in a linker set, use the
  *     'LINKERSET_ITERATE' macro.
  *
- *     The '_name' argument is the name of the linker set, as
- *     described above.
- *
- *     The '_var' argument is used by the macro to create a local
- *     variable which can be used to access the data associated with
- *     each linker set member.  The created variable will be typed as
- *     the 'linker set data type', as described above.
- *
- *     The '_body' argument is any custom code that you want to be
- *     executed for each element in the linker set.  You may use the
- *     '_var' argument to access fields of the data type.
- *
  *  NOTES:
  *
  *    It is possible to have a linker set that has no elements; in
@@ -127,7 +109,15 @@
  *   Both the linker set and the associated data are regular R/W data.
  */
 
-/* The __start_<name> & __stop_<name> symbols are generated
+#define LINKERSET_START(_name) \
+    &__start_##_name
+
+#define LINKERSET_STOP(_name) \
+    &__stop_##_name
+
+/* LINKERSET_DECLARE: Enable access to a linker set in a source file.
+ *
+ * The __start_<name> & __stop_<name> symbols are generated
  * automatically by the linker, but they are not marked as global.
  * Without otherwise being marked as global, the symbols will not be
  * accessible to the code generated by these macros.
@@ -143,18 +133,34 @@
     __asm__(".global __start_" #_name);         \
     __asm__(".global __stop_" #_name)
 
-#define LINKERSET_ADD_ITEM(_name, _desc_name)                          \
-    static void const * const __##_name##_ptr_##_desc_name             \
+/* LINKERSET_ADD_ITEM: Add an item to a linker set.
+ *
+ *  _name     : The name of the linker set, as described above.
+ *
+ *  _desc_name: The name of the variable which should be referenced by
+ *              the linker set entry.  This variable should be typed
+ *              as the 'linker set data type', as described above.
+ *              Generally this variable is statically initialized.
+ */
+#define LINKERSET_ADD_ITEM(_name, _desc_name)                    \
+    static void const *__##_name##_ptr_##_desc_name              \
     __attribute__((section(#_name),used)) = &_desc_name
 
-#define LINKERSET_START(_name) \
-    &__start_##_name
-
-#define LINKERSET_STOP(_name) \
-    &__stop_##_name
-
+/* LINKERSET_ITERATE: Iterate over an entire linker set.
+ *
+ *  _name: The name of the linker set, as described above.
+ *
+ *  _var : Used to create a local variable which can be used to access
+ *         the data associated with each linker set member.  The
+ *         created variable will be typed as a 'pointer to linker set
+ *         data type', as described above.
+ *
+ *  _body: Custom code that you want to be executed for each element
+ *         in the linker set.  You may use the '_var' argument to
+ *         access fields of the data type.
+ */
 #define LINKERSET_ITERATE(_name, _var, _body)           \
-    {                                                   \
+    do {                                                \
         _name##_t **_beg = LINKERSET_START(_name);      \
         _name##_t **_end = LINKERSET_STOP(_name);       \
         while (_beg < _end) {                           \
@@ -162,6 +168,39 @@
             _body;                                      \
             ++_beg;                                     \
         }                                               \
-    }
+    } while (0)
 
+/* LINKERSET_SIZE: Produces the number of elements in a linker set.
+ *
+ * _name: The name of the linker set, as described above.
+ * _type: The desired type of the result.
+ *
+ * Note: This macro creates a result that is ptrdiff_t, and it may
+ *       need further casting to meet size requirements.  Be sure to
+ *       test code using this macro on a machine where:
+ *
+ *          sizeof(size_t) != sizeof(ptrdiff_t)
+ *          sizeof(size_t) == sizeof(ptrdiff_t)
+ */
+#define LINKERSET_SIZE_PTRDIFF(_name)                   \
+    (LINKERSET_STOP(_name) - LINKERSET_START(_name))
+
+#define LINKERSET_SIZE(_name, _type)            \
+    (_type)LINKERSET_SIZE_PTRDIFF(_name)
+
+/* LINKERSET_SORT: Sort a linker set using qsort().
+ *
+ * _name   : The name of the linker set, as described above.
+ * _compare: A function which will be used as the compare function by
+ *           qsort().  Each argument passed to '_compare' will be of
+ *           type 'pointer to pointer to linker set data type'.
+ */
+#define LINKERSET_SORT(_name, _compare)                                 \
+    do {                                                                \
+        const size_t n = LINKERSET_SIZE(_name, size_t);                 \
+        assert((ptrdiff_t)n ==  /* Truncation? */                       \
+               LINKERSET_SIZE_PTRDIFF(_name));                          \
+        qsort(LINKERSET_START(_name), n,                                \
+              sizeof(LINKERSET_START(_name)), _compare);                \
+    } while (0)
 #endif