This commit adds support for tracking the number of packets and bytes sent through a mirror. The numbers are kept in the new "statistics" column on the mirror table in the "tx_packets" and "tx_bytes" keys. --- NEWS | 2 + ofproto/ofproto-dpif.c | 97 +++++++++++++++++++++++++++++++++++-------- ofproto/ofproto-provider.h | 9 ++++ ofproto/ofproto.c | 17 ++++++++ ofproto/ofproto.h | 2 + vswitchd/bridge.c | 35 ++++++++++++++- vswitchd/vswitch.ovsschema | 6 ++- vswitchd/vswitch.xml | 12 +++++ 8 files changed, 158 insertions(+), 22 deletions(-)
diff --git a/NEWS b/NEWS index 3b0e9ac..aec514d 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,8 @@ post-v1.3.0 - Added ability to match on TTL in IPv4 and IPv6 through NXM. - Added ability to modify ECN bits in IPv4. - Added ability to modify TTL in IPv4. + - ovs-vswitchd: + - Track packet and byte statistics sent on mirrors. - ovs-appctl: - New "fdb/flush" command to flush bridge's MAC learning table. - ovs-test: diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 650cb1f..ab65fd5 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -125,9 +125,16 @@ struct ofmirror { struct ofbundle *out; /* Output port or NULL. */ int out_vlan; /* Output VLAN or -1. */ mirror_mask_t dup_mirrors; /* Bitmap of mirrors with the same output. */ + + /* Counters. */ + int64_t packet_count; /* Number of packets sent. */ + int64_t byte_count; /* Number of bytes sent. */ }; static void mirror_destroy(struct ofmirror *); +static void update_mirror_stats(struct ofproto_dpif *ofproto, + mirror_mask_t mirrors, + uint64_t packets, uint64_t bytes); /* A group of one or more OpenFlow ports. */ #define OFBUNDLE_FLOOD ((struct ofbundle *) 1) @@ -204,6 +211,7 @@ struct action_xlate_ctx { bool has_learn; /* Actions include NXAST_LEARN? */ bool has_normal; /* Actions output to OFPP_NORMAL? */ uint16_t nf_output_iface; /* Output interface index for NetFlow. */ + mirror_mask_t mirrors; /* Bitmap of associated mirrors. */ /* xlate_actions() initializes and uses these members, but the client has no * reason to look at them. */ @@ -243,9 +251,9 @@ struct facet { uint64_t dp_packet_count; /* Last known packet count in the datapath. */ uint64_t dp_byte_count; /* Last known byte count in the datapath. */ - uint64_t rs_packet_count; /* Packets pushed to resubmit children. */ - uint64_t rs_byte_count; /* Bytes pushed to resubmit children. */ - long long int rs_used; /* Used time pushed to resubmit children. */ + uint64_t prev_packet_count; /* Number of packets from last stats push. */ + uint64_t prev_byte_count; /* Number of bytes from last stats push. */ + long long int prev_used; /* Used time from last stats push. */ uint64_t accounted_bytes; /* Bytes processed by facet_account(). */ @@ -262,6 +270,7 @@ struct facet { struct nlattr *actions; /* Datapath actions. */ tag_type tags; /* Tags. */ struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */ + mirror_mask_t mirrors; /* Bitmap of associated mirrors. */ }; static struct facet *facet_create(struct rule_dpif *, const struct flow *); @@ -1928,6 +1937,22 @@ mirror_destroy(struct ofmirror *mirror) mirror_update_dups(ofproto); } +static void +mirror_get_stats(struct ofproto *ofproto_, void *aux, + int64_t *packets, int64_t *bytes) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + struct ofmirror *mirror = mirror_lookup(ofproto, aux); + + if (!mirror) { + *packets = *bytes = 0; + return; + } + + *packets = mirror->packet_count; + *bytes = mirror->byte_count; +} + static int set_flood_vlans(struct ofproto *ofproto_, unsigned long *flood_vlans) { @@ -2920,6 +2945,7 @@ facet_make_actions(struct ofproto_dpif *p, struct facet *facet, facet->has_learn = ctx.has_learn; facet->has_normal = ctx.has_normal; facet->nf_flow.output_iface = ctx.nf_output_iface; + facet->mirrors = ctx.mirrors; if (facet->actions_len != odp_actions->size || memcmp(facet->actions, odp_actions->data, odp_actions->size)) { @@ -3235,6 +3261,7 @@ facet_revalidate(struct ofproto_dpif *ofproto, struct facet *facet) facet->may_install = ctx.may_set_up_flow; facet->has_learn = ctx.has_learn; facet->has_normal = ctx.has_normal; + facet->mirrors = ctx.mirrors; if (actions_changed) { free(facet->actions); facet->actions_len = odp_actions->size; @@ -3246,7 +3273,7 @@ facet_revalidate(struct ofproto_dpif *ofproto, struct facet *facet) list_push_back(&new_rule->facets, &facet->list_node); facet->rule = new_rule; facet->used = new_rule->up.created; - facet->rs_used = facet->used; + facet->prev_used = facet->used; } ofpbuf_delete(odp_actions); @@ -3293,30 +3320,33 @@ facet_reset_counters(struct facet *facet) { facet->packet_count = 0; facet->byte_count = 0; - facet->rs_packet_count = 0; - facet->rs_byte_count = 0; + facet->prev_packet_count = 0; + facet->prev_byte_count = 0; facet->accounted_bytes = 0; } static void facet_push_stats(struct facet *facet) { - uint64_t rs_packets, rs_bytes; + uint64_t new_packets, new_bytes; - assert(facet->packet_count >= facet->rs_packet_count); - assert(facet->byte_count >= facet->rs_byte_count); - assert(facet->used >= facet->rs_used); + assert(facet->packet_count >= facet->prev_packet_count); + assert(facet->byte_count >= facet->prev_byte_count); + assert(facet->used >= facet->prev_used); - rs_packets = facet->packet_count - facet->rs_packet_count; - rs_bytes = facet->byte_count - facet->rs_byte_count; + new_packets = facet->packet_count - facet->prev_packet_count; + new_bytes = facet->byte_count - facet->prev_byte_count; - if (rs_packets || rs_bytes || facet->used > facet->rs_used) { - facet->rs_packet_count = facet->packet_count; - facet->rs_byte_count = facet->byte_count; - facet->rs_used = facet->used; + if (new_packets || new_bytes || facet->used > facet->prev_used) { + facet->prev_packet_count = facet->packet_count; + facet->prev_byte_count = facet->byte_count; + facet->prev_used = facet->used; flow_push_stats(facet->rule, &facet->flow, - rs_packets, rs_bytes, facet->used); + new_packets, new_bytes, facet->used); + + update_mirror_stats(ofproto_dpif_cast(facet->rule->up.ofproto), + facet->mirrors, new_packets, new_bytes); } } @@ -3340,7 +3370,7 @@ push_resubmit(struct action_xlate_ctx *ctx, struct rule_dpif *rule) } /* Pushes flow statistics to the rules which 'flow' resubmits into given - * 'rule''s actions. */ + * 'rule''s actions and mirrors. */ static void flow_push_stats(const struct rule_dpif *rule, struct flow *flow, uint64_t packets, uint64_t bytes, @@ -3895,6 +3925,7 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port, ctx->sflow_n_outputs++; ctx->nf_output_iface = ofp_port; ctx->flow.nw_tos = flow_nw_tos; + ctx->mirrors = ofport->bundle->mirror_out; } static void @@ -4425,6 +4456,7 @@ xlate_actions(struct action_xlate_ctx *ctx, ctx->has_learn = false; ctx->has_normal = false; ctx->nf_output_iface = NF_OUT_DROP; + ctx->mirrors = 0; ctx->recurse = 0; ctx->original_priority = ctx->flow.priority; ctx->base_flow = ctx->flow; @@ -4760,6 +4792,34 @@ output_mirrors(struct action_xlate_ctx *ctx, } } +static void +update_mirror_stats(struct ofproto_dpif *ofproto, mirror_mask_t mirrors, + uint64_t packets, uint64_t bytes) +{ + if (!mirrors) { + return; + } + + for (; mirrors; mirrors &= mirrors - 1) { + struct ofmirror *m; + + m = ofproto->mirrors[mirror_mask_ffs(mirrors) - 1]; + + if (!m) { + /* In normal circumstances 'm' will not be NULL. However, + * if mirrors are reconfigured, we can temporarily get out + * of sync in facet_revalidate(). We could "correct" the + * mirror list before reaching here, but doing that would + * not properly account the traffic stats we've currently + * accumulated for previous mirror configuration. */ + continue; + } + + m->packet_count += packets; + m->byte_count += bytes; + } +} + /* A VM broadcasts a gratuitous ARP to indicate that it has resumed after * migration. Older Citrix-patched Linux DomU used gratuitous ARP replies to * indicate this; newer upstream kernels use gratuitous ARP requests. */ @@ -5485,6 +5545,7 @@ const struct ofproto_class ofproto_dpif_class = { bundle_set, bundle_remove, mirror_set, + mirror_get_stats, set_flood_vlans, is_mirror_output_bundle, forward_bpdu_changed, diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h index d303632..34d18f2 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -1018,6 +1018,15 @@ struct ofproto_class { int (*mirror_set)(struct ofproto *ofproto, void *aux, const struct ofproto_mirror_settings *s); + /* Retrieves statistics from mirror associated with client data pointer + * 'aux' in 'ofproto'. Stores packet and byte counts in 'packets' and + * 'bytes', respectively. + * + * EOPNOTSUPP as a return value indicates that this ofproto_class does not + * support STP. */ + void (*mirror_get_stats)(struct ofproto *ofproto, void *aux, + int64_t *packets, int64_t *bytes); + /* Configures the VLANs whose bits are set to 1 in 'flood_vlans' as VLANs * on which all packets are flooded, instead of using MAC learning. If * 'flood_vlans' is NULL, then MAC learning applies to all VLANs. diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index af02a0e..1f3a077 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -742,6 +742,23 @@ ofproto_mirror_unregister(struct ofproto *ofproto, void *aux) return ofproto_mirror_register(ofproto, aux, NULL); } +/* Retrieves statistics from mirror associated with client data pointer + * 'aux' in 'ofproto'. Stores packet and byte counts in 'packets' and + * 'bytes', respectively. */ +int +ofproto_mirror_get_stats(struct ofproto *ofproto, void *aux, + int64_t *packets, int64_t *bytes) +{ + if (!ofproto->ofproto_class->mirror_get_stats) { + *packets = *bytes = 0; + return EOPNOTSUPP; + } + + ofproto->ofproto_class->mirror_get_stats(ofproto, aux, + packets, bytes); + return 0; +} + /* Configures the VLANs whose bits are set to 1 in 'flood_vlans' as VLANs on * which all packets are flooded, instead of using MAC learning. If * 'flood_vlans' is NULL, then MAC learning applies to all VLANs. diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h index 74b3dec..6d4a42c 100644 --- a/ofproto/ofproto.h +++ b/ofproto/ofproto.h @@ -296,6 +296,8 @@ struct ofproto_mirror_settings { int ofproto_mirror_register(struct ofproto *, void *aux, const struct ofproto_mirror_settings *); int ofproto_mirror_unregister(struct ofproto *, void *aux); +int ofproto_mirror_get_stats(struct ofproto *, void *aux, + int64_t *packets, int64_t *bytes); int ofproto_set_flood_vlans(struct ofproto *, unsigned long *flood_vlans); bool ofproto_is_mirror_output_bundle(const struct ofproto *, void *aux); diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 3aac5ce..a00709d 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -77,6 +77,7 @@ struct mirror { struct hmap_node hmap_node; /* In struct bridge's "mirrors" hmap. */ struct bridge *bridge; char *name; + const struct ovsrec_mirror *cfg; }; struct port { @@ -190,7 +191,8 @@ static void bridge_configure_mirrors(struct bridge *); static struct mirror *mirror_create(struct bridge *, const struct ovsrec_mirror *); static void mirror_destroy(struct mirror *); -static bool mirror_configure(struct mirror *, const struct ovsrec_mirror *); +static bool mirror_configure(struct mirror *); +static void mirror_refresh_stats(struct mirror *); static void iface_configure_lacp(struct iface *, struct lacp_slave_settings *); static struct iface *iface_create(struct port *port, @@ -268,6 +270,7 @@ bridge_init(const char *remote) ovsdb_idl_omit(idl, &ovsrec_queue_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_mirror_col_external_ids); + ovsdb_idl_omit_alert(idl, &ovsrec_mirror_col_statistics); ovsdb_idl_omit(idl, &ovsrec_netflow_col_external_ids); @@ -1843,6 +1846,7 @@ bridge_run(void) txn = ovsdb_idl_txn_create(idl); HMAP_FOR_EACH (br, node, &all_bridges) { struct port *port; + struct mirror *m; HMAP_FOR_EACH (port, hmap_node, &br->ports) { struct iface *iface; @@ -1852,6 +1856,11 @@ bridge_run(void) iface_refresh_status(iface); } } + + HMAP_FOR_EACH (m, hmap_node, &br->mirrors) { + mirror_refresh_stats(m); + } + } refresh_system_stats(cfg); refresh_controller_status(); @@ -3114,7 +3123,7 @@ bridge_configure_mirrors(struct bridge *br) if (!m) { m = mirror_create(br, cfg); } - if (!mirror_configure(m, cfg)) { + if (!mirror_configure(m)) { mirror_destroy(m); } } @@ -3136,6 +3145,7 @@ mirror_create(struct bridge *br, const struct ovsrec_mirror *cfg) hmap_insert(&br->mirrors, &m->hmap_node, uuid_hash(&m->uuid)); m->bridge = br; m->name = xstrdup(cfg->name); + m->cfg = cfg; return m; } @@ -3180,8 +3190,9 @@ mirror_collect_ports(struct mirror *m, } static bool -mirror_configure(struct mirror *m, const struct ovsrec_mirror *cfg) +mirror_configure(struct mirror *m) { + const struct ovsrec_mirror *cfg = m->cfg; struct ofproto_mirror_settings s; /* Set name. */ @@ -3257,3 +3268,21 @@ mirror_configure(struct mirror *m, const struct ovsrec_mirror *cfg) return true; } + +static void +mirror_refresh_stats(struct mirror *m) +{ + struct ofproto *ofproto = m->bridge->ofproto; + char *keys[2]; + int64_t values[2]; + + if (ofproto_mirror_get_stats(ofproto, m, &values[0], &values[1])) { + ovsrec_mirror_set_statistics(m->cfg, NULL, NULL, 0); + return; + } + + keys[0] = "tx_packets"; + keys[1] = "tx_bytes"; + + ovsrec_mirror_set_statistics(m->cfg, keys, values, ARRAY_SIZE(values)); +} diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema index e2f231c..9d91b0f 100644 --- a/vswitchd/vswitch.ovsschema +++ b/vswitchd/vswitch.ovsschema @@ -1,6 +1,6 @@ {"name": "Open_vSwitch", "version": "6.4.0", - "cksum": "3757343995 15531", + "cksum": "923041702 15687", "tables": { "Open_vSwitch": { "columns": { @@ -299,6 +299,10 @@ "minInteger": 1, "maxInteger": 4095}, "min": 0, "max": 1}}, + "statistics": { + "type": {"key": "string", "value": "integer", + "min": 0, "max": "unlimited"}, + "ephemeral": true}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}}, diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index 770e10a..397a8c7 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -2058,6 +2058,18 @@ </column> </group> + <group title="Statistics: Mirror counters"> + <p> + Key-value pairs that report mirror statistics. + </p> + <column name="statistics" key="tx_packets"> + Number of packets transmitted through this mirror. + </column> + <column name="statistics" key="tx_bytes"> + Number of bytes transmitted through this mirror. + </column> + </group> + <group title="Common Columns"> The overall purpose of these columns is described under <code>Common Columns</code> at the beginning of this document. -- 1.7.4.1 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev