This feature patchset includes the following changes:
- 6 patches adding functionality to detect a WiFi interface under other virtual interfaces, like VLANs. They introduce a cache for the detected the WiFi configuration to avoid RTNL locking in critical sections. Patches have been prepared by Marek Lindner and Sven Eckelmann - Enable automatic module loading for genl requests, by Sven Eckelmann - Fix a potential race condition on interface removal. This is not happening very often in practice, but requires bigger changes to fix, so we are sending this to net-next. By Linus Luessing -----BEGIN PGP SIGNATURE----- iQJKBAABCgA0FiEE1ilQI7G+y+fdhnrfoSvjmEKSnqEFAlgwVFYWHHN3QHNpbW9u d3VuZGVybGljaC5kZQAKCRChK+OYQpKeoZRaD/9YkdTsT8N629/C2yCrfvt2Zjav xj9+sGtmIq5xtSLkJaiQilz+ua8dCt99TbuzB8c48xXn9O41kejtv6kPE/YzYWVN dkLSO7cpJpT20hAAKD54iRv8m+Ed9ozgTPZd+Lu28fjwDc+FAzdmM1gAKx21Wtk6 SLmRWguA/ezN+FWWLv4HYtThWuOCVOpkhx8Zk7wuzT2PQbryXOIqQ5JOgaKqm1PE iHFhsleaHJ74qnr6UReIZ/g3h27+RPGvhXtAtfo+HEupW4FTZowGr7C6Sm9BCpmU yMQ1DGckNbg51hz+irJH7nGT+9y6UP/mKNduMOW37JcOF2YKyDvXr+A7C+3Nmv/0 F+AoFrDKp6vRBTgyKYvuL8zMvDn5mwCh/436/jIbqRvrCVGJQUY1IsS1yK+kPldy b/XamVCKAzxzVTumIDz5UCOAxMqaJmhLbasSoMqLZum4fuEU/CAZblKc/2lz/2h2 o4Jpka3aGwGSIB+vZC0cat1a3RYKesxKuUmEIU7ZTnySOpP8FoiEZuz2qQhhlfNm fdGnL0YydBO4yOBBmSoSmS64hfvfdwZv9yuXt2NABXJDSD6lfJKNT3MGDlB9phLM OVzO5tQqP9AWel2iFSXafqtuxdqGvv+eFv7PqLGNRqHEr691AIFw10qugx9g4Bul dqlpQ7CoVC16LrOaQw== =Pdlv -----END PGP SIGNATURE----- Merge tag 'batadv-next-for-davem-20161119' of git://git.open-mesh.org/linux-merge Simon Wunderlich says: ==================== This feature patchset includes the following changes: - 6 patches adding functionality to detect a WiFi interface under other virtual interfaces, like VLANs. They introduce a cache for the detected the WiFi configuration to avoid RTNL locking in critical sections. Patches have been prepared by Marek Lindner and Sven Eckelmann - Enable automatic module loading for genl requests, by Sven Eckelmann - Fix a potential race condition on interface removal. This is not happening very often in practice, but requires bigger changes to fix, so we are sending this to net-next. By Linus Luessing ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
f463c99b20
|
@ -717,17 +717,10 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
|
|||
if (direct_link)
|
||||
forw_packet_aggr->direct_link_flags |= 1;
|
||||
|
||||
/* add new packet to packet list */
|
||||
spin_lock_bh(&bat_priv->forw_bat_list_lock);
|
||||
hlist_add_head(&forw_packet_aggr->list, &bat_priv->forw_bat_list);
|
||||
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
|
||||
|
||||
/* start timer for this packet */
|
||||
INIT_DELAYED_WORK(&forw_packet_aggr->delayed_work,
|
||||
batadv_iv_send_outstanding_bat_ogm_packet);
|
||||
queue_delayed_work(batadv_event_workqueue,
|
||||
&forw_packet_aggr->delayed_work,
|
||||
send_time - jiffies);
|
||||
|
||||
batadv_forw_packet_ogmv1_queue(bat_priv, forw_packet_aggr, send_time);
|
||||
}
|
||||
|
||||
/* aggregate a new packet into the existing ogm packet */
|
||||
|
@ -1272,7 +1265,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);
|
||||
|
||||
|
@ -1789,9 +1782,6 @@ static void batadv_iv_send_outstanding_bat_ogm_packet(struct work_struct *work)
|
|||
forw_packet = container_of(delayed_work, struct batadv_forw_packet,
|
||||
delayed_work);
|
||||
bat_priv = netdev_priv(forw_packet->if_incoming->soft_iface);
|
||||
spin_lock_bh(&bat_priv->forw_bat_list_lock);
|
||||
hlist_del(&forw_packet->list);
|
||||
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
|
||||
|
||||
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) {
|
||||
dropped = true;
|
||||
|
@ -1813,7 +1803,10 @@ static void batadv_iv_send_outstanding_bat_ogm_packet(struct work_struct *work)
|
|||
batadv_iv_ogm_schedule(forw_packet->if_incoming);
|
||||
|
||||
out:
|
||||
batadv_forw_packet_free(forw_packet, dropped);
|
||||
/* do we get something for free()? */
|
||||
if (batadv_forw_packet_steal(forw_packet,
|
||||
&bat_priv->forw_bat_list_lock))
|
||||
batadv_forw_packet_free(forw_packet, dropped);
|
||||
}
|
||||
|
||||
static int batadv_iv_ogm_receive(struct sk_buff *skb,
|
||||
|
|
|
@ -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;
|
||||
|
@ -89,23 +90,27 @@ 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 (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_wifi_hardif(hard_iface)) {
|
||||
if (!batadv_is_cfg80211_hardif(hard_iface))
|
||||
/* unsupported WiFi driver version */
|
||||
goto default_throughput;
|
||||
|
||||
/* unsupported WiFi driver version */
|
||||
goto default_throughput;
|
||||
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
|
||||
* the throughput metric to 0.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
if (!ret)
|
||||
return sinfo.expected_throughput / 100;
|
||||
}
|
||||
|
||||
/* if not a wifi interface, check if this device provides data via
|
||||
|
@ -187,7 +192,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
|
||||
|
@ -352,7 +357,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,
|
||||
|
|
|
@ -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 */
|
||||
|
@ -202,13 +202,77 @@ static bool batadv_is_valid_iface(const struct net_device *net_dev)
|
|||
}
|
||||
|
||||
/**
|
||||
* batadv_is_wifi_netdev - check if the given net_device struct is a wifi
|
||||
* interface
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
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_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: true if the net device is a 802.11 wireless device, false otherwise.
|
||||
* Return: the 'real' net device or the original net device and NULL in case
|
||||
* of an error.
|
||||
*/
|
||||
bool batadv_is_wifi_netdev(struct net_device *net_device)
|
||||
struct net_device *batadv_get_real_netdev(struct net_device *net_device)
|
||||
{
|
||||
struct net_device *real_netdev;
|
||||
|
||||
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
|
||||
*
|
||||
* 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;
|
||||
|
@ -221,6 +285,22 @@ bool batadv_is_wifi_netdev(struct net_device *net_device)
|
|||
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
|
||||
*
|
||||
* Return: true if the net device is a cfg80211 wireless device, false
|
||||
* otherwise.
|
||||
*/
|
||||
static 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;
|
||||
|
@ -228,6 +308,73 @@ bool batadv_is_wifi_netdev(struct net_device *net_device)
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
struct net_device *real_netdev;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT;
|
||||
|
||||
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_hardif(struct batadv_hard_iface *hard_iface)
|
||||
{
|
||||
if (!hard_iface)
|
||||
return false;
|
||||
|
||||
return hard_iface->wifi_flags != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_hardif_no_broadcast - check whether (re)broadcast is necessary
|
||||
* @if_outgoing: the outgoing interface checked and considered for (re)broadcast
|
||||
|
@ -748,7 +895,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);
|
||||
|
@ -857,6 +1005,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;
|
||||
}
|
||||
|
|
|
@ -65,8 +65,9 @@ enum batadv_hard_if_cleanup {
|
|||
|
||||
extern struct notifier_block batadv_hard_if_notifier;
|
||||
|
||||
bool batadv_is_wifi_netdev(struct net_device *net_device);
|
||||
bool batadv_is_wifi_iface(int ifindex);
|
||||
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*
|
||||
batadv_hardif_get_by_netdev(const struct net_device *net_dev);
|
||||
int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/crc32c.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/genetlink.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/init.h>
|
||||
|
@ -44,6 +45,7 @@
|
|||
#include <linux/workqueue.h>
|
||||
#include <net/dsfield.h>
|
||||
#include <net/rtnetlink.h>
|
||||
#include <uapi/linux/batman_adv.h>
|
||||
|
||||
#include "bat_algo.h"
|
||||
#include "bat_iv_ogm.h"
|
||||
|
@ -648,3 +650,4 @@ MODULE_DESCRIPTION(BATADV_DRIVER_DESC);
|
|||
MODULE_SUPPORTED_DEVICE(BATADV_DRIVER_DEVICE);
|
||||
MODULE_VERSION(BATADV_SOURCE_VERSION);
|
||||
MODULE_ALIAS_RTNL_LINK("batadv");
|
||||
MODULE_ALIAS_GENL_FAMILY(BATADV_NL_NAME);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "main.h"
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/etherdevice.h>
|
||||
|
@ -520,6 +521,8 @@ batadv_forw_packet_alloc(struct batadv_hard_iface *if_incoming,
|
|||
if (if_outgoing)
|
||||
kref_get(&if_outgoing->refcount);
|
||||
|
||||
INIT_HLIST_NODE(&forw_packet->list);
|
||||
INIT_HLIST_NODE(&forw_packet->cleanup_list);
|
||||
forw_packet->skb = NULL;
|
||||
forw_packet->queue_left = queue_left;
|
||||
forw_packet->if_incoming = if_incoming;
|
||||
|
@ -535,19 +538,191 @@ err:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
_batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
|
||||
struct batadv_forw_packet *forw_packet,
|
||||
unsigned long send_time)
|
||||
/**
|
||||
* batadv_forw_packet_was_stolen - check whether someone stole this packet
|
||||
* @forw_packet: the forwarding packet to check
|
||||
*
|
||||
* This function checks whether the given forwarding packet was claimed by
|
||||
* someone else for free().
|
||||
*
|
||||
* Return: True if someone stole it, false otherwise.
|
||||
*/
|
||||
static bool
|
||||
batadv_forw_packet_was_stolen(struct batadv_forw_packet *forw_packet)
|
||||
{
|
||||
/* add new packet to packet list */
|
||||
spin_lock_bh(&bat_priv->forw_bcast_list_lock);
|
||||
hlist_add_head(&forw_packet->list, &bat_priv->forw_bcast_list);
|
||||
spin_unlock_bh(&bat_priv->forw_bcast_list_lock);
|
||||
return !hlist_unhashed(&forw_packet->cleanup_list);
|
||||
}
|
||||
|
||||
/* start timer for this packet */
|
||||
queue_delayed_work(batadv_event_workqueue, &forw_packet->delayed_work,
|
||||
send_time);
|
||||
/**
|
||||
* batadv_forw_packet_steal - claim a forw_packet for free()
|
||||
* @forw_packet: the forwarding packet to steal
|
||||
* @lock: a key to the store to steal from (e.g. forw_{bat,bcast}_list_lock)
|
||||
*
|
||||
* This function tries to steal a specific forw_packet from global
|
||||
* visibility for the purpose of getting it for free(). That means
|
||||
* the caller is *not* allowed to requeue it afterwards.
|
||||
*
|
||||
* Return: True if stealing was successful. False if someone else stole it
|
||||
* before us.
|
||||
*/
|
||||
bool batadv_forw_packet_steal(struct batadv_forw_packet *forw_packet,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
/* did purging routine steal it earlier? */
|
||||
spin_lock_bh(lock);
|
||||
if (batadv_forw_packet_was_stolen(forw_packet)) {
|
||||
spin_unlock_bh(lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
hlist_del_init(&forw_packet->list);
|
||||
|
||||
/* Just to spot misuse of this function */
|
||||
hlist_add_fake(&forw_packet->cleanup_list);
|
||||
|
||||
spin_unlock_bh(lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_forw_packet_list_steal - claim a list of forward packets for free()
|
||||
* @forw_list: the to be stolen forward packets
|
||||
* @cleanup_list: a backup pointer, to be able to dispose the packet later
|
||||
* @hard_iface: the interface to steal forward packets from
|
||||
*
|
||||
* This function claims responsibility to free any forw_packet queued on the
|
||||
* given hard_iface. If hard_iface is NULL forwarding packets on all hard
|
||||
* interfaces will be claimed.
|
||||
*
|
||||
* The packets are being moved from the forw_list to the cleanup_list and
|
||||
* by that allows already running threads to notice the claiming.
|
||||
*/
|
||||
static void
|
||||
batadv_forw_packet_list_steal(struct hlist_head *forw_list,
|
||||
struct hlist_head *cleanup_list,
|
||||
const struct batadv_hard_iface *hard_iface)
|
||||
{
|
||||
struct batadv_forw_packet *forw_packet;
|
||||
struct hlist_node *safe_tmp_node;
|
||||
|
||||
hlist_for_each_entry_safe(forw_packet, safe_tmp_node,
|
||||
forw_list, list) {
|
||||
/* if purge_outstanding_packets() was called with an argument
|
||||
* we delete only packets belonging to the given interface
|
||||
*/
|
||||
if (hard_iface &&
|
||||
(forw_packet->if_incoming != hard_iface) &&
|
||||
(forw_packet->if_outgoing != hard_iface))
|
||||
continue;
|
||||
|
||||
hlist_del(&forw_packet->list);
|
||||
hlist_add_head(&forw_packet->cleanup_list, cleanup_list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_forw_packet_list_free - free a list of forward packets
|
||||
* @head: a list of to be freed forw_packets
|
||||
*
|
||||
* This function cancels the scheduling of any packet in the provided list,
|
||||
* waits for any possibly running packet forwarding thread to finish and
|
||||
* finally, safely frees this forward packet.
|
||||
*
|
||||
* This function might sleep.
|
||||
*/
|
||||
static void batadv_forw_packet_list_free(struct hlist_head *head)
|
||||
{
|
||||
struct batadv_forw_packet *forw_packet;
|
||||
struct hlist_node *safe_tmp_node;
|
||||
|
||||
hlist_for_each_entry_safe(forw_packet, safe_tmp_node, head,
|
||||
cleanup_list) {
|
||||
cancel_delayed_work_sync(&forw_packet->delayed_work);
|
||||
|
||||
hlist_del(&forw_packet->cleanup_list);
|
||||
batadv_forw_packet_free(forw_packet, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_forw_packet_queue - try to queue a forwarding packet
|
||||
* @forw_packet: the forwarding packet to queue
|
||||
* @lock: a key to the store (e.g. forw_{bat,bcast}_list_lock)
|
||||
* @head: the shelve to queue it on (e.g. forw_{bat,bcast}_list)
|
||||
* @send_time: timestamp (jiffies) when the packet is to be sent
|
||||
*
|
||||
* This function tries to (re)queue a forwarding packet. Requeuing
|
||||
* is prevented if the according interface is shutting down
|
||||
* (e.g. if batadv_forw_packet_list_steal() was called for this
|
||||
* packet earlier).
|
||||
*
|
||||
* Calling batadv_forw_packet_queue() after a call to
|
||||
* batadv_forw_packet_steal() is forbidden!
|
||||
*
|
||||
* Caller needs to ensure that forw_packet->delayed_work was initialized.
|
||||
*/
|
||||
static void batadv_forw_packet_queue(struct batadv_forw_packet *forw_packet,
|
||||
spinlock_t *lock, struct hlist_head *head,
|
||||
unsigned long send_time)
|
||||
{
|
||||
spin_lock_bh(lock);
|
||||
|
||||
/* did purging routine steal it from us? */
|
||||
if (batadv_forw_packet_was_stolen(forw_packet)) {
|
||||
/* If you got it for free() without trouble, then
|
||||
* don't get back into the queue after stealing...
|
||||
*/
|
||||
WARN_ONCE(hlist_fake(&forw_packet->cleanup_list),
|
||||
"Requeuing after batadv_forw_packet_steal() not allowed!\n");
|
||||
|
||||
spin_unlock_bh(lock);
|
||||
return;
|
||||
}
|
||||
|
||||
hlist_del_init(&forw_packet->list);
|
||||
hlist_add_head(&forw_packet->list, head);
|
||||
|
||||
queue_delayed_work(batadv_event_workqueue,
|
||||
&forw_packet->delayed_work,
|
||||
send_time - jiffies);
|
||||
spin_unlock_bh(lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_forw_packet_bcast_queue - try to queue a broadcast packet
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @forw_packet: the forwarding packet to queue
|
||||
* @send_time: timestamp (jiffies) when the packet is to be sent
|
||||
*
|
||||
* This function tries to (re)queue a broadcast packet.
|
||||
*
|
||||
* Caller needs to ensure that forw_packet->delayed_work was initialized.
|
||||
*/
|
||||
static void
|
||||
batadv_forw_packet_bcast_queue(struct batadv_priv *bat_priv,
|
||||
struct batadv_forw_packet *forw_packet,
|
||||
unsigned long send_time)
|
||||
{
|
||||
batadv_forw_packet_queue(forw_packet, &bat_priv->forw_bcast_list_lock,
|
||||
&bat_priv->forw_bcast_list, send_time);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_forw_packet_ogmv1_queue - try to queue an OGMv1 packet
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @forw_packet: the forwarding packet to queue
|
||||
* @send_time: timestamp (jiffies) when the packet is to be sent
|
||||
*
|
||||
* This function tries to (re)queue an OGMv1 packet.
|
||||
*
|
||||
* Caller needs to ensure that forw_packet->delayed_work was initialized.
|
||||
*/
|
||||
void batadv_forw_packet_ogmv1_queue(struct batadv_priv *bat_priv,
|
||||
struct batadv_forw_packet *forw_packet,
|
||||
unsigned long send_time)
|
||||
{
|
||||
batadv_forw_packet_queue(forw_packet, &bat_priv->forw_bat_list_lock,
|
||||
&bat_priv->forw_bat_list, send_time);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -600,7 +775,7 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
|
|||
INIT_DELAYED_WORK(&forw_packet->delayed_work,
|
||||
batadv_send_outstanding_bcast_packet);
|
||||
|
||||
_batadv_add_bcast_packet_to_list(bat_priv, forw_packet, delay);
|
||||
batadv_forw_packet_bcast_queue(bat_priv, forw_packet, jiffies + delay);
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
err_packet_free:
|
||||
|
@ -619,6 +794,7 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work)
|
|||
struct sk_buff *skb1;
|
||||
struct net_device *soft_iface;
|
||||
struct batadv_priv *bat_priv;
|
||||
unsigned long send_time = jiffies + msecs_to_jiffies(5);
|
||||
bool dropped = false;
|
||||
u8 *neigh_addr;
|
||||
u8 *orig_neigh;
|
||||
|
@ -630,10 +806,6 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work)
|
|||
soft_iface = forw_packet->if_incoming->soft_iface;
|
||||
bat_priv = netdev_priv(soft_iface);
|
||||
|
||||
spin_lock_bh(&bat_priv->forw_bcast_list_lock);
|
||||
hlist_del(&forw_packet->list);
|
||||
spin_unlock_bh(&bat_priv->forw_bcast_list_lock);
|
||||
|
||||
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) {
|
||||
dropped = true;
|
||||
goto out;
|
||||
|
@ -714,22 +886,34 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work)
|
|||
|
||||
/* if we still have some more bcasts to send */
|
||||
if (forw_packet->num_packets < BATADV_NUM_BCASTS_MAX) {
|
||||
_batadv_add_bcast_packet_to_list(bat_priv, forw_packet,
|
||||
msecs_to_jiffies(5));
|
||||
batadv_forw_packet_bcast_queue(bat_priv, forw_packet,
|
||||
send_time);
|
||||
return;
|
||||
}
|
||||
|
||||
out:
|
||||
batadv_forw_packet_free(forw_packet, dropped);
|
||||
/* do we get something for free()? */
|
||||
if (batadv_forw_packet_steal(forw_packet,
|
||||
&bat_priv->forw_bcast_list_lock))
|
||||
batadv_forw_packet_free(forw_packet, dropped);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_purge_outstanding_packets - stop/purge scheduled bcast/OGMv1 packets
|
||||
* @bat_priv: the bat priv with all the soft interface information
|
||||
* @hard_iface: the hard interface to cancel and purge bcast/ogm packets on
|
||||
*
|
||||
* This method cancels and purges any broadcast and OGMv1 packet on the given
|
||||
* hard_iface. If hard_iface is NULL, broadcast and OGMv1 packets on all hard
|
||||
* interfaces will be canceled and purged.
|
||||
*
|
||||
* This function might sleep.
|
||||
*/
|
||||
void
|
||||
batadv_purge_outstanding_packets(struct batadv_priv *bat_priv,
|
||||
const struct batadv_hard_iface *hard_iface)
|
||||
{
|
||||
struct batadv_forw_packet *forw_packet;
|
||||
struct hlist_node *safe_tmp_node;
|
||||
bool pending;
|
||||
struct hlist_head head = HLIST_HEAD_INIT;
|
||||
|
||||
if (hard_iface)
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
|
@ -739,57 +923,18 @@ batadv_purge_outstanding_packets(struct batadv_priv *bat_priv,
|
|||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"purge_outstanding_packets()\n");
|
||||
|
||||
/* free bcast list */
|
||||
/* claim bcast list for free() */
|
||||
spin_lock_bh(&bat_priv->forw_bcast_list_lock);
|
||||
hlist_for_each_entry_safe(forw_packet, safe_tmp_node,
|
||||
&bat_priv->forw_bcast_list, list) {
|
||||
/* if purge_outstanding_packets() was called with an argument
|
||||
* we delete only packets belonging to the given interface
|
||||
*/
|
||||
if ((hard_iface) &&
|
||||
(forw_packet->if_incoming != hard_iface) &&
|
||||
(forw_packet->if_outgoing != hard_iface))
|
||||
continue;
|
||||
|
||||
spin_unlock_bh(&bat_priv->forw_bcast_list_lock);
|
||||
|
||||
/* batadv_send_outstanding_bcast_packet() will lock the list to
|
||||
* delete the item from the list
|
||||
*/
|
||||
pending = cancel_delayed_work_sync(&forw_packet->delayed_work);
|
||||
spin_lock_bh(&bat_priv->forw_bcast_list_lock);
|
||||
|
||||
if (pending) {
|
||||
hlist_del(&forw_packet->list);
|
||||
batadv_forw_packet_free(forw_packet, true);
|
||||
}
|
||||
}
|
||||
batadv_forw_packet_list_steal(&bat_priv->forw_bcast_list, &head,
|
||||
hard_iface);
|
||||
spin_unlock_bh(&bat_priv->forw_bcast_list_lock);
|
||||
|
||||
/* free batman packet list */
|
||||
/* claim batman packet list for free() */
|
||||
spin_lock_bh(&bat_priv->forw_bat_list_lock);
|
||||
hlist_for_each_entry_safe(forw_packet, safe_tmp_node,
|
||||
&bat_priv->forw_bat_list, list) {
|
||||
/* if purge_outstanding_packets() was called with an argument
|
||||
* we delete only packets belonging to the given interface
|
||||
*/
|
||||
if ((hard_iface) &&
|
||||
(forw_packet->if_incoming != hard_iface) &&
|
||||
(forw_packet->if_outgoing != hard_iface))
|
||||
continue;
|
||||
|
||||
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
|
||||
|
||||
/* send_outstanding_bat_packet() will lock the list to
|
||||
* delete the item from the list
|
||||
*/
|
||||
pending = cancel_delayed_work_sync(&forw_packet->delayed_work);
|
||||
spin_lock_bh(&bat_priv->forw_bat_list_lock);
|
||||
|
||||
if (pending) {
|
||||
hlist_del(&forw_packet->list);
|
||||
batadv_forw_packet_free(forw_packet, true);
|
||||
}
|
||||
}
|
||||
batadv_forw_packet_list_steal(&bat_priv->forw_bat_list, &head,
|
||||
hard_iface);
|
||||
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
|
||||
|
||||
/* then cancel or wait for packet workers to finish and free */
|
||||
batadv_forw_packet_list_free(&head);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "main.h"
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "packet.h"
|
||||
|
@ -34,6 +35,10 @@ batadv_forw_packet_alloc(struct batadv_hard_iface *if_incoming,
|
|||
struct batadv_hard_iface *if_outgoing,
|
||||
atomic_t *queue_left,
|
||||
struct batadv_priv *bat_priv);
|
||||
bool batadv_forw_packet_steal(struct batadv_forw_packet *packet, spinlock_t *l);
|
||||
void batadv_forw_packet_ogmv1_queue(struct batadv_priv *bat_priv,
|
||||
struct batadv_forw_packet *forw_packet,
|
||||
unsigned long send_time);
|
||||
|
||||
int batadv_send_skb_to_orig(struct sk_buff *skb,
|
||||
struct batadv_orig_node *orig_node,
|
||||
|
|
|
@ -646,6 +646,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;
|
||||
|
@ -657,6 +658,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))
|
||||
|
@ -730,7 +734,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;
|
||||
|
@ -790,7 +794,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;
|
||||
|
@ -814,6 +818,8 @@ check_roaming:
|
|||
|
||||
ret = true;
|
||||
out:
|
||||
if (in_hardif)
|
||||
batadv_hardif_put(in_hardif);
|
||||
if (in_dev)
|
||||
dev_put(in_dev);
|
||||
if (tt_local)
|
||||
|
|
|
@ -118,12 +118,28 @@ struct batadv_hard_iface_bat_v {
|
|||
u8 flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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),
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 +158,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;
|
||||
|
@ -1368,6 +1385,7 @@ struct batadv_skb_cb {
|
|||
/**
|
||||
* struct batadv_forw_packet - structure for bcast packets to be sent/forwarded
|
||||
* @list: list node for batadv_priv::forw_{bat,bcast}_list
|
||||
* @cleanup_list: list node for purging functions
|
||||
* @send_time: execution time for delayed_work (packet sending)
|
||||
* @own: bool for locally generated packets (local OGMs are re-scheduled after
|
||||
* sending)
|
||||
|
@ -1384,6 +1402,7 @@ struct batadv_skb_cb {
|
|||
*/
|
||||
struct batadv_forw_packet {
|
||||
struct hlist_node list;
|
||||
struct hlist_node cleanup_list;
|
||||
unsigned long send_time;
|
||||
u8 own;
|
||||
struct sk_buff *skb;
|
||||
|
|
Loading…
Reference in a new issue