[PATCH] libertas: fix multicast filtering on eth and msh interfaces.

Dan Williams dcbw at redhat.com
Mon May 19 11:53:21 EDT 2008


On Mon, 2008-05-19 at 15:55 +0100, David Woodhouse wrote:
> We weren't properly handling multicast on the mesh interface. Fix that,
> which involves setting up the hardware to use the union of dev->mc_list
> for both eth%d and msh%d devices.
> 
> This means we can't do it directly from ->set_multicast_list() because
> we'd need to lock the other device to read its list, and we can't do
> that because it might deadlock. So punt the actual work to keventd. 
> 
> Also, invoke the same when taking an interface down; for some reason the
> core calls ->set_multicast_list while IFF_UP is still set in dev->flags
> when we're taking it down, so its addresses don't get removed then.
> 
> We also convert MAC_MULTICAST_ADR to a direct command while we're at it,
> removing one more entry from the big switch statement in the deprecated
> lbs_prepare_and_send_command() function. Change the priority of the
> 'unknown command' warnings in that switch statement too, because we
> really do want to know about it if it happens.
> 
> Signed-off-by: David Woodhouse <dwmw2 at infradead.org>

Looks OK to me.  Nothing obvious.

Acked-by: Dan Williams <dcbw at redhat.com>

> diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
> index 6328b95..2473ba8 100644
> --- a/drivers/net/wireless/libertas/cmd.c
> +++ b/drivers/net/wireless/libertas/cmd.c
> @@ -778,28 +778,6 @@ out:
>  	return ret;
>  }
>  
> -static int lbs_cmd_mac_multicast_adr(struct lbs_private *priv,
> -				      struct cmd_ds_command *cmd,
> -				      u16 cmd_action)
> -{
> -	struct cmd_ds_mac_multicast_adr *pMCastAdr = &cmd->params.madr;
> -
> -	lbs_deb_enter(LBS_DEB_CMD);
> -	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_mac_multicast_adr) +
> -			     S_DS_GEN);
> -	cmd->command = cpu_to_le16(CMD_MAC_MULTICAST_ADR);
> -
> -	lbs_deb_cmd("MULTICAST_ADR: setting %d addresses\n", pMCastAdr->nr_of_adrs);
> -	pMCastAdr->action = cpu_to_le16(cmd_action);
> -	pMCastAdr->nr_of_adrs =
> -	    cpu_to_le16((u16) priv->nr_of_multicastmacaddr);
> -	memcpy(pMCastAdr->maclist, priv->multicastlist,
> -	       priv->nr_of_multicastmacaddr * ETH_ALEN);
> -
> -	lbs_deb_leave(LBS_DEB_CMD);
> -	return 0;
> -}
> -
>  /**
>   *  @brief Get the radio channel
>   *
> @@ -1279,8 +1257,7 @@ void lbs_set_mac_control(struct lbs_private *priv)
>  	cmd.action = cpu_to_le16(priv->mac_control);
>  	cmd.reserved = 0;
>  
> -	lbs_cmd_async(priv, CMD_MAC_CONTROL,
> -		&cmd.hdr, sizeof(cmd));
> +	lbs_cmd_async(priv, CMD_MAC_CONTROL, &cmd.hdr, sizeof(cmd));
>  
>  	lbs_deb_leave(LBS_DEB_CMD);
>  }
> @@ -1392,10 +1369,6 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
>  							 cmdptr, cmd_action);
>  		break;
>  
> -	case CMD_MAC_MULTICAST_ADR:
> -		ret = lbs_cmd_mac_multicast_adr(priv, cmdptr, cmd_action);
> -		break;
> -
>  	case CMD_802_11_MONITOR_MODE:
>  		ret = lbs_cmd_802_11_monitor_mode(cmdptr,
>  				          cmd_action, pdata_buf);
> @@ -1484,7 +1457,7 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
>  		ret = lbs_cmd_bcn_ctrl(priv, cmdptr, cmd_action);
>  		break;
>  	default:
> -		lbs_deb_host("PREP_CMD: unknown command 0x%04x\n", cmd_no);
> +		lbs_pr_err("PREP_CMD: unknown command 0x%04x\n", cmd_no);
>  		ret = -1;
>  		break;
>  	}
> diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c
> index 5abecb7..4c3c5ec 100644
> --- a/drivers/net/wireless/libertas/cmdresp.c
> +++ b/drivers/net/wireless/libertas/cmdresp.c
> @@ -316,7 +316,6 @@ static inline int handle_cmd_response(struct lbs_private *priv,
>  
>  		break;
>  
> -	case CMD_RET(CMD_MAC_MULTICAST_ADR):
>  	case CMD_RET(CMD_802_11_RESET):
>  	case CMD_RET(CMD_802_11_AUTHENTICATE):
>  	case CMD_RET(CMD_802_11_BEACON_STOP):
> @@ -376,8 +375,8 @@ static inline int handle_cmd_response(struct lbs_private *priv,
>  		break;
>  
>  	default:
> -		lbs_deb_host("CMD_RESP: unknown cmd response 0x%04x\n",
> -			     le16_to_cpu(resp->command));
> +		lbs_pr_err("CMD_RESP: unknown cmd response 0x%04x\n",
> +			   le16_to_cpu(resp->command));
>  		break;
>  	}
>  	lbs_deb_leave(LBS_DEB_HOST);
> diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
> index 0d9edb9..e12ce65 100644
> --- a/drivers/net/wireless/libertas/dev.h
> +++ b/drivers/net/wireless/libertas/dev.h
> @@ -140,6 +140,8 @@ struct lbs_private {
>  	wait_queue_head_t waitq;
>  	struct workqueue_struct *work_thread;
>  
> +	struct work_struct mcast_work;
> +
>  	/** Scanning */
>  	struct delayed_work scan_work;
>  	struct delayed_work assoc_work;
> diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h
> index f29bc5b..c36ab31 100644
> --- a/drivers/net/wireless/libertas/hostcmd.h
> +++ b/drivers/net/wireless/libertas/hostcmd.h
> @@ -219,6 +219,7 @@ struct cmd_ds_mac_control {
>  };
>  
>  struct cmd_ds_mac_multicast_adr {
> +	struct cmd_header hdr;
>  	__le16 action;
>  	__le16 nr_of_adrs;
>  	u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE];
> @@ -703,7 +704,6 @@ struct cmd_ds_command {
>  		struct cmd_ds_802_11_rf_antenna rant;
>  		struct cmd_ds_802_11_monitor_mode monitor;
>  		struct cmd_ds_802_11_rate_adapt_rateset rateset;
> -		struct cmd_ds_mac_multicast_adr madr;
>  		struct cmd_ds_802_11_ad_hoc_join adj;
>  		struct cmd_ds_802_11_rssi rssi;
>  		struct cmd_ds_802_11_rssi_rsp rssirsp;
> diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
> index 406f54d..80b35da 100644
> --- a/drivers/net/wireless/libertas/main.c
> +++ b/drivers/net/wireless/libertas/main.c
> @@ -11,6 +11,7 @@
>  #include <linux/if_arp.h>
>  #include <linux/kthread.h>
>  #include <linux/kfifo.h>
> +#include <linux/stddef.h>
>  
>  #include <net/iw_handler.h>
>  #include <net/ieee80211.h>
> @@ -425,6 +426,7 @@ static int lbs_dev_open(struct net_device *dev)
>  	return ret;
>  }
>  
> +static void lbs_set_multicast_list(struct net_device *dev);
>  /**
>   *  @brief This function closes the mshX interface
>   *
> @@ -446,6 +448,8 @@ static int lbs_mesh_stop(struct net_device *dev)
>  
>  	spin_unlock_irq(&priv->driver_lock);
>  
> +	schedule_work(&priv->mcast_work);
> +
>  	lbs_deb_leave(LBS_DEB_MESH);
>  	return 0;
>  }
> @@ -467,6 +471,8 @@ static int lbs_eth_stop(struct net_device *dev)
>  	netif_stop_queue(dev);
>  	spin_unlock_irq(&priv->driver_lock);
>  
> +	schedule_work(&priv->mcast_work);
> +
>  	lbs_deb_leave(LBS_DEB_NET);
>  	return 0;
>  }
> @@ -563,89 +569,115 @@ done:
>  	return ret;
>  }
>  
> -static int lbs_copy_multicast_address(struct lbs_private *priv,
> -				     struct net_device *dev)
> -{
> -	int i = 0;
> -	struct dev_mc_list *mcptr = dev->mc_list;
>  
> -	for (i = 0; i < dev->mc_count; i++) {
> -		memcpy(&priv->multicastlist[i], mcptr->dmi_addr, ETH_ALEN);
> -		mcptr = mcptr->next;
> +static inline int mac_in_list(unsigned char *list, int list_len,
> +			      unsigned char *mac)
> +{
> +	while (list_len) {
> +		if (!memcmp(list, mac, ETH_ALEN))
> +			return 1;
> +		list += ETH_ALEN;
> +		list_len--;
>  	}
> -	return i;
> +	return 0;
>  }
>  
> -static void lbs_set_multicast_list(struct net_device *dev)
> +static void lbs_set_mcast_worker(struct work_struct *work)
>  {
> -	struct lbs_private *priv = dev->priv;
> -	int old_mac_control;
> +	struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work);
> +	struct cmd_ds_mac_multicast_adr mcast_cmd;
> +	struct net_device *dev;
> +	struct dev_mc_list *mc_list;
>  	DECLARE_MAC_BUF(mac);
> +	int dev_flags;
> +	int i, nr_eth_addrs;
> +	int old_mac_control = priv->mac_control;
>  
> -	lbs_deb_enter(LBS_DEB_NET);
> +	lbs_deb_enter(LBS_DEB_MAIN);
>  
> -	old_mac_control = priv->mac_control;
> +	dev_flags = dev_get_flags(priv->dev);
> +	if (priv->mesh_dev)
> +		dev_flags |= dev_get_flags(priv->mesh_dev);
> +
> +	if (dev_flags & IFF_PROMISC) {
> +		priv->mac_control |= CMD_ACT_MAC_PROMISCUOUS_ENABLE;
> +		priv->mac_control &= ~(CMD_ACT_MAC_ALL_MULTICAST_ENABLE |
> +				       CMD_ACT_MAC_MULTICAST_ENABLE);
> +		goto out_set_mac_control;
> +	} else if (dev_flags & IFF_ALLMULTI) {
> +	do_allmulti:
> +		priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
> +		priv->mac_control &= ~(CMD_ACT_MAC_PROMISCUOUS_ENABLE |
> +				       CMD_ACT_MAC_MULTICAST_ENABLE);
> +		goto out_set_mac_control;
> +	}
>  
> -	if (dev->flags & IFF_PROMISC) {
> -		lbs_deb_net("enable promiscuous mode\n");
> -		priv->mac_control |=
> -		    CMD_ACT_MAC_PROMISCUOUS_ENABLE;
> -		priv->mac_control &=
> -		    ~(CMD_ACT_MAC_ALL_MULTICAST_ENABLE |
> -		      CMD_ACT_MAC_MULTICAST_ENABLE);
> -	} else {
> -		/* Multicast */
> -		priv->mac_control &=
> -		    ~CMD_ACT_MAC_PROMISCUOUS_ENABLE;
> -
> -		if (dev->flags & IFF_ALLMULTI || dev->mc_count >
> -		    MRVDRV_MAX_MULTICAST_LIST_SIZE) {
> -			lbs_deb_net( "enabling all multicast\n");
> -			priv->mac_control |=
> -			    CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
> -			priv->mac_control &=
> -			    ~CMD_ACT_MAC_MULTICAST_ENABLE;
> -		} else {
> -			priv->mac_control &=
> -			    ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
> -
> -			if (!dev->mc_count) {
> -				lbs_deb_net("no multicast addresses, "
> -				       "disabling multicast\n");
> -				priv->mac_control &=
> -				    ~CMD_ACT_MAC_MULTICAST_ENABLE;
> -			} else {
> -				int i;
> -
> -				priv->mac_control |=
> -				    CMD_ACT_MAC_MULTICAST_ENABLE;
> -
> -				priv->nr_of_multicastmacaddr =
> -				    lbs_copy_multicast_address(priv, dev);
> -
> -				lbs_deb_net("multicast addresses: %d\n",
> -				       dev->mc_count);
> -
> -				for (i = 0; i < dev->mc_count; i++) {
> -					lbs_deb_net("Multicast address %d: %s\n",
> -					       i, print_mac(mac,
> -					       priv->multicastlist[i]));
> -				}
> -				/* send multicast addresses to firmware */
> -				lbs_prepare_and_send_command(priv,
> -						      CMD_MAC_MULTICAST_ADR,
> -						      CMD_ACT_SET, 0, 0,
> -						      NULL);
> +	i = 0;
> +	nr_eth_addrs = 0;
> +
> +	/* Once for priv->dev, again for priv->mesh_dev if it exists */
> +	for (dev = priv->dev; dev;
> +	     dev = (dev==priv->mesh_dev?NULL:priv->mesh_dev)) {
> +		if ((dev_get_flags(dev) & (IFF_UP|IFF_MULTICAST)) != 
> +		    (IFF_UP|IFF_MULTICAST))
> +			continue;
> +
> +		netif_tx_lock_bh(dev);		
> +		for (mc_list = dev->mc_list; mc_list; mc_list = mc_list->next) {
> +
> +			if (mac_in_list(mcast_cmd.maclist, nr_eth_addrs,
> +					mc_list->dmi_addr)) {
> +				lbs_deb_net("mcast address %s:%s skipped\n",
> +					    dev->name,
> +					    print_mac(mac, mc_list->dmi_addr));
> +				continue;
>  			}
> +
> +			if (i == MRVDRV_MAX_MULTICAST_LIST_SIZE)
> +				break;
> +			memcpy(&mcast_cmd.maclist[6*i], mc_list->dmi_addr,
> +			       ETH_ALEN);
> +			lbs_deb_net("mcast address %s:%s added to filter\n",
> +				    dev->name,
> +				    print_mac(mac, mc_list->dmi_addr));
> +			i++;
>  		}
> +		netif_tx_unlock_bh(dev);
> +		if (mc_list)
> +			goto do_allmulti;
> +
> +		nr_eth_addrs = i;
>  	}
>  
> +	if (i) {
> +		int size = offsetof(struct cmd_ds_mac_multicast_adr, maclist[6*i]);
> +		
> +		mcast_cmd.action = cpu_to_le16(CMD_ACT_SET);
> +		mcast_cmd.hdr.size = cpu_to_le16(size);
> +		mcast_cmd.nr_of_adrs = cpu_to_le16(i);
> +
> +		lbs_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &mcast_cmd.hdr, size);
> +
> +		priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE;
> +	} else
> +		priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE;
> +
> +	priv->mac_control &= ~(CMD_ACT_MAC_PROMISCUOUS_ENABLE |
> +			       CMD_ACT_MAC_ALL_MULTICAST_ENABLE);
> + out_set_mac_control:
>  	if (priv->mac_control != old_mac_control)
>  		lbs_set_mac_control(priv);
>  
>  	lbs_deb_leave(LBS_DEB_NET);
>  }
>  
> +static void lbs_set_multicast_list(struct net_device *dev)
> +{
> +	struct lbs_private *priv = dev->priv;
> +
> +	schedule_work(&priv->mcast_work);
> +}
> +
>  /**
>   *  @brief This function handles the major jobs in the LBS driver.
>   *  It handles all events generated by firmware, RX data received
> @@ -1133,6 +1165,7 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev)
>  	priv->work_thread = create_singlethread_workqueue("lbs_worker");
>  	INIT_DELAYED_WORK(&priv->assoc_work, lbs_association_worker);
>  	INIT_DELAYED_WORK(&priv->scan_work, lbs_scan_worker);
> +	INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker);
>  	INIT_WORK(&priv->sync_channel, lbs_sync_channel_worker);
>  
>  	sprintf(priv->mesh_ssid, "mesh");
> @@ -1169,6 +1202,7 @@ int lbs_remove_card(struct lbs_private *priv)
>  
>  	cancel_delayed_work(&priv->scan_work);
>  	cancel_delayed_work(&priv->assoc_work);
> +	cancel_work_sync(&priv->mcast_work);
>  	destroy_workqueue(priv->work_thread);
>  
>  	if (priv->psmode == LBS802_11POWERMODEMAX_PSP) {
> @@ -1331,6 +1365,8 @@ static int lbs_add_mesh(struct lbs_private *priv)
>  #ifdef	WIRELESS_EXT
>  	mesh_dev->wireless_handlers = (struct iw_handler_def *)&mesh_handler_def;
>  #endif
> +	mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
> +	mesh_dev->set_multicast_list = lbs_set_multicast_list;
>  	/* Register virtual mesh interface */
>  	ret = register_netdev(mesh_dev);
>  	if (ret) {
> @@ -1562,7 +1598,6 @@ static int lbs_add_rtap(struct lbs_private *priv)
>  	rtap_dev->stop = lbs_rtap_stop;
>  	rtap_dev->get_stats = lbs_rtap_get_stats;
>  	rtap_dev->hard_start_xmit = lbs_rtap_hard_start_xmit;
> -	rtap_dev->set_multicast_list = lbs_set_multicast_list;
>  	rtap_dev->priv = priv;
>  
>  	ret = register_netdev(rtap_dev);
> 




More information about the libertas-dev mailing list