numa_example.c Source File

Reference Documentation

Platform
Napatech SmartNIC
Content Type
Reference Information
Capture Software Version
Link™ Capture Software 12.15
Napatech Software Suite: examples/net/numa/numa_example.c Source File
numa_example.c
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2025 Napatech A/S. All Rights Reserved.
4  *
5  * 1. Copying, modification, and distribution of this file, or executable
6  * versions of this file, is governed by the terms of the Napatech Software
7  * license agreement under which this file was made available. If you do not
8  * agree to the terms of the license do not install, copy, access or
9  * otherwise use this file.
10  *
11  * 2. Under the Napatech Software license agreement you are granted a
12  * limited, non-exclusive, non-assignable, copyright license to copy, modify
13  * and distribute this file in conjunction with Napatech SmartNIC's and
14  * similar hardware manufactured or supplied by Napatech A/S.
15  *
16  * 3. The full Napatech Software License Agreement is included in this
17  * distribution, please see "NA-0009 Software License Agreement.pdf"
18  *
19  * 4. Redistributions of source code must retain this copyright notice,
20  * list of conditions and the following disclaimer.
21  *
22  * THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTIES, EXPRESS OR
23  * IMPLIED, AND NAPATECH DISCLAIMS ALL IMPLIED WARRANTIES INCLUDING ANY
24  * IMPLIED WARRANTY OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, OR OF
25  * FITNESS FOR A PARTICULAR PURPOSE. TO THE EXTENT NOT PROHIBITED BY
26  * APPLICABLE LAW, IN NO EVENT SHALL NAPATECH BE LIABLE FOR PERSONAL INJURY,
27  * OR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES WHATSOEVER,
28  * INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, CORRUPTION OR
29  * LOSS OF DATA, FAILURE TO TRANSMIT OR RECEIVE ANY DATA OR INFORMATION,
30  * BUSINESS INTERRUPTION OR ANY OTHER COMMERCIAL DAMAGES OR LOSSES, ARISING
31  * OUT OF OR RELATED TO YOUR USE OR INABILITY TO USE NAPATECH SOFTWARE OR
32  * SERVICES OR ANY THIRD PARTY SOFTWARE OR APPLICATIONS IN CONJUNCTION WITH
33  * THE NAPATECH SOFTWARE OR SERVICES, HOWEVER CAUSED, REGARDLESS OF THE THEORY
34  * OF LIABILITY (CONTRACT, TORT OR OTHERWISE) AND EVEN IF NAPATECH HAS BEEN
35  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME JURISDICTIONS DO NOT ALLOW
36  * THE EXCLUSION OR LIMITATION OF LIABILITY FOR PERSONAL INJURY, OR OF
37  * INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY TO YOU.
38  *
39  *
40 
41  */
42 
43 /**
44  * @example net/numa/numa_example.c
45  * @section numa_example_description Description
46  *
47  * This source file is an example of how to utilize host buffers
48  * allocated to a specific NUMA node using NTAPI. NUMA support is
49  * currently not available on FreeBSD, so this example only applies to
50  * Linux. The example uses host buffers on NUMA node 1 and receives
51  * data on port 0.
52  *
53  * The following NTAPI functions are used:
54  * - @ref NT_Init()
55  * - @ref NT_ConfigOpen()
56  * - @ref NT_NTPL()
57  * - @ref NT_ConfigClose()
58  * - @ref NT_NetRxOpen()
59  * - @ref NT_NetRxGet()
60  * - @ref NT_NET_GET_PKT_WIRE_LENGTH()
61  * - @ref NT_NetRxRelease()
62  * - @ref NT_NetRxClose()
63  * - @ref NT_Done()
64  * - @ref NT_ExplainError()
65  *
66  * @section numa_example_prerequisites Prerequisites
67  * A Napatech capture accelerator is need to run this example. The ntservice.ini
68  * must have at least one HostBuffersRx defined. Below is an example
69  * of a minimum ini-file. It will create a 32MB RX hostbuffer from
70  * NUMA node 1.
71  * @code
72  * [Adapter0]
73  * AdapterType = NT20E
74  * BusId = 00:0a:00.00
75  * HostBuffersRx = [1,32,1]
76  * @endcode
77  *
78  * @section numa_example_flow Program flow
79  * @{
80  * The following is required to use a host buffer from a specific NUMA node:
81  * - \#include/nt.h - Applications/Tools only need to include @ref
82  * nt.h to obtain prototypes, macros etc. from NTAPI.
83  * - Binding the current process to NUMA node 1 can be done in several ways:
84  * - For Linux:
85  * - Using sched_setaffinity() on Linux. This will require that it is known
86  * which CPU cores are located on NUMA node 1.
87  * - Using numa_bind() from libnuma on Linux to bind to NUMA node 1.
88  * - Using numactl on the command line to start the example on NUMA node 1. e.g
89  * @code numactl --cpubind=1 --membind=1 ./numa_example @endcode
90  * - For Windows:
91  * - Using GetNumaHighestNodeNumber, GetProcessAffinityMask and GetNumaNodeProcessorMask
92  * to find the numa node that the app/process is able to use. If the system is well known
93  * this step can be skipped.
94  * - Using SetProcessAffinityMask to move the app/process to the wanted processor (numa node).
95  * - @ref NT_Init(@ref NTAPI_VERSION) - Initialize the NTAPI
96  * library. @ref NTAPI_VERSION is a define that describes the version
97  * of the API described in the header files included by @ref
98  * nt.h. @ref NT_Init() will ask the NTAPI library to convert return data
99  * to the @ref NTAPI_VERSION if possible. This will ensure that
100  * applications can run on NTAPI libraries of newer versions.
101  * - @ref NT_ConfigOpen() - Open a configuration stream.
102  * - @ref NT_NTPL() - Setup stream 1 to use NUMA node 1. This must be
103  * done before assigning a filter.
104  * - @ref NT_NTPL() - Assign traffic to the stream. A stream doesn't
105  * return data until traffic is assigned to it by a filter. Stream
106  * IDs might be shared between other streams.
107  * - @ref NT_ConfigClose() - Close the configuration stream.
108  * - @ref NT_NetRxOpen() - Open the stream using a stream ID.
109  * The stream ID must match the one used when creating the
110  * filter.
111  * - @ref NT_NetRxGet() is called with a timeout of 1000ms and will return
112  * NT_STATUS_TIMEOUT in case nothing is received within 1000ms and
113  * will return NT_SUCCESS if something is returned. Return values
114  * different from that is an indication of an error. Packets that
115  * are prior to the expected time are released via @ref NT_NetRxRelease().
116  * - @ref NT_NetRxRelease() - Release a packet.
117  * - @ref NT_NetRxClose() - Close the stream when terminating.
118  * - @ref NT_NTPL() - Release the NTPL assignment made on the hostbuffer.
119  * - @ref NT_Done() - Close down the NTAPI library.
120  * - @ref NT_ExplainError() - Explain an error code returned by NTAPI functions.
121  *
122  *
123  *<hr>
124  * @section numa_example_code Code
125  * @}
126  */
127 
128  // Include this in order to access the Napatech API
129 #include <nt.h>
130 
131 #ifdef WIN32
132 #include <winsock2.h>
133 #endif
134 
135 #if defined(WIN32) || defined (WIN64)
136  #define snprintf(dst, ...) _snprintf_s((dst), _countof(dst), __VA_ARGS__)
137 #endif
138 
139 // Define to use sched_setaffinity
140 //#define USE_SCHED_SETAFFINITY
141 #ifdef USE_SCHED_SETAFFINITY
142 #define __USE_GNU
143 #include <sched.h>
144 #endif
145 
146 // Define to use libnuma
147 //#define USE_LIBNUMA
148 #ifdef USE_LIBNUMA
149 #include <numa.h>
150 #endif
151 
152 int main(void)
153 {
154  unsigned int numPackets=0; // The number of packets received
155  unsigned int numBytes=0; // The number of bytes received
156  char tmpBuffer[20]; // Buffer to build filter string
157  char errorBuffer[NT_ERRBUF_SIZE]; // Error buffer
158  int status; // Status variable
159  NtNetStreamRx_t hNetRx; // Handle to the RX stream
160  NtConfigStream_t hCfgStream; // Handle to a config stream
161  NtNtplInfo_t ntplInfo; // Return data structure from the NT_NTPL() call.
162  NtNetBuf_t hNetBuf; // Net buffer container. Packet data is returned in this when calling NT_NetRxGet().
163  bool firstFound = false;
164 
165 #ifdef USE_SCHED_SETAFFINITY
166  // In this example it is assumed that CPU core 1,3,5,7 are located on NUMA node 1
167  cpu_set_t mask;
168  CPU_ZERO(&mask);
169  CPU_SET(1, &mask);
170  CPU_SET(3, &mask);
171  CPU_SET(5, &mask);
172  CPU_SET(7, &mask);
173  sched_setaffinity(0, sizeof(cpu_set_t), &mask);
174 #endif
175 
176 #ifdef USE_LIBNUMA
177  // Uses the numa_bind() function from libnuma to bind current process to NUMA node 1.
178 #if (defined LIBNUMA_API_VERSION) && LIBNUMA_API_VERSION == 2
179  struct bitmask * mask = numa_bitmask_alloc(numa_num_possible_nodes());
180  numa_bitmask_setbit(mask, 1);
181  numa_bind(mask);
182  numa_bitmask_free(mask);
183 #else
184  nodemask_t mask;
185  nodemask_zero(&mask);
186  nodemask_set(&mask, 1);
187  numa_bind(&mask);
188 #endif
189 #endif
190 
191 #ifdef WIN32
192  {
193  DWORD_PTR processAffinityMask;
194  DWORD_PTR systemAffinityMask;
195  DWORD_PTR setAffinityMask;
196  ULONGLONG processorMask;
197  ULONG highestNodeNumber;
198 
199  // Is this a numa system
200  if (!GetNumaHighestNodeNumber(&highestNodeNumber)) {
201  fprintf(stderr, "GetNumaHighestNodeNumber failed with errorcode: %08X\n", GetLastError());
202  return -1;
203  }
204  if (highestNodeNumber == 0) {
205  fprintf(stderr, "This is not a numa system. Cannot run this example.\n");
206  return -1;
207  }
208  // Find the cores that this process is allowed to use
209  if (!GetProcessAffinityMask(GetCurrentProcess(), &processAffinityMask, &systemAffinityMask)) {
210  fprintf(stderr, "GetProcessAffinityMask failed with errorcode: %08X\n", GetLastError());
211  return -1;
212  }
213  // Find the cores belonging to numa node 1
214  if (!GetNumaNodeProcessorMask(1, &processorMask)) {
215  fprintf(stderr, "GetNumaNodeProcessorMask failed with errorcode: %08X\n", GetLastError());
216  return -1;
217  }
218  // Use only the cores that is allowed for this process
219  setAffinityMask = processAffinityMask & processorMask;
220  // Make this process to run on a processor on numa node 1
221  if (!SetProcessAffinityMask(GetCurrentProcess(), setAffinityMask)) {
222  fprintf(stderr, "SetProcessAffinityMask failed with errorcode: %08X\n", GetLastError());
223  return -1;
224  }
225  }
226 #endif
227 
228  // Initialize the NTAPI library and thereby check if NTAPI_VERSION can be used together with this library
229  if ((status = NT_Init(NTAPI_VERSION)) != NT_SUCCESS) {
230  // Get the status code as text
231  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
232  fprintf(stderr, "NT_Init() failed: %s\n", errorBuffer);
233  return -1;
234  }
235 
236  // Open a config stream to assign a filter to a stream ID.
237  if ((status = NT_ConfigOpen(&hCfgStream, "TestStream")) != NT_SUCCESS) {
238  // Get the status code as text
239  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
240  fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer);
241  return -1;
242  }
243 
244  // Setup stream 1 to use NUMA node 1. This must be done before assigning a filter.
245  if ((status = NT_NTPL(hCfgStream, "Setup[NUMANode=1] = StreamId==1", &ntplInfo, NT_NTPL_PARSER_VALIDATE_NORMAL)) != NT_SUCCESS) {
246  // Get the status code as text
247  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
248  fprintf(stderr, "NT_NTPL() failed: %s\n", errorBuffer);
249  fprintf(stderr, ">>> NTPL errorcode: %X\n", ntplInfo.u.errorData.errCode);
250  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[0]);
251  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[1]);
252  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[2]);
253  return -1;
254  }
255 
256  // Assign all traffic on port 0 to stream ID 1
257  if ((status = NT_NTPL(hCfgStream, "Assign[StreamId=1] = Port == 0",
258  &ntplInfo, NT_NTPL_PARSER_VALIDATE_NORMAL)) != NT_SUCCESS) {
259  // Get the status code as text
260  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
261  fprintf(stderr, "NT_NTPL() failed: %s\n", errorBuffer);
262  fprintf(stderr, ">>> NTPL errorcode: %X\n", ntplInfo.u.errorData.errCode);
263  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[0]);
264  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[1]);
265  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[2]);
266  return -1;
267  }
268 
269  // Close the config stream
270  if ((status = NT_ConfigClose(hCfgStream)) != NT_SUCCESS) {
271  // Get the status code as text
272  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
273  fprintf(stderr, "NT_ConfigClose() failed: %s\n", errorBuffer);
274  return -1;
275  }
276 
277  // Get a stream handle with stream ID 1. NT_NET_INTERFACE_PACKET
278  // specify that we will receive data in a packet based matter.
279  if ((status = NT_NetRxOpen(&hNetRx, "TestStream", NT_NET_INTERFACE_PACKET, 1, -1)) != NT_SUCCESS) {
280  // Get the status code as text
281  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
282  fprintf(stderr, "NT_NetRxOpen() failed: %s\n", errorBuffer);
283  return -1;
284  }
285 
286  // Optional step. Wait for the first packet that hit the NTPL assign command
287  printf("Waiting for the first packet\n");
288  while (!firstFound) {
289  if ((status = NT_NetRxGet(hNetRx, &hNetBuf, 1000)) != NT_SUCCESS) {
290  if ((status == NT_STATUS_TIMEOUT) || (status == NT_STATUS_TRYAGAIN)) {
291  // Timeouts are ok, we just need to wait a little longer for a packet
292  continue;
293  }
294  // Get the status code as text
295  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
296  fprintf(stderr, "NT_NetRxGet() failed: %s\n", errorBuffer);
297  return -1;
298  }
299  // We got a packet. Check if the timestamp is newer than when the NTPL assign command was applied
300  if (NT_NET_GET_PKT_TIMESTAMP(hNetBuf) > ntplInfo.ts) {
301  // We have received a packet that is received after the NTPL assign command was applied
302  // Note that this packet is released again.
303  firstFound = true;
304  }
305  // Release the packet as it is too "old".
306  if ((status = NT_NetRxRelease(hNetRx, hNetBuf)) != NT_SUCCESS) {
307  // Get the status code as text
308  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
309  fprintf(stderr, "NT_NetRxRelease() failed: %s\n", errorBuffer);
310  return -1;
311  }
312  }
313 
314  // Get 10000000 packets
315  #define NUMBER_OF_PACKETS 1000000
316  while (numPackets < NUMBER_OF_PACKETS) {
317  // Get packet
318  if ((status = NT_NetRxGet(hNetRx, &hNetBuf, 1000)) != NT_SUCCESS) {
319  if ((status == NT_STATUS_TIMEOUT) || (status == NT_STATUS_TRYAGAIN)) {
320  // Timeouts are ok, we just need to wait a little longer for a packet
321  continue;
322  }
323  fprintf(stderr, "NT_NetRxGet failed\n");
324  break;
325  }
326  numBytes += NT_NET_GET_PKT_WIRE_LENGTH(hNetBuf);
327  numPackets++;
328 
329  if ((numPackets % 1000) == 0) {
330  printf("Packets received %d of %d \r", numPackets, NUMBER_OF_PACKETS);
331  fflush(stdout);
332  }
333 
334  // Release packet after use
335  NT_NetRxRelease(hNetRx, hNetBuf);
336  }
337  printf("\n");
338 
339  // Close the stream and release the hostbuffer. This will also remove the NTPL assignments performed.
340  NT_NetRxClose(hNetRx);
341 
342  // Open a config stream to delete a filter.
343  if ((status = NT_ConfigOpen(&hCfgStream, "TestStream")) != NT_SUCCESS) {
344  // Get the status code as text
345  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
346  fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer);
347  return -1;
348  }
349 
350  // Delete the filter
351  snprintf(tmpBuffer, sizeof(tmpBuffer), "delete=%d", ntplInfo.ntplId);
352  if ((status = NT_NTPL(hCfgStream, tmpBuffer, &ntplInfo, NT_NTPL_PARSER_VALIDATE_NORMAL)) != NT_SUCCESS) {
353  // Get the status code as text
354  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
355  fprintf(stderr, "NT_NTPL() failed: %s\n", errorBuffer);
356  fprintf(stderr, ">>> NTPL errorcode: %X\n", ntplInfo.u.errorData.errCode);
357  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[0]);
358  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[1]);
359  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[2]);
360  return -1;
361  }
362 
363  // Close the config stream
364  if ((status = NT_ConfigClose(hCfgStream)) != NT_SUCCESS) {
365  // Get the status code as text
366  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
367  fprintf(stderr, "NT_ConfigClose() failed: %s\n", errorBuffer);
368  return -1;
369  }
370 
371  printf("Done: %16d packets, %16d bytes\n", numPackets, numBytes);
372 
373  // Close down the NTAPI library
374  NT_Done();
375 
376  return 0;
377 }