Subject: [PATCH] etun: Implement ethtool controls. From: Eric W. Biederman Date: 1137574796 -0700 - Allow ethtool to control rx/tx checksum avoidence - Allow ethtool to control sg mode. - Keep the __LINK_STATE_NOCARRIER flag in sync with the join link state of the pair of devices and use the generic ethtool carrier detect function. - Add a big fat comment explaining my checksum avoidance strategy by default disable everything but allow the user to override it. Finallly I have a fully featured etun device. --- drivers/net/etun.c | 123 +++++++++++++++++++++++++++++++++++++++++----------- 1 files changed, 96 insertions(+), 27 deletions(-) b6c3b937b939bc8c5f1d98e8221726a8e0b7e85d diff --git a/drivers/net/etun.c b/drivers/net/etun.c index 15009a8..da5ea8d 100644 --- a/drivers/net/etun.c +++ b/drivers/net/etun.c @@ -24,12 +24,37 @@ #include -/* I believe the possibility of briding precludes the disabling - * checksum checking, on an internal interface. +/* Device cheksum strategy. + * + * etun is designed to a be a pair of virutal devices + * connecting two network stack instances. + * + * Typically it will either be used with ethernet bridging or + * it will be used to route packets between the two stacks. + * + * The only checksum offloading I can do is to completely + * skip the checksumming step all together. + * + * When used for ethernet bridging I don't believe any + * checksum off loading is safe. * - If my source is an external interface the checksum may be * invalid so I don't want to report I have already checked it. - * - If my destination is an external interface I don't want to - * put a packet on the write with noone computing the checksum. + * - If my destination is an external interface I don't want to put + * a packet on the wire with someone computing the checksum. + * + * When used for routing between two stacks checksums should + * be as unnecessary as they are on the loopback device. + * + * So by default I am safe and disable checksumming and + * other advanced features like SG and TSO. + * + * However because I think these features could be useful + * I provide the ethtool functions to and enable/disable + * them at runtime. + * + * If you think you can correctly enable these go ahead. + * For checksums both the transmitter and the receiver must + * agree before the are actually disabled. */ #define ETUN_MUST_CHECKSUM 1 @@ -44,10 +69,11 @@ static struct { }; struct etun_info { - struct list_head list; - struct net_device *dev; struct net_device *rx_dev; + unsigned ip_summed; struct net_device_stats stats; + struct list_head list; + struct net_device *dev; }; /* @@ -72,11 +98,12 @@ static int etun_xmit(struct sk_buff *skb skb->pkt_type = PACKET_HOST; skb->protocol = eth_type_trans(skb, rx_dev); skb->dev = rx_dev; + skb->ip_summed = CHECKSUM_NONE; + + /* If both halves agree no checksum is needed */ + if (tx_dev->features & NETIF_F_NO_CSUM) + skb->ip_summed = rx_info->ip_summed; -#ifndef ETUN_MUST_CHECKSUM - /* There is not time for the checksums to go bad... */ - skb->ip_summed = CHECKSUM_UNNECESSARY; -#endif rx_dev->last_rx = jiffies; rx_info->stats.rx_packets++; rx_info->stats.rx_bytes += skb->len; @@ -114,14 +141,6 @@ static void etun_get_drvinfo(struct net_ strcpy(info->fw_version, "N/A"); } -static u32 etun_get_link(struct net_device *tx_dev) -{ - struct etun_info *tx_info = tx_dev->priv; - struct net_device *rx_dev = tx_info->rx_dev; - return !!(rx_dev->flags & IFF_UP); - -} - static void etun_get_strings(struct net_device *dev, u32 stringset, u8 *buf) { switch(stringset) { @@ -147,24 +166,76 @@ static void etun_get_ethtool_stats(struc data[0] = info->rx_dev->ifindex; } +static u32 etun_get_rx_csum(struct net_device *dev) +{ + struct etun_info *info = dev->priv; + return info->ip_summed == CHECKSUM_UNNECESSARY; +} + +static int etun_set_rx_csum(struct net_device *dev, u32 data) +{ + struct etun_info *info = dev->priv; + + info->ip_summed = data ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE; + + return 0; +} + +static u32 etun_get_tx_csum(struct net_device *dev) +{ + return (dev->features & NETIF_F_NO_CSUM) != 0; +} + +static int etun_set_tx_csum(struct net_device *dev, u32 data) +{ + dev->features &= NETIF_F_NO_CSUM; + if (data) + dev->features |= NETIF_F_NO_CSUM; + + return 0; +} + static struct ethtool_ops etun_ethtool_ops = { .get_settings = etun_get_settings, .get_drvinfo = etun_get_drvinfo, - .get_link = etun_get_link, + .get_link = ethtool_op_get_link, + .get_rx_csum = etun_get_rx_csum, + .set_rx_csum = etun_set_rx_csum, + .get_tx_csum = etun_get_tx_csum, + .set_tx_csum = etun_set_tx_csum, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, +#if 0 /* Does just setting the bit successfuly emulate tso? */ + .get_tso = ethtool_op_get_tso, + .set_tso = ethtool_op_set_tso, +#endif .get_strings = etun_get_strings, .get_stats_count = etun_get_stats_count, .get_ethtool_stats = etun_get_ethtool_stats, + .get_perm_addr = ethtool_op_get_perm_addr, }; -static int etun_open(struct net_device *dev) +static int etun_open(struct net_device *tx_dev) { - netif_start_queue(dev); + struct etun_info *tx_info = tx_dev->priv; + struct net_device *rx_dev = tx_info->rx_dev; + if (rx_dev->flags & IFF_UP) { + netif_carrier_on(tx_dev); + netif_carrier_on(rx_dev); + } + netif_start_queue(tx_dev); return 0; } -static int etun_stop(struct net_device *dev) +static int etun_stop(struct net_device *tx_dev) { - netif_stop_queue(dev); + struct etun_info *tx_info = tx_dev->priv; + struct net_device *rx_dev = tx_info->rx_dev; + netif_stop_queue(tx_dev); + if (netif_carrier_ok(tx_dev)) { + netif_carrier_off(tx_dev); + netif_carrier_off(rx_dev); + } return 0; } @@ -201,16 +272,14 @@ static struct net_device *etun_alloc(voi dev->stop = etun_stop; dev->set_multicast_list = etun_set_multicast_list; dev->do_ioctl = etun_ioctl; - dev->features = NETIF_F_SG | NETIF_F_FRAGLIST -#ifndef ETUN_MUST_CHECKSUM - | NETIF_F_NO_CSUM -#endif + dev->features = NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | NETIF_F_LLTX; dev->flags = IFF_BROADCAST | IFF_MULTICAST |IFF_PROMISC; dev->ethtool_ops = &etun_ethtool_ops; dev->priv = info; dev->destructor = free_netdev; + netif_carrier_off(dev); err = register_netdev(dev); if (err) { spin_lock(&etun_lock); -- 1.0.GIT