Search Linux Wireless

[RFC 5/6] wifi: mac80211: support per-radio filter flags

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This allows drivers to improve filtering of unwanted packets when using
different combinations of filter/monitor settings on radios.

Signed-off-by: Felix Fietkau <nbd@xxxxxxxx>
---
 include/net/mac80211.h     |  8 +++-
 net/mac80211/driver-ops.h  | 18 +++++++-
 net/mac80211/ieee80211_i.h | 21 ++++++---
 net/mac80211/iface.c       | 95 ++++++++++++++++++++++++---------------
 net/mac80211/main.c        | 60 +++++++++++++++++--------
 net/mac80211/mesh.c        | 33 ++++++++++----
 net/mac80211/trace.h       | 28 +++++++++++-
 7 files changed, 196 insertions(+), 67 deletions(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index d0dcd66e565e..7a5418713dfc 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -3921,6 +3921,10 @@ struct ieee80211_prep_tx_info {
  *	See the section "Frame filtering" for more information.
  *	This callback must be implemented and can sleep.
  *
+ * @config_radio_filter: Configure the radio's RX filter.
+ *	See the section "Frame filtering" for more information.
+ *	This callback is optional and can sleep.
+ *
  * @config_iface_filter: Configure the interface's RX filter.
  *	This callback is optional and is used to configure which frames
  *	should be passed to mac80211. The filter_flags is the combination
@@ -4489,6 +4493,10 @@ struct ieee80211_ops {
 				 unsigned int changed_flags,
 				 unsigned int *total_flags,
 				 u64 multicast);
+	void (*config_radio_filter)(struct ieee80211_hw *hw,
+				    unsigned int radio_idx,
+				    unsigned int changed_flags,
+				    unsigned int *total_flags);
 	void (*config_iface_filter)(struct ieee80211_hw *hw,
 				    struct ieee80211_vif *vif,
 				    unsigned int filter_flags,
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index cedc12b98bbb..4ee1d78b91c1 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -242,6 +242,24 @@ static inline void drv_configure_filter(struct ieee80211_local *local,
 	trace_drv_return_void(local);
 }
 
+static inline void drv_config_radio_filter(struct ieee80211_local *local,
+					   unsigned int radio_idx,
+					   unsigned int changed_flags,
+					   unsigned int *total_flags)
+{
+	might_sleep();
+	lockdep_assert_wiphy(local->hw.wiphy);
+
+	if (!local->ops->config_radio_filter)
+	    return;
+
+	trace_drv_config_radio_filter(local, radio_idx, changed_flags,
+				      total_flags);
+	local->ops->config_radio_filter(&local->hw, radio_idx, changed_flags,
+					total_flags);
+	trace_drv_return_void(local);
+}
+
 static inline void drv_config_iface_filter(struct ieee80211_local *local,
 					   struct ieee80211_sub_if_data *sdata,
 					   unsigned int filter_flags,
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index acc1a2d0f30f..55bb328b9c70 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1330,7 +1330,19 @@ enum mac80211_scan_state {
 
 DECLARE_STATIC_KEY_FALSE(aql_disable);
 
+struct ieee80211_radio_filter {
+	/* number of interfaces with corresponding FIF_ flags */
+	int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll,
+	    fif_probe_req;
+
+	/* number of interfaces with allmulti RX */
+	atomic_t iff_allmultis;
+
+	unsigned int filter_flags; /* FIF_* */
+};
+
 struct ieee80211_radio_data {
+	struct ieee80211_radio_filter filter;
 	u32 monitors;
 	u32 open_count;
 	bool started;
@@ -1378,12 +1390,10 @@ struct ieee80211_local {
 
 	int open_count;
 	int monitors, cooked_mntrs;
-	/* number of interfaces with corresponding FIF_ flags */
-	int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll,
-	    fif_probe_req;
+	struct ieee80211_radio_filter filter;
+
 	bool probe_req_reg;
 	bool rx_mcast_action_reg;
-	unsigned int filter_flags; /* FIF_* */
 
 	bool wiphy_ciphers_allocated;
 
@@ -1478,9 +1488,6 @@ struct ieee80211_local {
 
 	atomic_t agg_queue_stop[IEEE80211_MAX_QUEUES];
 
-	/* number of interfaces with allmulti RX */
-	atomic_t iff_allmultis;
-
 	struct rate_control_ref *rate_ctrl;
 
 	struct arc4_ctx wep_tx_ctx;
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 20a4a19c8ba1..b7617f98cef6 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -458,6 +458,39 @@ static int ieee80211_open(struct net_device *dev)
 	return err;
 }
 
+static bool
+__ieee80211_sdata_set_filter(struct ieee80211_radio_filter *filter,
+			     struct ieee80211_sub_if_data *sdata, int ofs)
+{
+	if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
+		atomic_add(ofs, &filter->iff_allmultis);
+
+	if (sdata->vif.type == NL80211_IFTYPE_AP) {
+		filter->fif_pspoll += ofs;
+		filter->fif_probe_req += ofs;
+	} else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+		filter->fif_probe_req += ofs;
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+static bool
+ieee80211_sdata_set_filter(struct ieee80211_sub_if_data *sdata, int ofs)
+{
+	struct ieee80211_local *local = sdata->local;
+	int i;
+
+	for (i = 0; i < local->hw.wiphy->n_radio; i++)
+		if (sdata->wdev.radio_mask & BIT(i))
+			__ieee80211_sdata_set_filter(&local->radio_data[i].filter,
+						     sdata, ofs);
+
+	return __ieee80211_sdata_set_filter(&local->filter, sdata, ofs);
+}
+
 static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_down)
 {
 	struct ieee80211_local *local = sdata->local;
@@ -514,16 +547,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
 	flushed = sta_info_flush(sdata, -1);
 	WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP_VLAN && flushed > 0);
 
-	/* don't count this interface for allmulti while it is down */
-	if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
-		atomic_dec(&local->iff_allmultis);
-
-	if (sdata->vif.type == NL80211_IFTYPE_AP) {
-		local->fif_pspoll--;
-		local->fif_probe_req--;
-	} else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
-		local->fif_probe_req--;
-	}
+	ieee80211_sdata_set_filter(sdata, -1);
 
 	if (sdata->dev) {
 		netif_addr_lock_bh(sdata->dev);
@@ -796,16 +820,22 @@ static void ieee80211_set_multicast_list(struct net_device *dev)
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_radio_filter *filter = &local->filter;
 	int allmulti, sdata_allmulti;
+	int i, ofs;
 
 	allmulti = !!(dev->flags & IFF_ALLMULTI);
 	sdata_allmulti = !!(sdata->flags & IEEE80211_SDATA_ALLMULTI);
 
 	if (allmulti != sdata_allmulti) {
-		if (dev->flags & IFF_ALLMULTI)
-			atomic_inc(&local->iff_allmultis);
-		else
-			atomic_dec(&local->iff_allmultis);
+		ofs = (dev->flags & IFF_ALLMULTI) ? 1 : -1;
+		atomic_add(ofs, &filter->iff_allmultis);
+		for (i = 0; i < local->hw.wiphy->n_radio; i++) {
+			if (!(sdata->wdev.radio_mask & BIT(i)))
+				continue;
+			filter = &local->radio_data[i].filter;
+			atomic_add(ofs, &filter->iff_allmultis);
+		}
 		sdata->flags ^= IEEE80211_SDATA_ALLMULTI;
 	}
 
@@ -1080,15 +1110,16 @@ void ieee80211_recalc_offload(struct ieee80211_local *local)
 	}
 }
 
-void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
-				    const int offset)
+static void
+__ieee80211_adjust_monitor_flags(struct ieee80211_radio_filter *filter,
+				 struct ieee80211_sub_if_data *sdata,
+				 const int offset)
 {
-	struct ieee80211_local *local = sdata->local;
 	u32 flags = sdata->u.mntr.flags;
 
 #define ADJUST(_f, _s)	do {					\
 	if (flags & MONITOR_FLAG_##_f)				\
-		local->fif_##_s += offset;			\
+		filter->fif_##_s += offset;			\
 	} while (0)
 
 	ADJUST(FCSFAIL, fcsfail);
@@ -1100,6 +1131,14 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
 #undef ADJUST
 }
 
+void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
+				    const int offset)
+{
+	struct ieee80211_local *local = sdata->local;
+
+	__ieee80211_adjust_monitor_flags(&local->filter, sdata, offset);
+}
+
 static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_local *local = sdata->local;
@@ -1399,15 +1438,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
 				goto err_del_interface;
 		}
 
-		if (sdata->vif.type == NL80211_IFTYPE_AP) {
-			local->fif_pspoll++;
-			local->fif_probe_req++;
-
-			ieee80211_configure_filter(local);
-		} else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
-			local->fif_probe_req++;
-		}
-
 		if (sdata->vif.probe_req_reg)
 			drv_config_iface_filter(local, sdata,
 						FIF_PROBE_REQ,
@@ -1445,6 +1475,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
 			sdata->vif.type != NL80211_IFTYPE_STATION);
 	}
 
