return ret;
 }
 
-int hclge_set_vlan_filter(struct hnae3_handle *handle, __be16 proto,
-                         u16 vlan_id, bool is_kill)
-{
-       struct hclge_vport *vport = hclge_get_vport(handle);
-       struct hclge_dev *hdev = vport->back;
-
-       return hclge_set_vlan_filter_hw(hdev, proto, vport->vport_id, vlan_id,
-                                       0, is_kill);
-}
-
-static int hclge_set_vf_vlan_filter(struct hnae3_handle *handle, int vfid,
-                                   u16 vlan, u8 qos, __be16 proto)
-{
-       struct hclge_vport *vport = hclge_get_vport(handle);
-       struct hclge_dev *hdev = vport->back;
-
-       if ((vfid >= hdev->num_alloc_vfs) || (vlan > 4095) || (qos > 7))
-               return -EINVAL;
-       if (proto != htons(ETH_P_8021Q))
-               return -EPROTONOSUPPORT;
-
-       return hclge_set_vlan_filter_hw(hdev, proto, vfid, vlan, qos, false);
-}
-
 static int hclge_set_vlan_tx_offload_cfg(struct hclge_vport *vport)
 {
        struct hclge_tx_vtag_cfg *vcfg = &vport->txvlan_cfg;
        return hclge_set_vlan_filter(handle, htons(ETH_P_8021Q), 0, false);
 }
 
-void hclge_add_vport_vlan_table(struct hclge_vport *vport, u16 vlan_id)
+static void hclge_add_vport_vlan_table(struct hclge_vport *vport, u16 vlan_id,
+                                      bool writen_to_tbl)
 {
        struct hclge_vport_vlan_cfg *vlan;
 
        if (!vlan)
                return;
 
-       vlan->hd_tbl_status = true;
+       vlan->hd_tbl_status = writen_to_tbl;
        vlan->vlan_id = vlan_id;
 
        list_add_tail(&vlan->node, &vport->vlan_list);
 }
 
-void hclge_rm_vport_vlan_table(struct hclge_vport *vport, u16 vlan_id,
-                              bool is_write_tbl)
+static int hclge_add_vport_all_vlan_table(struct hclge_vport *vport)
+{
+       struct hclge_vport_vlan_cfg *vlan, *tmp;
+       struct hclge_dev *hdev = vport->back;
+       int ret;
+
+       list_for_each_entry_safe(vlan, tmp, &vport->vlan_list, node) {
+               if (!vlan->hd_tbl_status) {
+                       ret = hclge_set_vlan_filter_hw(hdev, htons(ETH_P_8021Q),
+                                                      vport->vport_id,
+                                                      vlan->vlan_id, 0, false);
+                       if (ret) {
+                               dev_err(&hdev->pdev->dev,
+                                       "restore vport vlan list failed, ret=%d\n",
+                                       ret);
+                               return ret;
+                       }
+               }
+               vlan->hd_tbl_status = true;
+       }
+
+       return 0;
+}
+
+static void hclge_rm_vport_vlan_table(struct hclge_vport *vport, u16 vlan_id,
+                                     bool is_write_tbl)
 {
        struct hclge_vport_vlan_cfg *vlan, *tmp;
        struct hclge_dev *hdev = vport->back;
        return hclge_set_vlan_rx_offload_cfg(vport);
 }
 
