diff --git a/test_plans/perf_tso_test_plan.rst b/test_plans/perf_tso_test_plan.rst
new file mode 100644
index 00000000..9dcdd4c3
--- /dev/null
+++ b/test_plans/perf_tso_test_plan.rst
@@ -0,0 +1,82 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+   Copyright(c) 2015-2017 Intel Corporation
+
+=========================================
+Transmit Segmentation Offload (TSO) Tests
+=========================================
+
+Description
+===========
+
+This document provides the plan for testing the TSO (Transmit Segmentation
+Offload, also called Large Send offload - LSO) feature of
+Intel Ethernet Controller, including Intel 82599 10GbE Ethernet Controller and
+Intel® Ethernet Converged Network Adapter XL710-QDA2. TSO enables the TCP/IP stack to
+pass the network device a larger ULP datagram than the Maximum Transmit
+Unit Size (MTU). NIC divides the large ULP datagram to multiple segments
+according to the MTU size.
+
+
+Prerequisites
+=============
+
+The DUT must take one of the Ethernet controller ports connected to a port on another
+device that is controlled by the Scapy packet generator.
+
+The Ethernet interface identifier of the port that Scapy will use must be known.
+On tester, all offload feature should be disabled on tx port, and start rx port capture::
+
+  ethtool -K <tx port> rx off tx off tso off gso off gro off lro off
+  ip l set <tx port> up
+  tcpdump -n -e -i <rx port> -s 0 -w /tmp/cap
+
+
+On DUT, run pmd with parameter "--enable-rx-cksum". Then enable TSO on tx port
+and checksum on rx port. The test commands is below::
+
+  #enable hw checksum on rx port
+  tx_checksum set ip hw 0
+  tx_checksum set udp hw 0
+  tx_checksum set tcp hw 0
+  tx_checksum set sctp hw 0
+  set fwd csum
+
+  # enable TSO on tx port
+  *tso set 800 1
+
+
+
+Test case: TSO performance
+==========================
+
+Set the packet stream as below before testing
+
++-------+---------+---------+---------+----------+----------+
+| Frame | 1S/1C/1T| 1S/1C/1T| 1S/2C/1T| 1S/2C/2T | 1S/2C/2T |
+| Size  |         |         |         |          |          |
++-------+---------+---------+---------+----------+----------+
+|  64   |         |         |         |          |          |
++-------+---------+---------+---------+----------+----------+
+|  65   |         |         |         |          |          |
++-------+---------+---------+---------+----------+----------+
+|  128  |         |         |         |          |          |
++-------+---------+---------+---------+----------+----------+
+|  256  |         |         |         |          |          |
++-------+---------+---------+---------+----------+----------+
+|  512  |         |         |         |          |          |
++-------+---------+---------+---------+----------+----------+
+|  1024 |         |         |         |          |          |
++-------+---------+---------+---------+----------+----------+
+|  1280 |         |         |         |          |          |
++-------+---------+---------+---------+----------+----------+
+|  1518 |         |         |         |          |          |
++-------+---------+---------+---------+----------+----------+
+
+Then run the test application as below::
+
+   ./x86_64-native-linuxapp-gcc/app/dpdk-testpmd -c 0xffffffff -n 2 -- -i --rxd=512 --txd=512
+   --burst=32 --rxfreet=64 --mbcache=128 --portmask=0x3 --txpt=36 --txht=0 --txwt=0
+   --txfreet=32 --txrst=32 --enable-rx-cksum
+
+The -n command is used to select the number of memory channels. It should match the
+number of memory channels on that setup.
diff --git a/test_plans/tso_test_plan.rst b/test_plans/tso_test_plan.rst
index d0f96b2b..7b7077b7 100644
--- a/test_plans/tso_test_plan.rst
+++ b/test_plans/tso_test_plan.rst
@@ -131,38 +131,3 @@ Test nvgre() in scapy::
 
     sendp([Ether(dst="%s",src="52:00:00:00:00:00")/IP(src="192.168.1.1",dst="192.168.1.2",proto=47)/NVGRE()/Ether(dst=%s,src="52:00:00:00:00:00")/IP(src="192.168.1.1",dst="192.168.1.2")/TCP(sport="1021",dport="1021")/("X"*%s)], iface="%s")
 
