Commit 6cf29ab0 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-12146 Deprecate and remove innodb_instrument_semaphores

MDEV-7618 introduced configuration parameter innodb_instrument_semaphores
in MariaDB Server 10.1. The parameter seems to only affect the rw-lock
X-latch acquisition. Extra fields are added to rw_lock_t to remember one
X-latch holder or waiter. These fields are not being consulted or reported
anywhere. This is basically only adding code bloat.

If the intention is to debug hangs or deadlocks, we have better tools for
that in the debug server, and for the non-debug server, core dumps can
reveal a lot. For example, the mini-transaction memo records the
currently held buffer block or index rw-locks, to be released at
mtr_t::commit().

The configuration parameter innodb_instrument_semaphores will be
deprecated in 10.2.5 and removed in 10.3.0.

rw_lock_t: Remove the members lock_name, file_name, line, thread_id
which did not affect any output.
parent c1bcb205
--innodb-fatal-semaphore-wait-threshold=1 --innodb-fatal-semaphore-wait-threshold=1
--innodb-sys-semaphore-waits=1 --innodb-sys-semaphore-waits=1
--innodb-instrument-semaphores=1
#
# innodb_instrument_semaphores
#
# save the initial value
SET @innodb_instrument_semaphores_global_saved = @@global.innodb_instrument_semaphores;
# default
SELECT @@global.innodb_instrument_semaphores;
@@global.innodb_instrument_semaphores
0
# scope
SELECT @@session.innodb_instrument_semaphores;
ERROR HY000: Variable 'innodb_instrument_semaphores' is a GLOBAL variable
SET @@global.innodb_instrument_semaphores=OFF;
SELECT @@global.innodb_instrument_semaphores;
@@global.innodb_instrument_semaphores
0
SET @@global.innodb_instrument_semaphores=ON;
SELECT @@global.innodb_instrument_semaphores;
@@global.innodb_instrument_semaphores
1
# valid values
SET @@global.innodb_instrument_semaphores='OFF';
SELECT @@global.innodb_instrument_semaphores;
@@global.innodb_instrument_semaphores
0
SET @@global.innodb_instrument_semaphores=ON;
SELECT @@global.innodb_instrument_semaphores;
@@global.innodb_instrument_semaphores
1
SET @@global.innodb_instrument_semaphores=default;
SELECT @@global.innodb_instrument_semaphores;
@@global.innodb_instrument_semaphores
0
# invalid values
SET @@global.innodb_instrument_semaphores=NULL;
ERROR 42000: Variable 'innodb_instrument_semaphores' can't be set to the value of 'NULL'
SET @@global.innodb_instrument_semaphores='junk';
ERROR 42000: Variable 'innodb_instrument_semaphores' can't be set to the value of 'junk'
# restore the initial value
SET @@global.innodb_instrument_semaphores = @innodb_instrument_semaphores_global_saved;
# End of test
...@@ -12,24 +12,34 @@ SELECT @@global.innodb_instrument_semaphores; ...@@ -12,24 +12,34 @@ SELECT @@global.innodb_instrument_semaphores;
SELECT @@session.innodb_instrument_semaphores; SELECT @@session.innodb_instrument_semaphores;
ERROR HY000: Variable 'innodb_instrument_semaphores' is a GLOBAL variable ERROR HY000: Variable 'innodb_instrument_semaphores' is a GLOBAL variable
SET @@global.innodb_instrument_semaphores=OFF; SET @@global.innodb_instrument_semaphores=OFF;
Warnings:
Warning 131 Using innodb_instrument_semaphores is deprecated and the parameter will be removed in MariaDB 10.3.
SELECT @@global.innodb_instrument_semaphores; SELECT @@global.innodb_instrument_semaphores;
@@global.innodb_instrument_semaphores @@global.innodb_instrument_semaphores
0 0
SET @@global.innodb_instrument_semaphores=ON; SET @@global.innodb_instrument_semaphores=ON;
Warnings:
Warning 131 Using innodb_instrument_semaphores is deprecated and the parameter will be removed in MariaDB 10.3.
SELECT @@global.innodb_instrument_semaphores; SELECT @@global.innodb_instrument_semaphores;
@@global.innodb_instrument_semaphores @@global.innodb_instrument_semaphores
1 1
# valid values # valid values
SET @@global.innodb_instrument_semaphores='OFF'; SET @@global.innodb_instrument_semaphores='OFF';
Warnings:
Warning 131 Using innodb_instrument_semaphores is deprecated and the parameter will be removed in MariaDB 10.3.
SELECT @@global.innodb_instrument_semaphores; SELECT @@global.innodb_instrument_semaphores;
@@global.innodb_instrument_semaphores @@global.innodb_instrument_semaphores
0 0
SET @@global.innodb_instrument_semaphores=ON; SET @@global.innodb_instrument_semaphores=ON;
Warnings:
Warning 131 Using innodb_instrument_semaphores is deprecated and the parameter will be removed in MariaDB 10.3.
SELECT @@global.innodb_instrument_semaphores; SELECT @@global.innodb_instrument_semaphores;
@@global.innodb_instrument_semaphores @@global.innodb_instrument_semaphores
1 1
SET @@global.innodb_instrument_semaphores=default; SET @@global.innodb_instrument_semaphores=default;
Warnings:
Warning 131 Using innodb_instrument_semaphores is deprecated and the parameter will be removed in MariaDB 10.3.
SELECT @@global.innodb_instrument_semaphores; SELECT @@global.innodb_instrument_semaphores;
@@global.innodb_instrument_semaphores @@global.innodb_instrument_semaphores
0 0
...@@ -42,4 +52,6 @@ ERROR 42000: Variable 'innodb_instrument_semaphores' can't be set to the value o ...@@ -42,4 +52,6 @@ ERROR 42000: Variable 'innodb_instrument_semaphores' can't be set to the value o
# restore the initial value # restore the initial value
SET @@global.innodb_instrument_semaphores = @innodb_instrument_semaphores_global_saved; SET @@global.innodb_instrument_semaphores = @innodb_instrument_semaphores_global_saved;
Warnings:
Warning 131 Using innodb_instrument_semaphores is deprecated and the parameter will be removed in MariaDB 10.3.
# End of test # End of test
...@@ -8,7 +8,7 @@ COUNT(@@GLOBAL.innodb_use_trim) ...@@ -8,7 +8,7 @@ COUNT(@@GLOBAL.innodb_use_trim)
1 Expected 1 Expected
SET @@GLOBAL.innodb_use_trim=1; SET @@GLOBAL.innodb_use_trim=1;
Warnings: Warnings:
Warning 131 Using innodb_use_trim is deprecated and the parameter may be removed in future releases. See http://dev.mysql.com/doc/refman/5.7/en/innodb-file-format.html Warning 131 Using innodb_use_trim is deprecated and the parameter will be removed in MariaDB 10.3.
SELECT COUNT(@@GLOBAL.innodb_use_trim); SELECT COUNT(@@GLOBAL.innodb_use_trim);
COUNT(@@GLOBAL.innodb_use_trim) COUNT(@@GLOBAL.innodb_use_trim)
1 1
...@@ -31,7 +31,7 @@ COUNT(VARIABLE_VALUE) ...@@ -31,7 +31,7 @@ COUNT(VARIABLE_VALUE)
1 Expected 1 Expected
SET @@global.innodb_use_trim = @start_use_trim; SET @@global.innodb_use_trim = @start_use_trim;
Warnings: Warnings:
Warning 131 Using innodb_use_trim is deprecated and the parameter may be removed in future releases. See http://dev.mysql.com/doc/refman/5.7/en/innodb-file-format.html Warning 131 Using innodb_use_trim is deprecated and the parameter will be removed in MariaDB 10.3.
SELECT @@global.innodb_use_trim; SELECT @@global.innodb_use_trim;
@@global.innodb_use_trim @@global.innodb_use_trim
1 1
...@@ -1301,7 +1301,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME ...@@ -1301,7 +1301,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME
DEFAULT_VALUE OFF DEFAULT_VALUE OFF
VARIABLE_SCOPE GLOBAL VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE BOOLEAN VARIABLE_TYPE BOOLEAN
VARIABLE_COMMENT Enable semaphore request instrumentation. This could have some effect on performance but allows better information on long semaphore wait problems. (Default: not enabled) VARIABLE_COMMENT DEPRECATED. This setting has no effect.
NUMERIC_MIN_VALUE NULL NUMERIC_MIN_VALUE NULL
NUMERIC_MAX_VALUE NULL NUMERIC_MAX_VALUE NULL
NUMERIC_BLOCK_SIZE NULL NUMERIC_BLOCK_SIZE NULL
......
...@@ -3751,7 +3751,15 @@ static const char* deprecated_file_format_max ...@@ -3751,7 +3751,15 @@ static const char* deprecated_file_format_max
/** Deprecation message about innodb_use_trim */ /** Deprecation message about innodb_use_trim */
static const char* deprecated_use_trim static const char* deprecated_use_trim
= DEPRECATED_FORMAT_PARAMETER("innodb_use_trim"); = "Using innodb_use_trim is deprecated"
" and the parameter will be removed in MariaDB 10.3.";
/** Deprecation message about innodb_instrument_semaphores */
static const char* deprecated_instrument_semaphores
= "Using innodb_instrument_semaphores is deprecated"
" and the parameter will be removed in MariaDB 10.3.";
static my_bool innodb_instrument_semaphores;
/** Update log_checksum_algorithm_ptr with a pointer to the function /** Update log_checksum_algorithm_ptr with a pointer to the function
corresponding to whether checksums are enabled. corresponding to whether checksums are enabled.
...@@ -4108,6 +4116,10 @@ innobase_init( ...@@ -4108,6 +4116,10 @@ innobase_init(
ib::warn() << deprecated_file_format; ib::warn() << deprecated_file_format;
} }
if (innodb_instrument_semaphores) {
ib::warn() << deprecated_instrument_semaphores;
}
/* Validate the file format by animal name */ /* Validate the file format by animal name */
if (innobase_file_format_name != NULL) { if (innobase_file_format_name != NULL) {
...@@ -20620,6 +20632,25 @@ innodb_use_trim_update( ...@@ -20620,6 +20632,25 @@ innodb_use_trim_update(
HA_ERR_WRONG_COMMAND, deprecated_use_trim); HA_ERR_WRONG_COMMAND, deprecated_use_trim);
} }
/** Update the innodb_instrument_sempahores parameter.
@param[in] thd thread handle
@param[in] var system variable
@param[out] var_ptr current value
@param[in] save immediate result from check function */
static
void
innodb_instrument_semaphores_update(
THD* thd,
struct st_mysql_sys_var* var,
void* var_ptr,
const void* save)
{
innodb_instrument_semaphores = *static_cast<const my_bool*>(save);
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
HA_ERR_WRONG_COMMAND, deprecated_instrument_semaphores);
}
/* plugin options */ /* plugin options */
static MYSQL_SYSVAR_ENUM(checksum_algorithm, srv_checksum_algorithm, static MYSQL_SYSVAR_ENUM(checksum_algorithm, srv_checksum_algorithm,
...@@ -21833,11 +21864,10 @@ static MYSQL_SYSVAR_BOOL(debug_force_scrubbing, ...@@ -21833,11 +21864,10 @@ static MYSQL_SYSVAR_BOOL(debug_force_scrubbing,
NULL, NULL, FALSE); NULL, NULL, FALSE);
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
static MYSQL_SYSVAR_BOOL(instrument_semaphores, srv_instrument_semaphores, static MYSQL_SYSVAR_BOOL(instrument_semaphores, innodb_instrument_semaphores,
PLUGIN_VAR_OPCMDARG, PLUGIN_VAR_OPCMDARG,
"Enable semaphore request instrumentation. This could have some effect on performance but allows better" "DEPRECATED. This setting has no effect.",
" information on long semaphore wait problems. (Default: not enabled)", NULL, innodb_instrument_semaphores_update, FALSE);
0, 0, FALSE);
static struct st_mysql_sys_var* innobase_system_variables[]= { static struct st_mysql_sys_var* innobase_system_variables[]= {
MYSQL_SYSVAR(autoextend_increment), MYSQL_SYSVAR(autoextend_increment),
......
...@@ -571,9 +571,6 @@ that semaphore times out in InnoDB */ ...@@ -571,9 +571,6 @@ that semaphore times out in InnoDB */
#define DEFAULT_SRV_FATAL_SEMAPHORE_TIMEOUT 600 #define DEFAULT_SRV_FATAL_SEMAPHORE_TIMEOUT 600
extern ulong srv_fatal_semaphore_wait_threshold; extern ulong srv_fatal_semaphore_wait_threshold;
/** Enable semaphore request instrumentation */
extern my_bool srv_instrument_semaphores;
/** Buffer pool dump status frequence in percentages */ /** Buffer pool dump status frequence in percentages */
extern ulong srv_buf_dump_status_frequency; extern ulong srv_buf_dump_status_frequency;
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, Google Inc. Copyright (c) 2008, Google Inc.
Copyright (c) 2017, MariaDB Corporation. All Rights Reserved.
Portions of this file contain modifications contributed and copyrighted by Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described Google, Inc. Those modifications are gratefully acknowledged and are described
...@@ -38,9 +39,6 @@ Created 9/11/1995 Heikki Tuuri ...@@ -38,9 +39,6 @@ Created 9/11/1995 Heikki Tuuri
#include "os0event.h" #include "os0event.h"
#include "ut0mutex.h" #include "ut0mutex.h"
/** Enable semaphore request instrumentation */
extern my_bool srv_instrument_semaphores;
/** Counters for RW locks. */ /** Counters for RW locks. */
struct rw_lock_stats_t { struct rw_lock_stats_t {
typedef ib_counter_t<int64_t, IB_N_SLOTS> int64_counter_t; typedef ib_counter_t<int64_t, IB_N_SLOTS> int64_counter_t;
...@@ -127,10 +125,10 @@ if MySQL performance schema is enabled and "UNIV_PFS_RWLOCK" is ...@@ -127,10 +125,10 @@ if MySQL performance schema is enabled and "UNIV_PFS_RWLOCK" is
defined, the rwlock are instrumented with performance schema probes. */ defined, the rwlock are instrumented with performance schema probes. */
# ifdef UNIV_DEBUG # ifdef UNIV_DEBUG
# define rw_lock_create(K, L, level) \ # define rw_lock_create(K, L, level) \
rw_lock_create_func((L), (level), #L, __FILE__, __LINE__) rw_lock_create_func((L), (level), __FILE__, __LINE__)
# else /* UNIV_DEBUG */ # else /* UNIV_DEBUG */
# define rw_lock_create(K, L, level) \ # define rw_lock_create(K, L, level) \
rw_lock_create_func((L), #L, __FILE__, __LINE__) rw_lock_create_func((L), __FILE__, __LINE__)
# endif /* UNIV_DEBUG */ # endif /* UNIV_DEBUG */
/**************************************************************//** /**************************************************************//**
...@@ -215,10 +213,10 @@ unlocking, not the corresponding function. */ ...@@ -215,10 +213,10 @@ unlocking, not the corresponding function. */
/* Following macros point to Performance Schema instrumented functions. */ /* Following macros point to Performance Schema instrumented functions. */
# ifdef UNIV_DEBUG # ifdef UNIV_DEBUG
# define rw_lock_create(K, L, level) \ # define rw_lock_create(K, L, level) \
pfs_rw_lock_create_func((K), (L), (level), #L, __FILE__, __LINE__) pfs_rw_lock_create_func((K), (L), (level), __FILE__, __LINE__)
# else /* UNIV_DEBUG */ # else /* UNIV_DEBUG */
# define rw_lock_create(K, L, level) \ # define rw_lock_create(K, L, level) \
pfs_rw_lock_create_func((K), (L), #L, __FILE__, __LINE__) pfs_rw_lock_create_func((K), (L), __FILE__, __LINE__)
# endif /* UNIV_DEBUG */ # endif /* UNIV_DEBUG */
/****************************************************************** /******************************************************************
...@@ -303,7 +301,6 @@ rw_lock_create_func( ...@@ -303,7 +301,6 @@ rw_lock_create_func(
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
latch_level_t level, /*!< in: level */ latch_level_t level, /*!< in: level */
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
const char* cmutex_name, /*!< in: mutex name */
const char* cfile_name, /*!< in: file name where created */ const char* cfile_name, /*!< in: file name where created */
ulint cline); /*!< in: file line where created */ ulint cline); /*!< in: file line where created */
/******************************************************************//** /******************************************************************//**
...@@ -637,11 +634,6 @@ struct rw_lock_t ...@@ -637,11 +634,6 @@ struct rw_lock_t
/** Line number where last time x-locked */ /** Line number where last time x-locked */
unsigned last_x_line:14; unsigned last_x_line:14;
const char* lock_name;
const char* file_name;/*!< File name where the lock was obtained */
ulint line; /*!< Line where the rw-lock was locked */
os_thread_id_t thread_id;
/** Count of os_waits. May not be accurate */ /** Count of os_waits. May not be accurate */
uint32_t count_os_wait; uint32_t count_os_wait;
...@@ -744,7 +736,6 @@ pfs_rw_lock_create_func( ...@@ -744,7 +736,6 @@ pfs_rw_lock_create_func(
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
latch_level_t level, /*!< in: level */ latch_level_t level, /*!< in: level */
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
const char* cmutex_name, /*!< in: mutex name */
const char* cfile_name, /*!< in: file name where created */ const char* cfile_name, /*!< in: file name where created */
ulint cline); /*!< in: file line where created */ ulint cline); /*!< in: file line where created */
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, Google Inc. Copyright (c) 2008, Google Inc.
Copyright (c) 2017, MariaDB Corporation. All Rights Reserved.
Portions of this file contain modifications contributed and copyrighted by Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described Google, Inc. Those modifications are gratefully acknowledged and are described
...@@ -250,14 +251,6 @@ rw_lock_s_lock_low( ...@@ -250,14 +251,6 @@ rw_lock_s_lock_low(
lock->last_s_file_name = file_name; lock->last_s_file_name = file_name;
lock->last_s_line = line; lock->last_s_line = line;
/*
if (srv_instrument_semaphores) {
lock->thread_id = os_thread_get_curr_id();
lock->file_name = file_name;
lock->line = line;
}
*/
return(TRUE); /* locking succeeded */ return(TRUE); /* locking succeeded */
} }
...@@ -342,14 +335,6 @@ rw_lock_x_lock_func_nowait( ...@@ -342,14 +335,6 @@ rw_lock_x_lock_func_nowait(
ut_d(rw_lock_add_debug_info(lock, 0, RW_LOCK_X, file_name, line)); ut_d(rw_lock_add_debug_info(lock, 0, RW_LOCK_X, file_name, line));
/*
if (srv_instrument_semaphores) {
lock->thread_id = os_thread_get_curr_id();
lock->file_name = file_name;
lock->line = line;
}
*/
lock->last_x_file_name = file_name; lock->last_x_file_name = file_name;
lock->last_x_line = line; lock->last_x_line = line;
...@@ -507,7 +492,6 @@ pfs_rw_lock_create_func( ...@@ -507,7 +492,6 @@ pfs_rw_lock_create_func(
# ifdef UNIV_DEBUG # ifdef UNIV_DEBUG
latch_level_t level, /*!< in: level */ latch_level_t level, /*!< in: level */
# endif /* UNIV_DEBUG */ # endif /* UNIV_DEBUG */
const char* cmutex_name, /*!< in: mutex name */
const char* cfile_name, /*!< in: file name where created */ const char* cfile_name, /*!< in: file name where created */
ulint cline) /*!< in: file line where created */ ulint cline) /*!< in: file line where created */
{ {
...@@ -521,7 +505,6 @@ pfs_rw_lock_create_func( ...@@ -521,7 +505,6 @@ pfs_rw_lock_create_func(
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
level, level,
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
cmutex_name,
cfile_name, cfile_name,
cline); cline);
} }
......
...@@ -507,9 +507,6 @@ thread ensures that we flush the log files at least once per ...@@ -507,9 +507,6 @@ thread ensures that we flush the log files at least once per
second. */ second. */
static time_t srv_last_log_flush_time; static time_t srv_last_log_flush_time;
/** Enable semaphore request instrumentation */
UNIV_INTERN my_bool srv_instrument_semaphores = FALSE;
/* Interval in seconds at which various tasks are performed by the /* Interval in seconds at which various tasks are performed by the
master thread when server is active. In order to balance the workload, master thread when server is active. In order to balance the workload,
we should try to keep intervals such that they are not multiple of we should try to keep intervals such that they are not multiple of
......
...@@ -202,7 +202,6 @@ rw_lock_create_func( ...@@ -202,7 +202,6 @@ rw_lock_create_func(
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
latch_level_t level, /*!< in: level */ latch_level_t level, /*!< in: level */
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
const char* cmutex_name, /*!< in: mutex name */
const char* cfile_name, /*!< in: file name where created */ const char* cfile_name, /*!< in: file name where created */
ulint cline) /*!< in: file line where created */ ulint cline) /*!< in: file line where created */
{ {
...@@ -238,10 +237,7 @@ rw_lock_create_func( ...@@ -238,10 +237,7 @@ rw_lock_create_func(
less than 8192. cline is unsigned:13. */ less than 8192. cline is unsigned:13. */
ut_ad(cline <= 8192); ut_ad(cline <= 8192);
lock->cline = (unsigned int) cline; lock->cline = (unsigned int) cline;
lock->lock_name = cmutex_name;
lock->count_os_wait = 0; lock->count_os_wait = 0;
lock->file_name = "not yet reserved";
lock->line = 0;
lock->last_s_file_name = "not yet reserved"; lock->last_s_file_name = "not yet reserved";
lock->last_x_file_name = "not yet reserved"; lock->last_x_file_name = "not yet reserved";
lock->last_s_line = 0; lock->last_s_line = 0;
...@@ -479,12 +475,6 @@ rw_lock_x_lock_wait_func( ...@@ -479,12 +475,6 @@ rw_lock_x_lock_wait_func(
ut_d(rw_lock_remove_debug_info( ut_d(rw_lock_remove_debug_info(
lock, pass, RW_LOCK_X_WAIT)); lock, pass, RW_LOCK_X_WAIT));
if (srv_instrument_semaphores) {
lock->thread_id = os_thread_get_curr_id();
lock->file_name = file_name;
lock->line = line;
}
/* It is possible to wake when lock_word < 0. /* It is possible to wake when lock_word < 0.
We must pass the while-loop check to proceed.*/ We must pass the while-loop check to proceed.*/
...@@ -587,13 +577,6 @@ rw_lock_x_lock_low( ...@@ -587,13 +577,6 @@ rw_lock_x_lock_low(
ut_d(rw_lock_add_debug_info(lock, pass, RW_LOCK_X, file_name, line)); ut_d(rw_lock_add_debug_info(lock, pass, RW_LOCK_X, file_name, line));
if (srv_instrument_semaphores) {
lock->thread_id = os_thread_get_curr_id();
lock->file_name = file_name;
lock->line = line;
}
lock->last_x_file_name = file_name; lock->last_x_file_name = file_name;
lock->last_x_line = (unsigned int) line; lock->last_x_line = (unsigned int) line;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment