[BRIDGE]: limited ethtool support
Stephen Hemminger [Thu, 22 Dec 2005 03:00:58 +0000 (19:00 -0800)]
Add limited ethtool support to bridge to allow disabling
features.

Note: if underlying device does not support a feature (like checksum
offload), then the bridge device won't inherit it.

Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

net/bridge/br_device.c
net/bridge/br_if.c
net/bridge/br_private.h

index f7a66ab..0b33a7b 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/kernel.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/ethtool.h>
 
 #include <asm/uaccess.h>
 #include "br_private.h"
@@ -106,6 +107,64 @@ static int br_set_mac_address(struct net_device *dev, void *p)
        return err;
 }
 
+static void br_getinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+       strcpy(info->driver, "bridge");
+       strcpy(info->version, BR_VERSION);
+       strcpy(info->fw_version, "N/A");
+       strcpy(info->bus_info, "N/A");
+}
+
+static int br_set_sg(struct net_device *dev, u32 data)
+{
+       struct net_bridge *br = netdev_priv(dev);
+
+       if (data)
+               br->feature_mask |= NETIF_F_SG;
+       else
+               br->feature_mask &= ~NETIF_F_SG;
+
+       br_features_recompute(br);
+       return 0;
+}
+
+static int br_set_tso(struct net_device *dev, u32 data)
+{
+       struct net_bridge *br = netdev_priv(dev);
+
+       if (data)
+               br->feature_mask |= NETIF_F_TSO;
+       else
+               br->feature_mask &= ~NETIF_F_TSO;
+
+       br_features_recompute(br);
+       return 0;
+}
+
+static int br_set_tx_csum(struct net_device *dev, u32 data)
+{
+       struct net_bridge *br = netdev_priv(dev);
+
+       if (data)
+               br->feature_mask |= NETIF_F_IP_CSUM;
+       else
+               br->feature_mask &= ~NETIF_F_IP_CSUM;
+
+       br_features_recompute(br);
+       return 0;
+}
+
+static struct ethtool_ops br_ethtool_ops = {
+       .get_drvinfo = br_getinfo,
+       .get_link = ethtool_op_get_link,
+       .get_sg = ethtool_op_get_sg,
+       .set_sg = br_set_sg,
+       .get_tx_csum = ethtool_op_get_tx_csum,
+       .set_tx_csum = br_set_tx_csum,
+       .get_tso = ethtool_op_get_tso,
+       .set_tso = br_set_tso,
+};
+
 void br_dev_setup(struct net_device *dev)
 {
        memset(dev->dev_addr, 0, ETH_ALEN);
@@ -120,8 +179,12 @@ void br_dev_setup(struct net_device *dev)
        dev->change_mtu = br_change_mtu;
        dev->destructor = free_netdev;
        SET_MODULE_OWNER(dev);
+       SET_ETHTOOL_OPS(dev, &br_ethtool_ops);
        dev->stop = br_dev_stop;
        dev->tx_queue_len = 0;
        dev->set_mac_address = br_set_mac_address;
        dev->priv_flags = IFF_EBRIDGE;
+
+       dev->features = NETIF_F_SG | NETIF_F_FRAGLIST
+               | NETIF_F_HIGHDMA | NETIF_F_TSO | NETIF_F_IP_CSUM;
 }
index e6b8bb5..1132119 100644 (file)
@@ -182,6 +182,7 @@ static struct net_device *new_bridge_dev(const char *name)
        br->bridge_id.prio[1] = 0x00;
        memset(br->bridge_id.addr, 0, ETH_ALEN);
 
+       br->feature_mask = dev->features;
        br->stp_enabled = 0;
        br->designated_root = br->bridge_id;
        br->root_path_cost = 0;
@@ -349,9 +350,8 @@ void br_features_recompute(struct net_bridge *br)
        struct net_bridge_port *p;
        unsigned long features, checksum;
 
-       features = NETIF_F_SG | NETIF_F_FRAGLIST 
-               | NETIF_F_HIGHDMA | NETIF_F_TSO;
-       checksum = NETIF_F_IP_CSUM;     /* least commmon subset */
+       features = br->feature_mask &~ NETIF_F_IP_CSUM;
+       checksum = br->feature_mask & NETIF_F_IP_CSUM;
 
        list_for_each_entry(p, &br->port_list, list) {
                if (!(p->dev->features 
index 7ad53c2..db7b26b 100644 (file)
@@ -93,6 +93,7 @@ struct net_bridge
        spinlock_t                      hash_lock;
        struct hlist_head               hash[BR_HASH_SIZE];
        struct list_head                age_list;
+       unsigned long                   feature_mask;
 
        /* STP */
        bridge_id                       designated_root;