It's easier to make up stats replies if you don't have to accurately predict the number of bytes that will be necessary.
This immediately allows us to rewrite ofputil_append_flow_stats_reply() without using NXM_MAX_LEN, which in turn allows us to deleted NXM_MAX_LEN entirely, which is nice since keeping it up-to-date is error-prone. An upcoming commit will introduce a second user. Signed-off-by: Ben Pfaff <b...@nicira.com> --- lib/nx-match.h | 40 ---------------------------------------- lib/ofp-util.c | 45 +++++++++++++++++++++++++++++++++++++-------- lib/ofp-util.h | 1 + 3 files changed, 38 insertions(+), 48 deletions(-) diff --git a/lib/nx-match.h b/lib/nx-match.h index c814275..2b34804 100644 --- a/lib/nx-match.h +++ b/lib/nx-match.h @@ -90,46 +90,6 @@ void nxm_decode(struct mf_subfield *, ovs_be32 header, ovs_be16 ofs_nbits); void nxm_decode_discrete(struct mf_subfield *, ovs_be32 header, ovs_be16 ofs, ovs_be16 n_bits); -BUILD_ASSERT_DECL(FLOW_WC_SEQ == 12); -/* Upper bound on the length of an nx_match. The longest nx_match (an - * IPV6 neighbor discovery message using all the registers) would be: - * - * header value mask total - * ------ ----- ---- ----- - * NXM_OF_IN_PORT 4 2 -- 6 - * NXM_OF_ETH_DST_W 4 6 6 16 - * NXM_OF_ETH_SRC_W 4 6 6 16 - * NXM_OF_ETH_TYPE 4 2 -- 6 - * NXM_OF_VLAN_TCI 4 2 2 8 - * NXM_OF_IP_TOS 4 1 -- 5 - * NXM_NX_IP_ECN 4 1 -- 5 - * NXM_OF_IP_TTL 4 1 -- 5 - * NXM_NX_IP_FRAG 4 1 1 8 - * NXM_OF_IP_PROTO 4 2 -- 6 - * NXM_OF_IPV6_SRC_W 4 16 16 36 - * NXM_OF_IPV6_DST_W 4 16 16 36 - * NXM_OF_IPV6_LABEL 4 4 -- 8 - * NXM_OF_ICMP_TYPE 4 1 -- 5 - * NXM_OF_ICMP_CODE 4 1 -- 5 - * NXM_NX_ND_TARGET 4 16 16 36 - * NXM_NX_ND_SLL 4 6 -- 10 - * NXM_NX_REG_W(0) 4 4 4 12 - * NXM_NX_REG_W(1) 4 4 4 12 - * NXM_NX_REG_W(2) 4 4 4 12 - * NXM_NX_REG_W(3) 4 4 4 12 - * NXM_NX_REG_W(4) 4 4 4 12 - * NXM_NX_REG_W(5) 4 4 4 12 - * NXM_NX_REG_W(6) 4 4 4 12 - * NXM_NX_REG_W(7) 4 4 4 12 - * NXM_NX_TUN_ID_W 4 8 8 20 - * OXM_OF_METADATA 4 8 8 20 - * ------------------------------------------- - * total 353 - * - * So this value is conservative. - */ -#define NXM_MAX_LEN 400 - /* This is my guess at the length of a "typical" nx_match, for use in * predicting space requirements. */ #define NXM_TYPICAL_LEN 64 diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 60d0cfd..2c1dd47 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -2146,14 +2146,18 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, memcpy(ofs->actions, fs->actions, act_len); } else if (osm->type == htons(OFPST_VENDOR)) { struct nx_flow_stats *nfs; + unsigned int match_len; struct ofpbuf *msg; - size_t start_len; + size_t start_ofs; - msg = ofputil_reserve_stats_reply( - sizeof *nfs + NXM_MAX_LEN + act_len, replies); - start_len = msg->size; + msg = ofpbuf_from_list(list_back(replies)); + start_ofs = msg->size; - nfs = ofpbuf_put_uninit(msg, sizeof *nfs); + ofpbuf_put_uninit(msg, sizeof *nfs); + match_len = nx_put_match(msg, false, &fs->rule, 0, 0); + ofpbuf_put(msg, fs->actions, act_len); + + nfs = ofpbuf_at_assert(msg, start_ofs, sizeof *nfs); nfs->table_id = fs->table_id; nfs->pad = 0; nfs->duration_sec = htonl(fs->duration_sec); @@ -2167,12 +2171,13 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, nfs->hard_age = htons(fs->hard_age < 0 ? 0 : fs->hard_age < UINT16_MAX ? fs->hard_age + 1 : UINT16_MAX); - nfs->match_len = htons(nx_put_match(msg, false, &fs->rule, 0, 0)); + nfs->match_len = htons(match_len); nfs->cookie = fs->cookie; nfs->packet_count = htonll(fs->packet_count); nfs->byte_count = htonll(fs->byte_count); - ofpbuf_put(msg, fs->actions, act_len); - nfs->length = htons(msg->size - start_len); + nfs->length = htons(msg->size - start_ofs); + + ofputil_postappend_stats_reply(start_ofs, replies); } else { NOT_REACHED(); } @@ -3362,6 +3367,30 @@ ofputil_append_stats_reply(size_t len, struct list *replies) return ofpbuf_put_uninit(ofputil_reserve_stats_reply(len, replies), len); } +/* Sometimes, when composing stats replies, it's difficult to predict how long + * an individual reply chunk will be before actually encoding it into the reply + * buffer. This function allows easy handling of this case: just encode the + * reply, then use this function to break the message into two pieces if it + * exceeds the OpenFlow message limit. + * + * In detail, if the final stats message in 'replies' is too long for OpenFlow, + * this function breaks it into two separate stats replies, the first one with + * the first 'start_ofs' bytes, the second one containing the bytes from that + * offset onward. */ +void +ofputil_postappend_stats_reply(size_t start_ofs, struct list *replies) +{ + struct ofpbuf *msg = ofpbuf_from_list(list_back(replies)); + + assert(start_ofs <= UINT16_MAX); + if (msg->size > UINT16_MAX) { + size_t len = msg->size - start_ofs; + memcpy(ofputil_append_stats_reply(len, replies), + (const uint8_t *) msg->data + start_ofs, len); + msg->size = start_ofs; + } +} + /* Returns the first byte past the ofp_stats_msg header in 'oh'. */ const void * ofputil_stats_body(const struct ofp_header *oh) diff --git a/lib/ofp-util.h b/lib/ofp-util.h index aaab83c..b177c9e 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -531,6 +531,7 @@ void ofputil_start_stats_reply(const struct ofp_stats_msg *request, struct list *); struct ofpbuf *ofputil_reserve_stats_reply(size_t len, struct list *); void *ofputil_append_stats_reply(size_t len, struct list *); +void ofputil_postappend_stats_reply(size_t start_ofs, struct list *); void ofputil_append_port_desc_stats_reply(uint8_t ofp_version, const struct ofputil_phy_port *pp, -- 1.7.2.5 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev