Inspired by https://github.com/emmericp/ixy, which is a simple C-based user space NIC driver, this project is a C++ realization of it. By rebuilding the project in C++, it is better for beginners to understand the hierarchy and workflow of user-space NIC driver.
Note this program only support Linux.
This project does not realize all the functions provided in https://github.com/emmericp/ixy yet. It only supports VFIO based NIC. Also, in terms of example applications, only data sending is realized, which is from ixy-pktgen.c in ixy/src/app
- Clearer structure. The Ring buffer, Memory Pool and the device itself are all decoupled.
- Clearer ring buffers: The packet-buffer ring and descriptor ring are decoupled. The filling of packet-buffer ring is isolated from the modification of descriptor ring.
- Host and NIC isolation. The manipulations on the host and NIC (the registers) are isolated, which is easy for the reuse of code.
- Future extension.
BasicDevandRingBufferare all abstract classes. This makes future extension available. - Better Naming. Some functions and variables are renamed for better understanding.
BasicDevis an abstract class. In this project,Intel82599Devis a realization of it in Intel NIC. A new realization can be done in the future. (For example, FPGA-based NIC driver)DMAMemoryAllocatoris a helper class, which is implemented using singleton pattern. The function is to allocate DMA-enabled memory.DMAMemoryPoolis a DMA-memory based memory pool.RingBufferis an abstract class.IXGBE_RxRingBufferandIXGBE_TxRingBufferare the realization of theRingBuffer. Both of them containDMAMemoryPool.
Here a simplified ring buffer structure is given. When working with devices supporting VFIO, there will always be two types of memory addresses, one is for the outter devices, named IO virtual address (IOVA). The other is the vritual address for the host (shorten as virt here). The allocation of the two memory address is managed by dma_memory_allocator. This figure shows how Memory pool and the descriptors work together in one ring buffer so the outter device can access the data inside the memory pool indirectly.
fillPktBuf(): This function fills data into free packet buffers in the memory pool. Also, a packet ring buffer (an array) notes the addresses of the buffers with data, as shown below:.png)
linkPktWithDesc(): This function pushes the IOVA of the packet buffers with data to the descriptor ring. It also cleans the packet ring buffer. This is done by moving theheadindex in packt ring buffer. This behavior is also provided below:.png)
cleanDescriptorRing(): This function cleans the descriptor ring, and moves the indexfree_stack_topto the top, which means all the packet buffers are available now. Thedatainside each packet buffer now marked with grey, indicating that they are outdated..png)
- First of all, unbind the NIC from the keneral driver. This can be done by running
scripts/setup-vfio.sh. Just input the PCIe address, separated with space. - Enable hugepage in your Linux system. Run
scripts/setup-hugepages.shwith the number you desire. This script allocate 2-MB hugepages. - For compilation, just run
cmake -S . -B buildand thencmake --build build - For function testing. cd to
buildfolder and command./test_app_loopsend.The default PCIe addresses are0000:04:00.0and0000:05:00.0while the BAR index is0. The program will start two threads, each for one NIC. The two NICs should be connected together. What you expect is that two NICs send data to each other. For you own PCIe device, check the corresponding PCIe address by usinglspciin the shell.
- A new driver class for FPGA-based NIC will be added to this project.
- Multiple queue function is to be realized in the future.
- Rx.
This work is done by myself with the help of AI to help people new to NIC driver to learn the flow inside. Mistakes might be found. Any suggestion is welcome.

