From e47bd51a7b2ae66b00c19cc86f7dcb1eeb4ad105 Mon Sep 17 00:00:00 2001 From: Justin Pettit Date: Wed, 17 Aug 2011 14:38:08 -0700 Subject: [PATCH] netdev-linux: Introduce netdev_linux_ethtool_set_flag(). There will be a caller added soon. --- lib/netdev-linux.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++ lib/netdev-linux.h | 7 ++++++ 2 files changed, 61 insertions(+) diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 05b830c5..838dac6e 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -90,6 +90,15 @@ COVERAGE_DEFINE(netdev_ethtool); #define ADVERTISED_Asym_Pause (1 << 14) #endif +/* These were introduced in Linux 2.6.24, so they might be missing if we + * have old headers. */ +#ifndef ETHTOOL_GFLAGS +#define ETHTOOL_GFLAGS 0x00000025 /* Get flags bitmap(ethtool_value) */ +#endif +#ifndef ETHTOOL_SFLAGS +#define ETHTOOL_SFLAGS 0x00000026 /* Set flags bitmap(ethtool_value) */ +#endif + /* This was introduced in Linux 2.6.25, so it might be missing if we have old * headers. */ #ifndef TC_RTAB_SIZE @@ -4210,6 +4219,51 @@ netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *ecmd, } } +/* Modifies the 'flag' bit in ethtool's flags field for 'netdev'. If + * 'enable' is true, the bit is set. Otherwise, it is cleared. */ +int +netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag, + const char *flag_name, bool enable) +{ + const char *netdev_name = netdev_get_name(netdev); + struct ethtool_value evalue; + uint32_t new_flags; + int error; + + memset(&evalue, 0, sizeof evalue); + error = netdev_linux_do_ethtool(netdev_name, + (struct ethtool_cmd *)&evalue, + ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS"); + if (error) { + return error; + } + + evalue.data = new_flags = (evalue.data & ~flag) | (enable ? flag : 0); + error = netdev_linux_do_ethtool(netdev_name, + (struct ethtool_cmd *)&evalue, + ETHTOOL_SFLAGS, "ETHTOOL_SFLAGS"); + if (error) { + return error; + } + + memset(&evalue, 0, sizeof evalue); + error = netdev_linux_do_ethtool(netdev_name, + (struct ethtool_cmd *)&evalue, + ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS"); + if (error) { + return error; + } + + if (new_flags != evalue.data) { + VLOG_WARN_RL(&rl, "attempt to %s ethtool %s flag on network " + "device %s failed", enable ? "enable" : "disable", + flag_name, netdev_name); + return EOPNOTSUPP; + } + + return 0; +} + static int netdev_linux_do_ioctl(const char *name, struct ifreq *ifr, int cmd, const char *cmd_name) diff --git a/lib/netdev-linux.h b/lib/netdev-linux.h index 7a112049..d34a4401 100644 --- a/lib/netdev-linux.h +++ b/lib/netdev-linux.h @@ -17,9 +17,13 @@ #ifndef NETDEV_LINUX_H #define NETDEV_LINUX_H 1 +#include +#include + /* These functions are Linux specific, so they should be used directly only by * Linux-specific code. */ +struct netdev; struct netdev_stats; struct rtnl_link_stats; struct rtnl_link_stats64; @@ -31,4 +35,7 @@ void netdev_stats_from_rtnl_link_stats64(struct netdev_stats *dst, void netdev_stats_to_rtnl_link_stats64(struct rtnl_link_stats64 *dst, const struct netdev_stats *src); +int netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag, + const char *flag_name, bool enable); + #endif /* netdev-linux.h */ -- 2.30.2