Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3ac64bee authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville
Browse files

mac80211: allow configure_filter callback to sleep



Over time, a whole bunch of drivers have come up
with their own scheme to delay the configure_filter
operation to a workqueue. To be able to simplify
things, allow configure_filter to sleep, and add
a new prepare_multicast callback that drivers that
need the multicast address list implement. This new
callback must be atomic, but most drivers either
don't care or just calculate a hash which can be
done atomically and then uploaded to the hardware
non-atomically.

A cursory look suggests that at76c50x-usb, ar9170,
mwl8k (which is actually very broken now), rt2x00,
wl1251, wl1271 and zd1211 should make use of this
new capability.

Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent ea416a79
Loading
Loading
Loading
Loading
+28 −14
Original line number Diff line number Diff line
@@ -1328,16 +1328,39 @@ static void adm8211_bss_info_changed(struct ieee80211_hw *dev,
	}
}

static u64 adm8211_prepare_multicast(struct ieee80211_hw *hw,
				     int mc_count, struct dev_addr_list *mclist)
{
	unsigned int bit_nr, i;
	u32 mc_filter[2];

	mc_filter[1] = mc_filter[0] = 0;

	for (i = 0; i < mc_count; i++) {
		if (!mclist)
			break;
		bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26;

		bit_nr &= 0x3F;
		mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
		mclist = mclist->next;
	}

	return mc_filter[0] | ((u64)(mc_filter[1]) << 32);
}

