git sha: [email protected]
Author: David Marchand <[email protected]>
One line subject: dp-packet-gso: Refactor software segmentation code.
This commit refactors the software segmentation code in dp_packet_gso() by
extracting two helper functions and optimizing the first segment handling.
The refactoring changes dp_packet_gso() from returning bool to void and
removes the netdev_soft_seg_drops counter.
> diff --git a/lib/dp-packet-gso.c b/lib/dp-packet-gso.c
> index fe7186ddf4..6b8da8b370 100644
> --- a/lib/dp-packet-gso.c
> +++ b/lib/dp-packet-gso.c
> @@ -29,19 +29,19 @@ VLOG_DEFINE_THIS_MODULE(dp_packet_gso);
> *
> * The new packet is initialized with 'hdr_len' bytes from the
> * start of packet 'p' and then appended with 'data_len' bytes
> - * from the 'data' buffer.
> + * from the packet 'p' at offset 'data_off'.
> *
> * Note: The packet headers are not updated. */
> static struct dp_packet *
> dp_packet_gso_seg_new(const struct dp_packet *p, size_t hdr_len,
> - const char *data, size_t data_len)
> + size_t data_off, size_t data_len)
> {
> struct dp_packet *seg = dp_packet_new_with_headroom(hdr_len + data_len,
>
> dp_packet_headroom(p));
>
> /* Append the original packet headers and then the payload. */
> dp_packet_put(seg, dp_packet_data(p), hdr_len);
> - dp_packet_put(seg, data, data_len);
> + dp_packet_put(seg, (char *) dp_packet_data(p) + data_off, data_len);
[ ... ]
> -/* Perform software segmentation on packet 'p'.
> - *
> - * Segments packet 'p' into the array of preallocated batches in 'batches',
> - * updating the 'batches' pointer as needed and returns true.
> - *
> - * Returns false if the packet cannot be segmented. */
> -bool
> +/* Perform software segmentation on packet 'p'.
> + *
> + * Segments packet 'p' into the array of preallocated batches in 'batches',
> + * updating the 'batches' pointer as needed. */
> +void
> dp_packet_gso(struct dp_packet *p, struct dp_packet_batch **batches)
> {
> - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> struct dp_packet_batch *curr_batch = *batches;
> [ ... ]
> struct dp_packet *seg;
> uint16_t tso_segsz;
> - uint16_t inner_ip_id = 0;
> - uint16_t outer_ip_id = 0;
> - struct dp_packet *seg;
> - uint16_t tcp_offset;
> - uint32_t tcp_seq;
> - bool outer_ipv4;
> - int hdr_len;
> - int seg_len;
> - bool udp_tnl = dp_packet_tunnel_vxlan(p)
> - || dp_packet_tunnel_geneve(p);
> - bool gre_tnl = dp_packet_tunnel_gre(p);
> + size_t data_len;
> + size_t hdr_len;
> + int n_segs;
>
> tso_segsz = dp_packet_get_tso_segsz(p);
> - if (!tso_segsz) {
> - VLOG_WARN_RL(&rl, "GSO packet with len %d with no segment size.",
> - dp_packet_size(p));
> - return false;
> - }
> + ovs_assert(tso_segsz);
Does this code risk hitting the assert when processing legitimate packets?
The original function returned false for packets without tso_segsz, but now
it would abort. Are there any conditions where packets might reach this
function without tso_segsz being set?
> + n_segs = dp_packet_gso_nr_segs(p);
> +
> + /* Put back the first segment in the batch, it will be trimmed after
> + * all segments have been copied. */
> + if (dp_packet_batch_is_full(curr_batch)) {
> + curr_batch++;
> + }
> + dp_packet_batch_add(curr_batch, p);
[ ... ]
> + for (int i = 1; i < n_segs - 1; i++) {
> + seg = dp_packet_gso_seg_new(p, hdr_len, hdr_len + i * tso_segsz,
> + tso_segsz);
Does this arithmetic for the data_off parameter handle edge cases correctly?
When i=1, data_off becomes hdr_len + tso_segsz, which should point to the
second segment's payload start.
> + dp_packet_gso_update_segment(seg, i, n_segs, tso_segsz);
> +
> + if (dp_packet_batch_is_full(curr_batch)) {
> + curr_batch++;
> + }
> + dp_packet_batch_add(curr_batch, seg);
> + }
> +
> + /* Create the last segment. */
> + seg = dp_packet_gso_seg_new(p, hdr_len, hdr_len + (n_segs - 1) *
> tso_segsz,
> + data_len - (n_segs - 1) * tso_segsz);
Can this arithmetic for the last segment's data_len result in negative
values or underflow? If data_len is smaller than (n_segs - 1) * tso_segsz,
the subtraction could wrap around since data_len is size_t.
> + dp_packet_gso_update_segment(seg, n_segs - 1, n_segs, tso_segsz);
> +
> + if (dp_packet_batch_is_full(curr_batch)) {
> + curr_batch++;
> + }
> + dp_packet_batch_add(curr_batch, seg);
> +
> + /* Trim the first segment and reset TSO. */
> + dp_packet_set_size(p, hdr_len + tso_segsz);
> + dp_packet_set_tso_segsz(p, 0);
> + dp_packet_gso_update_segment(p, 0, n_segs, tso_segsz);
[ ... ]
> diff --git a/lib/netdev.c b/lib/netdev.c
> index 6a05e9a7e5..f493dab005 100644
> --- a/lib/netdev.c
> +++ b/lib/netdev.c
> @@ -71,7 +71,6 @@ COVERAGE_DEFINE(netdev_add_router);
> COVERAGE_DEFINE(netdev_get_stats);
> COVERAGE_DEFINE(netdev_push_header_drops);
> COVERAGE_DEFINE(netdev_soft_seg_good);
> -COVERAGE_DEFINE(netdev_soft_seg_drops);
[ ... ]
> @@ -843,17 +842,12 @@ netdev_send_tso(struct netdev *netdev, int qid,
> curr_batch = batches;
> DP_PACKET_BATCH_REFILL_FOR_EACH (k, size, packet, batch) {
> if (dp_packet_get_tso_segsz(packet)) {
> - if (dp_packet_gso(packet, &curr_batch)) {
> - COVERAGE_INC(netdev_soft_seg_good);
> - } else {
> - COVERAGE_INC(netdev_soft_seg_drops);
> - }
> - dp_packet_delete(packet);
> + dp_packet_gso(packet, &curr_batch);
> + COVERAGE_INC(netdev_soft_seg_good);
Does this code leak the original packet? The previous version called
dp_packet_delete(packet) after dp_packet_gso(), but now the packet p is
added to curr_batch and reused as the first segment. Is this intentional
behavior change properly handled?
> } else {
> if (dp_packet_batch_is_full(curr_batch)) {
> curr_batch++;
> }
> -
> dp_packet_batch_add(curr_batch, packet);
> }
> }
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev