diff options
Diffstat (limited to 'net/8021q/vlan_dev.c')
| -rw-r--r-- | net/8021q/vlan_dev.c | 151 | 
1 files changed, 99 insertions, 52 deletions
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 52984267781..3d59c9bf8fe 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -142,6 +142,7 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,  {  	struct vlan_hdr *vhdr;  	struct vlan_rx_stats *rx_stats; +	struct net_device *vlan_dev;  	u16 vlan_id;  	u16 vlan_tci; @@ -157,53 +158,71 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,  	vlan_id = vlan_tci & VLAN_VID_MASK;  	rcu_read_lock(); -	skb->dev = __find_vlan_dev(dev, vlan_id); -	if (!skb->dev) { -		pr_debug("%s: ERROR: No net_device for VID: %u on dev: %s\n", -			 __func__, vlan_id, dev->name); -		goto err_unlock; -	} +	vlan_dev = __find_vlan_dev(dev, vlan_id); -	rx_stats = per_cpu_ptr(vlan_dev_info(skb->dev)->vlan_rx_stats, -			       smp_processor_id()); -	rx_stats->rx_packets++; -	rx_stats->rx_bytes += skb->len; +	/* If the VLAN device is defined, we use it. +	 * If not, and the VID is 0, it is a 802.1p packet (not +	 * really a VLAN), so we will just netif_rx it later to the +	 * original interface, but with the skb->proto set to the +	 * wrapped proto: we do nothing here. +	 */ -	skb_pull_rcsum(skb, VLAN_HLEN); +	if (!vlan_dev) { +		if (vlan_id) { +			pr_debug("%s: ERROR: No net_device for VID: %u on dev: %s\n", +				 __func__, vlan_id, dev->name); +			goto err_unlock; +		} +		rx_stats = NULL; +	} else { +		skb->dev = vlan_dev; -	skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tci); +		rx_stats = per_cpu_ptr(vlan_dev_info(skb->dev)->vlan_rx_stats, +					smp_processor_id()); +		u64_stats_update_begin(&rx_stats->syncp); +		rx_stats->rx_packets++; +		rx_stats->rx_bytes += skb->len; -	pr_debug("%s: priority: %u for TCI: %hu\n", -		 __func__, skb->priority, vlan_tci); +		skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tci); -	switch (skb->pkt_type) { -	case PACKET_BROADCAST: /* Yeah, stats collect these together.. */ -		/* stats->broadcast ++; // no such counter :-( */ -		break; +		pr_debug("%s: priority: %u for TCI: %hu\n", +			 __func__, skb->priority, vlan_tci); -	case PACKET_MULTICAST: -		rx_stats->multicast++; -		break; +		switch (skb->pkt_type) { +		case PACKET_BROADCAST: +			/* Yeah, stats collect these together.. */ +			/* stats->broadcast ++; // no such counter :-( */ +			break; -	case PACKET_OTHERHOST: -		/* Our lower layer thinks this is not local, let's make sure. -		 * This allows the VLAN to have a different MAC than the -		 * underlying device, and still route correctly. -		 */ -		if (!compare_ether_addr(eth_hdr(skb)->h_dest, -					skb->dev->dev_addr)) -			skb->pkt_type = PACKET_HOST; -		break; -	default: -		break; +		case PACKET_MULTICAST: +			rx_stats->rx_multicast++; +			break; + +		case PACKET_OTHERHOST: +			/* Our lower layer thinks this is not local, let's make +			 * sure. +			 * This allows the VLAN to have a different MAC than the +			 * underlying device, and still route correctly. +			 */ +			if (!compare_ether_addr(eth_hdr(skb)->h_dest, +						skb->dev->dev_addr)) +				skb->pkt_type = PACKET_HOST; +			break; +		default: +			break; +		} +		u64_stats_update_end(&rx_stats->syncp);  	} +	skb_pull_rcsum(skb, VLAN_HLEN);  	vlan_set_encap_proto(skb, vhdr); -	skb = vlan_check_reorder_header(skb); -	if (!skb) { -		rx_stats->rx_errors++; -		goto err_unlock; +	if (vlan_dev) { +		skb = vlan_check_reorder_header(skb); +		if (!skb) { +			rx_stats->rx_errors++; +			goto err_unlock; +		}  	}  	netif_rx(skb); @@ -801,37 +820,65 @@ static u32 vlan_ethtool_get_flags(struct net_device *dev)  	return dev_ethtool_get_flags(vlan->real_dev);  } -static struct net_device_stats *vlan_dev_get_stats(struct net_device *dev) +static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)  { -	struct net_device_stats *stats = &dev->stats; -  	dev_txq_stats_fold(dev, stats);  	if (vlan_dev_info(dev)->vlan_rx_stats) { -		struct vlan_rx_stats *p, rx = {0}; +		struct vlan_rx_stats *p, accum = {0};  		int i;  		for_each_possible_cpu(i) { +			u64 rxpackets, rxbytes, rxmulticast; +			unsigned int start; +  			p = per_cpu_ptr(vlan_dev_info(dev)->vlan_rx_stats, i); -			rx.rx_packets += p->rx_packets; -			rx.rx_bytes   += p->rx_bytes; -			rx.rx_errors  += p->rx_errors; -			rx.multicast  += p->multicast; +			do { +				start = u64_stats_fetch_begin_bh(&p->syncp); +				rxpackets	= p->rx_packets; +				rxbytes		= p->rx_bytes; +				rxmulticast	= p->rx_multicast; +			} while (u64_stats_fetch_retry_bh(&p->syncp, start)); +			accum.rx_packets += rxpackets; +			accum.rx_bytes   += rxbytes; +			accum.rx_multicast += rxmulticast; +			/* rx_errors is an ulong, not protected by syncp */ +			accum.rx_errors  += p->rx_errors;  		} -		stats->rx_packets = rx.rx_packets; -		stats->rx_bytes   = rx.rx_bytes; -		stats->rx_errors  = rx.rx_errors; -		stats->multicast  = rx.multicast; +		stats->rx_packets = accum.rx_packets; +		stats->rx_bytes   = accum.rx_bytes; +		stats->rx_errors  = accum.rx_errors; +		stats->multicast  = accum.rx_multicast;  	}  	return stats;  } +static int vlan_ethtool_set_tso(struct net_device *dev, u32 data) +{ +       if (data) { +		struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + +		/* Underlying device must support TSO for VLAN-tagged packets +		 * and must have TSO enabled now. +		 */ +		if (!(real_dev->vlan_features & NETIF_F_TSO)) +			return -EOPNOTSUPP; +		if (!(real_dev->features & NETIF_F_TSO)) +			return -EINVAL; +		dev->features |= NETIF_F_TSO; +	} else { +		dev->features &= ~NETIF_F_TSO; +	} +	return 0; +} +  static const struct ethtool_ops vlan_ethtool_ops = {  	.get_settings	        = vlan_ethtool_get_settings,  	.get_drvinfo	        = vlan_ethtool_get_drvinfo,  	.get_link		= ethtool_op_get_link,  	.get_rx_csum		= vlan_ethtool_get_rx_csum,  	.get_flags		= vlan_ethtool_get_flags, +	.set_tso                = vlan_ethtool_set_tso,  };  static const struct net_device_ops vlan_netdev_ops = { @@ -848,7 +895,7 @@ static const struct net_device_ops vlan_netdev_ops = {  	.ndo_change_rx_flags	= vlan_dev_change_rx_flags,  	.ndo_do_ioctl		= vlan_dev_ioctl,  	.ndo_neigh_setup	= vlan_dev_neigh_setup, -	.ndo_get_stats		= vlan_dev_get_stats, +	.ndo_get_stats64	= vlan_dev_get_stats64,  #if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)  	.ndo_fcoe_ddp_setup	= vlan_dev_fcoe_ddp_setup,  	.ndo_fcoe_ddp_done	= vlan_dev_fcoe_ddp_done, @@ -872,7 +919,7 @@ static const struct net_device_ops vlan_netdev_accel_ops = {  	.ndo_change_rx_flags	= vlan_dev_change_rx_flags,  	.ndo_do_ioctl		= vlan_dev_ioctl,  	.ndo_neigh_setup	= vlan_dev_neigh_setup, -	.ndo_get_stats		= vlan_dev_get_stats, +	.ndo_get_stats64	= vlan_dev_get_stats64,  #if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)  	.ndo_fcoe_ddp_setup	= vlan_dev_fcoe_ddp_setup,  	.ndo_fcoe_ddp_done	= vlan_dev_fcoe_ddp_done, @@ -897,7 +944,7 @@ static const struct net_device_ops vlan_netdev_ops_sq = {  	.ndo_change_rx_flags	= vlan_dev_change_rx_flags,  	.ndo_do_ioctl		= vlan_dev_ioctl,  	.ndo_neigh_setup	= vlan_dev_neigh_setup, -	.ndo_get_stats		= vlan_dev_get_stats, +	.ndo_get_stats64	= vlan_dev_get_stats64,  #if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)  	.ndo_fcoe_ddp_setup	= vlan_dev_fcoe_ddp_setup,  	.ndo_fcoe_ddp_done	= vlan_dev_fcoe_ddp_done, @@ -922,7 +969,7 @@ static const struct net_device_ops vlan_netdev_accel_ops_sq = {  	.ndo_change_rx_flags	= vlan_dev_change_rx_flags,  	.ndo_do_ioctl		= vlan_dev_ioctl,  	.ndo_neigh_setup	= vlan_dev_neigh_setup, -	.ndo_get_stats		= vlan_dev_get_stats, +	.ndo_get_stats64	= vlan_dev_get_stats64,  #if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)  	.ndo_fcoe_ddp_setup	= vlan_dev_fcoe_ddp_setup,  	.ndo_fcoe_ddp_done	= vlan_dev_fcoe_ddp_done,  |