net/hsr: Implemented .ndo_fix_features (better device features handling).
authorArvid Brodin <arvid.brodin@alten.se>
Fri, 4 Jul 2014 21:38:57 +0000 (23:38 +0200)
committerDavid S. Miller <davem@davemloft.net>
Tue, 8 Jul 2014 18:35:31 +0000 (11:35 -0700)
Signed-off-by: Arvid Brodin <arvid.brodin@alten.se>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/hsr/hsr_device.c
net/hsr/hsr_slave.c

index 1fc4ea20752e035436f0e042a527c9a2a89a94e8..c489aed98df4a05e503148354b3c6f4f3f9fa024 100644 (file)
@@ -198,6 +198,39 @@ static int hsr_dev_close(struct net_device *dev)
 }
 
 
+static netdev_features_t hsr_features_recompute(struct hsr_priv *hsr,
+                                               netdev_features_t features)
+{
+       netdev_features_t mask;
+       struct hsr_port *port;
+
+       mask = features;
+
+       /* Mask out all features that, if supported by one device, should be
+        * enabled for all devices (see NETIF_F_ONE_FOR_ALL).
+        *
+        * Anything that's off in mask will not be enabled - so only things
+        * that were in features originally, and also is in NETIF_F_ONE_FOR_ALL,
+        * may become enabled.
+        */
+       features &= ~NETIF_F_ONE_FOR_ALL;
+       hsr_for_each_port(hsr, port)
+               features = netdev_increment_features(features,
+                                                    port->dev->features,
+                                                    mask);
+
+       return features;
+}
+
+static netdev_features_t hsr_fix_features(struct net_device *dev,
+                                         netdev_features_t features)
+{
+       struct hsr_priv *hsr = netdev_priv(dev);
+
+       return hsr_features_recompute(hsr, features);
+}
+
+
 static void hsr_fill_tag(struct hsr_ethhdr *hsr_ethhdr, struct hsr_priv *hsr)
 {
        unsigned long irqflags;
@@ -465,6 +498,7 @@ static const struct net_device_ops hsr_device_ops = {
        .ndo_open = hsr_dev_open,
        .ndo_stop = hsr_dev_close,
        .ndo_start_xmit = hsr_dev_xmit,
+       .ndo_fix_features = hsr_fix_features,
 };
 
 
@@ -478,6 +512,19 @@ void hsr_dev_setup(struct net_device *dev)
        dev->tx_queue_len        = 0;
 
        dev->destructor = hsr_dev_destroy;
+
+       dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
+                          NETIF_F_GSO_MASK | NETIF_F_HW_CSUM |
+                          NETIF_F_HW_VLAN_CTAG_TX;
+
+       dev->features = dev->hw_features;
+
+       /* Prevent recursive tx locking */
+       dev->features |= NETIF_F_LLTX;
+       /* VLAN on top of HSR needs testing and probably some work on
+        * hsr_header_create() etc.
+        */
+       dev->features |= NETIF_F_VLAN_CHALLENGED;
 }
 
 
@@ -513,14 +560,6 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
        if (res < 0)
                return res;
 
-       hsr_dev->features = slave[0]->features & slave[1]->features;
-       /* Prevent recursive tx locking */
-       hsr_dev->features |= NETIF_F_LLTX;
-       /* VLAN on top of HSR needs testing and probably some work on
-        * hsr_header_create() etc.
-        */
-       hsr_dev->features |= NETIF_F_VLAN_CHALLENGED;
-
        spin_lock_init(&hsr->seqnr_lock);
        /* Overflow soon to find bugs easier: */
        hsr->sequence_nr = USHRT_MAX - 1024;
index fffd69297c3ebb101afa00d89e79b39bd1028347..23817d0b765b68dff83b143b9b21091cffb1865b 100644 (file)
@@ -344,6 +344,7 @@ int hsr_add_port(struct hsr_priv *hsr, struct net_device *dev,
        if (dev->hard_header_len + HSR_HLEN > master->dev->hard_header_len)
                master->dev->hard_header_len = dev->hard_header_len + HSR_HLEN;
 
+       netdev_update_features(master->dev);
        dev_set_mtu(master->dev, hsr_get_max_mtu(hsr));
 
        return 0;
@@ -363,6 +364,7 @@ void hsr_del_port(struct hsr_port *port)
        list_del_rcu(&port->port_list);
 
        if (port != master) {
+               netdev_update_features(master->dev);
                dev_set_mtu(master->dev, hsr_get_max_mtu(hsr));
                netdev_rx_handler_unregister(port->dev);
                dev_set_promiscuity(port->dev, -1);