diff --git a/doc/guides/nics/features/hns3.ini b/doc/guides/nics/features/hns3.ini
index c3a8544..66abda7 100644
--- a/doc/guides/nics/features/hns3.ini
+++ b/doc/guides/nics/features/hns3.ini
@@ -9,6 +9,7 @@ Rx interrupt         = Y
 MTU update           = Y
 Jumbo frame          = Y
 TSO                  = Y
+LRO                  = Y
 Promiscuous mode     = Y
 Allmulticast mode    = Y
 Unicast MAC filter   = Y
diff --git a/doc/guides/nics/features/hns3_vf.ini b/doc/guides/nics/features/hns3_vf.ini
index 80773ac..71d246a 100644
--- a/doc/guides/nics/features/hns3_vf.ini
+++ b/doc/guides/nics/features/hns3_vf.ini
@@ -9,6 +9,7 @@ Rx interrupt         = Y
 MTU update           = Y
 Jumbo frame          = Y
 TSO                  = Y
+LRO                  = Y
 Promiscuous mode     = Y
 Allmulticast mode    = Y
 Unicast MAC filter   = Y
diff --git a/doc/guides/nics/hns3.rst b/doc/guides/nics/hns3.rst
index 05dbe41..ae3c5f6 100644
--- a/doc/guides/nics/hns3.rst
+++ b/doc/guides/nics/hns3.rst
@@ -18,6 +18,7 @@ Features of the HNS3 PMD are:
 - Packet type information
 - Checksum offload
 - TSO offload
+- LRO offload
 - Promiscuous mode
 - Multicast mode
 - Port hardware statistics
diff --git a/drivers/net/hns3/hns3_ethdev.c b/drivers/net/hns3/hns3_ethdev.c
index 62d8758..7758855 100644
--- a/drivers/net/hns3/hns3_ethdev.c
+++ b/drivers/net/hns3/hns3_ethdev.c
@@ -1099,25 +1099,6 @@ hns3_config_tso(struct hns3_hw *hw, unsigned int tso_mss_min,
 	return hns3_cmd_send(hw, &desc, 1);
 }
 
-int
-hns3_config_gro(struct hns3_hw *hw, bool en)
-{
-	struct hns3_cfg_gro_status_cmd *req;
-	struct hns3_cmd_desc desc;
-	int ret;
-
-	hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_GRO_GENERIC_CONFIG, false);
-	req = (struct hns3_cfg_gro_status_cmd *)desc.data;
-
-	req->gro_en = rte_cpu_to_le_16(en ? 1 : 0);
-
-	ret = hns3_cmd_send(hw, &desc, 1);
-	if (ret)
-		hns3_err(hw, "GRO hardware config cmd failed, ret = %d", ret);
-
-	return ret;
-}
-
 static int
 hns3_set_umv_space(struct hns3_hw *hw, uint16_t space_size,
 		   uint16_t *allocated_size, bool is_alloc)
@@ -2276,6 +2257,7 @@ hns3_dev_configure(struct rte_eth_dev *dev)
 	uint16_t nb_tx_q = dev->data->nb_tx_queues;
 	struct rte_eth_rss_conf rss_conf;
 	uint16_t mtu;
+	bool gro_en;
 	int ret;
 
 	/*
@@ -2342,6 +2324,12 @@ hns3_dev_configure(struct rte_eth_dev *dev)
 	if (ret)
 		goto cfg_err;
 
+	/* config hardware GRO */
+	gro_en = conf->rxmode.offloads & DEV_RX_OFFLOAD_TCP_LRO ? true : false;
+	ret = hns3_config_gro(hw, gro_en);
+	if (ret)
+		goto cfg_err;
+
 	hw->adapter_state = HNS3_NIC_CONFIGURED;
 
 	return 0;
@@ -2449,6 +2437,7 @@ hns3_dev_infos_get(struct rte_eth_dev *eth_dev, struct rte_eth_dev_info *info)
 	info->min_rx_bufsize = hw->rx_buf_len;
 	info->max_mac_addrs = HNS3_UC_MACADDR_NUM;
 	info->max_mtu = info->max_rx_pktlen - HNS3_ETH_OVERHEAD;
