diff options
Diffstat (limited to 'net/batman-adv/soft-interface.c')
-rw-r--r-- | net/batman-adv/soft-interface.c | 192 |
1 files changed, 133 insertions, 59 deletions
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 9ed26140a269..9e5fcd1596cf 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -43,8 +43,6 @@ static void bat_get_drvinfo(struct net_device *dev, static u32 bat_get_msglevel(struct net_device *dev); static void bat_set_msglevel(struct net_device *dev, u32 value); static u32 bat_get_link(struct net_device *dev); -static u32 bat_get_rx_csum(struct net_device *dev); -static int bat_set_rx_csum(struct net_device *dev, u32 data); static const struct ethtool_ops bat_ethtool_ops = { .get_settings = bat_get_settings, @@ -52,8 +50,6 @@ static const struct ethtool_ops bat_ethtool_ops = { .get_msglevel = bat_get_msglevel, .set_msglevel = bat_set_msglevel, .get_link = bat_get_link, - .get_rx_csum = bat_get_rx_csum, - .set_rx_csum = bat_set_rx_csum }; int my_skb_head_push(struct sk_buff *skb, unsigned int len) @@ -90,10 +86,51 @@ static void softif_neigh_free_ref(struct softif_neigh *softif_neigh) call_rcu(&softif_neigh->rcu, softif_neigh_free_rcu); } +static struct softif_neigh *softif_neigh_get_selected(struct bat_priv *bat_priv) +{ + struct softif_neigh *neigh; + + rcu_read_lock(); + neigh = rcu_dereference(bat_priv->softif_neigh); + + if (neigh && !atomic_inc_not_zero(&neigh->refcount)) + neigh = NULL; + + rcu_read_unlock(); + return neigh; +} + +static void softif_neigh_select(struct bat_priv *bat_priv, + struct softif_neigh *new_neigh) +{ + struct softif_neigh *curr_neigh; + + spin_lock_bh(&bat_priv->softif_neigh_lock); + + if (new_neigh && !atomic_inc_not_zero(&new_neigh->refcount)) + new_neigh = NULL; + + curr_neigh = bat_priv->softif_neigh; + rcu_assign_pointer(bat_priv->softif_neigh, new_neigh); + + if (curr_neigh) + softif_neigh_free_ref(curr_neigh); + + spin_unlock_bh(&bat_priv->softif_neigh_lock); +} + +static void softif_neigh_deselect(struct bat_priv *bat_priv) +{ + softif_neigh_select(bat_priv, NULL); +} + void softif_neigh_purge(struct bat_priv *bat_priv) { - struct softif_neigh *softif_neigh, *softif_neigh_tmp; + struct softif_neigh *softif_neigh, *curr_softif_neigh; struct hlist_node *node, *node_tmp; + char do_deselect = 0; + + curr_softif_neigh = softif_neigh_get_selected(bat_priv); spin_lock_bh(&bat_priv->softif_neigh_lock); @@ -105,22 +142,26 @@ void softif_neigh_purge(struct bat_priv *bat_priv) (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE)) continue; - hlist_del_rcu(&softif_neigh->list); - - if (bat_priv->softif_neigh == softif_neigh) { + if (curr_softif_neigh == softif_neigh) { bat_dbg(DBG_ROUTES, bat_priv, "Current mesh exit point '%pM' vanished " "(vid: %d).\n", softif_neigh->addr, softif_neigh->vid); - softif_neigh_tmp = bat_priv->softif_neigh; - bat_priv->softif_neigh = NULL; - softif_neigh_free_ref(softif_neigh_tmp); + do_deselect = 1; } + hlist_del_rcu(&softif_neigh->list); softif_neigh_free_ref(softif_neigh); } spin_unlock_bh(&bat_priv->softif_neigh_lock); + + /* soft_neigh_deselect() needs to acquire the softif_neigh_lock */ + if (do_deselect) + softif_neigh_deselect(bat_priv); + + if (curr_softif_neigh) + softif_neigh_free_ref(curr_softif_neigh); } static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv, @@ -170,26 +211,44 @@ int softif_neigh_seq_print_text(struct seq_file *seq, void *offset) struct net_device *net_dev = (struct net_device *)seq->private; struct bat_priv *bat_priv = netdev_priv(net_dev); struct softif_neigh *softif_neigh; + struct hard_iface *primary_if; struct hlist_node *node; + struct softif_neigh *curr_softif_neigh; + int ret = 0; + + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) { + ret = seq_printf(seq, "BATMAN mesh %s disabled - " + "please specify interfaces to enable it\n", + net_dev->name); + goto out; + } - if (!bat_priv->primary_if) { - return seq_printf(seq, "BATMAN mesh %s disabled - " - "please specify interfaces to enable it\n", - net_dev->name); + if (primary_if->if_status != IF_ACTIVE) { + ret = seq_printf(seq, "BATMAN mesh %s " + "disabled - primary interface not active\n", + net_dev->name); + goto out; } seq_printf(seq, "Softif neighbor list (%s)\n", net_dev->name); + curr_softif_neigh = softif_neigh_get_selected(bat_priv); rcu_read_lock(); hlist_for_each_entry_rcu(softif_neigh, node, &bat_priv->softif_neigh_list, list) seq_printf(seq, "%s %pM (vid: %d)\n", - bat_priv->softif_neigh == softif_neigh + curr_softif_neigh == softif_neigh ? "=>" : " ", softif_neigh->addr, softif_neigh->vid); rcu_read_unlock(); + if (curr_softif_neigh) + softif_neigh_free_ref(curr_softif_neigh); - return 0; +out: + if (primary_if) + hardif_free_ref(primary_if); + return ret; } static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev, @@ -198,7 +257,9 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev, struct bat_priv *bat_priv = netdev_priv(dev); struct ethhdr *ethhdr = (struct ethhdr *)skb->data; struct batman_packet *batman_packet; - struct softif_neigh *softif_neigh, *softif_neigh_tmp; + struct softif_neigh *softif_neigh = NULL; + struct hard_iface *primary_if = NULL; + struct softif_neigh *curr_softif_neigh = NULL; if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) batman_packet = (struct batman_packet *) @@ -207,63 +268,72 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev, batman_packet = (struct batman_packet *)(skb->data + ETH_HLEN); if (batman_packet->version != COMPAT_VERSION) - goto err; + goto out; if (batman_packet->packet_type != BAT_PACKET) - goto err; + goto out; if (!(batman_packet->flags & PRIMARIES_FIRST_HOP)) - goto err; + goto out; if (is_my_mac(batman_packet->orig)) - goto err; + goto out; softif_neigh = softif_neigh_get(bat_priv, batman_packet->orig, vid); - if (!softif_neigh) - goto err; + goto out; + + curr_softif_neigh = softif_neigh_get_selected(bat_priv); + if (!curr_softif_neigh) + goto out; + + if (curr_softif_neigh == softif_neigh) + goto out; - if (bat_priv->softif_neigh == softif_neigh) + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) goto out; /* we got a neighbor but its mac is 'bigger' than ours */ - if (memcmp(bat_priv->primary_if->net_dev->dev_addr, + if (memcmp(primary_if->net_dev->dev_addr, softif_neigh->addr, ETH_ALEN) < 0) goto out; /* switch to new 'smallest neighbor' */ - if ((bat_priv->softif_neigh) && - (memcmp(softif_neigh->addr, bat_priv->softif_neigh->addr, + if ((curr_softif_neigh) && + (memcmp(softif_neigh->addr, curr_softif_neigh->addr, ETH_ALEN) < 0)) { bat_dbg(DBG_ROUTES, bat_priv, "Changing mesh exit point from %pM (vid: %d) " "to %pM (vid: %d).\n", - bat_priv->softif_neigh->addr, - bat_priv->softif_neigh->vid, + curr_softif_neigh->addr, + curr_softif_neigh->vid, softif_neigh->addr, softif_neigh->vid); - softif_neigh_tmp = bat_priv->softif_neigh; - bat_priv->softif_neigh = softif_neigh; - softif_neigh_free_ref(softif_neigh_tmp); - /* we need to hold the additional reference */ - goto err; + + softif_neigh_select(bat_priv, softif_neigh); + goto out; } /* close own batX device and use softif_neigh as exit node */ - if ((!bat_priv->softif_neigh) && + if ((!curr_softif_neigh) && (memcmp(softif_neigh->addr, - bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN) < 0)) { + primary_if->net_dev->dev_addr, ETH_ALEN) < 0)) { bat_dbg(DBG_ROUTES, bat_priv, "Setting mesh exit point to %pM (vid: %d).\n", softif_neigh->addr, softif_neigh->vid); - bat_priv->softif_neigh = softif_neigh; - /* we need to hold the additional reference */ - goto err; + + softif_neigh_select(bat_priv, softif_neigh); + goto out; } out: - softif_neigh_free_ref(softif_neigh); -err: kfree_skb(skb); + if (softif_neigh) + softif_neigh_free_ref(softif_neigh); + if (curr_softif_neigh) + softif_neigh_free_ref(curr_softif_neigh); + if (primary_if) + hardif_free_ref(primary_if); return; } @@ -319,8 +389,10 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) { struct ethhdr *ethhdr = (struct ethhdr *)skb->data; struct bat_priv *bat_priv = netdev_priv(soft_iface); + struct hard_iface *primary_if = NULL; struct bcast_packet *bcast_packet; struct vlan_ethhdr *vhdr; + struct softif_neigh *curr_softif_neigh = NULL; int data_len = skb->len, ret; short vid = -1; bool do_bcast = false; @@ -348,7 +420,8 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) * if we have a another chosen mesh exit node in range * it will transport the packets to the mesh */ - if ((bat_priv->softif_neigh) && (bat_priv->softif_neigh->vid == vid)) + curr_softif_neigh = softif_neigh_get_selected(bat_priv); + if ((curr_softif_neigh) && (curr_softif_neigh->vid == vid)) goto dropped; /* TODO: check this for locks */ @@ -366,7 +439,8 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) /* ethernet packet should be broadcasted */ if (do_bcast) { - if (!bat_priv->primary_if) + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) goto dropped; if (my_skb_head_push(skb, sizeof(struct bcast_packet)) < 0) @@ -382,7 +456,7 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) /* hw address of first interface is the orig mac because only * this mac is known throughout the mesh */ memcpy(bcast_packet->orig, - bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); + primary_if->net_dev->dev_addr, ETH_ALEN); /* set broadcast sequence number */ bcast_packet->seqno = @@ -410,6 +484,10 @@ dropped: dropped_freed: bat_priv->stats.tx_dropped++; end: + if (curr_softif_neigh) + softif_neigh_free_ref(curr_softif_neigh); + if (primary_if) + hardif_free_ref(primary_if); return NETDEV_TX_OK; } @@ -421,6 +499,7 @@ void interface_rx(struct net_device *soft_iface, struct unicast_packet *unicast_packet; struct ethhdr *ethhdr; struct vlan_ethhdr *vhdr; + struct softif_neigh *curr_softif_neigh = NULL; short vid = -1; int ret; @@ -450,7 +529,8 @@ void interface_rx(struct net_device *soft_iface, * if we have a another chosen mesh exit node in range * it will transport the packets to the non-mesh network */ - if ((bat_priv->softif_neigh) && (bat_priv->softif_neigh->vid == vid)) { + curr_softif_neigh = softif_neigh_get_selected(bat_priv); + if (curr_softif_neigh && (curr_softif_neigh->vid == vid)) { skb_push(skb, hdr_size); unicast_packet = (struct unicast_packet *)skb->data; @@ -461,7 +541,7 @@ void interface_rx(struct net_device *soft_iface, skb_reset_mac_header(skb); memcpy(unicast_packet->dest, - bat_priv->softif_neigh->addr, ETH_ALEN); + curr_softif_neigh->addr, ETH_ALEN); ret = route_unicast_packet(skb, recv_if); if (ret == NET_RX_DROP) goto dropped; @@ -474,7 +554,7 @@ void interface_rx(struct net_device *soft_iface, goto dropped; skb->protocol = eth_type_trans(skb, soft_iface); - /* should not be neccesary anymore as we use skb_pull_rcsum() + /* should not be necessary anymore as we use skb_pull_rcsum() * TODO: please verify this and remove this TODO * -- Dec 21st 2009, Simon Wunderlich */ @@ -486,11 +566,13 @@ void interface_rx(struct net_device *soft_iface, soft_iface->last_rx = jiffies; netif_rx(skb); - return; + goto out; dropped: kfree_skb(skb); out: + if (curr_softif_neigh) + softif_neigh_free_ref(curr_softif_neigh); return; } @@ -524,6 +606,7 @@ static void interface_setup(struct net_device *dev) dev->hard_start_xmit = interface_tx; #endif dev->destructor = free_netdev; + dev->tx_queue_len = 0; /** * can't call min_mtu, because the needed variables @@ -640,7 +723,7 @@ static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { cmd->supported = 0; cmd->advertising = 0; - cmd->speed = SPEED_10; + ethtool_cmd_speed_set(cmd, SPEED_10); cmd->duplex = DUPLEX_FULL; cmd->port = PORT_TP; cmd->phy_address = 0; @@ -675,12 +758,3 @@ static u32 bat_get_link(struct net_device *dev) return 1; } -static u32 bat_get_rx_csum(struct net_device *dev) -{ - return 0; -} - -static int bat_set_rx_csum(struct net_device *dev, u32 data) -{ - return -EOPNOTSUPP; -} |