X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fnetdev-linux.c;h=c33405fd33cf9b9f53db8ff0f5e4188c51703fea;hb=6bf4c631ae776f4e936b5f01efe4bfbe190f5d99;hp=11d83e9716e559701de5e477a9259d1fbe9b7571;hpb=8398cf7efc3979cd6c8152915a584c13873ee322;p=openvswitch diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 11d83e97..c33405fd 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -85,7 +85,8 @@ enum { VALID_IN4 = 1 << 2, VALID_IN6 = 1 << 3, VALID_MTU = 1 << 4, - VALID_CARRIER = 1 << 5 + VALID_CARRIER = 1 << 5, + VALID_IS_INTERNAL = 1 << 6 }; /* Cached network device information. */ @@ -100,6 +101,7 @@ struct netdev_linux_cache { struct in6_addr in6; int mtu; int carrier; + bool is_internal; }; static struct shash cache_map = SHASH_INITIALIZER(&cache_map); @@ -201,7 +203,7 @@ netdev_linux_open(const char *name, char *suffix, int ethertype, int error; /* Allocate network device. */ - netdev = xcalloc(1, sizeof *netdev); + netdev = xzalloc(sizeof *netdev); netdev_init(&netdev->netdev, suffix, &netdev_linux_class); netdev->netdev_fd = -1; netdev->tap_fd = -1; @@ -237,9 +239,11 @@ netdev_linux_open(const char *name, char *suffix, int ethertype, /* Create tap device. */ ifr.ifr_flags = IFF_TAP | IFF_NO_PI; - error = netdev_linux_do_ioctl(&netdev->netdev, &ifr, - TUNSETIFF, "TUNSETIFF"); - if (error) { + strncpy(ifr.ifr_name, suffix, sizeof ifr.ifr_name); + if (ioctl(netdev->tap_fd, TUNSETIFF, &ifr) == -1) { + VLOG_WARN("%s: creating tap device failed: %s", suffix, + strerror(errno)); + error = errno; goto error; } @@ -366,7 +370,7 @@ netdev_linux_recv(struct netdev *netdev_, void *data, size_t size) if (netdev->tap_fd < 0) { /* Device was opened with NETDEV_ETH_TYPE_NONE. */ - return EAGAIN; + return -EAGAIN; } for (;;) { @@ -378,7 +382,7 @@ netdev_linux_recv(struct netdev *netdev_, void *data, size_t size) VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s", strerror(errno), netdev_get_name(netdev_)); } - return errno; + return -errno; } } } @@ -488,9 +492,17 @@ netdev_linux_set_etheraddr(struct netdev *netdev_, const uint8_t mac[ETH_ADDR_LEN]) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); - int error = set_etheraddr(netdev_get_name(netdev_), ARPHRD_ETHER, mac); - if (!error) { - memcpy(netdev->cache->etheraddr, mac, ETH_ADDR_LEN); + int error; + + if (!(netdev->cache->valid & VALID_ETHERADDR) + || !eth_addr_equals(netdev->cache->etheraddr, mac)) { + error = set_etheraddr(netdev_get_name(netdev_), ARPHRD_ETHER, mac); + if (!error) { + netdev->cache->valid |= VALID_ETHERADDR; + memcpy(netdev->cache->etheraddr, mac, ETH_ADDR_LEN); + } + } else { + error = 0; } return error; } @@ -625,25 +637,83 @@ check_for_working_netlink_stats(void) * XXX All of the members of struct netdev_stats are 64 bits wide, but on * 32-bit architectures the Linux network stats are only 32 bits. */ static int -netdev_linux_get_stats(const struct netdev *netdev, struct netdev_stats *stats) +netdev_linux_get_stats(const struct netdev *netdev_, struct netdev_stats *stats) { + struct netdev_linux *netdev = netdev_linux_cast(netdev_); static int use_netlink_stats = -1; int error; + struct netdev_stats raw_stats; + struct netdev_stats *collect_stats = stats; COVERAGE_INC(netdev_get_stats); + + if (!(netdev->cache->valid & VALID_IS_INTERNAL)) { + netdev->cache->is_internal = (netdev->tap_fd != -1); + + if (!netdev->cache->is_internal) { + struct ethtool_drvinfo drvinfo; + + memset(&drvinfo, 0, sizeof drvinfo); + error = netdev_linux_do_ethtool(&netdev->netdev, + (struct ethtool_cmd *)&drvinfo, + ETHTOOL_GDRVINFO, + "ETHTOOL_GDRVINFO"); + + if (!error) { + netdev->cache->is_internal = !strcmp(drvinfo.driver, + "openvswitch"); + } + } + + netdev->cache->valid |= VALID_IS_INTERNAL; + } + + if (netdev->cache->is_internal) { + collect_stats = &raw_stats; + } + if (use_netlink_stats < 0) { use_netlink_stats = check_for_working_netlink_stats(); } if (use_netlink_stats) { int ifindex; - error = get_ifindex(netdev, &ifindex); + error = get_ifindex(&netdev->netdev, &ifindex); if (!error) { - error = get_stats_via_netlink(ifindex, stats); + error = get_stats_via_netlink(ifindex, collect_stats); } } else { - error = get_stats_via_proc(netdev->name, stats); + error = get_stats_via_proc(netdev->netdev.name, collect_stats); + } + + /* If this port is an internal port then the transmit and receive stats + * will appear to be swapped relative to the other ports since we are the + * one sending the data, not a remote computer. For consistency, we swap + * them back here. */ + if (netdev->cache->is_internal) { + stats->rx_packets = raw_stats.tx_packets; + stats->tx_packets = raw_stats.rx_packets; + stats->rx_bytes = raw_stats.tx_bytes; + stats->tx_bytes = raw_stats.rx_bytes; + stats->rx_errors = raw_stats.tx_errors; + stats->tx_errors = raw_stats.rx_errors; + stats->rx_dropped = raw_stats.tx_dropped; + stats->tx_dropped = raw_stats.rx_dropped; + stats->multicast = raw_stats.multicast; + stats->collisions = raw_stats.collisions; + stats->rx_length_errors = 0; + stats->rx_over_errors = 0; + stats->rx_crc_errors = 0; + stats->rx_frame_errors = 0; + stats->rx_fifo_errors = 0; + stats->rx_missed_errors = 0; + stats->tx_aborted_errors = 0; + stats->tx_carrier_errors = 0; + stats->tx_fifo_errors = 0; + stats->tx_heartbeat_errors = 0; + stats->tx_window_errors = 0; } + return error; } @@ -1436,6 +1506,7 @@ get_stats_via_netlink(int ifindex, struct netdev_stats *stats) if (!attrs[IFLA_STATS]) { VLOG_WARN_RL(&rl, "RTM_GETLINK reply lacks stats"); + ofpbuf_delete(reply); return EPROTO; } @@ -1462,6 +1533,8 @@ get_stats_via_netlink(int ifindex, struct netdev_stats *stats) stats->tx_heartbeat_errors = rtnl_stats->tx_heartbeat_errors; stats->tx_window_errors = rtnl_stats->tx_window_errors; + ofpbuf_delete(reply); + return 0; }