+	info->max_lro_pkt_size = HNS3_MAX_LRO_SIZE;
 	info->rx_offload_capa = (DEV_RX_OFFLOAD_IPV4_CKSUM |
 				 DEV_RX_OFFLOAD_TCP_CKSUM |
 				 DEV_RX_OFFLOAD_UDP_CKSUM |
@@ -2460,7 +2449,8 @@ hns3_dev_infos_get(struct rte_eth_dev *eth_dev, struct rte_eth_dev_info *info)
 				 DEV_RX_OFFLOAD_VLAN_STRIP |
 				 DEV_RX_OFFLOAD_VLAN_FILTER |
 				 DEV_RX_OFFLOAD_JUMBO_FRAME |
-				 DEV_RX_OFFLOAD_RSS_HASH);
+				 DEV_RX_OFFLOAD_RSS_HASH |
+				 DEV_RX_OFFLOAD_TCP_LRO);
 	info->tx_queue_offload_capa = DEV_TX_OFFLOAD_MBUF_FAST_FREE;
 	info->tx_offload_capa = (DEV_TX_OFFLOAD_OUTER_IPV4_CKSUM |
 				 DEV_TX_OFFLOAD_IPV4_CKSUM |
@@ -4381,6 +4371,7 @@ hns3_uninit_pf(struct rte_eth_dev *eth_dev)
 
 	hns3_enable_hw_error_intr(hns, false);
 	hns3_rss_uninit(hns);
+	(void)hns3_config_gro(hw, false);
 	hns3_promisc_uninit(hw);
 	hns3_fdir_filter_uninit(hns);
 	hns3_uninit_umv_space(hw);
@@ -5215,6 +5206,10 @@ hns3_restore_conf(struct hns3_adapter *hns)
 	if (ret)
 		goto err_promisc;
 
+	ret = hns3_restore_gro_conf(hw);
+	if (ret)
+		goto err_promisc;
+
 	if (hns->hw.adapter_state == HNS3_NIC_STARTED) {
 		ret = hns3_do_start(hns, false);
 		if (ret)
diff --git a/drivers/net/hns3/hns3_ethdev.h b/drivers/net/hns3/hns3_ethdev.h
index 21412c4..93fe2fe 100644
--- a/drivers/net/hns3/hns3_ethdev.h
+++ b/drivers/net/hns3/hns3_ethdev.h
@@ -46,6 +46,7 @@
 #define HNS3_MAX_BD_PAYLEN		(1024 * 1024 - 1)
 #define HNS3_MAX_TSO_HDR_SIZE		512
 #define HNS3_MAX_TSO_HDR_BD_NUM		3
+#define HNS3_MAX_LRO_SIZE		64512
 
 #define HNS3_ETH_OVERHEAD \
 	(RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN + HNS3_VLAN_TAG_SIZE * 2)
@@ -654,7 +655,6 @@ hns3_test_and_clear_bit(unsigned int nr, volatile uint64_t *addr)
 }
 
 int hns3_buffer_alloc(struct hns3_hw *hw);
-int hns3_config_gro(struct hns3_hw *hw, bool en);
 int hns3_dev_filter_ctrl(struct rte_eth_dev *dev,
 			 enum rte_filter_type filter_type,
 			 enum rte_filter_op filter_op, void *arg);
diff --git a/drivers/net/hns3/hns3_ethdev_vf.c b/drivers/net/hns3/hns3_ethdev_vf.c
index 0fdee4d..ccf44a1 100644
--- a/drivers/net/hns3/hns3_ethdev_vf.c
+++ b/drivers/net/hns3/hns3_ethdev_vf.c
@@ -757,6 +757,7 @@ hns3vf_dev_configure(struct rte_eth_dev *dev)
 	uint16_t nb_tx_q = dev->data->nb_tx_queues;
 	struct rte_eth_rss_conf rss_conf;
 	uint16_t mtu;
+	bool gro_en;
 	int ret;
 
 	/*
@@ -817,6 +818,12 @@ hns3vf_dev_configure(struct rte_eth_dev *dev)
 	if (ret)
 		goto cfg_err;
 
+	/* config hardware GRO */
+	gro_en = conf->rxmode.offloads & DEV_RX_OFFLOAD_TCP_LRO ? true : false;
+	ret = hns3_config_gro(hw, gro_en);
+	if (ret)
+		goto cfg_err;
+
 	hw->adapter_state = HNS3_NIC_CONFIGURED;
 	return 0;
 
@@ -898,6 +905,7 @@ hns3vf_dev_infos_get(struct rte_eth_dev *eth_dev, struct rte_eth_dev_info *info)
 	info->min_rx_bufsize = hw->rx_buf_len;
 	info->max_mac_addrs = HNS3_VF_UC_MACADDR_NUM;
 	info->max_mtu = info->max_rx_pktlen - HNS3_ETH_OVERHEAD;
+	info->max_lro_pkt_size = HNS3_MAX_LRO_SIZE;
 
 	info->rx_offload_capa = (DEV_RX_OFFLOAD_IPV4_CKSUM |
 				 DEV_RX_OFFLOAD_UDP_CKSUM |
@@ -910,7 +918,8 @@ hns3vf_dev_infos_get(struct rte_eth_dev *eth_dev, struct rte_eth_dev_info *info)
 				 DEV_RX_OFFLOAD_VLAN_STRIP |
 				 DEV_RX_OFFLOAD_VLAN_FILTER |
 				 DEV_RX_OFFLOAD_JUMBO_FRAME |
-				 DEV_RX_OFFLOAD_RSS_HASH);
+				 DEV_RX_OFFLOAD_RSS_HASH |
+				 DEV_RX_OFFLOAD_TCP_LRO);
 	info->tx_queue_offload_capa = DEV_TX_OFFLOAD_MBUF_FAST_FREE;
 	info->tx_offload_capa = (DEV_TX_OFFLOAD_OUTER_IPV4_CKSUM |
 				 DEV_TX_OFFLOAD_IPV4_CKSUM |
@@ -1648,6 +1657,7 @@ hns3vf_uninit_vf(struct rte_eth_dev *eth_dev)
 	PMD_INIT_FUNC_TRACE();
 
 	hns3_rss_uninit(hns);
+	(void)hns3_config_gro(hw, false);
 	(void)hns3vf_set_alive(hw, false);
 	(void)hns3vf_set_promisc_mode(hw, false, false, false);
 	hns3vf_disable_irq0(hw);
@@ -2219,6 +2229,10 @@ hns3vf_restore_conf(struct hns3_adapter *hns)
 	if (ret)
 		goto err_vlan_table;
 
+	ret = hns3_restore_gro_conf(hw);
+	if (ret)
+		goto err_vlan_table;
+
 	if (hw->adapter_state == HNS3_NIC_STARTED) {
 		ret = hns3vf_do_start(hns, false);
 		if (ret)
diff --git a/drivers/net/hns3/hns3_rxtx.c b/drivers/net/hns3/hns3_rxtx.c
index 57017b6..4e3391d 100644
--- a/drivers/net/hns3/hns3_rxtx.c
+++ b/drivers/net/hns3/hns3_rxtx.c
@@ -1519,6 +1519,7 @@ hns3_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts)
 	uint32_t bd_base_info;
 	uint32_t cksum_err;
 	uint32_t l234_info;
+	uint32_t gro_size;
 	uint32_t ol_info;
 	uint64_t dma_addr;
 	uint16_t data_len;
@@ -1665,6 +1666,13 @@ hns3_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts)
 		}
 		rxm->next = NULL;
 
