list_add(&tmp->list_entry, &tmp_add_list);
 
        status = ice_add_vlan(&pf->hw, &tmp_add_list);
-       if (status) {
+       if (!status) {
+               vsi->num_vlan++;
+       } else {
                err = -ENODEV;
                dev_err(dev, "Failure Adding VLAN %d on VSI %i\n", vid,
                        vsi->vsi_num);
        list_add(&list->list_entry, &tmp_add_list);
 
        status = ice_remove_vlan(&pf->hw, &tmp_add_list);
-       if (status == ICE_ERR_DOES_NOT_EXIST) {
+       if (!status) {
+               vsi->num_vlan--;
+       } else if (status == ICE_ERR_DOES_NOT_EXIST) {
                dev_dbg(dev, "Failed to remove VLAN %d on VSI %i, it does not exist, status: %d\n",
                        vid, vsi->vsi_num, status);
-       } else if (status) {
+       } else {
                dev_err(dev, "Error removing VLAN %d on vsi %i error: %d\n",
                        vid, vsi->vsi_num, status);
                err = -EIO;
        return ice_vsi_stop_tx_rings(vsi, ICE_NO_RESET, 0, vsi->xdp_rings);
 }
 
+/**
+ * ice_vsi_is_vlan_pruning_ena - check if VLAN pruning is enabled or not
+ * @vsi: VSI to check whether or not VLAN pruning is enabled.
+ *
+ * returns true if Rx VLAN pruning and Tx VLAN anti-spoof is enabled and false
+ * otherwise.
+ */
+bool ice_vsi_is_vlan_pruning_ena(struct ice_vsi *vsi)
+{
+       u8 rx_pruning = ICE_AQ_VSI_SW_FLAG_RX_VLAN_PRUNE_ENA;
+       u8 tx_pruning = ICE_AQ_VSI_SEC_TX_VLAN_PRUNE_ENA <<
+               ICE_AQ_VSI_SEC_TX_PRUNE_ENA_S;
+
+       if (!vsi)
+               return false;
+
+       return ((vsi->info.sw_flags2 & rx_pruning) &&
+               (vsi->info.sec_flags & tx_pruning));
+}
+
 /**
  * ice_cfg_vlan_pruning - enable or disable VLAN pruning on the VSI
  * @vsi: VSI to enable or disable VLAN pruning on
                if (ret)
                        goto unroll_vector_base;
 
+               /* Always add VLAN ID 0 switch rule by default. This is needed
+                * in order to allow all untagged and 0 tagged priority traffic
+                * if Rx VLAN pruning is enabled. Also there are cases where we
+                * don't get the call to add VLAN 0 via ice_vlan_rx_add_vid()
+                * so this handles those cases (i.e. adding the PF to a bridge
+                * without the 8021q module loaded).
+                */
+               ret = ice_vsi_add_vlan(vsi, 0);
+               if (ret)
+                       goto unroll_clear_rings;
+
                ice_vsi_map_rings_to_vectors(vsi);
 
                /* Do not exit if configuring RSS had an issue, at least
 
        return vsi;
 
+unroll_clear_rings:
+       ice_vsi_clear_rings(vsi);
 unroll_vector_base:
        /* reclaim SW interrupts back to the common pool */
        ice_free_res(pf->irq_tracker, vsi->base_vector, vsi->idx);
 
        if (vsi->info.pvid)
                return -EINVAL;
 
-       /* Enable VLAN pruning when VLAN 0 is added */
-       if (unlikely(!vid)) {
+       /* VLAN 0 is added by default during load/reset */
+       if (!vid)
+               return 0;
+
+       /* Enable VLAN pruning when a VLAN other than 0 is added */
+       if (!ice_vsi_is_vlan_pruning_ena(vsi)) {
                ret = ice_cfg_vlan_pruning(vsi, true, false);
                if (ret)
                        return ret;
        }
 
-       /* Add all VLAN IDs including 0 to the switch filter. VLAN ID 0 is
-        * needed to continue allowing all untagged packets since VLAN prune
-        * list is applied to all packets by the switch
+       /* Add a switch rule for this VLAN ID so its corresponding VLAN tagged
+        * packets aren't pruned by the device's internal switch on Rx
         */
        ret = ice_vsi_add_vlan(vsi, vid);
        if (!ret) {
        if (vsi->info.pvid)
                return -EINVAL;
 
+       /* don't allow removal of VLAN 0 */
+       if (!vid)
+               return 0;
+
        /* Make sure ice_vsi_kill_vlan is successful before updating VLAN
         * information
         */
        if (ret)
                return ret;
 
-       /* Disable VLAN pruning when VLAN 0 is removed */
-       if (unlikely(!vid))
+       /* Disable pruning when VLAN 0 is the only VLAN rule */
+       if (vsi->num_vlan == 1 && ice_vsi_is_vlan_pruning_ena(vsi))
                ret = ice_cfg_vlan_pruning(vsi, false, false);
 
        vsi->vlan_ena = false;
 
                                goto error_param;
                        }
 
-                       vsi->num_vlan++;
-                       /* Enable VLAN pruning when VLAN is added */
-                       if (!vlan_promisc) {
+                       /* Enable VLAN pruning when non-zero VLAN is added */
+                       if (!vlan_promisc && vid &&
+                           !ice_vsi_is_vlan_pruning_ena(vsi)) {
                                status = ice_cfg_vlan_pruning(vsi, true, false);
                                if (status) {
                                        v_ret = VIRTCHNL_STATUS_ERR_PARAM;
                                                vid, status);
                                        goto error_param;
                                }
-                       } else {
+                       } else if (vlan_promisc) {
                                /* Enable Ucast/Mcast VLAN promiscuous mode */
                                promisc_m = ICE_PROMISC_VLAN_TX |
                                            ICE_PROMISC_VLAN_RX;
                                goto error_param;
                        }
 
-                       vsi->num_vlan--;
-                       /* Disable VLAN pruning when the last VLAN is removed */
-                       if (!vsi->num_vlan)
+                       /* Disable VLAN pruning when only VLAN 0 is left */
+                       if (vsi->num_vlan == 1 &&
+                           ice_vsi_is_vlan_pruning_ena(vsi))
                                ice_cfg_vlan_pruning(vsi, false, false);
 
                        /* Disable Unicast/Multicast VLAN promiscuous mode */