+	if (ieee80211_sdata_set_filter(sdata, 1))
+		ieee80211_configure_filter(local);
+
 	switch (sdata->vif.type) {
 	case NL80211_IFTYPE_P2P_DEVICE:
 		rcu_assign_pointer(local->p2p_sdata, sdata);
@@ -1458,14 +1491,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
 		break;
 	}
 
-	/*
-	 * set_multicast_list will be invoked by the networking core
-	 * which will check whether any increments here were done in
-	 * error and sync them down to the hardware as filter flags.
-	 */
-	if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
-		atomic_inc(&local->iff_allmultis);
-
 	if (coming_up) {
 		local->open_count++;
 		for (i = 0; i < local->hw.wiphy->n_radio; i++) {
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 2ce771744ea8..ce2ed4c06a8c 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -34,54 +34,80 @@
 #include "led.h"
 #include "debugfs.h"
 
-void ieee80211_configure_filter(struct ieee80211_local *local)
+static void
+__ieee80211_configure_filter(struct ieee80211_local *local,
+			   int radio_idx)
 {
-	u64 mc;
+	struct ieee80211_radio_filter *filter;
 	unsigned int changed_flags;
 	unsigned int new_flags = 0;
+	u32 monitors;
+	u64 mc;
 
-	if (atomic_read(&local->iff_allmultis))
+	if (radio_idx >= 0) {
+		filter = &local->radio_data[radio_idx].filter;
+		monitors = local->radio_data[radio_idx].monitors;
+	} else {
+		filter = &local->filter;
+		monitors = local->monitors;
+	}
+
+	if (atomic_read(&filter->iff_allmultis))
 		new_flags |= FIF_ALLMULTI;
 
-	if (local->monitors || test_bit(SCAN_SW_SCANNING, &local->scanning) ||
+	if (monitors || test_bit(SCAN_SW_SCANNING, &local->scanning) ||
 	    test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning))
 		new_flags |= FIF_BCN_PRBRESP_PROMISC;
 
-	if (local->fif_probe_req || local->probe_req_reg)
+	if (filter->fif_probe_req || local->probe_req_reg)
 		new_flags |= FIF_PROBE_REQ;
 
-	if (local->fif_fcsfail)
+	if (filter->fif_fcsfail)
 		new_flags |= FIF_FCSFAIL;
 
-	if (local->fif_plcpfail)
+	if (filter->fif_plcpfail)
 		new_flags |= FIF_PLCPFAIL;
 
-	if (local->fif_control)
+	if (filter->fif_control)
 		new_flags |= FIF_CONTROL;
 
-	if (local->fif_other_bss)
+	if (filter->fif_other_bss)
 		new_flags |= FIF_OTHER_BSS;
 
-	if (local->fif_pspoll)
+	if (filter->fif_pspoll)
 		new_flags |= FIF_PSPOLL;
 
 	if (local->rx_mcast_action_reg)
 		new_flags |= FIF_MCAST_ACTION;
 
 	spin_lock_bh(&local->filter_lock);
-	changed_flags = local->filter_flags ^ new_flags;
+	changed_flags = filter->filter_flags ^ new_flags;
 
-	mc = drv_prepare_multicast(local, &local->mc_list);
+	if (radio_idx < 0)
+		mc = drv_prepare_multicast(local, &local->mc_list);
 	spin_unlock_bh(&local->filter_lock);
 
-	/* be a bit nasty */
-	new_flags |= (1<<31);
 
-	drv_configure_filter(local, changed_flags, &new_flags, mc);
+	if (radio_idx < 0) {
+		/* be a bit nasty */
+		new_flags |= (1<<31);
+
+		drv_configure_filter(local, changed_flags, &new_flags, mc);
 
-	WARN_ON(new_flags & (1<<31));
+		WARN_ON(new_flags & (1<<31));
+	} else {
+		drv_config_radio_filter(local, radio_idx, changed_flags, &new_flags);
+	}
+
+	filter->filter_flags = new_flags & ~(1<<31);
+}
+
+void ieee80211_configure_filter(struct ieee80211_local *local)
+{
+	int i;
 
-	local->filter_flags = new_flags & ~(1<<31);
+	for (i = -1; i < local->hw.wiphy->n_radio; i++)
+		__ieee80211_configure_filter(local, i);
 }
 
 static void ieee80211_reconfig_filter(struct wiphy *wiphy,
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index f94e4be0be12..db4e6028074f 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -1170,6 +1170,29 @@ void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata,
 	wiphy_work_queue(sdata->local->hw.wiphy, &sdata->work);
 }
 
+static void
+__ieee80211_mesh_set_filter(struct ieee80211_radio_filter *filter, int ofs)
+{
+	filter->fif_other_bss += ofs;
+
+	/* mesh ifaces must set allmulti to forward mcast traffic */
+	atomic_add(ofs, &filter->iff_allmultis);
+}
+
+static void
+ieee80211_mesh_set_filter(struct ieee80211_sub_if_data *sdata, int ofs)
+{
+	struct ieee80211_local *local = sdata->local;
+	int i;
+
+	__ieee80211_mesh_set_filter(&local->filter, ofs);
+	for (i = 0; i < local->hw.wiphy->n_radio; i++)
+		if (sdata->wdev.radio_mask & BIT(i))
+			__ieee80211_mesh_set_filter(&local->radio_data[i].filter,
+						    ofs);
+	ieee80211_configure_filter(local);
+}
+
 int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
@@ -1181,11 +1204,7 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
 		      BSS_CHANGED_BEACON_INT |
 		      BSS_CHANGED_MCAST_RATE;
 
-	local->fif_other_bss++;
-	/* mesh ifaces must set allmulti to forward mcast traffic */
-	atomic_inc(&local->iff_allmultis);
-	ieee80211_configure_filter(local);
-
+	ieee80211_mesh_set_filter(sdata, 1);
 	ifmsh->mesh_cc_id = 0;	/* Disabled */
 	/* register sync ops from extensible synchronization framework */
 	ifmsh->sync_ops = ieee80211_mesh_sync_ops_get(ifmsh->mesh_sp_id);
@@ -1249,9 +1268,7 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
 	ifmsh->wrkq_flags = 0;
 	memset(ifmsh->mbss_changed, 0, sizeof(ifmsh->mbss_changed));
 
-	local->fif_other_bss--;
-	atomic_dec(&local->iff_allmultis);
-	ieee80211_configure_filter(local);
+	ieee80211_mesh_set_filter(sdata, -1);
 }
 
 static void ieee80211_mesh_csa_mark_radar(struct ieee80211_sub_if_data *sdata)
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 0b6b4ebc681a..6504935b11e3 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -610,6 +610,34 @@ TRACE_EVENT(drv_configure_filter,
 	)
 );
 
+TRACE_EVENT(drv_config_radio_filter,
+	TP_PROTO(struct ieee80211_local *local,
+		 unsigned int radio,
+		 unsigned int changed_flags,
+		 unsigned int *total_flags),
+
+	TP_ARGS(local, radio, changed_flags, total_flags),
+
+	TP_STRUCT__entry(
+		LOCAL_ENTRY
+		__field(unsigned int, radio)
+		__field(unsigned int, changed)
+		__field(unsigned int, total)
+	),
+
+	TP_fast_assign(
+		LOCAL_ASSIGN;
+		__entry->radio = radio;
+		__entry->changed = changed_flags;
+		__entry->total = *total_flags;
+	),
+
+	TP_printk(
+		LOCAL_PR_FMT " radio:%d, changed:%#x total:%#x",
+		LOCAL_PR_ARG, __entry->radio, __entry->changed, __entry->total
+	)
+);
+
 TRACE_EVENT(drv_config_iface_filter,
 	TP_PROTO(struct ieee80211_local *local,
 		 struct ieee80211_sub_if_data *sdata,
-- 
git-series 0.9.1




[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Wireless Regulations]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux