Merge branch 'master' into forward-port
[sliver-openvswitch.git] / lib / netdev-linux.c
index d2a5c7a..611b86d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -69,6 +69,7 @@
 #include "sset.h"
 #include "timer.h"
 #include "vlog.h"
+#include "tunalloc.h"
 
 VLOG_DEFINE_THIS_MODULE(netdev_linux);
 
@@ -77,7 +78,8 @@ 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);
 
 \f
 /* These were introduced in Linux 2.6.14, so they might be missing if we have
@@ -181,7 +183,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
@@ -221,7 +223,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.
@@ -232,7 +234,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'.
@@ -248,7 +250,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
@@ -262,7 +264,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'.
@@ -506,6 +508,7 @@ netdev_linux_get_drvinfo(struct netdev_dev_linux *netdev_dev)
         return 0;
     }
 
+    COVERAGE_INC(netdev_get_ethtool);
     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,
@@ -730,7 +733,7 @@ netdev_linux_destroy(struct netdev_dev *netdev_dev_)
         netdev_dev->tc->ops->tc_destroy(netdev_dev->tc);
     }
 
-    if (class == &netdev_tap_class) {
+    if (class == &netdev_tap_class || class == &netdev_tap_pl_class) {
         destroy_tap(netdev_dev);
     }
     free(netdev_dev);
@@ -766,7 +769,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
@@ -791,7 +794,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);
@@ -865,7 +868,8 @@ netdev_linux_recv(struct netdev *netdev_, void *data, size_t size)
     for (;;) {
         ssize_t retval;
 
-        retval = (netdev_->netdev_dev->netdev_class == &netdev_tap_class
+        retval = ((netdev_->netdev_dev->netdev_class == &netdev_tap_class ||
+                   netdev_->netdev_dev->netdev_class == &netdev_tap_pl_class)
                   ? read(netdev->fd, data, size)
                   : recv(netdev->fd, data, size, MSG_TRUNC));
         if (retval >= 0) {
@@ -898,7 +902,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");
@@ -953,7 +957,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;
@@ -1009,7 +1013,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.*/
@@ -1203,6 +1207,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");
@@ -1496,6 +1501,7 @@ netdev_linux_read_features(struct netdev_dev_linux *netdev_dev)
         return;
     }
 
+    COVERAGE_INC(netdev_get_ethtool);
     memset(&ecmd, 0, sizeof ecmd);
     error = netdev_linux_do_ethtool(netdev_dev->netdev_dev.name, &ecmd,
                                     ETHTOOL_GSET, "ETHTOOL_GSET");
@@ -1652,6 +1658,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");
@@ -1696,6 +1703,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");
 }
@@ -1780,6 +1788,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)
 {
@@ -1845,7 +1898,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));
@@ -1864,7 +1917,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));
@@ -1901,7 +1954,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));
@@ -1920,7 +1973,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));
@@ -2002,7 +2055,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, *next_queue;
-    struct shash details;
+    struct smap details;
     int last_error;
     int error;
 
@@ -2014,10 +2067,10 @@ netdev_linux_dump_queues(const struct netdev *netdev,
     }
 
     last_error = 0;
-    shash_init(&details);
+    smap_init(&details);
     HMAP_FOR_EACH_SAFE (queue, next_queue, hmap_node,
                         &netdev_dev->tc->queues) {
-        shash_clear(&details);
+        smap_clear(&details);
 
         error = netdev_dev->tc->ops->class_get(netdev, queue, &details);
         if (!error) {
@@ -2026,7 +2079,7 @@ netdev_linux_dump_queues(const struct netdev *netdev,
             last_error = error;
         }
     }
-    shash_destroy(&details);
+    smap_destroy(&details);
 
     return last_error;
 }
@@ -2271,7 +2324,7 @@ netdev_linux_get_next_hop(const struct in_addr *host, struct in_addr *next_hop,
 }
 
 static int
