diff options
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/Kconfig | 13 | ||||
-rw-r--r-- | net/mac80211/agg-tx.c | 8 | ||||
-rw-r--r-- | net/mac80211/cfg.c | 4 | ||||
-rw-r--r-- | net/mac80211/debugfs_netdev.c | 6 | ||||
-rw-r--r-- | net/mac80211/driver-ops.h | 38 | ||||
-rw-r--r-- | net/mac80211/driver-trace.h | 36 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 29 | ||||
-rw-r--r-- | net/mac80211/iface.c | 56 | ||||
-rw-r--r-- | net/mac80211/main.c | 48 | ||||
-rw-r--r-- | net/mac80211/mesh.c | 194 | ||||
-rw-r--r-- | net/mac80211/mesh.h | 32 | ||||
-rw-r--r-- | net/mac80211/mesh_hwmp.c | 38 | ||||
-rw-r--r-- | net/mac80211/mesh_pathtbl.c | 151 | ||||
-rw-r--r-- | net/mac80211/mesh_plink.c | 2 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 59 | ||||
-rw-r--r-- | net/mac80211/pm.c | 13 | ||||
-rw-r--r-- | net/mac80211/rc80211_minstrel.h | 1 | ||||
-rw-r--r-- | net/mac80211/rc80211_minstrel_debugfs.c | 4 | ||||
-rw-r--r-- | net/mac80211/rc80211_pid_algo.c | 16 | ||||
-rw-r--r-- | net/mac80211/rc80211_pid_debugfs.c | 2 | ||||
-rw-r--r-- | net/mac80211/rx.c | 90 | ||||
-rw-r--r-- | net/mac80211/scan.c | 35 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 2 | ||||
-rw-r--r-- | net/mac80211/tx.c | 166 | ||||
-rw-r--r-- | net/mac80211/util.c | 12 |
25 files changed, 635 insertions, 420 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 7dd77b6d4c9..4d5543af312 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -13,8 +13,7 @@ config MAC80211 comment "CFG80211 needs to be enabled for MAC80211" depends on CFG80211=n -menu "Rate control algorithm selection" - depends on MAC80211 != n +if MAC80211 != n config MAC80211_RC_PID bool "PID controller based rate control algorithm" if EMBEDDED @@ -61,17 +60,17 @@ config MAC80211_RC_DEFAULT default "pid" if MAC80211_RC_DEFAULT_PID default "" -endmenu +endif config MAC80211_MESH bool "Enable mac80211 mesh networking (pre-802.11s) support" depends on MAC80211 && EXPERIMENTAL - depends on BROKEN ---help--- This options enables support of Draft 802.11s mesh networking. - The implementation is based on Draft 1.08 of the Mesh Networking - amendment. For more information visit http://o11s.org/. - + The implementation is based on Draft 2.08 of the Mesh Networking + amendment. However, no compliance with that draft is claimed or even + possible, as drafts leave a number of identifiers to be defined after + ratification. For more information visit http://o11s.org/. config MAC80211_LEDS bool "Enable LED triggers" diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 1958c7c42cd..bd765f30dba 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -381,6 +381,14 @@ static void ieee80211_agg_splice_packets(struct ieee80211_local *local, &local->hw, queue, IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + if (!(sta->ampdu_mlme.tid_state_tx[tid] & HT_ADDBA_REQUESTED_MSK)) + return; + + if (WARN(!sta->ampdu_mlme.tid_tx[tid], + "TID %d gone but expected when splicing aggregates from" + "the pending queue\n", tid)) + return; + if (!skb_queue_empty(&sta->ampdu_mlme.tid_tx[tid]->pending)) { spin_lock_irqsave(&local->queue_stop_reason_lock, flags); /* copy over remaining packets */ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 4bbf5007799..5608f6c6841 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -323,6 +323,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = sta->sdata; + sinfo->generation = sdata->local->sta_generation; + sinfo->filled = STATION_INFO_INACTIVE_TIME | STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES | @@ -909,6 +911,8 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop, else memset(next_hop, 0, ETH_ALEN); + pinfo->generation = mesh_paths_generation; + pinfo->filled = MPATH_INFO_FRAME_QLEN | MPATH_INFO_DSN | MPATH_INFO_METRIC | diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index e9ec6cae2d3..61234e79022 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -116,6 +116,8 @@ IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); #ifdef CONFIG_MAC80211_MESH /* Mesh stats attributes */ +IEEE80211_IF_FILE(fwded_mcast, u.mesh.mshstats.fwded_mcast, DEC); +IEEE80211_IF_FILE(fwded_unicast, u.mesh.mshstats.fwded_unicast, DEC); IEEE80211_IF_FILE(fwded_frames, u.mesh.mshstats.fwded_frames, DEC); IEEE80211_IF_FILE(dropped_frames_ttl, u.mesh.mshstats.dropped_frames_ttl, DEC); IEEE80211_IF_FILE(dropped_frames_no_route, @@ -205,6 +207,8 @@ static void add_mesh_stats(struct ieee80211_sub_if_data *sdata) { sdata->mesh_stats_dir = debugfs_create_dir("mesh_stats", sdata->debugfsdir); + MESHSTATS_ADD(fwded_mcast); + MESHSTATS_ADD(fwded_unicast); MESHSTATS_ADD(fwded_frames); MESHSTATS_ADD(dropped_frames_ttl); MESHSTATS_ADD(dropped_frames_no_route); @@ -327,6 +331,8 @@ static void del_monitor_files(struct ieee80211_sub_if_data *sdata) static void del_mesh_stats(struct ieee80211_sub_if_data *sdata) { + MESHSTATS_DEL(fwded_mcast); + MESHSTATS_DEL(fwded_unicast); MESHSTATS_DEL(fwded_frames); MESHSTATS_DEL(dropped_frames_ttl); MESHSTATS_DEL(dropped_frames_no_route); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 4100c361a99..020a94a3110 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -12,7 +12,11 @@ static inline int drv_tx(struct ieee80211_local *local, struct sk_buff *skb) static inline int drv_start(struct ieee80211_local *local) { - int ret = local->ops->start(&local->hw); + int ret; + + local->started = true; + smp_mb(); + ret = local->ops->start(&local->hw); trace_drv_start(local, ret); return ret; } @@ -21,6 +25,14 @@ static inline void drv_stop(struct ieee80211_local *local) { local->ops->stop(&local->hw); trace_drv_stop(local); + + /* sync away all work on the tasklet before clearing started */ + tasklet_disable(&local->tasklet); + tasklet_enable(&local->tasklet); + + barrier(); + + local->started = false; } static inline int drv_add_interface(struct ieee80211_local *local, @@ -55,16 +67,32 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, trace_drv_bss_info_changed(local, vif, info, changed); } +static inline u64 drv_prepare_multicast(struct ieee80211_local *local, + int mc_count, + struct dev_addr_list *mc_list) +{ + u64 ret = 0; + + if (local->ops->prepare_multicast) + ret = local->ops->prepare_multicast(&local->hw, mc_count, + mc_list); + + trace_drv_prepare_multicast(local, mc_count, ret); + + return ret; +} + static inline void drv_configure_filter(struct ieee80211_local *local, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, - struct dev_addr_list *mc_list) + u64 multicast) { + might_sleep(); + local->ops->configure_filter(&local->hw, changed_flags, total_flags, - mc_count, mc_list); + multicast); trace_drv_configure_filter(local, changed_flags, total_flags, - mc_count); + multicast); } static inline int drv_set_tim(struct ieee80211_local *local, diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 5a10da2d70f..37b9051afcf 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -191,31 +191,55 @@ TRACE_EVENT(drv_bss_info_changed, ) ); +TRACE_EVENT(drv_prepare_multicast, + TP_PROTO(struct ieee80211_local *local, int mc_count, u64 ret), + + TP_ARGS(local, mc_count, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(int, mc_count) + __field(u64, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->mc_count = mc_count; + __entry->ret = ret; + ), + + TP_printk( + LOCAL_PR_FMT " prepare mc (%d): %llx", + LOCAL_PR_ARG, __entry->mc_count, + (unsigned long long) __entry->ret + ) +); + TRACE_EVENT(drv_configure_filter, TP_PROTO(struct ieee80211_local *local, unsigned int changed_flags, unsigned int *total_flags, - int mc_count), + u64 multicast), - TP_ARGS(local, changed_flags, total_flags, mc_count), + TP_ARGS(local, changed_flags, total_flags, multicast), TP_STRUCT__entry( LOCAL_ENTRY __field(unsigned int, changed) __field(unsigned int, total) - __field(int, mc) + __field(u64, multicast) ), TP_fast_assign( LOCAL_ASSIGN; __entry->changed = changed_flags; __entry->total = *total_flags; - __entry->mc = mc_count; + __entry->multicast = multicast; ), TP_printk( - LOCAL_PR_FMT " changed:%#x total:%#x mc:%d", - LOCAL_PR_ARG, __entry->changed, __entry->total, __entry->mc + LOCAL_PR_FMT " changed:%#x total:%#x", + LOCAL_PR_ARG, __entry->changed, __entry->total ) ); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 630a438180f..588005c84a6 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -212,7 +212,9 @@ struct ieee80211_if_vlan { }; struct mesh_stats { - __u32 fwded_frames; /* Mesh forwarded frames */ + __u32 fwded_mcast; /* Mesh forwarded multicast frames */ + __u32 fwded_unicast; /* Mesh forwarded unicast frames */ + __u32 fwded_frames; /* Mesh total forwarded frames */ __u32 dropped_frames_ttl; /* Not transmitted since mesh_ttl == 0*/ __u32 dropped_frames_no_route; /* Not transmitted, no route found */ atomic_t estab_plinks; @@ -284,6 +286,7 @@ struct ieee80211_if_managed { struct mutex mtx; struct ieee80211_bss *associated; + struct ieee80211_mgd_work *old_associate_work; struct list_head work_list; u8 bssid[ETH_ALEN]; @@ -354,7 +357,7 @@ struct ieee80211_if_mesh { unsigned long timers_running; - bool housekeeping; + unsigned long wrkq_flags; u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN]; size_t mesh_id_len; @@ -364,6 +367,10 @@ struct ieee80211_if_mesh { u8 mesh_pm_id[4]; /* Congestion Control Mode Identifier */ u8 mesh_cc_id[4]; + /* Synchronization Protocol Identifier */ + u8 mesh_sp_id[4]; + /* Authentication Protocol Identifier */ + u8 mesh_auth_id[4]; /* Local mesh Destination Sequence Number */ u32 dsn; /* Last used PREQ ID */ @@ -505,6 +512,8 @@ struct ieee80211_sub_if_data { #ifdef CONFIG_MAC80211_MESH struct dentry *mesh_stats_dir; struct { + struct dentry *fwded_mcast; + struct dentry *fwded_unicast; struct dentry *fwded_frames; struct dentry *dropped_frames_ttl; struct dentry *dropped_frames_no_route; @@ -635,6 +644,9 @@ struct ieee80211_local { /* protects the aggregated multicast list and filter calls */ spinlock_t filter_lock; + /* used for uploading changed mc list */ + struct work_struct reconfig_filter; + /* aggregated multicast list */ struct dev_addr_list *mc_list; int mc_count; @@ -655,6 +667,9 @@ struct ieee80211_local { */ bool quiescing; + /* device is started */ + bool started; + int tx_headroom; /* required headroom for hardware/radiotap */ /* Tasklet and skb queue to process calls from IRQ mode. All frames @@ -677,6 +692,7 @@ struct ieee80211_local { struct list_head sta_list; struct sta_info *sta_hash[STA_HASH_SIZE]; struct timer_list sta_cleanup; + int sta_generation; struct sk_buff_head pending[IEEE80211_MAX_QUEUES]; struct tasklet_struct tx_pending_tasklet; @@ -713,7 +729,7 @@ struct ieee80211_local { struct mutex scan_mtx; unsigned long scanning; struct cfg80211_ssid scan_ssid; - struct cfg80211_scan_request int_scan_req; + struct cfg80211_scan_request *int_scan_req; struct cfg80211_scan_request *scan_req; struct ieee80211_channel *scan_channel; const u8 *orig_ies; @@ -1034,8 +1050,10 @@ void ieee80211_recalc_idle(struct ieee80211_local *local); /* tx handling */ void ieee80211_clear_tx_pending(struct ieee80211_local *local); void ieee80211_tx_pending(unsigned long data); -int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev); -int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev); +netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, + struct net_device *dev); +netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, + struct net_device *dev); /* HT */ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband, @@ -1073,6 +1091,7 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, /* Suspend/resume and hw reconfiguration */ int ieee80211_reconfig(struct ieee80211_local *local); +void ieee80211_stop_device(struct ieee80211_local *local); #ifdef CONFIG_PM int __ieee80211_suspend(struct ieee80211_hw *hw); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index e8fb03b91a4..f6005adcbf9 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -227,9 +227,7 @@ static int ieee80211_open(struct net_device *dev) if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS) local->fif_other_bss++; - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); break; default: conf.vif = &sdata->vif; @@ -241,17 +239,13 @@ static int ieee80211_open(struct net_device *dev) if (ieee80211_vif_is_mesh(&sdata->vif)) { local->fif_other_bss++; - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); ieee80211_start_mesh(sdata); } else if (sdata->vif.type == NL80211_IFTYPE_AP) { local->fif_pspoll++; - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); } changed |= ieee80211_reset_erp_info(sdata); @@ -283,11 +277,6 @@ static int ieee80211_open(struct net_device *dev) } } - if (local->open_count == 0) { - tasklet_enable(&local->tx_pending_tasklet); - tasklet_enable(&local->tasklet); - } - /* * set_multicast_list will be invoked by the networking core * which will check whether any increments here were done in @@ -404,10 +393,11 @@ static int ieee80211_stop(struct net_device *dev) spin_lock_bh(&local->filter_lock); __dev_addr_unsync(&local->mc_list, &local->mc_count, &dev->mc_list, &dev->mc_count); - ieee80211_configure_filter(local); spin_unlock_bh(&local->filter_lock); netif_addr_unlock_bh(dev); + ieee80211_configure_filter(local); + del_timer_sync(&local->dynamic_ps_timer); cancel_work_sync(&local->dynamic_ps_enable_work); @@ -458,9 +448,7 @@ static int ieee80211_stop(struct net_device *dev) if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS) local->fif_other_bss--; - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); break; case NL80211_IFTYPE_STATION: del_timer_sync(&sdata->u.mgd.chswitch_timer); @@ -503,38 +491,14 @@ static int ieee80211_stop(struct net_device *dev) local->fif_other_bss--; atomic_dec(&local->iff_allmultis); - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); ieee80211_stop_mesh(sdata); } /* fall through */ default: - if (local->scan_sdata == sdata) { - if (!local->ops->hw_scan) - cancel_delayed_work_sync(&local->scan_work); - /* - * The software scan can no longer run now, so we can - * clear out the scan_sdata reference. However, the - * hardware scan may still be running. The complete - * function must be prepared to handle a NULL value. - */ - local->scan_sdata = NULL; - /* - * The memory barrier guarantees that another CPU - * that is hardware-scanning will now see the fact - * that this interface is gone. - */ - smp_mb(); - /* - * If software scanning, complete the scan but since - * the scan_sdata is NULL already don't send out a - * scan event to userspace -- the scan is incomplete. - */ - if (test_bit(SCAN_SW_SCANNING, &local->scanning)) - ieee80211_scan_completed(&local->hw, true); - } + if (local->scan_sdata == sdata) + ieee80211_scan_cancel(local); /* * Disable beaconing for AP and mesh, IBSS can't @@ -561,14 +525,8 @@ static int ieee80211_stop(struct net_device *dev) ieee80211_recalc_ps(local, -1); if (local->open_count == 0) { - drv_stop(local); - - ieee80211_led_radio(local, false); - - flush_workqueue(local->workqueue); - - tasklet_disable(&local->tx_pending_tasklet); - tasklet_disable(&local->tasklet); + ieee80211_clear_tx_pending(local); + ieee80211_stop_device(local); /* no reconfiguring after stop! */ hw_reconf_flags = 0; @@ -622,8 +580,8 @@ static void ieee80211_set_multicast_list(struct net_device *dev) spin_lock_bh(&local->filter_lock); __dev_addr_sync(&local->mc_list, &local->mc_count, &dev->mc_list, &dev->mc_count); - ieee80211_configure_filter(local); spin_unlock_bh(&local->filter_lock); + ieee80211_queue_work(&local->hw, &local->reconfig_filter); } /* diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 0c4f8e122ed..797f53942e5 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -50,9 +50,9 @@ struct ieee80211_tx_status_rtap_hdr { } __attribute__ ((packed)); -/* must be called under mdev tx lock */ void ieee80211_configure_filter(struct ieee80211_local *local) { + u64 mc; unsigned int changed_flags; unsigned int new_flags = 0; @@ -62,7 +62,7 @@ void ieee80211_configure_filter(struct ieee80211_local *local) if (atomic_read(&local->iff_allmultis)) new_flags |= FIF_ALLMULTI; - if (local->monitors) + if (local->monitors || local->scanning) new_flags |= FIF_BCN_PRBRESP_PROMISC; if (local->fif_fcsfail) @@ -80,20 +80,30 @@ void ieee80211_configure_filter(struct ieee80211_local *local) if (local->fif_pspoll) new_flags |= FIF_PSPOLL; + spin_lock_bh(&local->filter_lock); changed_flags = local->filter_flags ^ new_flags; + mc = drv_prepare_multicast(local, local->mc_count, local->mc_list); + spin_unlock_bh(&local->filter_lock); + /* be a bit nasty */ new_flags |= (1<<31); - drv_configure_filter(local, changed_flags, &new_flags, - local->mc_count, - local->mc_list); + drv_configure_filter(local, changed_flags, &new_flags, mc); WARN_ON(new_flags & (1<<31)); local->filter_flags = new_flags & ~(1<<31); } +static void ieee80211_reconfig_filter(struct work_struct *work) +{ + struct ieee80211_local *local = + container_of(work, struct ieee80211_local, reconfig_filter); + + ieee80211_configure_filter(local); +} + int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) { struct ieee80211_channel *chan, *scan_chan; @@ -231,9 +241,6 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, drv_bss_info_changed(local, &sdata->vif, &sdata->vif.bss_conf, changed); - - /* DEPRECATED */ - local->hw.conf.beacon_int = sdata->vif.bss_conf.beacon_int; } u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata) @@ -475,6 +482,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) } rate_control_tx_status(local, sband, sta, skb); + if (ieee80211_vif_is_mesh(&sta->sdata->vif)) + ieee80211s_update_metric(local, sta, skb); } rcu_read_unlock(); @@ -677,7 +686,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, local->hw.max_rates = 1; local->hw.conf.long_frame_max_tx_count = wiphy->retry_long; local->hw.conf.short_frame_max_tx_count = wiphy->retry_short; - local->hw.conf.radio_enabled = true; local->user_power_level = -1; INIT_LIST_HEAD(&local->interfaces); @@ -692,6 +700,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, INIT_WORK(&local->restart_work, ieee80211_restart_work); + INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); + INIT_WORK(&local->dynamic_ps_enable_work, ieee80211_dynamic_ps_enable_work); INIT_WORK(&local->dynamic_ps_disable_work, @@ -705,12 +715,10 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, skb_queue_head_init(&local->pending[i]); tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending, (unsigned long)local); - tasklet_disable(&local->tx_pending_tasklet); tasklet_init(&local->tasklet, ieee80211_tasklet_handler, (unsigned long) local); - tasklet_disable(&local->tasklet); skb_queue_head_init(&local->skb_queue); skb_queue_head_init(&local->skb_queue_unreliable); @@ -765,9 +773,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) supp_ht = supp_ht || sband->ht_cap.ht_supported; } - local->int_scan_req.n_channels = channels; - local->int_scan_req.channels = kzalloc(sizeof(void *) * channels, GFP_KERNEL); - if (!local->int_scan_req.channels) + local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) + + sizeof(void *) * channels, GFP_KERNEL); + if (!local->int_scan_req) return -ENOMEM; /* if low-level driver supports AP, we also support VLAN */ @@ -882,13 +890,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) /* alloc internal scan request */ i = 0; - local->int_scan_req.ssids = &local->scan_ssid; - local->int_scan_req.n_ssids = 1; + local->int_scan_req->ssids = &local->scan_ssid; + local->int_scan_req->n_ssids = 1; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!hw->wiphy->bands[band]) continue; for (j = 0; j < hw->wiphy->bands[band]->n_channels; j++) { - local->int_scan_req.channels[i] = + local->int_scan_req->channels[i] = &hw->wiphy->bands[band]->channels[j]; i++; } @@ -920,7 +928,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) fail_workqueue: wiphy_unregister(local->hw.wiphy); fail_wiphy_register: - kfree(local->int_scan_req.channels); + kfree(local->int_scan_req); return result; } EXPORT_SYMBOL(ieee80211_register_hw); @@ -946,6 +954,8 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) rtnl_unlock(); + cancel_work_sync(&local->reconfig_filter); + ieee80211_clear_tx_pending(local); sta_info_stop(local); rate_control_deinitialize(local); @@ -962,7 +972,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) wiphy_unregister(local->hw.wiphy); ieee80211_wep_free(local); ieee80211_led_exit(local); - kfree(local->int_scan_req.channels); + kfree(local->int_scan_req); } EXPORT_SYMBOL(ieee80211_unregister_hw); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 2f4f518ab45..f7364e56f1e 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -18,8 +18,11 @@ #define PP_OFFSET 1 /* Path Selection Protocol */ #define PM_OFFSET 5 /* Path Selection Metric */ #define CC_OFFSET 9 /* Congestion Control Mode */ -#define CAPAB_OFFSET 17 -#define ACCEPT_PLINKS 0x80 +#define SP_OFFSET 13 /* Synchronization Protocol */ +#define AUTH_OFFSET 17 /* Authentication Protocol */ +#define CAPAB_OFFSET 22 +#define CAPAB_ACCEPT_PLINKS 0x80 +#define CAPAB_FORWARDING 0x10 #define TMR_RUNNING_HK 0 #define TMR_RUNNING_MP 1 @@ -47,14 +50,14 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data) struct ieee80211_local *local = sdata->local; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; - ifmsh->housekeeping = true; + ifmsh->wrkq_flags |= MESH_WORK_HOUSEKEEPING; if (local->quiescing) { set_bit(TMR_RUNNING_HK, &ifmsh->timers_running); return; } - ieee80211_queue_work(local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(&local->hw, &ifmsh->work); } /** @@ -84,7 +87,9 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_dat memcmp(ifmsh->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 && memcmp(ifmsh->mesh_pp_id, ie->mesh_config + PP_OFFSET, 4) == 0 && memcmp(ifmsh->mesh_pm_id, ie->mesh_config + PM_OFFSET, 4) == 0 && - memcmp(ifmsh->mesh_cc_id, ie->mesh_config + CC_OFFSET, 4) == 0) + memcmp(ifmsh->mesh_cc_id, ie->mesh_config + CC_OFFSET, 4) == 0 && + memcmp(ifmsh->mesh_sp_id, ie->mesh_config + SP_OFFSET, 4) == 0 && + memcmp(ifmsh->mesh_auth_id, ie->mesh_config + AUTH_OFFSET, 4) == 0) return true; return false; @@ -97,7 +102,7 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_dat */ bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie) { - return (*(ie->mesh_config + CAPAB_OFFSET) & ACCEPT_PLINKS) != 0; + return (*(ie->mesh_config + CAPAB_OFFSET) & CAPAB_ACCEPT_PLINKS) != 0; } /** @@ -123,11 +128,18 @@ void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata) void mesh_ids_set_default(struct ieee80211_if_mesh *sta) { - u8 def_id[4] = {0x00, 0x0F, 0xAC, 0xff}; - - memcpy(sta->mesh_pp_id, def_id, 4); - memcpy(sta->mesh_pm_id, def_id, 4); - memcpy(sta->mesh_cc_id, def_id, 4); + u8 oui[3] = {0x00, 0x0F, 0xAC}; + + memcpy(sta->mesh_pp_id, oui, sizeof(oui)); + memcpy(sta->mesh_pm_id, oui, sizeof(oui)); + memcpy(sta->mesh_cc_id, oui, sizeof(oui)); + memcpy(sta->mesh_sp_id, oui, sizeof(oui)); + memcpy(sta->mesh_auth_id, oui, sizeof(oui)); + sta->mesh_pp_id[sizeof(oui)] = 0; + sta->mesh_pm_id[sizeof(oui)] = 0; + sta->mesh_cc_id[sizeof(oui)] = 0xff; + sta->mesh_sp_id[sizeof(oui)] = 0xff; + sta->mesh_auth_id[sizeof(oui)] = 0x0; } int mesh_rmc_init(struct ieee80211_sub_if_data *sdata) @@ -245,7 +257,7 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) if (sdata->u.mesh.mesh_id_len) memcpy(pos, sdata->u.mesh.mesh_id, sdata->u.mesh.mesh_id_len); - pos = skb_put(skb, 21); + pos = skb_put(skb, 2 + IEEE80211_MESH_CONFIG_LEN); *pos++ = WLAN_EID_MESH_CONFIG; *pos++ = IEEE80211_MESH_CONFIG_LEN; /* Version */ @@ -263,15 +275,22 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) memcpy(pos, sdata->u.mesh.mesh_cc_id, 4); pos += 4; - /* Channel precedence: - * Not running simple channel unification protocol - */ - memset(pos, 0x00, 4); + /* Synchronization protocol identifier */ + memcpy(pos, sdata->u.mesh.mesh_sp_id, 4); pos += 4; + /* Authentication Protocol identifier */ + memcpy(pos, sdata->u.mesh.mesh_auth_id, 4); + pos += 4; + + /* Mesh Formation Info */ + memset(pos, 0x00, 1); + pos += 1; + /* Mesh capability */ sdata->u.mesh.accepting_plinks = mesh_plink_availables(sdata); - *pos++ = sdata->u.mesh.accepting_plinks ? ACCEPT_PLINKS : 0x00; + *pos = CAPAB_FORWARDING; + *pos++ |= sdata->u.mesh.accepting_plinks ? CAPAB_ACCEPT_PLINKS : 0x00; *pos++ = 0x00; return; @@ -320,30 +339,6 @@ struct mesh_table *mesh_table_alloc(int size_order) return newtbl; } -static void __mesh_table_free(struct mesh_table *tbl) -{ - kfree(tbl->hash_buckets); - kfree(tbl->hashwlock); - kfree(tbl); -} - -void mesh_table_free(struct mesh_table *tbl, bool free_leafs) -{ - struct hlist_head *mesh_hash; - struct hlist_node *p, *q; - int i; - - mesh_hash = tbl->hash_buckets; - for (i = 0; i <= tbl->hash_mask; i++) { - spin_lock(&tbl->hashwlock[i]); - hlist_for_each_safe(p, q, &mesh_hash[i]) { - tbl->free_node(p, free_leafs); - atomic_dec(&tbl->entries); - } - spin_unlock(&tbl->hashwlock[i]); - } - __mesh_table_free(tbl); -} static void ieee80211_mesh_path_timer(unsigned long data) { @@ -357,63 +352,79 @@ static void ieee80211_mesh_path_timer(unsigned long data) return; } - ieee80211_queue_work(local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(&local->hw, &ifmsh->work); } -struct mesh_table *mesh_table_grow(struct mesh_table *tbl) -{ - struct mesh_table *newtbl; - struct hlist_head *oldhash; - struct hlist_node *p, *q; - int i; - - if (atomic_read(&tbl->entries) - < tbl->mean_chain_len * (tbl->hash_mask + 1)) - goto endgrow; - - newtbl = mesh_table_alloc(tbl->size_order + 1); - if (!newtbl) - goto endgrow; - - newtbl->free_node = tbl->free_node; - newtbl->mean_chain_len = tbl->mean_chain_len; - newtbl->copy_node = tbl->copy_node; - atomic_set(&newtbl->entries, atomic_read(&tbl->entries)); - - oldhash = tbl->hash_buckets; - for (i = 0; i <= tbl->hash_mask; i++) - hlist_for_each(p, &oldhash[i]) - if (tbl->copy_node(p, newtbl) < 0) - goto errcopy; - - return newtbl; - -errcopy: - for (i = 0; i <= newtbl->hash_mask; i++) { - hlist_for_each_safe(p, q, &newtbl->hash_buckets[i]) - tbl->free_node(p, 0); +/** + * ieee80211_fill_mesh_addresses - fill addresses of a locally originated mesh frame + * @hdr: 802.11 frame header + * @fc: frame control field + * @meshda: destination address in the mesh + * @meshsa: source address address in the mesh. Same as TA, as frame is + * locally originated. + * + * Return the length of the 802.11 (does not include a mesh control header) + */ +int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, char + *meshda, char *meshsa) { + if (is_multicast_ether_addr(meshda)) { + *fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); + /* DA TA SA */ + memcpy(hdr->addr1, meshda, ETH_ALEN); + memcpy(hdr->addr2, meshsa, ETH_ALEN); + memcpy(hdr->addr3, meshsa, ETH_ALEN); + return 24; + } else { + *fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | + IEEE80211_FCTL_TODS); + /* RA TA DA SA */ + memset(hdr->addr1, 0, ETH_ALEN); /* RA is resolved later */ + memcpy(hdr->addr2, meshsa, ETH_ALEN); + memcpy(hdr->addr3, meshda, ETH_ALEN); + memcpy(hdr->addr4, meshsa, ETH_ALEN); + return 30; } - __mesh_table_free(newtbl); -endgrow: - return NULL; } /** * ieee80211_new_mesh_header - create a new mesh header * @meshhdr: uninitialized mesh header * @sdata: mesh interface to be used + * @addr4: addr4 of the mesh frame (1st in ae header) + * may be NULL + * @addr5: addr5 of the mesh frame (1st or 2nd in ae header) + * may be NULL unless addr6 is present + * @addr6: addr6 of the mesh frame (2nd or 3rd in ae header) + * may be NULL unless addr5 is present * * Return the header length. */ int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr, - struct ieee80211_sub_if_data *sdata) + struct ieee80211_sub_if_data *sdata, char *addr4, + char *addr5, char *addr6) { - meshhdr->flags = 0; + int aelen = 0; + memset(meshhdr, 0, sizeof(meshhdr)); meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL; put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &meshhdr->seqnum); sdata->u.mesh.mesh_seqnum++; - - return 6; + if (addr4) { + meshhdr->flags |= MESH_FLAGS_AE_A4; + aelen += ETH_ALEN; + memcpy(meshhdr->eaddr1, addr4, ETH_ALEN); + } + if (addr5 && addr6) { + meshhdr->flags |= MESH_FLAGS_AE_A5_A6; + aelen += 2 * ETH_ALEN; + if (!addr4) { + memcpy(meshhdr->eaddr1, addr5, ETH_ALEN); + memcpy(meshhdr->eaddr2, addr6, ETH_ALEN); + } else { + memcpy(meshhdr->eaddr2, addr5, ETH_ALEN); + memcpy(meshhdr->eaddr3, addr6, ETH_ALEN); + } + } + return 6 + aelen; } static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata, @@ -433,7 +444,6 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata, if (free_plinks != sdata->u.mesh.accepting_plinks) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); - ifmsh->housekeeping = false; mod_timer(&ifmsh->housekeeping_timer, round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL)); } @@ -470,10 +480,12 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; - ifmsh->housekeeping = true; - ieee80211_queue_work(local->hw.workqueue, &ifmsh->work); + ifmsh->wrkq_flags |= MESH_WORK_HOUSEKEEPING; + ieee80211_queue_work(&local->hw, &ifmsh->work); + sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL; ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON | - BSS_CHANGED_BEACON_ENABLED); + BSS_CHANGED_BEACON_ENABLED | + BSS_CHANGED_BEACON_INT); } void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) @@ -608,7 +620,13 @@ static void ieee80211_mesh_work(struct work_struct *work) ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval))) mesh_path_start_discovery(sdata); - if (ifmsh->housekeeping) + if (test_and_clear_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags)) + mesh_mpath_table_grow(); + + if (test_and_clear_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags)) + mesh_mpp_table_grow(); + + if (test_and_clear_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags)) ieee80211_mesh_housekeeping(sdata, ifmsh); } @@ -619,7 +637,7 @@ void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) rcu_read_lock(); list_for_each_entry_rcu(sdata, &local->interfaces, list) if (ieee80211_vif_is_mesh(&sdata->vif)) - ieee80211_queue_work(local->hw.workqueue, &sdata->u.mesh.work); + ieee80211_queue_work(&local->hw, &sdata->u.mesh.work); rcu_read_unlock(); } @@ -692,7 +710,7 @@ ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) case IEEE80211_STYPE_PROBE_RESP: case IEEE80211_STYPE_BEACON: skb_queue_tail(&ifmsh->skb_queue, skb); - ieee80211_queue_work(local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(&local->hw, &ifmsh->work); return RX_QUEUED; } diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 2a2ed182cb7..dd1c19319f0 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -44,6 +44,23 @@ enum mesh_path_flags { }; /** + * enum mesh_deferred_task_flags - mac80211 mesh deferred tasks + * + * + * + * @MESH_WORK_HOUSEKEEPING: run the periodic mesh housekeeping tasks + * @MESH_WORK_GROW_MPATH_TABLE: the mesh path table is full and needs + * to grow. + * @MESH_WORK_GROW_MPP_TABLE: the mesh portals table is full and needs to + * grow + */ +enum mesh_deferred_task_flags { + MESH_WORK_HOUSEKEEPING, + MESH_WORK_GROW_MPATH_TABLE, + MESH_WORK_GROW_MPP_TABLE, +}; + +/** * struct mesh_path - mac80211 mesh path structure * * @dst: mesh path destination mac address @@ -61,7 +78,7 @@ enum mesh_path_flags { * retry * @discovery_retries: number of discovery retries * @flags: mesh path flags, as specified on &enum mesh_path_flags - * @state_lock: mesh pat state lock + * @state_lock: mesh path state lock * * * The combination of dst and sdata is unique in the mesh path table. Since the @@ -174,6 +191,7 @@ struct mesh_rmc { */ #define MESH_PATH_REFRESH_TIME 1000 #define MESH_MIN_DISCOVERY_TIMEOUT (2 * MESH_DIAM_TRAVERSAL_TIME) +#define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units */ #define MESH_MAX_PREQ_RETRIES 4 #define MESH_PATH_EXPIRE (600 * HZ) @@ -193,8 +211,11 @@ struct mesh_rmc { /* Public interfaces */ /* Various */ +int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, + char *da, char *sa); int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr, - struct ieee80211_sub_if_data *sdata); + struct ieee80211_sub_if_data *sdata, char *addr4, + char *addr5, char *addr6); int mesh_rmc_check(u8 *addr, struct ieee80211s_hdr *mesh_hdr, struct ieee80211_sub_if_data *sdata); bool mesh_matches_local(struct ieee802_11_elems *ie, @@ -205,6 +226,8 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, void mesh_rmc_free(struct ieee80211_sub_if_data *sdata); int mesh_rmc_init(struct ieee80211_sub_if_data *sdata); void ieee80211s_init(void); +void ieee80211s_update_metric(struct ieee80211_local *local, + struct sta_info *stainfo, struct sk_buff *skb); void ieee80211s_stop(void); void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata); ieee80211_rx_result @@ -246,7 +269,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, /* Mesh tables */ struct mesh_table *mesh_table_alloc(int size_order); void mesh_table_free(struct mesh_table *tbl, bool free_leafs); -struct mesh_table *mesh_table_grow(struct mesh_table *tbl); +void mesh_mpath_table_grow(void); +void mesh_mpp_table_grow(void); u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata, struct mesh_table *tbl); /* Mesh paths */ @@ -265,6 +289,8 @@ void mesh_path_discard_frame(struct sk_buff *skb, void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata); void mesh_path_restart(struct ieee80211_sub_if_data *sdata); +extern int mesh_paths_generation; + #ifdef CONFIG_MAC80211_MESH extern int mesh_allocated; diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 11ab71a68ff..e12a786e26b 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -201,6 +201,24 @@ int mesh_path_error_tx(u8 *dst, __le32 dst_dsn, u8 *ra, return 0; } +void ieee80211s_update_metric(struct ieee80211_local *local, + struct sta_info *stainfo, struct sk_buff *skb) +{ + struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + int failed; + + if (!ieee80211_is_data(hdr->frame_control)) + return; + + failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK); + + /* moving average, scaled to 100 */ + stainfo->fail_avg = ((80 * stainfo->fail_avg + 5) / 100 + 20 * failed); + if (stainfo->fail_avg > 95) + mesh_plink_broken(stainfo); +} + static u32 airtime_link_metric_get(struct ieee80211_local *local, struct sta_info *sta) { @@ -397,7 +415,8 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, - u8 *preq_elem, u32 metric) { + u8 *preq_elem, u32 metric) +{ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct mesh_path *mpath; u8 *dst_addr, *orig_addr; @@ -430,7 +449,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, if ((!(mpath->flags & MESH_PATH_DSN_VALID)) || DSN_LT(mpath->dsn, dst_dsn)) { mpath->dsn = dst_dsn; - mpath->flags &= MESH_PATH_DSN_VALID; + mpath->flags |= MESH_PATH_DSN_VALID; } else if ((!(dst_flags & MP_F_DO)) && (mpath->flags & MESH_PATH_ACTIVE)) { reply = true; @@ -478,6 +497,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, hopcount, ttl, cpu_to_le32(lifetime), cpu_to_le32(metric), cpu_to_le32(preq_id), sdata); + ifmsh->mshstats.fwded_mcast++; ifmsh->mshstats.fwded_frames++; } } @@ -536,6 +556,8 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, cpu_to_le32(lifetime), cpu_to_le32(metric), 0, sdata); rcu_read_unlock(); + + sdata->u.mesh.mshstats.fwded_unicast++; sdata->u.mesh.mshstats.fwded_frames++; return; @@ -660,14 +682,14 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags) spin_unlock(&ifmsh->mesh_preq_queue_lock); if (time_after(jiffies, ifmsh->last_preq + min_preq_int_jiff(sdata))) - ieee80211_queue_work(sdata->local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(&sdata->local->hw, &ifmsh->work); else if (time_before(jiffies, ifmsh->last_preq)) { /* avoid long wait if did not send preqs for a long time * and jiffies wrapped around */ ifmsh->last_preq = jiffies - min_preq_int_jiff(sdata) - 1; - ieee80211_queue_work(sdata->local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(&sdata->local->hw, &ifmsh->work); } else mod_timer(&ifmsh->mesh_path_timer, ifmsh->last_preq + min_preq_int_jiff(sdata)); @@ -791,7 +813,7 @@ int mesh_nexthop_lookup(struct sk_buff *skb, } if (mpath->flags & MESH_PATH_ACTIVE) { - if (time_after(jiffies, mpath->exp_time - + if (time_after(jiffies, mpath->exp_time + msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time)) && !memcmp(sdata->dev->dev_addr, hdr->addr4, ETH_ALEN) @@ -810,10 +832,8 @@ int mesh_nexthop_lookup(struct sk_buff *skb, } if (skb_queue_len(&mpath->frame_queue) >= - MESH_FRAME_QUEUE_LEN) { - skb_to_free = mpath->frame_queue.next; - skb_unlink(skb_to_free, &mpath->frame_queue); - } + MESH_FRAME_QUEUE_LEN) + skb_to_free = skb_dequeue(&mpath->frame_queue); info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; skb_queue_tail(&mpath->frame_queue, skb); diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 04b9e4d61b8..751c4d0e2b3 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -38,6 +38,71 @@ struct mpath_node { static struct mesh_table *mesh_paths; static struct mesh_table *mpp_paths; /* Store paths for MPP&MAP */ +int mesh_paths_generation; +static void __mesh_table_free(struct mesh_table *tbl) +{ + kfree(tbl->hash_buckets); + kfree(tbl->hashwlock); + kfree(tbl); +} + +void mesh_table_free(struct mesh_table *tbl, bool free_leafs) +{ + struct hlist_head *mesh_hash; + struct hlist_node *p, *q; + int i; + + mesh_hash = tbl->hash_buckets; + for (i = 0; i <= tbl->hash_mask; i++) { + spin_lock(&tbl->hashwlock[i]); + hlist_for_each_safe(p, q, &mesh_hash[i]) { + tbl->free_node(p, free_leafs); + atomic_dec(&tbl->entries); + } + spin_unlock(&tbl->hashwlock[i]); + } + __mesh_table_free(tbl); +} + +static struct mesh_table *mesh_table_grow(struct mesh_table *tbl) +{ + struct mesh_table *newtbl; + struct hlist_head *oldhash; + struct hlist_node *p, *q; + int i; + + if (atomic_read(&tbl->entries) + < tbl->mean_chain_len * (tbl->hash_mask + 1)) + goto endgrow; + + newtbl = mesh_table_alloc(tbl->size_order + 1); + if (!newtbl) + goto endgrow; + + newtbl->free_node = tbl->free_node; + newtbl->mean_chain_len = tbl->mean_chain_len; + newtbl->copy_node = tbl->copy_node; + atomic_set(&newtbl->entries, atomic_read(&tbl->entries)); + + oldhash = tbl->hash_buckets; + for (i = 0; i <= tbl->hash_mask; i++) + hlist_for_each(p, &oldhash[i]) + if (tbl->copy_node(p, newtbl) < 0) + goto errcopy; + + return newtbl; + +errcopy: + for (i = 0; i <= newtbl->hash_mask; i++) { + hlist_for_each_safe(p, q, &newtbl->hash_buckets[i]) + tbl->free_node(p, 0); + } + __mesh_table_free(newtbl); +endgrow: + return NULL; +} + + /* This lock will have the grow table function as writer and add / delete nodes * as readers. When reading the table (i.e. doing lookups) we are well protected * by RCU @@ -185,6 +250,8 @@ struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data */ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) { + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct ieee80211_local *local = sdata->local; struct mesh_path *mpath, *new_mpath; struct mpath_node *node, *new_node; struct hlist_head *bucket; @@ -193,8 +260,6 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) int err = 0; u32 hash_idx; - might_sleep(); - if (memcmp(dst, sdata->dev->dev_addr, ETH_ALEN) == 0) /* never add ourselves as neighbours */ return -ENOTSUPP; @@ -206,11 +271,11 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) return -ENOSPC; err = -ENOMEM; - new_mpath = kzalloc(sizeof(struct mesh_path), GFP_KERNEL); + new_mpath = kzalloc(sizeof(struct mesh_path), GFP_ATOMIC); if (!new_mpath) goto err_path_alloc; - new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL); + new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC); if (!new_node) goto err_node_alloc; @@ -243,23 +308,13 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) mesh_paths->mean_chain_len * (mesh_paths->hash_mask + 1)) grow = 1; + mesh_paths_generation++; + spin_unlock(&mesh_paths->hashwlock[hash_idx]); read_unlock(&pathtbl_resize_lock); if (grow) { - struct mesh_table *oldtbl, *newtbl; - - write_lock(&pathtbl_resize_lock); - oldtbl = mesh_paths; - newtbl = mesh_table_grow(mesh_paths); - if (!newtbl) { - write_unlock(&pathtbl_resize_lock); - return 0; - } - rcu_assign_pointer(mesh_paths, newtbl); - write_unlock(&pathtbl_resize_lock); - - synchronize_rcu(); - mesh_table_free(oldtbl, false); + set_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags); + ieee80211_queue_work(&local->hw, &ifmsh->work); } return 0; @@ -274,9 +329,46 @@ err_path_alloc: return err; } +void mesh_mpath_table_grow(void) +{ + struct mesh_table *oldtbl, *newtbl; + + write_lock(&pathtbl_resize_lock); + oldtbl = mesh_paths; + newtbl = mesh_table_grow(mesh_paths); + if (!newtbl) { + write_unlock(&pathtbl_resize_lock); + return; + } + rcu_assign_pointer(mesh_paths, newtbl); + write_unlock(&pathtbl_resize_lock); + + synchronize_rcu(); + mesh_table_free(oldtbl, false); +} + +void mesh_mpp_table_grow(void) +{ + struct mesh_table *oldtbl, *newtbl; + + write_lock(&pathtbl_resize_lock); + oldtbl = mpp_paths; + newtbl = mesh_table_grow(mpp_paths); + if (!newtbl) { + write_unlock(&pathtbl_resize_lock); + return; + } + rcu_assign_pointer(mpp_paths, newtbl); + write_unlock(&pathtbl_resize_lock); + + synchronize_rcu(); + mesh_table_free(oldtbl, false); +} int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) { + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct ieee80211_local *local = sdata->local; struct mesh_path *mpath, *new_mpath; struct mpath_node *node, *new_node; struct hlist_head *bucket; @@ -285,8 +377,6 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) int err = 0; u32 hash_idx; - might_sleep(); - if (memcmp(dst, sdata->dev->dev_addr, ETH_ALEN) == 0) /* never add ourselves as neighbours */ return -ENOTSUPP; @@ -295,11 +385,11 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) return -ENOTSUPP; err = -ENOMEM; - new_mpath = kzalloc(sizeof(struct mesh_path), GFP_KERNEL); + new_mpath = kzalloc(sizeof(struct mesh_path), GFP_ATOMIC); if (!new_mpath) goto err_path_alloc; - new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL); + new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC); if (!new_node) goto err_node_alloc; @@ -333,20 +423,8 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) spin_unlock(&mpp_paths->hashwlock[hash_idx]); read_unlock(&pathtbl_resize_lock); if (grow) { - struct mesh_table *oldtbl, *newtbl; - - write_lock(&pathtbl_resize_lock); - oldtbl = mpp_paths; - newtbl = mesh_table_grow(mpp_paths); - if (!newtbl) { - write_unlock(&pathtbl_resize_lock); - return 0; - } - rcu_assign_pointer(mpp_paths, newtbl); - write_unlock(&pathtbl_resize_lock); - - synchronize_rcu(); - mesh_table_free(oldtbl, false); + set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags); + ieee80211_queue_work(&local->hw, &ifmsh->work); } return 0; @@ -484,6 +562,7 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) err = -ENXIO; enddel: + mesh_paths_generation++; spin_unlock(&mesh_paths->hashwlock[hash_idx]); read_unlock(&pathtbl_resize_lock); return err; diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index cb14253587f..ffcbad75e09 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -409,7 +409,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m baselen = (u8 *) mgmt->u.action.u.plink_action.variable - (u8 *) mgmt; if (mgmt->u.action.u.plink_action.action_code == PLINK_CONFIRM) { baseaddr += 4; - baselen -= 4; + baselen += 4; } ieee802_11_parse_elems(baseaddr, len - baselen, &elems); if (!elems.peer_link) { diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6d5a1ee0445..97a278a2f48 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -880,10 +880,11 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, } static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, - struct ieee80211_bss *bss, + struct ieee80211_mgd_work *wk, u32 bss_info_changed) { struct ieee80211_local *local = sdata->local; + struct ieee80211_bss *bss = wk->bss; bss_info_changed |= BSS_CHANGED_ASSOC; /* set timing information */ @@ -896,6 +897,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, bss->cbss.capability, bss->has_erp_value, bss->erp_value); sdata->u.mgd.associated = bss; + sdata->u.mgd.old_associate_work = wk; memcpy(sdata->u.mgd.bssid, bss->cbss.bssid, ETH_ALEN); /* just to be sure */ @@ -1010,7 +1012,8 @@ ieee80211_authenticate(struct ieee80211_sub_if_data *sdata, return RX_MGMT_NONE; } -static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata) +static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, + bool deauth) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; @@ -1028,6 +1031,16 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata) ifmgd->associated = NULL; memset(ifmgd->bssid, 0, ETH_ALEN); + if (deauth) { + kfree(ifmgd->old_associate_work); + ifmgd->old_associate_work = NULL; + } else { + struct ieee80211_mgd_work *wk = ifmgd->old_associate_work; + + wk->state = IEEE80211_MGD_STATE_IDLE; + list_add(&wk->list, &ifmgd->work_list); + } + /* * we need to commit the associated = NULL change because the * scan code uses that to determine whether this iface should @@ -1345,7 +1358,7 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, sdata->dev->name, bssid, reason_code); if (!wk) { - ieee80211_set_disassoc(sdata); + ieee80211_set_disassoc(sdata, true); } else { list_del(&wk->list); kfree(wk); @@ -1378,7 +1391,7 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n", sdata->dev->name, reason_code); - ieee80211_set_disassoc(sdata); + ieee80211_set_disassoc(sdata, false); return RX_MGMT_CFG80211_DISASSOC; } @@ -1581,7 +1594,8 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, * ieee80211_set_associated() will tell the driver */ bss_conf->aid = aid; bss_conf->assoc_capability = capab_info; - ieee80211_set_associated(sdata, wk->bss, changed); + /* this will take ownership of wk */ + ieee80211_set_associated(sdata, wk, changed); /* * Start timer to probe the connection to the AP now. @@ -1590,7 +1604,6 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt); mod_beacon_timer(sdata); - kfree(wk); return RX_MGMT_CFG80211_ASSOC; } @@ -2096,7 +2109,7 @@ static void ieee80211_sta_work(struct work_struct *work) printk(KERN_DEBUG "No probe response from AP %pM" " after %dms, disconnecting.\n", bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ); - ieee80211_set_disassoc(sdata); + ieee80211_set_disassoc(sdata, true); mutex_unlock(&ifmgd->mtx); /* * must be outside lock due to cfg80211, @@ -2110,25 +2123,9 @@ static void ieee80211_sta_work(struct work_struct *work) } } - list_for_each_entry(wk, &ifmgd->work_list, list) { - if (wk->state != IEEE80211_MGD_STATE_IDLE) { - anybusy = true; - break; - } - } ieee80211_recalc_idle(local); - if (!anybusy) { - mutex_unlock(&ifmgd->mtx); - - if (test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request)) - ieee80211_queue_delayed_work(&local->hw, - &local->scan_work, - round_jiffies_relative(0)); - return; - } - list_for_each_entry_safe(wk, tmp, &ifmgd->work_list, list) { if (time_is_after_jiffies(wk->timeout)) { /* @@ -2174,6 +2171,18 @@ static void ieee80211_sta_work(struct work_struct *work) } } + list_for_each_entry(wk, &ifmgd->work_list, list) { + if (wk->state != IEEE80211_MGD_STATE_IDLE) { + anybusy = true; + break; + } + } + if (!anybusy && + test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request)) + ieee80211_queue_delayed_work(&local->hw, + &local->scan_work, + round_jiffies_relative(0)); + mutex_unlock(&ifmgd->mtx); list_for_each_entry_safe(wk, tmp, &free_work, list) { @@ -2500,7 +2509,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, if (ifmgd->associated && &ifmgd->associated->cbss == req->bss) { bssid = req->bss->bssid; - ieee80211_set_disassoc(sdata); + ieee80211_set_disassoc(sdata, true); } else list_for_each_entry(wk, &ifmgd->work_list, list) { if (&wk->bss->cbss == req->bss) { bssid = req->bss->bssid; @@ -2552,7 +2561,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, return -ENOLINK; } - ieee80211_set_disassoc(sdata); + ieee80211_set_disassoc(sdata, false); mutex_unlock(&ifmgd->mtx); diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index a5d2f1fb441..e535f1c988f 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -107,17 +107,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) } /* stop hardware - this must stop RX */ - if (local->open_count) { - ieee80211_led_radio(local, false); - drv_stop(local); - } - - /* - * flush again, in case driver queued work -- it - * shouldn't be doing (or cancel everything in the - * stop callback) that but better safe than sorry. - */ - flush_workqueue(local->workqueue); + if (local->open_count) + ieee80211_stop_device(local); local->suspended = true; /* need suspended to be visible before quiescing is false */ diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h index 869fe0ef951..38bf4168fc3 100644 --- a/net/mac80211/rc80211_minstrel.h +++ b/net/mac80211/rc80211_minstrel.h @@ -33,7 +33,6 @@ struct minstrel_rate { /* per-rate throughput */ u32 cur_tp; - u32 throughput; u64 succ_hist; u64 att_hist; diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c index 98f48070805..a715d9454f6 100644 --- a/net/mac80211/rc80211_minstrel_debugfs.c +++ b/net/mac80211/rc80211_minstrel_debugfs.c @@ -83,7 +83,7 @@ minstrel_stats_open(struct inode *inode, struct file *file) p += sprintf(p, "%3u%s", mr->bitrate / 2, (mr->bitrate & 1 ? ".5" : " ")); - tp = ((mr->cur_tp * 96) / 18000) >> 10; + tp = mr->cur_tp / ((18000 << 10) / 96); prob = mr->cur_prob / 18; eprob = mr->probability / 18; @@ -139,7 +139,7 @@ minstrel_stats_release(struct inode *inode, struct file *file) return 0; } -static struct file_operations minstrel_stat_fops = { +static const struct file_operations minstrel_stat_fops = { .owner = THIS_MODULE, .open = minstrel_stats_open, .read = minstrel_stats_read, diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c index 8c053be9dc2..699d3ed869c 100644 --- a/net/mac80211/rc80211_pid_algo.c +++ b/net/mac80211/rc80211_pid_algo.c @@ -169,19 +169,9 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo, * still a good measurement and copy it. */ if (unlikely(spinfo->tx_num_xmit == 0)) pf = spinfo->last_pf; - else { - /* XXX: BAD HACK!!! */ - struct sta_info *si = container_of(sta, struct sta_info, sta); - + else pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit; - if (ieee80211_vif_is_mesh(&si->sdata->vif) && pf == 100) - mesh_plink_broken(si); - pf <<= RC_PID_ARITH_SHIFT; - si->fail_avg = ((pf + (spinfo->last_pf << 3)) / 9) - >> RC_PID_ARITH_SHIFT; - } - spinfo->tx_num_xmit = 0; spinfo->tx_num_failed = 0; @@ -311,7 +301,6 @@ rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband, struct rc_pid_sta_info *spinfo = priv_sta; struct rc_pid_info *pinfo = priv; struct rc_pid_rateinfo *rinfo = pinfo->rinfo; - struct sta_info *si; int i, j, tmp; bool s; @@ -348,9 +337,6 @@ rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband, } spinfo->txrate_idx = rate_lowest_index(sband, sta); - /* HACK */ - si = container_of(sta, struct sta_info, sta); - si->fail_avg = 0; } static void *rate_control_pid_alloc(struct ieee80211_hw *hw, diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c index a08a9b53034..a59043fbb0f 100644 --- a/net/mac80211/rc80211_pid_debugfs.c +++ b/net/mac80211/rc80211_pid_debugfs.c @@ -198,7 +198,7 @@ static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf, #undef RC_PID_PRINT_BUF_SIZE -static struct file_operations rc_pid_fop_events = { +static const struct file_operations rc_pid_fop_events = { .owner = THIS_MODULE, .read = rate_control_pid_events_read, .poll = rate_control_pid_events_poll, diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 25a669c86e1..c01588f9d45 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -489,12 +489,21 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control); + char *dev_addr = rx->dev->dev_addr; if (ieee80211_is_data(hdr->frame_control)) { - if (!ieee80211_has_a4(hdr->frame_control)) - return RX_DROP_MONITOR; - if (memcmp(hdr->addr4, rx->dev->dev_addr, ETH_ALEN) == 0) - return RX_DROP_MONITOR; + if (is_multicast_ether_addr(hdr->addr1)) { + if (ieee80211_has_tods(hdr->frame_control) || + !ieee80211_has_fromds(hdr->frame_control)) + return RX_DROP_MONITOR; + if (memcmp(hdr->addr3, dev_addr, ETH_ALEN) == 0) + return RX_DROP_MONITOR; + } else { + if (!ieee80211_has_a4(hdr->frame_control)) + return RX_DROP_MONITOR; + if (memcmp(hdr->addr4, dev_addr, ETH_ALEN) == 0) + return RX_DROP_MONITOR; + } } /* If there is not an established peer link and this is not a peer link @@ -527,7 +536,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) if (ieee80211_is_data(hdr->frame_control) && is_multicast_ether_addr(hdr->addr1) && - mesh_rmc_check(hdr->addr4, msh_h_get(hdr, hdrlen), rx->sdata)) + mesh_rmc_check(hdr->addr3, msh_h_get(hdr, hdrlen), rx->sdata)) return RX_DROP_MONITOR; #undef msh_h_get @@ -1495,7 +1504,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) /* illegal frame */ return RX_DROP_MONITOR; - if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6){ + if (!is_multicast_ether_addr(hdr->addr1) && + (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6)) { struct mesh_path *mppath; rcu_read_lock(); @@ -1512,7 +1522,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) rcu_read_unlock(); } - if (compare_ether_addr(rx->dev->dev_addr, hdr->addr3) == 0) + /* Frame has reached destination. Don't forward */ + if (!is_multicast_ether_addr(hdr->addr1) && + compare_ether_addr(rx->dev->dev_addr, hdr->addr3) == 0) return RX_CONTINUE; mesh_hdr->ttl--; @@ -1532,27 +1544,32 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) rx->dev->name); fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data; - /* - * Save TA to addr1 to send TA a path error if a - * suitable next hop is not found - */ - memcpy(fwd_hdr->addr1, fwd_hdr->addr2, ETH_ALEN); memcpy(fwd_hdr->addr2, rx->dev->dev_addr, ETH_ALEN); info = IEEE80211_SKB_CB(fwd_skb); memset(info, 0, sizeof(*info)); info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; info->control.vif = &rx->sdata->vif; ieee80211_select_queue(local, fwd_skb); - if (is_multicast_ether_addr(fwd_hdr->addr3)) - memcpy(fwd_hdr->addr1, fwd_hdr->addr3, - ETH_ALEN); + if (is_multicast_ether_addr(fwd_hdr->addr1)) + IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, + fwded_mcast); else { - int err = mesh_nexthop_lookup(fwd_skb, sdata); + int err; + /* + * Save TA to addr1 to send TA a path error if a + * suitable next hop is not found + */ + memcpy(fwd_hdr->addr1, fwd_hdr->addr2, + ETH_ALEN); + err = mesh_nexthop_lookup(fwd_skb, sdata); /* Failed to immediately resolve next hop: * fwded frame was dropped or will be added * later to the pending skb queue. */ if (err) return RX_DROP_MONITOR; + + IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, + fwded_unicast); } IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, fwded_frames); @@ -1560,7 +1577,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) } } - if (is_multicast_ether_addr(hdr->addr3) || + if (is_multicast_ether_addr(hdr->addr1) || rx->dev->flags & IFF_PROMISC) return RX_CONTINUE; else @@ -2423,24 +2440,20 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local, * This is the receive path handler. It is called by a low level driver when an * 802.11 MPDU is received from the hardware. */ -void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) +void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_rate *rate = NULL; struct ieee80211_supported_band *sband; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); - if (status->band < 0 || - status->band >= IEEE80211_NUM_BANDS) { - WARN_ON(1); - return; - } + if (WARN_ON(status->band < 0 || + status->band >= IEEE80211_NUM_BANDS)) + goto drop; sband = local->hw.wiphy->bands[status->band]; - if (!sband) { - WARN_ON(1); - return; - } + if (WARN_ON(!sband)) + goto drop; /* * If we're suspending, it is possible although not too likely @@ -2449,16 +2462,21 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) * that might, for example, cause stations to be added or other * driver callbacks be invoked. */ - if (unlikely(local->quiescing || local->suspended)) { - kfree_skb(skb); - return; - } + if (unlikely(local->quiescing || local->suspended)) + goto drop; + + /* + * The same happens when we're not even started, + * but that's worth a warning. + */ + if (WARN_ON(!local->started)) + goto drop; if (status->flag & RX_FLAG_HT) { /* rate_idx is MCS index */ if (WARN_ON(status->rate_idx < 0 || status->rate_idx >= 76)) - return; + goto drop; /* HT rates are not in the table - use the highest legacy rate * for now since other parts of mac80211 may not yet be fully * MCS aware. */ @@ -2466,7 +2484,7 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) } else { if (WARN_ON(status->rate_idx < 0 || status->rate_idx >= sband->n_bitrates)) - return; + goto drop; rate = &sband->bitrates[status->rate_idx]; } @@ -2505,8 +2523,12 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) __ieee80211_rx_handle_packet(hw, skb, rate); rcu_read_unlock(); + + return; + drop: + kfree_skb(skb); } -EXPORT_SYMBOL(__ieee80211_rx); +EXPORT_SYMBOL(ieee80211_rx); /* This is a version of the rx handler that can be called from hard irq * context. Post the skb on the queue and schedule the tasklet */ diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 244f53f3c8b..039901109fa 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -277,9 +277,10 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) if (test_bit(SCAN_HW_SCANNING, &local->scanning)) ieee80211_restore_scan_ies(local); - if (local->scan_req != &local->int_scan_req) + if (local->scan_req != local->int_scan_req) cfg80211_scan_done(local->scan_req, aborted); local->scan_req = NULL; + local->scan_sdata = NULL; was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); local->scanning = 0; @@ -292,13 +293,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) if (was_hw_scan) goto done; - spin_lock_bh(&local->filter_lock); - local->filter_flags &= ~FIF_BCN_PRBRESP_PROMISC; - drv_configure_filter(local, FIF_BCN_PRBRESP_PROMISC, - &local->filter_flags, - local->mc_count, - local->mc_list); - spin_unlock_bh(&local->filter_lock); + ieee80211_configure_filter(local); drv_sw_scan_complete(local); @@ -376,13 +371,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) local->next_scan_state = SCAN_DECISION; local->scan_channel_idx = 0; - spin_lock_bh(&local->filter_lock); - local->filter_flags |= FIF_BCN_PRBRESP_PROMISC; - drv_configure_filter(local, FIF_BCN_PRBRESP_PROMISC, - &local->filter_flags, - local->mc_count, - local->mc_list); - spin_unlock_bh(&local->filter_lock); + ieee80211_configure_filter(local); /* TODO: start scan as soon as all nullfunc frames are ACKed */ ieee80211_queue_delayed_work(&local->hw, @@ -423,7 +412,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, local->scan_req = req; local->scan_sdata = sdata; - if (req != &local->int_scan_req && + if (req != local->int_scan_req && sdata->vif.type == NL80211_IFTYPE_STATION && !list_empty(&ifmgd->work_list)) { /* actually wait for the work it's doing to finish/time out */ @@ -672,6 +661,7 @@ void ieee80211_scan_work(struct work_struct *work) int rc; local->scan_req = NULL; + local->scan_sdata = NULL; rc = __ieee80211_start_scan(sdata, req); mutex_unlock(&local->scan_mtx); @@ -743,10 +733,10 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, if (local->scan_req) goto unlock; - memcpy(local->int_scan_req.ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN); - local->int_scan_req.ssids[0].ssid_len = ssid_len; + memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN); + local->int_scan_req->ssids[0].ssid_len = ssid_len; - ret = __ieee80211_start_scan(sdata, &sdata->local->int_scan_req); + ret = __ieee80211_start_scan(sdata, sdata->local->int_scan_req); unlock: mutex_unlock(&local->scan_mtx); return ret; @@ -754,7 +744,7 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, void ieee80211_scan_cancel(struct ieee80211_local *local) { - bool swscan; + bool abortscan; cancel_delayed_work_sync(&local->scan_work); @@ -763,9 +753,10 @@ void ieee80211_scan_cancel(struct ieee80211_local *local) * queued -- mostly at suspend under RTNL. */ mutex_lock(&local->scan_mtx); - swscan = test_bit(SCAN_SW_SCANNING, &local->scanning); + abortscan = test_bit(SCAN_SW_SCANNING, &local->scanning) || + (!local->scanning && local->scan_req); mutex_unlock(&local->scan_mtx); - if (swscan) + if (abortscan) ieee80211_scan_completed(&local->hw, true); } diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index a360bceeba5..eec001491e6 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -349,6 +349,7 @@ int sta_info_insert(struct sta_info *sta) goto out_free; } list_add(&sta->list, &local->sta_list); + local->sta_generation++; local->num_sta++; sta_info_hash_add(local, sta); @@ -485,6 +486,7 @@ static void __sta_info_unlink(struct sta_info **sta) } local->num_sta--; + local->sta_generation++; if (local->ops->sta_notify) { if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7cffaa046b3..5143d203256 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -317,30 +317,30 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx) if (!atomic_read(&tx->sdata->bss->num_sta_ps)) return TX_CONTINUE; + /* buffered in hardware */ + if (!(tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING)) { + info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM; + + return TX_CONTINUE; + } + /* buffered in mac80211 */ - if (tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING) { - if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER) - purge_old_ps_buffers(tx->local); - if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >= - AP_MAX_BC_BUFFER) { + if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER) + purge_old_ps_buffers(tx->local); + + if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >= AP_MAX_BC_BUFFER) { #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: BC TX buffer full - " - "dropping the oldest frame\n", - tx->dev->name); - } + if (net_ratelimit()) + printk(KERN_DEBUG "%s: BC TX buffer full - dropping the oldest frame\n", + tx->dev->name); #endif - dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf)); - } else - tx->local->total_ps_buffered++; - skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb); - return TX_QUEUED; - } + dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf)); + } else + tx->local->total_ps_buffered++; - /* buffered in hardware */ - info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM; + skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb); - return TX_CONTINUE; + return TX_QUEUED; } static int ieee80211_use_mfp(__le16 fc, struct sta_info *sta, @@ -700,7 +700,6 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) /* for pure STA mode without beacons, we can do it */ hdr->seq_ctrl = cpu_to_le16(tx->sdata->sequence_number); tx->sdata->sequence_number += 0x10; - tx->sdata->sequence_number &= IEEE80211_SCTL_SEQ; return TX_CONTINUE; } @@ -844,6 +843,23 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) } static ieee80211_tx_result debug_noinline +ieee80211_tx_h_stats(struct ieee80211_tx_data *tx) +{ + struct sk_buff *skb = tx->skb; + + if (!tx->sta) + return TX_CONTINUE; + + tx->sta->tx_packets++; + do { + tx->sta->tx_fragments++; + tx->sta->tx_bytes += skb->len; + } while ((skb = skb->next)); + + return TX_CONTINUE; +} + +static ieee80211_tx_result debug_noinline ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx) { if (!tx->key) @@ -887,23 +903,6 @@ ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx) return TX_CONTINUE; } -static ieee80211_tx_result debug_noinline -ieee80211_tx_h_stats(struct ieee80211_tx_data *tx) -{ - struct sk_buff *skb = tx->skb; - - if (!tx->sta) - return TX_CONTINUE; - - tx->sta->tx_packets++; - do { - tx->sta->tx_fragments++; - tx->sta->tx_bytes += skb->len; - } while ((skb = skb->next)); - - return TX_CONTINUE; -} - /* actual transmit path */ /* @@ -1154,6 +1153,9 @@ static int __ieee80211_tx(struct ieee80211_local *local, next = skb->next; len = skb->len; + if (next) + info->flags |= IEEE80211_TX_CTL_MORE_FRAMES; + sdata = vif_to_sdata(info->control.vif); switch (sdata->vif.type) { @@ -1210,9 +1212,9 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) CALL_TXH(ieee80211_tx_h_sequence) CALL_TXH(ieee80211_tx_h_fragment) /* handlers after fragment must be aware of tx info fragmentation! */ + CALL_TXH(ieee80211_tx_h_stats) CALL_TXH(ieee80211_tx_h_encrypt) CALL_TXH(ieee80211_tx_h_calculate_duration) - CALL_TXH(ieee80211_tx_h_stats) #undef CALL_TXH txh_done: @@ -1410,16 +1412,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; - if (ieee80211_vif_is_mesh(&sdata->vif) && - ieee80211_is_data(hdr->frame_control)) { - if (is_multicast_ether_addr(hdr->addr3)) - memcpy(hdr->addr1, hdr->addr3, ETH_ALEN); - else - if (mesh_nexthop_lookup(skb, sdata)) { - dev_put(sdata->dev); - return; - } - } else if (unlikely(sdata->vif.type == NL80211_IFTYPE_MONITOR)) { + if (unlikely(sdata->vif.type == NL80211_IFTYPE_MONITOR)) { int hdrlen; u16 len_rthdr; @@ -1476,13 +1469,22 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, info->control.vif = &sdata->vif; + if (ieee80211_vif_is_mesh(&sdata->vif) && + ieee80211_is_data(hdr->frame_control) && + !is_multicast_ether_addr(hdr->addr1)) + if (mesh_nexthop_lookup(skb, sdata)) { + /* skb queued: don't free */ + dev_put(sdata->dev); + return; + } + ieee80211_select_queue(local, skb); ieee80211_tx(sdata, skb, false); dev_put(sdata->dev); } -int ieee80211_monitor_start_xmit(struct sk_buff *skb, - struct net_device *dev) +netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, + struct net_device *dev) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_channel *chan = local->hw.conf.channel; @@ -1566,8 +1568,8 @@ fail: * encapsulated packet will then be passed to master interface, wlan#.11, for * transmission (through low-level driver). */ -int ieee80211_subif_start_xmit(struct sk_buff *skb, - struct net_device *dev) +netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, + struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; @@ -1617,52 +1619,58 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, break; #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT: - fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); if (!sdata->u.mesh.mshcfg.dot11MeshTTL) { /* Do not send frames with mesh_ttl == 0 */ sdata->u.mesh.mshstats.dropped_frames_ttl++; ret = NETDEV_TX_OK; goto fail; } - memset(&mesh_hdr, 0, sizeof(mesh_hdr)); if (compare_ether_addr(dev->dev_addr, skb->data + ETH_ALEN) == 0) { - /* RA TA DA SA */ - memset(hdr.addr1, 0, ETH_ALEN); - memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); - memcpy(hdr.addr3, skb->data, ETH_ALEN); - memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); - meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata); + hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc, + skb->data, skb->data + ETH_ALEN); + meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, + sdata, NULL, NULL, NULL); } else { /* packet from other interface */ struct mesh_path *mppath; + int is_mesh_mcast = 1; + char *mesh_da; - memset(hdr.addr1, 0, ETH_ALEN); - memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); - memcpy(hdr.addr4, dev->dev_addr, ETH_ALEN); - + rcu_read_lock(); if (is_multicast_ether_addr(skb->data)) - memcpy(hdr.addr3, skb->data, ETH_ALEN); + /* DA TA mSA AE:SA */ + mesh_da = skb->data; else { - rcu_read_lock(); mppath = mpp_path_lookup(skb->data, sdata); - if (mppath) - memcpy(hdr.addr3, mppath->mpp, ETH_ALEN); - else - memset(hdr.addr3, 0xff, ETH_ALEN); - rcu_read_unlock(); + if (mppath) { + /* RA TA mDA mSA AE:DA SA */ + mesh_da = mppath->mpp; + is_mesh_mcast = 0; + } else + /* DA TA mSA AE:SA */ + mesh_da = dev->broadcast; } + hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc, + mesh_da, dev->dev_addr); + rcu_read_unlock(); + if (is_mesh_mcast) + meshhdrlen = + ieee80211_new_mesh_header(&mesh_hdr, + sdata, + skb->data + ETH_ALEN, + NULL, + NULL); + else + meshhdrlen = + ieee80211_new_mesh_header(&mesh_hdr, + sdata, + NULL, + skb->data, + skb->data + ETH_ALEN); - mesh_hdr.flags |= MESH_FLAGS_AE_A5_A6; - mesh_hdr.ttl = sdata->u.mesh.mshcfg.dot11MeshTTL; - put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &mesh_hdr.seqnum); - memcpy(mesh_hdr.eaddr1, skb->data, ETH_ALEN); - memcpy(mesh_hdr.eaddr2, skb->data + ETH_ALEN, ETH_ALEN); - sdata->u.mesh.mesh_seqnum++; - meshhdrlen = 18; } - hdrlen = 30; break; #endif case NL80211_IFTYPE_STATION: diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e55d57f559e..dd656432136 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1007,6 +1007,16 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local, return supp_rates; } +void ieee80211_stop_device(struct ieee80211_local *local) +{ + ieee80211_led_radio(local, false); + + cancel_work_sync(&local->reconfig_filter); + drv_stop(local); + + flush_workqueue(local->workqueue); +} + int ieee80211_reconfig(struct ieee80211_local *local) { struct ieee80211_hw *hw = &local->hw; @@ -1076,9 +1086,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) /* reconfigure hardware */ ieee80211_hw_config(local, ~0); - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); /* Finally also reconfigure all the BSS information */ list_for_each_entry(sdata, &local->interfaces, list) { |