← Back to team overview

maria-developers team mailing list archive

Re: (MDEV-4403) Attempting to use cassandra storage engine causes "service 'my_snprintf_service' interface version mismatch"

 

Hi Sergei,

well, I guess there is no alternative way to workaround it. :(
Ok to push.

Regards,
Sergei

On Mon, Dec 09, 2013 at 01:51:51PM +0100, Sergei Golubchik wrote:
> Hi.
> 
> I don't see my commit emails on the list, so here you are:
> 
> first patch is the bug fix itself.
> second - cleanup of the existing code after the bug fix.
> 
> Regards,
> Sergei
> 

> ------------------------------------------------------------
> revno: 3927
> fixes bug: https://mariadb.atlassian.net/browse/MDEV-4403
> committer: Sergei Golubchik <sergii@xxxxxxxxx>
> branch nick: 10.0
> timestamp: Mon 2013-12-09 12:39:13 +0100
> message:
>   MDEV-4403 Attempting to use cassandra storage engine causes "service 'my_snprintf_service' interface version mismatch"
>   
>   When a DSO is loaded we rewrite service pointers to point to the actual service structures.
>   But when a DSO is unloaded, we have to restore their original values, in case this DSO
>   wasn't removed from memory on dlclose() and is later loaded again.
> added:
>   mysql-test/suite/plugins/r/cassandra_reinstall.result
>   mysql-test/suite/plugins/t/cassandra_reinstall.test
> modified:
>   sql/sql_plugin.cc
>   sql/sql_plugin.h
> diff:
> === added file 'mysql-test/suite/plugins/r/cassandra_reinstall.result'
> --- mysql-test/suite/plugins/r/cassandra_reinstall.result	1970-01-01 00:00:00 +0000
> +++ mysql-test/suite/plugins/r/cassandra_reinstall.result	2013-12-09 11:39:13 +0000
> @@ -0,0 +1,14 @@
> +install soname 'ha_cassandra';
> +select plugin_name,plugin_status,plugin_library from information_schema.plugins where plugin_name = 'cassandra';
> +plugin_name	plugin_status	plugin_library
> +CASSANDRA	ACTIVE	ha_cassandra.so
> +uninstall plugin cassandra;
> +select plugin_name,plugin_status,plugin_library from information_schema.plugins where plugin_name = 'cassandra';
> +plugin_name	plugin_status	plugin_library
> +install soname 'ha_cassandra';
> +select plugin_name,plugin_status,plugin_library from information_schema.plugins where plugin_name = 'cassandra';
> +plugin_name	plugin_status	plugin_library
> +CASSANDRA	ACTIVE	ha_cassandra.so
> +uninstall plugin cassandra;
> +select plugin_name,plugin_status,plugin_library from information_schema.plugins where plugin_name = 'cassandra';
> +plugin_name	plugin_status	plugin_library
> 
> === added file 'mysql-test/suite/plugins/t/cassandra_reinstall.test'
> --- mysql-test/suite/plugins/t/cassandra_reinstall.test	1970-01-01 00:00:00 +0000
> +++ mysql-test/suite/plugins/t/cassandra_reinstall.test	2013-12-09 11:39:13 +0000
> @@ -0,0 +1,16 @@
> +#
> +# MDEV-4403 Attempting to use cassandra storage engine causes "service 'my_snprintf_service' interface version mismatch"
> +#
> +if (!$HA_CASSANDRA_SO) {
> +  skip No Cassandra engine;
> +}
> +
> +install soname 'ha_cassandra';
> +select plugin_name,plugin_status,plugin_library from information_schema.plugins where plugin_name = 'cassandra';
> +uninstall plugin cassandra;
> +select plugin_name,plugin_status,plugin_library from information_schema.plugins where plugin_name = 'cassandra';
> +install soname 'ha_cassandra';
> +select plugin_name,plugin_status,plugin_library from information_schema.plugins where plugin_name = 'cassandra';
> +uninstall plugin cassandra;
> +select plugin_name,plugin_status,plugin_library from information_schema.plugins where plugin_name = 'cassandra';
> +
> 
> === modified file 'sql/sql_plugin.cc'
> --- sql/sql_plugin.cc	2013-11-13 22:03:48 +0000
> +++ sql/sql_plugin.cc	2013-12-09 11:39:13 +0000
> @@ -309,6 +309,7 @@ static void unlock_variables(THD *thd, s
>  static void cleanup_variables(THD *thd, struct system_variables *vars);
>  static void plugin_vars_free_values(sys_var *vars);
>  static void restore_pluginvar_names(sys_var *first);
> +static void restore_ptr_backup(uint n, st_ptr_backup *backup);
>  static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin);
>  static void intern_plugin_unlock(LEX *lex, plugin_ref plugin);
>  static void reap_plugins(void);
> @@ -473,9 +474,16 @@ static st_plugin_dl *plugin_dl_insert_or
>  #endif /* HAVE_DLOPEN */
>  
>  
> -static inline void free_plugin_mem(struct st_plugin_dl *p)
> +static void free_plugin_mem(struct st_plugin_dl *p)
>  {
>  #ifdef HAVE_DLOPEN
> +  if (p->ptr_backup)
> +  {
> +    DBUG_ASSERT(p->nbackups);
> +    DBUG_ASSERT(p->handle);
> +    restore_ptr_backup(p->nbackups, p->ptr_backup);
> +    my_free(p->ptr_backup);
> +  }
>    if (p->handle)
>      dlclose(p->handle);
>  #endif
> @@ -706,6 +714,7 @@ static st_plugin_dl *plugin_dl_add(const
>    uint plugin_dir_len, dummy_errors, dlpathlen, i;
>    struct st_plugin_dl *tmp= 0, plugin_dl;
>    void *sym;
> +  st_ptr_backup tmp_backup[array_elements(list_of_services)];
>    DBUG_ENTER("plugin_dl_add");
>    DBUG_PRINT("enter", ("dl->str: '%s', dl->length: %d",
>                         dl->str, (int) dl->length));
> @@ -772,7 +781,8 @@ static st_plugin_dl *plugin_dl_add(const
>    {
>      if ((sym= dlsym(plugin_dl.handle, list_of_services[i].name)))
>      {
> -      uint ver= (uint)(intptr)*(void**)sym;
> +      void **ptr= (void **)sym;
> +      uint ver= (uint)(intptr)*ptr;
>        if (ver > list_of_services[i].version ||
>          (ver >> 8) < (list_of_services[i].version >> 8))
>        {
> @@ -783,8 +793,22 @@ static st_plugin_dl *plugin_dl_add(const
>          report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, ENOEXEC, buf);
>          goto ret;
>        }
> -      *(void**)sym= list_of_services[i].service;
> +      tmp_backup[plugin_dl.nbackups++].save(ptr);
> +      *ptr= list_of_services[i].service;
> +    }
> +  }
> +
> +  if (plugin_dl.nbackups)
> +  {
> +    size_t bytes= plugin_dl.nbackups * sizeof(plugin_dl.ptr_backup[0]);
> +    plugin_dl.ptr_backup= (st_ptr_backup *)my_malloc(bytes, MYF(0));
> +    if (!plugin_dl.ptr_backup)
> +    {
> +      restore_ptr_backup(plugin_dl.nbackups, tmp_backup);
> +      report_error(report, ER_OUTOFMEMORY, bytes);
> +      goto ret;
>      }
> +    memcpy(plugin_dl.ptr_backup, tmp_backup, bytes);
>    }
>  
>    /* Duplicate and convert dll name */
> @@ -4017,3 +4041,38 @@ sys_var *find_plugin_sysvar(st_plugin_in
>    return 0;
>  }
>  
> +/*
> +  On dlclose() we need to restore values of all symbols that we've modified in
> +  the DSO. The reason is - the DSO might not actually be unloaded, so on the
> +  next dlopen() these symbols will have old values, they won't be
> +  reinitialized.
> +
> +  Perhaps, there can be many reason, why a DSO won't be unloaded. Strictly
> +  speaking, it's implementation defined whether to unload an unused DSO or to
> +  keep it in memory.
> +
> +  In particular, this happens for some plugins: In 2009 a new ELF stub was
> +  introduced, see Ulrich Drepper's email "Unique symbols for C++"
> +  http://www.redhat.com/archives/posix-c++-wg/2009-August/msg00002.html
> +
> +  DSO that has objects with this stub (STB_GNU_UNIQUE) cannot be unloaded
> +  (this is mentioned in the email, see the url above).
> +
> +  These "unique" objects are, for example, static variables in templates,
> +  in inline functions, in classes. So any DSO that uses them can
> +  only be loaded once. And because Boost has them, any DSO that uses Boost
> +  almost certainly cannot be unloaded.
> +
> +  To know whether a particular DSO has these objects, one can use
> +
> +    readelf -s /path/to/plugin.so|grep UNIQUE
> +
> +  There's nothing we can do about it, but to reset the DSO to its initial
> +  state before dlclose().
> +*/
> +static void restore_ptr_backup(uint n, st_ptr_backup *backup)
> +{
> +  while (n--)
> +    (backup++)->restore();
> +}
> +
> 
> === modified file 'sql/sql_plugin.h'
> --- sql/sql_plugin.h	2013-06-03 07:57:34 +0000
> +++ sql/sql_plugin.h	2013-12-09 11:39:13 +0000
> @@ -81,15 +81,24 @@ typedef struct st_mysql_show_var SHOW_VA
>  
>  /* A handle for the dynamic library containing a plugin or plugins. */
>  
> +struct st_ptr_backup {
> +  void **ptr;
> +  void *value;
> +  void save(void **p) { ptr= p; value= *p; }
> +  void restore() { *ptr= value; }
> +};
> +
>  struct st_plugin_dl
>  {
>    LEX_STRING dl;
>    void *handle;
>    struct st_maria_plugin *plugins;
> +  st_ptr_backup *ptr_backup;
> +  uint nbackups;
> +  uint ref_count;            /* number of plugins loaded from the library */
>    int mysqlversion;
>    int mariaversion;
>    bool   allocated;
> -  uint ref_count;            /* number of plugins loaded from the library */
>  };
>  
>  /* A handle of a plugin */

> ------------------------------------------------------------
> revno: 3928
> committer: Sergei Golubchik <sergii@xxxxxxxxx>
> branch nick: 10.0
> timestamp: Mon 2013-12-09 12:39:19 +0100
> message:
>   remove sys_var specific restore_pluginvar_names() function,
>   use generic restore_ptr_backup() approach
> modified:
>   sql/sql_plugin.cc
>   sql/sql_plugin.h
> diff:
> === modified file 'sql/sql_plugin.cc'
> --- sql/sql_plugin.cc	2013-12-09 11:39:13 +0000
> +++ sql/sql_plugin.cc	2013-12-09 11:39:19 +0000
> @@ -257,15 +257,6 @@ class sys_var_pluginvar: public sys_var
>  public:
>    struct st_plugin_int *plugin;
>    struct st_mysql_sys_var *plugin_var;
> -  /**
> -    variable name from whatever is hard-coded in the plugin source
> -    and doesn't have pluginname- prefix is replaced by an allocated name
> -    with a plugin prefix. When plugin is uninstalled we need to restore the
> -    pointer to point to the hard-coded value, because plugin may be
> -    installed/uninstalled many times without reloading the shared object.
> -  */
> -  const char *orig_pluginvar_name;
> -
>    static void *operator new(size_t size, MEM_ROOT *mem_root)
>    { return (void*) alloc_root(mem_root, size); }
>    static void operator delete(void *ptr_arg,size_t size)
> @@ -278,7 +269,7 @@ class sys_var_pluginvar: public sys_var
>               (plugin_var_arg->flags & PLUGIN_VAR_READONLY ? READONLY : 0),
>               0, -1, NO_ARG, pluginvar_show_type(plugin_var_arg), 0, 0,
>               VARIABLE_NOT_IN_BINLOG, NULL, NULL, NULL),
> -    plugin_var(plugin_var_arg), orig_pluginvar_name(plugin_var_arg->name)
> +    plugin_var(plugin_var_arg)
>    { plugin_var->name= name_arg; }
>    sys_var_pluginvar *cast_pluginvar() { return this; }
>    bool check_update_type(Item_result type);
> @@ -308,7 +299,6 @@ static bool register_builtin(struct st_m
>  static void unlock_variables(THD *thd, struct system_variables *vars);
>  static void cleanup_variables(THD *thd, struct system_variables *vars);
>  static void plugin_vars_free_values(sys_var *vars);
> -static void restore_pluginvar_names(sys_var *first);
>  static void restore_ptr_backup(uint n, st_ptr_backup *backup);
>  static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin);
>  static void intern_plugin_unlock(LEX *lex, plugin_ref plugin);
> @@ -1122,7 +1112,6 @@ static bool plugin_add(MEM_ROOT *tmp_roo
>      if (!(tmp_plugin_ptr= plugin_insert_or_reuse(&tmp)))
>      {
>        mysql_del_sys_var_chain(tmp.system_vars);
> -      restore_pluginvar_names(tmp.system_vars);
>        goto err;
>      }
>      plugin_array_version++;
> @@ -1139,6 +1128,8 @@ static bool plugin_add(MEM_ROOT *tmp_roo
>  
>  err:
>      errs++;
> +    if (tmp.nbackups)
> +      restore_ptr_backup(tmp.nbackups, tmp.ptr_backup);
>      if (name->str)
>        break;
>    }
> @@ -1217,7 +1208,7 @@ static void plugin_del(struct st_plugin_
>    mysql_rwlock_wrlock(&LOCK_system_variables_hash);
>    mysql_del_sys_var_chain(plugin->system_vars);
>    mysql_rwlock_unlock(&LOCK_system_variables_hash);
> -  restore_pluginvar_names(plugin->system_vars);
> +  restore_ptr_backup(plugin->nbackups, plugin->ptr_backup);
>    plugin_vars_free_values(plugin->system_vars);
>    my_hash_delete(&plugin_hash[plugin->plugin->type], (uchar*)plugin);
>    plugin_dl_del(plugin->plugin_dl);
> @@ -2938,16 +2929,6 @@ static st_bookmark *register_var(const c
>    return result;
>  }
>  
> -static void restore_pluginvar_names(sys_var *first)
> -{
> -  for (sys_var *var= first; var; var= var->next)
> -  {
> -    sys_var_pluginvar *pv= var->cast_pluginvar();
> -    pv->plugin_var->name= pv->orig_pluginvar_name;
> -  }
> -}
> -
> -
>  /*
>    returns a pointer to the memory which holds the thd-local variable or
>    a pointer to the global variable if thd==null.
> @@ -3823,7 +3804,7 @@ static my_option *construct_help_options
>      to get the correct (not double-prefixed) help text.
>      We won't need @@sysvars anymore and don't care about their proper names.
>    */
> -  restore_pluginvar_names(p->system_vars);
> +  restore_ptr_backup(p->nbackups, p->ptr_backup);
>  
>    if (construct_options(mem_root, p, opts))
>      DBUG_RETURN(NULL);
> @@ -3868,6 +3849,7 @@ static int test_plugin_options(MEM_ROOT
>    sys_var *v __attribute__((unused));
>    struct st_bookmark *var;
>    uint len, count= EXTRA_OPTIONS;
> +  st_ptr_backup *tmp_backup= 0;
>    DBUG_ENTER("test_plugin_options");
>    DBUG_ASSERT(tmp->plugin && tmp->name.str);
>  
> @@ -3940,59 +3922,85 @@ static int test_plugin_options(MEM_ROOT
>      plugin_name= tmp->name;
>  
>    error= 1;
> -  for (opt= tmp->plugin->system_vars; opt && *opt; opt++)
> +
> +  if (tmp->plugin->system_vars)
>    {
> -    st_mysql_sys_var *o= *opt;
> +    for (len=0, opt= tmp->plugin->system_vars; *opt; len++, opt++) /* no-op */;
> +    tmp_backup= (st_ptr_backup *)my_alloca(len * sizeof(tmp_backup[0]));
> +    DBUG_ASSERT(tmp->nbackups == 0);
> +    DBUG_ASSERT(tmp->ptr_backup == 0);
>  
> -    /*
> -      PLUGIN_VAR_STR command-line options without PLUGIN_VAR_MEMALLOC, point
> -      directly to values in the argv[] array. For plugins started at the
> -      server startup, argv[] array is allocated with load_defaults(), and
> -      freed when the server is shut down.  But for plugins loaded with
> -      INSTALL PLUGIN, the memory allocated with load_defaults() is freed with
> -      freed() at the end of mysql_install_plugin(). Which means we cannot
> -      allow any pointers into that area.
> -      Thus, for all plugins loaded after the server was started,
> -      we copy string values to a plugin's memroot.
> -    */
> -    if (mysqld_server_started &&
> -        ((o->flags & (PLUGIN_VAR_STR | PLUGIN_VAR_NOCMDOPT |
> -                       PLUGIN_VAR_MEMALLOC)) == PLUGIN_VAR_STR))
> +    for (opt= tmp->plugin->system_vars; *opt; opt++)
>      {
> -      sysvar_str_t* str= (sysvar_str_t *)o;
> -      if (*str->value)
> -        *str->value= strdup_root(mem_root, *str->value);
> -    }
> +      st_mysql_sys_var *o= *opt;
>  
> -    if (o->flags & PLUGIN_VAR_NOSYSVAR)
> -      continue;
> -    if ((var= find_bookmark(plugin_name.str, o->name, o->flags)))
> -      v= new (mem_root) sys_var_pluginvar(&chain, var->key + 1, o);
> -    else
> +      /*
> +        PLUGIN_VAR_STR command-line options without PLUGIN_VAR_MEMALLOC, point
> +        directly to values in the argv[] array. For plugins started at the
> +        server startup, argv[] array is allocated with load_defaults(), and
> +        freed when the server is shut down.  But for plugins loaded with
> +        INSTALL PLUGIN, the memory allocated with load_defaults() is freed with
> +        freed() at the end of mysql_install_plugin(). Which means we cannot
> +        allow any pointers into that area.
> +        Thus, for all plugins loaded after the server was started,
> +        we copy string values to a plugin's memroot.
> +      */
> +      if (mysqld_server_started &&
> +          ((o->flags & (PLUGIN_VAR_STR | PLUGIN_VAR_NOCMDOPT |
> +                         PLUGIN_VAR_MEMALLOC)) == PLUGIN_VAR_STR))
> +      {
> +        sysvar_str_t* str= (sysvar_str_t *)o;
> +        if (*str->value)
> +          *str->value= strdup_root(mem_root, *str->value);
> +      }
> +
> +      if (o->flags & PLUGIN_VAR_NOSYSVAR)
> +        continue;
> +      tmp_backup[tmp->nbackups++].save(&o->name);
> +      if ((var= find_bookmark(plugin_name.str, o->name, o->flags)))
> +        v= new (mem_root) sys_var_pluginvar(&chain, var->key + 1, o);
> +      else
> +      {
> +        len= plugin_name.length + strlen(o->name) + 2;
> +        varname= (char*) alloc_root(mem_root, len);
> +        strxmov(varname, plugin_name.str, "-", o->name, NullS);
> +        my_casedn_str(&my_charset_latin1, varname);
> +        convert_dash_to_underscore(varname, len-1);
> +        v= new (mem_root) sys_var_pluginvar(&chain, varname, o);
> +      }
> +      DBUG_ASSERT(v); /* check that an object was actually constructed */
> +    } /* end for */
> +
> +    if (tmp->nbackups)
>      {
> -      len= plugin_name.length + strlen(o->name) + 2;
> -      varname= (char*) alloc_root(mem_root, len);
> -      strxmov(varname, plugin_name.str, "-", o->name, NullS);
> -      my_casedn_str(&my_charset_latin1, varname);
> -      convert_dash_to_underscore(varname, len-1);
> -      v= new (mem_root) sys_var_pluginvar(&chain, varname, o);
> -    }
> -    DBUG_ASSERT(v); /* check that an object was actually constructed */
> -  } /* end for */
> -  if (chain.first)
> -  {
> -    chain.last->next = NULL;
> -    if (mysql_add_sys_var_chain(chain.first))
> +      size_t bytes= tmp->nbackups * sizeof(tmp->ptr_backup[0]);
> +      tmp->ptr_backup= (st_ptr_backup *)alloc_root(mem_root, bytes);
> +      if (!tmp->ptr_backup)
> +      {
> +        restore_ptr_backup(tmp->nbackups, tmp_backup);
> +        goto err;
> +      }
> +      memcpy(tmp->ptr_backup, tmp_backup, bytes);
> +    }
> +
> +    if (chain.first)
>      {
> -      sql_print_error("Plugin '%s' has conflicting system variables",
> -                      tmp->name.str);
> -      goto err;
> +      chain.last->next = NULL;
> +      if (mysql_add_sys_var_chain(chain.first))
> +      {
> +        sql_print_error("Plugin '%s' has conflicting system variables",
> +                        tmp->name.str);
> +        goto err;
> +      }
> +      tmp->system_vars= chain.first;
>      }
> -    tmp->system_vars= chain.first;
>    }
> +
>    DBUG_RETURN(0);
>    
>  err:
> +  if (tmp_backup)
> +    my_afree(tmp_backup);
>    if (opts)
>      my_cleanup_options(opts);
>    DBUG_RETURN(error);
> 
> === modified file 'sql/sql_plugin.h'
> --- sql/sql_plugin.h	2013-12-09 11:39:13 +0000
> +++ sql/sql_plugin.h	2013-12-09 11:39:19 +0000
> @@ -85,6 +85,7 @@ struct st_ptr_backup {
>    void **ptr;
>    void *value;
>    void save(void **p) { ptr= p; value= *p; }
> +  void save(const char **p) { save((void**)p); }
>    void restore() { *ptr= value; }
>  };
>  
> @@ -108,6 +109,8 @@ struct st_plugin_int
>    LEX_STRING name;
>    struct st_maria_plugin *plugin;
>    struct st_plugin_dl *plugin_dl;
> +  st_ptr_backup *ptr_backup;
> +  uint nbackups;
>    uint state;
>    uint ref_count;               /* number of threads using the plugin */
>    uint locks_total;             /* how many times the plugin was locked */



References