#ifndef PCAPPP_DPDK_DEVICE_LIST #define PCAPPP_DPDK_DEVICE_LIST // GCOVR_EXCL_START #include "SystemUtils.h" #include "DpdkDevice.h" #include "Logger.h" #include /** * @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() const = 0; }; class KniDeviceList; /** * @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 { friend class KniDeviceList; private: bool m_IsInitialized; static bool m_IsDpdkInitialized; static uint32_t m_MBufPoolSizePerDevice; static CoreMask m_CoreMask; std::vector m_DpdkDeviceList; std::vector m_WorkerThreads; DpdkDeviceList(); bool isInitialized() const { 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 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.py 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 * @param[in] masterCore The core DPDK will use as master to control all worker thread. The default, unless set otherwise, is 0 * @param[in] initDpdkArgc Number of optional arguments * @param[in] initDpdkArgv Optional arguments * @param[in] appName program name to be provided for the DPDK * @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, uint8_t masterCore = 0, uint32_t initDpdkArgc = 0, char **initDpdkArgv = NULL, const std::string& appName = "pcapplusplusapp"); /** * 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) const; /** * 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 std::string& pciAddr) const; /** * @return A vector of all DpdkDevice instances */ const std::vector& getDpdkDeviceList() const { return m_DpdkDeviceList; } /** * @return DPDK master core which is the core that initializes the application */ SystemCore getDpdkMasterCore() const; /** * Change the log level of all modules of DPDK * @param[in] logLevel The log level to set. Logger#Info is RTE_LOG_NOTICE and Logger#Debug is RTE_LOG_DEBUG */ void setDpdkLogLevel(Logger::LogLevel logLevel); /** * @return The current DPDK log level. RTE_LOG_NOTICE and lower are considered as Logger#Info. RTE_LOG_INFO or RTE_LOG_DEBUG * are considered as Logger#Debug */ Logger::LogLevel getDpdkLogLevel() const; /** * 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().
* 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& 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 // GCOVR_EXCL_STOP #endif /* PCAPPP_DPDK_DEVICE_LIST */