From: Victor Julien Date: Fri, 16 Jan 2015 10:53:29 +0000 (+0100) Subject: tcp: zero copy fast path in app-layer reassembly X-Git-Tag: suricata-2.1beta4~224 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=2bba5eb7044504044b9a0e913e95fa4f542e49e4;p=thirdparty%2Fsuricata.git tcp: zero copy fast path in app-layer reassembly Create 2 'fast paths' for app layer reassembly. Both are about reducing copying. In the cases described below, we pass the segment's data directly to the app layer API, instead of first copying it into a buffer than we then pass. This safes a copy. The first is for the case when we have just one single segment that was just ack'd. As we know that we won't use any other segment this round, we can just use the segment data. The second case is more aggressive. When the segment meets a certain size limit (currently hardcoded at 128 bytes), we pass it to the app-layer API directly. Thus invoking the app-layer somewhat more often to safe some copies. --- diff --git a/src/stream-tcp-reassemble.c b/src/stream-tcp-reassemble.c index bd5c9029fc..b61748dd10 100644 --- a/src/stream-tcp-reassemble.c +++ b/src/stream-tcp-reassemble.c @@ -2572,6 +2572,42 @@ static inline int DoReassemble(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, TcpSession *ssn, TcpStream *stream, TcpSegment *seg, ReassembleData *rd, Packet *p) { + /* fast path 1: segment is exactly what we need */ + if (likely(rd->data_len == 0 && SEQ_EQ(seg->seq, rd->ra_base_seq+1) && SEQ_EQ(stream->last_ack, (seg->seq + seg->payload_len)))) { + /* process single segment directly */ + AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream, + seg->payload, seg->payload_len, + StreamGetAppLayerFlags(ssn, stream, p)); + AppLayerProfilingStore(ra_ctx->app_tctx, p); + rd->data_sent += seg->payload_len; + rd->ra_base_seq += seg->payload_len; + + /* if after the first data chunk we have no alproto yet, + * there is no point in continueing here. */ + if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) { + SCLogDebug("no alproto after first data chunk"); + return 0; + } + return 1; + /* fast path 2: segment acked completely, meets minimal size req for 0copy processing */ + } else if (rd->data_len == 0 && SEQ_EQ(seg->seq, rd->ra_base_seq+1) && SEQ_GT(stream->last_ack, (seg->seq + seg->payload_len)) && seg->payload_len >= 128) { + /* process single segment directly */ + AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream, + seg->payload, seg->payload_len, + StreamGetAppLayerFlags(ssn, stream, p)); + AppLayerProfilingStore(ra_ctx->app_tctx, p); + rd->data_sent += seg->payload_len; + rd->ra_base_seq += seg->payload_len; + + /* if after the first data chunk we have no alproto yet, + * there is no point in continueing here. */ + if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) { + SCLogDebug("no alproto after first data chunk"); + return 0; + } + return 1; + } + uint16_t payload_offset = 0; uint16_t payload_len = 0;