mac80211: add driver callback for per-interface multicast filter
authorAlexander Bondar <alexander.bondar@intel.com>
Mon, 11 Feb 2013 12:56:29 +0000 (14:56 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 11 Mar 2013 14:22:14 +0000 (16:22 +0200)
Some devices have multicast filter capability for each individual
virtual interface rather than just a global one. Add an interface
specific driver callback allowing such drivers to configure this.

Signed-off-by: Alexander Bondar <alexander.bondar@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/mac80211.h
net/mac80211/driver-ops.h
net/mac80211/iface.c
net/mac80211/trace.h

index 8c0ca11a39c4b407529425e41f7a0f13fe9616f2..f5db5e9704285b0b9463612cc460bb4346714785 100644 (file)
@@ -2259,6 +2259,9 @@ enum ieee80211_roc_type {
  *     See the section "Frame filtering" for more information.
  *     This callback must be implemented and can sleep.
  *
+ * @set_multicast_list: Configure the device's interface specific RX multicast
+ *     filter. This callback is optional. This callback must be atomic.
+ *
  * @set_tim: Set TIM bit. mac80211 calls this function when a TIM bit
  *     must be set or cleared for a given STA. Must be atomic.
  *
@@ -2605,6 +2608,10 @@ struct ieee80211_ops {
                                 unsigned int changed_flags,
                                 unsigned int *total_flags,
                                 u64 multicast);
+       void (*set_multicast_list)(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif, bool allmulti,
+                                  struct netdev_hw_addr_list *mc_list);
+
        int (*set_tim)(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
                       bool set);
        int (*set_key)(struct ieee80211_hw *hw, enum set_key_cmd cmd,
index 832acea4a5cba0c86d929eab66d06ba438b8af9c..025b7592b797bb4b4cf31c43bc7c1d08059fd3a8 100644 (file)
@@ -241,6 +241,22 @@ static inline u64 drv_prepare_multicast(struct ieee80211_local *local,
        return ret;
 }
 
+static inline void drv_set_multicast_list(struct ieee80211_local *local,
+                                         struct ieee80211_sub_if_data *sdata,
+                                         struct netdev_hw_addr_list *mc_list)
+{
+       bool allmulti = sdata->flags & IEEE80211_SDATA_ALLMULTI;
+
+       trace_drv_set_multicast_list(local, sdata, mc_list->count);
+
+       check_sdata_in_driver(sdata);
+
+       if (local->ops->set_multicast_list)
+               local->ops->set_multicast_list(&local->hw, &sdata->vif,
+                                              allmulti, mc_list);
+       trace_drv_return_void(local);
+}
+
 static inline void drv_configure_filter(struct ieee80211_local *local,
                                        unsigned int changed_flags,
                                        unsigned int *total_flags,
index d85282f6440515c808768374c4636fc8463d9038..9875e321c9e839716c976b4986de3c7adb603f46 100644 (file)
@@ -919,6 +919,17 @@ static void ieee80211_set_multicast_list(struct net_device *dev)
                        atomic_dec(&local->iff_promiscs);
                sdata->flags ^= IEEE80211_SDATA_PROMISC;
        }
+
+       /*
+        * TODO: If somebody needs this on AP interfaces,
+        *       it can be enabled easily but multicast
+        *       addresses from VLANs need to be synced.
+        */
+       if (sdata->vif.type != NL80211_IFTYPE_MONITOR &&
+           sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+           sdata->vif.type != NL80211_IFTYPE_AP)
+               drv_set_multicast_list(local, sdata, &dev->mc);
+
        spin_lock_bh(&local->filter_lock);
        __hw_addr_sync(&local->mc_list, &dev->mc, dev->addr_len);
        spin_unlock_bh(&local->filter_lock);
index e7db2b804e0cd7e4f6946febe4022542fbae4b74..d97e4305cf1eef4200cfd5b8e459a441bc27ead4 100644 (file)
@@ -431,6 +431,30 @@ TRACE_EVENT(drv_prepare_multicast,
        )
 );
 
+TRACE_EVENT(drv_set_multicast_list,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata, int mc_count),
+
+       TP_ARGS(local, sdata, mc_count),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               __field(bool, allmulti)
+               __field(int, mc_count)
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               __entry->allmulti = sdata->flags & IEEE80211_SDATA_ALLMULTI;
+               __entry->mc_count = mc_count;
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT " configure mc filter, count=%d, allmulti=%d",
+               LOCAL_PR_ARG, __entry->mc_count, __entry->allmulti
+       )
+);
+
 TRACE_EVENT(drv_configure_filter,
        TP_PROTO(struct ieee80211_local *local,
                 unsigned int changed_flags,