[SRU][saucy][PATCH] net: calxedaxgmac: add mac address learning

Tim Gardner tim.gardner at canonical.com
Tue Nov 19 14:42:07 UTC 2013


On 11/15/2013 02:32 PM, Rob Herring wrote:
> On 11/15/2013 01:54 PM, Tim Gardner wrote:
>> More comments inline.
>>
>> On 11/14/2013 02:01 PM, Rob Herring wrote:
>>> From: Rob Herring <rob.herring at calxeda.com>
>>>
>>> The Calxeda xgmac must learn secondary mac addresses such as those
>>> behind a bridge in order to properly receive packets with those mac
>>> addresses. Add mac address learning on transmit with timestamping of
>>> entries. The mac addresses are added to the driver's unicast address
>>> list, so they are visible to user via "bridge fdb show" command.
>>>
>>> Addresses can get disabled when the AE bit is cleared, so address
>>> registers must be checked for possible removal and update the unicast
>>> list.
>>>
>>> Signed-off-by: Rob Herring <rob.herring at calxeda.com>
>>> ---
>>>  drivers/net/ethernet/calxeda/xgmac.c | 128 ++++++++++++++++++++++++++++++++++-
>>>  1 file changed, 127 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c
>>> index 48f5288..584aca8 100644
>>> --- a/drivers/net/ethernet/calxeda/xgmac.c
>>> +++ b/drivers/net/ethernet/calxeda/xgmac.c
>>> @@ -360,6 +360,13 @@ struct xgmac_extra_stats {
>>>  	unsigned long rx_process_stopped;
>>>  	unsigned long tx_early;
>>>  	unsigned long fatal_bus_error;
>>> +	unsigned long learning_drop;
>>> +};
>>> +
>>> +struct xgmac_mac {
>>> +	char mac_addr[ETH_ALEN];
>>> +	unsigned long time;
>>> +	bool rm_pending;
>>>  };
>>>  
>>>  struct xgmac_priv {
>>> @@ -384,6 +391,8 @@ struct xgmac_priv {
>>>  	struct napi_struct napi;
>>>  
>>>  	int max_macs;
>>> +	struct xgmac_mac mac_list[32];
>>> +
>>>  	struct xgmac_extra_stats xstats;
>>>  
>>>  	spinlock_t stats_lock;
>>> @@ -392,6 +401,7 @@ struct xgmac_priv {
>>>  	char tx_pause;
>>>  	int wolopts;
>>>  	struct work_struct tx_timeout_work;
>>> +	struct delayed_work mac_aging_work;
>>>  };
>>>  
>>>  /* XGMAC Configuration Settings */
>>> @@ -634,7 +644,7 @@ static void xgmac_set_mac_addr(void __iomem *ioaddr, unsigned char *addr,
>>>  	}
>>>  }
>>>  
>>> -static void xgmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr,
>>> +static bool xgmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr,
>>>  			       int num)
>>>  {
>>>  	u32 hi_addr, lo_addr;
>>> @@ -650,6 +660,8 @@ static void xgmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr,
>>>  	addr[3] = (lo_addr >> 24) & 0xff;
>>>  	addr[4] = hi_addr & 0xff;
>>>  	addr[5] = (hi_addr >> 8) & 0xff;
>>> +
>>> +	return (hi_addr & XGMAC_ADDR_AE) ? true : false;
>>>  }
>>>  
>>>  static int xgmac_set_flow_ctrl(struct xgmac_priv *priv, int rx, int tx)
>>> @@ -1047,6 +1059,8 @@ static int xgmac_open(struct net_device *dev)
>>>  	writel(DMA_INTR_DEFAULT_MASK, ioaddr + XGMAC_DMA_STATUS);
>>>  	writel(DMA_INTR_DEFAULT_MASK, ioaddr + XGMAC_DMA_INTR_ENA);
>>>  
>>> +	schedule_delayed_work(&priv->mac_aging_work, 5 * HZ);
>>> +
>>>  	return 0;
>>>  }
>>>  
>>> @@ -1059,6 +1073,7 @@ static int xgmac_open(struct net_device *dev)
>>>  static int xgmac_stop(struct net_device *dev)
>>>  {
>>>  	struct xgmac_priv *priv = netdev_priv(dev);
>>> +	int i;
>>>  
>>>  	netif_stop_queue(dev);
>>>  
>>> @@ -1073,9 +1088,111 @@ static int xgmac_stop(struct net_device *dev)
>>>  	/* Release and free the Rx/Tx resources */
>>>  	xgmac_free_dma_desc_rings(priv);
>>>  
>>> +	cancel_delayed_work_sync(&priv->mac_aging_work);
>>> +	for (i = 0; i < priv->max_macs; i++) {
>>> +		if (!is_valid_ether_addr(priv->mac_list[i].mac_addr))
>>> +			continue;
>>> +		dev_uc_del(dev, priv->mac_list[i].mac_addr);
>>> +		memset(&priv->mac_list[i], 0, sizeof(priv->mac_list[i]));
>>> +	}
>>> +
>>>  	return 0;
>>>  }
>>>  
>>> +static void xgmac_check_mac_addrs(struct xgmac_priv *priv)
>>> +{
>>> +	int i, j;
>>> +	char addr[ETH_ALEN];
>>> +
>>> +	for (i = 1; i < priv->max_macs; i++) {
>>> +		if (xgmac_get_mac_addr(priv->base, addr, i))
>>> +			continue;
>>> +
>>> +		if (!is_valid_ether_addr(addr))
>>> +			break;
>>> +
>>> +		for (j = 1; j < priv->max_macs; j++) {
>>> +			if (!ether_addr_equal(addr, priv->mac_list[j].mac_addr))
>>> +				continue;
>>> +			priv->mac_list[j].rm_pending = true;
>>> +			break;
>>> +		}
>>> +	}
>>> +}
>>
>> Shouldn't there be a test for how old a MAC address is before you mark
>> it for removal ? Otherwise every address in priv->mac_list that is also
>> in the HW table gets marked for removal each time this function is
>> called. Are entries getting removed from the HW table somewhere else ?
> 
> Yes. There is a valid bit which can be cleared by the firmware and we
> have to check this before changing the MAC registers. This would only
> happen if the same address shows up on another node which is the case
> for VM migration (which isn't really fully baked on arm yet).
> 
>> xgmac_check_mac_addrs() is also called from xgmac_set_rx_mode(). I just
>> can't see how that is going to work when addition of an address to the
>> HW table is predicated on xgmac_mac_rm_pending() being false.
> 
> The flow here is a bit weird because I can't call dev_uc_del within
> xgmac_set_rx_mode as the list lock is already held. So we have this
> intermediate state where the address needs to be removed but is still in
> the unicast list. The check is simply to avoid re-adding an address.
> 
> Rob
> 

Well, I still think this doesn't do you intend. How about adding some
debug so we can see when these MAC addresses come and go ? If debug is
flooding the log then we'll know something is wrong 'cause it should
only happen when you migrate a VM.

I wonder if there is already a general way of viewing MAC addresses that
have been entered into the dev MAC table via  dev_uc_add()/dev_uc_del() ?

rtg
-- 
Tim Gardner tim.gardner at canonical.com




More information about the kernel-team mailing list