mac80211: simplify mesh frame queue mapping and QoS
[deliverable/linux.git] / net / mac80211 / mesh_hwmp.c
index 7b517c46100d70e77a43fb05cd98dc583962040c..a7afb2d32def7bfe308566b0147328827eb80067 100644 (file)
@@ -8,6 +8,7 @@
  */
 
 #include <linux/slab.h>
+#include "wme.h"
 #include "mesh.h"
 
 #ifdef CONFIG_MAC80211_VERBOSE_MHWMP_DEBUG
@@ -112,20 +113,20 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
                struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_local *local = sdata->local;
-       struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
+       struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
-       u8 *pos;
-       int ie_len;
+       u8 *pos, ie_len;
+       int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.mesh_action) +
+                     sizeof(mgmt->u.action.u.mesh_action);
 
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+                           hdr_len +
+                           2 + 37); /* max HWMP IE */
        if (!skb)
                return -1;
        skb_reserve(skb, local->hw.extra_tx_headroom);
-       /* 25 is the size of the common mgmt part (24) plus the size of the
-        * common action part (1)
-        */
-       mgmt = (struct ieee80211_mgmt *)
-               skb_put(skb, 25 + sizeof(mgmt->u.action.u.mesh_action));
-       memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.mesh_action));
+       mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
+       memset(mgmt, 0, hdr_len);
        mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                          IEEE80211_STYPE_ACTION);
 
@@ -202,6 +203,26 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
        return 0;
 }
 
+
+/*  Headroom is not adjusted.  Caller should ensure that skb has sufficient
+ *  headroom in case the frame is encrypted. */
+static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata,
+               struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+       skb_set_mac_header(skb, 0);
+       skb_set_network_header(skb, 0);
+       skb_set_transport_header(skb, 0);
+
+       /* Send all internal mgmt frames on VO. Accordingly set TID to 7. */
+       skb_set_queue_mapping(skb, IEEE80211_AC_VO);
+       skb->priority = 7;
+
+       info->control.vif = &sdata->vif;
+       ieee80211_set_qos_hdr(sdata, skb);
+}
+
 /**
  * mesh_send_path error - Sends a PERR mesh management frame
  *
@@ -209,26 +230,30 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
  * @target_sn: SN of the broken destination
  * @target_rcode: reason code for this PERR
  * @ra: node this frame is addressed to
+ *
+ * Note: This function may be called with driver locks taken that the driver
+ * also acquires in the TX path.  To avoid a deadlock we don't transmit the
+ * frame directly but add it to the pending queue instead.
  */
 int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn,
                       __le16 target_rcode, const u8 *ra,
                       struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_local *local = sdata->local;
-       struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
+       struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
-       u8 *pos;
-       int ie_len;
+       u8 *pos, ie_len;
+       int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.mesh_action) +
+                     sizeof(mgmt->u.action.u.mesh_action);
 
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+                           hdr_len +
+                           2 + 15 /* PERR IE */);
        if (!skb)
                return -1;
-       skb_reserve(skb, local->hw.extra_tx_headroom);
-       /* 25 is the size of the common mgmt part (24) plus the size of the
-        * common action part (1)
-        */
-       mgmt = (struct ieee80211_mgmt *)
-               skb_put(skb, 25 + sizeof(mgmt->u.action.u.mesh_action));
-       memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.mesh_action));
+       skb_reserve(skb, local->tx_headroom + local->hw.extra_tx_headroom);
+       mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
+       memset(mgmt, 0, hdr_len);
        mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                          IEEE80211_STYPE_ACTION);
 
@@ -263,7 +288,9 @@ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn,
        pos += 4;
        memcpy(pos, &target_rcode, 2);
 
-       ieee80211_tx_skb(sdata, skb);
+       /* see note in function header */
+       prepare_frame_for_deferred_tx(sdata, skb);
+       ieee80211_add_pending_skb(local, skb);
        return 0;
 }
 
@@ -695,7 +722,7 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,
        u8 ttl, flags, hopcount;
        u8 *orig_addr;
        u32 orig_sn, metric;
-       u32 interval = cpu_to_le32(IEEE80211_MESH_RANN_INTERVAL);
+       u32 interval = ifmsh->mshcfg.dot11MeshHWMPRannInterval;
        bool root_is_gate;
 
        ttl = rann->rann_ttl;
