#define IFF_LOWER_UP 0x10000
#endif
+/* These were introduced in Linux 2.6.14, so they might be missing if we have
+ * old headers. */
+#ifndef ADVERTISED_Pause
+#define ADVERTISED_Pause (1 << 13)
+#endif
+#ifndef ADVERTISED_Asym_Pause
+#define ADVERTISED_Asym_Pause (1 << 14)
+#endif
+
#define THIS_MODULE VLM_netdev
#include "vlog.h"
int txqlen;
int hwaddr_family;
- /* Bitmaps of OFPPF_* that describe features. All bits disabled if
- * unsupported or unavailable. */
- uint32_t curr; /* Current features. */
- uint32_t advertised; /* Features being advertised by the port. */
- uint32_t supported; /* Features supported by the port. */
- uint32_t peer; /* Features advertised by the peer. */
-
int save_flags; /* Initial device flags. */
int changed_flags; /* Flags that we changed. */
};
fclose(file);
}
-static void
-do_ethtool(struct netdev *netdev)
+static int
+do_ethtool(struct netdev *netdev, struct ethtool_cmd *ecmd,
+ int cmd, const char *cmd_name)
{
struct ifreq ifr;
- struct ethtool_cmd ecmd;
-
- netdev->curr = 0;
- netdev->supported = 0;
- netdev->advertised = 0;
- netdev->peer = 0;
memset(&ifr, 0, sizeof ifr);
strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name);
- ifr.ifr_data = (caddr_t) &ecmd;
+ ifr.ifr_data = (caddr_t) ecmd;
- memset(&ecmd, 0, sizeof ecmd);
- ecmd.cmd = ETHTOOL_GSET;
+ ecmd->cmd = cmd;
if (ioctl(netdev->netdev_fd, SIOCETHTOOL, &ifr) == 0) {
- if (ecmd.supported & SUPPORTED_10baseT_Half) {
- netdev->supported |= OFPPF_10MB_HD;
- }
- if (ecmd.supported & SUPPORTED_10baseT_Full) {
- netdev->supported |= OFPPF_10MB_FD;
- }
- if (ecmd.supported & SUPPORTED_100baseT_Half) {
- netdev->supported |= OFPPF_100MB_HD;
- }
- if (ecmd.supported & SUPPORTED_100baseT_Full) {
- netdev->supported |= OFPPF_100MB_FD;
- }
- if (ecmd.supported & SUPPORTED_1000baseT_Half) {
- netdev->supported |= OFPPF_1GB_HD;
- }
- if (ecmd.supported & SUPPORTED_1000baseT_Full) {
- netdev->supported |= OFPPF_1GB_FD;
- }
- if (ecmd.supported & SUPPORTED_10000baseT_Full) {
- netdev->supported |= OFPPF_10GB_FD;
- }
- if (ecmd.supported & SUPPORTED_TP) {
- netdev->supported |= OFPPF_COPPER;
- }
- if (ecmd.supported & SUPPORTED_FIBRE) {
- netdev->supported |= OFPPF_FIBER;
- }
- if (ecmd.supported & SUPPORTED_Autoneg) {
- netdev->supported |= OFPPF_AUTONEG;
- }
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
- if (ecmd.supported & SUPPORTED_Pause) {
- netdev->supported |= OFPPF_PAUSE;
- }
- if (ecmd.supported & SUPPORTED_Asym_Pause) {
- netdev->supported |= OFPPF_PAUSE_ASYM;
- }
-#endif /* kernel >= 2.6.14 */
+ return 0;
+ } else {
+ VLOG_WARN_RL(&rl, "ethtool command %s on network device %s failed: %s",
+ cmd_name, netdev->name, strerror(errno));
+ return errno;
+ }
+}
- /* Set the advertised features */
- if (ecmd.advertising & ADVERTISED_10baseT_Half) {
- netdev->advertised |= OFPPF_10MB_HD;
- }
- if (ecmd.advertising & ADVERTISED_10baseT_Full) {
- netdev->advertised |= OFPPF_10MB_FD;
- }
- if (ecmd.advertising & ADVERTISED_100baseT_Half) {
- netdev->advertised |= OFPPF_100MB_HD;
- }
- if (ecmd.advertising & ADVERTISED_100baseT_Full) {
- netdev->advertised |= OFPPF_100MB_FD;
- }
- if (ecmd.advertising & ADVERTISED_1000baseT_Half) {
- netdev->advertised |= OFPPF_1GB_HD;
- }
- if (ecmd.advertising & ADVERTISED_1000baseT_Full) {
- netdev->advertised |= OFPPF_1GB_FD;
- }
- if (ecmd.advertising & ADVERTISED_10000baseT_Full) {
- netdev->advertised |= OFPPF_10GB_FD;
- }
- if (ecmd.advertising & ADVERTISED_TP) {
- netdev->advertised |= OFPPF_COPPER;
- }
- if (ecmd.advertising & ADVERTISED_FIBRE) {
- netdev->advertised |= OFPPF_FIBER;
- }
- if (ecmd.advertising & ADVERTISED_Autoneg) {
- netdev->advertised |= OFPPF_AUTONEG;
- }
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
- if (ecmd.advertising & ADVERTISED_Pause) {
- netdev->advertised |= OFPPF_PAUSE;
- }
- if (ecmd.advertising & ADVERTISED_Asym_Pause) {
- netdev->advertised |= OFPPF_PAUSE_ASYM;
- }
-#endif /* kernel >= 2.6.14 */
+static int
+do_get_features(struct netdev *netdev,
+ uint32_t *current, uint32_t *advertised,
+ uint32_t *supported, uint32_t *peer)
+{
+ struct ethtool_cmd ecmd;
+ int error;
- /* Set the current features */
- if (ecmd.speed == SPEED_10) {
- netdev->curr = (ecmd.duplex) ? OFPPF_10MB_FD : OFPPF_10MB_HD;
- }
- else if (ecmd.speed == SPEED_100) {
- netdev->curr = (ecmd.duplex) ? OFPPF_100MB_FD : OFPPF_100MB_HD;
- }
- else if (ecmd.speed == SPEED_1000) {
- netdev->curr = (ecmd.duplex) ? OFPPF_1GB_FD : OFPPF_1GB_HD;
- }
- else if (ecmd.speed == SPEED_10000) {
- netdev->curr = OFPPF_10GB_FD;
- }
+ *current = 0;
+ *supported = 0;
+ *advertised = 0;
+ *peer = 0;
- if (ecmd.port == PORT_TP) {
- netdev->curr |= OFPPF_COPPER;
- }
- else if (ecmd.port == PORT_FIBRE) {
- netdev->curr |= OFPPF_FIBER;
- }
+ memset(&ecmd, 0, sizeof ecmd);
+ error = do_ethtool(netdev, &ecmd, ETHTOOL_GSET, "ETHTOOL_GSET");
+ if (error) {
+ return error;
+ }
- if (ecmd.autoneg) {
- netdev->curr |= OFPPF_AUTONEG;
- }
- } else {
- VLOG_DBG("ioctl(SIOCETHTOOL) failed: %s", strerror(errno));
+ if (ecmd.supported & SUPPORTED_10baseT_Half) {
+ *supported |= OFPPF_10MB_HD;
+ }
+ if (ecmd.supported & SUPPORTED_10baseT_Full) {
+ *supported |= OFPPF_10MB_FD;
+ }
+ if (ecmd.supported & SUPPORTED_100baseT_Half) {
+ *supported |= OFPPF_100MB_HD;
+ }
+ if (ecmd.supported & SUPPORTED_100baseT_Full) {
+ *supported |= OFPPF_100MB_FD;
+ }
+ if (ecmd.supported & SUPPORTED_1000baseT_Half) {
+ *supported |= OFPPF_1GB_HD;
+ }
+ if (ecmd.supported & SUPPORTED_1000baseT_Full) {
+ *supported |= OFPPF_1GB_FD;
+ }
+ if (ecmd.supported & SUPPORTED_10000baseT_Full) {
+ *supported |= OFPPF_10GB_FD;
+ }
+ if (ecmd.supported & SUPPORTED_TP) {
+ *supported |= OFPPF_COPPER;
+ }
+ if (ecmd.supported & SUPPORTED_FIBRE) {
+ *supported |= OFPPF_FIBER;
+ }
+ if (ecmd.supported & SUPPORTED_Autoneg) {
+ *supported |= OFPPF_AUTONEG;
+ }
+ if (ecmd.supported & SUPPORTED_Pause) {
+ *supported |= OFPPF_PAUSE;
+ }
+ if (ecmd.supported & SUPPORTED_Asym_Pause) {
+ *supported |= OFPPF_PAUSE_ASYM;
+ }
+
+ /* Set the advertised features */
+ if (ecmd.advertising & ADVERTISED_10baseT_Half) {
+ *advertised |= OFPPF_10MB_HD;
+ }
+ if (ecmd.advertising & ADVERTISED_10baseT_Full) {
+ *advertised |= OFPPF_10MB_FD;
+ }
+ if (ecmd.advertising & ADVERTISED_100baseT_Half) {
+ *advertised |= OFPPF_100MB_HD;
+ }
+ if (ecmd.advertising & ADVERTISED_100baseT_Full) {
+ *advertised |= OFPPF_100MB_FD;
}
+ if (ecmd.advertising & ADVERTISED_1000baseT_Half) {
+ *advertised |= OFPPF_1GB_HD;
+ }
+ if (ecmd.advertising & ADVERTISED_1000baseT_Full) {
+ *advertised |= OFPPF_1GB_FD;
+ }
+ if (ecmd.advertising & ADVERTISED_10000baseT_Full) {
+ *advertised |= OFPPF_10GB_FD;
+ }
+ if (ecmd.advertising & ADVERTISED_TP) {
+ *advertised |= OFPPF_COPPER;
+ }
+ if (ecmd.advertising & ADVERTISED_FIBRE) {
+ *advertised |= OFPPF_FIBER;
+ }
+ if (ecmd.advertising & ADVERTISED_Autoneg) {
+ *advertised |= OFPPF_AUTONEG;
+ }
+ if (ecmd.advertising & ADVERTISED_Pause) {
+ *advertised |= OFPPF_PAUSE;
+ }
+ if (ecmd.advertising & ADVERTISED_Asym_Pause) {
+ *advertised |= OFPPF_PAUSE_ASYM;
+ }
+
+ /* Set the current features */
+ if (ecmd.speed == SPEED_10) {
+ *current = (ecmd.duplex) ? OFPPF_10MB_FD : OFPPF_10MB_HD;
+ }
+ else if (ecmd.speed == SPEED_100) {
+ *current = (ecmd.duplex) ? OFPPF_100MB_FD : OFPPF_100MB_HD;
+ }
+ else if (ecmd.speed == SPEED_1000) {
+ *current = (ecmd.duplex) ? OFPPF_1GB_FD : OFPPF_1GB_HD;
+ }
+ else if (ecmd.speed == SPEED_10000) {
+ *current = OFPPF_10GB_FD;
+ }
+
+ if (ecmd.port == PORT_TP) {
+ *current |= OFPPF_COPPER;
+ }
+ else if (ecmd.port == PORT_FIBRE) {
+ *current |= OFPPF_FIBER;
+ }
+
+ if (ecmd.autoneg) {
+ *current |= OFPPF_AUTONEG;
+ }
+ return 0;
}
/* Opens the network device named 'name' (e.g. "eth0") and returns zero if
netdev->mtu = mtu;
netdev->in6 = in6;
- /* Get speed, features. */
- do_ethtool(netdev);
-
/* Save flags to restore at close or exit. */
error = get_flags(netdev->name, &netdev->save_flags);
if (error) {
return netdev->mtu;
}
-/* Returns the features supported by 'netdev' of type 'type', as a bitmap
- * of bits from enum ofp_phy_features, in host byte order. */
-uint32_t
-netdev_get_features(struct netdev *netdev, int type)
+/* Stores the features supported by 'netdev' into each of '*current',
+ * '*advertised', '*supported', and '*peer' that are non-null. Each value is a
+ * bitmap of "enum ofp_port_features" bits, in host byte order. Returns 0 if
+ * successful, otherwise a positive errno value. On failure, all of the
+ * passed-in values are set to 0. */
+int
+netdev_get_features(struct netdev *netdev,
+ uint32_t *current, uint32_t *advertised,
+ uint32_t *supported, uint32_t *peer)
{
- do_ethtool(netdev);
- switch (type) {
- case NETDEV_FEAT_CURRENT:
- return netdev->curr;
- case NETDEV_FEAT_ADVERTISED:
- return netdev->advertised;
- case NETDEV_FEAT_SUPPORTED:
- return netdev->supported;
- case NETDEV_FEAT_PEER:
- return netdev->peer;
- default:
- VLOG_WARN("Unknown feature type: %d\n", type);
- return 0;
- }
+ uint32_t dummy[4];
+ return do_get_features(netdev,
+ current ? current : &dummy[0],
+ advertised ? advertised : &dummy[1],
+ supported ? supported : &dummy[2],
+ peer ? peer : &dummy[3]);
}
/* If 'netdev' has an assigned IPv4 address, sets '*in4' to that address (if