static void adm8211_configure_filter(struct ieee80211_hw *dev,
				     unsigned int changed_flags,
				     unsigned int *total_flags,
				     int mc_count, struct dev_mc_list *mclist)
				     u64 multicast)
{
	static const u8 bcast[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
	struct adm8211_priv *priv = dev->priv;
	unsigned int bit_nr, new_flags;
	unsigned int new_flags;
	u32 mc_filter[2];
	int i;

	mc_filter[0] = multicast;
	mc_filter[1] = multicast >> 32;

	new_flags = 0;

@@ -1346,23 +1369,13 @@ static void adm8211_configure_filter(struct ieee80211_hw *dev,
		priv->nar |= ADM8211_NAR_PR;
		priv->nar &= ~ADM8211_NAR_MM;
		mc_filter[1] = mc_filter[0] = ~0;
	} else if ((*total_flags & FIF_ALLMULTI) || (mc_count > 32)) {
	} else if (*total_flags & FIF_ALLMULTI || multicast == ~(0ULL)) {
		new_flags |= FIF_ALLMULTI;
		priv->nar &= ~ADM8211_NAR_PR;
		priv->nar |= ADM8211_NAR_MM;
		mc_filter[1] = mc_filter[0] = ~0;
	} else {
		priv->nar &= ~(ADM8211_NAR_MM | ADM8211_NAR_PR);
		mc_filter[1] = mc_filter[0] = 0;
		for (i = 0; i < mc_count; i++) {
			if (!mclist)
				break;
			bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26;

			bit_nr &= 0x3F;
			mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
			mclist = mclist->next;
		}
	}

	ADM8211_IDLE_RX();
@@ -1757,6 +1770,7 @@ static const struct ieee80211_ops adm8211_ops = {
	.remove_interface	= adm8211_remove_interface,
	.config			= adm8211_config,
	.bss_info_changed	= adm8211_bss_info_changed,
	.prepare_multicast	= adm8211_prepare_multicast,
	.configure_filter	= adm8211_configure_filter,
	.get_stats		= adm8211_get_stats,
	.get_tx_stats		= adm8211_get_tx_stats,
+3 −4
Original line number Diff line number Diff line
@@ -1997,15 +1997,14 @@ static void at76_bss_info_changed(struct ieee80211_hw *hw,
/* must be atomic */
static void at76_configure_filter(struct ieee80211_hw *hw,
				  unsigned int changed_flags,
				  unsigned int *total_flags, int mc_count,
				  struct dev_addr_list *mc_list)
				  unsigned int *total_flags, u64 multicast)
{
	struct at76_priv *priv = hw->priv;
	int flags;

	at76_dbg(DBG_MAC80211, "%s(): changed_flags=0x%08x "
		 "total_flags=0x%08x mc_count=%d",
		 __func__, changed_flags, *total_flags, mc_count);
		 "total_flags=0x%08x",
		 __func__, changed_flags, *total_flags);

	flags = changed_flags & AT76_SUPPORTED_FILTERS;
	*total_flags = AT76_SUPPORTED_FILTERS;
+25 −18
Original line number Diff line number Diff line
@@ -2100,10 +2100,29 @@ static void ar9170_set_filters(struct work_struct *work)
	mutex_unlock(&ar->mutex);
}

static u64 ar9170_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count,
				       struct dev_addr_list *mclist)
{
	u64 mchash;
	int i;

	/* always get broadcast frames */
	mchash = 1ULL << (0xff >> 2);

	for (i = 0; i < mc_count; i++) {
		if (WARN_ON(!mclist))
			break;
		mchash |= 1ULL << (mclist->dmi_addr[5] >> 2);
		mclist = mclist->next;
	}

	return mchash;
}

static void ar9170_op_configure_filter(struct ieee80211_hw *hw,
				       unsigned int changed_flags,
				       unsigned int *new_flags,
				       int mc_count, struct dev_mc_list *mclist)
				       u64 multicast)
{
	struct ar9170 *ar = hw->priv;

@@ -2116,24 +2135,11 @@ static void ar9170_op_configure_filter(struct ieee80211_hw *hw,
	 * then checking the error flags, later.
	 */

	if (changed_flags & FIF_ALLMULTI) {
		if (*new_flags & FIF_ALLMULTI) {
			ar->want_mc_hash = ~0ULL;
		} else {
			u64 mchash;
			int i;

			/* always get broadcast frames */
			mchash = 1ULL << (0xff >> 2);
	if (changed_flags & FIF_ALLMULTI && *new_flags & FIF_ALLMULTI)
			multicast = ~0ULL;

			for (i = 0; i < mc_count; i++) {
				if (WARN_ON(!mclist))
					break;
				mchash |= 1ULL << (mclist->dmi_addr[5] >> 2);
				mclist = mclist->next;
			}
		ar->want_mc_hash = mchash;
		}
	if (multicast != ar->want_mc_hash) {
		ar->want_mc_hash = multicast;
		set_bit(AR9170_FILTER_CHANGED_MULTICAST, &ar->filter_changed);
	}

@@ -2543,6 +2549,7 @@ static const struct ieee80211_ops ar9170_ops = {
	.add_interface		= ar9170_op_add_interface,
	.remove_interface	= ar9170_op_remove_interface,
	.config			= ar9170_op_config,
	.prepare_multicast	= ar9170_op_prepare_multicast,
	.configure_filter	= ar9170_op_configure_filter,
	.conf_tx		= ar9170_conf_tx,
	.bss_info_changed	= ar9170_op_bss_info_changed,
+39 −25
Original line number Diff line number Diff line
@@ -229,10 +229,12 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
static void ath5k_remove_interface(struct ieee80211_hw *hw,
		struct ieee80211_if_init_conf *conf);
static int ath5k_config(struct ieee80211_hw *hw, u32 changed);
static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw,
				   int mc_count, struct dev_addr_list *mc_list);
static void ath5k_configure_filter(struct ieee80211_hw *hw,
		unsigned int changed_flags,
		unsigned int *new_flags,
		int mc_count, struct dev_mc_list *mclist);
		u64 multicast);
static int ath5k_set_key(struct ieee80211_hw *hw,
		enum set_key_cmd cmd,
		struct ieee80211_vif *vif, struct ieee80211_sta *sta,
@@ -260,6 +262,7 @@ static const struct ieee80211_ops ath5k_hw_ops = {
	.add_interface 	= ath5k_add_interface,
	.remove_interface = ath5k_remove_interface,
	.config 	= ath5k_config,
	.prepare_multicast = ath5k_prepare_multicast,
	.configure_filter = ath5k_configure_filter,
	.set_key 	= ath5k_set_key,
	.get_stats 	= ath5k_get_stats,
@@ -2853,6 +2856,37 @@ ath5k_config(struct ieee80211_hw *hw, u32 changed)
	return ret;
}

static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw,
				   int mc_count, struct dev_addr_list *mclist)
{
	u32 mfilt[2], val;
	int i;
	u8 pos;

	mfilt[0] = 0;
	mfilt[1] = 1;

	for (i = 0; i < mc_count; i++) {
		if (!mclist)
			break;
		/* calculate XOR of eight 6-bit values */
		val = get_unaligned_le32(mclist->dmi_addr + 0);
		pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
		val = get_unaligned_le32(mclist->dmi_addr + 3);
		pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
		pos &= 0x3f;
		mfilt[pos / 32] |= (1 << (pos % 32));
		/* XXX: we might be able to just do this instead,
		* but not sure, needs testing, if we do use this we'd
		* neet to inform below to not reset the mcast */
		/* ath5k_hw_set_mcast_filterindex(ah,
		 *      mclist->dmi_addr[5]); */
		mclist = mclist->next;
	}

	return ((u64)(mfilt[1]) << 32) | mfilt[0];
}

#define SUPPORTED_FIF_FLAGS \
	FIF_PROMISC_IN_BSS |  FIF_ALLMULTI | FIF_FCSFAIL | \
	FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \
@@ -2878,16 +2912,14 @@ ath5k_config(struct ieee80211_hw *hw, u32 changed)
static void ath5k_configure_filter(struct ieee80211_hw *hw,
		unsigned int changed_flags,
		unsigned int *new_flags,
		int mc_count, struct dev_mc_list *mclist)
		u64 multicast)
{
	struct ath5k_softc *sc = hw->priv;
	struct ath5k_hw *ah = sc->ah;
	u32 mfilt[2], val, rfilt;
	u8 pos;
	int i;
	u32 mfilt[2], rfilt;

	mfilt[0] = 0;
	mfilt[1] = 0;
	mfilt[0] = multicast;
	mfilt[1] = multicast >> 32;

	/* Only deal with supported flags */
	changed_flags &= SUPPORTED_FIF_FLAGS;
@@ -2913,24 +2945,6 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
	if (*new_flags & FIF_ALLMULTI) {
		mfilt[0] =  ~0;
		mfilt[1] =  ~0;
	} else {
		for (i = 0; i < mc_count; i++) {
			if (!mclist)
				break;
			/* calculate XOR of eight 6-bit values */
			val = get_unaligned_le32(mclist->dmi_addr + 0);
			pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
			val = get_unaligned_le32(mclist->dmi_addr + 3);
			pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
			pos &= 0x3f;
			mfilt[pos / 32] |= (1 << (pos % 32));
			/* XXX: we might be able to just do this instead,
			* but not sure, needs testing, if we do use this we'd
			* neet to inform below to not reset the mcast */
			/* ath5k_hw_set_mcast_filterindex(ah,
			 *      mclist->dmi_addr[5]); */
			mclist = mclist->next;
		}
	}

	/* This is the best we can do */
+1 −2
Original line number Diff line number Diff line
@@ -2394,8 +2394,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
static void ath9k_configure_filter(struct ieee80211_hw *hw,
				   unsigned int changed_flags,
				   unsigned int *total_flags,
				   int mc_count,
				   struct dev_mc_list *mclist)
				   u64 multicast)
{
	struct ath_wiphy *aphy = hw->priv;
	struct ath_softc *sc = aphy->sc;
Loading