+		gro_size = hns3_get_field(bd_base_info, HNS3_RXD_GRO_SIZE_M,
+					  HNS3_RXD_GRO_SIZE_S);
+		if (gro_size != 0) {
+			first_seg->ol_flags |= PKT_RX_LRO;
+			first_seg->tso_segsz = gro_size;
+		}
+
 		ret = hns3_handle_bdinfo(rxq, first_seg, bd_base_info,
 					 l234_info, &cksum_err);
 		if (unlikely(ret))
@@ -1850,6 +1858,43 @@ hns3_tso_proc_tunnel(struct hns3_desc *desc, uint64_t ol_flags,
 	return 0;
 }
 
+int
+hns3_config_gro(struct hns3_hw *hw, bool en)
+{
+	struct hns3_cfg_gro_status_cmd *req;
+	struct hns3_cmd_desc desc;
+	int ret;
+
+	hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_GRO_GENERIC_CONFIG, false);
+	req = (struct hns3_cfg_gro_status_cmd *)desc.data;
+
+	req->gro_en = rte_cpu_to_le_16(en ? 1 : 0);
+
+	ret = hns3_cmd_send(hw, &desc, 1);
+	if (ret)
+		hns3_err(hw, "%s hardware GRO failed, ret = %d",
+			 en ? "enable" : "disable", ret);
+
+	return ret;
+}
+
+int
+hns3_restore_gro_conf(struct hns3_hw *hw)
+{
+	uint64_t offloads;
+	bool gro_en;
+	int ret;
+
+	offloads = hw->data->dev_conf.rxmode.offloads;
+	gro_en = offloads & DEV_RX_OFFLOAD_TCP_LRO ? true : false;
+	ret = hns3_config_gro(hw, gro_en);
+	if (ret)
+		hns3_err(hw, "restore hardware GRO to %s failed, ret = %d",
+			 gro_en ? "enabled" : "disabled", ret);
+
+	return ret;
+}
+
 static inline bool
 hns3_pkt_is_tso(struct rte_mbuf *m)
 {
diff --git a/drivers/net/hns3/hns3_rxtx.h b/drivers/net/hns3/hns3_rxtx.h
index 0cb92ce..380d27b 100644
--- a/drivers/net/hns3/hns3_rxtx.h
+++ b/drivers/net/hns3/hns3_rxtx.h
@@ -73,7 +73,7 @@
 #define HNS3_RXD_TSIND_M			(0x7 << HNS3_RXD_TSIND_S)
 #define HNS3_RXD_LKBK_B				15
 #define HNS3_RXD_GRO_SIZE_S			16
-#define HNS3_RXD_GRO_SIZE_M			(0x3ff << HNS3_RXD_GRO_SIZE_S)
+#define HNS3_RXD_GRO_SIZE_M			(0x3fff << HNS3_RXD_GRO_SIZE_S)
 
 #define HNS3_TXD_L3T_S				0
 #define HNS3_TXD_L3T_M				(0x3 << HNS3_TXD_L3T_S)
@@ -376,5 +376,7 @@ void hns3_set_queue_intr_rl(struct hns3_hw *hw, uint16_t queue_id,
 			    uint16_t rl_value);
 int hns3_set_fake_rx_or_tx_queues(struct rte_eth_dev *dev, uint16_t nb_rx_q,
 				  uint16_t nb_tx_q);
+int hns3_config_gro(struct hns3_hw *hw, bool en);
+int hns3_restore_gro_conf(struct hns3_hw *hw);
 
 #endif /* _HNS3_RXTX_H_ */
