Commit 77aa9c6f authored by mir's avatar mir

TASK_MANAGER_DECODING define removed

parent a50ad456
#ifndef TASK_MANAGER_WORKING_STEALING_H #ifndef TASK_MANAGER_WORKING_STEALING_H
#define TASK_MANAGER_WORKING_STEALING_H #define TASK_MANAGER_WORKING_STEALING_H
// Comment for deactivating ws tpool
#define TASK_MANAGER_DECODING
#define TASK_MANAGER_DEMODULATION #define TASK_MANAGER_DEMODULATION
#define TASK_MANAGER_CODING #define TASK_MANAGER_CODING
#define TASK_MANAGER_RU #define TASK_MANAGER_RU
......
...@@ -385,11 +385,10 @@ void init_gNB_Tpool(int inst) { ...@@ -385,11 +385,10 @@ void init_gNB_Tpool(int inst) {
gNB = RC.gNB[inst]; gNB = RC.gNB[inst];
// ULSCH decoding threadpool // ULSCH decoding threadpool
#ifdef TASK_MANAGER_DECODING
int const num_threads = parse_num_threads(get_softmodem_params()->threadPoolConfig); int const num_threads = parse_num_threads(get_softmodem_params()->threadPoolConfig);
init_task_manager(&gNB->man, num_threads); init_task_manager(&gNB->man, num_threads);
// 2nd tpool needed to avoid current cycle and deadlock
init_task_manager(&gNB->man_rx_tx_ru, 2); init_task_manager(&gNB->man_rx_tx_ru, 2);
#endif
gNB_L1_proc_t *proc = &gNB->proc; gNB_L1_proc_t *proc = &gNB->proc;
// PUSCH symbols per thread need to be calculated by how many threads we have // PUSCH symbols per thread need to be calculated by how many threads we have
...@@ -428,13 +427,9 @@ void init_gNB_Tpool(int inst) { ...@@ -428,13 +427,9 @@ void init_gNB_Tpool(int inst) {
void term_gNB_Tpool(int inst) { void term_gNB_Tpool(int inst) {
PHY_VARS_gNB *gNB = RC.gNB[inst]; PHY_VARS_gNB *gNB = RC.gNB[inst];
#ifdef TASK_MANAGER_DECODING
void (*clean)(task_t*) = NULL; void (*clean)(task_t*) = NULL;
free_task_manager(&gNB->man , clean); free_task_manager(&gNB->man , clean);
free_task_manager(&gNB->man_rx_tx_ru , clean); free_task_manager(&gNB->man_rx_tx_ru , clean);
#else
abortTpool(&gNB->threadPool);
#endif
abortNotifiedFIFO(&gNB->respDecode); abortNotifiedFIFO(&gNB->respDecode);
abortNotifiedFIFO(&gNB->resp_L1); abortNotifiedFIFO(&gNB->resp_L1);
abortNotifiedFIFO(&gNB->L1_tx_free); abortNotifiedFIFO(&gNB->L1_tx_free);
......
...@@ -58,10 +58,8 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, ...@@ -58,10 +58,8 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB,
uint32_t frame, uint32_t frame,
uint8_t nr_tti_rx, uint8_t nr_tti_rx,
uint8_t harq_pid, uint8_t harq_pid,
uint32_t G uint32_t G,
#ifdef TASK_MANAGER_DECODING thread_info_tm_t* t_info
, thread_info_tm_t* t_info
#endif
); );
/*! \brief Perform PUSCH unscrambling. TS 38.211 V15.4.0 subclause 6.3.1.1 /*! \brief Perform PUSCH unscrambling. TS 38.211 V15.4.0 subclause 6.3.1.1
......
...@@ -186,9 +186,8 @@ static void nr_processULSegment(void *arg) ...@@ -186,9 +186,8 @@ static void nr_processULSegment(void *arg)
LOG_E(PHY, "ulsch_decoding.c: Problem in rate_matching\n"); LOG_E(PHY, "ulsch_decoding.c: Problem in rate_matching\n");
rdata->decodeIterations = max_ldpc_iterations + 1; rdata->decodeIterations = max_ldpc_iterations + 1;
#ifdef TASK_MANAGER_DECODING // Task completed
completed_task_ans(rdata->ans); completed_task_ans(rdata->ans);
#endif
return; return;
} }
...@@ -230,9 +229,8 @@ static void nr_processULSegment(void *arg) ...@@ -230,9 +229,8 @@ static void nr_processULSegment(void *arg)
if (rdata->decodeIterations <= p_decoderParms->numMaxIter) if (rdata->decodeIterations <= p_decoderParms->numMaxIter)
memcpy(ulsch_harq->c[r],llrProcBuf, Kr>>3); memcpy(ulsch_harq->c[r],llrProcBuf, Kr>>3);
#ifdef TASK_MANAGER_DECODING // Task completed
completed_task_ans(rdata->ans); completed_task_ans(rdata->ans);
#endif
} }
int decode_offload(PHY_VARS_gNB *phy_vars_gNB, int decode_offload(PHY_VARS_gNB *phy_vars_gNB,
...@@ -338,11 +336,8 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, ...@@ -338,11 +336,8 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB,
uint32_t frame, uint32_t frame,
uint8_t nr_tti_rx, uint8_t nr_tti_rx,
uint8_t harq_pid, uint8_t harq_pid,
uint32_t G uint32_t G,
#ifdef TASK_MANAGER_DECODING thread_info_tm_t* t_info
// This is a broken idea. But so is the code arquitecture
, thread_info_tm_t* t_info
#endif
) )
{ {
if (!ulsch_llr) { if (!ulsch_llr) {
...@@ -452,16 +447,12 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, ...@@ -452,16 +447,12 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB,
set_abort(&harq_process->abort_decode, false); set_abort(&harq_process->abort_decode, false);
for (int r = 0; r < harq_process->C; r++) { for (int r = 0; r < harq_process->C; r++) {
int E = nr_get_E(G, harq_process->C, Qm, n_layers, r); int E = nr_get_E(G, harq_process->C, Qm, n_layers, r);
#ifdef TASK_MANAGER_DECODING
ldpcDecode_t* rdata = &((ldpcDecode_t*)t_info->buf)[t_info->len]; ldpcDecode_t* rdata = &((ldpcDecode_t*)t_info->buf)[t_info->len];
assert(t_info->len < 16); assert(t_info->len < 16);
rdata->ans = &t_info->ans[t_info->len]; rdata->ans = &t_info->ans[t_info->len];
t_info->len += 1; t_info->len += 1;
#else
union ldpcReqUnion id = {.s = {ulsch->rnti, frame, nr_tti_rx, 0, 0}};
notifiedFIFO_elt_t *req = newNotifiedFIFO_elt(sizeof(ldpcDecode_t), id.p, &phy_vars_gNB->respDecode, &nr_processULSegment);
ldpcDecode_t *rdata = (ldpcDecode_t *)NotifiedFifoData(req);
#endif
decParams.R = nr_get_R_ldpc_decoder(pusch_pdu->pusch_data.rv_index, decParams.R = nr_get_R_ldpc_decoder(pusch_pdu->pusch_data.rv_index,
E, E,
decParams.BG, decParams.BG,
...@@ -486,12 +477,10 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, ...@@ -486,12 +477,10 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB,
rdata->ulsch = ulsch; rdata->ulsch = ulsch;
rdata->ulsch_id = ULSCH_id; rdata->ulsch_id = ULSCH_id;
rdata->tbslbrm = pusch_pdu->maintenance_parms_v3.tbSizeLbrmBytes; rdata->tbslbrm = pusch_pdu->maintenance_parms_v3.tbSizeLbrmBytes;
#ifdef TASK_MANAGER_DECODING
task_t t = { .args = rdata, .func = &nr_processULSegment }; task_t t = {.func = &nr_processULSegment, .args = rdata};
async_task_manager(&phy_vars_gNB->man, t); async_task_manager(&phy_vars_gNB->man, t);
#else
pushTpool(&phy_vars_gNB->threadPool, req);
#endif
LOG_D(PHY, "Added a block to decode, in pipe: %d\n", r); LOG_D(PHY, "Added a block to decode, in pipe: %d\n", r);
r_offset += E; r_offset += E;
offset += ((harq_process->K >> 3) - (harq_process->F >> 3) - ((harq_process->C > 1) ? 3 : 0)); offset += ((harq_process->K >> 3) - (harq_process->F >> 3) - ((harq_process->C > 1) ? 3 : 0));
......
...@@ -729,7 +729,7 @@ typedef struct PHY_VARS_gNB_s { ...@@ -729,7 +729,7 @@ typedef struct PHY_VARS_gNB_s {
void *scopeData; void *scopeData;
/// structure for analyzing high-level RT measurements /// structure for analyzing high-level RT measurements
rt_L1_profiling_t rt_L1_profiling; rt_L1_profiling_t rt_L1_profiling;
#if defined(TASK_MANAGER_DECODING) || defined(TASK_MANAGER_CODING) || defined(TASK_MANAGER_DEMODULATION) || defined(TASK_MANAGER_RU) || !defined(TASK_MANAGER_SIM) #if defined(TASK_MANAGER_CODING) || defined(TASK_MANAGER_DEMODULATION) || defined(TASK_MANAGER_RU) || !defined(TASK_MANAGER_SIM)
task_manager_t man; task_manager_t man;
task_manager_t man_rx_tx_ru; task_manager_t man_rx_tx_ru;
#else #else
...@@ -749,9 +749,7 @@ typedef struct puschSymbolProc_s { ...@@ -749,9 +749,7 @@ typedef struct puschSymbolProc_s {
int16_t **llr_layers; int16_t **llr_layers;
int16_t *s; int16_t *s;
uint32_t nvar; uint32_t nvar;
#ifdef TASK_MANAGER_DECODING
task_ans_t* ans; task_ans_t* ans;
#endif
} puschSymbolProc_t; } puschSymbolProc_t;
struct puschSymbolReqId { struct puschSymbolReqId {
...@@ -786,9 +784,7 @@ typedef struct LDPCDecode_s { ...@@ -786,9 +784,7 @@ typedef struct LDPCDecode_s {
int offset; int offset;
int decodeIterations; int decodeIterations;
uint32_t tbslbrm; uint32_t tbslbrm;
#ifdef TASK_MANAGER_DECODING
task_ans_t* ans; task_ans_t* ans;
#endif
} ldpcDecode_t; } ldpcDecode_t;
struct ldpcReqId { struct ldpcReqId {
......
...@@ -254,14 +254,8 @@ void phy_procedures_gNB_TX(processingData_L1tx_t *msgTx, ...@@ -254,14 +254,8 @@ void phy_procedures_gNB_TX(processingData_L1tx_t *msgTx,
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_gNB_TX + gNB->CC_id, 0); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_gNB_TX + gNB->CC_id, 0);
} }
#ifdef TASK_MANAGER_DECODING
static void nr_postDecode(PHY_VARS_gNB *gNB, ldpcDecode_t *rdata) static void nr_postDecode(PHY_VARS_gNB *gNB, ldpcDecode_t *rdata)
{ {
#else
static void nr_postDecode(PHY_VARS_gNB *gNB, notifiedFIFO_elt_t *req)
{
ldpcDecode_t *rdata = (ldpcDecode_t*) NotifiedFifoData(req);
#endif
NR_UL_gNB_HARQ_t *ulsch_harq = rdata->ulsch_harq; NR_UL_gNB_HARQ_t *ulsch_harq = rdata->ulsch_harq;
NR_gNB_ULSCH_t *ulsch = rdata->ulsch; NR_gNB_ULSCH_t *ulsch = rdata->ulsch;
int r = rdata->segment_r; int r = rdata->segment_r;
...@@ -374,11 +368,7 @@ static void nr_postDecode(PHY_VARS_gNB *gNB, notifiedFIFO_elt_t *req) ...@@ -374,11 +368,7 @@ static void nr_postDecode(PHY_VARS_gNB *gNB, notifiedFIFO_elt_t *req)
} }
} }
#ifdef TASK_MANAGER_DECODING
static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int ULSCH_id, uint8_t harq_pid, thread_info_tm_t* t_info) static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int ULSCH_id, uint8_t harq_pid, thread_info_tm_t* t_info)
#else
static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int ULSCH_id, uint8_t harq_pid)
#endif
{ {
NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms;
nfapi_nr_pusch_pdu_t *pusch_pdu = &gNB->ulsch[ULSCH_id].harq_process->ulsch_pdu; nfapi_nr_pusch_pdu_t *pusch_pdu = &gNB->ulsch[ULSCH_id].harq_process->ulsch_pdu;
...@@ -425,12 +415,7 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int ...@@ -425,12 +415,7 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int
//---------------------------------------------------------- //----------------------------------------------------------
start_meas(&gNB->ulsch_decoding_stats); start_meas(&gNB->ulsch_decoding_stats);
#ifdef TASK_MANAGER_DECODING
int const nbDecode = nr_ulsch_decoding(gNB, ULSCH_id, gNB->pusch_vars[ULSCH_id].llr, frame_parms, pusch_pdu, frame_rx, slot_rx, harq_pid, G, t_info); int const nbDecode = nr_ulsch_decoding(gNB, ULSCH_id, gNB->pusch_vars[ULSCH_id].llr, frame_parms, pusch_pdu, frame_rx, slot_rx, harq_pid, G, t_info);
#else
int const nbDecode = nr_ulsch_decoding(gNB, ULSCH_id, gNB->pusch_vars[ULSCH_id].llr, frame_parms, pusch_pdu, frame_rx, slot_rx, harq_pid, G);
#endif
return nbDecode; return nbDecode;
} }
...@@ -867,11 +852,9 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) ...@@ -867,11 +852,9 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx)
} }
} }
#ifdef TASK_MANAGER_DECODING
ldpcDecode_t arr[16]; ldpcDecode_t arr[16];
task_ans_t ans[16] = {0}; task_ans_t ans[16] = {0};
thread_info_tm_t t_info = {.buf = (uint8_t*)arr, .len = 0, .ans = ans}; thread_info_tm_t t_info = {.buf = (uint8_t*)arr, .len = 0, .ans = ans};
#endif
int64_t const t0 = time_now_ns(); int64_t const t0 = time_now_ns();
int totalDecode = 0; int totalDecode = 0;
...@@ -971,49 +954,23 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) ...@@ -971,49 +954,23 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx)
// LOG_M("rxdataF_comp.m","rxF_comp",gNB->pusch_vars[0]->rxdataF_comp[0],6900,1,1); // LOG_M("rxdataF_comp.m","rxF_comp",gNB->pusch_vars[0]->rxdataF_comp[0],6900,1,1);
// LOG_M("rxdataF_ext.m","rxF_ext",gNB->pusch_vars[0]->rxdataF_ext[0],6900,1,1); // LOG_M("rxdataF_ext.m","rxF_ext",gNB->pusch_vars[0]->rxdataF_ext[0],6900,1,1);
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_ULSCH_PROCEDURES_RX, 1); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_ULSCH_PROCEDURES_RX, 1);
#ifdef TASK_MANAGER_DECODING
int const tasks_added = nr_ulsch_procedures(gNB, frame_rx, slot_rx, ULSCH_id, ulsch->harq_pid, &t_info);
#else
int const tasks_added = nr_ulsch_procedures(gNB, frame_rx, slot_rx, ULSCH_id, ulsch->harq_pid);
#endif
int const tasks_added = nr_ulsch_procedures(gNB, frame_rx, slot_rx, ULSCH_id, ulsch->harq_pid, &t_info);
if (tasks_added > 0) if (tasks_added > 0)
totalDecode += tasks_added; totalDecode += tasks_added;
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_ULSCH_PROCEDURES_RX, 0); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_ULSCH_PROCEDURES_RX, 0);
} }
} }
++cnt;
#ifdef TASK_MANAGER_DECODING
if (totalDecode > 0) {
assert(totalDecode == t_info.len); assert(totalDecode == t_info.len);
join_task_ans(t_info.ans, t_info.len); join_task_ans(t_info.ans, t_info.len);
for(int i = 0; i < t_info.len; ++i){ for(int i = 0; i < t_info.len; ++i){
nr_postDecode(gNB, &arr[i]); nr_postDecode(gNB, &arr[i]);
} }
if(cnt % 1024 == 0)
printf("Decoding time %ld \n", time_now_ns() - t0);
}
#else
bool const loop = totalDecode > 0;
while (totalDecode > 0) {
notifiedFIFO_elt_t *req = pullTpool(&gNB->respDecode, &gNB->threadPool);
if (req == NULL)
break; // Tpool has been stopped
nr_postDecode(gNB, req);
delNotifiedFIFO_elt(req);
totalDecode--;
}
<<<<<<< HEAD
=======
if(loop && (cnt % 1024 == 0))
printf("Decoding time %ld \n", time_now_ns() - t0);
#endif
stop_meas(&gNB->ulsch_decoding_stats); stop_meas(&gNB->ulsch_decoding_stats);
>>>>>>> ac01afe8d2 (Decoding in RF Sim working)
for (int i = 0; i < gNB->max_nb_srs; i++) { for (int i = 0; i < gNB->max_nb_srs; i++) {
NR_gNB_SRS_t *srs = &gNB->srs[i]; NR_gNB_SRS_t *srs = &gNB->srs[i];
if (srs) { if (srs) {
......
...@@ -93,14 +93,8 @@ void deref_sched_response(int _) ...@@ -93,14 +93,8 @@ void deref_sched_response(int _)
exit(1); exit(1);
} }
#ifdef TASK_MANAGER_DECODING
int nr_postDecode_sim(PHY_VARS_gNB *gNB, ldpcDecode_t *rdata , int *nb_ok) int nr_postDecode_sim(PHY_VARS_gNB *gNB, ldpcDecode_t *rdata , int *nb_ok)
{ {
#else
int nr_postDecode_sim(PHY_VARS_gNB *gNB, notifiedFIFO_elt_t *req, int *nb_ok)
{
ldpcDecode_t *rdata = (ldpcDecode_t*) NotifiedFifoData(req);
#endif
NR_UL_gNB_HARQ_t *ulsch_harq = rdata->ulsch_harq; NR_UL_gNB_HARQ_t *ulsch_harq = rdata->ulsch_harq;
int r = rdata->segment_r; int r = rdata->segment_r;
...@@ -417,12 +411,8 @@ int main(int argc, char **argv) ...@@ -417,12 +411,8 @@ int main(int argc, char **argv)
gNB = RC.gNB[0]; gNB = RC.gNB[0];
//gNB_config = &gNB->gNB_config; //gNB_config = &gNB->gNB_config;
#ifdef TASK_MANAGER_DECODING
int const num_threads = parse_num_threads("n"); int const num_threads = parse_num_threads("n");
init_task_manager(&gNB->man, num_threads); init_task_manager(&gNB->man, num_threads);
#else
initTpool("n", &gNB->threadPool, true);
#endif
initNotifiedFIFO(&gNB->respDecode); initNotifiedFIFO(&gNB->respDecode);
frame_parms = &gNB->frame_parms; //to be initialized I suppose (maybe not necessary for PBCH) frame_parms = &gNB->frame_parms; //to be initialized I suppose (maybe not necessary for PBCH)
...@@ -604,31 +594,18 @@ int main(int argc, char **argv) ...@@ -604,31 +594,18 @@ int main(int argc, char **argv)
exit(-1); exit(-1);
#endif #endif
#ifdef TASK_MANAGER_DECODING
ldpcDecode_t arr[16] = {0}; ldpcDecode_t arr[16] = {0};
task_ans_t ans[16] = {0}; task_ans_t ans[16] = {0};
thread_info_tm_t t_info = {.buf = (uint8_t*)arr, .len = 0, .ans = ans }; thread_info_tm_t t_info = {.buf = (uint8_t*)arr, .len = 0, .ans = ans };
int nbDecode = nr_ulsch_decoding(gNB, UE_id, channel_output_fixed, frame_parms, rel15_ul, frame, subframe, harq_pid, G, &t_info); int nbDecode = nr_ulsch_decoding(gNB, UE_id, channel_output_fixed, frame_parms, rel15_ul, frame, subframe, harq_pid, G, &t_info);
assert(nbDecode > 0); assert(nbDecode > 0);
#else
int nbDecode = nr_ulsch_decoding(gNB, UE_id, channel_output_fixed, frame_parms, rel15_ul, frame, subframe, harq_pid, G);
#endif
int nb_ok = 0; int nb_ok = 0;
#ifdef TASK_MANAGER_DECODING
join_task_ans(t_info.ans, t_info.len); join_task_ans(t_info.ans, t_info.len);
for(size_t i = 0; i < nbDecode; ++i){ for(size_t i = 0; i < nbDecode; ++i){
ret = nr_postDecode_sim(gNB, &arr[i], &nb_ok); ret = nr_postDecode_sim(gNB, &arr[i], &nb_ok);
} }
nbDecode = 0; nbDecode = 0;
#else
if (nbDecode > 0)
while (nbDecode > 0) {
notifiedFIFO_elt_t *req = pullTpool(&gNB->respDecode, &gNB->threadPool);
ret = nr_postDecode_sim(gNB, req, &nb_ok);
delNotifiedFIFO_elt(req);
nbDecode--;
}
#endif
if (ret) if (ret)
n_errors++; n_errors++;
} }
...@@ -655,10 +632,8 @@ int main(int argc, char **argv) ...@@ -655,10 +632,8 @@ int main(int argc, char **argv)
free(gNB->gNB_config.tdd_table.max_tdd_periodicity_list[i].max_num_of_symbol_per_slot_list); free(gNB->gNB_config.tdd_table.max_tdd_periodicity_list[i].max_num_of_symbol_per_slot_list);
free(gNB->gNB_config.tdd_table.max_tdd_periodicity_list); free(gNB->gNB_config.tdd_table.max_tdd_periodicity_list);
#ifdef TASK_MANAGER_DECODING
void (*clean)(task_t* args) = NULL; void (*clean)(task_t* args) = NULL;
free_task_manager(&gNB->man, clean); free_task_manager(&gNB->man, clean);
#endif
term_nr_ue_signal(UE, 1); term_nr_ue_signal(UE, 1);
free(UE); free(UE);
......
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