#define IAVF_FLAG_QUEUES_DISABLED              BIT(17)
 #define IAVF_FLAG_SETUP_NETDEV_FEATURES                BIT(18)
 #define IAVF_FLAG_REINIT_MSIX_NEEDED           BIT(20)
+#define IAVF_FLAG_FDIR_ENABLED                 BIT(21)
 /* duplicates for common code */
 #define IAVF_FLAG_DCB_ENABLED                  0
        /* flags for admin queue service task */
 
        struct iavf_fdir_fltr *rule = NULL;
        int ret = 0;
 
-       if (!FDIR_FLTR_SUPPORT(adapter))
+       if (!(adapter->flags & IAVF_FLAG_FDIR_ENABLED))
                return -EOPNOTSUPP;
 
        spin_lock_bh(&adapter->fdir_fltr_lock);
        unsigned int cnt = 0;
        int val = 0;
 
-       if (!FDIR_FLTR_SUPPORT(adapter))
+       if (!(adapter->flags & IAVF_FLAG_FDIR_ENABLED))
                return -EOPNOTSUPP;
 
        cmd->data = IAVF_MAX_FDIR_FILTERS;
        int count = 50;
        int err;
 
-       if (!FDIR_FLTR_SUPPORT(adapter))
+       if (!(adapter->flags & IAVF_FLAG_FDIR_ENABLED))
                return -EOPNOTSUPP;
 
        if (fsp->flow_type & FLOW_MAC_EXT)
        spin_lock_bh(&adapter->fdir_fltr_lock);
        iavf_fdir_list_add_fltr(adapter, fltr);
        adapter->fdir_active_fltr++;
-       fltr->state = IAVF_FDIR_FLTR_ADD_REQUEST;
-       adapter->aq_required |= IAVF_FLAG_AQ_ADD_FDIR_FILTER;
+       if (adapter->link_up) {
+               fltr->state = IAVF_FDIR_FLTR_ADD_REQUEST;
+               adapter->aq_required |= IAVF_FLAG_AQ_ADD_FDIR_FILTER;
+       } else {
+               fltr->state = IAVF_FDIR_FLTR_INACTIVE;
+       }
        spin_unlock_bh(&adapter->fdir_fltr_lock);
 
-       mod_delayed_work(adapter->wq, &adapter->watchdog_task, 0);
-
+       if (adapter->link_up)
+               mod_delayed_work(adapter->wq, &adapter->watchdog_task, 0);
 ret:
        if (err && fltr)
                kfree(fltr);
        struct iavf_fdir_fltr *fltr = NULL;
        int err = 0;
 
-       if (!FDIR_FLTR_SUPPORT(adapter))
+       if (!(adapter->flags & IAVF_FLAG_FDIR_ENABLED))
                return -EOPNOTSUPP;
 
        spin_lock_bh(&adapter->fdir_fltr_lock);
                if (fltr->state == IAVF_FDIR_FLTR_ACTIVE) {
                        fltr->state = IAVF_FDIR_FLTR_DEL_REQUEST;
                        adapter->aq_required |= IAVF_FLAG_AQ_DEL_FDIR_FILTER;
+               } else if (fltr->state == IAVF_FDIR_FLTR_INACTIVE) {
+                       list_del(&fltr->list);
+                       kfree(fltr);
+                       adapter->fdir_active_fltr--;
+                       fltr = NULL;
                } else {
                        err = -EBUSY;
                }
                ret = 0;
                break;
        case ETHTOOL_GRXCLSRLCNT:
-               if (!FDIR_FLTR_SUPPORT(adapter))
+               if (!(adapter->flags & IAVF_FLAG_FDIR_ENABLED))
                        break;
                spin_lock_bh(&adapter->fdir_fltr_lock);
                cmd->rule_cnt = adapter->fdir_active_fltr;
 
 
 struct iavf_adapter;
 
