Commit 9043bec9 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-12266: Make trx_rseg_t::space a pointer

trx_rsegf_get(), trx_undo_get_first_rec(): Change the parameter to
fil_space_t* so that fewer callers need to be adjusted.

trx_undo_free_page(): Remove the redundant parameter 'space'.
parent 39ed0743
......@@ -37,10 +37,7 @@ Created 3/26/1996 Heikki Tuuri
@return rollback segment header, page x-latched */
UNIV_INLINE
trx_rsegf_t*
trx_rsegf_get(
ulint space,
ulint page_no,
mtr_t* mtr);
trx_rsegf_get(fil_space_t* space, ulint page_no, mtr_t* mtr);
/** Gets a newly created rollback segment header.
@param[in] space space where placed
......@@ -133,7 +130,7 @@ struct trx_rseg_t {
RsegMutex mutex;
/** space where the rollback segment header is placed */
ulint space;
fil_space_t* space;
/** page number of the rollback segment header */
ulint page_no;
......@@ -186,20 +183,20 @@ struct trx_rseg_t {
/** @return whether the rollback segment is persistent */
bool is_persistent() const
{
ut_ad(space == SRV_TMP_SPACE_ID
|| space == TRX_SYS_SPACE
ut_ad(space == fil_system.temp_space
|| space == fil_system.sys_space
|| (srv_undo_space_id_start > 0
&& space >= srv_undo_space_id_start
&& space <= srv_undo_space_id_start
&& space->id >= srv_undo_space_id_start
&& space->id <= srv_undo_space_id_start
+ TRX_SYS_MAX_UNDO_SPACES));
ut_ad(space == SRV_TMP_SPACE_ID
|| space == TRX_SYS_SPACE
ut_ad(space == fil_system.temp_space
|| space == fil_system.sys_space
|| (srv_undo_space_id_start > 0
&& space >= srv_undo_space_id_start
&& space <= srv_undo_space_id_start
&& space->id >= srv_undo_space_id_start
&& space->id <= srv_undo_space_id_start
+ srv_undo_tablespaces_active)
|| !srv_was_started);
return(space != SRV_TMP_SPACE_ID);
return(space->id != SRV_TMP_SPACE_ID);
}
};
......
/*****************************************************************************
Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, MariaDB Corporation.
Copyright (c) 2017, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
......@@ -34,28 +34,18 @@ Created 3/26/1996 Heikki Tuuri
@return rollback segment header, page x-latched */
UNIV_INLINE
trx_rsegf_t*
trx_rsegf_get(
ulint space,
ulint page_no,
mtr_t* mtr)
trx_rsegf_get(fil_space_t* space, ulint page_no, mtr_t* mtr)
{
buf_block_t* block;
trx_rsegf_t* header;
ut_ad(space <= srv_undo_space_id_start + srv_undo_tablespaces_active
|| space == SRV_TMP_SPACE_ID
ut_ad(space == fil_system.sys_space || space == fil_system.temp_space
|| srv_is_undo_tablespace(space->id)
|| !srv_was_started);
ut_ad(space <= srv_undo_space_id_start + TRX_SYS_MAX_UNDO_SPACES
|| space == SRV_TMP_SPACE_ID);
block = buf_page_get(
page_id_t(space, page_no), univ_page_size, RW_X_LATCH, mtr);
buf_block_t* block = buf_page_get(page_id_t(space->id, page_no),
univ_page_size, RW_X_LATCH, mtr);
buf_block_dbg_add_level(block, SYNC_RSEG_HEADER);
header = TRX_RSEG + buf_block_get_frame(block);
return(header);
return TRX_RSEG + block->frame;
}
/** Gets a newly created rollback segment header.
......
......@@ -159,7 +159,7 @@ trx_undo_get_next_rec(
@return undo log record, the page latched, NULL if none */
trx_undo_rec_t*
trx_undo_get_first_rec(
ulint space,
fil_space_t* space,
ulint page_no,
ulint offset,
ulint mode,
......
......@@ -844,7 +844,7 @@ row_purge_upd_exist_or_extern_func(
btr_root_get(index, &mtr);
block = buf_page_get(
page_id_t(rseg->space, page_no),
page_id_t(rseg->space->id, page_no),
univ_page_size, RW_X_LATCH, &mtr);
buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE);
......
......@@ -115,8 +115,8 @@ inline bool TrxUndoRsegsIterator::set_next()
/* We assume in purge of externally stored fields that space id is
in the range of UNDO tablespace space ids */
ut_a(purge_sys.rseg->space == TRX_SYS_SPACE
|| srv_is_undo_tablespace(purge_sys.rseg->space));
ut_ad(purge_sys.rseg->space->id == TRX_SYS_SPACE
|| srv_is_undo_tablespace(purge_sys.rseg->space->id));
ut_a(purge_sys.tail.commit <= purge_sys.rseg->last_commit);
......@@ -361,7 +361,7 @@ trx_purge_free_segment(trx_rseg_t* rseg, fil_addr_t hdr_addr)
rseg_hdr = trx_rsegf_get(rseg->space, rseg->page_no, &mtr);
undo_page = trx_undo_page_get(
page_id_t(rseg->space, hdr_addr.page), &mtr);
page_id_t(rseg->space->id, hdr_addr.page), &mtr);
/* Mark the last undo log totally purged, so that if the
system crashes, the tail of the undo log will not get accessed
......@@ -384,7 +384,7 @@ trx_purge_free_segment(trx_rseg_t* rseg, fil_addr_t hdr_addr)
rseg_hdr = trx_rsegf_get(rseg->space, rseg->page_no, &mtr);
undo_page = trx_undo_page_get(
page_id_t(rseg->space, hdr_addr.page), &mtr);
page_id_t(rseg->space->id, hdr_addr.page), &mtr);
}
/* The page list may now be inconsistent, but the length field
......@@ -461,7 +461,7 @@ trx_purge_truncate_rseg_history(
return;
}
undo_page = trx_undo_page_get(page_id_t(rseg.space, hdr_addr.page),
undo_page = trx_undo_page_get(page_id_t(rseg.space->id, hdr_addr.page),
&mtr);
log_hdr = undo_page + hdr_addr.boffset;
......@@ -828,7 +828,8 @@ trx_purge_mark_undo_for_truncate(
for (ulint i = 0; i < TRX_SYS_N_RSEGS; ++i) {
if (trx_rseg_t* rseg = trx_sys.rseg_array[i]) {
ut_ad(rseg->is_persistent());
if (rseg->space == undo_trunc->get_marked_space_id()) {
if (rseg->space->id
== undo_trunc->get_marked_space_id()) {
/* Once set this rseg will not be allocated
to new booting transaction but we will wait
......@@ -870,7 +871,7 @@ trx_purge_cleanse_purge_queue(
it2 != it->end();
++it2) {
if ((*it2)->space
if ((*it2)->space->id
== undo_trunc->get_marked_space_id()) {
it->erase(it2);
break;
......@@ -1099,7 +1100,7 @@ trx_purge_rseg_get_next_history_log(
mtr_start(&mtr);
undo_page = trx_undo_page_get_s_latched(
page_id_t(rseg->space, rseg->last_page_no), &mtr);
page_id_t(rseg->space->id, rseg->last_page_no), &mtr);
log_hdr = undo_page + rseg->last_offset;
......@@ -1127,7 +1128,7 @@ trx_purge_rseg_get_next_history_log(
/* Read the previous log header. */
mtr_start(&mtr);
log_hdr = trx_undo_page_get_s_latched(page_id_t(rseg->space,
log_hdr = trx_undo_page_get_s_latched(page_id_t(rseg->space->id,
prev_log_addr.page),
&mtr)
+ prev_log_addr.boffset;
......@@ -1243,7 +1244,7 @@ trx_purge_get_next_rec(
ut_ad(purge_sys.next_stored);
ut_ad(purge_sys.tail.trx_no() < purge_sys.view.low_limit_no());
space = purge_sys.rseg->space;
space = purge_sys.rseg->space->id;
page_no = purge_sys.page_no;
offset = purge_sys.offset;
......
......@@ -1923,7 +1923,7 @@ trx_undo_report_rename(trx_t* trx, const dict_table_t* table)
undo->guess_block = block;
trx->undo_rseg_space
= trx->rsegs.m_redo.rseg->space;
= trx->rsegs.m_redo.rseg->space->id;
err = DB_SUCCESS;
break;
} else {
......@@ -2070,7 +2070,7 @@ trx_undo_report_row_operation(
undo->top_undo_no = trx->undo_no++;
undo->guess_block = undo_block;
trx->undo_rseg_space = rseg->space;
trx->undo_rseg_space = rseg->space->id;
mutex_exit(&trx->undo_mutex);
......@@ -2124,7 +2124,7 @@ trx_undo_report_row_operation(
" log pages. Please add new data file to the tablespace or"
" check if filesystem is full or enable auto-extension for"
" the tablespace",
undo->rseg->space == TRX_SYS_SPACE
undo->rseg->space == fil_system.sys_space
? "system" : is_temp ? "temporary" : "undo");
/* Did not succeed: out of space */
......@@ -2170,7 +2170,7 @@ trx_undo_get_undo_rec_low(
mtr_start(&mtr);
undo_page = trx_undo_page_get_s_latched(
page_id_t(rseg->space, page_no), &mtr);
page_id_t(rseg->space->id, page_no), &mtr);
undo_rec = trx_undo_rec_copy(undo_page + offset, heap);
......
......@@ -924,7 +924,7 @@ trx_roll_pop_top_rec(
ut_ad(mutex_own(&trx->undo_mutex));
page_t* undo_page = trx_undo_page_get_s_latched(
page_id_t(undo->rseg->space, undo->top_page_no), mtr);
page_id_t(undo->rseg->space->id, undo->top_page_no), mtr);
ulint offset = undo->top_offset;
......@@ -1035,10 +1035,10 @@ trx_roll_pop_top_rec_of_trx(trx_t* trx, roll_ptr_t* roll_ptr, mem_heap_t* heap)
}
ut_ad(trx_roll_check_undo_rec_ordering(
undo_no, undo->rseg->space, trx));
undo_no, undo->rseg->space->id, trx));
trx->undo_no = undo_no;
trx->undo_rseg_space = undo->rseg->space;
trx->undo_rseg_space = undo->rseg->space->id;
mutex_exit(&trx->undo_mutex);
trx_undo_rec_t* undo_rec_copy = trx_undo_rec_copy(undo_rec, heap);
......
......@@ -365,7 +365,7 @@ trx_rseg_mem_free(trx_rseg_t* rseg)
@param[in] page_no page number of the segment header */
static
trx_rseg_t*
trx_rseg_mem_create(ulint id, ulint space, ulint page_no)
trx_rseg_mem_create(ulint id, fil_space_t* space, ulint page_no)
{
trx_rseg_t* rseg = static_cast<trx_rseg_t*>(
ut_zalloc_nokey(sizeof *rseg));
......@@ -422,7 +422,7 @@ void
trx_rseg_mem_restore(trx_rseg_t* rseg, trx_id_t& max_trx_id, mtr_t* mtr)
{
trx_rsegf_t* rseg_header = trx_rsegf_get_new(
rseg->space, rseg->page_no, mtr);
rseg->space->id, rseg->page_no, mtr);
if (mach_read_from_4(rseg_header + TRX_RSEG_FORMAT) == 0) {
trx_id_t id = mach_read_from_8(rseg_header
......@@ -490,7 +490,7 @@ trx_rseg_mem_restore(trx_rseg_t* rseg, trx_id_t& max_trx_id, mtr_t* mtr)
rseg->last_offset = node_addr.boffset;
const trx_ulogf_t* undo_log_hdr = trx_undo_page_get(
page_id_t(rseg->space, node_addr.page), mtr)
page_id_t(rseg->space->id, node_addr.page), mtr)
+ node_addr.boffset;
trx_id_t id = mach_read_from_8(undo_log_hdr + TRX_UNDO_TRX_ID);
......@@ -569,8 +569,9 @@ trx_rseg_array_init()
sys, rseg_id);
if (page_no != FIL_NULL) {
trx_rseg_t* rseg = trx_rseg_mem_create(
rseg_id, trx_sysf_rseg_get_space(
sys, rseg_id),
rseg_id,
fil_space_get(trx_sysf_rseg_get_space(
sys, rseg_id)),
page_no);
ut_ad(rseg->is_persistent());
ut_ad(rseg->id == rseg_id);
......@@ -600,7 +601,7 @@ trx_rseg_create(ulint space_id)
/* To obey the latching order, acquire the file space
x-latch before the trx_sys.mutex. */
const fil_space_t* space = mtr_x_lock_space(space_id, &mtr);
fil_space_t* space = mtr_x_lock_space(space_id, &mtr);
ut_ad(space->purpose == FIL_TYPE_TABLESPACE);
if (buf_block_t* sys_header = trx_sysf_get(&mtr)) {
......@@ -612,7 +613,7 @@ trx_rseg_create(ulint space_id)
if (page_no != FIL_NULL) {
ut_ad(trx_sysf_rseg_get_space(sys_header, rseg_id)
== space_id);
rseg = trx_rseg_mem_create(rseg_id, space_id, page_no);
rseg = trx_rseg_mem_create(rseg_id, space, page_no);
ut_ad(rseg->id == rseg_id);
ut_ad(rseg->is_persistent());
ut_ad(!trx_sys.rseg_array[rseg->id]);
......@@ -639,7 +640,7 @@ trx_temp_rseg_create()
ulint page_no = trx_rseg_header_create(
fil_system.temp_space, i, NULL, &mtr);
trx_rseg_t* rseg = trx_rseg_mem_create(
i, SRV_TMP_SPACE_ID, page_no);
i, fil_system.temp_space, page_no);
ut_ad(!rseg->is_persistent());
ut_ad(!trx_sys.temp_rsegs[i]);
trx_sys.temp_rsegs[i] = rseg;
......
......@@ -678,6 +678,8 @@ trx_resurrect_table_locks(
ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE) ||
trx_state_eq(trx, TRX_STATE_PREPARED));
ut_ad(undo->rseg == trx->rsegs.m_redo.rseg);
if (undo->empty) {
return;
......@@ -688,7 +690,8 @@ trx_resurrect_table_locks(
/* trx_rseg_mem_create() may have acquired an X-latch on this
page, so we cannot acquire an S-latch. */
undo_page = trx_undo_page_get(
page_id_t(undo->rseg->space, undo->top_page_no), &mtr);
page_id_t(trx->rsegs.m_redo.rseg->space->id,
undo->top_page_no), &mtr);
undo_rec = undo_page + undo->top_offset;
......@@ -796,7 +799,7 @@ static void trx_resurrect(trx_undo_t *undo, trx_rseg_t *rseg,
if (!undo->empty)
{
trx->undo_no= undo->top_undo_no + 1;
trx->undo_rseg_space= undo->rseg->space;
trx->undo_rseg_space= undo->rseg->space->id;
}
trx->rsegs.m_redo.rseg= rseg;
......@@ -901,8 +904,7 @@ trx_lists_init_at_db_start()
}
trx->undo_no = undo->top_undo_no + 1;
trx->undo_rseg_space =
undo->rseg->space;
trx->undo_rseg_space = rseg->space->id;
}
trx_resurrect_table_locks(trx, undo);
}
......@@ -935,7 +937,7 @@ trx_assign_rseg_low()
}
/* The first slot is always assigned to the system tablespace. */
ut_ad(trx_sys.rseg_array[0]->space == TRX_SYS_SPACE);
ut_ad(trx_sys.rseg_array[0]->space == fil_system.sys_space);
/* Choose a rollback segment evenly distributed between 0 and
innodb_undo_logs-1 in a round-robin fashion, skipping those
......@@ -977,14 +979,14 @@ trx_assign_rseg_low()
ut_ad(rseg->is_persistent());
if (rseg->space != TRX_SYS_SPACE) {
if (rseg->space != fil_system.sys_space) {
ut_ad(srv_undo_tablespaces > 1);
if (rseg->skip_allocation) {
continue;
}
} else if (trx_rseg_t* next
= trx_sys.rseg_array[slot]) {
if (next->space != TRX_SYS_SPACE
if (next->space != fil_system.sys_space
&& srv_undo_tablespaces > 0) {
/** If dedicated
innodb_undo_tablespaces have
......
......@@ -351,7 +351,7 @@ trx_undo_get_next_rec(
@return undo log record, the page latched, NULL if none */
trx_undo_rec_t*
trx_undo_get_first_rec(
ulint space,
fil_space_t* space,
ulint page_no,
ulint offset,
ulint mode,
......@@ -360,7 +360,7 @@ trx_undo_get_first_rec(
page_t* undo_page;
trx_undo_rec_t* rec;
const page_id_t page_id(space, page_no);
const page_id_t page_id(space->id, page_no);
if (mode == RW_S_LATCH) {
undo_page = trx_undo_page_get_s_latched(page_id, mtr);
......@@ -374,7 +374,7 @@ trx_undo_get_first_rec(
return(rec);
}
return(trx_undo_get_next_rec_from_next_page(space,
return(trx_undo_get_next_rec_from_next_page(space->id,
undo_page, page_no, offset,
mode, mtr));
}
......@@ -782,9 +782,9 @@ trx_undo_add_page(trx_t* trx, trx_undo_t* undo, mtr_t* mtr)
mutex_enter(&rseg->mutex);
header_page = trx_undo_page_get(
page_id_t(undo->rseg->space, undo->hdr_page_no), mtr);
page_id_t(undo->rseg->space->id, undo->hdr_page_no), mtr);
if (!fsp_reserve_free_extents(&n_reserved, undo->rseg->space, 1,
if (!fsp_reserve_free_extents(&n_reserved, undo->rseg->space->id, 1,
FSP_UNDO, mtr)) {
goto func_exit;
}
......@@ -794,7 +794,7 @@ trx_undo_add_page(trx_t* trx, trx_undo_t* undo, mtr_t* mtr)
+ header_page,
undo->top_page_no + 1, FSP_UP, TRUE, mtr, mtr);
fil_space_release_free_extents(undo->rseg->space, n_reserved);
fil_space_release_free_extents(undo->rseg->space->id, n_reserved);
if (!new_block) {
goto func_exit;
......@@ -827,9 +827,8 @@ ulint
trx_undo_free_page(
/*===============*/
trx_rseg_t* rseg, /*!< in: rollback segment */
ibool in_history, /*!< in: TRUE if the undo log is in the history
bool in_history, /*!< in: TRUE if the undo log is in the history
list */
ulint space, /*!< in: space */
ulint hdr_page_no, /*!< in: header page number */
ulint page_no, /*!< in: page number to free: must not be the
header page */
......@@ -842,6 +841,7 @@ trx_undo_free_page(
fil_addr_t last_addr;
trx_rsegf_t* rseg_header;
ulint hist_size;
const ulint space = rseg->space->id;
ut_a(hdr_page_no != page_no);
ut_ad(mutex_own(&(rseg->mutex)));
......@@ -861,7 +861,7 @@ trx_undo_free_page(
rseg->curr_size--;
if (in_history) {
rseg_header = trx_rsegf_get(space, rseg->page_no, mtr);
rseg_header = trx_rsegf_get(rseg->space, rseg->page_no, mtr);
hist_size = mtr_read_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
MLOG_4BYTES, mtr);
......@@ -884,8 +884,7 @@ trx_undo_free_last_page(trx_undo_t* undo, mtr_t* mtr)
ut_ad(undo->size > 0);
undo->last_page_no = trx_undo_free_page(
undo->rseg, FALSE, undo->rseg->space,
undo->hdr_page_no, undo->last_page_no, mtr);
undo->rseg, false, undo->hdr_page_no, undo->last_page_no, mtr);
undo->size--;
}
......@@ -909,7 +908,7 @@ trx_undo_truncate_end(trx_undo_t* undo, undo_no_t limit, bool is_temp)
trx_undo_rec_t* trunc_here = NULL;
page_t* undo_page = trx_undo_page_get(
page_id_t(undo->rseg->space, undo->last_page_no),
page_id_t(undo->rseg->space->id, undo->last_page_no),
&mtr);
trx_undo_rec_t* rec = trx_undo_page_get_last_rec(
undo_page, undo->hdr_page_no, undo->hdr_offset);
......@@ -1013,8 +1012,7 @@ trx_undo_truncate_start(
mlog_write_ulint(undo_page + hdr_offset + TRX_UNDO_LOG_START,
end, MLOG_2BYTES, &mtr);
} else {
trx_undo_free_page(rseg, TRUE, rseg->space, hdr_page_no,
page_no, &mtr);
trx_undo_free_page(rseg, true, hdr_page_no, page_no, &mtr);
}
mtr_commit(&mtr);
......@@ -1050,7 +1048,7 @@ trx_undo_seg_free(
mutex_enter(&(rseg->mutex));
seg_header = trx_undo_page_get(page_id_t(undo->rseg->space,
seg_header = trx_undo_page_get(page_id_t(undo->rseg->space->id,
undo->hdr_page_no),
&mtr)
+ TRX_UNDO_SEG_HDR;
......@@ -1093,7 +1091,7 @@ trx_undo_mem_create_at_db_start(trx_rseg_t* rseg, ulint id, ulint page_no,
mtr.start();
const page_t* undo_page = trx_undo_page_get(
page_id_t(rseg->space, page_no), &mtr);
page_id_t(rseg->space->id, page_no), &mtr);
const ulint type = mach_read_from_2(
TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE + undo_page);
ut_ad(type == 0 || type == TRX_UNDO_INSERT || type == TRX_UNDO_UPDATE);
......@@ -1152,7 +1150,7 @@ trx_undo_mem_create_at_db_start(trx_rseg_t* rseg, ulint id, ulint page_no,
undo->top_page_no = last_addr.page;
page_t* last_page = trx_undo_page_get(
page_id_t(rseg->space, undo->last_page_no), &mtr);
page_id_t(rseg->space->id, undo->last_page_no), &mtr);
const trx_undo_rec_t* rec = trx_undo_page_get_last_rec(
last_page, page_no, offset);
......@@ -1340,7 +1338,7 @@ trx_undo_reuse_cached(trx_t* trx, trx_rseg_t* rseg, trx_undo_t** pundo,
ut_ad(undo->size == 1);
ut_ad(undo->id < TRX_RSEG_N_SLOTS);
buf_block_t* block = buf_page_get(page_id_t(undo->rseg->space,
buf_block_t* block = buf_page_get(page_id_t(undo->rseg->space->id,
undo->hdr_page_no),
univ_page_size, RW_X_LATCH, mtr);
if (!block) {
......@@ -1401,7 +1399,7 @@ trx_undo_assign(trx_t* trx, dberr_t* err, mtr_t* mtr)
if (undo) {
return buf_page_get_gen(
page_id_t(undo->rseg->space, undo->last_page_no),
page_id_t(undo->rseg->space->id, undo->last_page_no),
univ_page_size, RW_X_LATCH,
buf_pool_is_obsolete(undo->withdraw_clock)
? NULL : undo->guess_block,
......@@ -1458,7 +1456,7 @@ trx_undo_assign_low(trx_t* trx, trx_rseg_t* rseg, trx_undo_t** undo,
if (*undo) {
return buf_page_get_gen(
page_id_t(rseg->space, (*undo)->last_page_no),
page_id_t(rseg->space->id, (*undo)->last_page_no),
univ_page_size, RW_X_LATCH,
buf_pool_is_obsolete((*undo)->withdraw_clock)
? NULL : (*undo)->guess_block,
......@@ -1508,7 +1506,7 @@ trx_undo_set_state_at_finish(
ut_a(undo->id < TRX_RSEG_N_SLOTS);
undo_page = trx_undo_page_get(
page_id_t(undo->rseg->space, undo->hdr_page_no), mtr);
page_id_t(undo->rseg->space->id, undo->hdr_page_no), mtr);
seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
page_hdr = undo_page + TRX_UNDO_PAGE_HDR;
......@@ -1552,7 +1550,7 @@ trx_undo_set_state_at_prepare(
ut_a(undo->id < TRX_RSEG_N_SLOTS);
undo_page = trx_undo_page_get(
page_id_t(undo->rseg->space, undo->hdr_page_no), mtr);
page_id_t(undo->rseg->space->id, undo->hdr_page_no), mtr);
seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
......
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