This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC] Make Iconv aware of hwcaps


Hello,

I'm working on a few ICONV charset conversion modules for S/390. S/390 provides a set of instructions which could do the job much faster than the common software loops. Unfortunately these instructions have been added in several stages. So there are CPUs out there supporting none, a few or all of the necessary instructions. The idea is to express the availability of the instructions using the hardware capabilities bitmap in order to load certain iconv modules only if the current hardware is actually capable of executing them. The existing dlopen mechanism is already able to deal with this by adding search paths to the name of a shared object if the object is specified without a path. Unfortunately this does not apply to the iconv stuff. The iconv modules currently are always loaded by passing a fully qualified path to the dlopen call. The path of an iconv module consists of the directory where the gconv_modules file has been found plus the module name.

One point is that I don't want to implement a software variant of every conversion S/390 will be able to do in hardware. That's why I already while reading in the gconv_module file would like to check if there is a specialized version of the iconv module available and if not would just ignore the line in the gconv_modules file and let the iconv algorithm find a different way to perform the conversion.

I've hacked together a small patch extending the iconv module load mechanism with the hwcaps paths and would like to hear your opinion about the general idea.

Jakub mentioned that this might also be solved using the STT_IFUNC mechanism. What's the status of this feature? Is it planned to be integrated in the near future?

Bye,

-Andreas-

Index: libc/iconv/gconv_conf.c
===================================================================
--- libc.orig/iconv/gconv_conf.c
+++ libc/iconv/gconv_conf.c
@@ -31,6 +31,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <sys/param.h>
+#include <ldsodefs.h>

 #include <bits/libc-lock.h>
 #include <gconv_int.h>
@@ -52,6 +53,11 @@ static const struct path_elem empty_path
    along the path.  */
 static const char gconv_conf_filename[] = "gconv-modules";

+/* List of the hardware capabilities we might end up using.  */
+static const struct r_strlenpair *capstr = NULL;
+static size_t ncapstr = 0;
+static size_t max_capstrlen = 0;
+
 /* Filename extension for the modules.  */
 #ifndef MODULE_EXT
 # define MODULE_EXT ".so"
@@ -242,6 +248,41 @@ insert_module (struct gconv_module *newp
   *rootp = newp;
 }

+/* Generate a path using DIRECTORY and MODULE_NAME and write it into
+   RESULT.  Between DIRECTORY and MODULE_NAME the function puts the
+   most specific hwcaps path where the module can be found in.  The
+   function return false if the module hasn't been found at all.  As
+   RESULT the caller has to specify a buffer at least as big as
+   dir_len + mod_len + max_capstrlen.  */
+static int
+internal_function
+expand_hwcap_paths (char *result,
+		    const char *directory, const size_t dir_len,
+		    const char *module_name, const size_t mod_len)
+{
+  char *path;
+  char *ins_point;
+  struct stat64 st;
+  int i;
+
+  path = alloca (dir_len + mod_len + max_capstrlen);
+  ins_point = __mempcpy (path, directory, dir_len);
+
+  for (i = 0; i < ncapstr; i++)
+    {
+      memcpy (ins_point, capstr[i].str, capstr[i].len);
+      memcpy (ins_point + capstr[i].len, module_name, mod_len);
+      ins_point[capstr[i].len + mod_len] = 0;
+
+      if (__xstat64 (_STAT_VER, path, &st) == 0)
+	{
+	  memcpy (result, path, dir_len + mod_len + capstr[i].len + 1);
+	  return 1;
+	}
+    }
+
+  return 0;
+}

 /* Add new module.  */
 static void
@@ -328,10 +369,13 @@ add_module (char *rp, const char *direct
   new_module = (struct gconv_module *) calloc (1,
 					       sizeof (struct gconv_module)
 					       + (wp - from)
-					       + dir_len + need_ext);
+					       + dir_len + need_ext
+					       + (dir_len ? max_capstrlen : 0));
   if (new_module != NULL)
     {
       char *tmp;
+      char *mod_name = module;
+      size_t mod_name_len = wp - module + need_ext;

       new_module->from_string = tmp = (char *) (new_module + 1);
       tmp = __mempcpy (tmp, from, to - from);
@@ -344,19 +388,122 @@ add_module (char *rp, const char *direct

new_module->module_name = tmp;

-      if (dir_len != 0)
-	tmp = __mempcpy (tmp, directory, dir_len);
-
-      tmp = __mempcpy (tmp, module, wp - module);
-
       if (need_ext)
-	memcpy (tmp - 1, gconv_module_ext, sizeof (gconv_module_ext));
+	{
+	  mod_name = alloca (mod_name_len);
+	  memcpy (mod_name, module, wp - module - 1);
+	  memcpy (mod_name + (wp - module) - 1, gconv_module_ext,
+		  sizeof (gconv_module_ext));
+	}
+
+      if (dir_len)
+	{
+	  /* The module name does not contain a path.  So try to
+	     prepend hwcap paths.  */
+	  if (!expand_hwcap_paths (tmp,
+				   directory, dir_len,
+				   mod_name, mod_name_len))
+	    {
+	      /* The module could not be found in any of the hwcap
+		 dirs or in the gconv conf file path - ignore it.  */
+	      free (new_module);
+	      return;
+	    }
+	}
+      else
+	/* For module name with path information the hwcap dirs are
+	   ignored.  */
+	memcpy (tmp, mod_name, mod_name_len);

       /* Now insert the new module data structure in our search tree.  */
       insert_module (new_module, 1);
     }
 }

