aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorThomas Pedersen <thomas@cozybit.com>2013-02-14 14:20:13 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-02-15 03:41:40 -0500
commit2b5e19677592c167d012c2d129407f39d2bdeb8d (patch)
treedb898021baebc1b462b78d00048d0cf9ef3f0aa8 /net
parent4a3cb702b05868f67c4ee3da3380461c5b90b4ca (diff)
mac80211: cache mesh beacon
Previously, the entire mesh beacon would be generated each time the beacon timer fired. Instead generate a beacon head and tail (so the TIM can easily be inserted when mesh power save is on) when starting a mesh or the MBSS parameters change. Also add a mutex for protecting beacon updates and preventing leaks. Signed-off-by: Thomas Pedersen <thomas@cozybit.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/cfg.c9
-rw-r--r--net/mac80211/ieee80211_i.h3
-rw-r--r--net/mac80211/mesh.c154
-rw-r--r--net/mac80211/mesh.h5
-rw-r--r--net/mac80211/mesh_plink.c4
-rw-r--r--net/mac80211/tx.c61
6 files changed, 171 insertions, 65 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index c3869ba42343..0800fb331ce5 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1800,11 +1800,10 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
1800 conf->power_mode = nconf->power_mode; 1800 conf->power_mode = nconf->power_mode;
1801 ieee80211_mps_local_status_update(sdata); 1801 ieee80211_mps_local_status_update(sdata);
1802 } 1802 }
1803 if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask)) { 1803 if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask))
1804 conf->dot11MeshAwakeWindowDuration = 1804 conf->dot11MeshAwakeWindowDuration =
1805 nconf->dot11MeshAwakeWindowDuration; 1805 nconf->dot11MeshAwakeWindowDuration;
1806 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); 1806 ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON);
1807 }
1808 return 0; 1807 return 0;
1809} 1808}
1810 1809
@@ -1830,9 +1829,7 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
1830 if (err) 1829 if (err)
1831 return err; 1830 return err;
1832 1831
1833 ieee80211_start_mesh(sdata); 1832 return ieee80211_start_mesh(sdata);
1834
1835 return 0;
1836} 1833}
1837 1834
1838static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev) 1835static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index d702f7dc321b..388580a1bada 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -580,6 +580,9 @@ struct ieee80211_if_mesh {
580 u32 mesh_seqnum; 580 u32 mesh_seqnum;
581 bool accepting_plinks; 581 bool accepting_plinks;
582 int num_gates; 582 int num_gates;
583 struct beacon_data __rcu *beacon;
584 /* just protects beacon updates for now */
585 struct mutex mtx;
583 const u8 *ie; 586 const u8 *ie;
584 u8 ie_len; 587 u8 ie_len;
585 enum { 588 enum {
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 0adec3d539d3..6ff8ee9d9ff5 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -171,7 +171,7 @@ void mesh_sta_cleanup(struct sta_info *sta)
171 } 171 }
172 172
173 if (changed) 173 if (changed)
174 ieee80211_bss_info_change_notify(sdata, changed); 174 ieee80211_mbss_info_change_notify(sdata, changed);
175} 175}
176 176
177int mesh_rmc_init(struct ieee80211_sub_if_data *sdata) 177int mesh_rmc_init(struct ieee80211_sub_if_data *sdata)
@@ -593,7 +593,7 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata,
593 mesh_path_expire(sdata); 593 mesh_path_expire(sdata);
594 594
595 changed = mesh_accept_plinks_update(sdata); 595 changed = mesh_accept_plinks_update(sdata);
596 ieee80211_bss_info_change_notify(sdata, changed); 596 ieee80211_mbss_info_change_notify(sdata, changed);
597 597
598 mod_timer(&ifmsh->housekeeping_timer, 598 mod_timer(&ifmsh->housekeeping_timer,
599 round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL)); 599 round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL));
@@ -644,7 +644,140 @@ void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
644} 644}
645#endif 645#endif
646 646
647void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) 647static int
648ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
649{
650 struct beacon_data *bcn;
651 int head_len, tail_len;
652 struct sk_buff *skb;
653 struct ieee80211_mgmt *mgmt;
654 struct ieee80211_chanctx_conf *chanctx_conf;
655 enum ieee80211_band band;
656 u8 *pos;
657 struct ieee80211_sub_if_data *sdata;
658 int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) +
659 sizeof(mgmt->u.beacon);
660
661 sdata = container_of(ifmsh, struct ieee80211_sub_if_data, u.mesh);
662 rcu_read_lock();
663 chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
664 band = chanctx_conf->def.chan->band;
665 rcu_read_unlock();
666
667 head_len = hdr_len +
668 2 + /* NULL SSID */
669 2 + 8 + /* supported rates */
670 2 + 3; /* DS params */
671 tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
672 2 + sizeof(struct ieee80211_ht_cap) +
673 2 + sizeof(struct ieee80211_ht_operation) +
674 2 + ifmsh->mesh_id_len +
675 2 + sizeof(struct ieee80211_meshconf_ie) +
676 2 + sizeof(__le16) + /* awake window */
677 ifmsh->ie_len;
678
679 bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL);
680 /* need an skb for IE builders to operate on */
681 skb = dev_alloc_skb(max(head_len, tail_len));
682
683 if (!bcn || !skb)
684 goto out_free;
685
686 /*
687 * pointers go into the block we allocated,
688 * memory is | beacon_data | head | tail |
689 */
690 bcn->head = ((u8 *) bcn) + sizeof(*bcn);
691
692 /* fill in the head */
693 mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
694 memset(mgmt, 0, hdr_len);
695 mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
696 IEEE80211_STYPE_BEACON);
697 eth_broadcast_addr(mgmt->da);
698 memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
699 memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
700 ieee80211_mps_set_frame_flags(sdata, NULL, (void *) mgmt);
701 mgmt->u.beacon.beacon_int =
702 cpu_to_le16(sdata->vif.bss_conf.beacon_int);
703 mgmt->u.beacon.capab_info |= cpu_to_le16(
704 sdata->u.mesh.security ? WLAN_CAPABILITY_PRIVACY : 0);
705
706 pos = skb_put(skb, 2);
707 *pos++ = WLAN_EID_SSID;
708 *pos++ = 0x0;
709
710 if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
711 mesh_add_ds_params_ie(skb, sdata))
712 goto out_free;
713
714 bcn->head_len = skb->len;
715 memcpy(bcn->head, skb->data, bcn->head_len);
716
717 /* now the tail */
718 skb_trim(skb, 0);
719 bcn->tail = bcn->head + bcn->head_len;
720
721 if (ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
722 mesh_add_rsn_ie(skb, sdata) ||
723 mesh_add_ht_cap_ie(skb, sdata) ||
724 mesh_add_ht_oper_ie(skb, sdata) ||
725 mesh_add_meshid_ie(skb, sdata) ||
726 mesh_add_meshconf_ie(skb, sdata) ||
727 mesh_add_awake_window_ie(skb, sdata) ||
728 mesh_add_vendor_ies(skb, sdata))
729 goto out_free;
730
731 bcn->tail_len = skb->len;
732 memcpy(bcn->tail, skb->data, bcn->tail_len);
733
734 dev_kfree_skb(skb);
735 rcu_assign_pointer(ifmsh->beacon, bcn);
736 return 0;
737out_free:
738 kfree(bcn);
739 dev_kfree_skb(skb);
740 return -ENOMEM;
741}
742
743static int
744ieee80211_mesh_rebuild_beacon(struct ieee80211_if_mesh *ifmsh)
745{
746 struct ieee80211_sub_if_data *sdata;
747 struct beacon_data *old_bcn;
748 int ret;
749 sdata = container_of(ifmsh, struct ieee80211_sub_if_data, u.mesh);
750
751 mutex_lock(&ifmsh->mtx);
752
753 old_bcn = rcu_dereference_protected(ifmsh->beacon,
754 lockdep_is_held(&ifmsh->mtx));
755 ret = ieee80211_mesh_build_beacon(ifmsh);
756 if (ret)
757 /* just reuse old beacon */
758 goto out;
759
760 if (old_bcn)
761 kfree_rcu(old_bcn, rcu_head);
762out:
763 mutex_unlock(&ifmsh->mtx);
764 return ret;
765}
766
767void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata,
768 u32 changed)
769{
770 if (sdata->vif.bss_conf.enable_beacon &&
771 (changed & (BSS_CHANGED_BEACON |
772 BSS_CHANGED_HT |
773 BSS_CHANGED_BASIC_RATES |
774 BSS_CHANGED_BEACON_INT)))
775 if (ieee80211_mesh_rebuild_beacon(&sdata->u.mesh))
776 return;
777 ieee80211_bss_info_change_notify(sdata, changed);
778}
779
780int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
648{ 781{
649 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 782 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
650 struct ieee80211_local *local = sdata->local; 783 struct ieee80211_local *local = sdata->local;
@@ -677,15 +810,22 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
677 810
678 changed |= ieee80211_mps_local_status_update(sdata); 811 changed |= ieee80211_mps_local_status_update(sdata);
679 812
813 if (ieee80211_mesh_build_beacon(ifmsh)) {
814 ieee80211_stop_mesh(sdata);
815 return -ENOMEM;
816 }
817
680 ieee80211_bss_info_change_notify(sdata, changed); 818 ieee80211_bss_info_change_notify(sdata, changed);
681 819
682 netif_carrier_on(sdata->dev); 820 netif_carrier_on(sdata->dev);
821 return 0;
683} 822}
684 823
685void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) 824void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
686{ 825{
687 struct ieee80211_local *local = sdata->local; 826 struct ieee80211_local *local = sdata->local;
688 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 827 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
828 struct beacon_data *bcn;
689 829
690 netif_carrier_off(sdata->dev); 830 netif_carrier_off(sdata->dev);
691 831
@@ -694,6 +834,12 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
694 sdata->vif.bss_conf.enable_beacon = false; 834 sdata->vif.bss_conf.enable_beacon = false;
695 clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); 835 clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
696 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); 836 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
837 mutex_lock(&ifmsh->mtx);
838 bcn = rcu_dereference_protected(ifmsh->beacon,
839 lockdep_is_held(&ifmsh->mtx));
840 rcu_assign_pointer(ifmsh->beacon, NULL);
841 kfree_rcu(bcn, rcu_head);
842 mutex_unlock(&ifmsh->mtx);
697 843
698 /* flush STAs and mpaths on this iface */ 844 /* flush STAs and mpaths on this iface */
699 sta_info_flush(sdata); 845 sta_info_flush(sdata);
@@ -883,6 +1029,8 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
883 skb_queue_head_init(&ifmsh->ps.bc_buf); 1029 skb_queue_head_init(&ifmsh->ps.bc_buf);
884 spin_lock_init(&ifmsh->mesh_preq_queue_lock); 1030 spin_lock_init(&ifmsh->mesh_preq_queue_lock);
885 spin_lock_init(&ifmsh->sync_offset_lock); 1031 spin_lock_init(&ifmsh->sync_offset_lock);
1032 RCU_INIT_POINTER(ifmsh->beacon, NULL);
1033 mutex_init(&ifmsh->mtx);
886 1034
887 sdata->vif.bss_conf.bssid = zero_addr; 1035 sdata->vif.bss_conf.bssid = zero_addr;
888} 1036}
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index a1bad310f2e9..1a1da877b1d2 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -239,10 +239,13 @@ void ieee80211s_update_metric(struct ieee80211_local *local,
239 struct sta_info *sta, struct sk_buff *skb); 239 struct sta_info *sta, struct sk_buff *skb);
240void ieee80211s_stop(void); 240void ieee80211s_stop(void);
241void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata); 241void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata);
242void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata); 242int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata);
243void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata); 243void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata);
244void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh); 244void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh);
245const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method); 245const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method);
246/* wrapper for ieee80211_bss_info_change_notify() */
247void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata,
248 u32 changed);
246 249
247/* mesh power save */ 250/* mesh power save */
248u32 ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata); 251u32 ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata);
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 7765139b24aa..f7526e509aa8 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -508,7 +508,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
508 ieee80211_mps_frame_release(sta, elems); 508 ieee80211_mps_frame_release(sta, elems);
509out: 509out:
510 rcu_read_unlock(); 510 rcu_read_unlock();
511 ieee80211_bss_info_change_notify(sdata, changed); 511 ieee80211_mbss_info_change_notify(sdata, changed);
512} 512}
513 513
514static void mesh_plink_timer(unsigned long data) 514static void mesh_plink_timer(unsigned long data)
@@ -1090,5 +1090,5 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
1090 rcu_read_unlock(); 1090 rcu_read_unlock();
1091 1091
1092 if (changed) 1092 if (changed)
1093 ieee80211_bss_info_change_notify(sdata, changed); 1093 ieee80211_mbss_info_change_notify(sdata, changed);
1094} 1094}
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 7d8c629f1e6a..fe644f91ae05 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2444,71 +2444,26 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
2444 hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 2444 hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
2445 IEEE80211_STYPE_BEACON); 2445 IEEE80211_STYPE_BEACON);
2446 } else if (ieee80211_vif_is_mesh(&sdata->vif)) { 2446 } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
2447 struct ieee80211_mgmt *mgmt;
2448 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 2447 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
2449 u8 *pos; 2448 struct beacon_data *bcn = rcu_dereference(ifmsh->beacon);
2450 int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) +
2451 sizeof(mgmt->u.beacon);
2452 2449
2453#ifdef CONFIG_MAC80211_MESH 2450 if (!bcn)
2454 if (!sdata->u.mesh.mesh_id_len)
2455 goto out; 2451 goto out;
2456#endif
2457 2452
2458 if (ifmsh->sync_ops) 2453 if (ifmsh->sync_ops)
2459 ifmsh->sync_ops->adjust_tbtt( 2454 ifmsh->sync_ops->adjust_tbtt(
2460 sdata); 2455 sdata);
2461 2456
2462 skb = dev_alloc_skb(local->tx_headroom + 2457 skb = dev_alloc_skb(local->tx_headroom +
2463 hdr_len + 2458 bcn->head_len +
2464 2 + /* NULL SSID */
2465 2 + 8 + /* supported rates */
2466 2 + 3 + /* DS params */
2467 256 + /* TIM IE */ 2459 256 + /* TIM IE */
2468 2 + (IEEE80211_MAX_SUPP_RATES - 8) + 2460 bcn->tail_len);
2469 2 + sizeof(struct ieee80211_ht_cap) +
2470 2 + sizeof(struct ieee80211_ht_operation) +
2471 2 + sdata->u.mesh.mesh_id_len +
2472 2 + sizeof(struct ieee80211_meshconf_ie) +
2473 sdata->u.mesh.ie_len +
2474 2 + sizeof(__le16)); /* awake window */
2475 if (!skb) 2461 if (!skb)
2476 goto out; 2462 goto out;
2477 2463 skb_reserve(skb, local->tx_headroom);
2478 skb_reserve(skb, local->hw.extra_tx_headroom); 2464 memcpy(skb_put(skb, bcn->head_len), bcn->head, bcn->head_len);
2479 mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); 2465 ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb);
2480 memset(mgmt, 0, hdr_len); 2466 memcpy(skb_put(skb, bcn->tail_len), bcn->tail, bcn->tail_len);
2481 mgmt->frame_control =
2482 cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON);
2483 eth_broadcast_addr(mgmt->da);
2484 memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
2485 memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
2486 ieee80211_mps_set_frame_flags(sdata, NULL, (void *) mgmt);
2487 mgmt->u.beacon.beacon_int =
2488 cpu_to_le16(sdata->vif.bss_conf.beacon_int);
2489 mgmt->u.beacon.capab_info |= cpu_to_le16(
2490 sdata->u.mesh.security ? WLAN_CAPABILITY_PRIVACY : 0);
2491
2492 pos = skb_put(skb, 2);
2493 *pos++ = WLAN_EID_SSID;
2494 *pos++ = 0x0;
2495
2496 band = chanctx_conf->def.chan->band;
2497
2498 if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
2499 mesh_add_ds_params_ie(skb, sdata) ||
2500 ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb) ||
2501 ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
2502 mesh_add_rsn_ie(skb, sdata) ||
2503 mesh_add_ht_cap_ie(skb, sdata) ||
2504 mesh_add_ht_oper_ie(skb, sdata) ||
2505 mesh_add_meshid_ie(skb, sdata) ||
2506 mesh_add_meshconf_ie(skb, sdata) ||
2507 mesh_add_awake_window_ie(skb, sdata) ||
2508 mesh_add_vendor_ies(skb, sdata)) {
2509 pr_err("o11s: couldn't add ies!\n");
2510 goto out;
2511 }
2512 } else { 2467 } else {
2513 WARN_ON(1); 2468 WARN_ON(1);
2514 goto out; 2469 goto out;