diff options
Diffstat (limited to 'net/batman-adv/soft-interface.c')
| -rw-r--r-- | net/batman-adv/soft-interface.c | 190 | 
1 files changed, 132 insertions, 58 deletions
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 824e1f6e50f..9e5fcd1596c 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; @@ -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; -}  |