suricata
tmqh-packetpool.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2022 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Victor Julien <victor@inliniac.net>
22  *
23  * Packetpool queue handlers. Packet pool is implemented as a stack.
24  */
25 
26 #include "suricata-common.h"
27 #include "tmqh-packetpool.h"
28 #include "tm-queuehandlers.h"
29 #include "tm-threads.h"
30 #include "threads.h"
31 #include "decode.h"
32 #include "tm-modules.h"
33 #include "packet.h"
34 #include "util-profiling.h"
35 #include "util-validate.h"
36 #include "action-globals.h"
37 
38 extern uint16_t max_pending_packets;
39 
40 /* Number of freed packet to save for one pool before freeing them. */
41 #define MAX_PENDING_RETURN_PACKETS 32
42 static uint32_t max_pending_return_packets = MAX_PENDING_RETURN_PACKETS;
43 
44 thread_local PktPool thread_pkt_pool;
45 
46 static inline PktPool *GetThreadPacketPool(void)
47 {
48  return &thread_pkt_pool;
49 }
50 
51 /**
52  * \brief TmqhPacketpoolRegister
53  * \initonly
54  */
56 {
57  tmqh_table[TMQH_PACKETPOOL].name = "packetpool";
60 }
61 
62 static int PacketPoolIsEmpty(PktPool *pool)
63 {
64  /* Check local stack first. */
65  if (pool->head || pool->return_stack.head)
66  return 0;
67 
68  return 1;
69 }
70 
71 static void UpdateReturnThreshold(PktPool *pool)
72 {
73  const float perc = (float)pool->cnt / (float)max_pending_packets;
74  uint32_t threshold = (uint32_t)(perc * (float)max_pending_return_packets);
75  if (threshold != SC_ATOMIC_GET(pool->return_stack.return_threshold)) {
76  SC_ATOMIC_SET(pool->return_stack.return_threshold, threshold);
77  }
78 }
79 
80 void PacketPoolWait(void)
81 {
82  PktPool *my_pool = GetThreadPacketPool();
83 
84  if (PacketPoolIsEmpty(my_pool)) {
85  SC_ATOMIC_SET(my_pool->return_stack.return_threshold, 1);
86 
87  SCMutexLock(&my_pool->return_stack.mutex);
88  SCCondWait(&my_pool->return_stack.cond, &my_pool->return_stack.mutex);
89  SCMutexUnlock(&my_pool->return_stack.mutex);
90 
91  UpdateReturnThreshold(my_pool);
92  }
93 
94  while(PacketPoolIsEmpty(my_pool))
95  cc_barrier();
96 }
97 
98 /** \brief a initialized packet
99  *
100  * \warning Use *only* at init, not at packet runtime
101  */
102 static void PacketPoolStorePacket(Packet *p)
103 {
104  p->pool = GetThreadPacketPool();
107 }
108 
109 static void PacketPoolGetReturnedPackets(PktPool *pool)
110 {
111  SCMutexLock(&pool->return_stack.mutex);
112  /* Move all the packets from the locked return stack to the local stack. */
113  pool->head = pool->return_stack.head;
114  pool->return_stack.head = NULL;
115  pool->cnt += pool->return_stack.cnt;
116  pool->return_stack.cnt = 0;
117  SCMutexUnlock(&pool->return_stack.mutex);
118 }
119 
120 /** \brief Get a new packet from the packet pool
121  *
122  * Only allocates from the thread's local stack, or mallocs new packets.
123  * If the local stack is empty, first move all the return stack packets to
124  * the local stack.
125  * \retval Packet pointer, or NULL on failure.
126  */
128 {
129  PktPool *pool = GetThreadPacketPool();
130 #ifdef DEBUG_VALIDATION
131  BUG_ON(pool->initialized == 0);
132  BUG_ON(pool->destroyed == 1);
133 #endif /* DEBUG_VALIDATION */
134  if (pool->head) {
135  /* Stack is not empty. */
136  Packet *p = pool->head;
137  pool->head = p->next;
138  pool->cnt--;
139  p->pool = pool;
140  PacketReinit(p);
141 
142  UpdateReturnThreshold(pool);
143  SCLogDebug("pp: %0.2f cnt:%u max:%d threshold:%u",
144  ((float)pool->cnt / (float)max_pending_packets) * (float)100, pool->cnt,
145  max_pending_packets, SC_ATOMIC_GET(pool->return_stack.return_threshold));
146  return p;
147  }
148 
149  /* Local Stack is empty, so check the return stack, which requires
150  * locking. */
151  PacketPoolGetReturnedPackets(pool);
152 
153  /* Try to allocate again. Need to check for not empty again, since the
154  * return stack might have been empty too.
155  */
156  if (pool->head) {
157  /* Stack is not empty. */
158  Packet *p = pool->head;
159  pool->head = p->next;
160  pool->cnt--;
161  p->pool = pool;
162  PacketReinit(p);
163 
164  UpdateReturnThreshold(pool);
165  SCLogDebug("pp: %0.2f cnt:%u max:%d threshold:%u",
166  ((float)pool->cnt / (float)max_pending_packets) * (float)100, pool->cnt,
167  max_pending_packets, SC_ATOMIC_GET(pool->return_stack.return_threshold));
168  return p;
169  }
170 
171  /* Failed to allocate a packet, so return NULL. */
172  /* Optionally, could allocate a new packet here. */
173  return NULL;
174 }
175 
176 /** \brief Return packet to Packet pool
177  *
178  */
180 {
181  PktPool *my_pool = GetThreadPacketPool();
182  PktPool *pool = p->pool;
183  if (pool == NULL) {
184  PacketFree(p);
185  return;
186  }
187 
189 
190 #ifdef DEBUG_VALIDATION
191  BUG_ON(pool->initialized == 0);
192  BUG_ON(pool->destroyed == 1);
193  BUG_ON(my_pool->initialized == 0);
194  BUG_ON(my_pool->destroyed == 1);
195 #endif /* DEBUG_VALIDATION */
196 
197  if (pool == my_pool) {
198  /* Push back onto this thread's own stack, so no locking. */
199  p->next = my_pool->head;
200  my_pool->head = p;
201  my_pool->cnt++;
202  } else {
203  PktPool *pending_pool = my_pool->pending_pool;
204  if (pending_pool == NULL || pending_pool == pool) {
205  if (pending_pool == NULL) {
206  /* No pending packet, so store the current packet. */
207  p->next = NULL;
208  my_pool->pending_pool = pool;
209  my_pool->pending_head = p;
210  my_pool->pending_tail = p;
211  my_pool->pending_count = 1;
212  } else if (pending_pool == pool) {
213  /* Another packet for the pending pool list. */
214  p->next = my_pool->pending_head;
215  my_pool->pending_head = p;
216  my_pool->pending_count++;
217  }
218 
219  const uint32_t threshold = SC_ATOMIC_GET(pool->return_stack.return_threshold);
220  if (my_pool->pending_count >= threshold) {
221  /* Return the entire list of pending packets. */
222  SCMutexLock(&pool->return_stack.mutex);
223  my_pool->pending_tail->next = pool->return_stack.head;
224  pool->return_stack.head = my_pool->pending_head;
225  pool->return_stack.cnt += my_pool->pending_count;
226  SCCondSignal(&pool->return_stack.cond);
227  SCMutexUnlock(&pool->return_stack.mutex);
228  /* Clear the list of pending packets to return. */
229  my_pool->pending_pool = NULL;
230  my_pool->pending_head = NULL;
231  my_pool->pending_tail = NULL;
232  my_pool->pending_count = 0;
233  }
234  } else {
235  /* Push onto return stack for this pool */
236  SCMutexLock(&pool->return_stack.mutex);
237  p->next = pool->return_stack.head;
238  pool->return_stack.head = p;
239  pool->return_stack.cnt++;
240  SCCondSignal(&pool->return_stack.cond);
241  SCMutexUnlock(&pool->return_stack.mutex);
242  }
243  }
244 }
245 
246 void PacketPoolInit(void)
247 {
248  PktPool *my_pool = GetThreadPacketPool();
249 
250 #ifdef DEBUG_VALIDATION
251  BUG_ON(my_pool->initialized);
252  my_pool->initialized = 1;
253  my_pool->destroyed = 0;
254 #endif /* DEBUG_VALIDATION */
255 
256  SCMutexInit(&my_pool->return_stack.mutex, NULL);
257  SCCondInit(&my_pool->return_stack.cond, NULL);
258  SC_ATOMIC_INIT(my_pool->return_stack.return_threshold);
259  SC_ATOMIC_SET(my_pool->return_stack.return_threshold, 32);
260 
261  /* pre allocate packets */
262  SCLogDebug("preallocating packets... packet size %" PRIuMAX "",
263  (uintmax_t)SIZE_OF_PACKET);
264  int i = 0;
265  for (i = 0; i < max_pending_packets; i++) {
266  Packet *p = PacketGetFromAlloc();
267  if (unlikely(p == NULL)) {
268  FatalError("Fatal error encountered while allocating a packet. Exiting...");
269  }
270  PacketPoolStorePacket(p);
271  }
272 
273  //SCLogInfo("preallocated %"PRIiMAX" packets. Total memory %"PRIuMAX"",
274  // max_pending_packets, (uintmax_t)(max_pending_packets*SIZE_OF_PACKET));
275 }
276 
278 {
279  Packet *p = NULL;
280  PktPool *my_pool = GetThreadPacketPool();
281 
282 #ifdef DEBUG_VALIDATION
283  BUG_ON(my_pool && my_pool->destroyed);
284 #endif /* DEBUG_VALIDATION */
285 
286  if (my_pool && my_pool->pending_pool != NULL) {
287  p = my_pool->pending_head;
288  while (p) {
289  Packet *next_p = p->next;
290  PacketFree(p);
291  p = next_p;
292  my_pool->pending_count--;
293  }
294 #ifdef DEBUG_VALIDATION
295  BUG_ON(my_pool->pending_count);
296 #endif /* DEBUG_VALIDATION */
297  my_pool->pending_pool = NULL;
298  my_pool->pending_head = NULL;
299  my_pool->pending_tail = NULL;
300  }
301 
302  while ((p = PacketPoolGetPacket()) != NULL) {
303  PacketFree(p);
304  }
305 
306 #ifdef DEBUG_VALIDATION
307  my_pool->initialized = 0;
308  my_pool->destroyed = 1;
309 #endif /* DEBUG_VALIDATION */
310 }
311 
313 {
314  return PacketPoolGetPacket();
315 }
316 
318 {
319  bool proot = false;
320 
321  SCEnter();
322  SCLogDebug("Packet %p, p->root %p, alloced %s", p, p->root, BOOL2STR(p->pool == NULL));
323 
324  if (PacketIsTunnel(p)) {
325  SCLogDebug("Packet %p is a tunnel packet: %s",
326  p,p->root ? "upper layer" : "tunnel root");
327 
328  /* get a lock to access root packet fields */
330  SCSpinLock(lock);
331 
332  if (PacketIsTunnelRoot(p)) {
333  SCLogDebug("IS_TUNNEL_ROOT_PKT == TRUE");
334  CaptureStatsUpdate(t, p); // TODO move out of lock
335 
336  const uint16_t outstanding = TUNNEL_PKT_TPR(p) - TUNNEL_PKT_RTV(p);
337  SCLogDebug("root pkt: outstanding %u", outstanding);
338  if (outstanding == 0) {
339  SCLogDebug("no tunnel packets outstanding, no more tunnel "
340  "packet(s) depending on this root");
341  /* if this packet is the root and there are no
342  * more tunnel packets to consider
343  *
344  * return it to the pool */
345  } else {
346  SCLogDebug("tunnel root Packet %p: outstanding > 0, so "
347  "packets are still depending on this root, setting "
348  "SET_TUNNEL_PKT_VERDICTED", p);
349  /* if this is the root and there are more tunnel
350  * packets, return this to the pool. It's still referenced
351  * by the tunnel packets, and we will return it
352  * when we handle them */
353  PacketTunnelSetVerdicted(p);
354 
357  SCReturn;
358  }
359  } else {
360  SCLogDebug("NOT IS_TUNNEL_ROOT_PKT, so tunnel pkt");
361 
363  const uint16_t outstanding = TUNNEL_PKT_TPR(p) - TUNNEL_PKT_RTV(p);
364  SCLogDebug("tunnel pkt: outstanding %u", outstanding);
365  /* all tunnel packets are processed except us. Root already
366  * processed. So return tunnel pkt and root packet to the
367  * pool. */
368  if (outstanding == 0 && p->root && PacketTunnelIsVerdicted(p->root)) {
369  SCLogDebug("root verdicted == true && no outstanding");
370 
371  /* handle freeing the root as well*/
372  SCLogDebug("setting proot = 1 for root pkt, p->root %p "
373  "(tunnel packet %p)", p->root, p);
374  proot = true;
375 
376  /* fall through */
377 
378  } else {
379  /* root not ready yet, or not the last tunnel packet,
380  * so get rid of the tunnel pkt only */
381 
382  SCLogDebug("NOT IS_TUNNEL_PKT_VERDICTED (%s) || "
383  "outstanding > 0 (%u)",
384  (p->root && PacketTunnelIsVerdicted(p->root)) ? "true" : "false",
385  outstanding);
386 
387  /* fall through */
388  }
389  }
391 
392  SCLogDebug("tunnel stuff done, move on (proot %d)", proot);
393 
394  } else {
395  CaptureStatsUpdate(t, p);
396  }
397 
398  SCLogDebug("[packet %p][%s] %s", p,
399  PacketIsTunnel(p) ? PacketIsTunnelRoot(p) ? "tunnel::root" : "tunnel::leaf"
400  : "no tunnel",
401  (p->action & ACTION_DROP) ? "DROP" : "no drop");
402 
403  /* we're done with the tunnel root now as well */
404  if (proot == true) {
405  SCLogDebug("getting rid of root pkt... alloc'd %s", BOOL2STR(p->root->pool == NULL));
406 
408  p->root->ReleasePacket(p->root);
409  p->root = NULL;
410  }
411 
413 
415  p->ReleasePacket(p);
416 
417  SCReturn;
418 }
419 
420 /**
421  * \brief Release all the packets in the queue back to the packetpool. Mainly
422  * used by threads that have failed, and wants to return the packets back
423  * to the packetpool.
424  *
425  * \param pq Pointer to the packetqueue from which the packets have to be
426  * returned back to the packetpool
427  *
428  * \warning this function assumes that the pq does not use locking
429  */
431 {
432  Packet *p = NULL;
433 
434  if (pq == NULL)
435  return;
436 
437  while ((p = PacketDequeue(pq)) != NULL) {
438  DEBUG_VALIDATE_BUG_ON(p->flow != NULL);
439  TmqhOutputPacketpool(NULL, p);
440  }
441 
442  return;
443 }
444 
445 /** number of packets to keep reserved when calculating the pending
446  * return packets count. This assumes we need at max 10 packets in one
447  * PacketPoolWaitForN call. The actual number is 9 now, so this has a
448  * bit of margin. */
449 #define RESERVED_PACKETS 10
450 
451 /**
452  * \brief Set the max_pending_return_packets value
453  *
454  * Set it to the max pending packets value, divided by the number
455  * of lister threads. Normally, in autofp these are the stream/detect/log
456  * worker threads.
457  *
458  * The max_pending_return_packets value needs to stay below the packet
459  * pool size of the 'producers' (normally pkt capture threads but also
460  * flow timeout injection ) to avoid a deadlock where all the 'workers'
461  * keep packets in their return pools, while the capture thread can't
462  * continue because its pool is empty.
463  */
465 {
466  extern uint16_t max_pending_packets;
467  uint16_t pending_packets = max_pending_packets;
468  if (pending_packets < RESERVED_PACKETS) {
469  FatalError("'max-pending-packets' setting "
470  "must be at least %d",
472  }
474  if (threads == 0)
475  return;
476 
477  uint32_t packets = (pending_packets / threads) - 1;
478  if (packets < max_pending_return_packets)
479  max_pending_return_packets = packets;
480 
481  /* make sure to have a margin in the return logic */
482  if (max_pending_return_packets >= RESERVED_PACKETS)
483  max_pending_return_packets -= RESERVED_PACKETS;
484 
485  SCLogDebug("detect threads %u, max packets %u, max_pending_return_packets %u",
486  threads, packets, max_pending_return_packets);
487 }
tm-threads.h
max_pending_packets
uint16_t max_pending_packets
Definition: suricata.c:186
PktPool_::pending_tail
Packet * pending_tail
Definition: tmqh-packetpool.h:57
PktPool_::cnt
uint32_t cnt
Definition: tmqh-packetpool.h:48
SC_ATOMIC_INIT
#define SC_ATOMIC_INIT(name)
wrapper for initializing an atomic variable.
Definition: util-atomic.h:314
Packet_::persistent
struct Packet_::@40 persistent
PacketPoolReturnPacket
void PacketPoolReturnPacket(Packet *p)
Return packet to Packet pool.
Definition: tmqh-packetpool.c:179
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
SC_ATOMIC_SET
#define SC_ATOMIC_SET(name, val)
Set the value for the atomic variable.
Definition: util-atomic.h:386
PktPool_
Definition: tmqh-packetpool.h:43
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:269
RESERVED_PACKETS
#define RESERVED_PACKETS
Definition: tmqh-packetpool.c:449
TM_FLAG_DETECT_TM
#define TM_FLAG_DETECT_TM
Definition: tm-modules.h:35
PacketQueue_
simple fifo queue for packets with mutex and cond Calling the mutex or triggering the cond is respons...
Definition: packet-queue.h:49
action-globals.h
Packet_::action
uint8_t action
Definition: decode.h:590
threads.h
Tmqh_::OutHandler
void(* OutHandler)(ThreadVars *, Packet *)
Definition: tm-queuehandlers.h:40
Packet_::pool
struct PktPool_ * pool
Definition: decode.h:651
PacketReleaseRefs
void PacketReleaseRefs(Packet *p)
Definition: packet.c:70
SCCondInit
#define SCCondInit
Definition: threads-debug.h:138
TmThreadCountThreadsByTmmFlags
uint32_t TmThreadCountThreadsByTmmFlags(uint8_t flags)
returns a count of all the threads that match the flag
Definition: tm-threads.c:2011
SCMutexLock
#define SCMutexLock(mut)
Definition: threads-debug.h:117
tm-modules.h
SCSpinLock
#define SCSpinLock
Definition: threads-debug.h:235
PktPool_::pending_count
uint32_t pending_count
Definition: tmqh-packetpool.h:58
PacketPoolPostRunmodes
void PacketPoolPostRunmodes(void)
Set the max_pending_return_packets value.
Definition: tmqh-packetpool.c:464
MAX_PENDING_RETURN_PACKETS
#define MAX_PENDING_RETURN_PACKETS
Definition: tmqh-packetpool.c:41
TmqhOutputPacketpool
void TmqhOutputPacketpool(ThreadVars *t, Packet *p)
Definition: tmqh-packetpool.c:317
tmqh-packetpool.h
thread_pkt_pool
thread_local PktPool thread_pkt_pool
Definition: tmqh-packetpool.c:44
PktPool_::pending_head
Packet * pending_head
Definition: tmqh-packetpool.h:56
TmqhReleasePacketsToPacketPool
void TmqhReleasePacketsToPacketPool(PacketQueue *pq)
Release all the packets in the queue back to the packetpool. Mainly used by threads that have failed,...
Definition: tmqh-packetpool.c:430
PacketPoolInit
void PacketPoolInit(void)
Definition: tmqh-packetpool.c:246
Tmqh_::InHandler
Packet *(* InHandler)(ThreadVars *)
Definition: tm-queuehandlers.h:38
lock
HRLOCK_TYPE lock
Definition: host.h:0
decode.h
TUNNEL_PKT_TPR
#define TUNNEL_PKT_TPR(p)
Definition: decode.h:811
PacketReinit
void PacketReinit(Packet *p)
Recycle a packet structure for reuse.
Definition: packet.c:80
SCCondWait
#define SCCondWait
Definition: threads-debug.h:141
Packet_::tunnel_lock
SCSpinlock tunnel_lock
Definition: decode.h:664
SCMutexUnlock
#define SCMutexUnlock(mut)
Definition: threads-debug.h:119
TMQH_PACKETPOOL
@ TMQH_PACKETPOOL
Definition: tm-queuehandlers.h:30
BOOL2STR
#define BOOL2STR(b)
Definition: util-debug.h:527
CaptureStatsUpdate
void CaptureStatsUpdate(ThreadVars *tv, const Packet *p)
Definition: decode.c:959
SCEnter
#define SCEnter(...)
Definition: util-debug.h:271
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:57
PacketFree
void PacketFree(Packet *p)
Return a malloced packet.
Definition: decode.c:190
SCSpinUnlock
#define SCSpinUnlock
Definition: threads-debug.h:237
TmqhInputPacketpool
Packet * TmqhInputPacketpool(ThreadVars *tv)
Definition: tmqh-packetpool.c:312
SIZE_OF_PACKET
#define SIZE_OF_PACKET
Definition: decode.h:681
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:300
util-profiling.h
PacketPoolWait
void PacketPoolWait(void)
Definition: tmqh-packetpool.c:80
SCReturn
#define SCReturn
Definition: util-debug.h:273
Packet_
Definition: decode.h:437
Tmqh_::name
const char * name
Definition: tm-queuehandlers.h:37
SCMutexInit
#define SCMutexInit(mut, mutattrs)
Definition: threads-debug.h:116
PktPool_::return_stack
PktPoolLockedStack return_stack
Definition: tmqh-packetpool.h:72
tm-queuehandlers.h
TmqhPacketpoolRegister
void TmqhPacketpoolRegister(void)
TmqhPacketpoolRegister \initonly.
Definition: tmqh-packetpool.c:55
Packet_::ReleasePacket
void(* ReleasePacket)(struct Packet_ *)
Definition: decode.h:529
SCCondSignal
#define SCCondSignal
Definition: threads-debug.h:139
Packet_::flow
struct Flow_ * flow
Definition: decode.h:476
suricata-common.h
packet.h
ACTION_DROP
#define ACTION_DROP
Definition: action-globals.h:30
FatalError
#define FatalError(...)
Definition: util-debug.h:502
tmqh_table
Tmqh tmqh_table[TMQH_SIZE]
Definition: tm-queuehandlers.c:37
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:32
PacketDequeue
Packet * PacketDequeue(PacketQueue *q)
Definition: packet-queue.c:216
util-validate.h
PacketGetFromAlloc
Packet * PacketGetFromAlloc(void)
Get a malloced packet.
Definition: decode.c:229
TUNNEL_PKT_RTV
#define TUNNEL_PKT_RTV(p)
Definition: decode.h:810
Packet_::next
struct Packet_ * next
Definition: decode.h:616
Packet_::root
struct Packet_ * root
Definition: decode.h:634
TUNNEL_INCR_PKT_RTV_NOLOCK
#define TUNNEL_INCR_PKT_RTV_NOLOCK(p)
Definition: decode.h:798
PACKET_PROFILING_END
#define PACKET_PROFILING_END(p)
Definition: util-profiling.h:86
PacketPoolGetPacket
Packet * PacketPoolGetPacket(void)
Get a new packet from the packet pool.
Definition: tmqh-packetpool.c:127
SCSpinlock
#define SCSpinlock
Definition: threads-debug.h:234
cc_barrier
#define cc_barrier()
Definition: util-optimize.h:43
PktPool_::pending_pool
struct PktPool_ * pending_pool
Definition: tmqh-packetpool.h:55
SC_ATOMIC_GET
#define SC_ATOMIC_GET(name)
Get the value from the atomic variable.
Definition: util-atomic.h:375
PacketPoolDestroy
void PacketPoolDestroy(void)
Definition: tmqh-packetpool.c:277
PktPool_::head
Packet * head
Definition: tmqh-packetpool.h:47
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:103