summaryrefslogtreecommitdiffstats
path: root/drivers/staging/batman-adv/hard-interface.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/batman-adv/hard-interface.c')
-rw-r--r--drivers/staging/batman-adv/hard-interface.c291
1 files changed, 199 insertions, 92 deletions
diff --git a/drivers/staging/batman-adv/hard-interface.c b/drivers/staging/batman-adv/hard-interface.c
index 6e973a7..80cfa86 100644
--- a/drivers/staging/batman-adv/hard-interface.c
+++ b/drivers/staging/batman-adv/hard-interface.c
@@ -33,6 +33,9 @@
#define MIN(x, y) ((x) < (y) ? (x) : (y))
+/* protect update critical side of if_list - but not the content */
+static DEFINE_SPINLOCK(if_list_lock);
+
struct batman_if *get_batman_if_by_netdev(struct net_device *net_dev)
{
struct batman_if *batman_if;
@@ -46,6 +49,9 @@ struct batman_if *get_batman_if_by_netdev(struct net_device *net_dev)
batman_if = NULL;
out:
+ if (batman_if)
+ hardif_hold(batman_if);
+
rcu_read_unlock();
return batman_if;
}
@@ -77,13 +83,15 @@ static int is_valid_iface(struct net_device *net_dev)
return 1;
}
-static struct batman_if *get_active_batman_if(void)
+static struct batman_if *get_active_batman_if(struct net_device *soft_iface)
{
struct batman_if *batman_if;
- /* TODO: should check interfaces belonging to bat_priv */
rcu_read_lock();
list_for_each_entry_rcu(batman_if, &if_list, list) {
+ if (batman_if->soft_iface != soft_iface)
+ continue;
+
if (batman_if->if_status == IF_ACTIVE)
goto out;
}
@@ -91,31 +99,54 @@ static struct batman_if *get_active_batman_if(void)
batman_if = NULL;
out:
+ if (batman_if)
+ hardif_hold(batman_if);
+
rcu_read_unlock();
return batman_if;
}
+static void update_primary_addr(struct bat_priv *bat_priv)
+{
+ struct vis_packet *vis_packet;
+
+ vis_packet = (struct vis_packet *)
+ bat_priv->my_vis_info->skb_packet->data;
+ memcpy(vis_packet->vis_orig,
+ bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
+ memcpy(vis_packet->sender_orig,
+ bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
+}
+
static void set_primary_if(struct bat_priv *bat_priv,
struct batman_if *batman_if)
{
struct batman_packet *batman_packet;
+ struct batman_if *old_if;
+
+ if (batman_if)
+ hardif_hold(batman_if);
+ old_if = bat_priv->primary_if;
bat_priv->primary_if = batman_if;
+ if (old_if)
+ hardif_put(old_if);
+
if (!bat_priv->primary_if)
return;
- set_main_if_addr(batman_if->net_dev->dev_addr);
-
batman_packet = (struct batman_packet *)(batman_if->packet_buff);
batman_packet->flags = PRIMARIES_FIRST_HOP;
batman_packet->ttl = TTL;
+ update_primary_addr(bat_priv);
+
/***
* hacky trick to make sure that we send the HNA information via
* our new primary interface
*/
- atomic_set(&hna_local_changed, 1);
+ atomic_set(&bat_priv->hna_local_changed, 1);
}
static bool hardif_is_iface_up(struct batman_if *batman_if)
@@ -128,11 +159,6 @@ static bool hardif_is_iface_up(struct batman_if *batman_if)
static void update_mac_addresses(struct batman_if *batman_if)
{
- if (!batman_if || !batman_if->packet_buff)
- return;
-
- addr_to_string(batman_if->addr_str, batman_if->net_dev->dev_addr);
-
memcpy(((struct batman_packet *)(batman_if->packet_buff))->orig,
batman_if->net_dev->dev_addr, ETH_ALEN);
memcpy(((struct batman_packet *)(batman_if->packet_buff))->prev_sender,
@@ -153,49 +179,60 @@ static void check_known_mac_addr(uint8_t *addr)
continue;
pr_warning("The newly added mac address (%pM) already exists "
- "on: %s\n", addr, batman_if->dev);
+ "on: %s\n", addr, batman_if->net_dev->name);
pr_warning("It is strongly recommended to keep mac addresses "
"unique to avoid problems!\n");
}
rcu_read_unlock();
}
-int hardif_min_mtu(void)
+int hardif_min_mtu(struct net_device *soft_iface)
{
+ struct bat_priv *bat_priv = netdev_priv(soft_iface);
struct batman_if *batman_if;
/* allow big frames if all devices are capable to do so
* (have MTU > 1500 + BAT_HEADER_LEN) */
int min_mtu = ETH_DATA_LEN;
+ if (atomic_read(&bat_priv->frag_enabled))
+ goto out;
+
rcu_read_lock();
list_for_each_entry_rcu(batman_if, &if_list, list) {
- if ((batman_if->if_status == IF_ACTIVE) ||
- (batman_if->if_status == IF_TO_BE_ACTIVATED))
- min_mtu = MIN(batman_if->net_dev->mtu - BAT_HEADER_LEN,
- min_mtu);
+ if ((batman_if->if_status != IF_ACTIVE) &&
+ (batman_if->if_status != IF_TO_BE_ACTIVATED))
+ continue;
+
+ if (batman_if->soft_iface != soft_iface)
+ continue;
+
+ min_mtu = MIN(batman_if->net_dev->mtu - BAT_HEADER_LEN,
+ min_mtu);
}
rcu_read_unlock();
-
+out:
return min_mtu;
}
/* adjusts the MTU if a new interface with a smaller MTU appeared. */
-void update_min_mtu(void)
+void update_min_mtu(struct net_device *soft_iface)
{
int min_mtu;
- min_mtu = hardif_min_mtu();
- if (soft_device->mtu != min_mtu)
- soft_device->mtu = min_mtu;
+ min_mtu = hardif_min_mtu(soft_iface);
+ if (soft_iface->mtu != min_mtu)
+ soft_iface->mtu = min_mtu;
}
-static void hardif_activate_interface(struct net_device *net_dev,
- struct bat_priv *bat_priv,
- struct batman_if *batman_if)
+static void hardif_activate_interface(struct batman_if *batman_if)
{
+ struct bat_priv *bat_priv;
+
if (batman_if->if_status != IF_INACTIVE)
return;
+ bat_priv = netdev_priv(batman_if->soft_iface);
+
update_mac_addresses(batman_if);
batman_if->if_status = IF_TO_BE_ACTIVATED;
@@ -206,17 +243,14 @@ static void hardif_activate_interface(struct net_device *net_dev,
if (!bat_priv->primary_if)
set_primary_if(bat_priv, batman_if);
- bat_info(net_dev, "Interface activated: %s\n", batman_if->dev);
-
- if (atomic_read(&module_state) == MODULE_INACTIVE)
- activate_module();
+ bat_info(batman_if->soft_iface, "Interface activated: %s\n",
+ batman_if->net_dev->name);
- update_min_mtu();
+ update_min_mtu(batman_if->soft_iface);
return;
}
-static void hardif_deactivate_interface(struct net_device *net_dev,
- struct batman_if *batman_if)
+static void hardif_deactivate_interface(struct batman_if *batman_if)
{
if ((batman_if->if_status != IF_ACTIVE) &&
(batman_if->if_status != IF_TO_BE_ACTIVATED))
@@ -224,26 +258,39 @@ static void hardif_deactivate_interface(struct net_device *net_dev,
batman_if->if_status = IF_INACTIVE;
- bat_info(net_dev, "Interface deactivated: %s\n", batman_if->dev);
+ bat_info(batman_if->soft_iface, "Interface deactivated: %s\n",
+ batman_if->net_dev->name);
- update_min_mtu();
+ update_min_mtu(batman_if->soft_iface);
}
-int hardif_enable_interface(struct batman_if *batman_if)
+int hardif_enable_interface(struct batman_if *batman_if, char *iface_name)
{
- /* FIXME: each batman_if will be attached to a softif */
- struct bat_priv *bat_priv = netdev_priv(soft_device);
+ struct bat_priv *bat_priv;
struct batman_packet *batman_packet;
if (batman_if->if_status != IF_NOT_IN_USE)
goto out;
+ batman_if->soft_iface = dev_get_by_name(&init_net, iface_name);
+
+ if (!batman_if->soft_iface) {
+ batman_if->soft_iface = softif_create(iface_name);
+
+ if (!batman_if->soft_iface)
+ goto err;
+
+ /* dev_get_by_name() increases the reference counter for us */
+ dev_hold(batman_if->soft_iface);
+ }
+
+ bat_priv = netdev_priv(batman_if->soft_iface);
batman_if->packet_len = BAT_PACKET_LEN;
batman_if->packet_buff = kmalloc(batman_if->packet_len, GFP_ATOMIC);
if (!batman_if->packet_buff) {
- bat_err(soft_device, "Can't add interface packet (%s): "
- "out of memory\n", batman_if->dev);
+ bat_err(batman_if->soft_iface, "Can't add interface packet "
+ "(%s): out of memory\n", batman_if->net_dev->name);
goto err;
}
@@ -260,15 +307,44 @@ int hardif_enable_interface(struct batman_if *batman_if)
batman_if->if_status = IF_INACTIVE;
orig_hash_add_if(batman_if, bat_priv->num_ifaces);
+ batman_if->batman_adv_ptype.type = __constant_htons(ETH_P_BATMAN);
+ batman_if->batman_adv_ptype.func = batman_skb_recv;
+ batman_if->batman_adv_ptype.dev = batman_if->net_dev;
+ hardif_hold(batman_if);
+ dev_add_pack(&batman_if->batman_adv_ptype);
+
atomic_set(&batman_if->seqno, 1);
- bat_info(soft_device, "Adding interface: %s\n", batman_if->dev);
+ atomic_set(&batman_if->frag_seqno, 1);
+ bat_info(batman_if->soft_iface, "Adding interface: %s\n",
+ batman_if->net_dev->name);
+
+ if (atomic_read(&bat_priv->frag_enabled) && batman_if->net_dev->mtu <
+ ETH_DATA_LEN + BAT_HEADER_LEN)
+ bat_info(batman_if->soft_iface,
+ "The MTU of interface %s is too small (%i) to handle "
+ "the transport of batman-adv packets. Packets going "
+ "over this interface will be fragmented on layer2 "
+ "which could impact the performance. Setting the MTU "
+ "to %zi would solve the problem.\n",
+ batman_if->net_dev->name, batman_if->net_dev->mtu,
+ ETH_DATA_LEN + BAT_HEADER_LEN);
+
+ if (!atomic_read(&bat_priv->frag_enabled) && batman_if->net_dev->mtu <
+ ETH_DATA_LEN + BAT_HEADER_LEN)
+ bat_info(batman_if->soft_iface,
+ "The MTU of interface %s is too small (%i) to handle "
+ "the transport of batman-adv packets. If you experience"
+ " problems getting traffic through try increasing the "
+ "MTU to %zi.\n",
+ batman_if->net_dev->name, batman_if->net_dev->mtu,
+ ETH_DATA_LEN + BAT_HEADER_LEN);
if (hardif_is_iface_up(batman_if))
- hardif_activate_interface(soft_device, bat_priv, batman_if);
+ hardif_activate_interface(batman_if);
else
- bat_err(soft_device, "Not using interface %s "
+ bat_err(batman_if->soft_iface, "Not using interface %s "
"(retrying later): interface not active\n",
- batman_if->dev);
+ batman_if->net_dev->name);
/* begin scheduling originator messages on that interface */
schedule_own_packet(batman_if);
@@ -282,29 +358,46 @@ err:
void hardif_disable_interface(struct batman_if *batman_if)
{
- /* FIXME: each batman_if will be attached to a softif */
- struct bat_priv *bat_priv = netdev_priv(soft_device);
+ struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface);
if (batman_if->if_status == IF_ACTIVE)
- hardif_deactivate_interface(soft_device, batman_if);
+ hardif_deactivate_interface(batman_if);
if (batman_if->if_status != IF_INACTIVE)
return;
- bat_info(soft_device, "Removing interface: %s\n", batman_if->dev);
+ bat_info(batman_if->soft_iface, "Removing interface: %s\n",
+ batman_if->net_dev->name);
+ dev_remove_pack(&batman_if->batman_adv_ptype);
+ hardif_put(batman_if);
+
bat_priv->num_ifaces--;
orig_hash_del_if(batman_if, bat_priv->num_ifaces);
- if (batman_if == bat_priv->primary_if)
- set_primary_if(bat_priv, get_active_batman_if());
+ if (batman_if == bat_priv->primary_if) {
+ struct batman_if *new_if;
+
+ new_if = get_active_batman_if(batman_if->soft_iface);
+ set_primary_if(bat_priv, new_if);
+
+ if (new_if)
+ hardif_put(new_if);
+ }
kfree(batman_if->packet_buff);
batman_if->packet_buff = NULL;
batman_if->if_status = IF_NOT_IN_USE;
- if ((atomic_read(&module_state) == MODULE_ACTIVE) &&
- (bat_priv->num_ifaces == 0))
- deactivate_module();
+ /* delete all references to this batman_if */
+ purge_orig_ref(bat_priv);
+ purge_outstanding_packets(bat_priv, batman_if);
+ dev_put(batman_if->soft_iface);
+
+ /* nobody uses this interface anymore */
+ if (!bat_priv->num_ifaces)
+ softif_destroy(batman_if->soft_iface);
+
+ batman_if->soft_iface = NULL;
}
static struct batman_if *hardif_add_interface(struct net_device *net_dev)
@@ -325,26 +418,28 @@ static struct batman_if *hardif_add_interface(struct net_device *net_dev)
goto release_dev;
}
- batman_if->dev = kstrdup(net_dev->name, GFP_ATOMIC);
- if (!batman_if->dev)
- goto free_if;
-
ret = sysfs_add_hardif(&batman_if->hardif_obj, net_dev);
if (ret)
- goto free_dev;
+ goto free_if;
batman_if->if_num = -1;
batman_if->net_dev = net_dev;
+ batman_if->soft_iface = NULL;
batman_if->if_status = IF_NOT_IN_USE;
- batman_if->packet_buff = NULL;
INIT_LIST_HEAD(&batman_if->list);
+ atomic_set(&batman_if->refcnt, 0);
+ hardif_hold(batman_if);
check_known_mac_addr(batman_if->net_dev->dev_addr);
+
+ spin_lock(&if_list_lock);
list_add_tail_rcu(&batman_if->list, &if_list);
+ spin_unlock(&if_list_lock);
+
+ /* extra reference for return */
+ hardif_hold(batman_if);
return batman_if;
-free_dev:
- kfree(batman_if->dev);
free_if:
kfree(batman_if);
release_dev:
@@ -353,18 +448,6 @@ out:
return NULL;
}
-static void hardif_free_interface(struct rcu_head *rcu)
-{
- struct batman_if *batman_if = container_of(rcu, struct batman_if, rcu);
-
- /* delete all references to this batman_if */
- purge_orig(NULL);
- purge_outstanding_packets(batman_if);
-
- kfree(batman_if->dev);
- kfree(batman_if);
-}
-
static void hardif_remove_interface(struct batman_if *batman_if)
{
/* first deactivate interface */
@@ -375,18 +458,25 @@ static void hardif_remove_interface(struct batman_if *batman_if)
return;
batman_if->if_status = IF_TO_BE_REMOVED;
+
+ /* caller must take if_list_lock */
list_del_rcu(&batman_if->list);
+ synchronize_rcu();
sysfs_del_hardif(&batman_if->hardif_obj);
- dev_put(batman_if->net_dev);
- call_rcu(&batman_if->rcu, hardif_free_interface);
+ hardif_put(batman_if);
}
void hardif_remove_interfaces(void)
{
struct batman_if *batman_if, *batman_if_tmp;
- list_for_each_entry_safe(batman_if, batman_if_tmp, &if_list, list)
+ rtnl_lock();
+ spin_lock(&if_list_lock);
+ list_for_each_entry_safe(batman_if, batman_if_tmp, &if_list, list) {
hardif_remove_interface(batman_if);
+ }
+ spin_unlock(&if_list_lock);
+ rtnl_unlock();
}
static int hard_if_event(struct notifier_block *this,
@@ -394,37 +484,48 @@ static int hard_if_event(struct notifier_block *this,
{
struct net_device *net_dev = (struct net_device *)ptr;
struct batman_if *batman_if = get_batman_if_by_netdev(net_dev);
- /* FIXME: each batman_if will be attached to a softif */
- struct bat_priv *bat_priv = netdev_priv(soft_device);
+ struct bat_priv *bat_priv;
if (!batman_if && event == NETDEV_REGISTER)
- batman_if = hardif_add_interface(net_dev);
+ batman_if = hardif_add_interface(net_dev);
if (!batman_if)
goto out;
switch (event) {
case NETDEV_UP:
- hardif_activate_interface(soft_device, bat_priv, batman_if);
+ hardif_activate_interface(batman_if);
break;
case NETDEV_GOING_DOWN:
case NETDEV_DOWN:
- hardif_deactivate_interface(soft_device, batman_if);
+ hardif_deactivate_interface(batman_if);
break;
case NETDEV_UNREGISTER:
+ spin_lock(&if_list_lock);
hardif_remove_interface(batman_if);
+ spin_unlock(&if_list_lock);
break;
- case NETDEV_CHANGENAME:
+ case NETDEV_CHANGEMTU:
+ if (batman_if->soft_iface)
+ update_min_mtu(batman_if->soft_iface);
break;
case NETDEV_CHANGEADDR:
+ if (batman_if->if_status == IF_NOT_IN_USE) {
+ hardif_put(batman_if);
+ goto out;
+ }
+
check_known_mac_addr(batman_if->net_dev->dev_addr);
update_mac_addresses(batman_if);
+
+ bat_priv = netdev_priv(batman_if->soft_iface);
if (batman_if == bat_priv->primary_if)
- set_primary_if(bat_priv, batman_if);
+ update_primary_addr(bat_priv);
break;
default:
break;
};
+ hardif_put(batman_if);
out:
return NOTIFY_DONE;
@@ -435,23 +536,20 @@ out:
int batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *ptype, struct net_device *orig_dev)
{
- /* FIXME: each orig_node->batman_if will be attached to a softif */
- struct bat_priv *bat_priv = netdev_priv(soft_device);
+ struct bat_priv *bat_priv;
struct batman_packet *batman_packet;
struct batman_if *batman_if;
int ret;
+ batman_if = container_of(ptype, struct batman_if, batman_adv_ptype);
skb = skb_share_check(skb, GFP_ATOMIC);
/* skb was released by skb_share_check() */
if (!skb)
goto err_out;
- if (atomic_read(&module_state) != MODULE_ACTIVE)
- goto err_free;
-
/* packet should hold at least type and version */
- if (unlikely(skb_headlen(skb) < 2))
+ if (unlikely(!pskb_may_pull(skb, 2)))
goto err_free;
/* expect a valid ethernet header here. */
@@ -459,8 +557,12 @@ int batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
|| !skb_mac_header(skb)))
goto err_free;
- batman_if = get_batman_if_by_netdev(skb->dev);
- if (!batman_if)
+ if (!batman_if->soft_iface)
+ goto err_free;
+
+ bat_priv = netdev_priv(batman_if->soft_iface);
+
+ if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE)
goto err_free;
/* discard frames on not active interfaces */
@@ -487,7 +589,7 @@ int batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
/* batman icmp packet */
case BAT_ICMP:
- ret = recv_icmp_packet(skb);
+ ret = recv_icmp_packet(skb, batman_if);
break;
/* unicast packet */
@@ -495,14 +597,19 @@ int batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
ret = recv_unicast_packet(skb, batman_if);
break;
+ /* fragmented unicast packet */
+ case BAT_UNICAST_FRAG:
+ ret = recv_ucast_frag_packet(skb, batman_if);
+ break;
+
/* broadcast packet */
case BAT_BCAST:
- ret = recv_bcast_packet(skb);
+ ret = recv_bcast_packet(skb, batman_if);
break;
/* vis packet */
case BAT_VIS:
- ret = recv_vis_packet(skb);
+ ret = recv_vis_packet(skb, batman_if);
break;
default:
ret = NET_RX_DROP;
OpenPOWER on IntegriCloud