-/* State of Flow Director filter */
+/* State of Flow Director filter
+ *
+ * *_REQUEST states are used to mark filter to be sent to PF driver to perform
+ * an action (either add or delete filter). *_PENDING states are an indication
+ * that request was sent to PF and the driver is waiting for response.
+ *
+ * Both DELETE and DISABLE states are being used to delete a filter in PF.
+ * The difference is that after a successful response filter in DEL_PENDING
+ * state is being deleted from VF driver as well and filter in DIS_PENDING state
+ * is being changed to INACTIVE state.
+ */
 enum iavf_fdir_fltr_state_t {
        IAVF_FDIR_FLTR_ADD_REQUEST,     /* User requests to add filter */
        IAVF_FDIR_FLTR_ADD_PENDING,     /* Filter pending add by the PF */
        IAVF_FDIR_FLTR_DEL_REQUEST,     /* User requests to delete filter */
        IAVF_FDIR_FLTR_DEL_PENDING,     /* Filter pending delete by the PF */
+       IAVF_FDIR_FLTR_DIS_REQUEST,     /* Filter scheduled to be disabled */
+       IAVF_FDIR_FLTR_DIS_PENDING,     /* Filter pending disable by the PF */
+       IAVF_FDIR_FLTR_INACTIVE,        /* Filter inactive on link down */
        IAVF_FDIR_FLTR_ACTIVE,          /* Filter is active */
 };
 
 
  **/
 static void iavf_clear_fdir_filters(struct iavf_adapter *adapter)
 {
-       struct iavf_fdir_fltr *fdir, *fdirtmp;
+       struct iavf_fdir_fltr *fdir;
 
        /* remove all Flow Director filters */
        spin_lock_bh(&adapter->fdir_fltr_lock);
-       list_for_each_entry_safe(fdir, fdirtmp, &adapter->fdir_list_head,
-                                list) {
+       list_for_each_entry(fdir, &adapter->fdir_list_head, list) {
                if (fdir->state == IAVF_FDIR_FLTR_ADD_REQUEST) {
-                       list_del(&fdir->list);
-                       kfree(fdir);
-                       adapter->fdir_active_fltr--;
-               } else {
-                       fdir->state = IAVF_FDIR_FLTR_DEL_REQUEST;
+                       /* Cancel a request, keep filter as inactive */
+                       fdir->state = IAVF_FDIR_FLTR_INACTIVE;
+               } else if (fdir->state == IAVF_FDIR_FLTR_ADD_PENDING ||
+                        fdir->state == IAVF_FDIR_FLTR_ACTIVE) {
+                       /* Disable filters which are active or have a pending
+                        * request to PF to be added
+                        */
+                       fdir->state = IAVF_FDIR_FLTR_DIS_REQUEST;
                }
        }
        spin_unlock_bh(&adapter->fdir_fltr_lock);
        }
 }
 
+/**
+ * iavf_restore_fdir_filters
+ * @adapter: board private structure
+ *
+ * Restore existing FDIR filters when VF netdev comes back up.
+ **/
+static void iavf_restore_fdir_filters(struct iavf_adapter *adapter)
+{
+       struct iavf_fdir_fltr *f;
+
+       spin_lock_bh(&adapter->fdir_fltr_lock);
+       list_for_each_entry(f, &adapter->fdir_list_head, list) {
+               if (f->state == IAVF_FDIR_FLTR_DIS_REQUEST) {
+                       /* Cancel a request, keep filter as active */
+                       f->state = IAVF_FDIR_FLTR_ACTIVE;
+               } else if (f->state == IAVF_FDIR_FLTR_DIS_PENDING ||
+                          f->state == IAVF_FDIR_FLTR_INACTIVE) {
+                       /* Add filters which are inactive or have a pending
+                        * request to PF to be deleted
+                        */
+                       f->state = IAVF_FDIR_FLTR_ADD_REQUEST;
+                       adapter->aq_required |= IAVF_FLAG_AQ_ADD_FDIR_FILTER;
+               }
+       }
+       spin_unlock_bh(&adapter->fdir_fltr_lock);
+}
+
 /**
  * iavf_open - Called when a network interface is made active
  * @netdev: network interface device structure
 
        spin_unlock_bh(&adapter->mac_vlan_list_lock);
 
-       /* Restore VLAN filters that were removed with IFF_DOWN */
+       /* Restore filters that were removed with IFF_DOWN */
        iavf_restore_filters(adapter);
+       iavf_restore_fdir_filters(adapter);
 
        iavf_configure(adapter);
 
 
  **/
 void iavf_del_fdir_filter(struct iavf_adapter *adapter)
 {
+       struct virtchnl_fdir_del f = {};
        struct iavf_fdir_fltr *fdir;
-       struct virtchnl_fdir_del f;
        bool process_fltr = false;
        int len;
 
        list_for_each_entry(fdir, &adapter->fdir_list_head, list) {
                if (fdir->state == IAVF_FDIR_FLTR_DEL_REQUEST) {
                        process_fltr = true;
-                       memset(&f, 0, len);
                        f.vsi_id = fdir->vc_add_msg.vsi_id;
                        f.flow_id = fdir->flow_id;
                        fdir->state = IAVF_FDIR_FLTR_DEL_PENDING;
                        break;
+               } else if (fdir->state == IAVF_FDIR_FLTR_DIS_REQUEST) {
+                       process_fltr = true;
+                       f.vsi_id = fdir->vc_add_msg.vsi_id;
+                       f.flow_id = fdir->flow_id;
+                       fdir->state = IAVF_FDIR_FLTR_DIS_PENDING;
+                       break;
                }
        }
        spin_unlock_bh(&adapter->fdir_fltr_lock);
                netdev->features &= ~NETIF_F_HW_VLAN_CTAG_RX;
 }
 
