Commit 9798594c authored by David S. Miller's avatar David S. Miller

Merge branch 'hns3-Unicast-MAC-VLAN-table'

Salil Mehta says:

====================
Fixes, minor changes & cleanups for the Unicast MAC VLAN table

This patch-set presents necessary modifications, fixes & cleanups related
to Unicast MAC Vlan Table to support revision 0x21 hardware.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 226407dd 701a6d6a
......@@ -46,9 +46,6 @@ enum hclge_mbx_mac_vlan_subcode {
HCLGE_MBX_MAC_VLAN_MC_MODIFY, /* modify MC mac addr */
HCLGE_MBX_MAC_VLAN_MC_ADD, /* add new MC mac addr */
HCLGE_MBX_MAC_VLAN_MC_REMOVE, /* remove MC mac addr */
HCLGE_MBX_MAC_VLAN_MC_FUNC_MTA_ENABLE, /* config func MTA enable */
HCLGE_MBX_MAC_VLAN_MTA_TYPE_READ, /* read func MTA type */
HCLGE_MBX_MAC_VLAN_MTA_STATUS_UPDATE, /* update MTA status */
};
/* below are per-VF vlan cfg subcodes */
......
......@@ -355,8 +355,6 @@ struct hnae3_ae_ops {
const unsigned char *addr);
int (*rm_mc_addr)(struct hnae3_handle *handle,
const unsigned char *addr);
int (*update_mta_status)(struct hnae3_handle *handle);
void (*set_tso_stats)(struct hnae3_handle *handle, int enable);
void (*update_stats)(struct hnae3_handle *handle,
struct net_device_stats *net_stats);
......
......@@ -21,6 +21,7 @@
static void hns3_clear_all_ring(struct hnae3_handle *h);
static void hns3_force_clear_all_rx_ring(struct hnae3_handle *h);
static void hns3_remove_hw_addr(struct net_device *netdev);
static const char hns3_driver_name[] = "hns3";
const char hns3_driver_version[] = VERMAGIC_STRING;
......@@ -475,9 +476,6 @@ static void hns3_nic_set_rx_mode(struct net_device *netdev)
if (netdev->flags & IFF_MULTICAST) {
if (__dev_mc_sync(netdev, hns3_nic_mc_sync, hns3_nic_mc_unsync))
netdev_err(netdev, "sync mc address fail\n");
if (h->ae_algo->ops->update_mta_status)
h->ae_algo->ops->update_mta_status(h);
}
}
......@@ -2202,18 +2200,18 @@ static void hns3_rx_skb(struct hns3_enet_ring *ring, struct sk_buff *skb)
napi_gro_receive(&ring->tqp_vector->napi, skb);
}
static u16 hns3_parse_vlan_tag(struct hns3_enet_ring *ring,
struct hns3_desc *desc, u32 l234info)
static bool hns3_parse_vlan_tag(struct hns3_enet_ring *ring,
struct hns3_desc *desc, u32 l234info,
u16 *vlan_tag)
{
struct pci_dev *pdev = ring->tqp->handle->pdev;
u16 vlan_tag;
if (pdev->revision == 0x20) {
vlan_tag = le16_to_cpu(desc->rx.ot_vlan_tag);
if (!(vlan_tag & VLAN_VID_MASK))
vlan_tag = le16_to_cpu(desc->rx.vlan_tag);
*vlan_tag = le16_to_cpu(desc->rx.ot_vlan_tag);
if (!(*vlan_tag & VLAN_VID_MASK))
*vlan_tag = le16_to_cpu(desc->rx.vlan_tag);
return vlan_tag;
return (*vlan_tag != 0);
}
#define HNS3_STRP_OUTER_VLAN 0x1
......@@ -2222,17 +2220,14 @@ static u16 hns3_parse_vlan_tag(struct hns3_enet_ring *ring,
switch (hnae3_get_field(l234info, HNS3_RXD_STRP_TAGP_M,
HNS3_RXD_STRP_TAGP_S)) {
case HNS3_STRP_OUTER_VLAN:
vlan_tag = le16_to_cpu(desc->rx.ot_vlan_tag);
break;
*vlan_tag = le16_to_cpu(desc->rx.ot_vlan_tag);
return true;
case HNS3_STRP_INNER_VLAN:
vlan_tag = le16_to_cpu(desc->rx.vlan_tag);
break;
*vlan_tag = le16_to_cpu(desc->rx.vlan_tag);
return true;
default:
vlan_tag = 0;
break;
return false;
}
return vlan_tag;
}
static int hns3_handle_rx_bd(struct hns3_enet_ring *ring,
......@@ -2334,8 +2329,7 @@ static int hns3_handle_rx_bd(struct hns3_enet_ring *ring,
if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) {
u16 vlan_tag;
vlan_tag = hns3_parse_vlan_tag(ring, desc, l234info);
if (vlan_tag & VLAN_VID_MASK)
if (hns3_parse_vlan_tag(ring, desc, l234info, &vlan_tag))
__vlan_hwaccel_put_tag(skb,
htons(ETH_P_8021Q),
vlan_tag);
......@@ -3155,15 +3149,6 @@ static void hns3_init_mac_addr(struct net_device *netdev, bool init)
}
static void hns3_uninit_mac_addr(struct net_device *netdev)
{
struct hns3_nic_priv *priv = netdev_priv(netdev);
struct hnae3_handle *h = priv->ae_handle;
if (h->ae_algo->ops->rm_uc_addr)
h->ae_algo->ops->rm_uc_addr(h, netdev->dev_addr);
}
static int hns3_restore_fd_rules(struct net_device *netdev)
{
struct hnae3_handle *h = hns3_get_handle(netdev);
......@@ -3296,6 +3281,8 @@ static void hns3_client_uninit(struct hnae3_handle *handle, bool reset)
struct hns3_nic_priv *priv = netdev_priv(netdev);
int ret;
hns3_remove_hw_addr(netdev);
if (netdev->reg_state != NETREG_UNINITIALIZED)
unregister_netdev(netdev);
......@@ -3319,8 +3306,6 @@ static void hns3_client_uninit(struct hnae3_handle *handle, bool reset)
priv->ring_data = NULL;
hns3_uninit_mac_addr(netdev);
free_netdev(netdev);
}
......@@ -3392,6 +3377,25 @@ static void hns3_recover_hw_addr(struct net_device *ndev)
hns3_nic_mc_sync(ndev, ha->addr);
}
static void hns3_remove_hw_addr(struct net_device *netdev)
{
struct netdev_hw_addr_list *list;
struct netdev_hw_addr *ha, *tmp;
hns3_nic_uc_unsync(netdev, netdev->dev_addr);
/* go through and unsync uc_addr entries to the device */
list = &netdev->uc;
list_for_each_entry_safe(ha, tmp, &list->list, list)
hns3_nic_uc_unsync(netdev, ha->addr);
/* go through and unsync mc_addr entries to the device */
list = &netdev->mc;
list_for_each_entry_safe(ha, tmp, &list->list, list)
if (ha->refcount > 1)
hns3_nic_mc_unsync(netdev, ha->addr);
}
static void hns3_clear_tx_ring(struct hns3_enet_ring *ring)
{
while (ring->next_to_clean != ring->next_to_use) {
......@@ -3637,14 +3641,14 @@ static int hns3_reset_notify_uninit_enet(struct hnae3_handle *handle)
if (ret)
netdev_err(netdev, "uninit ring error\n");
hns3_uninit_mac_addr(netdev);
/* it is cumbersome for hardware to pick-and-choose rules for deletion
* from TCAM. Hence, for function reset software intervention is
* required to delete the rules
/* it is cumbersome for hardware to pick-and-choose entries for deletion
* from table space. Hence, for function reset software intervention is
* required to delete the entries
*/
if (hns3_dev_ongoing_func_reset(ae_dev))
if (hns3_dev_ongoing_func_reset(ae_dev)) {
hns3_remove_hw_addr(netdev);
hns3_del_all_fd_rules(netdev, false);
}
return ret;
}
......
......@@ -175,15 +175,9 @@ enum hclge_opcode_type {
HCLGE_OPC_MAC_VLAN_REMOVE = 0x1001,
HCLGE_OPC_MAC_VLAN_TYPE_ID = 0x1002,
HCLGE_OPC_MAC_VLAN_INSERT = 0x1003,
HCLGE_OPC_MAC_VLAN_ALLOCATE = 0x1004,
HCLGE_OPC_MAC_ETHTYPE_ADD = 0x1010,
HCLGE_OPC_MAC_ETHTYPE_REMOVE = 0x1011,
HCLGE_OPC_MAC_VLAN_MASK_SET = 0x1012,
/* Multicast linear table commands */
HCLGE_OPC_MTA_MAC_MODE_CFG = 0x1020,
HCLGE_OPC_MTA_MAC_FUNC_CFG = 0x1021,
HCLGE_OPC_MTA_TBL_ITEM_CFG = 0x1022,
HCLGE_OPC_MTA_TBL_ITEM_QUERY = 0x1023,
/* VLAN commands */
HCLGE_OPC_VLAN_FILTER_CTRL = 0x1100,
......@@ -402,6 +396,8 @@ struct hclge_pf_res_cmd {
#define HCLGE_CFG_RSS_SIZE_M GENMASK(31, 24)
#define HCLGE_CFG_SPEED_ABILITY_S 0
#define HCLGE_CFG_SPEED_ABILITY_M GENMASK(7, 0)
#define HCLGE_CFG_UMV_TBL_SPACE_S 16
#define HCLGE_CFG_UMV_TBL_SPACE_M GENMASK(31, 16)
struct hclge_cfg_param_cmd {
__le32 offset;
......@@ -591,13 +587,12 @@ struct hclge_mac_vlan_tbl_entry_cmd {
u8 rsv2[6];
};
#define HCLGE_VLAN_MASK_EN_B 0
struct hclge_mac_vlan_mask_entry_cmd {
u8 rsv0[2];
u8 vlan_mask;
u8 rsv1;
u8 mac_mask[6];
u8 rsv2[14];
#define HCLGE_UMV_SPC_ALC_B 0
struct hclge_umv_spc_alc_cmd {
u8 allocate;
u8 rsv1[3];
__le32 space_size;
u8 rsv2[16];
};
#define HCLGE_MAC_MGR_MASK_VLAN_B BIT(0)
......@@ -622,30 +617,6 @@ struct hclge_mac_mgr_tbl_entry_cmd {
u8 rsv3[2];
};
#define HCLGE_CFG_MTA_MAC_SEL_S 0
#define HCLGE_CFG_MTA_MAC_SEL_M GENMASK(1, 0)
#define HCLGE_CFG_MTA_MAC_EN_B 7
struct hclge_mta_filter_mode_cmd {
u8 dmac_sel_en; /* Use lowest 2 bit as sel_mode, bit 7 as enable */
u8 rsv[23];
};
#define HCLGE_CFG_FUNC_MTA_ACCEPT_B 0
struct hclge_cfg_func_mta_filter_cmd {
u8 accept; /* Only used lowest 1 bit */
u8 function_id;
u8 rsv[22];
};
#define HCLGE_CFG_MTA_ITEM_ACCEPT_B 0
#define HCLGE_CFG_MTA_ITEM_IDX_S 0
#define HCLGE_CFG_MTA_ITEM_IDX_M GENMASK(11, 0)
struct hclge_cfg_func_mta_item_cmd {
__le16 item_idx; /* Only used lowest 12 bit */
u8 accept; /* Only used lowest 1 bit */
u8 rsv[21];
};
struct hclge_mac_vlan_add_cmd {
__le16 flags;
__le16 mac_addr_hi16;
......
......@@ -14,6 +14,8 @@
#define HCLGE_MOD_VERSION "1.0"
#define HCLGE_DRIVER_NAME "hclge"
#define HCLGE_MAX_PF_NUM 8
#define HCLGE_INVALID_VPORT 0xffff
#define HCLGE_PF_CFG_BLOCK_SIZE 32
......@@ -53,7 +55,9 @@
#define HCLGE_RSS_TC_SIZE_6 64
#define HCLGE_RSS_TC_SIZE_7 128
#define HCLGE_MTA_TBL_SIZE 4096
#define HCLGE_UMV_TBL_SIZE 3072
#define HCLGE_DEFAULT_UMV_SPACE_PER_PF \
(HCLGE_UMV_TBL_SIZE / HCLGE_MAX_PF_NUM)
#define HCLGE_TQP_RESET_TRY_TIMES 10
......@@ -162,13 +166,6 @@ enum HCLGE_MAC_DUPLEX {
HCLGE_MAC_FULL
};
enum hclge_mta_dmac_sel_type {
HCLGE_MAC_ADDR_47_36,
HCLGE_MAC_ADDR_46_35,
HCLGE_MAC_ADDR_45_34,
HCLGE_MAC_ADDR_44_33,
};
struct hclge_mac {
u8 phy_addr;
u8 flag;
......@@ -251,6 +248,7 @@ struct hclge_cfg {
u8 default_speed;
u32 numa_node_map;
u8 speed_ability;
u16 umv_space;
};
struct hclge_tm_info {
......@@ -670,9 +668,6 @@ struct hclge_dev {
u32 pkt_buf_size; /* Total pf buf size for tx/rx */
u32 mps; /* Max packet size */
enum hclge_mta_dmac_sel_type mta_mac_sel_type;
bool enable_mta; /* Multicast filter enable */
struct hclge_vlan_type_cfg vlan_type_cfg;
unsigned long vlan_table[VLAN_N_VID][BITS_TO_LONGS(HCLGE_VPORT_NUM)];
......@@ -680,6 +675,15 @@ struct hclge_dev {
struct hclge_fd_cfg fd_cfg;
struct hlist_head fd_rule_list;
u16 hclge_fd_rule_num;
u16 wanted_umv_size;
/* max available unicast mac vlan space */
u16 max_umv_size;
/* private unicast mac vlan space, it's same for PF and its VFs */
u16 priv_umv_size;
/* unicast mac vlan space shared by PF and its VFs */
u16 share_umv_size;
struct mutex umv_mutex; /* protect share_umv_size */
};
/* VPort level vlan tag configuration for TX direction */
......@@ -732,13 +736,12 @@ struct hclge_vport {
struct hclge_tx_vtag_cfg txvlan_cfg;
struct hclge_rx_vtag_cfg rxvlan_cfg;
u16 used_umv_num;
int vport_id;
struct hclge_dev *back; /* Back reference to associated dev */
struct hnae3_handle nic;
struct hnae3_handle roce;
bool accept_mta_mc; /* whether to accept mta filter multicast */
unsigned long mta_shadow[BITS_TO_LONGS(HCLGE_MTA_TBL_SIZE)];
};
void hclge_promisc_param_init(struct hclge_promisc_param *param, bool en_uc,
......@@ -753,15 +756,6 @@ int hclge_add_mc_addr_common(struct hclge_vport *vport,
int hclge_rm_mc_addr_common(struct hclge_vport *vport,
const unsigned char *addr);
int hclge_cfg_func_mta_filter(struct hclge_dev *hdev,
u8 func_id,
bool enable);
int hclge_update_mta_status_common(struct hclge_vport *vport,
unsigned long *status,
u16 idx,
u16 count,
bool update_filter);
struct hclge_vport *hclge_get_vport(struct hnae3_handle *handle);
int hclge_bind_ring_with_vector(struct hclge_vport *vport,
int vector_id, bool en,
......
......@@ -233,43 +233,6 @@ static int hclge_set_vf_uc_mac_addr(struct hclge_vport *vport,
return 0;
}
static int hclge_set_vf_mc_mta_status(struct hclge_vport *vport,
u8 *msg, u8 idx, bool is_end)
{
#define HCLGE_MTA_STATUS_MSG_SIZE 13
#define HCLGE_MTA_STATUS_MSG_BITS \
(HCLGE_MTA_STATUS_MSG_SIZE * BITS_PER_BYTE)
#define HCLGE_MTA_STATUS_MSG_END_BITS \
(HCLGE_MTA_TBL_SIZE % HCLGE_MTA_STATUS_MSG_BITS)
unsigned long status[BITS_TO_LONGS(HCLGE_MTA_STATUS_MSG_BITS)];
u16 tbl_cnt;
u16 tbl_idx;
u8 msg_ofs;
u8 msg_bit;
tbl_cnt = is_end ? HCLGE_MTA_STATUS_MSG_END_BITS :
HCLGE_MTA_STATUS_MSG_BITS;
/* set msg field */
msg_ofs = 0;
msg_bit = 0;
memset(status, 0, sizeof(status));
for (tbl_idx = 0; tbl_idx < tbl_cnt; tbl_idx++) {
if (msg[msg_ofs] & BIT(msg_bit))
set_bit(tbl_idx, status);
msg_bit++;
if (msg_bit == BITS_PER_BYTE) {
msg_bit = 0;
msg_ofs++;
}
}
return hclge_update_mta_status_common(vport,
status, idx * HCLGE_MTA_STATUS_MSG_BITS,
tbl_cnt, is_end);
}
static int hclge_set_vf_mc_mac_addr(struct hclge_vport *vport,
struct hclge_mbx_vf_to_pf_cmd *mbx_req,
bool gen_resp)
......@@ -284,27 +247,6 @@ static int hclge_set_vf_mc_mac_addr(struct hclge_vport *vport,
status = hclge_add_mc_addr_common(vport, mac_addr);
} else if (mbx_req->msg[1] == HCLGE_MBX_MAC_VLAN_MC_REMOVE) {
status = hclge_rm_mc_addr_common(vport, mac_addr);
} else if (mbx_req->msg[1] == HCLGE_MBX_MAC_VLAN_MC_FUNC_MTA_ENABLE) {
u8 func_id = vport->vport_id;
bool enable = mbx_req->msg[2];
status = hclge_cfg_func_mta_filter(hdev, func_id, enable);
} else if (mbx_req->msg[1] == HCLGE_MBX_MAC_VLAN_MTA_TYPE_READ) {
resp_data = hdev->mta_mac_sel_type;
resp_len = sizeof(u8);
gen_resp = true;
status = 0;
} else if (mbx_req->msg[1] == HCLGE_MBX_MAC_VLAN_MTA_STATUS_UPDATE) {
/* mta status update msg format
* msg[2.6 : 2.0] msg index
* msg[2.7] msg is end
* msg[15 : 3] mta status bits[103 : 0]
*/
bool is_end = (mbx_req->msg[2] & 0x80) ? true : false;
status = hclge_set_vf_mc_mta_status(vport, &mbx_req->msg[3],
mbx_req->msg[2] & 0x7F,
is_end);
} else {
dev_err(&hdev->pdev->dev,
"failed to set mcast mac addr, unknown subcode %d\n",
......
......@@ -746,126 +746,6 @@ static void hclgevf_reset_tqp_stats(struct hnae3_handle *handle)
}
}
static int hclgevf_cfg_func_mta_type(struct hclgevf_dev *hdev)
{
u8 resp_msg = HCLGEVF_MTA_TYPE_SEL_MAX;
int ret;
ret = hclgevf_send_mbx_msg(hdev, HCLGE_MBX_SET_MULTICAST,
HCLGE_MBX_MAC_VLAN_MTA_TYPE_READ,
NULL, 0, true, &resp_msg, sizeof(u8));
if (ret) {
dev_err(&hdev->pdev->dev,
"Read mta type fail, ret=%d.\n", ret);
return ret;
}
if (resp_msg > HCLGEVF_MTA_TYPE_SEL_MAX) {
dev_err(&hdev->pdev->dev,
"Read mta type invalid, resp=%d.\n", resp_msg);
return -EINVAL;
}
hdev->mta_mac_sel_type = resp_msg;
return 0;
}
static u16 hclgevf_get_mac_addr_to_mta_index(struct hclgevf_dev *hdev,
const u8 *addr)
{
u32 rsh = HCLGEVF_MTA_TYPE_SEL_MAX - hdev->mta_mac_sel_type;
u16 high_val = addr[1] | (addr[0] << 8);
return (high_val >> rsh) & 0xfff;
}
static int hclgevf_do_update_mta_status(struct hclgevf_dev *hdev,
unsigned long *status)
{
#define HCLGEVF_MTA_STATUS_MSG_SIZE 13
#define HCLGEVF_MTA_STATUS_MSG_BITS \
(HCLGEVF_MTA_STATUS_MSG_SIZE * BITS_PER_BYTE)
#define HCLGEVF_MTA_STATUS_MSG_END_BITS \
(HCLGEVF_MTA_TBL_SIZE % HCLGEVF_MTA_STATUS_MSG_BITS)
u16 tbl_cnt;
u16 tbl_idx;
u8 msg_cnt;
u8 msg_idx;
int ret;
msg_cnt = DIV_ROUND_UP(HCLGEVF_MTA_TBL_SIZE,
HCLGEVF_MTA_STATUS_MSG_BITS);
tbl_idx = 0;
msg_idx = 0;
while (msg_cnt--) {
u8 msg[HCLGEVF_MTA_STATUS_MSG_SIZE + 1];
u8 *p = &msg[1];
u8 msg_ofs;
u8 msg_bit;
memset(msg, 0, sizeof(msg));
/* set index field */
msg[0] = 0x7F & msg_idx;
/* set end flag field */
if (msg_cnt == 0) {
msg[0] |= 0x80;
tbl_cnt = HCLGEVF_MTA_STATUS_MSG_END_BITS;
} else {
tbl_cnt = HCLGEVF_MTA_STATUS_MSG_BITS;
}
/* set status field */
msg_ofs = 0;
msg_bit = 0;
while (tbl_cnt--) {
if (test_bit(tbl_idx, status))
p[msg_ofs] |= BIT(msg_bit);
tbl_idx++;
msg_bit++;
if (msg_bit == BITS_PER_BYTE) {
msg_bit = 0;
msg_ofs++;
}
}
ret = hclgevf_send_mbx_msg(hdev, HCLGE_MBX_SET_MULTICAST,
HCLGE_MBX_MAC_VLAN_MTA_STATUS_UPDATE,
msg, sizeof(msg), false, NULL, 0);
if (ret)
break;
msg_idx++;
}
return ret;
}
static int hclgevf_update_mta_status(struct hnae3_handle *handle)
{
unsigned long mta_status[BITS_TO_LONGS(HCLGEVF_MTA_TBL_SIZE)];
struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
struct net_device *netdev = hdev->nic.kinfo.netdev;
struct netdev_hw_addr *ha;
u16 tbl_idx;
/* clear status */
memset(mta_status, 0, sizeof(mta_status));
/* update status from mc addr list */
netdev_for_each_mc_addr(ha, netdev) {
tbl_idx = hclgevf_get_mac_addr_to_mta_index(hdev, ha->addr);
set_bit(tbl_idx, mta_status);
}
return hclgevf_do_update_mta_status(hdev, mta_status);
}
static void hclgevf_get_mac_addr(struct hnae3_handle *handle, u8 *p)
{
struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
......@@ -1871,14 +1751,6 @@ static int hclgevf_init_hdev(struct hclgevf_dev *hdev)
goto err_config;
}
/* Initialize mta type for this VF */
ret = hclgevf_cfg_func_mta_type(hdev);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed(%d) to initialize MTA type\n", ret);
goto err_config;
}
/* Initialize RSS for this VF */
ret = hclgevf_rss_init_hw(hdev);
if (ret) {
......@@ -2038,7 +1910,6 @@ static const struct hnae3_ae_ops hclgevf_ops = {
.rm_uc_addr = hclgevf_rm_uc_addr,
.add_mc_addr = hclgevf_add_mc_addr,
.rm_mc_addr = hclgevf_rm_mc_addr,
.update_mta_status = hclgevf_update_mta_status,
.get_stats = hclgevf_get_stats,
.update_stats = hclgevf_update_stats,
.get_strings = hclgevf_get_strings,
......
......@@ -47,9 +47,6 @@
#define HCLGEVF_RSS_CFG_TBL_NUM \
(HCLGEVF_RSS_IND_TBL_SIZE / HCLGEVF_RSS_CFG_TBL_SIZE)
#define HCLGEVF_MTA_TBL_SIZE 4096
#define HCLGEVF_MTA_TYPE_SEL_MAX 4
/* states of hclgevf device & tasks */
enum hclgevf_states {
/* device states */
......@@ -157,8 +154,6 @@ struct hclgevf_dev {
u16 *vector_status;
int *vector_irq;
bool accept_mta_mc; /* whether to accept mta filter multicast */
u8 mta_mac_sel_type;
bool mbx_event_pending;
struct hclgevf_mbx_resp_status mbx_resp; /* mailbox response */
struct hclgevf_mbx_arq_ring arq; /* mailbox async rx queue */
......
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