Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelv...
[pandora-kernel.git] / drivers / staging / batman-adv / hard-interface.c
index 6e973a7..80cfa86 100644 (file)
@@ -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;