-Test case: TSO performance
-==========================
-
-Set the packet stream to be sent out from packet generator before testing as
-below.
-
-+-------+---------+---------+---------+----------+----------+
-| Frame | 1S/1C/1T| 1S/1C/1T| 1S/2C/1T| 1S/2C/2T | 1S/2C/2T |
-| Size  |         |         |         |          |          |
-+-------+---------+---------+---------+----------+----------+
-|  64   |         |         |         |          |          |
-+-------+---------+---------+---------+----------+----------+
-|  65   |         |         |         |          |          |
-+-------+---------+---------+---------+----------+----------+
-|  128  |         |         |         |          |          |
-+-------+---------+---------+---------+----------+----------+
-|  256  |         |         |         |          |          |
-+-------+---------+---------+---------+----------+----------+
-|  512  |         |         |         |          |          |
-+-------+---------+---------+---------+----------+----------+
-|  1024 |         |         |         |          |          |
-+-------+---------+---------+---------+----------+----------+
-|  1280 |         |         |         |          |          |
-+-------+---------+---------+---------+----------+----------+
-|  1518 |         |         |         |          |          |
-+-------+---------+---------+---------+----------+----------+
-
-Then run the test application as below::
-
-   ./x86_64-native-linuxapp-gcc/app/dpdk-testpmd -c 0xffffffff -n 2 -- -i --rxd=512 --txd=512
-   --burst=32 --rxfreet=64 --mbcache=128 --portmask=0x3 --txpt=36 --txht=0 --txwt=0
-   --txfreet=32 --txrst=32 --enable-rx-cksum
-
-The -n command is used to select the number of memory channels. It should match the
-number of memory channels on that setup.
diff --git a/tests/TestSuite_perf_tso.py b/tests/TestSuite_perf_tso.py
new file mode 100644
index 00000000..3821ecd1
--- /dev/null
+++ b/tests/TestSuite_perf_tso.py
@@ -0,0 +1,221 @@
+import os
+import time
+
+import framework.utils as utils
+from framework.pktgen import PacketGeneratorHelper
+from framework.settings import HEADER_SIZE
+from framework.test_case import TestCase
+
+DEFAULT_MUT = 1500
+TSO_MTU = 9000
+
+
+class TestTSO(TestCase):
+    dut_ports = []
+
+    def set_up_all(self):
+        """
+        Run at the start of each test suite.
+        """
+        # Based on h/w type, choose how many ports to use
+        self.dut_ports = self.dut.get_ports(self.nic)
+
+        # Verify that enough ports are available
+        self.verify(len(self.dut_ports) >= 2, "Insufficient ports for testing")
+
+        # Verify that enough threads are available
+        self.all_cores_mask = utils.create_mask(self.dut.get_core_list("all"))
+        self.portMask = utils.create_mask([self.dut_ports[0], self.dut_ports[1]])
+        self.ports_socket = self.dut.get_numa_id(self.dut_ports[0])
+
+        self.loading_sizes = [128, 800, 801, 1700, 2500]
+        self.rxfreet_values = [0, 8, 16, 32, 64, 128]
+
+        self.test_cycles = [{"cores": "", "Mpps": {}, "pct": {}}]
+
+        self.table_header = ["Frame Size"]
+        for test_cycle in self.test_cycles:
+            self.table_header.append("%s Mpps" % test_cycle["cores"])
+            self.table_header.append("% linerate")
+
+        self.eal_param = self.dut.create_eal_parameters(
+            cores="1S/2C/1T", socket=self.ports_socket, ports=self.dut_ports
+        )
+
+        self.headers_size = HEADER_SIZE["eth"] + HEADER_SIZE["ip"] + HEADER_SIZE["tcp"]
+        self.tester.send_expect(
+            "ifconfig %s mtu %s"
+            % (
+                self.tester.get_interface(
+                    self.tester.get_local_port(self.dut_ports[0])
+                ),
+                TSO_MTU,
+            ),
+            "# ",
+        )
+        # get dts output path
+        if self.logger.log_path.startswith(os.sep):
+            self.output_path = self.logger.log_path
+        else:
+            cur_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+            self.output_path = os.sep.join([cur_path, self.logger.log_path])
+        # create an instance to set stream field setting
+        self.pktgen_helper = PacketGeneratorHelper()
+        self.path = self.dut.apps_name["test-pmd"]
+
+    def set_up(self):
+        """
+        Run before each test case.
+        """
+        pass
+
+    def test_perf_TSO_2ports(self):
+        """
+        TSO Performance Benchmarking with 2 ports.
+        """
+
+        # prepare traffic generator input
+        tgen_input = []
+
+        # run testpmd for each core config
+        for test_cycle in self.test_cycles:
+            core_config = test_cycle["cores"]
+            cores = self.dut.get_core_list(core_config, socket=self.ports_socket)
+            self.coreMask = utils.create_mask(cores)
+            if len(cores) > 2:
+                queues = len(cores) // 2
+            else:
+                queues = 1
+
+            command_line = (
+                "%s %s -- -i --rxd=512 --txd=512 --burst=32 --rxfreet=64 --mbcache=128 --portmask=%s --max-pkt-len=%s --txpt=36 --txht=0 --txwt=0 --txfreet=32 --txrst=32 "
+                % (self.path, self.eal_param, self.portMask, TSO_MTU)
+            )
+            info = "Executing PMD using %s\n" % test_cycle["cores"]
+            self.logger.info(info)
+            self.rst_report(info, annex=True)
+            self.rst_report(command_line + "\n\n", frame=True, annex=True)
+
+            self.dut.send_expect(command_line, "testpmd> ", 120)
+            self.dut.send_expect("port stop all", "testpmd> ", 120)
+            self.dut.send_expect(
+                "csum set ip hw %d" % self.dut_ports[0], "testpmd> ", 120
+            )
+            self.dut.send_expect(
+                "csum set udp hw %d" % self.dut_ports[0], "testpmd> ", 120
+            )
+            self.dut.send_expect(
+                "csum set tcp hw %d" % self.dut_ports[0], "testpmd> ", 120
+            )
+            self.dut.send_expect(
+                "csum set sctp hw %d" % self.dut_ports[0], "testpmd> ", 120
+            )
+            self.dut.send_expect(
+                "csum set outer-ip hw %d" % self.dut_ports[0], "testpmd> ", 120
+            )
+            self.dut.send_expect(
+                "csum parse-tunnel on %d" % self.dut_ports[0], "testpmd> ", 120
+            )
+            self.dut.send_expect(
+                "csum set ip hw %d" % self.dut_ports[1], "testpmd> ", 120
+            )
+            self.dut.send_expect(
+                "csum set udp hw %d" % self.dut_ports[1], "testpmd> ", 120
+            )
+            self.dut.send_expect(
+                "csum set tcp hw %d" % self.dut_ports[1], "testpmd> ", 120
+            )
+            self.dut.send_expect(
+                "csum set sctp hw %d" % self.dut_ports[1], "testpmd> ", 120
+            )
+            self.dut.send_expect(
+                "csum set outer-ip hw %d" % self.dut_ports[1], "testpmd> ", 120
+            )
+            self.dut.send_expect(
+                "csum parse-tunnel on %d" % self.dut_ports[1], "testpmd> ", 120
+            )
+            self.dut.send_expect("tso set 800 %d" % self.dut_ports[1], "testpmd> ", 120)
+            self.dut.send_expect("set fwd csum", "testpmd> ", 120)
+            self.dut.send_expect("port start all", "testpmd> ", 120)
+            self.dut.send_expect("set promisc all off", "testpmd> ", 120)
+            self.dut.send_expect("start", "testpmd> ")
+            for loading_size in self.loading_sizes:
+                frame_size = loading_size + self.headers_size
+                wirespeed = self.wirespeed(self.nic, frame_size, 2)
+
+                # create pcap file
+                self.logger.info("Running with frame size %d " % frame_size)
+                payload_size = frame_size - self.headers_size
+                for _port in range(2):
+                    mac = self.dut.get_mac_address(self.dut_ports[_port])
+
+                    pcap = os.sep.join([self.output_path, "dts{0}.pcap".format(_port)])
+                    self.tester.scapy_append(
+                        'wrpcap("%s", [Ether(dst="%s",src="52:00:00:00:00:01")/IP(src="192.168.1.1",dst="192.168.1.2")/TCP(sport=1021,dport=1021)/("X"*%d)])'
+                        % (pcap, mac, payload_size)
+                    )
+                    tgen_input.append(
+                        (
+                            self.tester.get_local_port(self.dut_ports[_port]),
+                            self.tester.get_local_port(self.dut_ports[1 - _port]),
+                            "%s" % pcap,
+                        )
+                    )
+                self.tester.scapy_execute()
+
+                # clear streams before add new streams
+                self.tester.pktgen.clear_streams()
+                # run packet generator
+                streams = self.pktgen_helper.prepare_stream_from_tginput(
+                    tgen_input, 100, None, self.tester.pktgen
+                )
+                _, pps = self.tester.pktgen.measure_throughput(stream_ids=streams)
+
+                pps /= 1000000.0
+                test_cycle["Mpps"][loading_size] = pps
+                test_cycle["pct"][loading_size] = pps * 100 // wirespeed
+
+            self.dut.send_expect("stop", "testpmd> ")
+            self.dut.send_expect("quit", "# ", 30)
+            time.sleep(5)
+
+        for n in range(len(self.test_cycles)):
+            for loading_size in self.loading_sizes:
+                self.verify(
+                    self.test_cycles[n]["Mpps"][loading_size] > 0, "No traffic detected"
+                )
+
+        # Print results
+        self.result_table_create(self.table_header)
+        for loading_size in self.loading_sizes:
+            table_row = [loading_size]
+            for test_cycle in self.test_cycles:
+                table_row.append(test_cycle["Mpps"][loading_size])
+                table_row.append(test_cycle["pct"][loading_size])
+
+            self.result_table_add(table_row)
+
+        self.result_table_print()
+
+    def tear_down(self):
+        """
+        Run after each test case.
+        """
+        self.dut.send_expect("quit", "# ")
+        self.dut.kill_all()
+        time.sleep(2)
+
+    def tear_down_all(self):
+        """
+        Run after each test suite.
+        """
+        self.tester.send_expect(
+            "ifconfig %s mtu %s"
+            % (
+                self.tester.get_interface(
+                    self.tester.get_local_port(self.dut_ports[0])
+                ),
+                DEFAULT_MUT,
+            ),
+            "# ",
+        )
diff --git a/tests/TestSuite_tso.py b/tests/TestSuite_tso.py
index 49db011d..c4f25afc 100644
--- a/tests/TestSuite_tso.py
+++ b/tests/TestSuite_tso.py
@@ -487,134 +487,6 @@ class TestTSO(TestCase):
                     )
             self.get_chksum_value_and_verify(dump_pcap, save_file, Nic_list)
 
