Commit 2f21be44 authored by David S. Miller's avatar David S. Miller

Merge branch 'ionic-next'

Shannon Nelson says:

====================
ionic: driver updates 27-July-2021

This is a collection of small driver updates for adding a couple of
small features and for a bit of code cleaning.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 7c57706b 18d64264
...@@ -165,10 +165,10 @@ static int ionic_vf_alloc(struct ionic *ionic, int num_vfs) ...@@ -165,10 +165,10 @@ static int ionic_vf_alloc(struct ionic *ionic, int num_vfs)
goto out; goto out;
} }
ionic->num_vfs++;
/* ignore failures from older FW, we just won't get stats */ /* ignore failures from older FW, we just won't get stats */
(void)ionic_set_vf_config(ionic, i, IONIC_VF_ATTR_STATSADDR, (void)ionic_set_vf_config(ionic, i, IONIC_VF_ATTR_STATSADDR,
(u8 *)&v->stats_pa); (u8 *)&v->stats_pa);
ionic->num_vfs++;
} }
out: out:
......
...@@ -106,6 +106,8 @@ int ionic_dev_setup(struct ionic *ionic) ...@@ -106,6 +106,8 @@ int ionic_dev_setup(struct ionic *ionic)
idev->last_fw_hb = 0; idev->last_fw_hb = 0;
idev->fw_hb_ready = true; idev->fw_hb_ready = true;
idev->fw_status_ready = true; idev->fw_status_ready = true;
idev->fw_generation = IONIC_FW_STS_F_GENERATION &
ioread8(&idev->dev_info_regs->fw_status);
mod_timer(&ionic->watchdog_timer, mod_timer(&ionic->watchdog_timer,
round_jiffies(jiffies + ionic->watchdog_period)); round_jiffies(jiffies + ionic->watchdog_period));
...@@ -121,7 +123,9 @@ int ionic_heartbeat_check(struct ionic *ionic) ...@@ -121,7 +123,9 @@ int ionic_heartbeat_check(struct ionic *ionic)
{ {
struct ionic_dev *idev = &ionic->idev; struct ionic_dev *idev = &ionic->idev;
unsigned long check_time, last_check_time; unsigned long check_time, last_check_time;
bool fw_status_ready, fw_hb_ready; bool fw_status_ready = true;
bool fw_hb_ready;
u8 fw_generation;
u8 fw_status; u8 fw_status;
u32 fw_hb; u32 fw_hb;
...@@ -140,9 +144,29 @@ int ionic_heartbeat_check(struct ionic *ionic) ...@@ -140,9 +144,29 @@ int ionic_heartbeat_check(struct ionic *ionic)
/* firmware is useful only if the running bit is set and /* firmware is useful only if the running bit is set and
* fw_status != 0xff (bad PCI read) * fw_status != 0xff (bad PCI read)
* If fw_status is not ready don't bother with the generation.
*/ */
fw_status = ioread8(&idev->dev_info_regs->fw_status); fw_status = ioread8(&idev->dev_info_regs->fw_status);
fw_status_ready = (fw_status != 0xff) && (fw_status & IONIC_FW_STS_F_RUNNING);
if (fw_status == 0xff || !(fw_status & IONIC_FW_STS_F_RUNNING)) {
fw_status_ready = false;
} else {
fw_generation = fw_status & IONIC_FW_STS_F_GENERATION;
if (idev->fw_generation != fw_generation) {
dev_info(ionic->dev, "FW generation 0x%02x -> 0x%02x\n",
idev->fw_generation, fw_generation);
idev->fw_generation = fw_generation;
/* If the generation changed, the fw status is not
* ready so we need to trigger a fw-down cycle. After
* the down, the next watchdog will see the fw is up
* and the generation value stable, so will trigger
* the fw-up activity.
*/
fw_status_ready = false;
}
}
/* is this a transition? */ /* is this a transition? */
if (fw_status_ready != idev->fw_status_ready) { if (fw_status_ready != idev->fw_status_ready) {
......
...@@ -143,6 +143,7 @@ struct ionic_dev { ...@@ -143,6 +143,7 @@ struct ionic_dev {
u32 last_fw_hb; u32 last_fw_hb;
bool fw_hb_ready; bool fw_hb_ready;
bool fw_status_ready; bool fw_status_ready;
u8 fw_generation;
u64 __iomem *db_pages; u64 __iomem *db_pages;
dma_addr_t phy_db_pages; dma_addr_t phy_db_pages;
...@@ -160,8 +161,6 @@ struct ionic_dev { ...@@ -160,8 +161,6 @@ struct ionic_dev {
struct ionic_cq_info { struct ionic_cq_info {
union { union {
void *cq_desc; void *cq_desc;
struct ionic_txq_comp *txcq;
struct ionic_rxq_comp *rxcq;
struct ionic_admin_comp *admincq; struct ionic_admin_comp *admincq;
struct ionic_notifyq_event *notifyq; struct ionic_notifyq_event *notifyq;
}; };
......
...@@ -32,6 +32,9 @@ static void ionic_get_stats(struct net_device *netdev, ...@@ -32,6 +32,9 @@ static void ionic_get_stats(struct net_device *netdev,
struct ionic_lif *lif = netdev_priv(netdev); struct ionic_lif *lif = netdev_priv(netdev);
u32 i; u32 i;
if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
return;
memset(buf, 0, stats->n_stats * sizeof(*buf)); memset(buf, 0, stats->n_stats * sizeof(*buf));
for (i = 0; i < ionic_num_stats_grps; i++) for (i = 0; i < ionic_num_stats_grps; i++)
ionic_stats_groups[i].get_values(lif, &buf); ionic_stats_groups[i].get_values(lif, &buf);
...@@ -274,6 +277,9 @@ static int ionic_set_link_ksettings(struct net_device *netdev, ...@@ -274,6 +277,9 @@ static int ionic_set_link_ksettings(struct net_device *netdev,
struct ionic *ionic = lif->ionic; struct ionic *ionic = lif->ionic;
int err = 0; int err = 0;
if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
return -EBUSY;
/* set autoneg */ /* set autoneg */
if (ks->base.autoneg != idev->port_info->config.an_enable) { if (ks->base.autoneg != idev->port_info->config.an_enable) {
mutex_lock(&ionic->dev_cmd_lock); mutex_lock(&ionic->dev_cmd_lock);
...@@ -320,6 +326,9 @@ static int ionic_set_pauseparam(struct net_device *netdev, ...@@ -320,6 +326,9 @@ static int ionic_set_pauseparam(struct net_device *netdev,
u32 requested_pause; u32 requested_pause;
int err; int err;
if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
return -EBUSY;
if (pause->autoneg) if (pause->autoneg)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -372,6 +381,9 @@ static int ionic_set_fecparam(struct net_device *netdev, ...@@ -372,6 +381,9 @@ static int ionic_set_fecparam(struct net_device *netdev,
u8 fec_type; u8 fec_type;
int ret = 0; int ret = 0;
if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
return -EBUSY;
if (lif->ionic->idev.port_info->config.an_enable) { if (lif->ionic->idev.port_info->config.an_enable) {
netdev_err(netdev, "FEC request not allowed while autoneg is enabled\n"); netdev_err(netdev, "FEC request not allowed while autoneg is enabled\n");
return -EINVAL; return -EINVAL;
...@@ -528,6 +540,9 @@ static int ionic_set_ringparam(struct net_device *netdev, ...@@ -528,6 +540,9 @@ static int ionic_set_ringparam(struct net_device *netdev,
struct ionic_queue_params qparam; struct ionic_queue_params qparam;
int err; int err;
if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
return -EBUSY;
ionic_init_queue_params(lif, &qparam); ionic_init_queue_params(lif, &qparam);
if (ring->rx_mini_pending || ring->rx_jumbo_pending) { if (ring->rx_mini_pending || ring->rx_jumbo_pending) {
...@@ -597,6 +612,9 @@ static int ionic_set_channels(struct net_device *netdev, ...@@ -597,6 +612,9 @@ static int ionic_set_channels(struct net_device *netdev,
int max_cnt; int max_cnt;
int err; int err;
if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
return -EBUSY;
ionic_init_queue_params(lif, &qparam); ionic_init_queue_params(lif, &qparam);
if (ch->rx_count != ch->tx_count) { if (ch->rx_count != ch->tx_count) {
...@@ -947,6 +965,9 @@ static int ionic_nway_reset(struct net_device *netdev) ...@@ -947,6 +965,9 @@ static int ionic_nway_reset(struct net_device *netdev)
struct ionic *ionic = lif->ionic; struct ionic *ionic = lif->ionic;
int err = 0; int err = 0;
if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
return -EBUSY;
/* flap the link to force auto-negotiation */ /* flap the link to force auto-negotiation */
mutex_lock(&ionic->dev_cmd_lock); mutex_lock(&ionic->dev_cmd_lock);
......
...@@ -2936,6 +2936,8 @@ struct ionic_hwstamp_regs { ...@@ -2936,6 +2936,8 @@ struct ionic_hwstamp_regs {
* @asic_type: Asic type * @asic_type: Asic type
* @asic_rev: Asic revision * @asic_rev: Asic revision
* @fw_status: Firmware status * @fw_status: Firmware status
* bit 0 - 1 = fw running
* bit 4-7 - 4 bit generation number, changes on fw restart
* @fw_heartbeat: Firmware heartbeat counter * @fw_heartbeat: Firmware heartbeat counter
* @serial_num: Serial number * @serial_num: Serial number
* @fw_version: Firmware version * @fw_version: Firmware version
...@@ -2949,7 +2951,8 @@ union ionic_dev_info_regs { ...@@ -2949,7 +2951,8 @@ union ionic_dev_info_regs {
u8 version; u8 version;
u8 asic_type; u8 asic_type;
u8 asic_rev; u8 asic_rev;
#define IONIC_FW_STS_F_RUNNING 0x1 #define IONIC_FW_STS_F_RUNNING 0x01
#define IONIC_FW_STS_F_GENERATION 0xF0
u8 fw_status; u8 fw_status;
u32 fw_heartbeat; u32 fw_heartbeat;
char fw_version[IONIC_DEVINFO_FWVERS_BUFLEN]; char fw_version[IONIC_DEVINFO_FWVERS_BUFLEN];
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/cpumask.h> #include <linux/cpumask.h>
#include <linux/crash_dump.h>
#include "ionic.h" #include "ionic.h"
#include "ionic_bus.h" #include "ionic_bus.h"
...@@ -1606,7 +1607,6 @@ static int ionic_init_nic_features(struct ionic_lif *lif) ...@@ -1606,7 +1607,6 @@ static int ionic_init_nic_features(struct ionic_lif *lif)
features = NETIF_F_HW_VLAN_CTAG_TX | features = NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_FILTER |
NETIF_F_RXHASH |
NETIF_F_SG | NETIF_F_SG |
NETIF_F_HW_CSUM | NETIF_F_HW_CSUM |
NETIF_F_RXCSUM | NETIF_F_RXCSUM |
...@@ -1614,6 +1614,9 @@ static int ionic_init_nic_features(struct ionic_lif *lif) ...@@ -1614,6 +1614,9 @@ static int ionic_init_nic_features(struct ionic_lif *lif)
NETIF_F_TSO6 | NETIF_F_TSO6 |
NETIF_F_TSO_ECN; NETIF_F_TSO_ECN;
if (lif->nxqs > 1)
features |= NETIF_F_RXHASH;
err = ionic_set_nic_features(lif, features); err = ionic_set_nic_features(lif, features);
if (err) if (err)
return err; return err;
...@@ -2587,22 +2590,26 @@ int ionic_reconfigure_queues(struct ionic_lif *lif, ...@@ -2587,22 +2590,26 @@ int ionic_reconfigure_queues(struct ionic_lif *lif,
struct ionic_qcq **tx_qcqs = NULL; struct ionic_qcq **tx_qcqs = NULL;
struct ionic_qcq **rx_qcqs = NULL; struct ionic_qcq **rx_qcqs = NULL;
unsigned int flags, i; unsigned int flags, i;
int err = -ENOMEM; int err = 0;
/* allocate temporary qcq arrays to hold new queue structs */ /* allocate temporary qcq arrays to hold new queue structs */
if (qparam->nxqs != lif->nxqs || qparam->ntxq_descs != lif->ntxq_descs) { if (qparam->nxqs != lif->nxqs || qparam->ntxq_descs != lif->ntxq_descs) {
tx_qcqs = devm_kcalloc(lif->ionic->dev, lif->ionic->ntxqs_per_lif, tx_qcqs = devm_kcalloc(lif->ionic->dev, lif->ionic->ntxqs_per_lif,
sizeof(struct ionic_qcq *), GFP_KERNEL); sizeof(struct ionic_qcq *), GFP_KERNEL);
if (!tx_qcqs) if (!tx_qcqs) {
err = -ENOMEM;
goto err_out; goto err_out;
}
} }
if (qparam->nxqs != lif->nxqs || if (qparam->nxqs != lif->nxqs ||
qparam->nrxq_descs != lif->nrxq_descs || qparam->nrxq_descs != lif->nrxq_descs ||
qparam->rxq_features != lif->rxq_features) { qparam->rxq_features != lif->rxq_features) {
rx_qcqs = devm_kcalloc(lif->ionic->dev, lif->ionic->nrxqs_per_lif, rx_qcqs = devm_kcalloc(lif->ionic->dev, lif->ionic->nrxqs_per_lif,
sizeof(struct ionic_qcq *), GFP_KERNEL); sizeof(struct ionic_qcq *), GFP_KERNEL);
if (!rx_qcqs) if (!rx_qcqs) {
err = -ENOMEM;
goto err_out; goto err_out;
}
} }
/* allocate new desc_info and rings, but leave the interrupt setup /* allocate new desc_info and rings, but leave the interrupt setup
...@@ -2781,6 +2788,9 @@ int ionic_reconfigure_queues(struct ionic_lif *lif, ...@@ -2781,6 +2788,9 @@ int ionic_reconfigure_queues(struct ionic_lif *lif,
ionic_qcq_free(lif, lif->rxqcqs[i]); ionic_qcq_free(lif, lif->rxqcqs[i]);
} }
if (err)
netdev_info(lif->netdev, "%s: failed %d\n", __func__, err);
return err; return err;
} }
...@@ -2834,8 +2844,14 @@ int ionic_lif_alloc(struct ionic *ionic) ...@@ -2834,8 +2844,14 @@ int ionic_lif_alloc(struct ionic *ionic)
lif->ionic = ionic; lif->ionic = ionic;
lif->index = 0; lif->index = 0;
lif->ntxq_descs = IONIC_DEF_TXRX_DESC;
lif->nrxq_descs = IONIC_DEF_TXRX_DESC; if (is_kdump_kernel()) {
lif->ntxq_descs = IONIC_MIN_TXRX_DESC;
lif->nrxq_descs = IONIC_MIN_TXRX_DESC;
} else {
lif->ntxq_descs = IONIC_DEF_TXRX_DESC;
lif->nrxq_descs = IONIC_DEF_TXRX_DESC;
}
/* Convert the default coalesce value to actual hw resolution */ /* Convert the default coalesce value to actual hw resolution */
lif->rx_coalesce_usecs = IONIC_ITR_COAL_USEC_DEFAULT; lif->rx_coalesce_usecs = IONIC_ITR_COAL_USEC_DEFAULT;
...@@ -3519,6 +3535,7 @@ int ionic_lif_size(struct ionic *ionic) ...@@ -3519,6 +3535,7 @@ int ionic_lif_size(struct ionic *ionic)
unsigned int min_intrs; unsigned int min_intrs;
int err; int err;
/* retrieve basic values from FW */
lc = &ident->lif.eth.config; lc = &ident->lif.eth.config;
dev_nintrs = le32_to_cpu(ident->dev.nintrs); dev_nintrs = le32_to_cpu(ident->dev.nintrs);
neqs_per_lif = le32_to_cpu(ident->lif.rdma.eq_qtype.qid_count); neqs_per_lif = le32_to_cpu(ident->lif.rdma.eq_qtype.qid_count);
...@@ -3526,6 +3543,15 @@ int ionic_lif_size(struct ionic *ionic) ...@@ -3526,6 +3543,15 @@ int ionic_lif_size(struct ionic *ionic)
ntxqs_per_lif = le32_to_cpu(lc->queue_count[IONIC_QTYPE_TXQ]); ntxqs_per_lif = le32_to_cpu(lc->queue_count[IONIC_QTYPE_TXQ]);
nrxqs_per_lif = le32_to_cpu(lc->queue_count[IONIC_QTYPE_RXQ]); nrxqs_per_lif = le32_to_cpu(lc->queue_count[IONIC_QTYPE_RXQ]);
/* limit values to play nice with kdump */
if (is_kdump_kernel()) {
dev_nintrs = 2;
neqs_per_lif = 0;
nnqs_per_lif = 0;
ntxqs_per_lif = 1;
nrxqs_per_lif = 1;
}
/* reserve last queue id for hardware timestamping */ /* reserve last queue id for hardware timestamping */
if (lc->features & cpu_to_le64(IONIC_ETH_HW_TIMESTAMP)) { if (lc->features & cpu_to_le64(IONIC_ETH_HW_TIMESTAMP)) {
if (ntxqs_per_lif <= 1 || nrxqs_per_lif <= 1) { if (ntxqs_per_lif <= 1 || nrxqs_per_lif <= 1) {
......
...@@ -450,6 +450,8 @@ int ionic_identify(struct ionic *ionic) ...@@ -450,6 +450,8 @@ int ionic_identify(struct ionic *ionic)
} }
mutex_unlock(&ionic->dev_cmd_lock); mutex_unlock(&ionic->dev_cmd_lock);
dev_info(ionic->dev, "FW: %s\n", idev->dev_info.fw_version);
if (err) { if (err) {
dev_err(ionic->dev, "Cannot identify ionic: %dn", err); dev_err(ionic->dev, "Cannot identify ionic: %dn", err);
goto err_out; goto err_out;
......
...@@ -119,8 +119,8 @@ static int ionic_lif_hwstamp_set_ts_config(struct ionic_lif *lif, ...@@ -119,8 +119,8 @@ static int ionic_lif_hwstamp_set_ts_config(struct ionic_lif *lif,
config->rx_filter = HWTSTAMP_FILTER_ALL; config->rx_filter = HWTSTAMP_FILTER_ALL;
} }
dev_dbg(ionic->dev, "config_rx_filter %d rx_filt %#llx rx_all %d\n", dev_dbg(ionic->dev, "%s: config_rx_filter %d rx_filt %#llx rx_all %d\n",
config->rx_filter, rx_filt, rx_all); __func__, config->rx_filter, rx_filt, rx_all);
if (tx_mode) { if (tx_mode) {
err = ionic_lif_create_hwstamp_txq(lif); err = ionic_lif_create_hwstamp_txq(lif);
......
...@@ -32,19 +32,13 @@ static inline struct netdev_queue *q_to_ndq(struct ionic_queue *q) ...@@ -32,19 +32,13 @@ static inline struct netdev_queue *q_to_ndq(struct ionic_queue *q)
return netdev_get_tx_queue(q->lif->netdev, q->index); return netdev_get_tx_queue(q->lif->netdev, q->index);
} }
static void ionic_rx_buf_reset(struct ionic_buf_info *buf_info)
{
buf_info->page = NULL;
buf_info->page_offset = 0;
buf_info->dma_addr = 0;
}
static int ionic_rx_page_alloc(struct ionic_queue *q, static int ionic_rx_page_alloc(struct ionic_queue *q,
struct ionic_buf_info *buf_info) struct ionic_buf_info *buf_info)
{ {
struct net_device *netdev = q->lif->netdev; struct net_device *netdev = q->lif->netdev;
struct ionic_rx_stats *stats; struct ionic_rx_stats *stats;
struct device *dev; struct device *dev;
struct page *page;
dev = q->dev; dev = q->dev;
stats = q_to_rx_stats(q); stats = q_to_rx_stats(q);
...@@ -55,26 +49,27 @@ static int ionic_rx_page_alloc(struct ionic_queue *q, ...@@ -55,26 +49,27 @@ static int ionic_rx_page_alloc(struct ionic_queue *q,
return -EINVAL; return -EINVAL;
} }
buf_info->page = alloc_pages(IONIC_PAGE_GFP_MASK, 0); page = alloc_pages(IONIC_PAGE_GFP_MASK, 0);
if (unlikely(!buf_info->page)) { if (unlikely(!page)) {
net_err_ratelimited("%s: %s page alloc failed\n", net_err_ratelimited("%s: %s page alloc failed\n",
netdev->name, q->name); netdev->name, q->name);
stats->alloc_err++; stats->alloc_err++;
return -ENOMEM; return -ENOMEM;
} }
buf_info->page_offset = 0;
buf_info->dma_addr = dma_map_page(dev, buf_info->page, buf_info->page_offset, buf_info->dma_addr = dma_map_page(dev, page, 0,
IONIC_PAGE_SIZE, DMA_FROM_DEVICE); IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
if (unlikely(dma_mapping_error(dev, buf_info->dma_addr))) { if (unlikely(dma_mapping_error(dev, buf_info->dma_addr))) {
__free_pages(buf_info->page, 0); __free_pages(page, 0);
ionic_rx_buf_reset(buf_info);
net_err_ratelimited("%s: %s dma map failed\n", net_err_ratelimited("%s: %s dma map failed\n",
netdev->name, q->name); netdev->name, q->name);
stats->dma_map_err++; stats->dma_map_err++;
return -EIO; return -EIO;
} }
buf_info->page = page;
buf_info->page_offset = 0;
return 0; return 0;
} }
...@@ -95,7 +90,7 @@ static void ionic_rx_page_free(struct ionic_queue *q, ...@@ -95,7 +90,7 @@ static void ionic_rx_page_free(struct ionic_queue *q,
dma_unmap_page(dev, buf_info->dma_addr, IONIC_PAGE_SIZE, DMA_FROM_DEVICE); dma_unmap_page(dev, buf_info->dma_addr, IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
__free_pages(buf_info->page, 0); __free_pages(buf_info->page, 0);
ionic_rx_buf_reset(buf_info); buf_info->page = NULL;
} }
static bool ionic_rx_buf_recycle(struct ionic_queue *q, static bool ionic_rx_buf_recycle(struct ionic_queue *q,
...@@ -139,7 +134,7 @@ static struct sk_buff *ionic_rx_frags(struct ionic_queue *q, ...@@ -139,7 +134,7 @@ static struct sk_buff *ionic_rx_frags(struct ionic_queue *q,
buf_info = &desc_info->bufs[0]; buf_info = &desc_info->bufs[0];
len = le16_to_cpu(comp->len); len = le16_to_cpu(comp->len);
prefetch(buf_info->page); prefetchw(buf_info->page);
skb = napi_get_frags(&q_to_qcq(q)->napi); skb = napi_get_frags(&q_to_qcq(q)->napi);
if (unlikely(!skb)) { if (unlikely(!skb)) {
...@@ -170,7 +165,7 @@ static struct sk_buff *ionic_rx_frags(struct ionic_queue *q, ...@@ -170,7 +165,7 @@ static struct sk_buff *ionic_rx_frags(struct ionic_queue *q,
if (!ionic_rx_buf_recycle(q, buf_info, frag_len)) { if (!ionic_rx_buf_recycle(q, buf_info, frag_len)) {
dma_unmap_page(dev, buf_info->dma_addr, dma_unmap_page(dev, buf_info->dma_addr,
IONIC_PAGE_SIZE, DMA_FROM_DEVICE); IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
ionic_rx_buf_reset(buf_info); buf_info->page = NULL;
} }
buf_info++; buf_info++;
......
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