diff --git a/DPDK-Filtering-Rule-Analysis/Pics/1.png b/DPDK-Filtering-Rule-Analysis/Pics/1.png
new file mode 100644
index 0000000..a286e6a
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/1.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/2.png b/DPDK-Filtering-Rule-Analysis/Pics/2.png
new file mode 100644
index 0000000..1ce33f8
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/2.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/3.png b/DPDK-Filtering-Rule-Analysis/Pics/3.png
new file mode 100644
index 0000000..77a4f64
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/3.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/4.png b/DPDK-Filtering-Rule-Analysis/Pics/4.png
new file mode 100644
index 0000000..14e3446
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/4.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/5.png b/DPDK-Filtering-Rule-Analysis/Pics/5.png
new file mode 100644
index 0000000..7304276
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/5.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/6.png b/DPDK-Filtering-Rule-Analysis/Pics/6.png
new file mode 100644
index 0000000..1bff0d9
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/6.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/create_flow_rule.png b/DPDK-Filtering-Rule-Analysis/Pics/create_flow_rule.png
new file mode 100644
index 0000000..0b892ca
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/create_flow_rule.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/flow_destroy.png b/DPDK-Filtering-Rule-Analysis/Pics/flow_destroy.png
new file mode 100644
index 0000000..f95bc30
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/flow_destroy.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/flow_list.png b/DPDK-Filtering-Rule-Analysis/Pics/flow_list.png
new file mode 100644
index 0000000..8644c0e
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/flow_list.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/image1.png b/DPDK-Filtering-Rule-Analysis/Pics/image1.png
new file mode 100644
index 0000000..3cdbb2b
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/image1.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/image10.png b/DPDK-Filtering-Rule-Analysis/Pics/image10.png
new file mode 100644
index 0000000..21bc0ee
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/image10.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/image11.png b/DPDK-Filtering-Rule-Analysis/Pics/image11.png
new file mode 100644
index 0000000..5c7e82d
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/image11.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/image2.png b/DPDK-Filtering-Rule-Analysis/Pics/image2.png
new file mode 100644
index 0000000..b10c093
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/image2.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/image3.png b/DPDK-Filtering-Rule-Analysis/Pics/image3.png
new file mode 100644
index 0000000..d7bd684
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/image3.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/image4.png b/DPDK-Filtering-Rule-Analysis/Pics/image4.png
new file mode 100644
index 0000000..afc5ce7
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/image4.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/image5.png b/DPDK-Filtering-Rule-Analysis/Pics/image5.png
new file mode 100644
index 0000000..ed39869
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/image5.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/image6.png b/DPDK-Filtering-Rule-Analysis/Pics/image6.png
new file mode 100644
index 0000000..1c0fd6a
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/image6.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/image7.png b/DPDK-Filtering-Rule-Analysis/Pics/image7.png
new file mode 100644
index 0000000..a1cfe8a
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/image7.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/image8.png b/DPDK-Filtering-Rule-Analysis/Pics/image8.png
new file mode 100644
index 0000000..826abaf
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/image8.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/image9.png b/DPDK-Filtering-Rule-Analysis/Pics/image9.png
new file mode 100644
index 0000000..ae07865
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/image9.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/run_script.png b/DPDK-Filtering-Rule-Analysis/Pics/run_script.png
new file mode 100644
index 0000000..4cc7432
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/run_script.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/show_stats.png b/DPDK-Filtering-Rule-Analysis/Pics/show_stats.png
new file mode 100644
index 0000000..339156d
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/show_stats.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/start_command.png b/DPDK-Filtering-Rule-Analysis/Pics/start_command.png
new file mode 100644
index 0000000..3eb73a1
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/start_command.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/Pics/tcpreplay_run.png b/DPDK-Filtering-Rule-Analysis/Pics/tcpreplay_run.png
new file mode 100644
index 0000000..9a2fd07
Binary files /dev/null and b/DPDK-Filtering-Rule-Analysis/Pics/tcpreplay_run.png differ
diff --git a/DPDK-Filtering-Rule-Analysis/README.md b/DPDK-Filtering-Rule-Analysis/README.md
index f6141ca..cbaf755 100644
--- a/DPDK-Filtering-Rule-Analysis/README.md
+++ b/DPDK-Filtering-Rule-Analysis/README.md
@@ -1 +1,576 @@
-# DPDK-Filtering-Rule-Analysis
\ No newline at end of file
+# Dpdk-TAP-Driver
+
+This repository contains dpdk TAP driver and test-pmd application analysis with lttng tool and trace compass gui.
+
+## System Specifications
+
+The project was executed on the following system:
+
+```bash
+Architecture: x86_64
+CPU op-mode(s): 32-bit, 64-bit
+Byte Order: Little Endian
+Address sizes: 39 bits physical, 48 bits virtual
+CPU(s): 8
+On-line CPU(s) list: 0-7
+Thread(s) per core: 2
+Core(s) per socket: 4
+Socket(s): 1
+NUMA node(s): 1
+Vendor ID: GenuineIntel
+Model name: Intel(R) Core(TM) i7-1065G7 CPU @ 1.30GHz
+CPU min MHz: 400.000
+CPU max MHz: 3900.0000
+...
+```
+
+## Core Isolation
+
+IRQs (both software and hardware) may cause context switches, degrading dpdk aplication performance. This Effect can be decreasesd by defining the rules for each CPU core but not fully deleted as dpdk does not fully bypass kernel.
+
+```bash
+sudo nano /etc/default/grub
+```
+
+Grub configuration file values should be changed to assign differnet jobs to different cores.
+
+```bash
+GRUB_CMDLINE_LINUX_DEFAULT="quiet splash isolcpus=4,5 nohz_full=4,5 rcu_nocbs=4,5 irqaffinity=0,1,2,3,6,7"
+```
+
+Value of `GRUB_CMDLINE_LINUX_DEFAULT` variable changes:
+
+`isolcpus=4,5` isolates CPUs 4 and 5 from the general Linux scheduler.
+
+`nohz_full=4,5` enables full tickless mode on CPUs 4 and 5.
+
+`rcu_nocbs=4,5` offloads RCU (Read-Copy-Update) callbacks from CPUs 4 and 5.
+
+`irqaffinity=0,1,2,3,6,7` restricts hardware interrupt handling to CPUs 0-3 and 6-7.
+
+```bash
+sudo update-grub
+sudo reboot
+```
+
+`sudo update-grub` updates the grub bootloader configuration file and regenarates `/boot/grub/grub.cfg` file. Changes will finally take place after `sudo reboot` that can be assured by `dmesg | grep isolcpus`.
+
+
+
+## 1. Installation and Build of DPDK
+
+1. **Download the latest version from [the official website](https://core.dpdk.org/download/)**
+
+
+
+ 2. **Extract the Downloaded Archive**
+
+ ```shell
+ tar xJf dpdk-.tar.xz
+ cd dpdk-
+ ```
+
+
+
+ 3. **Use Meson for Configuration of Build Environment**
+
+ ```shell
+ meson setup build \
+ -Dexamples=all \
+ -Dlibdir=lib \
+ -Denable_trace_fp=true \
+ -Dc_args="-finstrument-functions"
+ ```
+
+
+
+ 4. **Use Ninja for Build and Install**
+
+ ```shell
+ cd build
+ ninja
+ sudo meson install
+ sudo ldconfig
+ ```
+
+
+
+
+
+ You can validate the successful install by looking for compiled binaries in the /build/app directory.
+
+
+
+
+
+## 2. Configure Hugepages
+
+HugePages were allocated to provide large, contiguous memory pages for DPDK. A total of 1024 HugePages (2 GB) were configured.
+
+```shell
+sudo sysctl -w vm.nr_hugepages=1024
+mount -t hugetlbfs none /dev/hugepages
+```
+
+> Note: With each system reboot you will need to configure Hugepages again, so it is recommended to make a bash file for this purpose.
+
+
+
+## 3. Create two TAP interfaces for DPDK's TAP Poll Mode Driver (PMD)
+
+Within the dpdk-/build directory, execute the testpmd application using the following command:
+
+```shell
+sudo LD_PRELOAD=/usr/lib/x86_64-linux-gnu/liblttng-ust-cyg-profile.so.1 ./app/dpdk-testpmd -l 0-1 -n 2 --vdev=net_tap0,iface=tap0 --vdev=net_tap1,iface=tap1 -- -i
+```
+
+Command breakdown:
+
+- `LD_PRELOAD=/usr/lib/x86_64-linux-gnu/liblttng-ust-cyg-profile.so.1`:
+ forces the DPDK application to load LTTng's function tracing library first, enabling detailed profiling of function calls for performance analysis. This allows tracking exact timing and frequency of every function call in DPDK (like packet processing functions) to identify bottlenecks.
+- `-l 0-1`: Assigns 2 cores
+- `-n 2 --vdev=net_tap0,iface=tap0 --vdev=net_tap1,iface=tap1`:
+ Creates 2 virtual devices named net_tap0 and net_tap1 with interfaces tap0 and tap1
+- `-i`: Starts in interactive mode
+
+You may run into some issues after running the code above depending on your kernel version because `liblttng-ust-cyg-profile.so.1` file my not be present. To determine which version is compatible with your system run the following command:
+
+```shell
+ls /usr/lib/x86_64-linux-gnu/ | grep liblttng-ust-cyg-profile.so
+```
+
+ You may see an output like this:
+
+
+
+After executing the testpmd command you should see the following output:
+
+
+## 4. Create Additional RX/TX Queues
+
+Now, we will add a new queue while operating in TAP mode. However, let us retrieve relevant configuration details using the `show port stats all` and `show config fwd` commands.
+
+
+
+By executing the command `show port stats all`, you will be able to view the statistics for all ports.
+
+
+
+By executing the command `show config fwd`, you will see the output above.
+Let us break it down:
+
+- `Forwarding Mode: io` -> This means packets received on a port are simply transmitted out through the corresponding transmit queue of another port.
+- `Ports: 2` -> Two physical or virtual ports are active.
+- `Cores: 1` -> One logical core (Core 5 in this case) is being used to process all forwarding tasks.
+- `Streams: 2` -> Two forwarding streams are configured, each representing a receive/transmit (RX/TX) path between ports.
+- `NUMA support: Enabled` -> The application is aware of NUMA (Non-Uniform Memory Access) node placement.
+- `MP Allocation Mode: native` -> Memory pools are allocated in native DPDK mode.
+
+
+
+For adding a new queue, we need to perform the following actions in testpmd:
+
+- Stoping all ports: `port stop all`
+- Creating new RX and TX queues: `port config all rxq 2` & `port config all txq 2`
+- Start the ports again: `port start all`
+
+
+
+After this step we can verify the added queues by running `show config fwd` once more:
+
+
+
+## 5. Setting Up a Flow Filtering Rule in testpmd
+
+You can direct specific traffic types to designated queues by creating a flow filtering rule in `testpmd` using the command below.
+
+```shell
+flow create 0 ingress pattern eth / ipv4 / udp / end actions queue index 0 / end
+```
+
+Upon successful execution, the output will look similar to this:
+
+
+
+This rule, applied to port 0, does the following:
+- Detects all incoming UDP traffic flowing over Ethernet and IPv4.
+- Directs the identified packets to RX queue 0.
+
+### Managing Active Rules
+
+To view active rules in `testpmd`, run:
+```shell
+flow list 0
+```
+- `0` is the port number.
+
+ The output will appear similar to this:
+
+ 
+
+### Removing a Filter
+
+To delete a filter in `testpmd`, use:
+```shell
+flow destroy 0 rule 0
+```
+- The first `0` is the port, the second `0` is the rule number.
+
+The output will appear similar to this:
+
+ 
+
+
+
+**Note:** In this scenario, we need the filter, so removal isn't necessary. However, if we need to trace traffic without filters and accidentally set one, this command can easily remove it.
+
+
+## 6. Getting Started with tcpreplay
+
+To begin, download the tcpreplay-4.5.1.tar.gz file from the TCP-Replay repository using this link: [TCP-Replay](https://github.com/appneta/tcpreplay/releases/tag/v4.5.1). This tool helps us send traffic to one of the interfaces we’ve created using testpmd. Once you have it, you’ll need to compile and install it on your system. After that, open a new terminal window and run these commands one by one:
+
+```shell
+./configure --disable-tuntap
+make
+sudo make install
+```
+
+**Important Tip:**
+You might run into issues compiling tcpreplay if you don’t have the right build tools, like make or automake. If that happens you can fix this by installing them with these commands:
+
+```shell
+sudo apt update
+sudo apt install build-essential automake
+```
+
+### What Does tcpreplay Do?
+tcpreplay replays captured network traffic (like pcap files) through a network interface to simulate real network activity. It's useful for simulating traffic scenarios.
+
+### Running tcpreplay
+After installing tcpreplay, you can use it to play a pcap file you’ve already created. Here’s the command to do that:
+
+```shell
+tcpreplay -i tap0 --loop=1000 ./Capture.pcap
+```
+
+- `-i tap0`: This tells tcpreplay to send the traffic through the `tap0` interface.
+- `--loop=1000`: This makes it repeat the pcap file 1000 times, which is great for testing under repeated conditions.
+- `./Capture.pcap`: This is the name of your pcap file that contains the captured traffic you want to replay.
+
+
+
+
+
+
+**Note:**
+If you don't have a `pcap` file to replay, you can capture network traffic using tools like `tcpdump` or `Wireshark`.
+
+### Capturing Traffic with tcpdump:
+
+To install `tcpdump` and capture network traffic, follow these steps:
+
+1. Install `tcpdump`:
+
+ ```shell
+ sudo apt update
+ sudo apt install tcpdump
+ ```
+
+2. Capture traffic from your main network interface :
+
+ ```shell
+ sudo tcpdump -i wlp3s0 -w Capture.pcap
+ ```
+
+ This command will capture traffic from wireless interface (`wlp3s0`) and save it in the `Capture.pcap` file. You can stop the capture by pressing `Ctrl+C` when you're done.
+
+### Capturing Traffic with Wireshark:
+
+Wireshark is a graphical tool for capturing and analyzing network traffic. You can download and install Wireshark from the official website here: [Wireshark Download](https://www.wireshark.org/).
+
+1. Install Wireshark:
+
+ ```shell
+ sudo apt update
+ sudo apt install wireshark
+ ```
+
+2. Launch Wireshark and start capturing on your desired network interface. Once you've captured the traffic, save it as a `pcap` file, and you can use that file with `tcpreplay`.
+
+
+
+
+After that, in the `testpmd` terminal, just type `start`, and then run `show port stats all` to see the traffic on the TAP interface.
+
+
+
+
+
+
+
+
+
+## 7. Setting Up an LTTng Trace Session
+
+To automate the LTTng capture process, you'll need to create a shell script that configures the LTTng session. This script will set up the session, enable the necessary context, start tracing, pause for a defined period, and then stop and clean up the session.
+
+1. First, create a new script file and make it executable:
+
+ ```shell
+ touch script.sh
+ chmod +x script.sh
+ nano script.sh
+ ```
+
+2. Now, paste the following commands into your script:
+
+ ```shell
+ #!/bin/bash
+ lttng create libpcap
+ lttng enable-channel --userspace --num-subbuf=4 --subbuf-size=40M channel0
+ #lttng enable-channel --userspace channel0
+ lttng enable-event --channel channel0 --userspace --all
+ lttng add-context --channel channel0 --userspace --type=vpid --type=vtid --type=procname
+ lttng start
+ sleep 2
+ lttng stop
+ lttng destroy
+ ```
+
+
+
+### Understanding the Script:
+
+
+1. **Initialize the LTTng Session:**
+
+ ```shell
+ lttng create libpcap
+ ```
+
+ * Creates a new trace session named `libpcap`.
+
+2. **Enable a Trace Channel:**
+
+ ```shell
+ lttng enable-channel --userspace --num-subbuf=4 --subbuf-size=40M channel0
+ ```
+
+ * Sets up a userspace channel (`channel0`) with 4 subbuffers, each 40MB in size, to store trace data.
+
+4. **Enable All Trace Events:**
+
+ ```shell
+ lttng enable-event --channel channel0 --userspace --all
+ ```
+
+ * Enables all userspace events on `channel0` to be traced.
+
+5. **Add Context Information:**
+
+ ```shell
+ lttng add-context --channel channel0 --userspace --type=vpid --type=vtid --type=procname
+ ```
+
+ * Adds context information such as the process ID (vpid), thread ID (vtid), and process name to the trace.
+
+6. **Start the Trace:**
+
+ ```shell
+ lttng start
+ ```
+
+ * Starts the tracing session.
+
+7. **Pause the Script:**
+
+ ```shell
+ sleep 2
+ ```
+
+ * Pauses the script for 2 seconds to allow some events to be captured.
+
+8. **Stop and Clean Up:**
+
+ ```shell
+ lttng stop
+ lttng destroy
+ ```
+
+ * Stops the trace and destroys the session, cleaning up any resources used.
+
+
+
+### Running the Script
+
+
+Once `tcpreplay` is running and generating traffic, you can execute the script by running the following command:
+
+```shell
+sudo ./script.sh
+```
+
+This will start the LTTng trace session, capturing the relevant events while `tcpreplay` continues to generate traffic.
+
+
+
+
+### Viewing the Trace Results
+
+After running the script, the trace results will be saved in the following directory:
+
+```
+/root/lttng-traces/
+```
+
+To analyze the results, you can open the trace with **Trace Compass**, a graphical tool for visualizing traces.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Tracing Analysis - UDP Filtering
+
+13 milion Events captured with 887 nanosecond average standard deviation. Optimization rule increased events captured by 11.0% and decreased average of standard deviation by 88.6%.
+
+## Alternative Density Shift
+
+Event density drops to zero when context switch happens for lcore 5 without regrads to the function that is being called, it should wait for another switch to load dpdk-worker5 application variables. It is worthy to point out pmd_rx_burst helper chain contains more events against pmd_tx_bursts' that will cause sigmoid dense shift while their call stack is conversed.
+
+> ▸ _As specified OS uses CFS (Completely Fair Scheduling) that works based on priority scheduling, it is more probable for some threads that are being ran with lower priority will experince more context switches (the reason of high standard deviation for several functions or huge self time/duration in flame graph) for instance: `rte_net_get_ptype`, `pmd_rx_burst`, `pmd_tx_burst`_
+
+---
+
+
+
+
+
+
+## Hot Code Paths
+
+| Function | ratio | Responsibility |
+| ------------------------------- |-------| ---------------------------------------- |
+| rte_net_get_ptype | 52.8% | Determine packet type by parsing headers |
+| rte_ethdev_trace_rx_burst_empty | 60.0% | Trace an empty RX burst (0 packets) |
+| pmd_rx_burst | 97.7% | Poll RX queue and fetch received packets |
+
+> ▸ _These paths can be found via functions with two layers upper towards `tap_trigger_cb` (can be called at last layer of call stack because of its event driven nature) that have bigger ratio of self time over duration than others._
+
+---
+
+
+
+
+
+## Practical mitigation options
+
+| Mitigation | Applicable Context | Implementation |
+| ------------------------------ | ------------------------- | -------------------------------------------------- |
+| Process priority surge | networking environments | nice() or setpriority() to change process priority |
+| CPU core isolation | deterministic performance | At boot time, isolate core via kernel parameters |
+| Pre-slice traffic in generator | Synthetic workload | Send UDP frames to a dedicated TAP. |
+
+> ▸ _The fourth core is dedicated for running app and 5 to 7 are in charge of forwarding packets, but only core 5 was used.This can be increased as described in [Intel Manual](https://www.bing.com/ck/a?!&&p=133bafcb2e934382baeacdda3640a23fc7aff520198bc1d12ebcd43320968603JmltdHM9MTc1MzkyMDAwMA&ptn=3&ver=2&hsh=4&fclid=1272d906-47db-60ab-2561-cd0446d26150&psq=dpdk+bad+througput&u=a1aHR0cHM6Ly93d3cuaW50ZWwuY29tL2NvbnRlbnQvZGFtL2RldmVsb3AvZXh0ZXJuYWwvdXMvZW4vZG9jdW1lbnRzL3Rlc3RpbmctZHBkay1wZXJmb3JtYW5jZS1hbmQtZmVhdHVyZXMtd2l0aC10ZXN0cG1kLTcyMTA5MC5wZGY&ntb=1)._
+
+---
+
+
+
+## Context Switch
+
+Another trace was done with both kernel and userspace involved to prove the idea that was discussed earlier.
+
+> ▸ _interrupt and followed context switches of core 5 causes delay in dpdk-worker5 functions and causes the thread to wait for CPU cycles._
+
+---
+
+
+
+
+
+## Throughput
+
+Each `tap_trigger_cb` means a frame is received from NIC (tap driver) causing `rte_pktmbuf_alloc`, `rte_net_get_ptype` subroutine activation per each frame. The worst case happens when tap trigger is called on dpdk/worker5 directly.
+
+> ▸ _Processing 8 packets took 176.3 microseconds (12 nanoseconds of wasted time between `pkt_burst_io_forward` calls is negligible) concluding application is capable of receiving and sending 5672 packets per second. Assuming mtu is set to 1500 as default the maximum forwarding would be 8.5 gigabyte per second. VPP makes great difference becuase of linear rate packet processing (larger packets mean higher throughput)._
+
+---
+
+
+
+
+
+## Receiving Helper Chain
+
+`pmd_rx_burst` is called whenever `tap_trigger_cb` interrupt occurs. Within one burst there could be different calls of `rte_pktmbuf_alloc` with `rte_net_get_ptype` followed which matches the number of frames received on tap driver.
+
+> ▸ _Flame chart includes `rte_constant_bswap16` calls inside ptype function but source code does not include this function. Actually they are called via `rte_cpu_to_be16`. This routine is neccessary becuase CPU byte order (little Endian as specified) differs from Network byte order._
+
+---
+
+
+
+
+
+## Flow Rule Impact
+
+Without flow rules set on testpmd, Events captured decreases by 24.2% (other settings as the same). Average standard deviation is 2.1 microseconds while average runtime of functions is 1.2 microseconds, proving less CPU cycle intensity as expected.
+
+> ▸ _Hot code paths are remaining the same with their almost previous ratio except for `pmd_rx_burst` with 73.8%. `tap_trigger_cb` is called 4291 times totally (refer to Descriptive Statistics) and 4100 times (refer to Weighted Tree Viewer new Callstack) is happened on 4 other threads of dpdk (testpmd, intr, mp-msg, telemet), meaning 4.4% of tap triggers happened in dpdk-worker which needs less context switches._
+
+---
+
+
+
+## Challenges and Solutions
+
+### 1. Missing Meson Module
+
+* **Issue**: Encountering No module named 'mesonbuild.machinefile' in running `meson install` after `ninja` command.
+
+* **Solution**: uninstall all meson versions that are installed with pip or apt and their relative libraries in usr/ folder, then install it with root priviledges (not as a superuser) by pip.
+
+ ```bash
+ sudo pip install meson
+ ```
+
+* **Outcome**: meson successfully installs the compiled artifacts.
+
+### 2. Empty Trace Folder
+
+* **Issue**: After running lttng script, the ust folder of trace is empty.
+* **Solution**: Clean install lttng stable ppa version with [LTTng Stable 2.13 PPA installation Guide](https://lttng.org/docs/v2.13/#doc-ubuntu-ppa)
+* **Outcome**: Trace has captured preloaded fuctions.
+
+### 3. Null Trace Compass Views
+
+* **Issue**: Call stack views ( Flame Graph & Flame Graph Selection) are empty.
+
+* **Solution**: install java-21 and set as deafult version of java.
+
+ ```bash
+ sudo apt install openjdk-21-jdk -y
+ ```
+
+* **Outcome**: Call stack gets visible (still needs sometime based on host ram and CPU).
+
+### 4. Incomplete Symbol Mapping
+
+* **Issue**: some functions are available only by their address not their names.
+* **Solution**: click on symbol mapping button shown below (available for flame chart, flame graph, ...) then check `Use custom target root directory` without choosing any directory and press ok.
+
+ 
+
+* **Outcome**: Function names will be loaded completely for each window that is opened afterwards.
diff --git a/gro/GRO Implementation Files/gro_gtp_tcp4.c b/gro/GRO Implementation Files/gro_gtp_tcp4.c
new file mode 100644
index 0000000..499bd00
--- /dev/null
+++ b/gro/GRO Implementation Files/gro_gtp_tcp4.c
@@ -0,0 +1,501 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+* Copyright(c) 2018 Intel Corporation
+*/
+
+#include
+#include
+#include
+#include
+
+#include "gro_gtp_tcp4.h"
+
+void *
+gro_gtp_tcp4_tbl_create(uint16_t socket_id,
+ uint16_t max_flow_num,
+ uint16_t max_item_per_flow)
+{
+ struct gro_gtp_tcp4_tbl *tbl;
+ size_t size;
+ uint32_t entries_num, i;
+
+ entries_num = max_flow_num * max_item_per_flow;
+ entries_num = RTE_MIN(entries_num, GRO_GTP_TCP4_TBL_MAX_ITEM_NUM);
+
+ if (entries_num == 0)
+ return NULL;
+
+ tbl = rte_zmalloc_socket(__func__,
+ sizeof(struct gro_gtp_tcp4_tbl),
+ RTE_CACHE_LINE_SIZE,
+ socket_id);
+ if (tbl == NULL)
+ return NULL;
+
+ size = sizeof(struct gro_gtp_tcp4_item) * entries_num;
+ tbl->items = rte_zmalloc_socket(__func__,
+ size,
+ RTE_CACHE_LINE_SIZE,
+ socket_id);
+ if (tbl->items == NULL) {
+ rte_free(tbl);
+ return NULL;
+ }
+ tbl->max_item_num = entries_num;
+
+ size = sizeof(struct gro_gtp_tcp4_flow) * entries_num;
+ tbl->flows = rte_zmalloc_socket(__func__,
+ size,
+ RTE_CACHE_LINE_SIZE,
+ socket_id);
+ if (tbl->flows == NULL) {
+ rte_free(tbl->items);
+ rte_free(tbl);
+ return NULL;
+ }
+
+ for (i = 0; i < entries_num; i++)
+ tbl->flows[i].start_index = INVALID_ARRAY_INDEX;
+ tbl->max_flow_num = entries_num;
+
+ return tbl;
+}
+
+void
+gro_gtp_tcp4_tbl_destroy(void *tbl)
+{
+ struct gro_gtp_tcp4_tbl *gtp_tbl = tbl;
+
+ if (gtp_tbl) {
+ rte_free(gtp_tbl->items);
+ rte_free(gtp_tbl->flows);
+ }
+ rte_free(gtp_tbl);
+}
+
+static inline uint32_t
+find_an_empty_item(struct gro_gtp_tcp4_tbl *tbl)
+{
+ uint32_t max_item_num = tbl->max_item_num, i;
+
+ for (i = 0; i < max_item_num; i++)
+ if (tbl->items[i].inner_item.firstseg == NULL)
+ return i;
+ return INVALID_ARRAY_INDEX;
+}
+
+static inline uint32_t
+find_an_empty_flow(struct gro_gtp_tcp4_tbl *tbl)
+{
+ uint32_t max_flow_num = tbl->max_flow_num, i;
+
+ for (i = 0; i < max_flow_num; i++)
+ if (tbl->flows[i].start_index == INVALID_ARRAY_INDEX)
+ return i;
+ return INVALID_ARRAY_INDEX;
+}
+
+static inline uint32_t
+insert_new_item(struct gro_gtp_tcp4_tbl *tbl,
+ struct rte_mbuf *pkt,
+ uint64_t start_time,
+ uint32_t prev_idx,
+ uint32_t sent_seq,
+ uint16_t outer_ip_id,
+ uint16_t ip_id,
+ uint8_t outer_is_atomic,
+ uint8_t is_atomic)
+{
+ uint32_t item_idx;
+
+ item_idx = find_an_empty_item(tbl);
+ if (unlikely(item_idx == INVALID_ARRAY_INDEX))
+ return INVALID_ARRAY_INDEX;
+
+ tbl->items[item_idx].inner_item.firstseg = pkt;
+ tbl->items[item_idx].inner_item.lastseg = rte_pktmbuf_lastseg(pkt);
+ tbl->items[item_idx].inner_item.start_time = start_time;
+ tbl->items[item_idx].inner_item.next_pkt_idx = INVALID_ARRAY_INDEX;
+ tbl->items[item_idx].inner_item.sent_seq = sent_seq;
+ tbl->items[item_idx].inner_item.l3.ip_id = ip_id;
+ tbl->items[item_idx].inner_item.nb_merged = 1;
+ tbl->items[item_idx].inner_item.is_atomic = is_atomic;
+ tbl->items[item_idx].outer_ip_id = outer_ip_id;
+ tbl->items[item_idx].outer_is_atomic = outer_is_atomic;
+ tbl->item_num++;
+
+ /* If the previous packet exists, chain the new one with it. */
+ if (prev_idx != INVALID_ARRAY_INDEX) {
+ tbl->items[item_idx].inner_item.next_pkt_idx =
+ tbl->items[prev_idx].inner_item.next_pkt_idx;
+ tbl->items[prev_idx].inner_item.next_pkt_idx = item_idx;
+ }
+
+ return item_idx;
+}
+
+static inline uint32_t
+delete_item(struct gro_gtp_tcp4_tbl *tbl,
+ uint32_t item_idx,
+ uint32_t prev_item_idx)
+{
+ uint32_t next_idx = tbl->items[item_idx].inner_item.next_pkt_idx;
+
+ /* NULL indicates an empty item. */
+ tbl->items[item_idx].inner_item.firstseg = NULL;
+ tbl->item_num--;
+ if (prev_item_idx != INVALID_ARRAY_INDEX)
+ tbl->items[prev_item_idx].inner_item.next_pkt_idx = next_idx;
+
+ return next_idx;
+}
+
+static inline uint32_t
+insert_new_flow(struct gro_gtp_tcp4_tbl *tbl,
+ struct gtp_tcp4_flow_key *src,
+ uint32_t item_idx)
+{
+ struct gtp_tcp4_flow_key *dst;
+ uint32_t flow_idx;
+
+ flow_idx = find_an_empty_flow(tbl);
+ if (unlikely(flow_idx == INVALID_ARRAY_INDEX))
+ return INVALID_ARRAY_INDEX;
+
+ dst = &(tbl->flows[flow_idx].key);
+
+ ASSIGN_COMMON_TCP_KEY((&(src->inner_key.cmn_key)), (&(dst->inner_key.cmn_key)));
+ dst->inner_key.ip_src_addr = src->inner_key.ip_src_addr;
+ dst->inner_key.ip_dst_addr = src->inner_key.ip_dst_addr;
+
+ // dst->vxlan_hdr.vx_flags = src->vxlan_hdr.vx_flags;
+ // dst->vxlan_hdr.vx_vni = src->vxlan_hdr.vx_vni;
+ dst->gtp_hdr.teid = src->gtp_hdr.teid;
+ rte_ether_addr_copy(&(src->outer_eth_saddr), &(dst->outer_eth_saddr));
+ rte_ether_addr_copy(&(src->outer_eth_daddr), &(dst->outer_eth_daddr));
+ dst->outer_ip_src_addr = src->outer_ip_src_addr;
+ dst->outer_ip_dst_addr = src->outer_ip_dst_addr;
+ dst->outer_src_port = src->outer_src_port;
+ dst->outer_dst_port = src->outer_dst_port;
+
+ tbl->flows[flow_idx].start_index = item_idx;
+ tbl->flow_num++;
+
+ return flow_idx;
+}
+
+static inline int
+is_same_gtp_tcp4_flow(struct gtp_tcp4_flow_key k1,
+ struct gtp_tcp4_flow_key k2)
+{
+ return (rte_is_same_ether_addr(&k1.outer_eth_saddr,
+ &k2.outer_eth_saddr) &&
+ rte_is_same_ether_addr(&k1.outer_eth_daddr,
+ &k2.outer_eth_daddr) &&
+ (k1.outer_ip_src_addr == k2.outer_ip_src_addr) &&
+ (k1.outer_ip_dst_addr == k2.outer_ip_dst_addr) &&
+ (k1.outer_src_port == k2.outer_src_port) &&
+ (k1.outer_dst_port == k2.outer_dst_port) &&
+ // (k1.vxlan_hdr.vx_flags == k2.vxlan_hdr.vx_flags) &&
+ // (k1.vxlan_hdr.vx_vni == k2.vxlan_hdr.vx_vni) &&
+ (k1.gtp_hdr.teid == k2.gtp_hdr.teid) &&
+ is_same_tcp4_flow(k1.inner_key, k2.inner_key));
+}
+
+static inline int
+check_gtp_seq_option(struct gro_gtp_tcp4_item *item,
+ struct rte_tcp_hdr *tcp_hdr,
+ uint32_t sent_seq,
+ uint16_t outer_ip_id,
+ uint16_t ip_id,
+ uint16_t tcp_hl,
+ uint16_t tcp_dl,
+ uint8_t outer_is_atomic,
+ uint8_t is_atomic)
+{
+ struct rte_mbuf *pkt = item->inner_item.firstseg;
+ int cmp;
+ uint16_t l2_offset;
+
+ /* Don't merge packets whose outer DF bits are different. */
+ if (unlikely(item->outer_is_atomic ^ outer_is_atomic))
+ return 0;
+
+ l2_offset = pkt->outer_l2_len + pkt->outer_l3_len;
+ cmp = check_seq_option(&item->inner_item, tcp_hdr, sent_seq, ip_id,
+ tcp_hl, tcp_dl, l2_offset, is_atomic);
+ if ((cmp > 0) && (outer_is_atomic ||
+ (outer_ip_id == item->outer_ip_id + 1)))
+ /* Append the new packet. */
+ return 1;
+ else if ((cmp < 0) && (outer_is_atomic ||
+ (outer_ip_id + item->inner_item.nb_merged ==
+ item->outer_ip_id)))
+ /* Prepend the new packet. */
+ return -1;
+
+ return 0;
+}
+
+static inline int
+merge_two_gtp_tcp4_packets(struct gro_gtp_tcp4_item *item,
+ struct rte_mbuf *pkt,
+ int cmp,
+ uint32_t sent_seq,
+ uint8_t tcp_flags,
+ uint16_t outer_ip_id,
+ uint16_t ip_id)
+{
+ if (merge_two_tcp_packets(&item->inner_item, pkt, cmp, sent_seq, tcp_flags,
+ ip_id, pkt->outer_l2_len +
+ pkt->outer_l3_len)) {
+ /* Update the outer IPv4 ID to the large value. */
+ item->outer_ip_id = cmp > 0 ? outer_ip_id : item->outer_ip_id;
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline void
+update_gtp_header(struct gro_gtp_tcp4_item *item)
+{
+ struct rte_ipv4_hdr *ipv4_hdr;
+ struct rte_udp_hdr *udp_hdr;
+ struct rte_mbuf *pkt = item->inner_item.firstseg;
+ uint16_t len;
+
+ /* Update the outer IPv4 header. */
+ len = pkt->pkt_len - pkt->outer_l2_len;
+ ipv4_hdr = rte_pktmbuf_mtod_offset(pkt, struct rte_ipv4_hdr *,
+ pkt->outer_l2_len);
+ ipv4_hdr->total_length = rte_cpu_to_be_16(len);
+
+ /* Update the outer UDP header. */
+ len -= pkt->outer_l3_len;
+ udp_hdr = (struct rte_udp_hdr *)((char *)ipv4_hdr + pkt->outer_l3_len);
+ udp_hdr->dgram_len = rte_cpu_to_be_16(len);
+
+ /* Update the inner IPv4 header. */
+ len -= pkt->l2_len;
+ ipv4_hdr = (struct rte_ipv4_hdr *)((char *)udp_hdr + pkt->l2_len);
+ ipv4_hdr->total_length = rte_cpu_to_be_16(len);
+}
+
+int32_t
+gro_gtp_tcp4_reassemble(struct rte_mbuf *pkt,
+ struct gro_gtp_tcp4_tbl *tbl,
+ uint64_t start_time)
+{
+ struct rte_ether_hdr *outer_eth_hdr, *eth_hdr;
+ struct rte_ipv4_hdr *outer_ipv4_hdr, *ipv4_hdr;
+ struct rte_tcp_hdr *tcp_hdr;
+ struct rte_udp_hdr *udp_hdr;
+ struct rte_gtp_hdr *gtp_hdr;
+ uint32_t sent_seq;
+ int32_t tcp_dl;
+ uint16_t frag_off, outer_ip_id, ip_id;
+ uint8_t outer_is_atomic, is_atomic;
+
+ struct gtp_tcp4_flow_key key;
+ uint32_t cur_idx, prev_idx, item_idx;
+ uint32_t i, max_flow_num, remaining_flow_num;
+ int cmp;
+ uint16_t hdr_len;
+ uint8_t find;
+
+ /*
+ * Don't process the packet whose TCP header length is greater
+ * than 60 bytes or less than 20 bytes.
+ */
+ if (unlikely(INVALID_TCP_HDRLEN(pkt->l4_len)))
+ return -1;
+
+ outer_eth_hdr = rte_pktmbuf_mtod(pkt, struct rte_ether_hdr *);
+ outer_ipv4_hdr = (struct rte_ipv4_hdr *)((char *)outer_eth_hdr +
+ pkt->outer_l2_len);
+ udp_hdr = (struct rte_udp_hdr *)((char *)outer_ipv4_hdr +
+ pkt->outer_l3_len);
+ gtp_hdr = (struct rte_gtp_hdr *)((char *)udp_hdr +
+ sizeof(struct rte_udp_hdr));
+ eth_hdr = (struct rte_ether_hdr *)((char *)gtp_hdr +
+ sizeof(struct rte_gtp_hdr));
+ ipv4_hdr = (struct rte_ipv4_hdr *)((char *)udp_hdr + pkt->l2_len);
+ tcp_hdr = (struct rte_tcp_hdr *)((char *)ipv4_hdr + pkt->l3_len);
+
+ /*
+ * Don't process the packet which has FIN, SYN, RST, PSH, URG,
+ * ECE or CWR set.
+ */
+ if (tcp_hdr->tcp_flags != RTE_TCP_ACK_FLAG)
+ return -1;
+
+ hdr_len = pkt->outer_l2_len + pkt->outer_l3_len + pkt->l2_len +
+ pkt->l3_len + pkt->l4_len;
+ /*
+ * Don't process the packet whose payload length is less than or
+ * equal to 0.
+ */
+ tcp_dl = pkt->pkt_len - hdr_len;
+ if (tcp_dl <= 0)
+ return -1;
+
+ /*
+ * Save IPv4 ID for the packet whose DF bit is 0. For the packet
+ * whose DF bit is 1, IPv4 ID is ignored.
+ */
+ frag_off = rte_be_to_cpu_16(outer_ipv4_hdr->fragment_offset);
+ outer_is_atomic =
+ (frag_off & RTE_IPV4_HDR_DF_FLAG) == RTE_IPV4_HDR_DF_FLAG;
+ outer_ip_id = outer_is_atomic ? 0 :
+ rte_be_to_cpu_16(outer_ipv4_hdr->packet_id);
+ frag_off = rte_be_to_cpu_16(ipv4_hdr->fragment_offset);
+ is_atomic = (frag_off & RTE_IPV4_HDR_DF_FLAG) == RTE_IPV4_HDR_DF_FLAG;
+ ip_id = is_atomic ? 0 : rte_be_to_cpu_16(ipv4_hdr->packet_id);
+
+ sent_seq = rte_be_to_cpu_32(tcp_hdr->sent_seq);
+
+ rte_ether_addr_copy(&(eth_hdr->src_addr), &(key.inner_key.cmn_key.eth_saddr));
+ rte_ether_addr_copy(&(eth_hdr->dst_addr), &(key.inner_key.cmn_key.eth_daddr));
+ key.inner_key.ip_src_addr = ipv4_hdr->src_addr;
+ key.inner_key.ip_dst_addr = ipv4_hdr->dst_addr;
+ key.inner_key.cmn_key.recv_ack = tcp_hdr->recv_ack;
+ key.inner_key.cmn_key.src_port = tcp_hdr->src_port;
+ key.inner_key.cmn_key.dst_port = tcp_hdr->dst_port;
+
+ // key.vxlan_hdr.vx_flags = vxlan_hdr->vx_flags;
+ // key.vxlan_hdr.vx_vni = vxlan_hdr->vx_vni;
+ key.gtp_hdr.teid = gtp_hdr->teid;
+ rte_ether_addr_copy(&(outer_eth_hdr->src_addr), &(key.outer_eth_saddr));
+ rte_ether_addr_copy(&(outer_eth_hdr->dst_addr), &(key.outer_eth_daddr));
+ key.outer_ip_src_addr = outer_ipv4_hdr->src_addr;
+ key.outer_ip_dst_addr = outer_ipv4_hdr->dst_addr;
+ key.outer_src_port = udp_hdr->src_port;
+ key.outer_dst_port = udp_hdr->dst_port;
+
+ /* Search for a matched flow. */
+ max_flow_num = tbl->max_flow_num;
+ remaining_flow_num = tbl->flow_num;
+ find = 0;
+ for (i = 0; i < max_flow_num && remaining_flow_num; i++) {
+ if (tbl->flows[i].start_index != INVALID_ARRAY_INDEX) {
+ if (is_same_gtp_tcp4_flow(tbl->flows[i].key, key)) {
+ find = 1;
+ break;
+ }
+ remaining_flow_num--;
+ }
+ }
+
+ /*
+ * Can't find a matched flow. Insert a new flow and store the
+ * packet into the flow.
+ */
+ if (find == 0) {
+ item_idx = insert_new_item(tbl, pkt, start_time,
+ INVALID_ARRAY_INDEX, sent_seq, outer_ip_id,
+ ip_id, outer_is_atomic, is_atomic);
+ if (item_idx == INVALID_ARRAY_INDEX)
+ return -1;
+ if (insert_new_flow(tbl, &key, item_idx) ==
+ INVALID_ARRAY_INDEX) {
+ /*
+ * Fail to insert a new flow, so
+ * delete the inserted packet.
+ */
+ delete_item(tbl, item_idx, INVALID_ARRAY_INDEX);
+ return -1;
+ }
+ return 0;
+ }
+
+ /* Check all packets in the flow and try to find a neighbor. */
+ cur_idx = tbl->flows[i].start_index;
+ prev_idx = cur_idx;
+ do {
+ cmp = check_gtp_seq_option(&(tbl->items[cur_idx]), tcp_hdr,
+ sent_seq, outer_ip_id, ip_id, pkt->l4_len,
+ tcp_dl, outer_is_atomic, is_atomic);
+ if (cmp) {
+ if (merge_two_gtp_tcp4_packets(&(tbl->items[cur_idx]),
+ pkt, cmp, sent_seq, tcp_hdr->tcp_flags,
+ outer_ip_id, ip_id))
+ return 1;
+ /*
+ * Can't merge two packets, as the packet
+ * length will be greater than the max value.
+ * Insert the packet into the flow.
+ */
+ if (insert_new_item(tbl, pkt, start_time, prev_idx,
+ sent_seq, outer_ip_id,
+ ip_id, outer_is_atomic,
+ is_atomic) ==
+ INVALID_ARRAY_INDEX)
+ return -1;
+ return 0;
+ }
+ prev_idx = cur_idx;
+ cur_idx = tbl->items[cur_idx].inner_item.next_pkt_idx;
+ } while (cur_idx != INVALID_ARRAY_INDEX);
+
+ /* Can't find neighbor. Insert the packet into the flow. */
+ if (insert_new_item(tbl, pkt, start_time, prev_idx, sent_seq,
+ outer_ip_id, ip_id, outer_is_atomic,
+ is_atomic) == INVALID_ARRAY_INDEX)
+ return -1;
+
+ return 0;
+}
+
+uint16_t
+gro_gtp_tcp4_tbl_timeout_flush(struct gro_gtp_tcp4_tbl *tbl,
+ uint64_t flush_timestamp,
+ struct rte_mbuf **out,
+ uint16_t nb_out)
+{
+ uint16_t k = 0;
+ uint32_t i, j;
+ uint32_t max_flow_num = tbl->max_flow_num;
+
+ for (i = 0; i < max_flow_num; i++) {
+ if (unlikely(tbl->flow_num == 0))
+ return k;
+
+ j = tbl->flows[i].start_index;
+ while (j != INVALID_ARRAY_INDEX) {
+ if (tbl->items[j].inner_item.start_time <=
+ flush_timestamp) {
+ out[k++] = tbl->items[j].inner_item.firstseg;
+ if (tbl->items[j].inner_item.nb_merged > 1)
+ update_gtp_header(&(tbl->items[j]));
+ /*
+ * Delete the item and get the next packet
+ * index.
+ */
+ j = delete_item(tbl, j, INVALID_ARRAY_INDEX);
+ tbl->flows[i].start_index = j;
+ if (j == INVALID_ARRAY_INDEX)
+ tbl->flow_num--;
+
+ if (unlikely(k == nb_out))
+ return k;
+ } else
+ /*
+ * The left packets in the flow won't be
+ * timeout. Go to check other flows.
+ */
+ break;
+ }
+ }
+ return k;
+}
+
+uint32_t
+gro_gtp_tcp4_tbl_pkt_count(void *tbl)
+{
+ struct gro_gtp_tcp4_tbl *gro_tbl = tbl;
+
+ if (gro_tbl)
+ return gro_tbl->item_num;
+
+ return 0;
+}
diff --git a/gro/GRO Implementation Files/gro_gtp_tcp4.h b/gro/GRO Implementation Files/gro_gtp_tcp4.h
new file mode 100644
index 0000000..5e4e943
--- /dev/null
+++ b/gro/GRO Implementation Files/gro_gtp_tcp4.h
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+
+ #ifndef _GRO_GTP_TCP4_H_
+ #define _GRO_GTP_TCP4_H_
+
+ #include "gro_tcp4.h"
+
+ #define GRO_GTP_TCP4_TBL_MAX_ITEM_NUM (1024UL * 1024UL)
+
+ /* Header fields representing a GTP flow */
+ struct gtp_tcp4_flow_key {
+ struct tcp4_flow_key inner_key;
+ struct rte_gtp_hdr gtp_hdr;
+
+ struct rte_ether_addr outer_eth_saddr;
+ struct rte_ether_addr outer_eth_daddr;
+
+ uint32_t outer_ip_src_addr;
+ uint32_t outer_ip_dst_addr;
+
+ /* Outer UDP ports */
+ uint16_t outer_src_port;
+ uint16_t outer_dst_port;
+
+ };
+
+ struct gro_gtp_tcp4_flow {
+ struct gtp_tcp4_flow_key key;
+ /*
+ * The index of the first packet in the flow. INVALID_ARRAY_INDEX
+ * indicates an empty flow.
+ */
+ uint32_t start_index;
+ };
+
+ struct gro_gtp_tcp4_item {
+ struct gro_tcp_item inner_item;
+ /* IPv4 ID in the outer IPv4 header */
+ uint16_t outer_ip_id;
+ /* Indicate if outer IPv4 ID can be ignored */
+ uint8_t outer_is_atomic;
+ };
+
+ /*
+ * VxLAN (with an outer IPv4 header and an inner TCP/IPv4 packet)
+ * reassembly table structure
+ */
+ struct gro_gtp_tcp4_tbl {
+ /* item array */
+ struct gro_gtp_tcp4_item *items;
+ /* flow array */
+ struct gro_gtp_tcp4_flow *flows;
+ /* current item number */
+ uint32_t item_num;
+ /* current flow number */
+ uint32_t flow_num;
+ /* the maximum item number */
+ uint32_t max_item_num;
+ /* the maximum flow number */
+ uint32_t max_flow_num;
+ };
+
+ /**
+ * This function creates a VxLAN reassembly table for VxLAN packets
+ * which have an outer IPv4 header and an inner TCP/IPv4 packet.
+ *
+ * @param socket_id
+ * Socket index for allocating the table
+ * @param max_flow_num
+ * The maximum number of flows in the table
+ * @param max_item_per_flow
+ * The maximum number of packets per flow
+ *
+ * @return
+ * - Return the table pointer on success.
+ * - Return NULL on failure.
+ */
+ void *gro_gtp_tcp4_tbl_create(uint16_t socket_id,
+ uint16_t max_flow_num,
+ uint16_t max_item_per_flow);
+
+ /**
+ * This function destroys a VxLAN reassembly table.
+ *
+ * @param tbl
+ * Pointer pointing to the VxLAN reassembly table
+ */
+ void gro_gtp_tcp4_tbl_destroy(void *tbl);
+
+ /**
+ * This function merges a VxLAN packet which has an outer IPv4 header and
+ * an inner TCP/IPv4 packet. It doesn't process the packet, whose TCP
+ * header has SYN, FIN, RST, PSH, CWR, ECE or URG bit set, or which
+ * doesn't have payload.
+ *
+ * This function doesn't check if the packet has correct checksums and
+ * doesn't re-calculate checksums for the merged packet. Additionally,
+ * it assumes the packets are complete (i.e., MF==0 && frag_off==0), when
+ * IP fragmentation is possible (i.e., DF==0). It returns the packet, if
+ * the packet has invalid parameters (e.g. SYN bit is set) or there is no
+ * available space in the table.
+ *
+ * @param pkt
+ * Packet to reassemble
+ * @param tbl
+ * Pointer pointing to the VxLAN reassembly table
+ * @start_time
+ * The time when the packet is inserted into the table
+ *
+ * @return
+ * - Return a positive value if the packet is merged.
+ * - Return zero if the packet isn't merged but stored in the table.
+ * - Return a negative value for invalid parameters or no available
+ * space in the table.
+ */
+ int32_t gro_gtp_tcp4_reassemble(struct rte_mbuf *pkt,
+ struct gro_gtp_tcp4_tbl *tbl,
+ uint64_t start_time);
+
+ /**
+ * This function flushes timeout packets in the VxLAN reassembly table,
+ * and without updating checksums.
+ *
+ * @param tbl
+ * Pointer pointing to a VxLAN GRO table
+ * @param flush_timestamp
+ * This function flushes packets which are inserted into the table
+ * before or at the flush_timestamp.
+ * @param out
+ * Pointer array used to keep flushed packets
+ * @param nb_out
+ * The element number in 'out'. It also determines the maximum number of
+ * packets that can be flushed finally.
+ *
+ * @return
+ * The number of flushed packets
+ */
+ uint16_t gro_gtp_tcp4_tbl_timeout_flush(struct gro_gtp_tcp4_tbl *tbl,
+ uint64_t flush_timestamp,
+ struct rte_mbuf **out,
+ uint16_t nb_out);
+
+ /**
+ * This function returns the number of the packets in a VxLAN
+ * reassembly table.
+ *
+ * @param tbl
+ * Pointer pointing to the VxLAN reassembly table
+ *
+ * @return
+ * The number of packets in the table
+ */
+ uint32_t gro_gtp_tcp4_tbl_pkt_count(void *tbl);
+ #endif
+
\ No newline at end of file
diff --git a/gro/GRO Implementation Files/gro_gtp_udp4.c b/gro/GRO Implementation Files/gro_gtp_udp4.c
new file mode 100644
index 0000000..8e1097a
--- /dev/null
+++ b/gro/GRO Implementation Files/gro_gtp_udp4.c
@@ -0,0 +1,547 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+* Copyright(c) 2020 Inspur Corporation
+*/
+
+#include
+#include
+#include
+#include
+
+#include "gro_gtp_udp4.h"
+
+void *
+gro_gtp_udp4_tbl_create(uint16_t socket_id,
+ uint16_t max_flow_num,
+ uint16_t max_item_per_flow)
+{
+ struct gro_gtp_udp4_tbl *tbl;
+ size_t size;
+ uint32_t entries_num, i;
+
+ entries_num = max_flow_num * max_item_per_flow;
+ entries_num = RTE_MIN(entries_num, GRO_GTP_UDP4_TBL_MAX_ITEM_NUM);
+
+ if (entries_num == 0)
+ return NULL;
+
+ tbl = rte_zmalloc_socket(__func__,
+ sizeof(struct gro_gtp_udp4_tbl),
+ RTE_CACHE_LINE_SIZE,
+ socket_id);
+ if (tbl == NULL)
+ return NULL;
+
+ size = sizeof(struct gro_gtp_udp4_item) * entries_num;
+ tbl->items = rte_zmalloc_socket(__func__,
+ size,
+ RTE_CACHE_LINE_SIZE,
+ socket_id);
+ if (tbl->items == NULL) {
+ rte_free(tbl);
+ return NULL;
+ }
+ tbl->max_item_num = entries_num;
+
+ size = sizeof(struct gro_gtp_udp4_flow) * entries_num;
+ tbl->flows = rte_zmalloc_socket(__func__,
+ size,
+ RTE_CACHE_LINE_SIZE,
+ socket_id);
+ if (tbl->flows == NULL) {
+ rte_free(tbl->items);
+ rte_free(tbl);
+ return NULL;
+ }
+
+ for (i = 0; i < entries_num; i++)
+ tbl->flows[i].start_index = INVALID_ARRAY_INDEX;
+ tbl->max_flow_num = entries_num;
+
+ return tbl;
+}
+
+void
+gro_gtp_udp4_tbl_destroy(void *tbl)
+{
+ struct gro_gtp_udp4_tbl *gtp_tbl = tbl;
+
+ if (gtp_tbl) {
+ rte_free(gtp_tbl->items);
+ rte_free(gtp_tbl->flows);
+ }
+ rte_free(gtp_tbl);
+}
+
+static inline uint32_t
+find_an_empty_item(struct gro_gtp_udp4_tbl *tbl)
+{
+ uint32_t max_item_num = tbl->max_item_num, i;
+
+ for (i = 0; i < max_item_num; i++)
+ if (tbl->items[i].inner_item.firstseg == NULL)
+ return i;
+ return INVALID_ARRAY_INDEX;
+}
+
+static inline uint32_t
+find_an_empty_flow(struct gro_gtp_udp4_tbl *tbl)
+{
+ uint32_t max_flow_num = tbl->max_flow_num, i;
+
+ for (i = 0; i < max_flow_num; i++)
+ if (tbl->flows[i].start_index == INVALID_ARRAY_INDEX)
+ return i;
+ return INVALID_ARRAY_INDEX;
+}
+
+static inline uint32_t
+insert_new_item(struct gro_gtp_udp4_tbl *tbl,
+ struct rte_mbuf *pkt,
+ uint64_t start_time,
+ uint32_t prev_idx,
+ uint16_t frag_offset,
+ uint8_t is_last_frag)
+{
+ uint32_t item_idx;
+
+ item_idx = find_an_empty_item(tbl);
+ if (unlikely(item_idx == INVALID_ARRAY_INDEX))
+ return INVALID_ARRAY_INDEX;
+
+ tbl->items[item_idx].inner_item.firstseg = pkt;
+ tbl->items[item_idx].inner_item.lastseg = rte_pktmbuf_lastseg(pkt);
+ tbl->items[item_idx].inner_item.start_time = start_time;
+ tbl->items[item_idx].inner_item.next_pkt_idx = INVALID_ARRAY_INDEX;
+ tbl->items[item_idx].inner_item.frag_offset = frag_offset;
+ tbl->items[item_idx].inner_item.is_last_frag = is_last_frag;
+ tbl->items[item_idx].inner_item.nb_merged = 1;
+ tbl->item_num++;
+
+ /* If the previous packet exists, chain the new one with it. */
+ if (prev_idx != INVALID_ARRAY_INDEX) {
+ tbl->items[item_idx].inner_item.next_pkt_idx =
+ tbl->items[prev_idx].inner_item.next_pkt_idx;
+ tbl->items[prev_idx].inner_item.next_pkt_idx = item_idx;
+ }
+
+ return item_idx;
+}
+
+static inline uint32_t
+delete_item(struct gro_gtp_udp4_tbl *tbl,
+ uint32_t item_idx,
+ uint32_t prev_item_idx)
+{
+ uint32_t next_idx = tbl->items[item_idx].inner_item.next_pkt_idx;
+
+ /* NULL indicates an empty item. */
+ tbl->items[item_idx].inner_item.firstseg = NULL;
+ tbl->item_num--;
+ if (prev_item_idx != INVALID_ARRAY_INDEX)
+ tbl->items[prev_item_idx].inner_item.next_pkt_idx = next_idx;
+
+ return next_idx;
+}
+
+static inline uint32_t
+insert_new_flow(struct gro_gtp_udp4_tbl *tbl,
+ struct gtp_udp4_flow_key *src,
+ uint32_t item_idx)
+{
+ struct gtp_udp4_flow_key *dst;
+ uint32_t flow_idx;
+
+ flow_idx = find_an_empty_flow(tbl);
+ if (unlikely(flow_idx == INVALID_ARRAY_INDEX))
+ return INVALID_ARRAY_INDEX;
+
+ dst = &(tbl->flows[flow_idx].key);
+
+ rte_ether_addr_copy(&(src->inner_key.eth_saddr),
+ &(dst->inner_key.eth_saddr));
+ rte_ether_addr_copy(&(src->inner_key.eth_daddr),
+ &(dst->inner_key.eth_daddr));
+ dst->inner_key.ip_src_addr = src->inner_key.ip_src_addr;
+ dst->inner_key.ip_dst_addr = src->inner_key.ip_dst_addr;
+ dst->inner_key.ip_id = src->inner_key.ip_id;
+
+ // dst->vxlan_hdr.vx_flags = src->vxlan_hdr.vx_flags;
+ // dst->vxlan_hdr.vx_vni = src->vxlan_hdr.vx_vni;
+ dst->gtp_hdr.teid = src->gtp_hdr.teid;
+ rte_ether_addr_copy(&(src->outer_eth_saddr), &(dst->outer_eth_saddr));
+ rte_ether_addr_copy(&(src->outer_eth_daddr), &(dst->outer_eth_daddr));
+ dst->outer_ip_src_addr = src->outer_ip_src_addr;
+ dst->outer_ip_dst_addr = src->outer_ip_dst_addr;
+ dst->outer_dst_port = src->outer_dst_port;
+
+ tbl->flows[flow_idx].start_index = item_idx;
+ tbl->flow_num++;
+
+ return flow_idx;
+}
+
+static inline int
+is_same_gtp_udp4_flow(struct gtp_udp4_flow_key k1,
+ struct gtp_udp4_flow_key k2)
+{
+ /* For GTP packet, outer udp src port is calculated from
+ * inner packet RSS hash, udp src port of the first UDP
+ * fragment is different from one of other UDP fragments
+ * even if they are same flow, so we have to skip outer udp
+ * src port comparison here.
+ */
+ return (rte_is_same_ether_addr(&k1.outer_eth_saddr,
+ &k2.outer_eth_saddr) &&
+ rte_is_same_ether_addr(&k1.outer_eth_daddr,
+ &k2.outer_eth_daddr) &&
+ (k1.outer_ip_src_addr == k2.outer_ip_src_addr) &&
+ (k1.outer_ip_dst_addr == k2.outer_ip_dst_addr) &&
+ (k1.outer_dst_port == k2.outer_dst_port) &&
+ // (k1.vxlan_hdr.vx_flags == k2.vxlan_hdr.vx_flags) &&
+ // (k1.vxlan_hdr.vx_vni == k2.vxlan_hdr.vx_vni) &&
+ (k1.gtp_hdr.teid == k2.gtp_hdr.teid) &&
+ is_same_udp4_flow(k1.inner_key, k2.inner_key));
+}
+
+static inline int
+udp4_check_gtp_neighbor(struct gro_gtp_udp4_item *item,
+ uint16_t frag_offset,
+ uint16_t ip_dl)
+{
+ struct rte_mbuf *pkt = item->inner_item.firstseg;
+ int cmp;
+ uint16_t l2_offset;
+ int ret = 0;
+
+ /* Note: if outer DF bit is set, i.e outer_is_atomic is 0,
+ * we needn't compare outer_ip_id because they are same,
+ * for the case outer_is_atomic is 1, we also have no way
+ * to compare outer_ip_id because the difference between
+ * outer_ip_ids of two received packets isn't always +/-1.
+ * So skip outer_ip_id comparison here.
+ */
+
+ l2_offset = pkt->outer_l2_len + pkt->outer_l3_len;
+ cmp = udp4_check_neighbor(&item->inner_item, frag_offset, ip_dl,
+ l2_offset);
+ if (cmp > 0)
+ /* Append the new packet. */
+ ret = 1;
+ else if (cmp < 0)
+ /* Prepend the new packet. */
+ ret = -1;
+
+ return ret;
+}
+
+static inline int
+merge_two_gtp_udp4_packets(struct gro_gtp_udp4_item *item,
+ struct rte_mbuf *pkt,
+ int cmp,
+ uint16_t frag_offset,
+ uint8_t is_last_frag)
+{
+ if (merge_two_udp4_packets(&item->inner_item, pkt, cmp, frag_offset,
+ is_last_frag,
+ pkt->outer_l2_len + pkt->outer_l3_len)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline void
+update_gtp_header(struct gro_gtp_udp4_item *item)
+{
+ struct rte_ipv4_hdr *ipv4_hdr;
+ struct rte_udp_hdr *udp_hdr;
+ struct rte_mbuf *pkt = item->inner_item.firstseg;
+ uint16_t len;
+ uint16_t frag_offset;
+
+ /* Update the outer IPv4 header. */
+ len = pkt->pkt_len - pkt->outer_l2_len;
+ ipv4_hdr = rte_pktmbuf_mtod_offset(pkt, struct rte_ipv4_hdr *,
+ pkt->outer_l2_len);
+ ipv4_hdr->total_length = rte_cpu_to_be_16(len);
+
+ /* Update the outer UDP header. */
+ len -= pkt->outer_l3_len;
+ udp_hdr = (struct rte_udp_hdr *)((char *)ipv4_hdr + pkt->outer_l3_len);
+ udp_hdr->dgram_len = rte_cpu_to_be_16(len);
+
+ /* Update the inner IPv4 header. */
+ len -= pkt->l2_len;
+ ipv4_hdr = (struct rte_ipv4_hdr *)((char *)udp_hdr + pkt->l2_len);
+ ipv4_hdr->total_length = rte_cpu_to_be_16(len);
+
+ /* Clear MF bit if it is last fragment */
+ if (item->inner_item.is_last_frag) {
+ frag_offset = rte_be_to_cpu_16(ipv4_hdr->fragment_offset);
+ ipv4_hdr->fragment_offset =
+ rte_cpu_to_be_16(frag_offset & ~RTE_IPV4_HDR_MF_FLAG);
+ }
+}
+
+int32_t
+gro_gtp_udp4_reassemble(struct rte_mbuf *pkt,
+ struct gro_gtp_udp4_tbl *tbl,
+ uint64_t start_time)
+{
+ struct rte_ether_hdr *outer_eth_hdr, *eth_hdr;
+ struct rte_ipv4_hdr *outer_ipv4_hdr, *ipv4_hdr;
+ struct rte_udp_hdr *udp_hdr;
+ struct rte_gtp_hdr *gtp_hdr;
+ uint16_t frag_offset;
+ uint8_t is_last_frag;
+ int16_t ip_dl;
+ uint16_t ip_id;
+
+ struct gtp_udp4_flow_key key;
+ uint32_t cur_idx, prev_idx, item_idx;
+ uint32_t i, max_flow_num, remaining_flow_num;
+ int cmp;
+ uint16_t hdr_len;
+ uint8_t find;
+
+ outer_eth_hdr = rte_pktmbuf_mtod(pkt, struct rte_ether_hdr *);
+ outer_ipv4_hdr = (struct rte_ipv4_hdr *)((char *)outer_eth_hdr +
+ pkt->outer_l2_len);
+
+ udp_hdr = (struct rte_udp_hdr *)((char *)outer_ipv4_hdr +
+ pkt->outer_l3_len);
+ gtp_hdr = (struct rte_gtp_hdr *)((char *)udp_hdr +
+ sizeof(struct rte_udp_hdr));
+ eth_hdr = (struct rte_ether_hdr *)((char *)gtp_hdr +
+ sizeof(struct rte_gtp_hdr));
+ /* l2_len = outer udp hdr len + gtp hdr len + inner l2 len */
+ ipv4_hdr = (struct rte_ipv4_hdr *)((char *)udp_hdr + pkt->l2_len);
+
+ /*
+ * Don't process the packet which has non-fragment inner IP.
+ */
+ if (!is_ipv4_fragment(ipv4_hdr))
+ return -1;
+
+ hdr_len = pkt->outer_l2_len + pkt->outer_l3_len + pkt->l2_len +
+ pkt->l3_len;
+ /*
+ * Don't process the packet whose payload length is less than or
+ * equal to 0.
+ */
+ if (pkt->pkt_len <= hdr_len)
+ return -1;
+
+ ip_dl = pkt->pkt_len - hdr_len;
+
+ ip_id = rte_be_to_cpu_16(ipv4_hdr->packet_id);
+ frag_offset = rte_be_to_cpu_16(ipv4_hdr->fragment_offset);
+ is_last_frag = ((frag_offset & RTE_IPV4_HDR_MF_FLAG) == 0) ? 1 : 0;
+ frag_offset = (uint16_t)(frag_offset & RTE_IPV4_HDR_OFFSET_MASK) << 3;
+
+ rte_ether_addr_copy(&(eth_hdr->src_addr), &(key.inner_key.eth_saddr));
+ rte_ether_addr_copy(&(eth_hdr->dst_addr), &(key.inner_key.eth_daddr));
+ key.inner_key.ip_src_addr = ipv4_hdr->src_addr;
+ key.inner_key.ip_dst_addr = ipv4_hdr->dst_addr;
+ key.inner_key.ip_id = ip_id;
+
+ // key.vxlan_hdr.vx_flags = vxlan_hdr->vx_flags;
+ // key.vxlan_hdr.vx_vni = vxlan_hdr->vx_vni;
+ key.gtp_hdr.teid = gtp_hdr->teid;
+ rte_ether_addr_copy(&(outer_eth_hdr->src_addr), &(key.outer_eth_saddr));
+ rte_ether_addr_copy(&(outer_eth_hdr->dst_addr), &(key.outer_eth_daddr));
+ key.outer_ip_src_addr = outer_ipv4_hdr->src_addr;
+ key.outer_ip_dst_addr = outer_ipv4_hdr->dst_addr;
+ /* Note: It is unnecessary to save outer_src_port here because it can
+ * be different for GTP UDP fragments from the same flow.
+ */
+ key.outer_dst_port = udp_hdr->dst_port;
+
+ /* Search for a matched flow. */
+ max_flow_num = tbl->max_flow_num;
+ remaining_flow_num = tbl->flow_num;
+ find = 0;
+ for (i = 0; i < max_flow_num && remaining_flow_num; i++) {
+ if (tbl->flows[i].start_index != INVALID_ARRAY_INDEX) {
+ if (is_same_gtp_udp4_flow(tbl->flows[i].key, key)) {
+ find = 1;
+ break;
+ }
+ remaining_flow_num--;
+ }
+ }
+
+ /*
+ * Can't find a matched flow. Insert a new flow and store the
+ * packet into the flow.
+ */
+ if (find == 0) {
+ item_idx = insert_new_item(tbl, pkt, start_time,
+ INVALID_ARRAY_INDEX, frag_offset,
+ is_last_frag);
+ if (unlikely(item_idx == INVALID_ARRAY_INDEX))
+ return -1;
+ if (insert_new_flow(tbl, &key, item_idx) ==
+ INVALID_ARRAY_INDEX) {
+ /*
+ * Fail to insert a new flow, so
+ * delete the inserted packet.
+ */
+ delete_item(tbl, item_idx, INVALID_ARRAY_INDEX);
+ return -1;
+ }
+ return 0;
+ }
+
+ /* Check all packets in the flow and try to find a neighbor. */
+ cur_idx = tbl->flows[i].start_index;
+ prev_idx = cur_idx;
+ do {
+ cmp = udp4_check_gtp_neighbor(&(tbl->items[cur_idx]),
+ frag_offset, ip_dl);
+ if (cmp) {
+ if (merge_two_gtp_udp4_packets(
+ &(tbl->items[cur_idx]),
+ pkt, cmp, frag_offset,
+ is_last_frag)) {
+ return 1;
+ }
+ /*
+ * Can't merge two packets, as the packet
+ * length will be greater than the max value.
+ * Insert the packet into the flow.
+ */
+ if (insert_new_item(tbl, pkt, start_time, prev_idx,
+ frag_offset, is_last_frag) ==
+ INVALID_ARRAY_INDEX)
+ return -1;
+ return 0;
+ }
+
+ /* Ensure inserted items are ordered by frag_offset */
+ if (frag_offset
+ < tbl->items[cur_idx].inner_item.frag_offset) {
+ break;
+ }
+
+ prev_idx = cur_idx;
+ cur_idx = tbl->items[cur_idx].inner_item.next_pkt_idx;
+ } while (cur_idx != INVALID_ARRAY_INDEX);
+
+ /* Can't find neighbor. Insert the packet into the flow. */
+ if (cur_idx == tbl->flows[i].start_index) {
+ /* Insert it before the first packet of the flow */
+ item_idx = insert_new_item(tbl, pkt, start_time,
+ INVALID_ARRAY_INDEX, frag_offset,
+ is_last_frag);
+ if (unlikely(item_idx == INVALID_ARRAY_INDEX))
+ return -1;
+ tbl->items[item_idx].inner_item.next_pkt_idx = cur_idx;
+ tbl->flows[i].start_index = item_idx;
+ } else {
+ if (insert_new_item(tbl, pkt, start_time, prev_idx,
+ frag_offset, is_last_frag
+ ) == INVALID_ARRAY_INDEX)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+gro_gtp_udp4_merge_items(struct gro_gtp_udp4_tbl *tbl,
+ uint32_t start_idx)
+{
+ uint16_t frag_offset;
+ uint8_t is_last_frag;
+ int16_t ip_dl;
+ struct rte_mbuf *pkt;
+ int cmp;
+ uint32_t item_idx;
+ uint16_t hdr_len;
+
+ item_idx = tbl->items[start_idx].inner_item.next_pkt_idx;
+ while (item_idx != INVALID_ARRAY_INDEX) {
+ pkt = tbl->items[item_idx].inner_item.firstseg;
+ hdr_len = pkt->outer_l2_len + pkt->outer_l3_len + pkt->l2_len +
+ pkt->l3_len;
+ ip_dl = pkt->pkt_len - hdr_len;
+ frag_offset = tbl->items[item_idx].inner_item.frag_offset;
+ is_last_frag = tbl->items[item_idx].inner_item.is_last_frag;
+ cmp = udp4_check_gtp_neighbor(&(tbl->items[start_idx]),
+ frag_offset, ip_dl);
+ if (cmp) {
+ if (merge_two_gtp_udp4_packets(
+ &(tbl->items[start_idx]),
+ pkt, cmp, frag_offset,
+ is_last_frag)) {
+ item_idx = delete_item(tbl, item_idx,
+ INVALID_ARRAY_INDEX);
+ tbl->items[start_idx].inner_item.next_pkt_idx
+ = item_idx;
+ } else
+ return 0;
+ } else
+ return 0;
+ }
+
+ return 0;
+}
+
+uint16_t
+gro_gtp_udp4_tbl_timeout_flush(struct gro_gtp_udp4_tbl *tbl,
+ uint64_t flush_timestamp,
+ struct rte_mbuf **out,
+ uint16_t nb_out)
+{
+ uint16_t k = 0;
+ uint32_t i, j;
+ uint32_t max_flow_num = tbl->max_flow_num;
+
+ for (i = 0; i < max_flow_num; i++) {
+ if (unlikely(tbl->flow_num == 0))
+ return k;
+
+ j = tbl->flows[i].start_index;
+ while (j != INVALID_ARRAY_INDEX) {
+ if (tbl->items[j].inner_item.start_time <=
+ flush_timestamp) {
+ gro_gtp_udp4_merge_items(tbl, j);
+ out[k++] = tbl->items[j].inner_item.firstseg;
+ if (tbl->items[j].inner_item.nb_merged > 1)
+ update_gtp_header(&(tbl->items[j]));
+ /*
+ * Delete the item and get the next packet
+ * index.
+ */
+ j = delete_item(tbl, j, INVALID_ARRAY_INDEX);
+ tbl->flows[i].start_index = j;
+ if (j == INVALID_ARRAY_INDEX)
+ tbl->flow_num--;
+
+ if (unlikely(k == nb_out))
+ return k;
+ } else
+ /*
+ * Flushing packets does not strictly follow
+ * timestamp. It does not flush left packets of
+ * the flow this time once it finds one item
+ * whose start_time is greater than
+ * flush_timestamp. So go to check other flows.
+ */
+ break;
+ }
+ }
+ return k;
+}
+
+uint32_t
+gro_gtp_udp4_tbl_pkt_count(void *tbl)
+{
+ struct gro_gtp_udp4_tbl *gro_tbl = tbl;
+
+ if (gro_tbl)
+ return gro_tbl->item_num;
+
+ return 0;
+}
diff --git a/gro/GRO Implementation Files/gro_gtp_udp4.h b/gro/GRO Implementation Files/gro_gtp_udp4.h
new file mode 100644
index 0000000..ea8cfc8
--- /dev/null
+++ b/gro/GRO Implementation Files/gro_gtp_udp4.h
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Inspur Corporation
+ */
+
+ #ifndef _GRO_GTP_UDP4_H_
+ #define _GRO_GTP_UDP4_H_
+
+ #include "gro_udp4.h"
+
+ #define GRO_GTP_UDP4_TBL_MAX_ITEM_NUM (1024UL * 1024UL)
+
+ /* Header fields representing a GTP flow */
+ struct gtp_udp4_flow_key {
+ struct udp4_flow_key inner_key;
+ struct rte_gtp_hdr gtp_hdr;
+
+ struct rte_ether_addr outer_eth_saddr;
+ struct rte_ether_addr outer_eth_daddr;
+
+ uint32_t outer_ip_src_addr;
+ uint32_t outer_ip_dst_addr;
+
+ /* Note: It is unnecessary to save outer_src_port here because it can
+ * be different for GTP UDP fragments from the same flow.
+ */
+ uint16_t outer_dst_port;
+ };
+
+ struct gro_gtp_udp4_flow {
+ struct gtp_udp4_flow_key key;
+ /*
+ * The index of the first packet in the flow. INVALID_ARRAY_INDEX
+ * indicates an empty flow.
+ */
+ uint32_t start_index;
+ };
+
+ struct gro_gtp_udp4_item {
+ struct gro_udp4_item inner_item;
+ /* Note: GTP UDP/IPv4 GRO needn't check outer_ip_id because
+ * the difference between outer_ip_ids of two received packets
+ * isn't always +/-1 in case of OVS DPDK. So no outer_ip_id
+ * and outer_is_atomic fields here.
+ */
+ };
+
+ /*
+ * GTP (with an outer IPv4 header and an inner UDP/IPv4 packet)
+ * reassembly table structure
+ */
+ struct gro_gtp_udp4_tbl {
+ /* item array */
+ struct gro_gtp_udp4_item *items;
+ /* flow array */
+ struct gro_gtp_udp4_flow *flows;
+ /* current item number */
+ uint32_t item_num;
+ /* current flow number */
+ uint32_t flow_num;
+ /* the maximum item number */
+ uint32_t max_item_num;
+ /* the maximum flow number */
+ uint32_t max_flow_num;
+ };
+
+ /**
+ * This function creates a GTP reassembly table for GTP packets
+ * which have an outer IPv4 header and an inner UDP/IPv4 packet.
+ *
+ * @param socket_id
+ * Socket index for allocating the table
+ * @param max_flow_num
+ * The maximum number of flows in the table
+ * @param max_item_per_flow
+ * The maximum number of packets per flow
+ *
+ * @return
+ * - Return the table pointer on success.
+ * - Return NULL on failure.
+ */
+ void *gro_gtp_udp4_tbl_create(uint16_t socket_id,
+ uint16_t max_flow_num,
+ uint16_t max_item_per_flow);
+
+ /**
+ * This function destroys a GTP reassembly table.
+ *
+ * @param tbl
+ * Pointer pointing to the GTP reassembly table
+ */
+ void gro_gtp_udp4_tbl_destroy(void *tbl);
+
+ /**
+ * This function merges a GTP packet which has an outer IPv4 header and
+ * an inner UDP/IPv4 packet. It does not process the packet which does not
+ * have payload.
+ *
+ * This function does not check if the packet has correct checksums and
+ * does not re-calculate checksums for the merged packet. It returns the
+ * packet if there is no available space in the table.
+ *
+ * @param pkt
+ * Packet to reassemble
+ * @param tbl
+ * Pointer pointing to the GTP reassembly table
+ * @start_time
+ * The time when the packet is inserted into the table
+ *
+ * @return
+ * - Return a positive value if the packet is merged.
+ * - Return zero if the packet isn't merged but stored in the table.
+ * - Return a negative value for invalid parameters or no available
+ * space in the table.
+ */
+ int32_t gro_gtp_udp4_reassemble(struct rte_mbuf *pkt,
+ struct gro_gtp_udp4_tbl *tbl,
+ uint64_t start_time);
+
+ /**
+ * This function flushes timeout packets in the GTP reassembly table,
+ * and without updating checksums.
+ *
+ * @param tbl
+ * Pointer pointing to a GTP GRO table
+ * @param flush_timestamp
+ * This function flushes packets which are inserted into the table
+ * before or at the flush_timestamp.
+ * @param out
+ * Pointer array used to keep flushed packets
+ * @param nb_out
+ * The element number in 'out'. It also determines the maximum number of
+ * packets that can be flushed finally.
+ *
+ * @return
+ * The number of flushed packets
+ */
+ uint16_t gro_gtp_udp4_tbl_timeout_flush(struct gro_gtp_udp4_tbl *tbl,
+ uint64_t flush_timestamp,
+ struct rte_mbuf **out,
+ uint16_t nb_out);
+
+ /**
+ * This function returns the number of the packets in a GTP
+ * reassembly table.
+ *
+ * @param tbl
+ * Pointer pointing to the GTP reassembly table
+ *
+ * @return
+ * The number of packets in the table
+ */
+ uint32_t gro_gtp_udp4_tbl_pkt_count(void *tbl);
+ #endif
+
\ No newline at end of file
diff --git a/gro/GRO Implementation Files/meson.build b/gro/GRO Implementation Files/meson.build
new file mode 100644
index 0000000..208441e
--- /dev/null
+++ b/gro/GRO Implementation Files/meson.build
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2017 Intel Corporation
+
+sources = files(
+ 'rte_gro.c',
+ 'gro_tcp4.c',
+ 'gro_tcp6.c',
+ 'gro_udp4.c',
+ 'gro_vxlan_tcp4.c',
+ 'gro_vxlan_udp4.c',
+ 'gro_gtp_tcp4.c',
+ 'gro_gtp_udp4.c',
+)
+headers = files('rte_gro.h')
+deps += ['ethdev']
diff --git a/gro/GRO Implementation Files/rte_gro.c b/gro/GRO Implementation Files/rte_gro.c
new file mode 100644
index 0000000..ba5826f
--- /dev/null
+++ b/gro/GRO Implementation Files/rte_gro.c
@@ -0,0 +1,630 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2017 Intel Corporation
+ */
+
+#include
+#include
+#include
+
+#include "rte_gro.h"
+#include "gro_tcp4.h"
+#include "gro_tcp6.h"
+#include "gro_udp4.h"
+#include "gro_vxlan_tcp4.h"
+#include "gro_vxlan_udp4.h"
+#include "gro_gtp_tcp4.h"
+#include "gro_gtp_udp4.h"
+
+typedef void *(*gro_tbl_create_fn)(uint16_t socket_id,
+ uint16_t max_flow_num,
+ uint16_t max_item_per_flow);
+typedef void (*gro_tbl_destroy_fn)(void *tbl);
+typedef uint32_t (*gro_tbl_pkt_count_fn)(void *tbl);
+
+static gro_tbl_create_fn tbl_create_fn[RTE_GRO_TYPE_MAX_NUM] = {
+ gro_tcp4_tbl_create, gro_vxlan_tcp4_tbl_create,
+ gro_udp4_tbl_create, gro_vxlan_udp4_tbl_create, gro_tcp6_tbl_create,
+ gro_gtp_tcp4_tbl_create, gro_gtp_udp4_tbl_create, NULL};
+static gro_tbl_destroy_fn tbl_destroy_fn[RTE_GRO_TYPE_MAX_NUM] = {
+ gro_tcp4_tbl_destroy, gro_vxlan_tcp4_tbl_destroy,
+ gro_udp4_tbl_destroy, gro_vxlan_udp4_tbl_destroy,
+ gro_tcp6_tbl_destroy, gro_gtp_tcp4_tbl_destroy,
+ gro_gtp_udp4_tbl_destroy, NULL};
+static gro_tbl_pkt_count_fn tbl_pkt_count_fn[RTE_GRO_TYPE_MAX_NUM] = {
+ gro_tcp4_tbl_pkt_count, gro_vxlan_tcp4_tbl_pkt_count,
+ gro_udp4_tbl_pkt_count, gro_vxlan_udp4_tbl_pkt_count,
+ gro_tcp6_tbl_pkt_count, gro_gtp_tcp4_tbl_pkt_count,
+ gro_gtp_udp4_tbl_pkt_count, NULL};
+
+#define IS_IPV4_TCP_PKT(ptype) (RTE_ETH_IS_IPV4_HDR(ptype) && \
+ ((ptype & RTE_PTYPE_L4_TCP) == RTE_PTYPE_L4_TCP) && \
+ ((ptype & RTE_PTYPE_L4_FRAG) != RTE_PTYPE_L4_FRAG) && \
+ (RTE_ETH_IS_TUNNEL_PKT(ptype) == 0))
+
+/* GRO with extension headers is not supported */
+#define IS_IPV6_TCP_PKT(ptype) (RTE_ETH_IS_IPV6_HDR(ptype) && \
+ ((ptype & RTE_PTYPE_L4_TCP) == RTE_PTYPE_L4_TCP) && \
+ ((ptype & RTE_PTYPE_L4_FRAG) != RTE_PTYPE_L4_FRAG) && \
+ (RTE_ETH_IS_TUNNEL_PKT(ptype) == 0))
+
+#define IS_IPV4_UDP_PKT(ptype) (RTE_ETH_IS_IPV4_HDR(ptype) && \
+ ((ptype & RTE_PTYPE_L4_UDP) == RTE_PTYPE_L4_UDP) && \
+ (RTE_ETH_IS_TUNNEL_PKT(ptype) == 0))
+
+#define IS_IPV4_VXLAN_TCP4_PKT(ptype) (RTE_ETH_IS_IPV4_HDR(ptype) && \
+ ((ptype & RTE_PTYPE_L4_UDP) == RTE_PTYPE_L4_UDP) && \
+ ((ptype & RTE_PTYPE_L4_FRAG) != RTE_PTYPE_L4_FRAG) && \
+ ((ptype & RTE_PTYPE_TUNNEL_VXLAN) == \
+ RTE_PTYPE_TUNNEL_VXLAN) && \
+ ((ptype & RTE_PTYPE_INNER_L4_TCP) == \
+ RTE_PTYPE_INNER_L4_TCP) && \
+ (((ptype & RTE_PTYPE_INNER_L3_MASK) == \
+ RTE_PTYPE_INNER_L3_IPV4) || \
+ ((ptype & RTE_PTYPE_INNER_L3_MASK) == \
+ RTE_PTYPE_INNER_L3_IPV4_EXT) || \
+ ((ptype & RTE_PTYPE_INNER_L3_MASK) == \
+ RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN)))
+
+#define IS_IPV4_VXLAN_UDP4_PKT(ptype) (RTE_ETH_IS_IPV4_HDR(ptype) && \
+ ((ptype & RTE_PTYPE_L4_UDP) == RTE_PTYPE_L4_UDP) && \
+ ((ptype & RTE_PTYPE_TUNNEL_VXLAN) == \
+ RTE_PTYPE_TUNNEL_VXLAN) && \
+ ((ptype & RTE_PTYPE_INNER_L4_UDP) == \
+ RTE_PTYPE_INNER_L4_UDP) && \
+ (((ptype & RTE_PTYPE_INNER_L3_MASK) == \
+ RTE_PTYPE_INNER_L3_IPV4) || \
+ ((ptype & RTE_PTYPE_INNER_L3_MASK) == \
+ RTE_PTYPE_INNER_L3_IPV4_EXT) || \
+ ((ptype & RTE_PTYPE_INNER_L3_MASK) == \
+ RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN)))
+
+#define IS_IPV4_GTP_UDP4_PKT(ptype) (RTE_ETH_IS_IPV4_HDR(ptype) && \
+ ((ptype & RTE_PTYPE_L4_UDP) == RTE_PTYPE_L4_UDP) && \
+ ((ptype & RTE_PTYPE_TUNNEL_GTPU) == \
+ RTE_PTYPE_TUNNEL_GTPU) && \
+ ((ptype & RTE_PTYPE_INNER_L4_UDP) == \
+ RTE_PTYPE_INNER_L4_UDP) && \
+ (((ptype & RTE_PTYPE_INNER_L3_MASK) == \
+ RTE_PTYPE_INNER_L3_IPV4) || \
+ ((ptype & RTE_PTYPE_INNER_L3_MASK) == \
+ RTE_PTYPE_INNER_L3_IPV4_EXT) || \
+ ((ptype & RTE_PTYPE_INNER_L3_MASK) == \
+ RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN)))
+
+#define IS_IPV4_GTP_TCP4_PKT(ptype) (RTE_ETH_IS_IPV4_HDR(ptype) && \
+ ((ptype & RTE_PTYPE_L4_UDP) == RTE_PTYPE_L4_UDP) && \
+ ((ptype & RTE_PTYPE_L4_FRAG) != RTE_PTYPE_L4_FRAG) && \
+ ((ptype & RTE_PTYPE_TUNNEL_GTPU) == \
+ RTE_PTYPE_TUNNEL_GTPU) && \
+ ((ptype & RTE_PTYPE_INNER_L4_TCP) == \
+ RTE_PTYPE_INNER_L4_TCP) && \
+ (((ptype & RTE_PTYPE_INNER_L3_MASK) == \
+ RTE_PTYPE_INNER_L3_IPV4) || \
+ ((ptype & RTE_PTYPE_INNER_L3_MASK) == \
+ RTE_PTYPE_INNER_L3_IPV4_EXT) || \
+ ((ptype & RTE_PTYPE_INNER_L3_MASK) == \
+ RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN)))
+
+/*
+ * GRO context structure. It keeps the table structures, which are
+ * used to merge packets, for different GRO types. Before using
+ * rte_gro_reassemble(), applications need to create the GRO context
+ * first.
+ */
+struct gro_ctx {
+ /* GRO types to perform */
+ uint64_t gro_types;
+ /* reassembly tables */
+ void *tbls[RTE_GRO_TYPE_MAX_NUM];
+};
+
+void *
+rte_gro_ctx_create(const struct rte_gro_param *param)
+{
+ struct gro_ctx *gro_ctx;
+ gro_tbl_create_fn create_tbl_fn;
+ uint64_t gro_type_flag = 0;
+ uint64_t gro_types = 0;
+ uint8_t i;
+
+ gro_ctx = rte_zmalloc_socket(__func__,
+ sizeof(struct gro_ctx),
+ RTE_CACHE_LINE_SIZE,
+ param->socket_id);
+ if (gro_ctx == NULL)
+ return NULL;
+
+ for (i = 0; i < RTE_GRO_TYPE_MAX_NUM; i++) {
+ gro_type_flag = 1ULL << i;
+ if ((param->gro_types & gro_type_flag) == 0)
+ continue;
+
+ create_tbl_fn = tbl_create_fn[i];
+ if (create_tbl_fn == NULL)
+ continue;
+
+ gro_ctx->tbls[i] = create_tbl_fn(param->socket_id,
+ param->max_flow_num,
+ param->max_item_per_flow);
+ if (gro_ctx->tbls[i] == NULL) {
+ /* destroy all created tables */
+ gro_ctx->gro_types = gro_types;
+ rte_gro_ctx_destroy(gro_ctx);
+ return NULL;
+ }
+ gro_types |= gro_type_flag;
+ }
+ gro_ctx->gro_types = param->gro_types;
+
+ return gro_ctx;
+}
+
+void
+rte_gro_ctx_destroy(void *ctx)
+{
+ gro_tbl_destroy_fn destroy_tbl_fn;
+ struct gro_ctx *gro_ctx = ctx;
+ uint64_t gro_type_flag;
+ uint8_t i;
+
+ for (i = 0; i < RTE_GRO_TYPE_MAX_NUM; i++) {
+ gro_type_flag = 1ULL << i;
+ if ((gro_ctx->gro_types & gro_type_flag) == 0)
+ continue;
+ destroy_tbl_fn = tbl_destroy_fn[i];
+ if (destroy_tbl_fn)
+ destroy_tbl_fn(gro_ctx->tbls[i]);
+ }
+ rte_free(gro_ctx);
+}
+
+uint16_t
+rte_gro_reassemble_burst(struct rte_mbuf **pkts,
+ uint16_t nb_pkts,
+ const struct rte_gro_param *param)
+{
+ /* allocate a reassembly table for TCP/IPv4 GRO */
+ struct gro_tcp4_tbl tcp_tbl;
+ struct gro_tcp4_flow tcp_flows[RTE_GRO_MAX_BURST_ITEM_NUM];
+ struct gro_tcp_item tcp_items[RTE_GRO_MAX_BURST_ITEM_NUM] = {{0} };
+
+ struct gro_tcp6_tbl tcp6_tbl;
+ struct gro_tcp6_flow tcp6_flows[RTE_GRO_MAX_BURST_ITEM_NUM];
+ struct gro_tcp_item tcp6_items[RTE_GRO_MAX_BURST_ITEM_NUM] = {{0} };
+
+ /* allocate a reassembly table for UDP/IPv4 GRO */
+ struct gro_udp4_tbl udp_tbl;
+ struct gro_udp4_flow udp_flows[RTE_GRO_MAX_BURST_ITEM_NUM];
+ struct gro_udp4_item udp_items[RTE_GRO_MAX_BURST_ITEM_NUM] = {{0} };
+
+ /* Allocate a reassembly table for VXLAN TCP GRO */
+ struct gro_vxlan_tcp4_tbl vxlan_tcp_tbl;
+ struct gro_vxlan_tcp4_flow vxlan_tcp_flows[RTE_GRO_MAX_BURST_ITEM_NUM];
+ struct gro_vxlan_tcp4_item vxlan_tcp_items[RTE_GRO_MAX_BURST_ITEM_NUM]
+ = {{{0}, 0, 0} };
+
+ /* Allocate a reassembly table for VXLAN UDP GRO */
+ struct gro_vxlan_udp4_tbl vxlan_udp_tbl;
+ struct gro_vxlan_udp4_flow vxlan_udp_flows[RTE_GRO_MAX_BURST_ITEM_NUM];
+ struct gro_vxlan_udp4_item vxlan_udp_items[RTE_GRO_MAX_BURST_ITEM_NUM]
+ = {{{0}} };
+
+ /* Allocate a reassembly table for GTP TCP GRO */
+ struct gro_gtp_tcp4_tbl gtp_tcp_tbl;
+ struct gro_gtp_tcp4_flow gtp_tcp_flows[RTE_GRO_MAX_BURST_ITEM_NUM];
+ struct gro_gtp_tcp4_item gtp_tcp_items[RTE_GRO_MAX_BURST_ITEM_NUM]
+ = {{{0}, 0, 0} };
+
+ /* Allocate a reassembly table for GTP UDP GRO */
+ struct gro_gtp_udp4_tbl gtp_udp_tbl;
+ struct gro_gtp_udp4_flow gtp_udp_flows[RTE_GRO_MAX_BURST_ITEM_NUM];
+ struct gro_gtp_udp4_item gtp_udp_items[RTE_GRO_MAX_BURST_ITEM_NUM]
+ = {{{0}} };
+
+ uint32_t item_num;
+ int32_t ret;
+ uint16_t i, unprocess_num = 0, nb_after_gro = nb_pkts;
+ uint8_t do_tcp4_gro = 0, do_vxlan_tcp_gro = 0, do_udp4_gro = 0,
+ do_vxlan_udp_gro = 0, do_tcp6_gro = 0, do_gtp_tcp_gro = 0,
+ do_gtp_udp_gro = 0;
+
+ if (unlikely((param->gro_types & (RTE_GRO_IPV4_VXLAN_TCP_IPV4 |
+ RTE_GRO_TCP_IPV4 | RTE_GRO_TCP_IPV6 |
+ RTE_GRO_IPV4_VXLAN_UDP_IPV4 |
+ RTE_GRO_IPV4_GTP_TCP_IPV4 |
+ RTE_GRO_IPV4_GTP_UDP_IPV4 |
+ RTE_GRO_UDP_IPV4)) == 0))
+ return nb_pkts;
+
+ /* Get the maximum number of packets */
+ item_num = RTE_MIN(nb_pkts, (param->max_flow_num *
+ param->max_item_per_flow));
+ item_num = RTE_MIN(item_num, RTE_GRO_MAX_BURST_ITEM_NUM);
+
+ if (param->gro_types & RTE_GRO_IPV4_VXLAN_TCP_IPV4) {
+ for (i = 0; i < item_num; i++)
+ vxlan_tcp_flows[i].start_index = INVALID_ARRAY_INDEX;
+
+ vxlan_tcp_tbl.flows = vxlan_tcp_flows;
+ vxlan_tcp_tbl.items = vxlan_tcp_items;
+ vxlan_tcp_tbl.flow_num = 0;
+ vxlan_tcp_tbl.item_num = 0;
+ vxlan_tcp_tbl.max_flow_num = item_num;
+ vxlan_tcp_tbl.max_item_num = item_num;
+ do_vxlan_tcp_gro = 1;
+ }
+
+ if (param->gro_types & RTE_GRO_IPV4_VXLAN_UDP_IPV4) {
+ for (i = 0; i < item_num; i++)
+ vxlan_udp_flows[i].start_index = INVALID_ARRAY_INDEX;
+
+ vxlan_udp_tbl.flows = vxlan_udp_flows;
+ vxlan_udp_tbl.items = vxlan_udp_items;
+ vxlan_udp_tbl.flow_num = 0;
+ vxlan_udp_tbl.item_num = 0;
+ vxlan_udp_tbl.max_flow_num = item_num;
+ vxlan_udp_tbl.max_item_num = item_num;
+ do_vxlan_udp_gro = 1;
+ }
+
+ if (param->gro_types & RTE_GRO_IPV4_GTP_TCP_IPV4) {
+ for (i = 0; i < item_num; i++)
+ gtp_tcp_flows[i].start_index = INVALID_ARRAY_INDEX;
+
+ gtp_tcp_tbl.flows = gtp_tcp_flows;
+ gtp_tcp_tbl.items = gtp_tcp_items;
+ gtp_tcp_tbl.flow_num = 0;
+ gtp_tcp_tbl.item_num = 0;
+ gtp_tcp_tbl.max_flow_num = item_num;
+ gtp_tcp_tbl.max_item_num = item_num;
+ do_gtp_tcp_gro = 1;
+ }
+
+ if (param->gro_types & RTE_GRO_IPV4_GTP_UDP_IPV4) {
+ for (i = 0; i < item_num; i++)
+ gtp_udp_flows[i].start_index = INVALID_ARRAY_INDEX;
+
+ gtp_udp_tbl.flows = gtp_udp_flows;
+ gtp_udp_tbl.items = gtp_udp_items;
+ gtp_udp_tbl.flow_num = 0;
+ gtp_udp_tbl.item_num = 0;
+ gtp_udp_tbl.max_flow_num = item_num;
+ gtp_udp_tbl.max_item_num = item_num;
+ do_gtp_udp_gro = 1;
+ }
+
+ if (param->gro_types & RTE_GRO_TCP_IPV4) {
+ for (i = 0; i < item_num; i++)
+ tcp_flows[i].start_index = INVALID_ARRAY_INDEX;
+
+ tcp_tbl.flows = tcp_flows;
+ tcp_tbl.items = tcp_items;
+ tcp_tbl.flow_num = 0;
+ tcp_tbl.item_num = 0;
+ tcp_tbl.max_flow_num = item_num;
+ tcp_tbl.max_item_num = item_num;
+ do_tcp4_gro = 1;
+ }
+
+ if (param->gro_types & RTE_GRO_UDP_IPV4) {
+ for (i = 0; i < item_num; i++)
+ udp_flows[i].start_index = INVALID_ARRAY_INDEX;
+
+ udp_tbl.flows = udp_flows;
+ udp_tbl.items = udp_items;
+ udp_tbl.flow_num = 0;
+ udp_tbl.item_num = 0;
+ udp_tbl.max_flow_num = item_num;
+ udp_tbl.max_item_num = item_num;
+ do_udp4_gro = 1;
+ }
+
+ if (param->gro_types & RTE_GRO_TCP_IPV6) {
+ for (i = 0; i < item_num; i++)
+ tcp6_flows[i].start_index = INVALID_ARRAY_INDEX;
+
+ tcp6_tbl.flows = tcp6_flows;
+ tcp6_tbl.items = tcp6_items;
+ tcp6_tbl.flow_num = 0;
+ tcp6_tbl.item_num = 0;
+ tcp6_tbl.max_flow_num = item_num;
+ tcp6_tbl.max_item_num = item_num;
+ do_tcp6_gro = 1;
+ }
+
+ for (i = 0; i < nb_pkts; i++) {
+ /*
+ * The timestamp is ignored, since all packets
+ * will be flushed from the tables.
+ */
+ if (IS_IPV4_VXLAN_TCP4_PKT(pkts[i]->packet_type) &&
+ do_vxlan_tcp_gro) {
+ ret = gro_vxlan_tcp4_reassemble(pkts[i],
+ &vxlan_tcp_tbl, 0);
+ if (ret > 0)
+ /* Merge successfully */
+ nb_after_gro--;
+ else if (ret < 0)
+ pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV4_VXLAN_UDP4_PKT(pkts[i]->packet_type) &&
+ do_vxlan_udp_gro) {
+ ret = gro_vxlan_udp4_reassemble(pkts[i],
+ &vxlan_udp_tbl, 0);
+ if (ret > 0)
+ /* Merge successfully */
+ nb_after_gro--;
+ else if (ret < 0)
+ pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV4_GTP_TCP4_PKT(pkts[i]->packet_type) &&
+ do_gtp_tcp_gro) {
+ ret = gro_gtp_tcp4_reassemble(pkts[i],
+ >p_tcp_tbl, 0);
+ if (ret > 0)
+ /* Merge successfully */
+ nb_after_gro--;
+ else if (ret < 0)
+ pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV4_GTP_UDP4_PKT(pkts[i]->packet_type) &&
+ do_gtp_udp_gro) {
+ ret = gro_gtp_udp4_reassemble(pkts[i],
+ >p_udp_tbl, 0);
+ if (ret > 0)
+ /* Merge successfully */
+ nb_after_gro--;
+ else if (ret < 0)
+ pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV4_TCP_PKT(pkts[i]->packet_type) &&
+ do_tcp4_gro) {
+ ret = gro_tcp4_reassemble(pkts[i], &tcp_tbl, 0);
+ if (ret > 0)
+ /* merge successfully */
+ nb_after_gro--;
+ else if (ret < 0)
+ pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV4_UDP_PKT(pkts[i]->packet_type) &&
+ do_udp4_gro) {
+ ret = gro_udp4_reassemble(pkts[i], &udp_tbl, 0);
+ if (ret > 0)
+ /* merge successfully */
+ nb_after_gro--;
+ else if (ret < 0)
+ pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV6_TCP_PKT(pkts[i]->packet_type) &&
+ do_tcp6_gro) {
+ ret = gro_tcp6_reassemble(pkts[i], &tcp6_tbl, 0);
+ if (ret > 0)
+ /* merge successfully */
+ nb_after_gro--;
+ else if (ret < 0)
+ pkts[unprocess_num++] = pkts[i];
+ } else
+ pkts[unprocess_num++] = pkts[i];
+ }
+
+ if ((nb_after_gro < nb_pkts)
+ || (unprocess_num < nb_pkts)) {
+
+ i = unprocess_num;
+
+ /* Flush all packets from the tables */
+ if (do_vxlan_tcp_gro) {
+ i += gro_vxlan_tcp4_tbl_timeout_flush(&vxlan_tcp_tbl,
+ 0, &pkts[i], nb_pkts - i);
+ }
+
+ if (do_vxlan_udp_gro) {
+ i += gro_vxlan_udp4_tbl_timeout_flush(&vxlan_udp_tbl,
+ 0, &pkts[i], nb_pkts - i);
+
+ }
+
+ if (do_gtp_tcp_gro) {
+ i += gro_gtp_tcp4_tbl_timeout_flush(>p_tcp_tbl,
+ 0, &pkts[i], nb_pkts - i);
+ }
+
+ if (do_gtp_udp_gro) {
+ i += gro_gtp_udp4_tbl_timeout_flush(>p_udp_tbl,
+ 0, &pkts[i], nb_pkts - i);
+ }
+
+ if (do_tcp4_gro) {
+ i += gro_tcp4_tbl_timeout_flush(&tcp_tbl, 0,
+ &pkts[i], nb_pkts - i);
+ }
+
+ if (do_udp4_gro) {
+ i += gro_udp4_tbl_timeout_flush(&udp_tbl, 0,
+ &pkts[i], nb_pkts - i);
+ }
+
+ if (do_tcp6_gro) {
+ i += gro_tcp6_tbl_timeout_flush(&tcp6_tbl, 0,
+ &pkts[i], nb_pkts - i);
+ }
+ }
+
+ return nb_after_gro;
+}
+
+uint16_t
+rte_gro_reassemble(struct rte_mbuf **pkts,
+ uint16_t nb_pkts,
+ void *ctx)
+{
+ struct gro_ctx *gro_ctx = ctx;
+ void *tcp_tbl, *udp_tbl, *vxlan_tcp_tbl, *vxlan_udp_tbl, *tcp6_tbl, *gtp_tcp_tbl, *gtp_udp_tbl;
+ uint64_t current_time;
+ uint16_t i, unprocess_num = 0;
+ uint8_t do_tcp4_gro, do_vxlan_tcp_gro, do_udp4_gro, do_vxlan_udp_gro, do_tcp6_gro,
+ do_gtp_tcp_gro, do_gtp_udp_gro;
+
+ if (unlikely((gro_ctx->gro_types & (RTE_GRO_IPV4_VXLAN_TCP_IPV4 |
+ RTE_GRO_TCP_IPV4 | RTE_GRO_TCP_IPV6 |
+ RTE_GRO_IPV4_VXLAN_UDP_IPV4 |
+ RTE_GRO_IPV4_GTP_TCP_IPV4 |
+ RTE_GRO_IPV4_GTP_UDP_IPV4 |
+ RTE_GRO_UDP_IPV4)) == 0))
+ return nb_pkts;
+
+ tcp_tbl = gro_ctx->tbls[RTE_GRO_TCP_IPV4_INDEX];
+ vxlan_tcp_tbl = gro_ctx->tbls[RTE_GRO_IPV4_VXLAN_TCP_IPV4_INDEX];
+ udp_tbl = gro_ctx->tbls[RTE_GRO_UDP_IPV4_INDEX];
+ vxlan_udp_tbl = gro_ctx->tbls[RTE_GRO_IPV4_VXLAN_UDP_IPV4_INDEX];
+ gtp_tcp_tbl = gro_ctx->tbls[RTE_GRO_IPV4_GTP_TCP_IPV4_INDEX];
+ gtp_udp_tbl = gro_ctx->tbls[RTE_GRO_IPV4_GTP_UDP_IPV4_INDEX];
+ tcp6_tbl = gro_ctx->tbls[RTE_GRO_TCP_IPV6_INDEX];
+
+ do_tcp4_gro = (gro_ctx->gro_types & RTE_GRO_TCP_IPV4) ==
+ RTE_GRO_TCP_IPV4;
+ do_vxlan_tcp_gro = (gro_ctx->gro_types & RTE_GRO_IPV4_VXLAN_TCP_IPV4) ==
+ RTE_GRO_IPV4_VXLAN_TCP_IPV4;
+ do_udp4_gro = (gro_ctx->gro_types & RTE_GRO_UDP_IPV4) ==
+ RTE_GRO_UDP_IPV4;
+ do_vxlan_udp_gro = (gro_ctx->gro_types & RTE_GRO_IPV4_VXLAN_UDP_IPV4) ==
+ RTE_GRO_IPV4_VXLAN_UDP_IPV4;
+ do_gtp_tcp_gro = (gro_ctx->gro_types & RTE_GRO_IPV4_GTP_TCP_IPV4) ==
+ RTE_GRO_IPV4_GTP_TCP_IPV4;
+ do_gtp_udp_gro = (gro_ctx->gro_types & RTE_GRO_IPV4_GTP_UDP_IPV4) ==
+ RTE_GRO_IPV4_GTP_UDP_IPV4;
+ do_tcp6_gro = (gro_ctx->gro_types & RTE_GRO_TCP_IPV6) == RTE_GRO_TCP_IPV6;
+
+ current_time = rte_rdtsc();
+
+ for (i = 0; i < nb_pkts; i++) {
+ if (IS_IPV4_VXLAN_TCP4_PKT(pkts[i]->packet_type) &&
+ do_vxlan_tcp_gro) {
+ if (gro_vxlan_tcp4_reassemble(pkts[i], vxlan_tcp_tbl,
+ current_time) < 0)
+ pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV4_VXLAN_UDP4_PKT(pkts[i]->packet_type) &&
+ do_vxlan_udp_gro) {
+ if (gro_vxlan_udp4_reassemble(pkts[i], vxlan_udp_tbl,
+ current_time) < 0)
+ pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV4_GTP_TCP4_PKT(pkts[i]->packet_type) &&
+ do_gtp_tcp_gro) {
+ if (gro_gtp_tcp4_reassemble(pkts[i], gtp_tcp_tbl,
+ current_time) < 0)
+ pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV4_GTP_UDP4_PKT(pkts[i]->packet_type) &&
+ do_gtp_udp_gro) {
+ if (gro_gtp_udp4_reassemble(pkts[i], gtp_udp_tbl,
+ current_time) < 0)
+ pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV4_TCP_PKT(pkts[i]->packet_type) &&
+ do_tcp4_gro) {
+ if (gro_tcp4_reassemble(pkts[i], tcp_tbl,
+ current_time) < 0)
+ pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV4_UDP_PKT(pkts[i]->packet_type) &&
+ do_udp4_gro) {
+ if (gro_udp4_reassemble(pkts[i], udp_tbl,
+ current_time) < 0)
+ pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV6_TCP_PKT(pkts[i]->packet_type) &&
+ do_tcp6_gro) {
+ if (gro_tcp6_reassemble(pkts[i], tcp6_tbl,
+ current_time) < 0)
+ pkts[unprocess_num++] = pkts[i];
+ } else
+ pkts[unprocess_num++] = pkts[i];
+ }
+
+ return unprocess_num;
+}
+
+uint16_t
+rte_gro_timeout_flush(void *ctx,
+ uint64_t timeout_cycles,
+ uint64_t gro_types,
+ struct rte_mbuf **out,
+ uint16_t max_nb_out)
+{
+ struct gro_ctx *gro_ctx = ctx;
+ uint64_t flush_timestamp;
+ uint16_t num = 0;
+ uint16_t left_nb_out = max_nb_out;
+
+ gro_types = gro_types & gro_ctx->gro_types;
+ flush_timestamp = rte_rdtsc() - timeout_cycles;
+
+ if (gro_types & RTE_GRO_IPV4_VXLAN_TCP_IPV4) {
+ num = gro_vxlan_tcp4_tbl_timeout_flush(gro_ctx->tbls[
+ RTE_GRO_IPV4_VXLAN_TCP_IPV4_INDEX],
+ flush_timestamp, out, left_nb_out);
+ left_nb_out = max_nb_out - num;
+ }
+
+ if ((gro_types & RTE_GRO_IPV4_VXLAN_UDP_IPV4) && left_nb_out > 0) {
+ num += gro_vxlan_udp4_tbl_timeout_flush(gro_ctx->tbls[
+ RTE_GRO_IPV4_VXLAN_UDP_IPV4_INDEX],
+ flush_timestamp, &out[num], left_nb_out);
+ left_nb_out = max_nb_out - num;
+ }
+
+ if (gro_types & RTE_GRO_IPV4_GTP_TCP_IPV4) {
+ num = gro_gtp_tcp4_tbl_timeout_flush(gro_ctx->tbls[
+ RTE_GRO_IPV4_GTP_TCP_IPV4_INDEX],
+ flush_timestamp, out, left_nb_out);
+ left_nb_out = max_nb_out - num;
+ }
+
+ if ((gro_types & RTE_GRO_IPV4_GTP_UDP_IPV4) && left_nb_out > 0) {
+ num += gro_gtp_udp4_tbl_timeout_flush(gro_ctx->tbls[
+ RTE_GRO_IPV4_GTP_UDP_IPV4_INDEX],
+ flush_timestamp, &out[num], left_nb_out);
+ left_nb_out = max_nb_out - num;
+ }
+
+ /* If no available space in 'out', stop flushing. */
+ if ((gro_types & RTE_GRO_TCP_IPV4) && left_nb_out > 0) {
+ num += gro_tcp4_tbl_timeout_flush(
+ gro_ctx->tbls[RTE_GRO_TCP_IPV4_INDEX],
+ flush_timestamp,
+ &out[num], left_nb_out);
+ left_nb_out = max_nb_out - num;
+ }
+
+ /* If no available space in 'out', stop flushing. */
+ if ((gro_types & RTE_GRO_UDP_IPV4) && left_nb_out > 0) {
+ num += gro_udp4_tbl_timeout_flush(
+ gro_ctx->tbls[RTE_GRO_UDP_IPV4_INDEX],
+ flush_timestamp,
+ &out[num], left_nb_out);
+ left_nb_out = max_nb_out - num;
+ }
+
+ if ((gro_types & RTE_GRO_TCP_IPV6) && left_nb_out > 0) {
+ num += gro_tcp6_tbl_timeout_flush(
+ gro_ctx->tbls[RTE_GRO_TCP_IPV6_INDEX],
+ flush_timestamp,
+ &out[num], left_nb_out);
+
+ }
+
+ return num;
+}
+
+uint64_t
+rte_gro_get_pkt_count(void *ctx)
+{
+ struct gro_ctx *gro_ctx = ctx;
+ gro_tbl_pkt_count_fn pkt_count_fn;
+ uint64_t gro_types = gro_ctx->gro_types, flag;
+ uint64_t item_num = 0;
+ uint8_t i;
+
+ for (i = 0; i < RTE_GRO_TYPE_MAX_NUM && gro_types; i++) {
+ flag = 1ULL << i;
+ if ((gro_types & flag) == 0)
+ continue;
+
+ gro_types ^= flag;
+ pkt_count_fn = tbl_pkt_count_fn[i];
+ if (pkt_count_fn)
+ item_num += pkt_count_fn(gro_ctx->tbls[i]);
+ }
+
+ return item_num;
+}
diff --git a/gro/GRO Implementation Files/rte_gro.h b/gro/GRO Implementation Files/rte_gro.h
new file mode 100644
index 0000000..d6f235f
--- /dev/null
+++ b/gro/GRO Implementation Files/rte_gro.h
@@ -0,0 +1,204 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2017 Intel Corporation
+ */
+
+#ifndef _RTE_GRO_H_
+#define _RTE_GRO_H_
+
+/**
+ * @file
+ * Interface to GRO library
+ */
+
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RTE_GRO_MAX_BURST_ITEM_NUM 128U
+/**< the max number of packets that rte_gro_reassemble_burst()
+ * can process in each invocation.
+ */
+#define RTE_GRO_TYPE_MAX_NUM 64
+/**< the max number of supported GRO types */
+#define RTE_GRO_TYPE_SUPPORT_NUM 2
+/**< the number of currently supported GRO types */
+
+#define RTE_GRO_TCP_IPV4_INDEX 0
+#define RTE_GRO_TCP_IPV4 (1ULL << RTE_GRO_TCP_IPV4_INDEX)
+/**< TCP/IPv4 GRO flag */
+#define RTE_GRO_IPV4_VXLAN_TCP_IPV4_INDEX 1
+#define RTE_GRO_IPV4_VXLAN_TCP_IPV4 (1ULL << RTE_GRO_IPV4_VXLAN_TCP_IPV4_INDEX)
+/**< VxLAN TCP/IPv4 GRO flag. */
+#define RTE_GRO_UDP_IPV4_INDEX 2
+#define RTE_GRO_UDP_IPV4 (1ULL << RTE_GRO_UDP_IPV4_INDEX)
+/**< UDP/IPv4 GRO flag */
+#define RTE_GRO_IPV4_VXLAN_UDP_IPV4_INDEX 3
+#define RTE_GRO_IPV4_VXLAN_UDP_IPV4 (1ULL << RTE_GRO_IPV4_VXLAN_UDP_IPV4_INDEX)
+/**< VxLAN UDP/IPv4 GRO flag. */
+#define RTE_GRO_TCP_IPV6_INDEX 4
+#define RTE_GRO_TCP_IPV6 (1ULL << RTE_GRO_TCP_IPV6_INDEX)
+/**< TCP/IPv6 GRO flag. */
+#define RTE_GRO_IPV4_GTP_TCP_IPV4_INDEX 5
+#define RTE_GRO_IPV4_GTP_TCP_IPV4 (1ULL << RTE_GRO_IPV4_GTP_TCP_IPV4_INDEX)
+/**< GTP TCP/IPv4 GRO flag. */
+#define RTE_GRO_IPV4_GTP_UDP_IPV4_INDEX 6
+#define RTE_GRO_IPV4_GTP_UDP_IPV4 (1ULL << RTE_GRO_IPV4_GTP_UDP_IPV4_INDEX)
+/**< GTP UDP/IPv4 GRO flag. */
+
+/**
+ * Structure used to create GRO context objects or used to pass
+ * application-determined parameters to rte_gro_reassemble_burst().
+ */
+struct rte_gro_param {
+ uint64_t gro_types;
+ /**< desired GRO types */
+ uint16_t max_flow_num;
+ /**< max flow number */
+ uint16_t max_item_per_flow;
+ /**< max packet number per flow */
+ uint16_t socket_id;
+ /**< socket index for allocating GRO related data structures,
+ * like reassembly tables. When use rte_gro_reassemble_burst(),
+ * applications don't need to set this value.
+ */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This function create a GRO context object, which is used to merge
+ * packets in rte_gro_reassemble().
+ *
+ * @param param
+ * applications use it to pass needed parameters to create a GRO
+ * context object.
+ *
+ * @return
+ * if create successfully, return a pointer which points to the GRO
+ * context object. Otherwise, return NULL.
+ */
+void *rte_gro_ctx_create(const struct rte_gro_param *param);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This function destroys a GRO context object.
+ *
+ * @param ctx
+ * pointer points to a GRO context object.
+ */
+void rte_gro_ctx_destroy(void *ctx);
+
+/**
+ * This is one of the main reassembly APIs, which merges numbers of
+ * packets at a time. It doesn't check if input packets have correct
+ * checksums and doesn't re-calculate checksums for merged packets.
+ * It assumes the packets are complete (i.e., MF==0 && frag_off==0),
+ * when IP fragmentation is possible (i.e., DF==0). The GROed packets
+ * are returned as soon as the function finishes.
+ *
+ * @param pkts
+ * Pointer array pointing to the packets to reassemble. Besides, it
+ * keeps MBUF addresses for the GROed packets.
+ * @param nb_pkts
+ * The number of packets to reassemble
+ * @param param
+ * Application-determined parameters for reassembling packets.
+ *
+ * @return
+ * The number of packets after been GROed. If no packets are merged,
+ * the return value is equals to nb_pkts.
+ */
+uint16_t rte_gro_reassemble_burst(struct rte_mbuf **pkts,
+ uint16_t nb_pkts,
+ const struct rte_gro_param *param);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Reassembly function, which tries to merge input packets with the
+ * existed packets in the reassembly tables of a given GRO context.
+ * It doesn't check if input packets have correct checksums and doesn't
+ * re-calculate checksums for merged packets. Additionally, it assumes
+ * the packets are complete (i.e., MF==0 && frag_off==0), when IP
+ * fragmentation is possible (i.e., DF==0).
+ *
+ * If the input packets have invalid parameters (e.g. no data payload,
+ * unsupported GRO types), they are returned to applications. Otherwise,
+ * they are either merged or inserted into the table. Applications need
+ * to flush packets from the tables by flush API, if they want to get the
+ * GROed packets.
+ *
+ * @param pkts
+ * Packets to reassemble. It's also used to store the unprocessed packets.
+ * @param nb_pkts
+ * The number of packets to reassemble
+ * @param ctx
+ * GRO context object pointer
+ *
+ * @return
+ * The number of unprocessed packets.
+ */
+uint16_t rte_gro_reassemble(struct rte_mbuf **pkts,
+ uint16_t nb_pkts,
+ void *ctx);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This function flushes the timeout packets from the reassembly tables
+ * of desired GRO types. The max number of flushed packets is the
+ * element number of 'out'.
+ *
+ * Additionally, the flushed packets may have incorrect checksums, since
+ * this function doesn't re-calculate checksums for merged packets.
+ *
+ * @param ctx
+ * GRO context object pointer.
+ * @param timeout_cycles
+ * The max TTL for packets in reassembly tables, measured in nanosecond.
+ * @param gro_types
+ * This function flushes packets whose GRO types are specified by
+ * gro_types.
+ * @param out
+ * Pointer array used to keep flushed packets.
+ * @param max_nb_out
+ * The element number of 'out'. It's also the max number of timeout
+ * packets that can be flushed finally.
+ *
+ * @return
+ * The number of flushed packets.
+ */
+uint16_t rte_gro_timeout_flush(void *ctx,
+ uint64_t timeout_cycles,
+ uint64_t gro_types,
+ struct rte_mbuf **out,
+ uint16_t max_nb_out);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This function returns the number of packets in all reassembly tables
+ * of a given GRO context.
+ *
+ * @param ctx
+ * GRO context object pointer.
+ *
+ * @return
+ * The number of packets in the tables.
+ */
+uint64_t rte_gro_get_pkt_count(void *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_GRO_H_ */
diff --git a/gro/PCAP Files/fragmented.pcap b/gro/PCAP Files/fragmented.pcap
new file mode 100644
index 0000000..bc96fed
Binary files /dev/null and b/gro/PCAP Files/fragmented.pcap differ
diff --git a/gro/PCAP Files/gtp_simulation.pcap b/gro/PCAP Files/gtp_simulation.pcap
new file mode 100644
index 0000000..13d9f53
Binary files /dev/null and b/gro/PCAP Files/gtp_simulation.pcap differ
diff --git a/gro/PCAP Files/out_of_order.pcap b/gro/PCAP Files/out_of_order.pcap
new file mode 100644
index 0000000..934e2a5
Binary files /dev/null and b/gro/PCAP Files/out_of_order.pcap differ
diff --git a/gro/PCAP Files/out_of_orderbetter.pcap b/gro/PCAP Files/out_of_orderbetter.pcap
new file mode 100644
index 0000000..3ff89a9
Binary files /dev/null and b/gro/PCAP Files/out_of_orderbetter.pcap differ
diff --git a/gro/PCAP Files/test.pcap b/gro/PCAP Files/test.pcap
new file mode 100644
index 0000000..8177c3d
Binary files /dev/null and b/gro/PCAP Files/test.pcap differ
diff --git a/gro/PCAP Files/timing_gaps.pcap b/gro/PCAP Files/timing_gaps.pcap
new file mode 100644
index 0000000..d4e6cff
Binary files /dev/null and b/gro/PCAP Files/timing_gaps.pcap differ
diff --git a/gro/Pics/analysis1.png b/gro/Pics/analysis1.png
new file mode 100644
index 0000000..f783ea7
Binary files /dev/null and b/gro/Pics/analysis1.png differ
diff --git a/gro/Pics/analysis2.png b/gro/Pics/analysis2.png
new file mode 100644
index 0000000..7a5d2c1
Binary files /dev/null and b/gro/Pics/analysis2.png differ
diff --git a/gro/Pics/ch.png b/gro/Pics/ch.png
new file mode 100644
index 0000000..1c9a958
Binary files /dev/null and b/gro/Pics/ch.png differ
diff --git a/gro/Pics/imfl.png b/gro/Pics/imfl.png
new file mode 100644
index 0000000..ced12ec
Binary files /dev/null and b/gro/Pics/imfl.png differ
diff --git a/gro/Pics/ooo.png b/gro/Pics/ooo.png
new file mode 100644
index 0000000..3204ed7
Binary files /dev/null and b/gro/Pics/ooo.png differ
diff --git a/gro/Pics/str1.png b/gro/Pics/str1.png
new file mode 100644
index 0000000..b343c2b
Binary files /dev/null and b/gro/Pics/str1.png differ
diff --git a/gro/Pics/str2.png b/gro/Pics/str2.png
new file mode 100644
index 0000000..eec820f
Binary files /dev/null and b/gro/Pics/str2.png differ
diff --git a/gro/Pics/str3.png b/gro/Pics/str3.png
new file mode 100644
index 0000000..aa0bf3a
Binary files /dev/null and b/gro/Pics/str3.png differ
diff --git a/gro/Pics/str4.png b/gro/Pics/str4.png
new file mode 100644
index 0000000..a4edf47
Binary files /dev/null and b/gro/Pics/str4.png differ
diff --git a/gro/Pics/tcp.png b/gro/Pics/tcp.png
new file mode 100644
index 0000000..2064854
Binary files /dev/null and b/gro/Pics/tcp.png differ
diff --git a/gro/Pics/th1.png b/gro/Pics/th1.png
new file mode 100644
index 0000000..1cdeec9
Binary files /dev/null and b/gro/Pics/th1.png differ
diff --git a/gro/Pics/th124.png b/gro/Pics/th124.png
new file mode 100644
index 0000000..cc972da
Binary files /dev/null and b/gro/Pics/th124.png differ
diff --git a/gro/Pics/th2.png b/gro/Pics/th2.png
new file mode 100644
index 0000000..b90ea10
Binary files /dev/null and b/gro/Pics/th2.png differ
diff --git a/gro/Pics/th4.png b/gro/Pics/th4.png
new file mode 100644
index 0000000..035463c
Binary files /dev/null and b/gro/Pics/th4.png differ
diff --git a/gro/Pics/tout.png b/gro/Pics/tout.png
new file mode 100644
index 0000000..00a73cd
Binary files /dev/null and b/gro/Pics/tout.png differ
diff --git a/gro/Pics/tpmd1.png b/gro/Pics/tpmd1.png
new file mode 100644
index 0000000..b026700
Binary files /dev/null and b/gro/Pics/tpmd1.png differ
diff --git a/gro/Pics/tpmd2.png b/gro/Pics/tpmd2.png
new file mode 100644
index 0000000..ccdb333
Binary files /dev/null and b/gro/Pics/tpmd2.png differ
diff --git a/gro/Pics/tpmd4.png b/gro/Pics/tpmd4.png
new file mode 100644
index 0000000..c02aa36
Binary files /dev/null and b/gro/Pics/tpmd4.png differ
diff --git a/gro/Pics/udp.png b/gro/Pics/udp.png
new file mode 100644
index 0000000..d270a5e
Binary files /dev/null and b/gro/Pics/udp.png differ
diff --git a/gro/Pics/udpngh.png b/gro/Pics/udpngh.png
new file mode 100644
index 0000000..cc5cc62
Binary files /dev/null and b/gro/Pics/udpngh.png differ
diff --git a/gro/Pics/udporder.png b/gro/Pics/udporder.png
new file mode 100644
index 0000000..148521a
Binary files /dev/null and b/gro/Pics/udporder.png differ
diff --git a/gro/README.md b/gro/README.md
new file mode 100644
index 0000000..88d86ab
--- /dev/null
+++ b/gro/README.md
@@ -0,0 +1,388 @@
+# DPDK Generic Receive Offload (GRO)
+This document outlines the architecture and operation of the Generic Receive Offload (GRO) library within the Data Plane Development Kit (DPDK).
+
+## Architecture
+
+Generic Receive Offload files are on `/lib/gro/` directory. `rte_gro` is responsible for reading packets in bursts, containing 512 packets of different protocols at most then it will pass the packets with the same type to relative gro_ptype file to be merged. GRO on DPDK supports different packet types: **TCP4**, **TCP6**, **UDP4**, **VXLAN_TCP4**, **VXLAN_UDP4**
+
+### 1. Naming Convention
+- each packet is called an item in source code (both merged and unmerged).
+- common key of an item is a tuple of (source/destination IP, source/destination port, protocol)
+- each flow contains items with the same common key
+- two items with same common key with consecutive sequence numbers are called neighbors
+- each reassembly table contains flows of the same protocol
+
+### 2. Parameters
+GRO has 3 modes based on its [Documentation](https://doc.dpdk.org/dts-20.02/test_plans/dpdk_gro_lib_test_plan.html#test-case1-dpdk-gro-lightmode-test-with-tcp-ipv4-traffic). Different modes can be set via flush parameter in testpmd interactive mode. `rte_gro_timeout_flush` sends the packet to upper layer when timeout occurs after flush parameter value of receive burst calls. it will also update the header if the packet is merged. If any merged packet reaches maximum size of its type (like 64KB for TCP4) then it is sent to upper layer
+
+### 3. Data Structures
+General reassembly table of `rte_gro` passes different packet types to protocol-specific reassembly tables like TCP where merging is handled. Each reassembly table is a hash table, where its key is its protocol common key and its value contains a pointer to the head mbuf of the reassembled packet and other state information (e.g., the TCP sequence number it's waiting for next). The main responsibilites of reassembly tables' associated functions are:
+- **Check TCP sequence numbers** to ensure packets are contiguous.
+- **Compare the TCP 5-tuple** (source/destination IP, source/destination port, protocol) for flow matching.
+- **Handle TCP flags** like FIN and PUSH correctly, which often signal the end of a merge opportunity.
+
+# Instrumentation
+Trace points can be put inside function bodies of both header and C files that their name start with `rte_`.These trace points may emit the input parameters of functions or extra arguments in `gro_trace.h` and each of their names should be regestered in `gro_trace_points.c`, determining dumped trace point name. As `rte_gro.h` functions do not contain bodies, we shall use normal instrumentation instead of fast path. It is neccessary to add file names to `meson.build` located in `lib/gro/`. Changed and new files are accessible via gro folder of this repo.
+
+# Setup
+
+We use dpdk-testpmd to forward packets between two virtual ethernet (veth) ports and experiment with various offloading options of dpdk including gro.
+
+Two namespaces and two veth interfaces are created, with one end of each veth interface in each of the namespaces and the other end in the host side. The host end of veth interfaces is connected to dpdk-testpmd and traffic is sent from one namespace to the other using iperf3.
+
+**Create Two veth Interfaces**
+```shell
+sudo ip netns del ns1
+sudo ip link add veth-ns1 type veth peer name veth-host1
+sudo ip addr add 10.1.1.2/24 dev veth-host1
+sudo ip link set veth-host1 up
+
+sudo ip netns del ns2
+sudo ip link add veth-ns2 type veth peer name veth-host2
+sudo ip addr add 10.1.1.4/24 dev veth-host2
+sudo ip link set veth-host2 up
+```
+
+**Create and Configure Namespace ns1**
+```shell
+sudo ip netns add ns1
+sudo ip link set veth-ns1 netns ns1
+sudo ip netns exec ns1 ip link set veth-ns1 up
+sudo ip netns exec ns1 ifconfig veth-ns1 10.1.1.1/24 up
+```
+
+**Create and Configure Namespace ns2**
+```shell
+sudo ip netns add ns2
+sudo ip link set veth-ns2 netns ns2
+sudo ip netns exec ns2 ip link set veth-ns2 up
+sudo ip netns exec ns2 ifconfig veth-ns2 10.1.1.3/24 up
+```
+
+**Turn off tx/rx offloads on the kernel driver:**
+Some offload features on the kernel side mess up testpmd when forwarding tcp traffic. Turn them off:
+
+```shell
+sudo ip netns exec ns1 ethtool -K veth-ns1 tso on
+sudo ethtool -K veth-host2 gro off
+sudo ethtool -K veth-host1 tx off rx off
+sudo ethtool -K veth-host2 tx off rx off
+sudo ip netns exec ns1 ethtool -K veth-ns1 tx off rx off
+sudo ip netns exec ns2 ethtool -K veth-ns2 tx off rx off
+```
+
+**Meson Configuration of Build Environment**
+```shell
+meson setup build \
+-Dexamples=all \
+-Dlibdir=lib \
+-Denable_trace_fp=true \
+-Dc_args="-finstrument-functions"
+```
+
+**Ninja for Build and Install**
+```shell
+cd build
+ninja
+sudo meson install
+sudo ldconfig
+```
+
+**Hugepages Configuration**
+```shell
+sudo sysctl -w vm.nr_hugepages=1024
+mount -t hugetlbfs none /dev/hugepages
+```
+
+
+**Run dpdk-testpmd**
+```shell
+sudo dpdk-testpmd -l 0-3 -n4 --vdev 'eth_af_packet0,iface=veth-host1' --vdev 'eth_af_packet1,iface=veth-host2' --trace=lib.gro.* --trace-mode=overwrite --trace-bufsz=20M -- -i --forward-mode=io
+```
+
+**Interactive configure dpdk-testpmd**
+```shell
+set fwd csum
+csum mac-swap off 0
+csum mac-swap off 1
+stop
+port stop 0
+port stop 1
+set port 0 gro on
+set port 1 gro on
+set gro flush 1
+port start 0
+port start 1
+start
+```
+
+**Generate Traffic**
+
+On namespace ns2 run the server side
+```shell
+sudo ip netns exec ns2 iperf3 -s -B 10.1.1.3
+```
+
+On namespace ns1 run the client side
+```shell
+sudo ip netns exec ns1 iperf3 -c 10.1.1.3 -t 0
+```
+
+# Performance Tuning
+
+In this section, we summarize the evaluation of the **Generic Receive Offload (GRO)** feature in the **Data Plane Development Kit (DPDK)**. The focus was to analyze the impact of GRO on packet reception and transmission, particularly how different **flush values** affect network performance.
+
+### Goal
+
+The tests aim to measure:
+
+* **Throughput**: How fast data is transmitted over the network.
+* **Packet Processing**: The number of packets received and transmitted.
+
+We tested flush values of 1, 2, and 4 to explore the trade-offs between packet aggregation and throughput.
+
+---
+
+## Flush 1
+
+**Command**: `set gro flush 1`
+
+### Test Results
+
+#### Throughput (iperf3)
+
+* **Average Throughput**: 2.71 Gbps
+* **Observation**: Throughput was stable with minor fluctuations.
+
+
+
+#### Packet Statistics (dpdk-testpmd)
+
+* **RX Packets**: \~7.03M, with no errors or drops
+* **TX Packets**: \~3.29M, roughly half of the received packets
+* **RX/TX Ratio**: \~2.14, indicating effective packet merging
+
+**Observation**: GRO successfully reduced the packet rate while maintaining the same data volume.
+
+
+
+---
+
+## Flush 2
+
+**Command**: `set gro flush 2`
+
+### Test Results
+
+#### Throughput (iperf3)
+
+* **Average Throughput**: 2.65 Gbps
+* **Observation**: Slightly lower than Flush 1 due to added buffering, causing minor latency and occasional TCP retransmissions.
+
+
+
+#### Packet Statistics (dpdk-testpmd)
+
+* **RX Packets**: \~6.76M
+* **TX Packets**: \~3.15M
+* **RX/TX Ratio**: \~2.15, showing slightly improved packet merging
+
+
+
+---
+
+## Flush 4
+
+**Command**: `set gro flush 4`
+
+### Test Results
+
+#### Throughput (iperf3)
+
+* **Average Throughput**: 2.64 Gbps
+
+
+
+#### Packet Statistics (dpdk-testpmd)
+
+* **RX Packets**: \~6.83M
+* **TX Packets**: \~3.18M
+* **RX/TX Ratio**: \~2.15, confirming effective packet aggregation
+
+
+
+---
+
+## Conclusion
+
+
+
+
+
+
+
+
+* **Flush 1** gives the highest throughput (2.71 Gbps) but generates more packets to process.
+* **Flush 2** slightly reduces throughput (\~2.65 Gbps) but improves packet aggregation efficiency.
+* **Flush 4** achieves the best balance between throughput and packet merging, reducing CPU load without sacrificing much speed.
+
+
+# Analysis
+## 1. Light mode
+`rte_gro_reassemble_burst` can handle at most 32 packets in each call and merge them as most as possible. There is no timeout set for the packets since they will be flushed as soon as they get merged.
+
+---
+> ▸ _`pkts` is a pointer to mbuf linked list. `nb_pkts` is the number of packets that are supposed to be processed on that specific burst call, varying from 1 to 32. `param_max_flow_types` is the maximum number of flows inwhich reassembly table will store till sending them to application layer which is set to 4._
+
+
+
+## 2. Heavy mode
+with flush parameter set to 2 and above heavy mode is activated, making gro library to process packets by including timeout parameter.
+
+---
+> ▸ _`rte_gro_ctx_create` is called 3 times in each run in first place. At first glance it may seem contradictory where `param_max_item_per_flow` is set to 512, but this context is general and responsible for all protocols but will send 32 packets to specific protocol to be merged._
+
+
+
+
+# GRO Behavior for Out-of-Order Packets
+
+## Introduction
+
+In this report, we explore the behavior of **Generic Receive Offload (GRO)** when handling **out-of-order (OOO)** TCP packets. We will detail the sequence of events when GRO encounters packets that do not arrive in the expected order, how these packets are processed, and how they are eventually merged or stored.
+
+## Flow Overview
+
+GRO is responsible for efficiently combining multiple incoming TCP packets into larger segments before they are passed to the application. When packets arrive out of order, GRO uses various mechanisms to ensure data integrity and efficient processing.
+
+## Behavior of GRO for Out-of-Order Packets
+
+### 1. Initial Packet Processing
+
+* **Validation of TCP Header**: GRO first checks if the incoming packet's TCP flags (e.g., ACK, PSH, FIN) are valid. Packets with invalid flags (e.g., SYN, RST) are rejected.
+* **Payload Size**: The payload length is checked to ensure it is greater than zero. If not, the packet is discarded.
+* **Flow Lookup**: GRO attempts to find an existing flow for the incoming packet. If no flow is found, a new flow is created (typically for pure ACK packets).
+
+```c
+if (unlikely(INVALID_TCP_HDRLEN(pkt->l4_len))) return -1;
+if (tcp_hdr->tcp_flags & ~VALID_GRO_TCP_FLAGS) return -1;
+tcp_dl = pkt->pkt_len - hdr_len;
+if (tcp_dl <= 0) return -1;
+```
+
+### 2. Neighbor Search and Sequence Check
+
+* If a matching flow is found, GRO checks whether the incoming packet can be merged with existing packets in that flow. This is done by comparing the sequence number and IP ID.
+* **Sequence Check**: The sequence number (`sent_seq`) and IP ID of the incoming packet are compared with the existing packets. If they match, the packets are merged.
+
+```c
+cmp = check_seq_option(&items[cur_idx], tcp_hdr,
+ sent_seq, ip_id, pkt->l4_len, tcp_dl, 0, is_atomic);
+if (cmp) {
+ if (merge_two_tcp_packets(&items[cur_idx], pkt, cmp, sent_seq,
+ tcp_hdr->tcp_flags, ip_id, 0))
+ return 1;
+}
+```
+
+### 3. Packet Merging
+
+* **Appending or Prepending**: If the incoming packet is part of an existing flow, it is either appended or prepended to the existing packets based on the sequence number comparison.
+* **Limits on Merging**: There are certain constraints on how much data can be merged, such as the total IP packet size and the number of segments.
+
+```c
+if (pkt_head->pkt_len - l2_len + pkt_tail->pkt_len - hdr_len > MAX_IP_PKT_LENGTH) return 0;
+if (unlikely(pkt_head->nb_segs >= 20)) return 0;
+```
+
+* If the merge is successful, the merged packet is updated and the flow is updated accordingly.
+
+### 4. Out-of-Order Packet Storage
+
+* If the incoming packet cannot be merged with existing packets, it is stored as a **separate island** in the flow’s list. It is added at the end of the linked list of packets, waiting for the missing segments to arrive.
+
+
+
+
+
+### 5. Immediate Flush for PSH/FIN Packets
+
+* If the packet contains the **PSH** or **FIN** flag, even if it is out of order, it is processed and flushed immediately. This ensures that the TCP flow can complete promptly, even when there are gaps in the sequence of received packets.
+
+
+
+
+### 6. Timeout Flush
+
+* GRO periodically checks if any packets in the flow have timed out and are ready to be processed. Packets with a `start_time` earlier than the current timestamp are flushed to the application, and the flow is updated.
+* **Flow Processing**: If multiple segments have been merged, the total IPv4 length is updated.
+
+
+
+
+
+
+## Summary
+
+* **Out-of-order packets** are handled by **searching for a matching flow**, **checking if they can be merged** with existing packets based on sequence number and IP ID, and either merging them or storing them as separate "islands" for future merging.
+* **PSH/FIN packets** are **immediately flushed** to ensure timely processing, even if they arrive out of order.
+* **Timeouts** trigger the flushing of merged packets that have been held for too long.
+
+
+
+
+
+
+---
+
+# GRO Behavior for Fragmented Packets (TCP vs UDP)
+
+## TCP/IPv4 (fragmentation)
+
+* **Scope:** TCP-GRO is **not** an IP reassembly path; it only merges **complete** TCP/IPv4 packets (full L4 header + payload > 0).
+ [For more details, refer to the official DPDK documentation.](https://doc.dpdk.org/guides-18.05/prog_guide/generic_receive_offload_lib.html)
+
+
+
+
+* **Fragmented input:** If a packet is IPv4-fragmented (MF=1 or `frag_offset>0`), it is **not reassembled** by TCP-GRO and is **skipped** by the library .
+* **DF/ID rule for complete packets:**
+
+ * **DF=1 (atomic):** ignore IPv4 ID.
+ * **DF=0:** require IPv4 ID continuity **only** to validate merging of complete packets (still not IP reassembly).
+
+## UDP/IPv4 (fragmentation)
+
+* **Scope:** GRO-UDP targets **IPv4 fragments**. Non-fragmented UDP packets are **skipped** (no merge).
+
+
+* **Fragment handling:** Fragments of the same datagram are grouped by **IP-ID**; adjacency is determined by **fragment offset + length**.
+
+
+
+* **Finalization:** If a fragment can’t merge immediately, it’s kept for that datagram; on timeout/flush, contiguous fragments are merged and the IPv4 header is updated (total length set, **MF** cleared when the last fragment is present).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/gro/Trace Files/gro_trace.h b/gro/Trace Files/gro_trace.h
new file mode 100644
index 0000000..4a771f5
--- /dev/null
+++ b/gro/Trace Files/gro_trace.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell International Ltd.
+ */
+
+#ifndef GRO_TRACE_H
+#define GRO_TRACE_H
+
+/**
+ * @file
+ *
+ * API for gro trace support
+ */
+
+#include
+#include
+
+#include "rte_gro.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+RTE_TRACE_POINT(
+ rte_gro_trace_ctx_create,
+ RTE_TRACE_POINT_ARGS(const struct rte_gro_param *param),
+ rte_trace_point_emit_u64(param->gro_types);
+ rte_trace_point_emit_u16(param->max_flow_num);
+ rte_trace_point_emit_u16(param->max_item_per_flow);
+)
+
+RTE_TRACE_POINT(
+ rte_gro_trace_ctx_destroy,
+ RTE_TRACE_POINT_ARGS(const void *ctx),
+ rte_trace_point_emit_ptr(ctx);
+)
+
+RTE_TRACE_POINT(
+ rte_gro_trace_reassemble_burst,
+ RTE_TRACE_POINT_ARGS(struct rte_mbuf **pkts,
+ uint16_t nb_pkts,
+ const struct rte_gro_param *param),
+ rte_trace_point_emit_ptr(pkts);
+ rte_trace_point_emit_u16(nb_pkts);
+ rte_trace_point_emit_u64(param->gro_types);
+ rte_trace_point_emit_u16(param->max_flow_num);
+ rte_trace_point_emit_u16(param->max_item_per_flow);
+)
+
+RTE_TRACE_POINT(
+ rte_gro_trace_reassemble,
+ RTE_TRACE_POINT_ARGS(struct rte_mbuf **pkts,
+ uint16_t nb_pkts,
+ const void *ctx),
+ rte_trace_point_emit_ptr(pkts);
+ rte_trace_point_emit_u16(nb_pkts);
+ rte_trace_point_emit_ptr(ctx);
+)
+
+RTE_TRACE_POINT(
+ rte_gro_trace_timeout_flush,
+ RTE_TRACE_POINT_ARGS(const void *ctx,
+ uint64_t timeout_cycles,
+ uint64_t gro_types,
+ struct rte_mbuf **out,
+ uint16_t max_nb_out),
+ rte_trace_point_emit_ptr(ctx);
+ rte_trace_point_emit_u64(timeout_cycles);
+ rte_trace_point_emit_u64(gro_types);
+ rte_trace_point_emit_ptr(out);
+ rte_trace_point_emit_u16(max_nb_out);
+)
+
+RTE_TRACE_POINT(
+ rte_gro_trace_get_pkt_count,
+ RTE_TRACE_POINT_ARGS(const void *ctx),
+ rte_trace_point_emit_ptr(ctx);
+)
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRO_TRACE_H */
+
diff --git a/gro/Trace Files/gro_trace_points.c b/gro/Trace Files/gro_trace_points.c
new file mode 100644
index 0000000..1753c74
--- /dev/null
+++ b/gro/Trace Files/gro_trace_points.c
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include
+
+#include
+
+RTE_TRACE_POINT_REGISTER(rte_gro_trace_ctx_create,
+ lib.gro.ctx.create)
+
+RTE_TRACE_POINT_REGISTER(rte_gro_trace_ctx_destroy,
+ lib.gro.ctx.destroy)
+
+RTE_TRACE_POINT_REGISTER(rte_gro_trace_reassemble_burst,
+ lib.gro.reassemble.burst)
+
+RTE_TRACE_POINT_REGISTER(rte_gro_trace_reassemble,
+ lib.gro.reassemble)
+
+RTE_TRACE_POINT_REGISTER(rte_gro_trace_timeout_flush,
+ lib.gro.timeout.flush)
+
+RTE_TRACE_POINT_REGISTER(rte_gro_trace_get_pkt_count,
+ lib.gro.get.pkt.count)
+
diff --git a/gro/Trace Files/meson.build b/gro/Trace Files/meson.build
new file mode 100644
index 0000000..c34facb
--- /dev/null
+++ b/gro/Trace Files/meson.build
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2017 Intel Corporation
+
+sources = files(
+ 'rte_gro.c',
+ 'gro_tcp4.c',
+ 'gro_tcp6.c',
+ 'gro_udp4.c',
+ 'gro_vxlan_tcp4.c',
+ 'gro_vxlan_udp4.c',
+ 'gro_trace_points.c',
+)
+headers = files(
+ 'rte_gro.h',
+ 'gro_trace.h',
+)
+deps += ['ethdev']
diff --git a/gro/Trace Files/rte_gro.c b/gro/Trace Files/rte_gro.c
new file mode 100644
index 0000000..3ae15bb
--- /dev/null
+++ b/gro/Trace Files/rte_gro.c
@@ -0,0 +1,525 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2017 Intel Corporation
+ */
+
+#include
+#include
+#include
+
+#include "rte_gro.h"
+#include "gro_tcp4.h"
+#include "gro_tcp6.h"
+#include "gro_udp4.h"
+#include "gro_vxlan_tcp4.h"
+#include "gro_vxlan_udp4.h"
+
+#include "gro_trace.h"
+
+typedef void *(*gro_tbl_create_fn)(uint16_t socket_id,
+ uint16_t max_flow_num,
+ uint16_t max_item_per_flow);
+typedef void (*gro_tbl_destroy_fn)(void *tbl);
+typedef uint32_t (*gro_tbl_pkt_count_fn)(void *tbl);
+
+static gro_tbl_create_fn tbl_create_fn[RTE_GRO_TYPE_MAX_NUM] = {
+ gro_tcp4_tbl_create, gro_vxlan_tcp4_tbl_create,
+ gro_udp4_tbl_create, gro_vxlan_udp4_tbl_create, gro_tcp6_tbl_create, NULL};
+static gro_tbl_destroy_fn tbl_destroy_fn[RTE_GRO_TYPE_MAX_NUM] = {
+ gro_tcp4_tbl_destroy, gro_vxlan_tcp4_tbl_destroy,
+ gro_udp4_tbl_destroy, gro_vxlan_udp4_tbl_destroy,
+ gro_tcp6_tbl_destroy,
+ NULL};
+static gro_tbl_pkt_count_fn tbl_pkt_count_fn[RTE_GRO_TYPE_MAX_NUM] = {
+ gro_tcp4_tbl_pkt_count, gro_vxlan_tcp4_tbl_pkt_count,
+ gro_udp4_tbl_pkt_count, gro_vxlan_udp4_tbl_pkt_count,
+ gro_tcp6_tbl_pkt_count,
+ NULL};
+
+#define IS_IPV4_TCP_PKT(ptype) (RTE_ETH_IS_IPV4_HDR(ptype) && \
+ ((ptype & RTE_PTYPE_L4_TCP) == RTE_PTYPE_L4_TCP) && \
+ ((ptype & RTE_PTYPE_L4_FRAG) != RTE_PTYPE_L4_FRAG) && \
+ (RTE_ETH_IS_TUNNEL_PKT(ptype) == 0))
+
+/* GRO with extension headers is not supported */
+#define IS_IPV6_TCP_PKT(ptype) (RTE_ETH_IS_IPV6_HDR(ptype) && \
+ ((ptype & RTE_PTYPE_L4_TCP) == RTE_PTYPE_L4_TCP) && \
+ ((ptype & RTE_PTYPE_L4_FRAG) != RTE_PTYPE_L4_FRAG) && \
+ (RTE_ETH_IS_TUNNEL_PKT(ptype) == 0))
+
+#define IS_IPV4_UDP_PKT(ptype) (RTE_ETH_IS_IPV4_HDR(ptype) && \
+ ((ptype & RTE_PTYPE_L4_UDP) == RTE_PTYPE_L4_UDP) && \
+ (RTE_ETH_IS_TUNNEL_PKT(ptype) == 0))
+
+#define IS_IPV4_VXLAN_TCP4_PKT(ptype) (RTE_ETH_IS_IPV4_HDR(ptype) && \
+ ((ptype & RTE_PTYPE_L4_UDP) == RTE_PTYPE_L4_UDP) && \
+ ((ptype & RTE_PTYPE_L4_FRAG) != RTE_PTYPE_L4_FRAG) && \
+ ((ptype & RTE_PTYPE_TUNNEL_VXLAN) == \
+ RTE_PTYPE_TUNNEL_VXLAN) && \
+ ((ptype & RTE_PTYPE_INNER_L4_TCP) == \
+ RTE_PTYPE_INNER_L4_TCP) && \
+ (((ptype & RTE_PTYPE_INNER_L3_MASK) == \
+ RTE_PTYPE_INNER_L3_IPV4) || \
+ ((ptype & RTE_PTYPE_INNER_L3_MASK) == \
+ RTE_PTYPE_INNER_L3_IPV4_EXT) || \
+ ((ptype & RTE_PTYPE_INNER_L3_MASK) == \
+ RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN)))
+
+#define IS_IPV4_VXLAN_UDP4_PKT(ptype) (RTE_ETH_IS_IPV4_HDR(ptype) && \
+ ((ptype & RTE_PTYPE_L4_UDP) == RTE_PTYPE_L4_UDP) && \
+ ((ptype & RTE_PTYPE_TUNNEL_VXLAN) == \
+ RTE_PTYPE_TUNNEL_VXLAN) && \
+ ((ptype & RTE_PTYPE_INNER_L4_UDP) == \
+ RTE_PTYPE_INNER_L4_UDP) && \
+ (((ptype & RTE_PTYPE_INNER_L3_MASK) == \
+ RTE_PTYPE_INNER_L3_IPV4) || \
+ ((ptype & RTE_PTYPE_INNER_L3_MASK) == \
+ RTE_PTYPE_INNER_L3_IPV4_EXT) || \
+ ((ptype & RTE_PTYPE_INNER_L3_MASK) == \
+ RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN)))
+
+/*
+ * GRO context structure. It keeps the table structures, which are
+ * used to merge packets, for different GRO types. Before using
+ * rte_gro_reassemble(), applications need to create the GRO context
+ * first.
+ */
+struct gro_ctx {
+ /* GRO types to perform */
+ uint64_t gro_types;
+ /* reassembly tables */
+ void *tbls[RTE_GRO_TYPE_MAX_NUM];
+};
+
+void *
+rte_gro_ctx_create(const struct rte_gro_param *param)
+{
+ struct gro_ctx *gro_ctx;
+ gro_tbl_create_fn create_tbl_fn;
+ uint64_t gro_type_flag = 0;
+ uint64_t gro_types = 0;
+ uint8_t i;
+
+ gro_ctx = rte_zmalloc_socket(__func__,
+ sizeof(struct gro_ctx),
+ RTE_CACHE_LINE_SIZE,
+ param->socket_id);
+ if (gro_ctx == NULL)
+ return NULL;
+
+ for (i = 0; i < RTE_GRO_TYPE_MAX_NUM; i++) {
+ gro_type_flag = 1ULL << i;
+ if ((param->gro_types & gro_type_flag) == 0)
+ continue;
+
+ create_tbl_fn = tbl_create_fn[i];
+ if (create_tbl_fn == NULL)
+ continue;
+
+ gro_ctx->tbls[i] = create_tbl_fn(param->socket_id,
+ param->max_flow_num,
+ param->max_item_per_flow);
+ if (gro_ctx->tbls[i] == NULL) {
+ /* destroy all created tables */
+ gro_ctx->gro_types = gro_types;
+ rte_gro_ctx_destroy(gro_ctx);
+ return NULL;
+ }
+ gro_types |= gro_type_flag;
+ }
+ gro_ctx->gro_types = param->gro_types;
+
+ rte_gro_trace_ctx_create(param);
+
+ return gro_ctx;
+}
+
+void
+rte_gro_ctx_destroy(void *ctx)
+{
+ gro_tbl_destroy_fn destroy_tbl_fn;
+ struct gro_ctx *gro_ctx = ctx;
+ uint64_t gro_type_flag;
+ uint8_t i;
+
+ for (i = 0; i < RTE_GRO_TYPE_MAX_NUM; i++) {
+ gro_type_flag = 1ULL << i;
+ if ((gro_ctx->gro_types & gro_type_flag) == 0)
+ continue;
+ destroy_tbl_fn = tbl_destroy_fn[i];
+ if (destroy_tbl_fn)
+ destroy_tbl_fn(gro_ctx->tbls[i]);
+ }
+
+ rte_gro_trace_ctx_destroy(ctx);
+
+ rte_free(gro_ctx);
+}
+
+uint16_t
+rte_gro_reassemble_burst(struct rte_mbuf **pkts,
+ uint16_t nb_pkts,
+ const struct rte_gro_param *param)
+{
+ /* allocate a reassembly table for TCP/IPv4 GRO */
+ struct gro_tcp4_tbl tcp_tbl;
+ struct gro_tcp4_flow tcp_flows[RTE_GRO_MAX_BURST_ITEM_NUM];
+ struct gro_tcp_item tcp_items[RTE_GRO_MAX_BURST_ITEM_NUM] = {{0} };
+
+ struct gro_tcp6_tbl tcp6_tbl;
+ struct gro_tcp6_flow tcp6_flows[RTE_GRO_MAX_BURST_ITEM_NUM];
+ struct gro_tcp_item tcp6_items[RTE_GRO_MAX_BURST_ITEM_NUM] = {{0} };
+
+ /* allocate a reassembly table for UDP/IPv4 GRO */
+ struct gro_udp4_tbl udp_tbl;
+ struct gro_udp4_flow udp_flows[RTE_GRO_MAX_BURST_ITEM_NUM];
+ struct gro_udp4_item udp_items[RTE_GRO_MAX_BURST_ITEM_NUM] = {{0} };
+
+ /* Allocate a reassembly table for VXLAN TCP GRO */
+ struct gro_vxlan_tcp4_tbl vxlan_tcp_tbl;
+ struct gro_vxlan_tcp4_flow vxlan_tcp_flows[RTE_GRO_MAX_BURST_ITEM_NUM];
+ struct gro_vxlan_tcp4_item vxlan_tcp_items[RTE_GRO_MAX_BURST_ITEM_NUM]
+ = {{{0}, 0, 0} };
+
+ /* Allocate a reassembly table for VXLAN UDP GRO */
+ struct gro_vxlan_udp4_tbl vxlan_udp_tbl;
+ struct gro_vxlan_udp4_flow vxlan_udp_flows[RTE_GRO_MAX_BURST_ITEM_NUM];
+ struct gro_vxlan_udp4_item vxlan_udp_items[RTE_GRO_MAX_BURST_ITEM_NUM]
+ = {{{0}} };
+
+ struct rte_mbuf *unprocess_pkts[nb_pkts];
+ uint32_t item_num;
+ int32_t ret;
+ uint16_t i, unprocess_num = 0, nb_after_gro = nb_pkts;
+ uint8_t do_tcp4_gro = 0, do_vxlan_tcp_gro = 0, do_udp4_gro = 0,
+ do_vxlan_udp_gro = 0, do_tcp6_gro = 0;
+
+ if (unlikely((param->gro_types & (RTE_GRO_IPV4_VXLAN_TCP_IPV4 |
+ RTE_GRO_TCP_IPV4 | RTE_GRO_TCP_IPV6 |
+ RTE_GRO_IPV4_VXLAN_UDP_IPV4 |
+ RTE_GRO_UDP_IPV4)) == 0))
+ return nb_pkts;
+
+ /* Get the maximum number of packets */
+ item_num = RTE_MIN(nb_pkts, (param->max_flow_num *
+ param->max_item_per_flow));
+ item_num = RTE_MIN(item_num, RTE_GRO_MAX_BURST_ITEM_NUM);
+
+ if (param->gro_types & RTE_GRO_IPV4_VXLAN_TCP_IPV4) {
+ for (i = 0; i < item_num; i++)
+ vxlan_tcp_flows[i].start_index = INVALID_ARRAY_INDEX;
+
+ vxlan_tcp_tbl.flows = vxlan_tcp_flows;
+ vxlan_tcp_tbl.items = vxlan_tcp_items;
+ vxlan_tcp_tbl.flow_num = 0;
+ vxlan_tcp_tbl.item_num = 0;
+ vxlan_tcp_tbl.max_flow_num = item_num;
+ vxlan_tcp_tbl.max_item_num = item_num;
+ do_vxlan_tcp_gro = 1;
+ }
+
+ if (param->gro_types & RTE_GRO_IPV4_VXLAN_UDP_IPV4) {
+ for (i = 0; i < item_num; i++)
+ vxlan_udp_flows[i].start_index = INVALID_ARRAY_INDEX;
+
+ vxlan_udp_tbl.flows = vxlan_udp_flows;
+ vxlan_udp_tbl.items = vxlan_udp_items;
+ vxlan_udp_tbl.flow_num = 0;
+ vxlan_udp_tbl.item_num = 0;
+ vxlan_udp_tbl.max_flow_num = item_num;
+ vxlan_udp_tbl.max_item_num = item_num;
+ do_vxlan_udp_gro = 1;
+ }
+
+ if (param->gro_types & RTE_GRO_TCP_IPV4) {
+ for (i = 0; i < item_num; i++)
+ tcp_flows[i].start_index = INVALID_ARRAY_INDEX;
+
+ tcp_tbl.flows = tcp_flows;
+ tcp_tbl.items = tcp_items;
+ tcp_tbl.flow_num = 0;
+ tcp_tbl.item_num = 0;
+ tcp_tbl.max_flow_num = item_num;
+ tcp_tbl.max_item_num = item_num;
+ do_tcp4_gro = 1;
+ }
+
+ if (param->gro_types & RTE_GRO_UDP_IPV4) {
+ for (i = 0; i < item_num; i++)
+ udp_flows[i].start_index = INVALID_ARRAY_INDEX;
+
+ udp_tbl.flows = udp_flows;
+ udp_tbl.items = udp_items;
+ udp_tbl.flow_num = 0;
+ udp_tbl.item_num = 0;
+ udp_tbl.max_flow_num = item_num;
+ udp_tbl.max_item_num = item_num;
+ do_udp4_gro = 1;
+ }
+
+ if (param->gro_types & RTE_GRO_TCP_IPV6) {
+ for (i = 0; i < item_num; i++)
+ tcp6_flows[i].start_index = INVALID_ARRAY_INDEX;
+
+ tcp6_tbl.flows = tcp6_flows;
+ tcp6_tbl.items = tcp6_items;
+ tcp6_tbl.flow_num = 0;
+ tcp6_tbl.item_num = 0;
+ tcp6_tbl.max_flow_num = item_num;
+ tcp6_tbl.max_item_num = item_num;
+ do_tcp6_gro = 1;
+ }
+
+ for (i = 0; i < nb_pkts; i++) {
+ /*
+ * The timestamp is ignored, since all packets
+ * will be flushed from the tables.
+ */
+ if (IS_IPV4_VXLAN_TCP4_PKT(pkts[i]->packet_type) &&
+ do_vxlan_tcp_gro) {
+ ret = gro_vxlan_tcp4_reassemble(pkts[i],
+ &vxlan_tcp_tbl, 0);
+ if (ret > 0)
+ /* Merge successfully */
+ nb_after_gro--;
+ else if (ret < 0)
+ unprocess_pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV4_VXLAN_UDP4_PKT(pkts[i]->packet_type) &&
+ do_vxlan_udp_gro) {
+ ret = gro_vxlan_udp4_reassemble(pkts[i],
+ &vxlan_udp_tbl, 0);
+ if (ret > 0)
+ /* Merge successfully */
+ nb_after_gro--;
+ else if (ret < 0)
+ unprocess_pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV4_TCP_PKT(pkts[i]->packet_type) &&
+ do_tcp4_gro) {
+ ret = gro_tcp4_reassemble(pkts[i], &tcp_tbl, 0);
+ if (ret > 0)
+ /* merge successfully */
+ nb_after_gro--;
+ else if (ret < 0)
+ unprocess_pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV4_UDP_PKT(pkts[i]->packet_type) &&
+ do_udp4_gro) {
+ ret = gro_udp4_reassemble(pkts[i], &udp_tbl, 0);
+ if (ret > 0)
+ /* merge successfully */
+ nb_after_gro--;
+ else if (ret < 0)
+ unprocess_pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV6_TCP_PKT(pkts[i]->packet_type) &&
+ do_tcp6_gro) {
+ ret = gro_tcp6_reassemble(pkts[i], &tcp6_tbl, 0);
+ if (ret > 0)
+ /* merge successfully */
+ nb_after_gro--;
+ else if (ret < 0)
+ unprocess_pkts[unprocess_num++] = pkts[i];
+ } else
+ unprocess_pkts[unprocess_num++] = pkts[i];
+ }
+
+ if ((nb_after_gro < nb_pkts)
+ || (unprocess_num < nb_pkts)) {
+ i = 0;
+ /* Copy unprocessed packets */
+ if (unprocess_num > 0) {
+ memcpy(&pkts[i], unprocess_pkts,
+ sizeof(struct rte_mbuf *) *
+ unprocess_num);
+ i = unprocess_num;
+ }
+
+ /* Flush all packets from the tables */
+ if (do_vxlan_tcp_gro) {
+ i += gro_vxlan_tcp4_tbl_timeout_flush(&vxlan_tcp_tbl,
+ 0, &pkts[i], nb_pkts - i);
+ }
+
+ if (do_vxlan_udp_gro) {
+ i += gro_vxlan_udp4_tbl_timeout_flush(&vxlan_udp_tbl,
+ 0, &pkts[i], nb_pkts - i);
+
+ }
+
+ if (do_tcp4_gro) {
+ i += gro_tcp4_tbl_timeout_flush(&tcp_tbl, 0,
+ &pkts[i], nb_pkts - i);
+ }
+
+ if (do_udp4_gro) {
+ i += gro_udp4_tbl_timeout_flush(&udp_tbl, 0,
+ &pkts[i], nb_pkts - i);
+ }
+
+ if (do_tcp6_gro) {
+ i += gro_tcp6_tbl_timeout_flush(&tcp6_tbl, 0,
+ &pkts[i], nb_pkts - i);
+ }
+ }
+
+ rte_gro_trace_reassemble_burst(pkts, nb_pkts, param);
+
+ return nb_after_gro;
+}
+
+uint16_t
+rte_gro_reassemble(struct rte_mbuf **pkts,
+ uint16_t nb_pkts,
+ void *ctx)
+{
+ struct rte_mbuf *unprocess_pkts[nb_pkts];
+ struct gro_ctx *gro_ctx = ctx;
+ void *tcp_tbl, *udp_tbl, *vxlan_tcp_tbl, *vxlan_udp_tbl, *tcp6_tbl;
+ uint64_t current_time;
+ uint16_t i, unprocess_num = 0;
+ uint8_t do_tcp4_gro, do_vxlan_tcp_gro, do_udp4_gro, do_vxlan_udp_gro, do_tcp6_gro;
+
+ if (unlikely((gro_ctx->gro_types & (RTE_GRO_IPV4_VXLAN_TCP_IPV4 |
+ RTE_GRO_TCP_IPV4 | RTE_GRO_TCP_IPV6 |
+ RTE_GRO_IPV4_VXLAN_UDP_IPV4 |
+ RTE_GRO_UDP_IPV4)) == 0))
+ return nb_pkts;
+
+ tcp_tbl = gro_ctx->tbls[RTE_GRO_TCP_IPV4_INDEX];
+ vxlan_tcp_tbl = gro_ctx->tbls[RTE_GRO_IPV4_VXLAN_TCP_IPV4_INDEX];
+ udp_tbl = gro_ctx->tbls[RTE_GRO_UDP_IPV4_INDEX];
+ vxlan_udp_tbl = gro_ctx->tbls[RTE_GRO_IPV4_VXLAN_UDP_IPV4_INDEX];
+ tcp6_tbl = gro_ctx->tbls[RTE_GRO_TCP_IPV6_INDEX];
+
+ do_tcp4_gro = (gro_ctx->gro_types & RTE_GRO_TCP_IPV4) ==
+ RTE_GRO_TCP_IPV4;
+ do_vxlan_tcp_gro = (gro_ctx->gro_types & RTE_GRO_IPV4_VXLAN_TCP_IPV4) ==
+ RTE_GRO_IPV4_VXLAN_TCP_IPV4;
+ do_udp4_gro = (gro_ctx->gro_types & RTE_GRO_UDP_IPV4) ==
+ RTE_GRO_UDP_IPV4;
+ do_vxlan_udp_gro = (gro_ctx->gro_types & RTE_GRO_IPV4_VXLAN_UDP_IPV4) ==
+ RTE_GRO_IPV4_VXLAN_UDP_IPV4;
+ do_tcp6_gro = (gro_ctx->gro_types & RTE_GRO_TCP_IPV6) == RTE_GRO_TCP_IPV6;
+
+ current_time = rte_rdtsc();
+
+ for (i = 0; i < nb_pkts; i++) {
+ if (IS_IPV4_VXLAN_TCP4_PKT(pkts[i]->packet_type) &&
+ do_vxlan_tcp_gro) {
+ if (gro_vxlan_tcp4_reassemble(pkts[i], vxlan_tcp_tbl,
+ current_time) < 0)
+ unprocess_pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV4_VXLAN_UDP4_PKT(pkts[i]->packet_type) &&
+ do_vxlan_udp_gro) {
+ if (gro_vxlan_udp4_reassemble(pkts[i], vxlan_udp_tbl,
+ current_time) < 0)
+ unprocess_pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV4_TCP_PKT(pkts[i]->packet_type) &&
+ do_tcp4_gro) {
+ if (gro_tcp4_reassemble(pkts[i], tcp_tbl,
+ current_time) < 0)
+ unprocess_pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV4_UDP_PKT(pkts[i]->packet_type) &&
+ do_udp4_gro) {
+ if (gro_udp4_reassemble(pkts[i], udp_tbl,
+ current_time) < 0)
+ unprocess_pkts[unprocess_num++] = pkts[i];
+ } else if (IS_IPV6_TCP_PKT(pkts[i]->packet_type) &&
+ do_tcp6_gro) {
+ if (gro_tcp6_reassemble(pkts[i], tcp6_tbl,
+ current_time) < 0)
+ unprocess_pkts[unprocess_num++] = pkts[i];
+ } else
+ unprocess_pkts[unprocess_num++] = pkts[i];
+ }
+ if (unprocess_num > 0) {
+ memcpy(pkts, unprocess_pkts, sizeof(struct rte_mbuf *) *
+ unprocess_num);
+ }
+
+ rte_gro_trace_reassemble(pkts, nb_pkts, ctx);
+
+ return unprocess_num;
+}
+
+uint16_t
+rte_gro_timeout_flush(void *ctx,
+ uint64_t timeout_cycles,
+ uint64_t gro_types,
+ struct rte_mbuf **out,
+ uint16_t max_nb_out)
+{
+ struct gro_ctx *gro_ctx = ctx;
+ uint64_t flush_timestamp;
+ uint16_t num = 0;
+ uint16_t left_nb_out = max_nb_out;
+
+ gro_types = gro_types & gro_ctx->gro_types;
+ flush_timestamp = rte_rdtsc() - timeout_cycles;
+
+ if (gro_types & RTE_GRO_IPV4_VXLAN_TCP_IPV4) {
+ num = gro_vxlan_tcp4_tbl_timeout_flush(gro_ctx->tbls[
+ RTE_GRO_IPV4_VXLAN_TCP_IPV4_INDEX],
+ flush_timestamp, out, left_nb_out);
+ left_nb_out = max_nb_out - num;
+ }
+
+ if ((gro_types & RTE_GRO_IPV4_VXLAN_UDP_IPV4) && left_nb_out > 0) {
+ num += gro_vxlan_udp4_tbl_timeout_flush(gro_ctx->tbls[
+ RTE_GRO_IPV4_VXLAN_UDP_IPV4_INDEX],
+ flush_timestamp, &out[num], left_nb_out);
+ left_nb_out = max_nb_out - num;
+ }
+
+ /* If no available space in 'out', stop flushing. */
+ if ((gro_types & RTE_GRO_TCP_IPV4) && left_nb_out > 0) {
+ num += gro_tcp4_tbl_timeout_flush(
+ gro_ctx->tbls[RTE_GRO_TCP_IPV4_INDEX],
+ flush_timestamp,
+ &out[num], left_nb_out);
+ left_nb_out = max_nb_out - num;
+ }
+
+ /* If no available space in 'out', stop flushing. */
+ if ((gro_types & RTE_GRO_UDP_IPV4) && left_nb_out > 0) {
+ num += gro_udp4_tbl_timeout_flush(
+ gro_ctx->tbls[RTE_GRO_UDP_IPV4_INDEX],
+ flush_timestamp,
+ &out[num], left_nb_out);
+ left_nb_out = max_nb_out - num;
+ }
+
+ if ((gro_types & RTE_GRO_TCP_IPV6) && left_nb_out > 0) {
+ num += gro_tcp6_tbl_timeout_flush(
+ gro_ctx->tbls[RTE_GRO_TCP_IPV6_INDEX],
+ flush_timestamp,
+ &out[num], left_nb_out);
+
+ }
+
+ rte_gro_trace_timeout_flush(ctx, timeout_cycles, gro_types, out, max_nb_out);
+
+ return num;
+}
+
+uint64_t
+rte_gro_get_pkt_count(void *ctx)
+{
+ struct gro_ctx *gro_ctx = ctx;
+ gro_tbl_pkt_count_fn pkt_count_fn;
+ uint64_t gro_types = gro_ctx->gro_types, flag;
+ uint64_t item_num = 0;
+ uint8_t i;
+
+ for (i = 0; i < RTE_GRO_TYPE_MAX_NUM && gro_types; i++) {
+ flag = 1ULL << i;
+ if ((gro_types & flag) == 0)
+ continue;
+
+ gro_types ^= flag;
+ pkt_count_fn = tbl_pkt_count_fn[i];
+ if (pkt_count_fn)
+ item_num += pkt_count_fn(gro_ctx->tbls[i]);
+ }
+
+ rte_gro_trace_get_pkt_count(ctx);
+
+ return item_num;
+}