void netif_stacked_transfer_operstate(const struct net_device *rootdev,
                                        struct net_device *dev);
 
-int netif_get_vlan_features(struct sk_buff *skb, struct net_device *dev);
+int netif_skb_features(struct sk_buff *skb);
 
 static inline int net_gso_ok(int features, int gso_type)
 {
 static inline int netif_needs_gso(struct net_device *dev, struct sk_buff *skb)
 {
        if (skb_is_gso(skb)) {
-               int features = netif_get_vlan_features(skb, dev);
+               int features = netif_skb_features(skb);
 
                return (!skb_gso_ok(skb, features) ||
                        unlikely(skb->ip_summed != CHECKSUM_PARTIAL));
 
        }
 }
 
-int netif_get_vlan_features(struct sk_buff *skb, struct net_device *dev)
+static int harmonize_features(struct sk_buff *skb, __be16 protocol, int features)
+{
+       if (!can_checksum_protocol(protocol, features)) {
+               features &= ~NETIF_F_ALL_CSUM;
+               features &= ~NETIF_F_SG;
+       } else if (illegal_highdma(skb->dev, skb)) {
+               features &= ~NETIF_F_SG;
+       }
+
+       return features;
+}
+
+int netif_skb_features(struct sk_buff *skb)
 {
        __be16 protocol = skb->protocol;
+       int features = skb->dev->features;
 
        if (protocol == htons(ETH_P_8021Q)) {
                struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data;
                protocol = veh->h_vlan_encapsulated_proto;
-       } else if (!skb->vlan_tci)
-               return dev->features;
+       } else if (!vlan_tx_tag_present(skb)) {
+               return harmonize_features(skb, protocol, features);
+       }
 
-       if (protocol != htons(ETH_P_8021Q))
-               return dev->features & dev->vlan_features;
-       else
-               return 0;
+       features &= skb->dev->vlan_features;
+
+       if (protocol != htons(ETH_P_8021Q)) {
+               return harmonize_features(skb, protocol, features);
+       } else {
+               features &= NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST |
+                               NETIF_F_GEN_CSUM;
+               return harmonize_features(skb, protocol, features);
+       }
 }
-EXPORT_SYMBOL(netif_get_vlan_features);
+EXPORT_SYMBOL(netif_skb_features);
 
 /*
  * Returns true if either: