X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fnetdev-linux.c;h=75a230c706b7bed9300f761eb3f39851f712aa58;hb=01227b953da9bb735f085c674f5bdac5954295f3;hp=27a123cd4ea9c87642d04f7d7dbb3110898e5205;hpb=02e83e83b4c8d88f50317ba56fec53cd6d3fb909;p=sliver-openvswitch.git diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 27a123cd4..75a230c70 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, 2011 Nicira Networks. + * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -70,6 +69,7 @@ #include "sset.h" #include "timer.h" #include "vlog.h" +#include "tunalloc.h" VLOG_DEFINE_THIS_MODULE(netdev_linux); @@ -79,6 +79,7 @@ COVERAGE_DEFINE(netdev_get_ifindex); COVERAGE_DEFINE(netdev_get_hwaddr); COVERAGE_DEFINE(netdev_set_hwaddr); COVERAGE_DEFINE(netdev_ethtool); + /* These were introduced in Linux 2.6.14, so they might be missing if we have * old headers. */ @@ -114,7 +115,9 @@ enum { VALID_IN6 = 1 << 3, VALID_MTU = 1 << 4, VALID_POLICING = 1 << 5, - VALID_HAVE_VPORT_STATS = 1 << 6 + VALID_VPORT_STAT_ERROR = 1 << 6, + VALID_DRVINFO = 1 << 7, + VALID_FEATURES = 1 << 8, }; struct tap_state { @@ -368,11 +371,24 @@ struct netdev_dev_linux { struct in_addr address, netmask; struct in6_addr in6; int mtu; - bool carrier; + unsigned int ifi_flags; long long int carrier_resets; uint32_t kbits_rate; /* Policing data. */ uint32_t kbits_burst; - bool have_vport_stats; + int vport_stats_error; /* Cached error code from vport_get_stats(). + 0 or an errno value. */ + int netdev_mtu_error; /* Cached error code from SIOCGIFMTU or SIOCSIFMTU. */ + int ether_addr_error; /* Cached error code from set/get etheraddr. */ + int netdev_policing_error; /* Cached error code from set policing. */ + int get_features_error; /* Cached error code from ETHTOOL_GSET. */ + int get_ifindex_error; /* Cached error code from SIOCGIFINDEX. */ + + enum netdev_features current; /* Cached from ETHTOOL_GSET. */ + enum netdev_features advertised; /* Cached from ETHTOOL_GSET. */ + enum netdev_features supported; /* Cached from ETHTOOL_GSET. */ + enum netdev_features peer; /* Cached from ETHTOOL_GSET. */ + + struct ethtool_drvinfo drvinfo; /* Cached from ETHTOOL_GDRVINFO. */ struct tc *tc; union { @@ -403,19 +419,17 @@ static int netdev_linux_do_ioctl(const char *name, struct ifreq *, int cmd, const char *cmd_name); static int netdev_linux_get_ipv4(const struct netdev *, struct in_addr *, int cmd, const char *cmd_name); -static int get_flags(const struct netdev *, int *flagsp); -static int set_flags(struct netdev *, int flags); +static int get_flags(const struct netdev_dev *, unsigned int *flags); +static int set_flags(struct netdev *, unsigned int flags); static int do_get_ifindex(const char *netdev_name); static int get_ifindex(const struct netdev *, int *ifindexp); static int do_set_addr(struct netdev *netdev, int ioctl_nr, const char *ioctl_name, struct in_addr addr); static int get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN]); -static int set_etheraddr(const char *netdev_name, int hwaddr_family, - const uint8_t[ETH_ADDR_LEN]); +static int set_etheraddr(const char *netdev_name, const uint8_t[ETH_ADDR_LEN]); static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats); static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats); -static int get_carrier_via_sysfs(const char *name, bool *carrier); static int af_packet_sock(void); static void netdev_linux_miimon_run(void); static void netdev_linux_miimon_wait(void); @@ -483,14 +497,73 @@ netdev_linux_wait(void) netdev_linux_miimon_wait(); } +static int +netdev_linux_get_drvinfo(struct netdev_dev_linux *netdev_dev) +{ + + int error; + + if (netdev_dev->cache_valid & VALID_DRVINFO) { + return 0; + } + + memset(&netdev_dev->drvinfo, 0, sizeof netdev_dev->drvinfo); + error = netdev_linux_do_ethtool(netdev_dev->netdev_dev.name, + (struct ethtool_cmd *)&netdev_dev->drvinfo, + ETHTOOL_GDRVINFO, + "ETHTOOL_GDRVINFO"); + if (!error) { + netdev_dev->cache_valid |= VALID_DRVINFO; + } + return error; +} + static void -netdev_dev_linux_changed(struct netdev_dev_linux *dev) +netdev_dev_linux_changed(struct netdev_dev_linux *dev, + unsigned int ifi_flags, + unsigned int mask) { dev->change_seq++; if (!dev->change_seq) { dev->change_seq++; } - dev->cache_valid = 0; + + if ((dev->ifi_flags ^ ifi_flags) & IFF_RUNNING) { + dev->carrier_resets++; + } + dev->ifi_flags = ifi_flags; + + dev->cache_valid &= mask; +} + +static void +netdev_dev_linux_update(struct netdev_dev_linux *dev, + const struct rtnetlink_link_change *change) +{ + if (change->nlmsg_type == RTM_NEWLINK) { + /* Keep drv-info */ + netdev_dev_linux_changed(dev, change->ifi_flags, VALID_DRVINFO); + + /* Update netdev from rtnl-change msg. */ + if (change->mtu) { + dev->mtu = change->mtu; + dev->cache_valid |= VALID_MTU; + dev->netdev_mtu_error = 0; + } + + if (!eth_addr_is_zero(change->addr)) { + memcpy(dev->etheraddr, change->addr, ETH_ADDR_LEN); + dev->cache_valid |= VALID_ETHERADDR; + dev->ether_addr_error = 0; + } + + dev->ifindex = change->ifi_index; + dev->cache_valid |= VALID_IFINDEX; + dev->get_ifindex_error = 0; + + } else { + netdev_dev_linux_changed(dev, change->ifi_flags, 0); + } } static void @@ -506,13 +579,7 @@ netdev_linux_cache_cb(const struct rtnetlink_link_change *change, if (is_netdev_linux_class(netdev_class)) { dev = netdev_dev_linux_cast(base_dev); - - if (dev->carrier != change->running) { - dev->carrier = change->running; - dev->carrier_resets++; - } - - netdev_dev_linux_changed(dev); + netdev_dev_linux_update(dev, change); } } } else { @@ -522,17 +589,12 @@ netdev_linux_cache_cb(const struct rtnetlink_link_change *change, shash_init(&device_shash); netdev_dev_get_devices(&netdev_linux_class, &device_shash); SHASH_FOR_EACH (node, &device_shash) { - bool carrier; + unsigned int flags; dev = node->data; - get_carrier_via_sysfs(node->name, &carrier); - if (dev->carrier != carrier) { - dev->carrier = carrier; - dev->carrier_resets++; - } - - netdev_dev_linux_changed(dev); + get_flags(&dev->netdev_dev, &flags); + netdev_dev_linux_changed(dev, flags, 0); } shash_destroy(&device_shash); } @@ -583,7 +645,7 @@ netdev_linux_create(const struct netdev_class *class, const char *name, netdev_dev = xzalloc(sizeof *netdev_dev); netdev_dev->change_seq = 1; netdev_dev_init(&netdev_dev->netdev_dev, name, class); - get_carrier_via_sysfs(name, &netdev_dev->carrier); + get_flags(&netdev_dev->netdev_dev, &netdev_dev->ifi_flags); *netdev_devp = &netdev_dev->netdev_dev; return 0; @@ -705,7 +767,7 @@ netdev_linux_open(struct netdev_dev *netdev_dev_, struct netdev **netdevp) } } - if (!strcmp(netdev_dev_get_type(netdev_dev_), "tap") && + if (!strncmp(netdev_dev_get_type(netdev_dev_), "tap", 3) && !netdev_dev->state.tap.opened) { /* We assume that the first user of the tap device is the primary user @@ -730,7 +792,7 @@ netdev_linux_close(struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (netdev->fd > 0 && strcmp(netdev_get_type(netdev_), "tap")) { + if (netdev->fd > 0 && strncmp(netdev_get_type(netdev_), "tap", 3)) { close(netdev->fd); } free(netdev); @@ -802,12 +864,13 @@ netdev_linux_recv(struct netdev *netdev_, void *data, size_t size) } for (;;) { - ssize_t retval = recv(netdev->fd, data, size, MSG_TRUNC); - if (retval > size) { - /* Received packet was longer than supplied buffer. */ - return -EMSGSIZE; - } else if (retval >= 0) { - return retval; + ssize_t retval; + + retval = (netdev_->netdev_dev->netdev_class == &netdev_tap_class + ? read(netdev->fd, data, size) + : recv(netdev->fd, data, size, MSG_TRUNC)); + if (retval >= 0) { + return retval <= size ? retval : -EMSGSIZE; } else if (errno != EINTR) { if (errno != EAGAIN) { VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s", @@ -836,7 +899,7 @@ netdev_linux_drain(struct netdev *netdev_) struct netdev_linux *netdev = netdev_linux_cast(netdev_); if (netdev->fd < 0) { return 0; - } else if (!strcmp(netdev_get_type(netdev_), "tap")) { + } else if (!strncmp(netdev_get_type(netdev_), "tap", 3)) { struct ifreq ifr; int error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr, SIOCGIFTXQLEN, "SIOCGIFTXQLEN"); @@ -947,7 +1010,7 @@ netdev_linux_send_wait(struct netdev *netdev_) struct netdev_linux *netdev = netdev_linux_cast(netdev_); if (netdev->fd < 0) { /* Nothing to do. */ - } else if (strcmp(netdev_get_type(netdev_), "tap")) { + } else if (strncmp(netdev_get_type(netdev_), "tap", 3)) { poll_fd_wait(netdev->fd, POLLOUT); } else { /* TAP device always accepts packets.*/ @@ -965,37 +1028,49 @@ netdev_linux_set_etheraddr(struct netdev *netdev_, netdev_dev_linux_cast(netdev_get_dev(netdev_)); int error; - if (!(netdev_dev->cache_valid & VALID_ETHERADDR) - || !eth_addr_equals(netdev_dev->etheraddr, mac)) { - error = set_etheraddr(netdev_get_name(netdev_), ARPHRD_ETHER, mac); + if (netdev_dev->cache_valid & VALID_ETHERADDR) { + if (netdev_dev->ether_addr_error) { + return netdev_dev->ether_addr_error; + } + if (eth_addr_equals(netdev_dev->etheraddr, mac)) { + return 0; + } + netdev_dev->cache_valid &= ~VALID_ETHERADDR; + } + + error = set_etheraddr(netdev_get_name(netdev_), mac); + if (!error || error == ENODEV) { + netdev_dev->ether_addr_error = error; + netdev_dev->cache_valid |= VALID_ETHERADDR; if (!error) { - netdev_dev->cache_valid |= VALID_ETHERADDR; memcpy(netdev_dev->etheraddr, mac, ETH_ADDR_LEN); } - } else { - error = 0; } + return error; } -/* Returns a pointer to 'netdev''s MAC address. The caller must not modify or - * free the returned buffer. */ +/* Copies 'netdev''s MAC address to 'mac' which is passed as param. */ static int netdev_linux_get_etheraddr(const struct netdev *netdev_, uint8_t mac[ETH_ADDR_LEN]) { struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev_)); + if (!(netdev_dev->cache_valid & VALID_ETHERADDR)) { int error = get_etheraddr(netdev_get_name(netdev_), netdev_dev->etheraddr); - if (error) { - return error; - } + + netdev_dev->ether_addr_error = error; netdev_dev->cache_valid |= VALID_ETHERADDR; } - memcpy(mac, netdev_dev->etheraddr, ETH_ADDR_LEN); - return 0; + + if (!netdev_dev->ether_addr_error) { + memcpy(mac, netdev_dev->etheraddr, ETH_ADDR_LEN); + } + + return netdev_dev->ether_addr_error; } /* Returns the maximum size of transmitted (and received) packets on 'netdev', @@ -1012,14 +1087,16 @@ netdev_linux_get_mtu(const struct netdev *netdev_, int *mtup) error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr, SIOCGIFMTU, "SIOCGIFMTU"); - if (error) { - return error; - } + + netdev_dev->netdev_mtu_error = error; netdev_dev->mtu = ifr.ifr_mtu; netdev_dev->cache_valid |= VALID_MTU; } - *mtup = netdev_dev->mtu; - return 0; + + if (!netdev_dev->netdev_mtu_error) { + *mtup = netdev_dev->mtu; + } + return netdev_dev->netdev_mtu_error; } /* Sets the maximum size of transmitted (MTU) for given device using linux @@ -1033,16 +1110,24 @@ netdev_linux_set_mtu(const struct netdev *netdev_, int mtu) struct ifreq ifr; int error; + if (netdev_dev->cache_valid & VALID_MTU) { + if (netdev_dev->netdev_mtu_error) { + return netdev_dev->netdev_mtu_error; + } + if (netdev_dev->mtu == mtu) { + return 0; + } + netdev_dev->cache_valid &= ~VALID_MTU; + } ifr.ifr_mtu = mtu; error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr, SIOCSIFMTU, "SIOCSIFMTU"); - if (error) { - return error; + if (!error || error == ENODEV) { + netdev_dev->netdev_mtu_error = error; + netdev_dev->mtu = ifr.ifr_mtu; + netdev_dev->cache_valid |= VALID_MTU; } - - netdev_dev->mtu = ifr.ifr_mtu; - netdev_dev->cache_valid |= VALID_MTU; - return 0; + return error; } /* Returns the ifindex of 'netdev', if successful, as a positive number. @@ -1065,7 +1150,7 @@ netdev_linux_get_carrier(const struct netdev *netdev_, bool *carrier) if (netdev_dev->miimon_interval > 0) { *carrier = netdev_dev->miimon; } else { - *carrier = netdev_dev->carrier; + *carrier = (netdev_dev->ifi_flags & IFF_RUNNING) != 0; } return 0; @@ -1171,7 +1256,7 @@ netdev_linux_miimon_run(void) netdev_linux_get_miimon(dev->netdev_dev.name, &miimon); if (miimon != dev->miimon) { dev->miimon = miimon; - netdev_dev_linux_changed(dev); + netdev_dev_linux_changed(dev, dev->ifi_flags, 0); } timer_set_duration(&dev->miimon_timer, dev->miimon_interval); @@ -1241,8 +1326,8 @@ get_stats_via_vport(const struct netdev *netdev_, struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev_)); - if (netdev_dev->have_vport_stats || - !(netdev_dev->cache_valid & VALID_HAVE_VPORT_STATS)) { + if (!netdev_dev->vport_stats_error || + !(netdev_dev->cache_valid & VALID_VPORT_STAT_ERROR)) { int error; error = netdev_vport_get_stats(netdev_, stats); @@ -1250,8 +1335,8 @@ get_stats_via_vport(const struct netdev *netdev_, VLOG_WARN_RL(&rl, "%s: obtaining netdev stats via vport failed " "(%s)", netdev_get_name(netdev_), strerror(error)); } - netdev_dev->have_vport_stats = !error; - netdev_dev->cache_valid |= VALID_HAVE_VPORT_STATS; + netdev_dev->vport_stats_error = error; + netdev_dev->cache_valid |= VALID_VPORT_STAT_ERROR; } } @@ -1300,14 +1385,14 @@ netdev_linux_get_stats(const struct netdev *netdev_, error = netdev_linux_sys_get_stats(netdev_, &dev_stats); if (error) { - if (!netdev_dev->have_vport_stats) { + if (netdev_dev->vport_stats_error) { return error; } else { return 0; } } - if (!netdev_dev->have_vport_stats) { + if (netdev_dev->vport_stats_error) { /* stats not available from OVS then use ioctl stats. */ *stats = dev_stats; } else { @@ -1335,7 +1420,7 @@ netdev_linux_get_stats(const struct netdev *netdev_, /* Retrieves current device stats for 'netdev-tap' netdev or * netdev-internal. */ static int -netdev_pseudo_get_stats(const struct netdev *netdev_, +netdev_tap_get_stats(const struct netdev *netdev_, struct netdev_stats *stats) { struct netdev_dev_linux *netdev_dev = @@ -1347,7 +1432,7 @@ netdev_pseudo_get_stats(const struct netdev *netdev_, error = netdev_linux_sys_get_stats(netdev_, &dev_stats); if (error) { - if (!netdev_dev->have_vport_stats) { + if (netdev_dev->vport_stats_error) { return error; } else { return 0; @@ -1360,7 +1445,7 @@ netdev_pseudo_get_stats(const struct netdev *netdev_, * them back here. This does not apply if we are getting stats from the * vport layer because it always tracks stats from the perspective of the * switch. */ - if (!netdev_dev->have_vport_stats) { + if (netdev_dev->vport_stats_error) { *stats = dev_stats; swap_uint64(&stats->rx_packets, &stats->tx_packets); swap_uint64(&stats->rx_bytes, &stats->tx_bytes); @@ -1390,135 +1475,180 @@ netdev_pseudo_get_stats(const struct netdev *netdev_, return 0; } -/* 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. */ static int -netdev_linux_get_features(const struct netdev *netdev, - uint32_t *current, uint32_t *advertised, - uint32_t *supported, uint32_t *peer) +netdev_internal_get_stats(const struct netdev *netdev_, + struct netdev_stats *stats) +{ + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); + + get_stats_via_vport(netdev_, stats); + return netdev_dev->vport_stats_error; +} + +static void +netdev_linux_read_features(struct netdev_dev_linux *netdev_dev) { struct ethtool_cmd ecmd; + uint32_t speed; int error; + if (netdev_dev->cache_valid & VALID_FEATURES) { + return; + } + memset(&ecmd, 0, sizeof ecmd); - error = netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd, + error = netdev_linux_do_ethtool(netdev_dev->netdev_dev.name, &ecmd, ETHTOOL_GSET, "ETHTOOL_GSET"); if (error) { - return error; + goto out; } /* Supported features. */ - *supported = 0; + netdev_dev->supported = 0; if (ecmd.supported & SUPPORTED_10baseT_Half) { - *supported |= OFPPF_10MB_HD; + netdev_dev->supported |= NETDEV_F_10MB_HD; } if (ecmd.supported & SUPPORTED_10baseT_Full) { - *supported |= OFPPF_10MB_FD; + netdev_dev->supported |= NETDEV_F_10MB_FD; } if (ecmd.supported & SUPPORTED_100baseT_Half) { - *supported |= OFPPF_100MB_HD; + netdev_dev->supported |= NETDEV_F_100MB_HD; } if (ecmd.supported & SUPPORTED_100baseT_Full) { - *supported |= OFPPF_100MB_FD; + netdev_dev->supported |= NETDEV_F_100MB_FD; } if (ecmd.supported & SUPPORTED_1000baseT_Half) { - *supported |= OFPPF_1GB_HD; + netdev_dev->supported |= NETDEV_F_1GB_HD; } if (ecmd.supported & SUPPORTED_1000baseT_Full) { - *supported |= OFPPF_1GB_FD; + netdev_dev->supported |= NETDEV_F_1GB_FD; } if (ecmd.supported & SUPPORTED_10000baseT_Full) { - *supported |= OFPPF_10GB_FD; + netdev_dev->supported |= NETDEV_F_10GB_FD; } if (ecmd.supported & SUPPORTED_TP) { - *supported |= OFPPF_COPPER; + netdev_dev->supported |= NETDEV_F_COPPER; } if (ecmd.supported & SUPPORTED_FIBRE) { - *supported |= OFPPF_FIBER; + netdev_dev->supported |= NETDEV_F_FIBER; } if (ecmd.supported & SUPPORTED_Autoneg) { - *supported |= OFPPF_AUTONEG; + netdev_dev->supported |= NETDEV_F_AUTONEG; } if (ecmd.supported & SUPPORTED_Pause) { - *supported |= OFPPF_PAUSE; + netdev_dev->supported |= NETDEV_F_PAUSE; } if (ecmd.supported & SUPPORTED_Asym_Pause) { - *supported |= OFPPF_PAUSE_ASYM; + netdev_dev->supported |= NETDEV_F_PAUSE_ASYM; } /* Advertised features. */ - *advertised = 0; + netdev_dev->advertised = 0; if (ecmd.advertising & ADVERTISED_10baseT_Half) { - *advertised |= OFPPF_10MB_HD; + netdev_dev->advertised |= NETDEV_F_10MB_HD; } if (ecmd.advertising & ADVERTISED_10baseT_Full) { - *advertised |= OFPPF_10MB_FD; + netdev_dev->advertised |= NETDEV_F_10MB_FD; } if (ecmd.advertising & ADVERTISED_100baseT_Half) { - *advertised |= OFPPF_100MB_HD; + netdev_dev->advertised |= NETDEV_F_100MB_HD; } if (ecmd.advertising & ADVERTISED_100baseT_Full) { - *advertised |= OFPPF_100MB_FD; + netdev_dev->advertised |= NETDEV_F_100MB_FD; } if (ecmd.advertising & ADVERTISED_1000baseT_Half) { - *advertised |= OFPPF_1GB_HD; + netdev_dev->advertised |= NETDEV_F_1GB_HD; } if (ecmd.advertising & ADVERTISED_1000baseT_Full) { - *advertised |= OFPPF_1GB_FD; + netdev_dev->advertised |= NETDEV_F_1GB_FD; } if (ecmd.advertising & ADVERTISED_10000baseT_Full) { - *advertised |= OFPPF_10GB_FD; + netdev_dev->advertised |= NETDEV_F_10GB_FD; } if (ecmd.advertising & ADVERTISED_TP) { - *advertised |= OFPPF_COPPER; + netdev_dev->advertised |= NETDEV_F_COPPER; } if (ecmd.advertising & ADVERTISED_FIBRE) { - *advertised |= OFPPF_FIBER; + netdev_dev->advertised |= NETDEV_F_FIBER; } if (ecmd.advertising & ADVERTISED_Autoneg) { - *advertised |= OFPPF_AUTONEG; + netdev_dev->advertised |= NETDEV_F_AUTONEG; } if (ecmd.advertising & ADVERTISED_Pause) { - *advertised |= OFPPF_PAUSE; + netdev_dev->advertised |= NETDEV_F_PAUSE; } if (ecmd.advertising & ADVERTISED_Asym_Pause) { - *advertised |= OFPPF_PAUSE_ASYM; + netdev_dev->advertised |= NETDEV_F_PAUSE_ASYM; } /* Current settings. */ - 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; + speed = ecmd.speed; + if (speed == SPEED_10) { + netdev_dev->current = ecmd.duplex ? NETDEV_F_10MB_FD : NETDEV_F_10MB_HD; + } else if (speed == SPEED_100) { + netdev_dev->current = ecmd.duplex ? NETDEV_F_100MB_FD : NETDEV_F_100MB_HD; + } else if (speed == SPEED_1000) { + netdev_dev->current = ecmd.duplex ? NETDEV_F_1GB_FD : NETDEV_F_1GB_HD; + } else if (speed == SPEED_10000) { + netdev_dev->current = NETDEV_F_10GB_FD; + } else if (speed == 40000) { + netdev_dev->current = NETDEV_F_40GB_FD; + } else if (speed == 100000) { + netdev_dev->current = NETDEV_F_100GB_FD; + } else if (speed == 1000000) { + netdev_dev->current = NETDEV_F_1TB_FD; } else { - *current = 0; + netdev_dev->current = 0; } if (ecmd.port == PORT_TP) { - *current |= OFPPF_COPPER; + netdev_dev->current |= NETDEV_F_COPPER; } else if (ecmd.port == PORT_FIBRE) { - *current |= OFPPF_FIBER; + netdev_dev->current |= NETDEV_F_FIBER; } if (ecmd.autoneg) { - *current |= OFPPF_AUTONEG; + netdev_dev->current |= NETDEV_F_AUTONEG; } /* Peer advertisements. */ - *peer = 0; /* XXX */ + netdev_dev->peer = 0; /* XXX */ - return 0; +out: + netdev_dev->cache_valid |= VALID_FEATURES; + netdev_dev->get_features_error = error; +} + +/* Stores the features supported by 'netdev' into each of '*current', + * '*advertised', '*supported', and '*peer' that are non-null. Each value is a + * bitmap of NETDEV_* bits. Returns 0 if successful, otherwise a positive + * errno value. */ +static int +netdev_linux_get_features(const struct netdev *netdev_, + enum netdev_features *current, + enum netdev_features *advertised, + enum netdev_features *supported, + enum netdev_features *peer) +{ + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); + + netdev_linux_read_features(netdev_dev); + + if (!netdev_dev->get_features_error) { + *current = netdev_dev->current; + *advertised = netdev_dev->advertised; + *supported = netdev_dev->supported; + *peer = netdev_dev->peer; + } + return netdev_dev->get_features_error; } /* Set the features advertised by 'netdev' to 'advertise'. */ static int -netdev_linux_set_advertisements(struct netdev *netdev, uint32_t advertise) +netdev_linux_set_advertisements(struct netdev *netdev, + enum netdev_features advertise) { struct ethtool_cmd ecmd; int error; @@ -1531,40 +1661,40 @@ netdev_linux_set_advertisements(struct netdev *netdev, uint32_t advertise) } ecmd.advertising = 0; - if (advertise & OFPPF_10MB_HD) { + if (advertise & NETDEV_F_10MB_HD) { ecmd.advertising |= ADVERTISED_10baseT_Half; } - if (advertise & OFPPF_10MB_FD) { + if (advertise & NETDEV_F_10MB_FD) { ecmd.advertising |= ADVERTISED_10baseT_Full; } - if (advertise & OFPPF_100MB_HD) { + if (advertise & NETDEV_F_100MB_HD) { ecmd.advertising |= ADVERTISED_100baseT_Half; } - if (advertise & OFPPF_100MB_FD) { + if (advertise & NETDEV_F_100MB_FD) { ecmd.advertising |= ADVERTISED_100baseT_Full; } - if (advertise & OFPPF_1GB_HD) { + if (advertise & NETDEV_F_1GB_HD) { ecmd.advertising |= ADVERTISED_1000baseT_Half; } - if (advertise & OFPPF_1GB_FD) { + if (advertise & NETDEV_F_1GB_FD) { ecmd.advertising |= ADVERTISED_1000baseT_Full; } - if (advertise & OFPPF_10GB_FD) { + if (advertise & NETDEV_F_10GB_FD) { ecmd.advertising |= ADVERTISED_10000baseT_Full; } - if (advertise & OFPPF_COPPER) { + if (advertise & NETDEV_F_COPPER) { ecmd.advertising |= ADVERTISED_TP; } - if (advertise & OFPPF_FIBER) { + if (advertise & NETDEV_F_FIBER) { ecmd.advertising |= ADVERTISED_FIBRE; } - if (advertise & OFPPF_AUTONEG) { + if (advertise & NETDEV_F_AUTONEG) { ecmd.advertising |= ADVERTISED_Autoneg; } - if (advertise & OFPPF_PAUSE) { + if (advertise & NETDEV_F_PAUSE) { ecmd.advertising |= ADVERTISED_Pause; } - if (advertise & OFPPF_PAUSE_ASYM) { + if (advertise & NETDEV_F_PAUSE_ASYM) { ecmd.advertising |= ADVERTISED_Asym_Pause; } return netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd, @@ -1582,25 +1712,31 @@ netdev_linux_set_policing(struct netdev *netdev, const char *netdev_name = netdev_get_name(netdev); int error; - COVERAGE_INC(netdev_set_policing); kbits_burst = (!kbits_rate ? 0 /* Force to 0 if no rate specified. */ : !kbits_burst ? 1000 /* Default to 1000 kbits if 0. */ : kbits_burst); /* Stick with user-specified value. */ - if (netdev_dev->cache_valid & VALID_POLICING - && netdev_dev->kbits_rate == kbits_rate - && netdev_dev->kbits_burst == kbits_burst) { - /* Assume that settings haven't changed since we last set them. */ - return 0; + if (netdev_dev->cache_valid & VALID_POLICING) { + if (netdev_dev->netdev_policing_error) { + return netdev_dev->netdev_policing_error; + } + + if (netdev_dev->kbits_rate == kbits_rate && + netdev_dev->kbits_burst == kbits_burst) { + /* Assume that settings haven't changed since we last set them. */ + return 0; + } + netdev_dev->cache_valid &= ~VALID_POLICING; } + COVERAGE_INC(netdev_set_policing); /* Remove any existing ingress qdisc. */ error = tc_add_del_ingress_qdisc(netdev, false); if (error) { VLOG_WARN_RL(&rl, "%s: removing policing failed: %s", netdev_name, strerror(error)); - return error; + goto out; } if (kbits_rate) { @@ -1608,22 +1744,26 @@ netdev_linux_set_policing(struct netdev *netdev, if (error) { VLOG_WARN_RL(&rl, "%s: adding policing qdisc failed: %s", netdev_name, strerror(error)); - return error; + goto out; } error = tc_add_policer(netdev, kbits_rate, kbits_burst); if (error){ VLOG_WARN_RL(&rl, "%s: adding policing action failed: %s", netdev_name, strerror(error)); - return error; + goto out; } } netdev_dev->kbits_rate = kbits_rate; netdev_dev->kbits_burst = kbits_burst; - netdev_dev->cache_valid |= VALID_POLICING; - return 0; +out: + if (!error || error == ENODEV) { + netdev_dev->netdev_policing_error = error; + netdev_dev->cache_valid |= VALID_POLICING; + } + return error; } static int @@ -1641,6 +1781,51 @@ netdev_linux_get_qos_types(const struct netdev *netdev OVS_UNUSED, return 0; } +static int +netdev_linux_create_tap_pl(const struct netdev_class *class OVS_UNUSED, + const char *name, struct netdev_dev **netdev_devp) +{ + struct netdev_dev_linux *netdev_dev; + struct tap_state *state; + char real_name[IFNAMSIZ]; + int error; + + netdev_dev = xzalloc(sizeof *netdev_dev); + state = &netdev_dev->state.tap; + + error = cache_notifier_ref(); + if (error) { + goto error; + } + + /* Open tap device. */ + state->fd = tun_alloc(IFF_TAP, real_name); + if (state->fd < 0) { + error = errno; + VLOG_WARN("tun_alloc(IFF_TAP, %s) failed: %s", name, strerror(error)); + goto error_unref_notifier; + } + if (strcmp(name, real_name)) { + VLOG_WARN("tap_pl: requested %s, created %s", name, real_name); + } + + /* Make non-blocking. */ + error = set_nonblocking(state->fd); + if (error) { + goto error_unref_notifier; + } + + netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_tap_pl_class); + *netdev_devp = &netdev_dev->netdev_dev; + return 0; + +error_unref_notifier: + cache_notifier_unref(); +error: + free(netdev_dev); + return error; +} + static const struct tc_ops * tc_lookup_ovs_name(const char *name) { @@ -1862,7 +2047,7 @@ netdev_linux_dump_queues(const struct netdev *netdev, { struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev)); - struct tc_queue *queue; + struct tc_queue *queue, *next_queue; struct shash details; int last_error; int error; @@ -1876,7 +2061,8 @@ netdev_linux_dump_queues(const struct netdev *netdev, last_error = 0; shash_init(&details); - HMAP_FOR_EACH (queue, hmap_node, &netdev_dev->tc->queues) { + HMAP_FOR_EACH_SAFE (queue, next_queue, hmap_node, + &netdev_dev->tc->queues) { shash_clear(&details); error = netdev_dev->tc->ops->class_get(netdev, queue, &details); @@ -2131,25 +2317,28 @@ netdev_linux_get_next_hop(const struct in_addr *host, struct in_addr *next_hop, } static int -netdev_linux_get_status(const struct netdev *netdev, struct shash *sh) +netdev_linux_get_drv_info(const struct netdev *netdev, struct shash *sh) { - struct ethtool_drvinfo drvinfo; int error; + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev)); - memset(&drvinfo, 0, sizeof drvinfo); - error = netdev_linux_do_ethtool(netdev_get_name(netdev), - (struct ethtool_cmd *)&drvinfo, - ETHTOOL_GDRVINFO, - "ETHTOOL_GDRVINFO"); + error = netdev_linux_get_drvinfo(netdev_dev); if (!error) { - shash_add(sh, "driver_name", xstrdup(drvinfo.driver)); - shash_add(sh, "driver_version", xstrdup(drvinfo.version)); - shash_add(sh, "firmware_version", xstrdup(drvinfo.fw_version)); + shash_add(sh, "driver_name", xstrdup(netdev_dev->drvinfo.driver)); + shash_add(sh, "driver_version", xstrdup(netdev_dev->drvinfo.version)); + shash_add(sh, "firmware_version", xstrdup(netdev_dev->drvinfo.fw_version)); } - return error; } +static int +netdev_internal_get_drv_info(const struct netdev *netdev OVS_UNUSED, struct shash *sh) +{ + shash_add(sh, "driver_name", xstrdup("openvswitch")); + return 0; +} + /* Looks up the ARP table entry for 'ip' on 'netdev'. If one exists and can be * successfully retrieved, it stores the corresponding MAC address in 'mac' and * returns 0. Otherwise, it returns a positive errno value; in particular, @@ -2212,16 +2401,17 @@ static int netdev_linux_update_flags(struct netdev *netdev, enum netdev_flags off, enum netdev_flags on, enum netdev_flags *old_flagsp) { + struct netdev_dev_linux *netdev_dev; int old_flags, new_flags; - int error; + int error = 0; - error = get_flags(netdev, &old_flags); - if (!error) { - *old_flagsp = iff_to_nd_flags(old_flags); - new_flags = (old_flags & ~nd_to_iff_flags(off)) | nd_to_iff_flags(on); - if (new_flags != old_flags) { - error = set_flags(netdev, new_flags); - } + netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev)); + old_flags = netdev_dev->ifi_flags; + *old_flagsp = iff_to_nd_flags(old_flags); + new_flags = (old_flags & ~nd_to_iff_flags(off)) | nd_to_iff_flags(on); + if (new_flags != old_flags) { + error = set_flags(netdev, new_flags); + get_flags(&netdev_dev->netdev_dev, &netdev_dev->ifi_flags); } return error; } @@ -2232,7 +2422,8 @@ netdev_linux_change_seq(const struct netdev *netdev) return netdev_dev_linux_cast(netdev_get_dev(netdev))->change_seq; } -#define NETDEV_LINUX_CLASS(NAME, CREATE, GET_STATS, SET_STATS) \ +#define NETDEV_LINUX_CLASS(NAME, CREATE, GET_STATS, SET_STATS, \ + GET_FEATURES, GET_STATUS) \ { \ NAME, \ \ @@ -2267,7 +2458,7 @@ netdev_linux_change_seq(const struct netdev *netdev) GET_STATS, \ SET_STATS, \ \ - netdev_linux_get_features, \ + GET_FEATURES, \ netdev_linux_set_advertisements, \ \ netdev_linux_set_policing, \ @@ -2287,7 +2478,7 @@ netdev_linux_change_seq(const struct netdev *netdev) netdev_linux_get_in6, \ netdev_linux_add_router, \ netdev_linux_get_next_hop, \ - netdev_linux_get_status, \ + GET_STATUS, \ netdev_linux_arp_lookup, \ \ netdev_linux_update_flags, \ @@ -2300,21 +2491,36 @@ const struct netdev_class netdev_linux_class = "system", netdev_linux_create, netdev_linux_get_stats, - NULL); /* set_stats */ + NULL, /* set_stats */ + netdev_linux_get_features, + netdev_linux_get_drv_info); const struct netdev_class netdev_tap_class = NETDEV_LINUX_CLASS( "tap", netdev_linux_create_tap, - netdev_pseudo_get_stats, - NULL); /* set_stats */ + netdev_tap_get_stats, + NULL, /* set_stats */ + netdev_linux_get_features, + netdev_linux_get_drv_info); const struct netdev_class netdev_internal_class = NETDEV_LINUX_CLASS( "internal", netdev_linux_create, - netdev_pseudo_get_stats, - netdev_vport_set_stats); + netdev_internal_get_stats, + netdev_vport_set_stats, + NULL, /* get_features */ + netdev_internal_get_drv_info); + +const struct netdev_class netdev_tap_pl_class = + NETDEV_LINUX_CLASS( + "tap_pl", + netdev_linux_create_tap_pl, + netdev_tap_get_stats, + NULL, /* set_stats */ + netdev_linux_get_features, + netdev_linux_get_drv_info); /* HTB traffic control class. */ @@ -2507,7 +2713,7 @@ htb_parse_qdisc_details__(struct netdev *netdev, max_rate_s = shash_find_data(details, "max-rate"); hc->max_rate = max_rate_s ? strtoull(max_rate_s, NULL, 10) / 8 : 0; if (!hc->max_rate) { - uint32_t current; + enum netdev_features current; netdev_get_features(netdev, ¤t, NULL, NULL, NULL); hc->max_rate = netdev_features_to_bps(current) / 8; @@ -2986,7 +3192,7 @@ hfsc_parse_qdisc_details__(struct netdev *netdev, const struct shash *details, max_rate = max_rate_s ? strtoull(max_rate_s, NULL, 10) / 8 : 0; if (!max_rate) { - uint32_t current; + enum netdev_features current; netdev_get_features(netdev, ¤t, NULL, NULL, NULL); max_rate = netdev_features_to_bps(current) / 8; @@ -4217,71 +4423,22 @@ get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats) } static int -get_carrier_via_sysfs(const char *name, bool *carrier) -{ - char line[8]; - int retval; - - int error = 0; - char *fn = NULL; - int fd = -1; - - *carrier = false; - - fn = xasprintf("/sys/class/net/%s/carrier", name); - fd = open(fn, O_RDONLY); - if (fd < 0) { - error = errno; - VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, strerror(error)); - goto exit; - } - - retval = read(fd, line, sizeof line); - if (retval < 0) { - error = errno; - if (error == EINVAL) { - /* This is the normal return value when we try to check carrier if - * the network device is not up. */ - } else { - VLOG_WARN_RL(&rl, "%s: read failed: %s", fn, strerror(error)); - } - goto exit; - } else if (retval == 0) { - error = EPROTO; - VLOG_WARN_RL(&rl, "%s: unexpected end of file", fn); - goto exit; - } - - if (line[0] != '0' && line[0] != '1') { - error = EPROTO; - VLOG_WARN_RL(&rl, "%s: value is %c (expected 0 or 1)", fn, line[0]); - goto exit; - } - *carrier = line[0] != '0'; - error = 0; - -exit: - if (fd >= 0) { - close(fd); - } - free(fn); - return error; -} - -static int -get_flags(const struct netdev *netdev, int *flags) +get_flags(const struct netdev_dev *dev, unsigned int *flags) { struct ifreq ifr; int error; - error = netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, SIOCGIFFLAGS, + *flags = 0; + error = netdev_linux_do_ioctl(dev->name, &ifr, SIOCGIFFLAGS, "SIOCGIFFLAGS"); - *flags = ifr.ifr_flags; + if (!error) { + *flags = ifr.ifr_flags; + } return error; } static int -set_flags(struct netdev *netdev, int flags) +set_flags(struct netdev *netdev, unsigned int flags) { struct ifreq ifr; @@ -4310,17 +4467,22 @@ get_ifindex(const struct netdev *netdev_, int *ifindexp) { struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev_)); - *ifindexp = 0; + if (!(netdev_dev->cache_valid & VALID_IFINDEX)) { int ifindex = do_get_ifindex(netdev_get_name(netdev_)); + if (ifindex < 0) { - return -ifindex; + netdev_dev->get_ifindex_error = -ifindex; + netdev_dev->ifindex = 0; + } else { + netdev_dev->get_ifindex_error = 0; + netdev_dev->ifindex = ifindex; } netdev_dev->cache_valid |= VALID_IFINDEX; - netdev_dev->ifindex = ifindex; } + *ifindexp = netdev_dev->ifindex; - return 0; + return netdev_dev->get_ifindex_error; } static int @@ -4351,14 +4513,14 @@ get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN]) } static int -set_etheraddr(const char *netdev_name, int hwaddr_family, +set_etheraddr(const char *netdev_name, const uint8_t mac[ETH_ADDR_LEN]) { struct ifreq ifr; memset(&ifr, 0, sizeof ifr); ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name); - ifr.ifr_hwaddr.sa_family = hwaddr_family; + ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; memcpy(ifr.ifr_hwaddr.sa_data, mac, ETH_ADDR_LEN); COVERAGE_INC(netdev_set_hwaddr); if (ioctl(af_inet_sock, SIOCSIFHWADDR, &ifr) < 0) {