/
DpdkDeviceList.h
198 lines (171 loc) · 9.19 KB
/
DpdkDeviceList.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#ifndef PCAPPP_DPDK_DEVICE_LIST
#define PCAPPP_DPDK_DEVICE_LIST
#include <SystemUtils.h>
#include <DpdkDevice.h>
#include <Logger.h>
#include <vector>
/**
* @file
* For details about PcapPlusPlus support for DPDK see DpdkDevice.h file description
*/
/**
* \namespace pcpp
* \brief The main namespace for the PcapPlusPlus lib
*/
namespace pcpp
{
/**
* @class DpdkWorkerThread
* There are two ways to capture packets using DpdkDevice: one of them is using worker threads and the other way is using
* a callback which is invoked on each a burst of packets are captured (see DpdkDevice#startCaptureSingleThread() ). This class
* is a base class for implementing workers. A worker is basically a class that is activated by DpdkDeviceList#startDpdkWorkerThreads()
* and runs on a designated core. When it runs it can do whatever the user wants it to do. The most common use it running in an
* endless loop and receive, analyze and send packets using one or more DpdkDevice instances. It can do all kinds of processing for
* these packets. The only restriction for a worker class is that it must implement the 3 abstract methods stated in this class-interface
* for start running, stop running and get the core ID the worker is running on.
*/
class DpdkWorkerThread
{
public:
/**
* A virtual d'tor. Can be overridden by child class if needed
*/
virtual ~DpdkWorkerThread() {}
/**
* An abstract method that must be implemented by child class. It's the indication for the worker to start running
* @param[in] coreId The core ID the worker is running on (should be returned in getCoreId() )
* @return True if all went well or false otherwise
*/
virtual bool run(uint32_t coreId) = 0;
/**
* An abstract method that must be implemented by child class. It's the indication for the worker to stop running. After
* this method is called the caller expects the worker to stop running as fast as possible
*/
virtual void stop() = 0;
/**
* An abstract method that must be implemented by child class. Get the core ID the worker is running on (as sent to the run() method
* as a parameter)
* @return The core ID the worker is running on
*/
virtual uint32_t getCoreId() = 0;
};
/**
* @class DpdkDeviceList
* A singleton class that encapsulates DPDK initialization and holds the list of DpdkDevice instances. As it's a singleton, it has only
* one active instance doesn't have a public c'tor. This class has several main uses:
* - it contains the initDpdk() static method which initializes the DPDK infrastructure. It should be called once in every application at
* its startup process
* - it contains the list of DpdkDevice instances and enables access to them
* - it has methods to start and stop worker threads. See more details in startDpdkWorkerThreads()
*/
class DpdkDeviceList
{
private:
bool m_IsInitialized;
static bool m_IsDpdkInitialized;
static uint32_t m_MBufPoolSizePerDevice;
static CoreMask m_CoreMask;
std::vector<DpdkDevice*> m_DpdkDeviceList;
std::vector<DpdkWorkerThread*> m_WorkerThreads;
DpdkDeviceList();
inline bool isInitialized() { return (m_IsInitialized && m_IsDpdkInitialized); }
bool initDpdkDevices(uint32_t mBufPoolSizePerDevice);
static bool verifyHugePagesAndDpdkDriver();
static int dpdkWorkerThreadStart(void *ptr);
public:
~DpdkDeviceList();
/**
* As DpdkDeviceList is a singleton, this is the static getter to retrieve its instance. Note that if the static method
* initDpdk() was not called or returned false this instance won't be initialized and DpdkDevices won't be initialized either
* @return The singleton instance of DpdkDeviceList
*/
static inline DpdkDeviceList& getInstance()
{
static DpdkDeviceList instance;
if (!instance.isInitialized())
instance.initDpdkDevices(DpdkDeviceList::m_MBufPoolSizePerDevice);
return instance;
}
/**
* A static method that has to be called once at the startup of every application that uses DPDK. It does several things:
* - verifies huge-pages are set and DPDK kernel module is loaded (these are set by the setup-dpdk.sh external script that
* has to be run before application is started)
* - initializes the DPDK infrastructure
* - creates DpdkDevice instances for all ports available for DPDK
*
* @param[in] coreMask The cores to initialize DPDK with. After initialization, DPDK will only be able to use these cores
* for its work. The core mask should have a bit set for every core to use. For example: if the user want to use cores 1,2
* the core mask should be 6 (binary: 110)
* @param[in] mBufPoolSizePerDevice The mbuf pool size each DpdkDevice will have. This has to be a number which is a power of 2
* minus 1, for example: 1023 (= 2^10-1) or 4,294,967,295 (= 2^32-1), etc. This is a DPDK limitation, not PcapPlusPlus.
* The size of the mbuf pool size dictates how many packets can be handled by the application at the same time. For example: if
* pool size is 1023 it means that no more than 1023 packets can be handled or stored in application memory at every point in time
* @return True if initialization succeeded or false if huge-pages or DPDK kernel driver are not loaded, if mBufPoolSizePerDevice
* isn't power of 2 minus 1, if DPDK infra initialization failed or if DpdkDevice initialization failed. Anyway, if this method
* returned false it's impossible to use DPDK with PcapPlusPlus. You can get some more details about mbufs and pools in
* DpdkDevice.h file description or in DPDK web site
*/
static bool initDpdk(CoreMask coreMask, uint32_t mBufPoolSizePerDevice);
/**
* Get a DpdkDevice by port ID
* @param[in] portId The port ID
* @return A pointer to the DpdkDevice or NULL if no such device is found
*/
DpdkDevice* getDeviceByPort(int portId);
/**
* Get a DpdkDevice by port PCI address
* @param[in] pciAddr The port PCI address
* @return A pointer to the DpdkDevice or NULL if no such device is found
*/
DpdkDevice* getDeviceByPciAddress(const PciAddress& pciAddr);
/**
* @return A vector of all DpdkDevice instances
*/
inline const std::vector<DpdkDevice*>& getDpdkDeviceList() { return m_DpdkDeviceList; }
/**
* @return DPDK master core which is the core that initializes the application
*/
SystemCore getDpdkMasterCore();
/**
* Change the log level of all modules of DPDK
* @param[in] logLevel The log level to set. LoggerPP#Normal is RTE_LOG_NOTICE and LoggerPP#Debug is RTE_LOG_DEBUG
*/
void setDpdkLogLevel(LoggerPP::LogLevel logLevel);
/**
* @return The current DPDK log level. RTE_LOG_NOTICE and lower are considered as LoggerPP#Normal. RTE_LOG_INFO or RTE_LOG_DEBUG
* are considered as LoggerPP#Debug
*/
LoggerPP::LogLevel getDpdkLogLevel();
/**
* Order DPDK to write all its logs to a file
* @param[in] logFile The file to write to
* @return True if action succeeded, false otherwise
*/
bool writeDpdkLogToFile(FILE* logFile);
/**
* There are two ways to capture packets using DpdkDevice: one of them is using worker threads and the other way is setting
* a callback which is invoked each time a burst of packets is captured (see DpdkDevice#startCaptureSingleThread() ). This
* method implements the first way. See a detailed description of workers in DpdkWorkerThread class description. This method
* gets a vector of workers (classes that implement the DpdkWorkerThread interface) and a core mask and starts a worker thread
* on each core (meaning - call the worker's DpdkWorkerThread#run() method). Workers usually run in an endless loop and will
* be ordered to stop by calling stopDpdkWorkerThreads().<BR>
* Note that number of cores in the core mask must be equal to the number of workers. In addition it's impossible to run a
* worker thread on DPDK master core, so the core mask shouldn't include the master core (you can find the master core by
* calling getDpdkMasterCore() ).
* @param[in] coreMask The bitmask of cores to run worker threads on. This list shouldn't include DPDK master core
* @param[in] workerThreadsVec A vector of worker instances to run (classes who implement the DpdkWorkerThread interface).
* Number of workers in this vector must be equal to the number of cores in the core mask. Notice that the instances of
* DpdkWorkerThread shouldn't be freed until calling stopDpdkWorkerThreads() as these instances are running
* @return True if all worker threads started successfully or false if: DPDK isn't initialized (initDpdk() wasn't called or
* returned false), number of cores differs from number of workers, core mask includes DPDK master core or if one of the
* worker threads couldn't be run
*/
bool startDpdkWorkerThreads(CoreMask coreMask, std::vector<DpdkWorkerThread*>& workerThreadsVec);
/**
* Assuming worker threads are running, this method orders them to stop by calling DpdkWorkerThread#stop(). Then it waits until
* they stop running
*/
void stopDpdkWorkerThreads();
};
} // namespace pcpp
#endif /* PCAPPP_DPDK_DEVICE_LIST */