X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fnetdev-linux.c;h=e1936fa6809fd5608070ee0d768aa29c8ae717fc;hb=85b20fd6ee585f462e012fbcc7f966a81edab2ed;hp=7c2b901b3631050f101235397ac2929bec5b74a7;hpb=8e8cddf7946aa19a8567d8f2edc369009520e931;p=sliver-openvswitch.git diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 7c2b901b3..e1936fa68 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks. + * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * 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 @@ -78,7 +77,9 @@ COVERAGE_DEFINE(netdev_arp_lookup); COVERAGE_DEFINE(netdev_get_ifindex); COVERAGE_DEFINE(netdev_get_hwaddr); COVERAGE_DEFINE(netdev_set_hwaddr); -COVERAGE_DEFINE(netdev_ethtool); +COVERAGE_DEFINE(netdev_get_ethtool); +COVERAGE_DEFINE(netdev_set_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_VPORT_STAT_ERROR = 1 << 6 + VALID_VPORT_STAT_ERROR = 1 << 6, + VALID_DRVINFO = 1 << 7, + VALID_FEATURES = 1 << 8, }; struct tap_state { @@ -179,7 +182,7 @@ struct tc_ops { * * (This function is null for tc_ops_other, which cannot be installed. For * other TC classes it should always be nonnull.) */ - int (*tc_install)(struct netdev *netdev, const struct shash *details); + int (*tc_install)(struct netdev *netdev, const struct smap *details); /* Called when the netdev code determines (through a Netlink query) that * this TC class's qdisc is installed on 'netdev', but we didn't install @@ -219,7 +222,7 @@ struct tc_ops { * * This function may be null if 'tc' is not configurable. */ - int (*qdisc_get)(const struct netdev *netdev, struct shash *details); + int (*qdisc_get)(const struct netdev *netdev, struct smap *details); /* Reconfigures 'netdev->tc' according to 'details', performing any * required Netlink calls to complete the reconfiguration. @@ -230,7 +233,7 @@ struct tc_ops { * * This function may be null if 'tc' is not configurable. */ - int (*qdisc_set)(struct netdev *, const struct shash *details); + int (*qdisc_set)(struct netdev *, const struct smap *details); /* Retrieves details of 'queue' on 'netdev->tc' into 'details'. 'queue' is * one of the 'struct tc_queue's within 'netdev->tc->queues'. @@ -246,7 +249,7 @@ struct tc_ops { * This function may be null if 'tc' does not have queues ('n_queues' is * 0). */ int (*class_get)(const struct netdev *netdev, const struct tc_queue *queue, - struct shash *details); + struct smap *details); /* Configures or reconfigures 'queue_id' on 'netdev->tc' according to * 'details', perfoming any required Netlink calls to complete the @@ -260,7 +263,7 @@ struct tc_ops { * This function may be null if 'tc' does not have queues or its queues are * not configurable. */ int (*class_set)(struct netdev *, unsigned int queue_id, - const struct shash *details); + const struct smap *details); /* Deletes 'queue' from 'netdev->tc'. 'queue' is one of the 'struct * tc_queue's within 'netdev->tc->queues'. @@ -374,6 +377,18 @@ struct netdev_dev_linux { uint32_t kbits_burst; 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 { @@ -412,8 +427,7 @@ 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 af_packet_sock(void); @@ -484,7 +498,9 @@ netdev_linux_wait(void) } static void -netdev_dev_linux_changed(struct netdev_dev_linux *dev, unsigned int ifi_flags) +netdev_dev_linux_changed(struct netdev_dev_linux *dev, + unsigned int ifi_flags, + unsigned int mask) { dev->change_seq++; if (!dev->change_seq) { @@ -496,7 +512,37 @@ netdev_dev_linux_changed(struct netdev_dev_linux *dev, unsigned int ifi_flags) } dev->ifi_flags = ifi_flags; - dev->cache_valid = 0; + 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 @@ -512,7 +558,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); - netdev_dev_linux_changed(dev, change->ifi_flags); + netdev_dev_linux_update(dev, change); } } } else { @@ -527,7 +573,7 @@ netdev_linux_cache_cb(const struct rtnetlink_link_change *change, dev = node->data; get_flags(&dev->netdev_dev, &flags); - netdev_dev_linux_changed(dev, flags); + netdev_dev_linux_changed(dev, flags, 0); } shash_destroy(&device_shash); } @@ -675,7 +721,6 @@ netdev_linux_destroy(struct netdev_dev *netdev_dev_) static int netdev_linux_open(struct netdev_dev *netdev_dev_, struct netdev **netdevp) { - struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_dev_); struct netdev_linux *netdev; enum netdev_flags flags; int error; @@ -700,17 +745,6 @@ netdev_linux_open(struct netdev_dev *netdev_dev_, struct netdev **netdevp) } } - if (!strcmp(netdev_dev_get_type(netdev_dev_), "tap") && - !netdev_dev->state.tap.opened) { - - /* We assume that the first user of the tap device is the primary user - * and give them the tap FD. Subsequent users probably just expect - * this to be a system device so open it normally to avoid send/receive - * directions appearing to be reversed. */ - netdev->fd = netdev_dev->state.tap.fd; - netdev_dev->state.tap.opened = true; - } - *netdevp = &netdev->netdev; return 0; @@ -735,6 +769,8 @@ static int netdev_linux_listen(struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); struct sockaddr_ll sll; int ifindex; int error; @@ -744,6 +780,13 @@ netdev_linux_listen(struct netdev *netdev_) return 0; } + if (!strcmp(netdev_get_type(netdev_), "tap") + && !netdev_dev->state.tap.opened) { + netdev->fd = netdev_dev->state.tap.fd; + netdev_dev->state.tap.opened = true; + return 0; + } + /* Create file descriptor. */ fd = socket(PF_PACKET, SOCK_RAW, 0); if (fd < 0) { @@ -887,7 +930,7 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size) sll.sll_family = AF_PACKET; sll.sll_ifindex = ifindex; - iov.iov_base = (void *) data; + iov.iov_base = CONST_CAST(void *, data); iov.iov_len = size; msg.msg_name = &sll; @@ -960,38 +1003,64 @@ netdev_linux_set_etheraddr(struct netdev *netdev_, struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev_)); int error; + bool up_again = false; - 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; + } + + /* Tap devices must be brought down before setting the address. */ + if (!strcmp(netdev_get_type(netdev_), "tap")) { + enum netdev_flags flags; + + if (!netdev_get_flags(netdev_, &flags) && (flags & NETDEV_UP)) { + netdev_turn_flags_off(netdev_, NETDEV_UP, false); + up_again = true; + } + } + 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; } + + if (up_again) { + netdev_turn_flags_on(netdev_, NETDEV_UP, false); + } + 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', @@ -1008,14 +1077,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 @@ -1029,20 +1100,24 @@ netdev_linux_set_mtu(const struct netdev *netdev_, int mtu) struct ifreq ifr; int error; - if (netdev_dev->cache_valid & VALID_MTU && - netdev_dev->mtu == mtu) { - return 0; + 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. @@ -1119,6 +1194,7 @@ netdev_linux_get_miimon(const char *name, bool *miimon) VLOG_DBG_RL(&rl, "%s: failed to query MII, falling back to ethtool", name); + COVERAGE_INC(netdev_get_ethtool); memset(&ecmd, 0, sizeof ecmd); error = netdev_linux_do_ethtool(name, &ecmd, ETHTOOL_GLINK, "ETHTOOL_GLINK"); @@ -1171,7 +1247,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, dev->ifi_flags); + netdev_dev_linux_changed(dev, dev->ifi_flags, 0); } timer_set_duration(&dev->miimon_timer, dev->miimon_interval); @@ -1246,7 +1322,7 @@ get_stats_via_vport(const struct netdev *netdev_, int error; error = netdev_vport_get_stats(netdev_, stats); - if (error) { + if (error && error != ENOENT) { VLOG_WARN_RL(&rl, "%s: obtaining netdev stats via vport failed " "(%s)", netdev_get_name(netdev_), strerror(error)); } @@ -1401,140 +1477,199 @@ netdev_internal_get_stats(const struct netdev *netdev_, return netdev_dev->vport_stats_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) +netdev_internal_set_stats(struct netdev *netdev, + const struct netdev_stats *stats) +{ + struct ovs_vport_stats vport_stats; + struct dpif_linux_vport vport; + int err; + + vport_stats.rx_packets = stats->rx_packets; + vport_stats.tx_packets = stats->tx_packets; + vport_stats.rx_bytes = stats->rx_bytes; + vport_stats.tx_bytes = stats->tx_bytes; + vport_stats.rx_errors = stats->rx_errors; + vport_stats.tx_errors = stats->tx_errors; + vport_stats.rx_dropped = stats->rx_dropped; + vport_stats.tx_dropped = stats->tx_dropped; + + dpif_linux_vport_init(&vport); + vport.cmd = OVS_VPORT_CMD_SET; + vport.name = netdev_get_name(netdev); + vport.stats = &vport_stats; + + err = dpif_linux_vport_transact(&vport, NULL, NULL); + + /* If the vport layer doesn't know about the device, that doesn't mean it + * doesn't exist (after all were able to open it when netdev_open() was + * called), it just means that it isn't attached and we'll be getting + * stats a different way. */ + if (err == ENODEV) { + err = EOPNOTSUPP; + } + + return err; +} + +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; + } + + COVERAGE_INC(netdev_get_ethtool); 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 |= NETDEV_F_10MB_HD; + netdev_dev->supported |= NETDEV_F_10MB_HD; } if (ecmd.supported & SUPPORTED_10baseT_Full) { - *supported |= NETDEV_F_10MB_FD; + netdev_dev->supported |= NETDEV_F_10MB_FD; } if (ecmd.supported & SUPPORTED_100baseT_Half) { - *supported |= NETDEV_F_100MB_HD; + netdev_dev->supported |= NETDEV_F_100MB_HD; } if (ecmd.supported & SUPPORTED_100baseT_Full) { - *supported |= NETDEV_F_100MB_FD; + netdev_dev->supported |= NETDEV_F_100MB_FD; } if (ecmd.supported & SUPPORTED_1000baseT_Half) { - *supported |= NETDEV_F_1GB_HD; + netdev_dev->supported |= NETDEV_F_1GB_HD; } if (ecmd.supported & SUPPORTED_1000baseT_Full) { - *supported |= NETDEV_F_1GB_FD; + netdev_dev->supported |= NETDEV_F_1GB_FD; } if (ecmd.supported & SUPPORTED_10000baseT_Full) { - *supported |= NETDEV_F_10GB_FD; + netdev_dev->supported |= NETDEV_F_10GB_FD; } if (ecmd.supported & SUPPORTED_TP) { - *supported |= NETDEV_F_COPPER; + netdev_dev->supported |= NETDEV_F_COPPER; } if (ecmd.supported & SUPPORTED_FIBRE) { - *supported |= NETDEV_F_FIBER; + netdev_dev->supported |= NETDEV_F_FIBER; } if (ecmd.supported & SUPPORTED_Autoneg) { - *supported |= NETDEV_F_AUTONEG; + netdev_dev->supported |= NETDEV_F_AUTONEG; } if (ecmd.supported & SUPPORTED_Pause) { - *supported |= NETDEV_F_PAUSE; + netdev_dev->supported |= NETDEV_F_PAUSE; } if (ecmd.supported & SUPPORTED_Asym_Pause) { - *supported |= NETDEV_F_PAUSE_ASYM; + netdev_dev->supported |= NETDEV_F_PAUSE_ASYM; } /* Advertised features. */ - *advertised = 0; + netdev_dev->advertised = 0; if (ecmd.advertising & ADVERTISED_10baseT_Half) { - *advertised |= NETDEV_F_10MB_HD; + netdev_dev->advertised |= NETDEV_F_10MB_HD; } if (ecmd.advertising & ADVERTISED_10baseT_Full) { - *advertised |= NETDEV_F_10MB_FD; + netdev_dev->advertised |= NETDEV_F_10MB_FD; } if (ecmd.advertising & ADVERTISED_100baseT_Half) { - *advertised |= NETDEV_F_100MB_HD; + netdev_dev->advertised |= NETDEV_F_100MB_HD; } if (ecmd.advertising & ADVERTISED_100baseT_Full) { - *advertised |= NETDEV_F_100MB_FD; + netdev_dev->advertised |= NETDEV_F_100MB_FD; } if (ecmd.advertising & ADVERTISED_1000baseT_Half) { - *advertised |= NETDEV_F_1GB_HD; + netdev_dev->advertised |= NETDEV_F_1GB_HD; } if (ecmd.advertising & ADVERTISED_1000baseT_Full) { - *advertised |= NETDEV_F_1GB_FD; + netdev_dev->advertised |= NETDEV_F_1GB_FD; } if (ecmd.advertising & ADVERTISED_10000baseT_Full) { - *advertised |= NETDEV_F_10GB_FD; + netdev_dev->advertised |= NETDEV_F_10GB_FD; } if (ecmd.advertising & ADVERTISED_TP) { - *advertised |= NETDEV_F_COPPER; + netdev_dev->advertised |= NETDEV_F_COPPER; } if (ecmd.advertising & ADVERTISED_FIBRE) { - *advertised |= NETDEV_F_FIBER; + netdev_dev->advertised |= NETDEV_F_FIBER; } if (ecmd.advertising & ADVERTISED_Autoneg) { - *advertised |= NETDEV_F_AUTONEG; + netdev_dev->advertised |= NETDEV_F_AUTONEG; } if (ecmd.advertising & ADVERTISED_Pause) { - *advertised |= NETDEV_F_PAUSE; + netdev_dev->advertised |= NETDEV_F_PAUSE; } if (ecmd.advertising & ADVERTISED_Asym_Pause) { - *advertised |= NETDEV_F_PAUSE_ASYM; + netdev_dev->advertised |= NETDEV_F_PAUSE_ASYM; } /* Current settings. */ speed = ecmd.speed; if (speed == SPEED_10) { - *current = ecmd.duplex ? NETDEV_F_10MB_FD : NETDEV_F_10MB_HD; + netdev_dev->current = ecmd.duplex ? NETDEV_F_10MB_FD : NETDEV_F_10MB_HD; } else if (speed == SPEED_100) { - *current = ecmd.duplex ? NETDEV_F_100MB_FD : NETDEV_F_100MB_HD; + netdev_dev->current = ecmd.duplex ? NETDEV_F_100MB_FD : NETDEV_F_100MB_HD; } else if (speed == SPEED_1000) { - *current = ecmd.duplex ? NETDEV_F_1GB_FD : NETDEV_F_1GB_HD; + netdev_dev->current = ecmd.duplex ? NETDEV_F_1GB_FD : NETDEV_F_1GB_HD; } else if (speed == SPEED_10000) { - *current = NETDEV_F_10GB_FD; + netdev_dev->current = NETDEV_F_10GB_FD; } else if (speed == 40000) { - *current = NETDEV_F_40GB_FD; + netdev_dev->current = NETDEV_F_40GB_FD; } else if (speed == 100000) { - *current = NETDEV_F_100GB_FD; + netdev_dev->current = NETDEV_F_100GB_FD; } else if (speed == 1000000) { - *current = NETDEV_F_1TB_FD; + netdev_dev->current = NETDEV_F_1TB_FD; } else { - *current = 0; + netdev_dev->current = 0; } if (ecmd.port == PORT_TP) { - *current |= NETDEV_F_COPPER; + netdev_dev->current |= NETDEV_F_COPPER; } else if (ecmd.port == PORT_FIBRE) { - *current |= NETDEV_F_FIBER; + netdev_dev->current |= NETDEV_F_FIBER; } if (ecmd.autoneg) { - *current |= NETDEV_F_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'. */ @@ -1545,6 +1680,7 @@ netdev_linux_set_advertisements(struct netdev *netdev, struct ethtool_cmd ecmd; int error; + COVERAGE_INC(netdev_get_ethtool); memset(&ecmd, 0, sizeof ecmd); error = netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd, ETHTOOL_GSET, "ETHTOOL_GSET"); @@ -1589,6 +1725,7 @@ netdev_linux_set_advertisements(struct netdev *netdev, if (advertise & NETDEV_F_PAUSE_ASYM) { ecmd.advertising |= ADVERTISED_Asym_Pause; } + COVERAGE_INC(netdev_set_ethtool); return netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd, ETHTOOL_SSET, "ETHTOOL_SSET"); } @@ -1609,11 +1746,17 @@ netdev_linux_set_policing(struct netdev *netdev, : !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); @@ -1622,7 +1765,7 @@ netdev_linux_set_policing(struct netdev *netdev, if (error) { VLOG_WARN_RL(&rl, "%s: removing policing failed: %s", netdev_name, strerror(error)); - return error; + goto out; } if (kbits_rate) { @@ -1630,22 +1773,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 @@ -1728,7 +1875,7 @@ netdev_linux_get_qos_capabilities(const struct netdev *netdev OVS_UNUSED, static int netdev_linux_get_qos(const struct netdev *netdev, - const char **typep, struct shash *details) + const char **typep, struct smap *details) { struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev)); @@ -1747,7 +1894,7 @@ netdev_linux_get_qos(const struct netdev *netdev, static int netdev_linux_set_qos(struct netdev *netdev, - const char *type, const struct shash *details) + const char *type, const struct smap *details) { struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev)); @@ -1784,7 +1931,7 @@ netdev_linux_set_qos(struct netdev *netdev, static int netdev_linux_get_queue(const struct netdev *netdev, - unsigned int queue_id, struct shash *details) + unsigned int queue_id, struct smap *details) { struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev)); @@ -1803,7 +1950,7 @@ netdev_linux_get_queue(const struct netdev *netdev, static int netdev_linux_set_queue(struct netdev *netdev, - unsigned int queue_id, const struct shash *details) + unsigned int queue_id, const struct smap *details) { struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev)); @@ -1884,8 +2031,8 @@ 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 shash details; + struct tc_queue *queue, *next_queue; + struct smap details; int last_error; int error; @@ -1897,9 +2044,10 @@ netdev_linux_dump_queues(const struct netdev *netdev, } last_error = 0; - shash_init(&details); - HMAP_FOR_EACH (queue, hmap_node, &netdev_dev->tc->queues) { - shash_clear(&details); + smap_init(&details); + HMAP_FOR_EACH_SAFE (queue, next_queue, hmap_node, + &netdev_dev->tc->queues) { + smap_clear(&details); error = netdev_dev->tc->ops->class_get(netdev, queue, &details); if (!error) { @@ -1908,7 +2056,7 @@ netdev_linux_dump_queues(const struct netdev *netdev, last_error = error; } } - shash_destroy(&details); + smap_destroy(&details); return last_error; } @@ -2153,25 +2301,42 @@ 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_status(const struct netdev *netdev, struct smap *smap) { - struct ethtool_drvinfo drvinfo; - int error; + struct netdev_dev_linux *netdev_dev; + int error = 0; - memset(&drvinfo, 0, sizeof drvinfo); - error = netdev_linux_do_ethtool(netdev_get_name(netdev), - (struct ethtool_cmd *)&drvinfo, - ETHTOOL_GDRVINFO, - "ETHTOOL_GDRVINFO"); - 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)); + netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev)); + if (!(netdev_dev->cache_valid & VALID_DRVINFO)) { + struct ethtool_cmd *cmd = (struct ethtool_cmd *) &netdev_dev->drvinfo; + + COVERAGE_INC(netdev_get_ethtool); + memset(&netdev_dev->drvinfo, 0, sizeof netdev_dev->drvinfo); + error = netdev_linux_do_ethtool(netdev_dev->netdev_dev.name, + cmd, + ETHTOOL_GDRVINFO, + "ETHTOOL_GDRVINFO"); + if (!error) { + netdev_dev->cache_valid |= VALID_DRVINFO; + } } + if (!error) { + smap_add(smap, "driver_name", netdev_dev->drvinfo.driver); + smap_add(smap, "driver_version", netdev_dev->drvinfo.version); + smap_add(smap, "firmware_version", netdev_dev->drvinfo.fw_version); + } return error; } +static int +netdev_internal_get_status(const struct netdev *netdev OVS_UNUSED, + struct smap *smap) +{ + smap_add(smap, "driver_name", "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, @@ -2199,7 +2364,7 @@ netdev_linux_arp_lookup(const struct netdev *netdev, memcpy(mac, r.arp_ha.sa_data, ETH_ADDR_LEN); } else if (retval != ENXIO) { VLOG_WARN_RL(&rl, "%s: could not look up ARP entry for "IP_FMT": %s", - netdev_get_name(netdev), IP_ARGS(&ip), strerror(retval)); + netdev_get_name(netdev), IP_ARGS(ip), strerror(retval)); } return retval; } @@ -2255,7 +2420,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,6 +2433,7 @@ netdev_linux_change_seq(const struct netdev *netdev) netdev_linux_destroy, \ NULL, /* get_config */ \ NULL, /* set_config */ \ + NULL, /* get_tunnel_config */ \ \ netdev_linux_open, \ netdev_linux_close, \ @@ -2290,7 +2457,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, \ @@ -2310,7 +2477,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, \ @@ -2323,21 +2490,27 @@ 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_status); const struct netdev_class netdev_tap_class = NETDEV_LINUX_CLASS( "tap", netdev_linux_create_tap, netdev_tap_get_stats, - NULL); /* set_stats */ + NULL, /* set_stats */ + netdev_linux_get_features, + netdev_linux_get_status); const struct netdev_class netdev_internal_class = NETDEV_LINUX_CLASS( "internal", netdev_linux_create, netdev_internal_get_stats, - netdev_vport_set_stats); + netdev_internal_set_stats, + NULL, /* get_features */ + netdev_internal_get_status); /* HTB traffic control class. */ @@ -2523,17 +2696,17 @@ htb_parse_tcmsg__(struct ofpbuf *tcmsg, unsigned int *queue_id, static void htb_parse_qdisc_details__(struct netdev *netdev, - const struct shash *details, struct htb_class *hc) + const struct smap *details, struct htb_class *hc) { const char *max_rate_s; - max_rate_s = shash_find_data(details, "max-rate"); + max_rate_s = smap_get(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; + hc->max_rate = netdev_features_to_bps(current, 100 * 1000 * 1000) / 8; } hc->min_rate = hc->max_rate; hc->burst = 0; @@ -2542,13 +2715,13 @@ htb_parse_qdisc_details__(struct netdev *netdev, static int htb_parse_class_details__(struct netdev *netdev, - const struct shash *details, struct htb_class *hc) + const struct smap *details, struct htb_class *hc) { const struct htb *htb = htb_get__(netdev); - const char *min_rate_s = shash_find_data(details, "min-rate"); - const char *max_rate_s = shash_find_data(details, "max-rate"); - const char *burst_s = shash_find_data(details, "burst"); - const char *priority_s = shash_find_data(details, "priority"); + const char *min_rate_s = smap_get(details, "min-rate"); + const char *max_rate_s = smap_get(details, "max-rate"); + const char *burst_s = smap_get(details, "burst"); + const char *priority_s = smap_get(details, "priority"); int mtu, error; error = netdev_get_mtu(netdev, &mtu); @@ -2606,7 +2779,7 @@ htb_query_class__(const struct netdev *netdev, unsigned int handle, } static int -htb_tc_install(struct netdev *netdev, const struct shash *details) +htb_tc_install(struct netdev *netdev, const struct smap *details) { int error; @@ -2698,15 +2871,15 @@ htb_tc_destroy(struct tc *tc) } static int -htb_qdisc_get(const struct netdev *netdev, struct shash *details) +htb_qdisc_get(const struct netdev *netdev, struct smap *details) { const struct htb *htb = htb_get__(netdev); - shash_add(details, "max-rate", xasprintf("%llu", 8ULL * htb->max_rate)); + smap_add_format(details, "max-rate", "%llu", 8ULL * htb->max_rate); return 0; } static int -htb_qdisc_set(struct netdev *netdev, const struct shash *details) +htb_qdisc_set(struct netdev *netdev, const struct smap *details) { struct htb_class hc; int error; @@ -2722,24 +2895,24 @@ htb_qdisc_set(struct netdev *netdev, const struct shash *details) static int htb_class_get(const struct netdev *netdev OVS_UNUSED, - const struct tc_queue *queue, struct shash *details) + const struct tc_queue *queue, struct smap *details) { const struct htb_class *hc = htb_class_cast__(queue); - shash_add(details, "min-rate", xasprintf("%llu", 8ULL * hc->min_rate)); + smap_add_format(details, "min-rate", "%llu", 8ULL * hc->min_rate); if (hc->min_rate != hc->max_rate) { - shash_add(details, "max-rate", xasprintf("%llu", 8ULL * hc->max_rate)); + smap_add_format(details, "max-rate", "%llu", 8ULL * hc->max_rate); } - shash_add(details, "burst", xasprintf("%llu", 8ULL * hc->burst)); + smap_add_format(details, "burst", "%llu", 8ULL * hc->burst); if (hc->priority) { - shash_add(details, "priority", xasprintf("%u", hc->priority)); + smap_add_format(details, "priority", "%u", hc->priority); } return 0; } static int htb_class_set(struct netdev *netdev, unsigned int queue_id, - const struct shash *details) + const struct smap *details) { struct htb_class hc; int error; @@ -2999,20 +3172,20 @@ hfsc_query_class__(const struct netdev *netdev, unsigned int handle, } static void -hfsc_parse_qdisc_details__(struct netdev *netdev, const struct shash *details, +hfsc_parse_qdisc_details__(struct netdev *netdev, const struct smap *details, struct hfsc_class *class) { uint32_t max_rate; const char *max_rate_s; - max_rate_s = shash_find_data(details, "max-rate"); + max_rate_s = smap_get(details, "max-rate"); 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; + max_rate = netdev_features_to_bps(current, 100 * 1000 * 1000) / 8; } class->min_rate = max_rate; @@ -3021,7 +3194,7 @@ hfsc_parse_qdisc_details__(struct netdev *netdev, const struct shash *details, static int hfsc_parse_class_details__(struct netdev *netdev, - const struct shash *details, + const struct smap *details, struct hfsc_class * class) { const struct hfsc *hfsc; @@ -3029,8 +3202,8 @@ hfsc_parse_class_details__(struct netdev *netdev, const char *min_rate_s, *max_rate_s; hfsc = hfsc_get__(netdev); - min_rate_s = shash_find_data(details, "min-rate"); - max_rate_s = shash_find_data(details, "max-rate"); + min_rate_s = smap_get(details, "min-rate"); + max_rate_s = smap_get(details, "max-rate"); min_rate = min_rate_s ? strtoull(min_rate_s, NULL, 10) / 8 : 0; min_rate = MAX(min_rate, 1); @@ -3131,7 +3304,7 @@ hfsc_setup_class__(struct netdev *netdev, unsigned int handle, } static int -hfsc_tc_install(struct netdev *netdev, const struct shash *details) +hfsc_tc_install(struct netdev *netdev, const struct smap *details) { int error; struct hfsc_class class; @@ -3199,16 +3372,16 @@ hfsc_tc_destroy(struct tc *tc) } static int -hfsc_qdisc_get(const struct netdev *netdev, struct shash *details) +hfsc_qdisc_get(const struct netdev *netdev, struct smap *details) { const struct hfsc *hfsc; hfsc = hfsc_get__(netdev); - shash_add(details, "max-rate", xasprintf("%llu", 8ULL * hfsc->max_rate)); + smap_add_format(details, "max-rate", "%llu", 8ULL * hfsc->max_rate); return 0; } static int -hfsc_qdisc_set(struct netdev *netdev, const struct shash *details) +hfsc_qdisc_set(struct netdev *netdev, const struct smap *details) { int error; struct hfsc_class class; @@ -3226,21 +3399,21 @@ hfsc_qdisc_set(struct netdev *netdev, const struct shash *details) static int hfsc_class_get(const struct netdev *netdev OVS_UNUSED, - const struct tc_queue *queue, struct shash *details) + const struct tc_queue *queue, struct smap *details) { const struct hfsc_class *hc; hc = hfsc_class_cast__(queue); - shash_add(details, "min-rate", xasprintf("%llu", 8ULL * hc->min_rate)); + smap_add_format(details, "min-rate", "%llu", 8ULL * hc->min_rate); if (hc->min_rate != hc->max_rate) { - shash_add(details, "max-rate", xasprintf("%llu", 8ULL * hc->max_rate)); + smap_add_format(details, "max-rate", "%llu", 8ULL * hc->max_rate); } return 0; } static int hfsc_class_set(struct netdev *netdev, unsigned int queue_id, - const struct shash *details) + const struct smap *details) { int error; struct hfsc_class class; @@ -3345,7 +3518,7 @@ default_install__(struct netdev *netdev) static int default_tc_install(struct netdev *netdev, - const struct shash *details OVS_UNUSED) + const struct smap *details OVS_UNUSED) { default_install__(netdev); return 0; @@ -3971,7 +4144,7 @@ tc_query_qdisc(const struct netdev *netdev) } /* Instantiate it. */ - load_error = ops->tc_load((struct netdev *) netdev, qdisc); + load_error = ops->tc_load(CONST_CAST(struct netdev *, netdev), qdisc); assert((load_error == 0) == (netdev_dev->tc != NULL)); ofpbuf_delete(qdisc); @@ -4068,6 +4241,7 @@ netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag, uint32_t new_flags; int error; + COVERAGE_INC(netdev_get_ethtool); memset(&evalue, 0, sizeof evalue); error = netdev_linux_do_ethtool(netdev_name, (struct ethtool_cmd *)&evalue, @@ -4076,6 +4250,7 @@ netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag, return error; } + COVERAGE_INC(netdev_set_ethtool); evalue.data = new_flags = (evalue.data & ~flag) | (enable ? flag : 0); error = netdev_linux_do_ethtool(netdev_name, (struct ethtool_cmd *)&evalue, @@ -4084,6 +4259,7 @@ netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag, return error; } + COVERAGE_INC(netdev_get_ethtool); memset(&evalue, 0, sizeof evalue); error = netdev_linux_do_ethtool(netdev_name, (struct ethtool_cmd *)&evalue, @@ -4284,17 +4460,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 @@ -4325,14 +4506,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) { @@ -4354,7 +4535,6 @@ netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *ecmd, ifr.ifr_data = (caddr_t) ecmd; ecmd->cmd = cmd; - COVERAGE_INC(netdev_ethtool); if (ioctl(af_inet_sock, SIOCETHTOOL, &ifr) == 0) { return 0; } else {