Hi,
Marek created some patches a while ago to automatically detect the wifi interface under virtual interfaces like VLANs. These patches had some problems because they failed to correctly set the num_bcasts in these situations and also had some locking problems.
The following patches try to address these by introducing a cache for the detected wifi configuration (wifi_flags which make taking the rtnl lock in critical section unnecessary) and by updating the num_bcast and wifi_flags when the interface behind iflink changes.
* added patch "batman-adv: Return non-const ptr in batadv_getlink_net" * added patch "batman-adv: Close two alignment holes in batadv_hard_iface" * added patch "batman-adv: Cache the type of wifi device for each hardif" * added patch "batman-adv: Update wifi flags on upper link change" * introduced batadv_get_real_netdevice which can be used inside rtnl locked contexts * replaced wifi check functions with ones that use the wifi_flags cache * use batadv_getlink_net to get the iflink net_namespace * force rtnl_lock in batadv_get_real_netdev
Kind regards, Sven
Marek Lindner (3): batman-adv: refactor wifi interface detection batman-adv: additional checks for virtual interfaces on top of WiFi batman-adv: retrieve B.A.T.M.A.N. V WiFi neighbor stats from real interface
Sven Eckelmann (4): batman-adv: Return non-const ptr in batadv_getlink_net batman-adv: Close two alignment holes in batadv_hard_iface batman-adv: Cache the type of wifi device for each hardif batman-adv: Update wifi flags on upper link change
net/batman-adv/bat_iv_ogm.c | 2 +- net/batman-adv/bat_v_elp.c | 41 +++++---- net/batman-adv/hard-interface.c | 173 ++++++++++++++++++++++++++++++++++--- net/batman-adv/hard-interface.h | 5 +- net/batman-adv/translation-table.c | 10 ++- net/batman-adv/types.h | 21 ++++- 6 files changed, 217 insertions(+), 35 deletions(-)
The returned net_namespace of batadv_getlink_net may be used with functions that potentially modify the struct. Thus it must return the pointer as non-const like rtnl_link_ops::get_link_net does.
Signed-off-by: Sven Eckelmann sven.eckelmann@open-mesh.com --- v2: - new patch --- net/batman-adv/hard-interface.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index 08ce361..a0b01cb 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -92,8 +92,8 @@ out: * * Return: result of rtnl_link_ops->get_link_net or @fallback_net */ -static const struct net *batadv_getlink_net(const struct net_device *netdev, - const struct net *fallback_net) +static struct net *batadv_getlink_net(const struct net_device *netdev, + struct net *fallback_net) { if (!netdev->rtnl_link_ops) return fallback_net; @@ -116,9 +116,9 @@ static const struct net *batadv_getlink_net(const struct net_device *netdev, * Return: true if the devices are each others parent, otherwise false */ static bool batadv_mutual_parents(const struct net_device *dev1, - const struct net *net1, + struct net *net1, const struct net_device *dev2, - const struct net *net2) + struct net *net2) { int dev1_parent_iflink = dev_get_iflink(dev1); int dev2_parent_iflink = dev_get_iflink(dev2); @@ -154,7 +154,7 @@ static bool batadv_is_on_batman_iface(const struct net_device *net_dev) { struct net *net = dev_net(net_dev); struct net_device *parent_dev; - const struct net *parent_net; + struct net *parent_net; bool ret;
/* check if this is a batman-adv mesh interface */
Signed-off-by: Sven Eckelmann sven.eckelmann@open-mesh.com --- v2: - new patch --- net/batman-adv/types.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index b3dd1a3..8eef6a3 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -123,8 +123,8 @@ struct batadv_hard_iface_bat_v { * @list: list node for batadv_hardif_list * @if_num: identificator of the interface * @if_status: status of the interface for batman-adv - * @net_dev: pointer to the net_device * @num_bcasts: number of payload re-broadcasts on this interface (ARQ) + * @net_dev: pointer to the net_device * @hardif_obj: kobject of the per interface sysfs "mesh" directory * @refcount: number of contexts the object is used * @batman_adv_ptype: packet type describing packets that should be processed by @@ -141,8 +141,8 @@ struct batadv_hard_iface { struct list_head list; s16 if_num; char if_status; - struct net_device *net_dev; u8 num_bcasts; + struct net_device *net_dev; struct kobject *hardif_obj; struct kref refcount; struct packet_type batman_adv_ptype;
From: Marek Lindner mareklindner@neomailbox.ch
The ELP protocol requires cfg80211 to auto-detect the WiFi througput to a given neighbor. Use batadv_is_cfg80211_netdev() to determine whether or not an interface is eligible.
Signed-off-by: Marek Lindner mareklindner@neomailbox.ch Signed-off-by: Sven Eckelmann sven.eckelmann@open-mesh.com --- v2: - rebased on top of current master --- net/batman-adv/bat_v_elp.c | 29 ++++++++++++++--------------- net/batman-adv/hard-interface.c | 26 +++++++++++++++++++++----- net/batman-adv/hard-interface.h | 1 + 3 files changed, 36 insertions(+), 20 deletions(-)
diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c index ee08540..b61ec79 100644 --- a/net/batman-adv/bat_v_elp.c +++ b/net/batman-adv/bat_v_elp.c @@ -90,22 +90,21 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh) * cfg80211 API */ if (batadv_is_wifi_netdev(hard_iface->net_dev)) { - if (hard_iface->net_dev->ieee80211_ptr) { - ret = cfg80211_get_station(hard_iface->net_dev, - neigh->addr, &sinfo); - if (ret == -ENOENT) { - /* Node is not associated anymore! It would be - * possible to delete this neighbor. For now set - * the throughput metric to 0. - */ - return 0; - } - if (!ret) - return sinfo.expected_throughput / 100; + if (!batadv_is_cfg80211_netdev(hard_iface->net_dev)) + /* unsupported WiFi driver version */ + goto default_throughput; + + ret = cfg80211_get_station(hard_iface->net_dev, + neigh->addr, &sinfo); + if (ret == -ENOENT) { + /* Node is not associated anymore! It would be + * possible to delete this neighbor. For now set + * the throughput metric to 0. + */ + return 0; } - - /* unsupported WiFi driver version */ - goto default_throughput; + if (!ret) + return sinfo.expected_throughput / 100; }
/* if not a wifi interface, check if this device provides data via diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index a0b01cb..b09fec2 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -202,6 +202,26 @@ static bool batadv_is_valid_iface(const struct net_device *net_dev) }
/** + * batadv_is_cfg80211_netdev - check if the given net_device struct is a + * cfg80211 wifi interface + * @net_device: the device to check + * + * Return: true if the net device is a cfg80211 wireless device, false + * otherwise. + */ +bool batadv_is_cfg80211_netdev(struct net_device *net_device) +{ + if (!net_device) + return false; + + /* cfg80211 drivers have to set ieee80211_ptr */ + if (net_device->ieee80211_ptr) + return true; + + return false; +} + +/** * batadv_is_wifi_netdev - check if the given net_device struct is a wifi * interface * @net_device: the device to check @@ -221,11 +241,7 @@ bool batadv_is_wifi_netdev(struct net_device *net_device) return true; #endif
- /* cfg80211 drivers have to set ieee80211_ptr */ - if (net_device->ieee80211_ptr) - return true; - - return false; + return batadv_is_cfg80211_netdev(net_device); }
static struct batadv_hard_iface * diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h index a76724d..e0893de 100644 --- a/net/batman-adv/hard-interface.h +++ b/net/batman-adv/hard-interface.h @@ -51,6 +51,7 @@ enum batadv_hard_if_cleanup {
extern struct notifier_block batadv_hard_if_notifier;
+bool batadv_is_cfg80211_netdev(struct net_device *net_device); bool batadv_is_wifi_netdev(struct net_device *net_device); bool batadv_is_wifi_iface(int ifindex); struct batadv_hard_iface*
batman-adv is requiring the type of wifi device in different contexts. Some of them can take the rtnl semaphore and some of them already have the semaphore taken. But even others don't allow that the semaphore will be taken.
The data has to be retrieved when the hardif is added to batman-adv because some of the wifi information for an hardif will only be available with rtnl lock. It can then be cached in the batadv_hard_iface and the functions is_wifi_netdev and is_cfg80211_netdev can just compare the correct bits without imposing extra locking requirements.
Signed-off-by: Sven Eckelmann sven.eckelmann@open-mesh.com --- v2: - new patch --- net/batman-adv/bat_iv_ogm.c | 2 +- net/batman-adv/bat_v_elp.c | 8 ++-- net/batman-adv/hard-interface.c | 82 +++++++++++++++++++++++++++++++------- net/batman-adv/hard-interface.h | 5 +-- net/batman-adv/translation-table.c | 10 ++++- net/batman-adv/types.h | 13 ++++++ 6 files changed, 95 insertions(+), 25 deletions(-)
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index e2d18d0..7079d55 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -1272,7 +1272,7 @@ static bool batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node, */ tq_iface_penalty = BATADV_TQ_MAX_VALUE; if (if_outgoing && (if_incoming == if_outgoing) && - batadv_is_wifi_netdev(if_outgoing->net_dev)) + batadv_is_wifi_hardif(if_outgoing)) tq_iface_penalty = batadv_hop_penalty(BATADV_TQ_MAX_VALUE, bat_priv);
diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c index b61ec79..cdcaf6b 100644 --- a/net/batman-adv/bat_v_elp.c +++ b/net/batman-adv/bat_v_elp.c @@ -89,8 +89,8 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh) /* if this is a wireless device, then ask its throughput through * cfg80211 API */ - if (batadv_is_wifi_netdev(hard_iface->net_dev)) { - if (!batadv_is_cfg80211_netdev(hard_iface->net_dev)) + if (batadv_is_wifi_hardif(hard_iface)) { + if (!batadv_is_cfg80211_hardif(hard_iface)) /* unsupported WiFi driver version */ goto default_throughput;
@@ -186,7 +186,7 @@ batadv_v_elp_wifi_neigh_probe(struct batadv_hardif_neigh_node *neigh) int elp_skb_len;
/* this probing routine is for Wifi neighbours only */ - if (!batadv_is_wifi_netdev(hard_iface->net_dev)) + if (!batadv_is_wifi_hardif(hard_iface)) return true;
/* probe the neighbor only if no unicast packets have been sent @@ -351,7 +351,7 @@ int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface) /* warn the user (again) if there is no throughput data is available */ hard_iface->bat_v.flags &= ~BATADV_WARNING_DEFAULT;
- if (batadv_is_wifi_netdev(hard_iface->net_dev)) + if (batadv_is_wifi_hardif(hard_iface)) hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX;
INIT_DELAYED_WORK(&hard_iface->bat_v.elp_wq, diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index b09fec2..341fbb5 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -202,6 +202,30 @@ static bool batadv_is_valid_iface(const struct net_device *net_dev) }
/** + * batadv_is_wext_netdev - check if the given net_device struct is a + * wext wifi interface + * @net_device: the device to check + * + * Return: true if the net device is a wext wireless device, false + * otherwise. + */ +static bool batadv_is_wext_netdev(struct net_device *net_device) +{ + if (!net_device) + return false; + +#ifdef CONFIG_WIRELESS_EXT + /* pre-cfg80211 drivers have to implement WEXT, so it is possible to + * check for wireless_handlers != NULL + */ + if (net_device->wireless_handlers) + return true; +#endif + + return false; +} + +/** * batadv_is_cfg80211_netdev - check if the given net_device struct is a * cfg80211 wifi interface * @net_device: the device to check @@ -209,7 +233,7 @@ static bool batadv_is_valid_iface(const struct net_device *net_dev) * Return: true if the net device is a cfg80211 wireless device, false * otherwise. */ -bool batadv_is_cfg80211_netdev(struct net_device *net_device) +static bool batadv_is_cfg80211_netdev(struct net_device *net_device) { if (!net_device) return false; @@ -222,26 +246,53 @@ bool batadv_is_cfg80211_netdev(struct net_device *net_device) }
/** - * batadv_is_wifi_netdev - check if the given net_device struct is a wifi - * interface + * batadv_wifi_flags_evaluate - calculate wifi flags for net_device * @net_device: the device to check * + * Return: batadv_hard_iface_wifi_flags flags of the device + */ +static u32 batadv_wifi_flags_evaluate(struct net_device *net_device) +{ + u32 wifi_flags = 0; + + if (batadv_is_wext_netdev(net_device)) + wifi_flags |= BATADV_HARDIF_WIFI_WEXT_DIRECT; + + if (batadv_is_cfg80211_netdev(net_device)) + wifi_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT; + + return wifi_flags; +} + +/** + * batadv_is_cfg80211_hardif - check if the given hardif is a cfg80211 wifi + * interface + * @hard_iface: the device to check + * + * Return: true if the net device is a cfg80211 wireless device, false + * otherwise. + */ +bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface) +{ + u32 allowed_flags = 0; + + allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT; + + return !!(hard_iface->wifi_flags & allowed_flags); +} + +/** + * batadv_is_wifi_hardif - check if the given hardif is a wifi interface + * @hard_iface: the device to check + * * Return: true if the net device is a 802.11 wireless device, false otherwise. */ -bool batadv_is_wifi_netdev(struct net_device *net_device) +bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface) { - if (!net_device) + if (!hard_iface) return false;
-#ifdef CONFIG_WIRELESS_EXT - /* pre-cfg80211 drivers have to implement WEXT, so it is possible to - * check for wireless_handlers != NULL - */ - if (net_device->wireless_handlers) - return true; -#endif - - return batadv_is_cfg80211_netdev(net_device); + return hard_iface->wifi_flags != 0; }
static struct batadv_hard_iface * @@ -713,7 +764,8 @@ batadv_hardif_add_interface(struct net_device *net_dev) kref_init(&hard_iface->refcount);
hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT; - if (batadv_is_wifi_netdev(net_dev)) + hard_iface->wifi_flags = batadv_wifi_flags_evaluate(net_dev); + if (batadv_is_wifi_hardif(hard_iface)) hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
batadv_v_hardif_init(hard_iface); diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h index e0893de..957fd8f 100644 --- a/net/batman-adv/hard-interface.h +++ b/net/batman-adv/hard-interface.h @@ -51,9 +51,8 @@ enum batadv_hard_if_cleanup {
extern struct notifier_block batadv_hard_if_notifier;
-bool batadv_is_cfg80211_netdev(struct net_device *net_device); -bool batadv_is_wifi_netdev(struct net_device *net_device); -bool batadv_is_wifi_iface(int ifindex); +bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface); +bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface); struct batadv_hard_iface* batadv_hardif_get_by_netdev(const struct net_device *net_dev); int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 7f66309..7c76f83 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -647,6 +647,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr, struct net *net = dev_net(soft_iface); struct batadv_softif_vlan *vlan; struct net_device *in_dev = NULL; + struct batadv_hard_iface *in_hardif = NULL; struct hlist_head *head; struct batadv_tt_orig_list_entry *orig_entry; int hash_added, table_size, packet_size_max; @@ -658,6 +659,9 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr, if (ifindex != BATADV_NULL_IFINDEX) in_dev = dev_get_by_index(net, ifindex);
+ if (in_dev) + in_hardif = batadv_hardif_get_by_netdev(in_dev); + tt_local = batadv_tt_local_hash_find(bat_priv, addr, vid);
if (!is_multicast_ether_addr(addr)) @@ -731,7 +735,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr, */ tt_local->common.flags = BATADV_TT_CLIENT_NEW; tt_local->common.vid = vid; - if (batadv_is_wifi_netdev(in_dev)) + if (batadv_is_wifi_hardif(in_hardif)) tt_local->common.flags |= BATADV_TT_CLIENT_WIFI; kref_init(&tt_local->common.refcount); tt_local->last_seen = jiffies; @@ -791,7 +795,7 @@ check_roaming: */ remote_flags = tt_local->common.flags & BATADV_TT_REMOTE_MASK;
- if (batadv_is_wifi_netdev(in_dev)) + if (batadv_is_wifi_hardif(in_hardif)) tt_local->common.flags |= BATADV_TT_CLIENT_WIFI; else tt_local->common.flags &= ~BATADV_TT_CLIENT_WIFI; @@ -815,6 +819,8 @@ check_roaming:
ret = true; out: + if (in_hardif) + batadv_hardif_put(in_hardif); if (in_dev) dev_put(in_dev); if (tt_local) diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 8eef6a3..fe64669 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -119,11 +119,23 @@ struct batadv_hard_iface_bat_v { };
/** + * enum batadv_hard_iface_wifi_flags - Flags describing the wifi configuration + * of a batadv_hard_iface + * @BATADV_HARDIF_WIFI_WEXT_DIRECT: it is a wext wifi device + * @BATADV_HARDIF_WIFI_CFG80211_DIRECT: it is a cfg80211 wifi device + */ +enum batadv_hard_iface_wifi_flags { + BATADV_HARDIF_WIFI_WEXT_DIRECT = BIT(0), + BATADV_HARDIF_WIFI_CFG80211_DIRECT = BIT(1), +}; + +/** * struct batadv_hard_iface - network device known to batman-adv * @list: list node for batadv_hardif_list * @if_num: identificator of the interface * @if_status: status of the interface for batman-adv * @num_bcasts: number of payload re-broadcasts on this interface (ARQ) + * @wifi_flags: flags whether this is (directly or indirectly) a wifi interface * @net_dev: pointer to the net_device * @hardif_obj: kobject of the per interface sysfs "mesh" directory * @refcount: number of contexts the object is used @@ -142,6 +154,7 @@ struct batadv_hard_iface { s16 if_num; char if_status; u8 num_bcasts; + u32 wifi_flags; struct net_device *net_dev; struct kobject *hardif_obj; struct kref refcount;
From: Marek Lindner mareklindner@neomailbox.ch
In a few situations batman-adv tries to determine whether a given interface is a WiFi interface to enable specific WiFi optimizations. If the interface batman-adv has been configured with is a virtual interface (e.g. VLAN) it would not be properly detected as WiFi interface and thus not benefit from the special WiFi treatment. This patch changes that by peeking under the hood whenever a virtual interface is in play.
Signed-off-by: Marek Lindner mareklindner@neomailbox.ch [sven.eckelmann@open-mesh.com: integrate in wifi_flags caching, retrieve namespace of link interface] Signed-off-by: Sven Eckelmann sven.eckelmann@open-mesh.com --- v2: - introduced batadv_get_real_netdevice which can be used inside rtnl locked contexts - replace check functions with ones that use the wifi_flags cache - use batadv_getlink_net to get the iflink net_namespace --- net/batman-adv/hard-interface.c | 58 +++++++++++++++++++++++++++++++++++++++++ net/batman-adv/types.h | 4 +++ 2 files changed, 62 insertions(+)
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index 341fbb5..ba587fb 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -202,6 +202,47 @@ static bool batadv_is_valid_iface(const struct net_device *net_dev) }
/** + * batadv_get_real_netdevice - check if the given netdev struct is a virtual + * interface on top of another 'real' interface + * @netdev: the device to check + * + * Return: the 'real' net device or the original net device and NULL in case + * of an error. + */ +static struct net_device *batadv_get_real_netdevice(struct net_device *netdev) +{ + struct batadv_hard_iface *hard_iface = NULL; + struct net_device *real_netdev = NULL; + struct net *real_net; + struct net *net; + int ifindex; + + ASSERT_RTNL(); + + if (!netdev) + return NULL; + + if (netdev->ifindex == dev_get_iflink(netdev)) { + dev_hold(netdev); + return netdev; + } + + hard_iface = batadv_hardif_get_by_netdev(netdev); + if (!hard_iface || !hard_iface->soft_iface) + goto out; + + net = dev_net(hard_iface->soft_iface); + ifindex = dev_get_iflink(netdev); + real_net = batadv_getlink_net(netdev, net); + real_netdev = dev_get_by_index(real_net, ifindex); + +out: + if (hard_iface) + batadv_hardif_put(hard_iface); + return real_netdev; +} + +/** * batadv_is_wext_netdev - check if the given net_device struct is a * wext wifi interface * @net_device: the device to check @@ -254,6 +295,7 @@ static bool batadv_is_cfg80211_netdev(struct net_device *net_device) static u32 batadv_wifi_flags_evaluate(struct net_device *net_device) { u32 wifi_flags = 0; + struct net_device *real_netdev;
if (batadv_is_wext_netdev(net_device)) wifi_flags |= BATADV_HARDIF_WIFI_WEXT_DIRECT; @@ -261,6 +303,21 @@ static u32 batadv_wifi_flags_evaluate(struct net_device *net_device) if (batadv_is_cfg80211_netdev(net_device)) wifi_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT;
+ real_netdev = batadv_get_real_netdevice(net_device); + if (!real_netdev) + return wifi_flags; + + if (real_netdev == net_device) + goto out; + + if (batadv_is_wext_netdev(real_netdev)) + wifi_flags |= BATADV_HARDIF_WIFI_WEXT_INDIRECT; + + if (batadv_is_cfg80211_netdev(real_netdev)) + wifi_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT; + +out: + dev_put(real_netdev); return wifi_flags; }
@@ -277,6 +334,7 @@ bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface) u32 allowed_flags = 0;
allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT; + allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT;
return !!(hard_iface->wifi_flags & allowed_flags); } diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index fe64669..a6d8c98 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -123,10 +123,14 @@ struct batadv_hard_iface_bat_v { * of a batadv_hard_iface * @BATADV_HARDIF_WIFI_WEXT_DIRECT: it is a wext wifi device * @BATADV_HARDIF_WIFI_CFG80211_DIRECT: it is a cfg80211 wifi device + * @BATADV_HARDIF_WIFI_WEXT_INDIRECT: link device is a wext wifi device + * @BATADV_HARDIF_WIFI_CFG80211_INDIRECT: link device is a cfg80211 wifi device */ enum batadv_hard_iface_wifi_flags { BATADV_HARDIF_WIFI_WEXT_DIRECT = BIT(0), BATADV_HARDIF_WIFI_CFG80211_DIRECT = BIT(1), + BATADV_HARDIF_WIFI_WEXT_INDIRECT = BIT(2), + BATADV_HARDIF_WIFI_CFG80211_INDIRECT = BIT(3), };
/**
From: Marek Lindner mareklindner@neomailbox.ch
Signed-off-by: Marek Lindner mareklindner@neomailbox.ch [sven.eckelmann@open-mesh.com: re-add batadv_get_real_netdev to take rtnl semaphore for batadv_get_real_netdevice] Signed-off-by: Sven Eckelmann sven.eckelmann@open-mesh.com --- v2: - reintroduce batadv_get_real_netdev for situations were the rtnl_lock has to be taken --- net/batman-adv/bat_v_elp.c | 10 ++++++++-- net/batman-adv/hard-interface.c | 22 ++++++++++++++++++++++ net/batman-adv/hard-interface.h | 1 + 3 files changed, 31 insertions(+), 2 deletions(-)
diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c index cdcaf6b..89488dc 100644 --- a/net/batman-adv/bat_v_elp.c +++ b/net/batman-adv/bat_v_elp.c @@ -75,6 +75,7 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh) { struct batadv_hard_iface *hard_iface = neigh->if_incoming; struct ethtool_link_ksettings link_settings; + struct net_device *real_netdev; struct station_info sinfo; u32 throughput; int ret; @@ -94,8 +95,13 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh) /* unsupported WiFi driver version */ goto default_throughput;
- ret = cfg80211_get_station(hard_iface->net_dev, - neigh->addr, &sinfo); + real_netdev = batadv_get_real_netdev(hard_iface->net_dev); + if (!real_netdev) + goto default_throughput; + + ret = cfg80211_get_station(real_netdev, neigh->addr, &sinfo); + + dev_put(real_netdev); if (ret == -ENOENT) { /* Node is not associated anymore! It would be * possible to delete this neighbor. For now set diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index ba587fb..7d336e1 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -206,6 +206,9 @@ static bool batadv_is_valid_iface(const struct net_device *net_dev) * interface on top of another 'real' interface * @netdev: the device to check * + * Callers must hold the rtnl semaphore. You may want batadv_get_real_netdev() + * instead of this. + * * Return: the 'real' net device or the original net device and NULL in case * of an error. */ @@ -243,6 +246,25 @@ out: }
/** + * batadv_get_real_netdev - check if the given net_device struct is a virtual + * interface on top of another 'real' interface + * @net_device: the device to check + * + * Return: the 'real' net device or the original net device and NULL in case + * of an error. + */ +struct net_device *batadv_get_real_netdev(struct net_device *net_device) +{ + struct net_device *real_netdev = NULL; + + rtnl_lock(); + real_netdev = batadv_get_real_netdevice(net_device); + rtnl_unlock(); + + return real_netdev; +} + +/** * batadv_is_wext_netdev - check if the given net_device struct is a * wext wifi interface * @net_device: the device to check diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h index 957fd8f..0c7ff14 100644 --- a/net/batman-adv/hard-interface.h +++ b/net/batman-adv/hard-interface.h @@ -51,6 +51,7 @@ enum batadv_hard_if_cleanup {
extern struct notifier_block batadv_hard_if_notifier;
+struct net_device *batadv_get_real_netdev(struct net_device *net_device); bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface); bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface); struct batadv_hard_iface*
Things like VLANs don't have their link set when they are created. Thus the wifi flags have to be evaluated later to fix their contents for the link interface.
Signed-off-by: Sven Eckelmann sven.eckelmann@open-mesh.com --- v2: - new patch --- compat-include/linux/netdevice.h | 2 ++ net/batman-adv/hard-interface.c | 5 +++++ 2 files changed, 7 insertions(+)
diff --git a/compat-include/linux/netdevice.h b/compat-include/linux/netdevice.h index fb5b519..e71e614 100644 --- a/compat-include/linux/netdevice.h +++ b/compat-include/linux/netdevice.h @@ -43,6 +43,8 @@
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0)
+#define NETDEV_CHANGEUPPER 0x0015 + #define netdev_notifier_info_to_dev(ptr) ptr
#endif /* < KERNEL_VERSION(3, 11, 0) */ diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index 7d336e1..400935f 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -954,6 +954,11 @@ static int batadv_hard_if_event(struct notifier_block *this, if (hard_iface == primary_if) batadv_primary_if_update_addr(bat_priv, NULL); break; + case NETDEV_CHANGEUPPER: + hard_iface->wifi_flags = batadv_wifi_flags_evaluate(net_dev); + if (batadv_is_wifi_hardif(hard_iface)) + hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS; + break; default: break; }
On Friday, September 30, 2016 3:20:23 PM CEST Sven Eckelmann wrote:
Hi,
Marek created some patches a while ago to automatically detect the wifi interface under virtual interfaces like VLANs. These patches had some problems because they failed to correctly set the num_bcasts in these situations and also had some locking problems.
The following patches try to address these by introducing a cache for the detected wifi configuration (wifi_flags which make taking the rtnl lock in critical section unnecessary) and by updating the num_bcast and wifi_flags when the interface behind iflink changes.
- added patch "batman-adv: Return non-const ptr in batadv_getlink_net"
- added patch "batman-adv: Close two alignment holes in batadv_hard_iface"
- added patch "batman-adv: Cache the type of wifi device for each hardif"
- added patch "batman-adv: Update wifi flags on upper link change"
- introduced batadv_get_real_netdevice which can be used inside rtnl locked contexts
- replaced wifi check functions with ones that use the wifi_flags cache
- use batadv_getlink_net to get the iflink net_namespace
- force rtnl_lock in batadv_get_real_netdev
Kind regards, Sven
Applied this series on 451eab1..846df98.
Thanks, Simon
b.a.t.m.a.n@lists.open-mesh.org