+/* Generate a list of hwcaps path and store it into the file scope
+ variables CAPSTR, NCAPSTR and MAX_CAPSTRLEN. The list will only
+ contain hwcaps which actually exist when prepending them with
+ DIRECTORY. */
+static void
+internal_function
+prepare_hwcap_dirs (const char *directory, size_t dir_len)
+{
+ int i;
+ char *str = NULL;
+ struct stat64 st;
+ const struct r_strlenpair *temp_capstr = NULL;
+ struct r_strlenpair *new_capstr = NULL;
+ size_t temp_ncapstr, temp_max_capstrlen;
+ int *found_dirs = NULL;
+ char *strbuf = NULL;
+ int total;
+
+ /* This function in the dynamic loader generates a compressed list
+ of paths starting with the most specific path ending with an
+ empty path. */
+ temp_capstr = GLRO(dl_important_hwcaps) (GLRO(dl_platform), GLRO(dl_platformlen),
+ &temp_ncapstr, &temp_max_capstrlen);
+
+ if (!temp_capstr)
+ return;
+
+ str = malloc (dir_len + temp_max_capstrlen + 1);
+
+ if (!str)
+ goto fail1;
+
+ found_dirs = malloc (temp_ncapstr * sizeof (int));
+
+ if (!found_dirs)
+ goto fail2;
+
+ total = ncapstr = max_capstrlen = 0;
+ memcpy (str, directory, dir_len);
+
+ /* Determine the existing paths and calculate the total size to be
+ allocated for the result array. */
+ for (i = 0; i < temp_ncapstr; i++)
+ {
+ memcpy (&str[dir_len], temp_capstr[i].str, temp_capstr[i].len);
+ str[temp_capstr[i].len + dir_len] = 0;
+
+ if (__xstat64 (_STAT_VER, str, &st) == 0
+ && S_ISDIR (st.st_mode))
+ {
+ found_dirs[ncapstr++] = i;
+ total += temp_capstr[i].len + 1;
+ if (temp_capstr[i].len > max_capstrlen)
+ max_capstrlen = temp_capstr[i].len;
+ }
+ }
+
+ if (!ncapstr)
+ goto fail3;
+
+ new_capstr = malloc (ncapstr * sizeof (struct r_strlenpair) + total);
+ if (!new_capstr)
+ goto fail3;
+
+ strbuf = (char*)(new_capstr + ncapstr);
+
+ for (i = 0; i < ncapstr; i++)
+ {
+ new_capstr[i].len = temp_capstr[found_dirs[i]].len;
+ new_capstr[i].str = strbuf;
+ strbuf = __mempcpy (strbuf, temp_capstr[found_dirs[i]].str,
+ new_capstr[i].len);
+ *(strbuf++) = 0;
+ }
+
+ capstr = new_capstr;
+
+ fail3:
+ free (found_dirs);
+ fail2:
+ free ((void*)temp_capstr);
+ fail1:
+ free (str);
+}


 /* Read the next configuration file.  */
 static void
@@ -376,6 +523,8 @@ read_conf_file (const char *filename, co
   if (fp == NULL)
     return;

+  prepare_hwcap_dirs (directory, dir_len);
+
   /* No threads reading from this stream.  */
   __fsetlocking (fp, FSETLOCKING_BYCALLER);

@@ -422,6 +571,11 @@ read_conf_file (const char *filename, co

free (line);

+  ncapstr = 0;
+  max_capstrlen = 0;
+  free (capstr);
+  capstr = NULL;
+
   fclose (fp);
 }

Index: libc/elf/rtld.c
===================================================================
--- libc.orig/elf/rtld.c
+++ libc/elf/rtld.c
@@ -163,6 +163,7 @@ struct rtld_global_ro _rtld_global_ro at
     ._dl_open = _dl_open,
     ._dl_close = _dl_close,
     ._dl_tls_get_addr_soft = _dl_tls_get_addr_soft,
+    ._dl_important_hwcaps = _dl_important_hwcaps,
 #ifdef HAVE_DL_DISCOVER_OSVERSION
     ._dl_discover_osversion = _dl_discover_osversion
 #endif
Index: libc/sysdeps/generic/ldsodefs.h
===================================================================
--- libc.orig/sysdeps/generic/ldsodefs.h
+++ libc/sysdeps/generic/ldsodefs.h
@@ -657,6 +657,9 @@ struct rtld_global_ro
 		     Lmid_t nsid, int argc, char *argv[], char *env[]);
   void (*_dl_close) (void *map);
   void *(*_dl_tls_get_addr_soft) (struct link_map *);
+  const struct r_strlenpair *(*_dl_important_hwcaps) (const char *, size_t,
+						      size_t *, size_t *);
+
 #ifdef HAVE_DL_DISCOVER_OSVERSION
   int (*_dl_discover_osversion) (void);
 #endif


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]