+static int hclge_update_vlan_filter_entries(struct hclge_vport *vport,
+                                           u16 port_base_vlan_state,
+                                           struct hclge_vlan_info *new_info,
+                                           struct hclge_vlan_info *old_info)
+{
+       struct hclge_dev *hdev = vport->back;
+       int ret;
+
+       if (port_base_vlan_state == HNAE3_PORT_BASE_VLAN_ENABLE) {
+               hclge_rm_vport_all_vlan_table(vport, false);
+               return hclge_set_vlan_filter_hw(hdev,
+                                                htons(new_info->vlan_proto),
+                                                vport->vport_id,
+                                                new_info->vlan_tag,
+                                                new_info->qos, false);
+       }
+
+       ret = hclge_set_vlan_filter_hw(hdev, htons(old_info->vlan_proto),
+                                      vport->vport_id, old_info->vlan_tag,
+                                      old_info->qos, true);
+       if (ret)
+               return ret;
+
+       return hclge_add_vport_all_vlan_table(vport);
+}
+
+int hclge_update_port_base_vlan_cfg(struct hclge_vport *vport, u16 state,
+                                   struct hclge_vlan_info *vlan_info)
+{
+       struct hnae3_handle *nic = &vport->nic;
+       struct hclge_vlan_info *old_vlan_info;
+       struct hclge_dev *hdev = vport->back;
+       int ret;
+
+       old_vlan_info = &vport->port_base_vlan_cfg.vlan_info;
+
+       ret = hclge_vlan_offload_cfg(vport, state, vlan_info->vlan_tag);
+       if (ret)
+               return ret;
+
+       if (state == HNAE3_PORT_BASE_VLAN_MODIFY) {
+               /* add new VLAN tag */
+               ret = hclge_set_vlan_filter_hw(hdev, vlan_info->vlan_proto,
+                                              vport->vport_id,
+                                              vlan_info->vlan_tag,
+                                              vlan_info->qos, false);
+               if (ret)
+                       return ret;
+
+               /* remove old VLAN tag */
+               ret = hclge_set_vlan_filter_hw(hdev, old_vlan_info->vlan_proto,
+                                              vport->vport_id,
+                                              old_vlan_info->vlan_tag,
+                                              old_vlan_info->qos, true);
+               if (ret)
+                       return ret;
+
+               goto update;
+       }
+
+       ret = hclge_update_vlan_filter_entries(vport, state, vlan_info,
+                                              old_vlan_info);
+       if (ret)
+               return ret;
+
+       /* update state only when disable/enable port based VLAN */
+       vport->port_base_vlan_cfg.state = state;
+       if (state == HNAE3_PORT_BASE_VLAN_DISABLE)
+               nic->port_base_vlan_state = HNAE3_PORT_BASE_VLAN_DISABLE;
+       else
+               nic->port_base_vlan_state = HNAE3_PORT_BASE_VLAN_ENABLE;
+
+update:
+       vport->port_base_vlan_cfg.vlan_info.vlan_tag = vlan_info->vlan_tag;
+       vport->port_base_vlan_cfg.vlan_info.qos = vlan_info->qos;
+       vport->port_base_vlan_cfg.vlan_info.vlan_proto = vlan_info->vlan_proto;
+
+       return 0;
+}
+
+static u16 hclge_get_port_base_vlan_state(struct hclge_vport *vport,
+                                         enum hnae3_port_base_vlan_state state,
+                                         u16 vlan)
+{
+       if (state == HNAE3_PORT_BASE_VLAN_DISABLE) {
+               if (!vlan)
+                       return HNAE3_PORT_BASE_VLAN_NOCHANGE;
+               else
+                       return HNAE3_PORT_BASE_VLAN_ENABLE;
+       } else {
+               if (!vlan)
+                       return HNAE3_PORT_BASE_VLAN_DISABLE;
+               else if (vport->port_base_vlan_cfg.vlan_info.vlan_tag == vlan)
+                       return HNAE3_PORT_BASE_VLAN_NOCHANGE;
+               else
+                       return HNAE3_PORT_BASE_VLAN_MODIFY;
+       }
+}
+
+static int hclge_set_vf_vlan_filter(struct hnae3_handle *handle, int vfid,
+                                   u16 vlan, u8 qos, __be16 proto)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
+       struct hclge_vlan_info vlan_info;
+       u16 state;
+       int ret;
+
+       if (hdev->pdev->revision == 0x20)
+               return -EOPNOTSUPP;
+
+       /* qos is a 3 bits value, so can not be bigger than 7 */
+       if (vfid >= hdev->num_alloc_vfs || vlan > VLAN_N_VID - 1 || qos > 7)
+               return -EINVAL;
+       if (proto != htons(ETH_P_8021Q))
+               return -EPROTONOSUPPORT;
+
+       vport = &hdev->vport[vfid];
+       state = hclge_get_port_base_vlan_state(vport,
+                                              vport->port_base_vlan_cfg.state,
+                                              vlan);
+       if (state == HNAE3_PORT_BASE_VLAN_NOCHANGE)
+               return 0;
+
+       vlan_info.vlan_tag = vlan;
+       vlan_info.qos = qos;
+       vlan_info.vlan_proto = ntohs(proto);
+
+       /* update port based VLAN for PF */
+       if (!vfid) {
+               hclge_notify_client(hdev, HNAE3_DOWN_CLIENT);
+               ret = hclge_update_port_base_vlan_cfg(vport, state, &vlan_info);
+               hclge_notify_client(hdev, HNAE3_UP_CLIENT);
+
+               return ret;
+       }
+
+       return -EOPNOTSUPP;
+}
+
+int hclge_set_vlan_filter(struct hnae3_handle *handle, __be16 proto,
+                         u16 vlan_id, bool is_kill)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
+       bool writen_to_tbl = false;
+       int ret = 0;
+
+       /* when port based VLAN enabled, we use port based VLAN as the VLAN
+        * filter entry. In this case, we don't update VLAN filter table
+        * when user add new VLAN or remove exist VLAN, just update the vport
+        * VLAN list. The VLAN id in VLAN list won't be writen in VLAN filter
+        * table until port based VLAN disabled
+        */
+       if (handle->port_base_vlan_state == HNAE3_PORT_BASE_VLAN_DISABLE) {
+               ret = hclge_set_vlan_filter_hw(hdev, proto, vport->vport_id,
+                                              vlan_id, 0, is_kill);
+               writen_to_tbl = true;
+       }
+
+       if (ret)
+               return ret;
+
+       if (is_kill)
+               hclge_rm_vport_vlan_table(vport, vlan_id, false);
+       else
+               hclge_add_vport_vlan_table(vport, vlan_id,
+                                          writen_to_tbl);
+
+       return 0;
+}
+
 static int hclge_set_mac_mtu(struct hclge_dev *hdev, int new_mps)
 {
        struct hclge_config_max_frm_size_cmd *req;