X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=drivers%2Fstaging%2Fbatman-adv%2Fhard-interface.c;h=ff0ac98250014f5ef4848e15f72ca76787056972;hb=641ee3f8f4375bde4e216aacd26d286a34efed61;hp=6e973a79aa25b6fec509639687fc4e196a568397;hpb=506ecbca71d07fa327dd986be1682e90885678ee;p=firefly-linux-kernel-4.4.55.git diff --git a/drivers/staging/batman-adv/hard-interface.c b/drivers/staging/batman-adv/hard-interface.c index 6e973a79aa25..ff0ac9825001 100644 --- a/drivers/staging/batman-adv/hard-interface.c +++ b/drivers/staging/batman-adv/hard-interface.c @@ -33,6 +33,18 @@ #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); + +static void hardif_free_rcu(struct rcu_head *rcu) +{ + struct batman_if *batman_if; + + batman_if = container_of(rcu, struct batman_if, rcu); + dev_put(batman_if->net_dev); + kref_put(&batman_if->refcount, hardif_free_ref); +} + struct batman_if *get_batman_if_by_netdev(struct net_device *net_dev) { struct batman_if *batman_if; @@ -46,6 +58,9 @@ struct batman_if *get_batman_if_by_netdev(struct net_device *net_dev) batman_if = NULL; out: + if (batman_if) + kref_get(&batman_if->refcount); + rcu_read_unlock(); return batman_if; } @@ -77,13 +92,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 +108,54 @@ static struct batman_if *get_active_batman_if(void) batman_if = NULL; out: + if (batman_if) + kref_get(&batman_if->refcount); + 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) + kref_get(&batman_if->refcount); + old_if = bat_priv->primary_if; bat_priv->primary_if = batman_if; + if (old_if) + kref_put(&old_if->refcount, hardif_free_ref); + 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,18 +168,13 @@ 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, batman_if->net_dev->dev_addr, ETH_ALEN); } -static void check_known_mac_addr(uint8_t *addr) +static void check_known_mac_addr(struct net_device *net_dev) { struct batman_if *batman_if; @@ -149,53 +184,69 @@ static void check_known_mac_addr(uint8_t *addr) (batman_if->if_status != IF_TO_BE_ACTIVATED)) continue; - if (!compare_orig(batman_if->net_dev->dev_addr, addr)) + if (batman_if->net_dev == net_dev) + continue; + + if (!compare_orig(batman_if->net_dev->dev_addr, + net_dev->dev_addr)) continue; pr_warning("The newly added mac address (%pM) already exists " - "on: %s\n", addr, batman_if->dev); + "on: %s\n", net_dev->dev_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->fragmentation)) + 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 +257,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 +272,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 +321,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; + kref_get(&batman_if->refcount); + 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->fragmentation) && 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->fragmentation) && 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 +372,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); + kref_put(&batman_if->refcount, hardif_free_ref); + 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) + kref_put(&new_if->refcount, hardif_free_ref); + } 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 +432,27 @@ 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); + kref_init(&batman_if->refcount); + + check_known_mac_addr(batman_if->net_dev); - 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 */ + kref_get(&batman_if->refcount); return batman_if; -free_dev: - kfree(batman_if->dev); free_if: kfree(batman_if); release_dev: @@ -353,18 +461,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 +471,29 @@ static void hardif_remove_interface(struct batman_if *batman_if) return; batman_if->if_status = IF_TO_BE_REMOVED; - list_del_rcu(&batman_if->list); sysfs_del_hardif(&batman_if->hardif_obj); - dev_put(batman_if->net_dev); - call_rcu(&batman_if->rcu, hardif_free_interface); + call_rcu(&batman_if->rcu, hardif_free_rcu); } void hardif_remove_interfaces(void) { struct batman_if *batman_if, *batman_if_tmp; + struct list_head if_queue; + + INIT_LIST_HEAD(&if_queue); - list_for_each_entry_safe(batman_if, batman_if_tmp, &if_list, list) + spin_lock(&if_list_lock); + list_for_each_entry_safe(batman_if, batman_if_tmp, &if_list, list) { + list_del_rcu(&batman_if->list); + list_add_tail(&batman_if->list, &if_queue); + } + spin_unlock(&if_list_lock); + + rtnl_lock(); + list_for_each_entry_safe(batman_if, batman_if_tmp, &if_queue, list) { hardif_remove_interface(batman_if); + } + rtnl_unlock(); } static int hard_if_event(struct notifier_block *this, @@ -394,38 +501,50 @@ 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); + list_del_rcu(&batman_if->list); + spin_unlock(&if_list_lock); + hardif_remove_interface(batman_if); break; - case NETDEV_CHANGENAME: + case NETDEV_CHANGEMTU: + if (batman_if->soft_iface) + update_min_mtu(batman_if->soft_iface); break; case NETDEV_CHANGEADDR: - check_known_mac_addr(batman_if->net_dev->dev_addr); + if (batman_if->if_status == IF_NOT_IN_USE) + goto hardif_put; + + check_known_mac_addr(batman_if->net_dev); 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: + kref_put(&batman_if->refcount, hardif_free_ref); out: return NOTIFY_DONE; } @@ -435,23 +554,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 +575,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 +607,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 +615,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;