-    def test_perf_TSO_2ports(self):
-        """
-        TSO Performance Benchmarking with 2 ports.
-        """
-
-        # prepare traffic generator input
-        tgen_input = []
-
-        # run testpmd for each core config
-        for test_cycle in self.test_cycles:
-            core_config = test_cycle["cores"]
-            cores = self.dut.get_core_list(core_config, socket=self.ports_socket)
-            self.coreMask = utils.create_mask(cores)
-            if len(cores) > 2:
-                queues = len(cores) // 2
-            else:
-                queues = 1
-
-            command_line = (
-                "%s %s -- -i --rxd=512 --txd=512 --burst=32 --rxfreet=64 --mbcache=128 --portmask=%s --max-pkt-len=%s --txpt=36 --txht=0 --txwt=0 --txfreet=32 --txrst=32 "
-                % (self.path, self.eal_param, self.portMask, TSO_MTU)
-            )
-            info = "Executing PMD using %s\n" % test_cycle["cores"]
-            self.logger.info(info)
-            self.rst_report(info, annex=True)
-            self.rst_report(command_line + "\n\n", frame=True, annex=True)
-
-            self.dut.send_expect(command_line, "testpmd> ", 120)
-            self.dut.send_expect("port stop all", "testpmd> ", 120)
-            self.dut.send_expect(
-                "csum set ip hw %d" % self.dut_ports[0], "testpmd> ", 120
-            )
-            self.dut.send_expect(
-                "csum set udp hw %d" % self.dut_ports[0], "testpmd> ", 120
-            )
-            self.dut.send_expect(
-                "csum set tcp hw %d" % self.dut_ports[0], "testpmd> ", 120
-            )
-            self.dut.send_expect(
-                "csum set sctp hw %d" % self.dut_ports[0], "testpmd> ", 120
-            )
-            self.dut.send_expect(
-                "csum set outer-ip hw %d" % self.dut_ports[0], "testpmd> ", 120
-            )
-            self.dut.send_expect(
-                "csum parse-tunnel on %d" % self.dut_ports[0], "testpmd> ", 120
-            )
-            self.dut.send_expect(
-                "csum set ip hw %d" % self.dut_ports[1], "testpmd> ", 120
-            )
-            self.dut.send_expect(
-                "csum set udp hw %d" % self.dut_ports[1], "testpmd> ", 120
-            )
-            self.dut.send_expect(
-                "csum set tcp hw %d" % self.dut_ports[1], "testpmd> ", 120
-            )
-            self.dut.send_expect(
-                "csum set sctp hw %d" % self.dut_ports[1], "testpmd> ", 120
-            )
-            self.dut.send_expect(
-                "csum set outer-ip hw %d" % self.dut_ports[1], "testpmd> ", 120
-            )
-            self.dut.send_expect(
-                "csum parse-tunnel on %d" % self.dut_ports[1], "testpmd> ", 120
-            )
-            self.dut.send_expect("tso set 800 %d" % self.dut_ports[1], "testpmd> ", 120)
-            self.dut.send_expect("set fwd csum", "testpmd> ", 120)
-            self.dut.send_expect("port start all", "testpmd> ", 120)
-            self.dut.send_expect("set promisc all off", "testpmd> ", 120)
-            self.dut.send_expect("start", "testpmd> ")
-            for loading_size in self.loading_sizes:
-                frame_size = loading_size + self.headers_size
-                wirespeed = self.wirespeed(self.nic, frame_size, 2)
-
-                # create pcap file
-                self.logger.info("Running with frame size %d " % frame_size)
-                payload_size = frame_size - self.headers_size
-                for _port in range(2):
-                    mac = self.dut.get_mac_address(self.dut_ports[_port])
-
-                    pcap = os.sep.join([self.output_path, "dts{0}.pcap".format(_port)])
-                    self.tester.scapy_append(
-                        'wrpcap("%s", [Ether(dst="%s",src="52:00:00:00:00:01")/IP(src="192.168.1.1",dst="192.168.1.2")/TCP(sport=1021,dport=1021)/("X"*%d)])'
-                        % (pcap, mac, payload_size)
-                    )
-                    tgen_input.append(
-                        (
-                            self.tester.get_local_port(self.dut_ports[_port]),
-                            self.tester.get_local_port(self.dut_ports[1 - _port]),
-                            "%s" % pcap,
-                        )
-                    )
-                self.tester.scapy_execute()
-
-                # clear streams before add new streams
-                self.tester.pktgen.clear_streams()
-                # run packet generator
-                streams = self.pktgen_helper.prepare_stream_from_tginput(
-                    tgen_input, 100, None, self.tester.pktgen
-                )
-                _, pps = self.tester.pktgen.measure_throughput(stream_ids=streams)
-
-                pps /= 1000000.0
-                test_cycle["Mpps"][loading_size] = pps
-                test_cycle["pct"][loading_size] = pps * 100 // wirespeed
-
-            self.dut.send_expect("stop", "testpmd> ")
-            self.dut.send_expect("quit", "# ", 30)
-            time.sleep(5)
-
-        for n in range(len(self.test_cycles)):
-            for loading_size in self.loading_sizes:
-                self.verify(
-                    self.test_cycles[n]["Mpps"][loading_size] > 0, "No traffic detected"
-                )
-
-        # Print results
-        self.result_table_create(self.table_header)
-        for loading_size in self.loading_sizes:
-            table_row = [loading_size]
-            for test_cycle in self.test_cycles:
-                table_row.append(test_cycle["Mpps"][loading_size])
-                table_row.append(test_cycle["pct"][loading_size])
-
-            self.result_table_add(table_row)
-
-        self.result_table_print()
-
     def tear_down(self):
         """
         Run after each test case.