+/**
+ * iavf_activate_fdir_filters - Reactivate all FDIR filters after a reset
+ * @adapter: private adapter structure
+ *
+ * Called after a reset to re-add all FDIR filters and delete some of them
+ * if they were pending to be deleted.
+ */
+static void iavf_activate_fdir_filters(struct iavf_adapter *adapter)
+{
+       struct iavf_fdir_fltr *f, *ftmp;
+       bool add_filters = false;
+
+       spin_lock_bh(&adapter->fdir_fltr_lock);
+       list_for_each_entry_safe(f, ftmp, &adapter->fdir_list_head, list) {
+               if (f->state == IAVF_FDIR_FLTR_ADD_REQUEST ||
+                   f->state == IAVF_FDIR_FLTR_ADD_PENDING ||
+                   f->state == IAVF_FDIR_FLTR_ACTIVE) {
+                       /* All filters and requests have been removed in PF,
+                        * restore them
+                        */
+                       f->state = IAVF_FDIR_FLTR_ADD_REQUEST;
+                       add_filters = true;
+               } else if (f->state == IAVF_FDIR_FLTR_DIS_REQUEST ||
+                          f->state == IAVF_FDIR_FLTR_DIS_PENDING) {
+                       /* Link down state, leave filters as inactive */
+                       f->state = IAVF_FDIR_FLTR_INACTIVE;
+               } else if (f->state == IAVF_FDIR_FLTR_DEL_REQUEST ||
+                          f->state == IAVF_FDIR_FLTR_DEL_PENDING) {
+                       /* Delete filters that were pending to be deleted, the
+                        * list on PF is already cleared after a reset
+                        */
+                       list_del(&f->list);
+                       kfree(f);
+                       adapter->fdir_active_fltr--;
+               }
+       }
+       spin_unlock_bh(&adapter->fdir_fltr_lock);
+
+       if (add_filters)
+               adapter->aq_required |= IAVF_FLAG_AQ_ADD_FDIR_FILTER;
+}
+
 /**
  * iavf_virtchnl_completion
  * @adapter: adapter structure
                        spin_lock_bh(&adapter->fdir_fltr_lock);
                        list_for_each_entry(fdir, &adapter->fdir_list_head,
                                            list) {
-                               if (fdir->state == IAVF_FDIR_FLTR_DEL_PENDING) {
+                               if (fdir->state == IAVF_FDIR_FLTR_DEL_PENDING ||
+                                   fdir->state == IAVF_FDIR_FLTR_DIS_PENDING) {
                                        fdir->state = IAVF_FDIR_FLTR_ACTIVE;
                                        dev_info(&adapter->pdev->dev, "Failed to del Flow Director filter, error %s\n",
                                                 iavf_stat_str(&adapter->hw,
 
                spin_unlock_bh(&adapter->mac_vlan_list_lock);
 
+               iavf_activate_fdir_filters(adapter);
+
                iavf_parse_vf_resource_msg(adapter);
 
                /* negotiated VIRTCHNL_VF_OFFLOAD_VLAN_V2, so wait for the
                list_for_each_entry_safe(fdir, fdir_tmp, &adapter->fdir_list_head,
                                         list) {
                        if (fdir->state == IAVF_FDIR_FLTR_DEL_PENDING) {
-                               if (del_fltr->status == VIRTCHNL_FDIR_SUCCESS) {
+                               if (del_fltr->status == VIRTCHNL_FDIR_SUCCESS ||
+                                   del_fltr->status ==
+                                   VIRTCHNL_FDIR_FAILURE_RULE_NONEXIST) {
                                        dev_info(&adapter->pdev->dev, "Flow Director filter with location %u is deleted\n",
                                                 fdir->loc);
                                        list_del(&fdir->list);
                                                 del_fltr->status);
                                        iavf_print_fdir_fltr(adapter, fdir);
                                }
+                       } else if (fdir->state == IAVF_FDIR_FLTR_DIS_PENDING) {
+                               if (del_fltr->status == VIRTCHNL_FDIR_SUCCESS ||
+                                   del_fltr->status ==
+                                   VIRTCHNL_FDIR_FAILURE_RULE_NONEXIST) {
+                                       fdir->state = IAVF_FDIR_FLTR_INACTIVE;
+                               } else {
+                                       fdir->state = IAVF_FDIR_FLTR_ACTIVE;
+                                       dev_info(&adapter->pdev->dev, "Failed to disable Flow Director filter with status: %d\n",
+                                                del_fltr->status);
+                                       iavf_print_fdir_fltr(adapter, fdir);
+                               }
                        }
                }
                spin_unlock_bh(&adapter->fdir_fltr_lock);