ofproto-dpif-ipfix: Make the ofproto-dpif-ipfix module thread safe.
[sliver-openvswitch.git] / ofproto / ofproto-dpif-ipfix.c
index 083fe15..5f01327 100644 (file)
@@ -33,6 +33,7 @@
 VLOG_DEFINE_THIS_MODULE(ipfix);
 
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
 
 /* Cf. IETF RFC 5101 Section 10.3.4. */
 #define IPFIX_DEFAULT_COLLECTOR_PORT 4739
@@ -62,6 +63,7 @@ struct dpif_ipfix_flow_exporter_map_node {
 struct dpif_ipfix {
     struct dpif_ipfix_bridge_exporter bridge_exporter;
     struct hmap flow_exporter_map;  /* dpif_ipfix_flow_exporter_map_nodes. */
+    atomic_int ref_cnt;
 };
 
 #define IPFIX_VERSION 0x000a
@@ -72,23 +74,25 @@ struct dpif_ipfix {
 #define IPFIX_TEMPLATE_INTERVAL 600
 
 /* Cf. IETF RFC 5101 Section 3.1. */
+OVS_PACKED(
 struct ipfix_header {
     ovs_be16 version;  /* IPFIX_VERSION. */
     ovs_be16 length;  /* Length in bytes including this header. */
     ovs_be32 export_time;  /* Seconds since the epoch. */
     ovs_be32 seq_number;  /* Message sequence number. */
     ovs_be32 obs_domain_id;  /* Observation Domain ID. */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_header) == 16);
 
 #define IPFIX_SET_ID_TEMPLATE 2
 #define IPFIX_SET_ID_OPTION_TEMPLATE 3
 
 /* Cf. IETF RFC 5101 Section 3.3.2. */
+OVS_PACKED(
 struct ipfix_set_header {
     ovs_be16 set_id;  /* IPFIX_SET_ID_* or valid template ID for Data Sets. */
     ovs_be16 length;  /* Length of the set in bytes including header. */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_set_header) == 4);
 
 /* Alternatives for templates at each layer.  A template is defined by
@@ -114,10 +118,11 @@ enum ipfix_proto_l4 {
 #define IPFIX_TEMPLATE_ID_MIN 256
 
 /* Cf. IETF RFC 5101 Section 3.4.1. */
+OVS_PACKED(
 struct ipfix_template_record_header {
     ovs_be16 template_id;
     ovs_be16 field_count;
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_template_record_header) == 4);
 
 enum ipfix_entity_id {
@@ -130,14 +135,16 @@ enum ipfix_entity_size {
 #include "ofproto/ipfix-entities.def"
 };
 
+OVS_PACKED(
 struct ipfix_template_field_specifier {
     ovs_be16 element_id;  /* IPFIX_ENTITY_ID_*. */
     ovs_be16 field_length;  /* Length of the field's value, in bytes. */
     /* No Enterprise ID, since only standard element IDs are specified. */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_template_field_specifier) == 4);
 
 /* Part of data record for common metadata and Ethernet entities. */
+OVS_PACKED(
 struct ipfix_data_record_common {
     ovs_be32 observation_point_id;  /* OBSERVATION_POINT_ID */
     ovs_be64 packet_delta_count;  /* PACKET_DELTA_COUNT */
@@ -147,18 +154,20 @@ struct ipfix_data_record_common {
     ovs_be16 ethernet_type;  /* ETHERNET_TYPE */
     ovs_be16 ethernet_total_length;  /* ETHERNET_TOTAL_LENGTH */
     uint8_t ethernet_header_length;  /* ETHERNET_HEADER_LENGTH */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_common) == 37);
 
 /* Part of data record for VLAN entities. */
+OVS_PACKED(
 struct ipfix_data_record_vlan {
     ovs_be16 vlan_id;  /* VLAN_ID */
     ovs_be16 dot1q_vlan_id;  /* DOT1Q_VLAN_ID */
     uint8_t dot1q_priority;  /* DOT1Q_PRIORITY */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_vlan) == 5);
 
 /* Part of data record for IP entities. */
+OVS_PACKED(
 struct ipfix_data_record_ip {
     uint8_t ip_version;  /* IP_VERSION */
     uint8_t ip_ttl;  /* IP_TTL */
@@ -166,29 +175,32 @@ struct ipfix_data_record_ip {
     uint8_t ip_diff_serv_code_point;  /* IP_DIFF_SERV_CODE_POINT */
     uint8_t ip_precedence;  /* IP_PRECEDENCE */
     uint8_t ip_class_of_service;  /* IP_CLASS_OF_SERVICE */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_ip) == 6);
 
 /* Part of data record for IPv4 entities. */
+OVS_PACKED(
 struct ipfix_data_record_ipv4 {
     ovs_be32 source_ipv4_address;  /* SOURCE_IPV4_ADDRESS */
     ovs_be32 destination_ipv4_address;  /* DESTINATION_IPV4_ADDRESS */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_ipv4) == 8);
 
 /* Part of data record for IPv4 entities. */
+OVS_PACKED(
 struct ipfix_data_record_ipv6 {
     uint8_t source_ipv6_address[16];  /* SOURCE_IPV6_ADDRESS */
     uint8_t destination_ipv6_address[16];  /* DESTINATION_IPV6_ADDRESS */
     ovs_be32 flow_label_ipv6;  /* FLOW_LABEL_IPV6 */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_ipv6) == 36);
 
 /* Part of data record for TCP/UDP entities. */
+OVS_PACKED(
 struct ipfix_data_record_tcpudp {
     ovs_be16 source_transport_port;  /* SOURCE_TRANSPORT_PORT */
     ovs_be16 destination_transport_port;  /* DESTINATION_TRANSPORT_PORT */
-} __attribute__((packed));
+});
 BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_tcpudp) == 4);
 
 static bool
@@ -400,13 +412,14 @@ dpif_ipfix_set_options(
     struct dpif_ipfix *di,
     const struct ofproto_ipfix_bridge_exporter_options *bridge_exporter_options,
     const struct ofproto_ipfix_flow_exporter_options *flow_exporters_options,
-    size_t n_flow_exporters_options)
+    size_t n_flow_exporters_options) OVS_EXCLUDED(mutex)
 {
     int i;
     struct ofproto_ipfix_flow_exporter_options *options;
     struct dpif_ipfix_flow_exporter_map_node *node, *next;
     size_t n_broken_flow_exporters_options = 0;
 
+    ovs_mutex_lock(&mutex);
     dpif_ipfix_bridge_exporter_set_options(&di->bridge_exporter,
                                            bridge_exporter_options);
 
@@ -455,6 +468,7 @@ dpif_ipfix_set_options(
 
     ovs_assert(hmap_count(&di->flow_exporter_map) ==
                (n_flow_exporters_options - n_broken_flow_exporters_options));
+    ovs_mutex_unlock(&mutex);
 }
 
 struct dpif_ipfix *
@@ -464,17 +478,35 @@ dpif_ipfix_create(void)
     di = xzalloc(sizeof *di);
     dpif_ipfix_exporter_clear(&di->bridge_exporter.exporter);
     hmap_init(&di->flow_exporter_map);
+    atomic_init(&di->ref_cnt, 1);
+    return di;
+}
+
+struct dpif_ipfix *
+dpif_ipfix_ref(const struct dpif_ipfix *di_)
+{
+    struct dpif_ipfix *di = CONST_CAST(struct dpif_ipfix *, di_);
+    if (di) {
+        int orig;
+        atomic_add(&di->ref_cnt, 1, &orig);
+        ovs_assert(orig > 0);
+    }
     return di;
 }
 
 uint32_t
 dpif_ipfix_get_bridge_exporter_probability(const struct dpif_ipfix *di)
+    OVS_EXCLUDED(mutex)
 {
-    return di->bridge_exporter.probability;
+    uint32_t ret;
+    ovs_mutex_lock(&mutex);
+    ret = di->bridge_exporter.probability;
+    ovs_mutex_unlock(&mutex);
+    return ret;
 }
 
 static void
-dpif_ipfix_clear(struct dpif_ipfix *di)
+dpif_ipfix_clear(struct dpif_ipfix *di) OVS_REQ_WRLOCK(mutex)
 {
     struct dpif_ipfix_flow_exporter_map_node *node, *next;
 
@@ -488,12 +520,22 @@ dpif_ipfix_clear(struct dpif_ipfix *di)
 }
 
 void
-dpif_ipfix_destroy(struct dpif_ipfix *di)
+dpif_ipfix_unref(struct dpif_ipfix *di) OVS_EXCLUDED(mutex)
 {
-    if (di) {
+    int orig;
+
+    if (!di) {
+        return;
+    }
+
+    atomic_sub(&di->ref_cnt, 1, &orig);
+    ovs_assert(orig > 0);
+    if (orig == 1) {
+        ovs_mutex_lock(&mutex);
         dpif_ipfix_clear(di);
         hmap_destroy(&di->flow_exporter_map);
         free(di);
+        ovs_mutex_unlock(&mutex);
     }
 }
 
@@ -814,35 +856,37 @@ dpif_ipfix_sample(struct dpif_ipfix_exporter *exporter,
 
 void
 dpif_ipfix_bridge_sample(struct dpif_ipfix *di, struct ofpbuf *packet,
-                         const struct flow *flow)
+                         const struct flow *flow) OVS_EXCLUDED(mutex)
 {
+    uint64_t packet_delta_count;
+
+    ovs_mutex_lock(&mutex);
     /* Use the sampling probability as an approximation of the number
      * of matched packets. */
-    uint64_t packet_delta_count = UINT32_MAX / di->bridge_exporter.probability;
-
+    packet_delta_count = UINT32_MAX / di->bridge_exporter.probability;
     dpif_ipfix_sample(&di->bridge_exporter.exporter, packet, flow,
                       packet_delta_count,
                       di->bridge_exporter.options->obs_domain_id,
                       di->bridge_exporter.options->obs_point_id);
+    ovs_mutex_unlock(&mutex);
 }
 
 void
 dpif_ipfix_flow_sample(struct dpif_ipfix *di, struct ofpbuf *packet,
                        const struct flow *flow, uint32_t collector_set_id,
                        uint16_t probability, uint32_t obs_domain_id,
-                       uint32_t obs_point_id)
+                       uint32_t obs_point_id) OVS_EXCLUDED(mutex)
 {
     struct dpif_ipfix_flow_exporter_map_node *node;
     /* Use the sampling probability as an approximation of the number
      * of matched packets. */
     uint64_t packet_delta_count = USHRT_MAX / probability;
 
+    ovs_mutex_lock(&mutex);
     node = dpif_ipfix_find_flow_exporter_map_node(di, collector_set_id);
-
-    if (!node) {
-        return;
+    if (node) {
+        dpif_ipfix_sample(&node->exporter.exporter, packet, flow,
+                          packet_delta_count, obs_domain_id, obs_point_id);
     }
-
-    dpif_ipfix_sample(&node->exporter.exporter, packet, flow,
-                      packet_delta_count, obs_domain_id, obs_point_id);
+    ovs_mutex_unlock(&mutex);
 }