[ovs-dev,2/2] dpif-netlink: Probe for broken Linux meter implementations.

Message ID 20180809003522.10837-2-jpettit@ovn.org
State Accepted
Headers show
Series
  • [ovs-dev,1/2] dpif: Don't pass in '*meter_id' to meter_set commands.
Related show

Commit Message

Justin Pettit Aug. 9, 2018, 12:35 a.m.
Meter support was introduced in Linux 4.15.  In some versions of Linux
4.15, 4.16, and 4.17, there was a bug that never set the id when the
meter was created, so all meters essentially had an id of zero.  This
commit adds a probe to check for that condition and disable meters on
those kernels.

Signed-off-by: Justin Pettit <jpettit@ovn.org>
---
 lib/dpif-netlink.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)

Patch

diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index bf94e5413e71..60ce1a6d22a3 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -2926,6 +2926,12 @@  dpif_netlink_ct_flush(struct dpif *dpif OVS_UNUSED, const uint16_t *zone,
 #define DP_SUPPORTED_METER_FLAGS_MASK \
     (OFPMF13_STATS | OFPMF13_PKTPS | OFPMF13_KBPS | OFPMF13_BURST)
 
+/* Meter support was introduced in Linux 4.15.  In some versions of
+ * Linux 4.15, 4.16, and 4.17, there was a bug that never set the id
+ * when the meter was created, so all meters essentially had an id of
+ * zero.  Check for that condition and disable meters on those kernels. */
+static bool probe_broken_meters(struct dpif *);
+
 static void
 dpif_netlink_meter_init(struct dpif_netlink *dpif, struct ofpbuf *buf,
                         void *stub, size_t size, uint32_t command)
@@ -2977,6 +2983,11 @@  static void
 dpif_netlink_meter_get_features(const struct dpif *dpif_,
                                 struct ofputil_meter_features *features)
 {
+    if (probe_broken_meters((struct dpif *)dpif_)) {
+        features = NULL;
+        return;
+    }
+
     struct ofpbuf buf, *msg;
     uint64_t stub[1024 / 8];
 
@@ -3033,6 +3044,10 @@  static int
 dpif_netlink_meter_set(struct dpif *dpif_, ofproto_meter_id meter_id,
                        struct ofputil_meter_config *config)
 {
+    if (probe_broken_meters(dpif_)) {
+        return ENOMEM;
+    }
+
     struct dpif_netlink *dpif = dpif_netlink_cast(dpif_);
     struct ofpbuf buf, *msg;
     uint64_t stub[1024 / 8];
@@ -3206,6 +3221,45 @@  dpif_netlink_meter_del(struct dpif *dpif, ofproto_meter_id meter_id,
                                         OVS_METER_CMD_DEL);
 }
 
+static bool
+probe_broken_meters(struct dpif *dpif)
+{
+    static bool checked = false;
+    static bool broken_meters = false;
+
+    if (checked) {
+        return broken_meters;
+    }
+    checked = true;
+
+    /* This test is destructive if a probe occurs while ovs-vswitchd is
+     * running (e.g., an ovs-dpctl meter command is called), so choose a
+     * random high meter id to make this less likely to occur. */
+    ofproto_meter_id id1 = { 54545401 };
+    ofproto_meter_id id2 = { 54545402 };
+    struct ofputil_meter_band band = {OFPMBT13_DROP, 0, 1, 0};
+    struct ofputil_meter_config config1 = { 1, OFPMF13_KBPS, 1, &band};
+    struct ofputil_meter_config config2 = { 2, OFPMF13_KBPS, 1, &band};
+
+    /* Try adding two meters and make sure that they both come back with
+     * the proper meter id. */
+    dpif_netlink_meter_set(dpif, id1, &config1);
+    dpif_netlink_meter_set(dpif, id2, &config2);
+
+    if (dpif_netlink_meter_get(dpif, id1, NULL, 0)
+        || dpif_netlink_meter_get(dpif, id2, NULL, 0)) {
+        VLOG_INFO("The kernel module has a broken meter implementation.");
+        broken_meters = true;
+        goto done;
+    }
+
+    dpif_netlink_meter_del(dpif, id1, NULL, 0);
+    dpif_netlink_meter_del(dpif, id2, NULL, 0);
+
+done:
+    return broken_meters;
+}
+
 
 const struct dpif_class dpif_netlink_class = {
     "system",