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@pisem.net> 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@pisem.net> 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 */