mac80211: support use of NAPI for bottom-half processing
John W. Linville [Thu, 29 Jul 2010 20:14:13 +0000 (16:14 -0400)]
This patch implement basic infrastructure to support use of NAPI by
mac80211-based hardware drivers.

Because mac80211 devices can support multiple netdevs, a dummy netdev
is used for interfacing with the NAPI code in the core of the network
stack.  That structure is hidden from the hardware drivers, but the
actual napi_struct is exposed in the ieee80211_hw structure so that the
poll routines in drivers can retrieve that structure.  Hardware drivers
can also specify their own weight value for NAPI polling.

Signed-off-by: John W. Linville <linville@tuxdriver.com>

include/net/mac80211.h
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/main.c

index b0787a1..3f1e03b 100644 (file)
@@ -1102,6 +1102,10 @@ enum ieee80211_hw_flags {
  *
  * @max_rates: maximum number of alternate rate retry stages
  * @max_rate_tries: maximum number of tries for each stage
+ *
+ * @napi_weight: weight used for NAPI polling.  You must specify an
+ *     appropriate value here if a napi_poll operation is provided
+ *     by your driver.
  */
 struct ieee80211_hw {
        struct ieee80211_conf conf;
@@ -1113,6 +1117,7 @@ struct ieee80211_hw {
        int channel_change_time;
        int vif_data_size;
        int sta_data_size;
+       int napi_weight;
        u16 queues;
        u16 max_listen_interval;
        s8 max_signal;
@@ -1687,6 +1692,8 @@ enum ieee80211_ampdu_mlme_action {
  *     switch operation for CSAs received from the AP may implement this
  *     callback. They must then call ieee80211_chswitch_done() to indicate
  *     completion of the channel switch.
+ *
+ * @napi_poll: Poll Rx queue for incoming data frames.
  */
 struct ieee80211_ops {
        int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb);
@@ -1752,6 +1759,7 @@ struct ieee80211_ops {
        void (*flush)(struct ieee80211_hw *hw, bool drop);
        void (*channel_switch)(struct ieee80211_hw *hw,
                               struct ieee80211_channel_switch *ch_switch);
+       int (*napi_poll)(struct ieee80211_hw *hw, int budget);
 };
 
 /**
@@ -1897,6 +1905,22 @@ void ieee80211_free_hw(struct ieee80211_hw *hw);
  */
 void ieee80211_restart_hw(struct ieee80211_hw *hw);
 
+/** ieee80211_napi_schedule - schedule NAPI poll
+ *
+ * Use this function to schedule NAPI polling on a device.
+ *
+ * @hw: the hardware to start polling
+ */
+void ieee80211_napi_schedule(struct ieee80211_hw *hw);
+
+/** ieee80211_napi_complete - complete NAPI polling
+ *
+ * Use this function to finish NAPI polling on a device.
+ *
+ * @hw: the hardware to stop polling
+ */
+void ieee80211_napi_complete(struct ieee80211_hw *hw);
+
 /**
  * ieee80211_rx - receive frame
  *
index 65e0ed6..79d5645 100644 (file)
@@ -870,6 +870,11 @@ struct ieee80211_local {
                struct dentry *keys;
        } debugfs;
 #endif
+
+       /* dummy netdev for use w/ NAPI */
+       struct net_device napi_dev;
+
+       struct napi_struct napi;
 };
 
 static inline struct ieee80211_sub_if_data *
index ebbe264..c1008a9 100644 (file)
@@ -187,6 +187,8 @@ static int ieee80211_open(struct net_device *dev)
                res = drv_start(local);
                if (res)
                        goto err_del_bss;
+               if (local->ops->napi_poll)
+                       napi_enable(&local->napi);
                /* we're brought up, everything changes */
                hw_reconf_flags = ~0;
                ieee80211_led_radio(local, true);
@@ -519,6 +521,8 @@ static int ieee80211_stop(struct net_device *dev)
        ieee80211_recalc_ps(local, -1);
 
        if (local->open_count == 0) {
+               if (local->ops->napi_poll)
+                       napi_disable(&local->napi);
                ieee80211_clear_tx_pending(local);
                ieee80211_stop_device(local);
 
index 798a91b..1ed956c 100644 (file)
@@ -390,6 +390,30 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,
 }
 #endif
 
+static int ieee80211_napi_poll(struct napi_struct *napi, int budget)
+{
+       struct ieee80211_local *local =
+               container_of(napi, struct ieee80211_local, napi);
+
+       return local->ops->napi_poll(&local->hw, budget);
+}
+
+void ieee80211_napi_schedule(struct ieee80211_hw *hw)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+
+       napi_schedule(&local->napi);
+}
+EXPORT_SYMBOL(ieee80211_napi_schedule);
+
+void ieee80211_napi_complete(struct ieee80211_hw *hw)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+
+       napi_complete(&local->napi);
+}
+EXPORT_SYMBOL(ieee80211_napi_complete);
+
 struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
                                        const struct ieee80211_ops *ops)
 {
@@ -494,6 +518,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
        skb_queue_head_init(&local->skb_queue);
        skb_queue_head_init(&local->skb_queue_unreliable);
 
+       /* init dummy netdev for use w/ NAPI */
+       init_dummy_netdev(&local->napi_dev);
+
        return local_to_hw(local);
 }
 EXPORT_SYMBOL(ieee80211_alloc_hw);
@@ -683,6 +710,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                goto fail_ifa;
 #endif
 
+       netif_napi_add(&local->napi_dev, &local->napi, ieee80211_napi_poll,
+                       local->hw.napi_weight);
+
        return 0;
 
 #ifdef CONFIG_INET