maria-developers team mailing list archive
-
maria-developers team
-
Mailing list archive
-
Message #06606
(MDEV-4403) Attempting to use cassandra storage engine causes "service 'my_snprintf_service' interface version mismatch"
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 */
Follow ups