-netdev_linux_get_drv_info(const struct netdev *netdev, struct shash *sh)
+netdev_linux_get_drv_info(const struct netdev *netdev, struct smap *smap)
 {
     int error;
     struct netdev_dev_linux *netdev_dev =
@@ -2279,17 +2332,18 @@ netdev_linux_get_drv_info(const struct netdev *netdev, struct shash *sh)
 
     error = netdev_linux_get_drvinfo(netdev_dev);
     if (!error) {
-        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));
+        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_drv_info(const struct netdev *netdev OVS_UNUSED, struct shash *sh)
+netdev_internal_get_drv_info(const struct netdev *netdev OVS_UNUSED,
+                             struct smap *smap)
 {
-    shash_add(sh, "driver_name", xstrdup("openvswitch"));
+    smap_add(smap, "driver_name", "openvswitch");
     return 0;
 }
 
@@ -2370,6 +2424,13 @@ netdev_linux_update_flags(struct netdev *netdev, enum netdev_flags off,
     return error;
 }
 
+static int
+netdev_tap_pl_update_flags(struct netdev *netdev, enum netdev_flags off,
+                          enum netdev_flags on, enum netdev_flags *old_flagsp)
+{
+    return 0;
+}
+
 static unsigned int
 netdev_linux_change_seq(const struct netdev *netdev)
 {
@@ -2377,7 +2438,8 @@ netdev_linux_change_seq(const struct netdev *netdev)
 }
 
 #define NETDEV_LINUX_CLASS(NAME, CREATE, GET_STATS, SET_STATS,  \
-                           GET_FEATURES, GET_STATUS)            \
+                           GET_FEATURES, GET_STATUS,            \
+                           UPDATE_FLAGS)                        \
 {                                                               \
     NAME,                                                       \
                                                                 \
@@ -2435,7 +2497,7 @@ netdev_linux_change_seq(const struct netdev *netdev)
     GET_STATUS,                                                 \
     netdev_linux_arp_lookup,                                    \
                                                                 \
-    netdev_linux_update_flags,                                  \
+    UPDATE_FLAGS,                                               \
                                                                 \
     netdev_linux_change_seq                                     \
 }
@@ -2447,7 +2509,8 @@ const struct netdev_class netdev_linux_class =
         netdev_linux_get_stats,
         NULL,                    /* set_stats */
         netdev_linux_get_features,
-        netdev_linux_get_drv_info);
+        netdev_linux_get_drv_info,
+        netdev_linux_update_flags);
 
 const struct netdev_class netdev_tap_class =
     NETDEV_LINUX_CLASS(
@@ -2456,7 +2519,8 @@ const struct netdev_class netdev_tap_class =
         netdev_tap_get_stats,
         NULL,                   /* set_stats */
         netdev_linux_get_features,
-        netdev_linux_get_drv_info);
+        netdev_linux_get_drv_info,
+        netdev_linux_update_flags);
 
 const struct netdev_class netdev_internal_class =
     NETDEV_LINUX_CLASS(
@@ -2465,7 +2529,18 @@ const struct netdev_class netdev_internal_class =
         netdev_internal_get_stats,
         netdev_vport_set_stats,
         NULL,                  /* get_features */
-        netdev_internal_get_drv_info);
+        netdev_internal_get_drv_info,
+        netdev_linux_update_flags);
+
+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,
+       netdev_tap_pl_update_flags);    
 \f
 /* HTB traffic control class. */
 
@@ -2651,11 +2726,11 @@ 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) {
         enum netdev_features current;
@@ -2670,13 +2745,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);
@@ -2734,7 +2809,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;
 
@@ -2826,15 +2901,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;
@@ -2850,24 +2925,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;
@@ -3127,13 +3202,13 @@ 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) {
@@ -3149,7 +3224,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;
@@ -3157,8 +3232,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);
@@ -3259,7 +3334,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;
@@ -3327,16 +3402,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;
@@ -3354,21 +3429,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;
@@ -3473,7 +3548,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;
@@ -4099,7 +4174,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);
 
@@ -4196,6 +4271,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,
@@ -4204,6 +4280,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,
@@ -4212,6 +4289,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,
@@ -4487,7 +4565,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 {