cfg80211: track monitor interfaces count
authorMichal Kazior <michal.kazior@tieto.com>
Fri, 29 Jun 2012 10:47:01 +0000 (12:47 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 29 Jun 2012 11:39:16 +0000 (13:39 +0200)
Implements .set_monitor_enabled(wiphy, enabled).

Notifies driver upon change of interface layout.

If only monitor interfaces become present it is
called with 2nd argument being true. If
non-monitor interface appears then 2nd argument
is false. Driver is notified only upon change.

This makes it more obvious about the fact that
cfg80211 supports single monitor channel. Once we
implement multi-channel we don't want to allow
setting monitor channel while other interface
types are running. Otherwise it would be ambiguous
once we start considering num_different_channels.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/cfg80211.h
net/wireless/core.c
net/wireless/core.h
net/wireless/util.c

index e030c6af86dd07a5c1bd08551bdf34b28aba1544..f0d213dd8fe7ba6205806d85312f0dee344caf77 100644 (file)
@@ -1499,6 +1499,8 @@ struct cfg80211_gtk_rekey_data {
  *     interfaces are active this callback should reject the configuration.
  *     If no interfaces are active or the device is down, the channel should
  *     be stored for when a monitor interface becomes active.
+ * @set_monitor_enabled: Notify driver that there are only monitor
+ *     interfaces running.
  * @get_channel: Get the current operating channel, should return %NULL if
  *     there's no single defined operating channel if for example the
  *     device implements channel hopping for multi-channel virtual interfaces.
@@ -1817,6 +1819,8 @@ struct cfg80211_ops {
                                struct ethtool_stats *stats, u64 *data);
        void    (*get_et_strings)(struct wiphy *wiphy, struct net_device *dev,
                                  u32 sset, u8 *data);
+
+       void (*set_monitor_enabled)(struct wiphy *wiphy, bool enabled);
 };
 
 /*
index c65f59c952c9a0f6e34aa870fded27c873d194f9..8412da7d0f25dccd37255170f875f663dbec12af 100644 (file)
@@ -717,6 +717,24 @@ static struct device_type wiphy_type = {
        .name   = "wlan",
 };
 
+void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
+                              enum nl80211_iftype iftype, int num)
+{
+       bool has_monitors_only_old = cfg80211_has_monitors_only(rdev);
+       bool has_monitors_only_new;
+
+       ASSERT_RDEV_LOCK(rdev);
+
+       rdev->num_running_ifaces += num;
+       if (iftype == NL80211_IFTYPE_MONITOR)
+               rdev->num_running_monitor_ifaces += num;
+
+       has_monitors_only_new = cfg80211_has_monitors_only(rdev);
+       if (has_monitors_only_new != has_monitors_only_old)
+               rdev->ops->set_monitor_enabled(&rdev->wiphy,
+                                              has_monitors_only_new);
+}
+
 static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                                         unsigned long state,
                                         void *ndev)
@@ -820,6 +838,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                break;
        case NETDEV_DOWN:
                dev_hold(dev);
+               cfg80211_lock_rdev(rdev);
+               cfg80211_update_iface_num(rdev, wdev->iftype, -1);
+               cfg80211_unlock_rdev(rdev);
                queue_work(cfg80211_wq, &wdev->cleanup_work);
                break;
        case NETDEV_UP:
@@ -927,6 +948,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                ret = cfg80211_can_add_interface(rdev, wdev->iftype);
                if (ret)
                        return notifier_from_errno(ret);
+               cfg80211_lock_rdev(rdev);
+               cfg80211_update_iface_num(rdev, wdev->iftype, 1);
+               cfg80211_unlock_rdev(rdev);
                break;
        }
 
index 56f18c2eb9190242d30c01f670227692df22c250..99acd51343b11742c020ae57ef8395596696bbef 100644 (file)
@@ -56,6 +56,9 @@ struct cfg80211_registered_device {
 
        u32 ap_beacons_nlpid;
 
+       int num_running_ifaces;
+       int num_running_monitor_ifaces;
+
        /* BSSes/scanning */
        spinlock_t bss_lock;
        struct list_head bss_list;
@@ -197,6 +200,14 @@ static inline void wdev_unlock(struct wireless_dev *wdev)
 #define ASSERT_RDEV_LOCK(rdev) lockdep_assert_held(&(rdev)->mtx)
 #define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx)
 
+static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev)
+{
+       ASSERT_RDEV_LOCK(rdev);
+
+       return rdev->num_running_ifaces == rdev->num_running_monitor_ifaces &&
+              rdev->num_running_ifaces > 0;
+}
+
 enum cfg80211_event_type {
        EVENT_CONNECT_RESULT,
        EVENT_ROAMED,
@@ -444,6 +455,9 @@ int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
 int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
                                 u32 beacon_int);
 
+void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
+                              enum nl80211_iftype iftype, int num);
+
 #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS
 #define CFG80211_DEV_WARN_ON(cond)     WARN_ON(cond)
 #else
index fc948d0a53f3973c93ad9d3a7c0ad06e9cab21bd..9b92ec57d07bd66c4ef11afa3dae829b39d49ba3 100644 (file)
@@ -871,6 +871,11 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
                }
        }
 
+       if (!err && ntype != otype && netif_running(dev)) {
+               cfg80211_update_iface_num(rdev, ntype, 1);
+               cfg80211_update_iface_num(rdev, otype, -1);
+       }
+
        return err;
 }