@@ -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",
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(+)