numa_example.c Source File

Reference Documentation

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