@@ -743,7 +770,7 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,
                mesh_path_sel_frame_tx(MPATH_RANN, flags, orig_addr,
                                       cpu_to_le32(orig_sn),
                                       0, NULL, 0, broadcast_addr,
-                                      hopcount, ttl, interval,
+                                      hopcount, ttl, cpu_to_le32(interval),
                                       cpu_to_le32(metric + mpath->metric),
                                       0, sdata);
                mpath->sn = orig_sn;
@@ -762,11 +789,20 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
        struct ieee802_11_elems elems;
        size_t baselen;
        u32 last_hop_metric;
+       struct sta_info *sta;
 
        /* need action_code */
        if (len < IEEE80211_MIN_ACTION_SIZE + 1)
                return;
 
+       rcu_read_lock();
+       sta = sta_info_get(sdata, mgmt->sa);
+       if (!sta || sta->plink_state != NL80211_PLINK_ESTAB) {
+               rcu_read_unlock();
+               return;
+       }
+       rcu_read_unlock();
+
        baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt;
        ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable,
                        len - baselen, &elems);
@@ -831,9 +867,19 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags)
                return;
        }
 
+       spin_lock_bh(&mpath->state_lock);
+       if (mpath->flags & MESH_PATH_REQ_QUEUED) {
+               spin_unlock_bh(&mpath->state_lock);
+               spin_unlock_bh(&ifmsh->mesh_preq_queue_lock);
+               return;
+       }
+
        memcpy(preq_node->dst, mpath->dst, ETH_ALEN);
        preq_node->flags = flags;
 
+       mpath->flags |= MESH_PATH_REQ_QUEUED;
+       spin_unlock_bh(&mpath->state_lock);
+
        list_add_tail(&preq_node->list, &ifmsh->preq_queue.list);
        ++ifmsh->preq_queue_len;
        spin_unlock_bh(&ifmsh->mesh_preq_queue_lock);
@@ -885,6 +931,7 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
                goto enddiscovery;
 
        spin_lock_bh(&mpath->state_lock);
+       mpath->flags &= ~MESH_PATH_REQ_QUEUED;
        if (preq_node->flags & PREQ_Q_F_START) {
                if (mpath->flags & MESH_PATH_RESOLVING) {
                        spin_unlock_bh(&mpath->state_lock);
@@ -992,11 +1039,11 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
                        mesh_queue_preq(mpath, PREQ_Q_F_START);
                }
 
-               if (skb_queue_len(&mpath->frame_queue) >=
-                               MESH_FRAME_QUEUE_LEN)
+               if (skb_queue_len(&mpath->frame_queue) >= MESH_FRAME_QUEUE_LEN)
                        skb_to_free = skb_dequeue(&mpath->frame_queue);
 
                info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
+               ieee80211_set_qos_hdr(sdata, skb);
                skb_queue_tail(&mpath->frame_queue, skb);
                if (skb_to_free)
                        mesh_path_discard_frame(skb_to_free, sdata);
@@ -1025,6 +1072,7 @@ void mesh_path_timer(unsigned long data)
        } else if (mpath->discovery_retries < max_preq_retries(sdata)) {
                ++mpath->discovery_retries;
                mpath->discovery_timeout *= 2;
+               mpath->flags &= ~MESH_PATH_REQ_QUEUED;
                spin_unlock_bh(&mpath->state_lock);
                mesh_queue_preq(mpath, 0);
        } else {
@@ -1044,11 +1092,14 @@ void
 mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-       u32 interval = cpu_to_le32(IEEE80211_MESH_RANN_INTERVAL);
+       u32 interval = ifmsh->mshcfg.dot11MeshHWMPRannInterval;
+       u8 flags;
 
-       mesh_path_sel_frame_tx(MPATH_RANN, 0, sdata->vif.addr,
+       flags = (ifmsh->mshcfg.dot11MeshGateAnnouncementProtocol)
+                       ? RANN_FLAG_IS_GATE : 0;
+       mesh_path_sel_frame_tx(MPATH_RANN, flags, sdata->vif.addr,
                               cpu_to_le32(++ifmsh->sn),
                               0, NULL, 0, broadcast_addr,
                               0, sdata->u.mesh.mshcfg.element_ttl,
-                              interval, 0, 0, sdata);
+                              cpu_to_le32(interval), 0, 0, sdata);
 }
This page took 0.054434 seconds and 5 git commands to generate.