nvethernet: use L2 filter command to set MAC address

Issue:
1) Missing support for packet MC/BC packet duplication
2) Remove MAC UC and BC address on ether_close()

Fix:
1) Update OSI_CMD_L2_FILTER structure parameter to support
new algorithm.
2) MAC UC and BC address added to filter from ether_open()
and ether_resume() function.
3) Configure MAC register to support MC/BC packet
duplication

Bug 200711542
Bug 200711544
Bug 200713215

Change-Id: Ie589892de121e5873f5dfa9b9db9bf5c441ece71
Signed-off-by: Rakesh Goyal <rgoyal@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2519591
Reviewed-by: Bhadram Varka <vbhadram@nvidia.com>
This commit is contained in:
Rakesh Goyal
2021-03-23 15:03:53 +05:30
committed by Revanth Kumar Uppala
parent 90a4dce1fb
commit 32ed166339
2 changed files with 210 additions and 13 deletions

View File

@@ -1699,6 +1699,74 @@ static void power_ungate(struct ether_priv_data *pdata)
}
/**
* @brief function to set unicast/Broadcast MAC address filter
*
* Algorithm: algo to add Unicast MAC/Broadcast address in L2 filter register
*
* @param[in] pdata: Pointer to private data structure.
* @param[in] ioctl_data: OSI IOCTL data structure.
* @param[in] en_dis: enable(1)/disable(0) L2 filter.
* @param[in] uc_bc: MAC address(1)/ Broadcast address(0).
*
* @note Ethernet driver probe need to be completed successfully
* with ethernet network device created.
*
* @retval 0 on success
* @retval "negative value" on failure.
*/
static int ether_update_mac_addr_filter(struct ether_priv_data *pdata,
struct osi_ioctl *ioctl_data,
unsigned int en_dis,
unsigned int uc_bc)
{
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_dma_priv_data *osi_dma = pdata->osi_dma;
nveu32_t dma_channel = osi_dma->dma_chans[0];
unsigned char bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
if ((en_dis > OSI_ENABLE) || (uc_bc > ETHER_ADDRESS_MAC)) {
dev_err(pdata->dev,
"%s(): wrong argument en_dis=0x%01x, uc_bc=0x%01x\n",
__func__, en_dis, uc_bc);
return -1;
}
memset(&ioctl_data->l2_filter, 0x0, sizeof(struct osi_filter));
/* Set MAC address with DCS set to route all legacy Rx
* packets from RxQ0 to default DMA at index 0.
*/
ioctl_data->l2_filter.oper_mode = (OSI_OPER_EN_PERFECT |
OSI_OPER_DIS_PROMISC |
OSI_OPER_DIS_ALLMULTI);
if (en_dis == OSI_ENABLE) {
ioctl_data->l2_filter.oper_mode |= OSI_OPER_ADDR_UPDATE;
} else {
ioctl_data->l2_filter.oper_mode |= OSI_OPER_ADDR_DEL;
}
if (uc_bc == ETHER_ADDRESS_MAC) {
ioctl_data->l2_filter.index = ETHER_MAC_ADDRESS_INDEX;
memcpy(ioctl_data->l2_filter.mac_address, osi_core->mac_addr,
ETH_ALEN);
} else {
if (osi_dma->num_dma_chans > 1) {
dma_channel = osi_dma->dma_chans[1];
} else {
dma_channel = osi_dma->dma_chans[0];
}
ioctl_data->l2_filter.index = ETHER_BC_ADDRESS_INDEX;
memcpy(ioctl_data->l2_filter.mac_address, bc_addr, ETH_ALEN);
}
ioctl_data->l2_filter.dma_routing = OSI_ENABLE;
ioctl_data->l2_filter.dma_chan = dma_channel;
ioctl_data->l2_filter.addr_mask = OSI_AMASK_DISABLE;
ioctl_data->l2_filter.src_dest = OSI_DA_MATCH;
ioctl_data->cmd = OSI_CMD_L2_FILTER;
return osi_handle_ioctl(osi_core, ioctl_data);
}
/**
* @brief Call back to handle bring up of Ethernet interface
*
@@ -1807,6 +1875,20 @@ static int ether_open(struct net_device *dev)
goto err_hw_init;
}
ret = ether_update_mac_addr_filter(pdata, &ioctl_data, OSI_ENABLE,
ETHER_ADDRESS_MAC);
if (ret < 0) {
dev_err(pdata->dev, "failed to set MAC address\n");
goto err_hw_init;
}
ret = ether_update_mac_addr_filter(pdata, &ioctl_data, OSI_ENABLE,
ETHER_ADDRESS_BC);
if (ret < 0) {
dev_err(pdata->dev, "failed to set BC address\n");
goto err_hw_init;
}
/* DMA init */
ret = osi_hw_dma_init(pdata->osi_dma);
if (ret < 0) {
@@ -1936,6 +2018,63 @@ static inline void ether_reset_stats(struct ether_priv_data *pdata)
memset(&osi_dma->pkt_err_stats, 0U, sizeof(struct osi_pkt_err_stats));
}
/**
* @brief Delete L2 filters from HW register when interface is down
*
* Algorithm: This routine takes care of below
* - Remove MAC address filter
* - Remove BC address filter (remove DMA channel form DCS field)
* - Remove all L2 filters
*
* @param[in] pdata: Pointer to private data structure.
*
* @note MAC Interface need to be registered.
*/
static inline void ether_delete_l2_filter(struct ether_priv_data *pdata)
{
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_dma_priv_data *osi_dma = pdata->osi_dma;
struct osi_ioctl ioctl_data = {};
int ret, i;
memset(&ioctl_data.l2_filter, 0x0, sizeof(struct osi_filter));
ret = ether_update_mac_addr_filter(pdata, &ioctl_data, OSI_DISABLE,
ETHER_ADDRESS_MAC);
if (ret < 0) {
dev_err(pdata->dev, "issue in deleting MAC address\n");
}
memset(&ioctl_data.l2_filter, 0x0, sizeof(struct osi_filter));
ret = ether_update_mac_addr_filter(pdata, &ioctl_data, OSI_DISABLE,
ETHER_ADDRESS_BC);
if (ret < 0) {
dev_err(pdata->dev, "issue in deleting BC address\n");
}
/* Loop to delete l2 address list filters */
for (i = ETHER_MAC_ADDRESS_INDEX + 1; i <= pdata->last_filter_index;
i++) {
/* Reset the filter structure to avoid any old value */
memset(&ioctl_data.l2_filter, 0x0, sizeof(struct osi_filter));
ioctl_data.l2_filter.oper_mode = OSI_OPER_ADDR_DEL;
ioctl_data.l2_filter.index = i;
ioctl_data.l2_filter.dma_routing = OSI_ENABLE;
if (osi_dma->num_dma_chans > 1) {
ioctl_data.l2_filter.dma_chan = osi_dma->dma_chans[1];
} else {
ioctl_data.l2_filter.dma_chan = osi_dma->dma_chans[0];
}
ioctl_data.l2_filter.addr_mask = OSI_AMASK_DISABLE;
ioctl_data.l2_filter.src_dest = OSI_DA_MATCH;
ioctl_data.cmd = OSI_CMD_L2_FILTER;
ret = osi_handle_ioctl(osi_core, &ioctl_data);
if (ret < 0) {
dev_err(pdata->dev,
"failed to delete L2 filter index = %d\n", i);
}
}
}
/**
* @brief Call back to handle bring down of Ethernet interface
*
@@ -1953,7 +2092,7 @@ static inline void ether_reset_stats(struct ether_priv_data *pdata)
static int ether_close(struct net_device *ndev)
{
struct ether_priv_data *pdata = netdev_priv(ndev);
int ret = 0, i;
int i;
unsigned int chan = 0x0;
/* Unregister broadcasting MAC timestamp to clients */
@@ -1997,6 +2136,8 @@ static int ether_close(struct net_device *ndev)
}
}
ether_delete_l2_filter(pdata);
/* DMA De init */
osi_hw_dma_deinit(pdata->osi_dma);
@@ -2037,7 +2178,7 @@ static int ether_close(struct net_device *ndev)
/* Reset stats since interface is going down */
ether_reset_stats(pdata);
return ret;
return 0;
}
/**
@@ -2436,8 +2577,9 @@ static int ether_prepare_mc_list(struct net_device *dev,
{
struct ether_priv_data *pdata = netdev_priv(dev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_dma_priv_data *osi_dma = pdata->osi_dma;
struct netdev_hw_addr *ha;
unsigned int i = 1;
unsigned int i = ETHER_MAC_ADDRESS_INDEX + 1;
int ret = -1;
if (ioctl_data == NULL) {
@@ -2484,8 +2626,14 @@ static int ether_prepare_mc_list(struct net_device *dev,
ioctl_data->l2_filter.index = i;
memcpy(ioctl_data->l2_filter.mac_address, ha->addr,
ETH_ALEN);
ioctl_data->l2_filter.dma_routing = OSI_DISABLE;
ioctl_data->l2_filter.dma_chan = 0x0;
ioctl_data->l2_filter.dma_routing = OSI_ENABLE;
if (osi_dma->num_dma_chans > 1) {
ioctl_data->l2_filter.dma_chan =
osi_dma->dma_chans[1];
} else {
ioctl_data->l2_filter.dma_chan =
osi_dma->dma_chans[0];
}
ioctl_data->l2_filter.addr_mask = OSI_AMASK_DISABLE;
ioctl_data->l2_filter.src_dest = OSI_DA_MATCH;
ioctl_data->cmd = OSI_CMD_L2_FILTER;
@@ -2528,6 +2676,7 @@ static int ether_prepare_uc_list(struct net_device *dev,
{
struct ether_priv_data *pdata = netdev_priv(dev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_dma_priv_data *osi_dma = pdata->osi_dma;
/* last valid MC/MAC DA + 1 should be start of UC addresses */
unsigned int i = pdata->last_filter_index + 1;
struct netdev_hw_addr *ha;
@@ -2574,15 +2723,22 @@ static int ether_prepare_uc_list(struct net_device *dev,
ioctl_data->l2_filter.index = i;
memcpy(ioctl_data->l2_filter.mac_address, ha->addr,
ETH_ALEN);
ioctl_data->l2_filter.dma_routing = OSI_DISABLE;
ioctl_data->l2_filter.dma_chan = 0x0;
ioctl_data->l2_filter.dma_routing = OSI_ENABLE;
if (osi_dma->num_dma_chans > 1) {
ioctl_data->l2_filter.dma_chan =
osi_dma->dma_chans[1];
} else {
ioctl_data->l2_filter.dma_chan =
osi_dma->dma_chans[0];
}
ioctl_data->l2_filter.addr_mask = OSI_AMASK_DISABLE;
ioctl_data->l2_filter.src_dest = OSI_DA_MATCH;
ioctl_data->cmd = OSI_CMD_L2_FILTER;
ret = osi_handle_ioctl(osi_core, ioctl_data);
if (ret < 0) {
dev_err(pdata->dev, "issue in creating uc list\n");
dev_err(pdata->dev,
"issue in creating uc list\n");
pdata->last_filter_index = i - 1;
return ret;
}
@@ -2613,6 +2769,7 @@ void ether_set_rx_mode(struct net_device *dev)
{
struct ether_priv_data *pdata = netdev_priv(dev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_dma_priv_data *osi_dma = pdata->osi_dma;
/* store last call last_uc_filter_index in temporary variable */
int last_index = pdata->last_filter_index;
struct osi_ioctl ioctl_data = {};
@@ -2657,7 +2814,8 @@ void ether_set_rx_mode(struct net_device *dev)
dev_err(pdata->dev, "Setting MC address failed\n");
}
} else {
pdata->last_filter_index = 0;
/* start index after MAC and BC address index */
pdata->last_filter_index = ETHER_MAC_ADDRESS_INDEX;
}
if (!netdev_uc_empty(dev)) {
@@ -2666,14 +2824,18 @@ void ether_set_rx_mode(struct net_device *dev)
}
}
/* Reset the filter structure to avoid any old value */
memset(&ioctl_data.l2_filter, 0x0, sizeof(struct osi_filter));
/* invalidate remaining ealier address */
for (i = pdata->last_filter_index + 1; i <= last_index; i++) {
/* Reset the filter structure to avoid any old value */
memset(&ioctl_data.l2_filter, 0x0, sizeof(struct osi_filter));
ioctl_data.l2_filter.oper_mode = OSI_OPER_ADDR_DEL;
ioctl_data.l2_filter.index = i;
ioctl_data.l2_filter.dma_routing = OSI_DISABLE;
ioctl_data.l2_filter.dma_chan = OSI_CHAN_ANY;
ioctl_data.l2_filter.dma_routing = OSI_ENABLE;
if (osi_dma->num_dma_chans > 1) {
ioctl_data.l2_filter.dma_chan = osi_dma->dma_chans[1];
} else {
ioctl_data.l2_filter.dma_chan = osi_dma->dma_chans[0];
}
ioctl_data.l2_filter.addr_mask = OSI_AMASK_DISABLE;
ioctl_data.l2_filter.src_dest = OSI_DA_MATCH;
ioctl_data.cmd = OSI_CMD_L2_FILTER;
@@ -4875,6 +5037,7 @@ static int ether_suspend_noirq(struct device *dev)
struct osi_dma_priv_data *osi_dma = pdata->osi_dma;
struct osi_ioctl ioctl_data = {};
unsigned int i = 0, chan = 0;
int ret;
if (!netif_running(ndev))
return 0;
@@ -4901,6 +5064,18 @@ static int ether_suspend_noirq(struct device *dev)
netif_tx_disable(ndev);
ether_napi_disable(pdata);
ret = ether_update_mac_addr_filter(pdata, &ioctl_data, OSI_DISABLE,
ETHER_ADDRESS_MAC);
if (ret < 0) {
dev_err(pdata->dev, "issue in deleting MAC address\n");
}
ret = ether_update_mac_addr_filter(pdata, &ioctl_data, OSI_DISABLE,
ETHER_ADDRESS_BC);
if (ret < 0) {
dev_err(pdata->dev, "issue in deleting BC address\n");
}
osi_hw_dma_deinit(osi_dma);
osi_hw_core_deinit(osi_core);
@@ -4984,6 +5159,20 @@ static int ether_resume(struct ether_priv_data *pdata)
goto err_core;
}
ret = ether_update_mac_addr_filter(pdata, &ioctl_data, OSI_ENABLE,
ETHER_ADDRESS_MAC);
if (ret < 0) {
dev_err(pdata->dev, "failed to set MAC address\n");
goto err_dma;
}
ret = ether_update_mac_addr_filter(pdata, &ioctl_data, OSI_ENABLE,
ETHER_ADDRESS_BC);
if (ret < 0) {
dev_err(pdata->dev, "failed to set BC address\n");
goto err_dma;
}
/* dma init */
ret = osi_hw_dma_init(osi_dma);
if (ret < 0) {