};
 
 #define I40E_DEFAULT_ATR_SAMPLE_RATE   20
-#define I40E_FDIR_MAX_RAW_PACKET_SIZE   512
+#define I40E_FDIR_MAX_RAW_PACKET_SIZE  512
+#define I40E_FDIR_BUFFER_FULL_MARGIN   10
+#define I40E_FDIR_BUFFER_HEAD_ROOM     200
+
 struct i40e_fdir_filter {
        struct hlist_node fdir_node;
        /* filter ipnut set */
                             struct i40e_pf *pf, bool add);
 int i40e_add_del_fdir(struct i40e_vsi *vsi,
                      struct i40e_fdir_filter *input, bool add);
+void i40e_fdir_check_and_reenable(struct i40e_pf *pf);
+int i40e_get_current_fd_count(struct i40e_pf *pf);
 void i40e_set_ethtool_ops(struct net_device *netdev);
 struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi,
                                        u8 *macaddr, s16 vlan,
 
  **/
 static void i40e_dbg_cmd_fd_ctrl(struct i40e_pf *pf, u64 flag, bool enable)
 {
-       if (enable)
+       if (enable) {
                pf->flags |= flag;
-       else
+       } else {
                pf->flags &= ~flag;
+               pf->auto_disable_flags |= flag;
+       }
        dev_info(&pf->pdev->dev, "requesting a pf reset\n");
        i40e_do_reset_safe(pf, (1 << __I40E_PF_RESET_REQUESTED));
 }
                bool add = false;
                int ret;
 
+               if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED))
+                       goto command_write_done;
+
+               if (strncmp(cmd_buf, "add", 3) == 0)
+                       add = true;
+
+               if (add && (pf->auto_disable_flags & I40E_FLAG_FD_SB_ENABLED))
+                       goto command_write_done;
+
                asc_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_SIZE,
                                     GFP_KERNEL);
                if (!asc_packet)
                        goto command_write_done;
                }
 
-               if (strncmp(cmd_buf, "add", 3) == 0)
-                       add = true;
                cnt = sscanf(&cmd_buf[13],
                             "%hx %2hhx %2hhx %hx %2hhx %2hhx %hx %x %hd %511s",
                             &fd_data.q_index,
 
 
        ret = i40e_update_ethtool_fdir_entry(vsi, NULL, fsp->location, cmd);
 
+       i40e_fdir_check_and_reenable(pf);
        return ret;
 }
 
        if (!vsi)
                return -EINVAL;
 
-       fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
        pf = vsi->back;
 
+       if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED))
+               return -EOPNOTSUPP;
+
+       if (add && (pf->auto_disable_flags & I40E_FLAG_FD_SB_ENABLED))
+               return -ENOSPC;
+
+       fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
+
        if (fsp->location >= (pf->hw.func_caps.fd_filters_best_effort +
                              pf->hw.func_caps.fd_filters_guaranteed)) {
                return -EINVAL;
 
        struct i40e_pf *pf = vsi->back;
        struct hlist_node *node;
 
+       if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED))
+               return;
+
        hlist_for_each_entry_safe(filter, node,
                                  &pf->fdir_filter_list, fdir_node) {
                i40e_add_del_fdir(vsi, filter, true);
        clear_bit(__I40E_SERVICE_SCHED, &pf->state);
 }
 
+/**
+ * i40e_get_current_fd_count - Get the count of FD filters programmed in the HW
+ * @pf: board private structure
+ **/
+int i40e_get_current_fd_count(struct i40e_pf *pf)
+{
+       int val, fcnt_prog;
+       val = rd32(&pf->hw, I40E_PFQF_FDSTAT);
+       fcnt_prog = (val & I40E_PFQF_FDSTAT_GUARANT_CNT_MASK) +
+                   ((val & I40E_PFQF_FDSTAT_BEST_CNT_MASK) >>
+                     I40E_PFQF_FDSTAT_BEST_CNT_SHIFT);
+       return fcnt_prog;
+}
+
+/**
+ * i40e_fdir_check_and_reenable - Function to reenabe FD ATR or SB if disabled
+ * @pf: board private structure
+ **/
+void i40e_fdir_check_and_reenable(struct i40e_pf *pf)
+{
+       u32 fcnt_prog, fcnt_avail;
+
+       /* Check if, FD SB or ATR was auto disabled and if there is enough room
+        * to re-enable
+        */
+       if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) &&
+           (pf->flags & I40E_FLAG_FD_SB_ENABLED))
+               return;
+       fcnt_prog = i40e_get_current_fd_count(pf);
+       fcnt_avail = pf->hw.fdir_shared_filter_count +
+                                              pf->fdir_pf_filter_count;
+       if (fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM)) {
+               if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) &&
+                   (pf->auto_disable_flags & I40E_FLAG_FD_SB_ENABLED)) {
+                       pf->auto_disable_flags &= ~I40E_FLAG_FD_SB_ENABLED;
+                       dev_info(&pf->pdev->dev, "FD Sideband/ntuple is being enabled since we have space in the table now\n");
+               }
+       }
+       /* Wait for some more space to be available to turn on ATR */
+       if (fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM * 2)) {
+               if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) &&
+                   (pf->auto_disable_flags & I40E_FLAG_FD_ATR_ENABLED)) {
+                       pf->auto_disable_flags &= ~I40E_FLAG_FD_ATR_ENABLED;
+                       dev_info(&pf->pdev->dev, "ATR is being enabled since we have space in the table now\n");
+               }
+       }
+}
+
 /**
  * i40e_fdir_reinit_subtask - Worker thread to reinit FDIR filter table
  * @pf: board private structure
        if (!(pf->flags & I40E_FLAG_FDIR_REQUIRES_REINIT))
                return;
 
-       pf->flags &= ~I40E_FLAG_FDIR_REQUIRES_REINIT;
-
        /* if interface is down do nothing */
        if (test_bit(__I40E_DOWN, &pf->state))
                return;
+       i40e_fdir_check_and_reenable(pf);
+
+       if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) &&
+           (pf->flags & I40E_FLAG_FD_SB_ENABLED))
+               pf->flags &= ~I40E_FLAG_FDIR_REQUIRES_REINIT;
 }
 
 /**
 
 /**
  * i40e_fd_handle_status - check the Programming Status for FD
  * @rx_ring: the Rx ring for this descriptor
- * @qw: the descriptor data
+ * @rx_desc: the Rx descriptor for programming Status, not a packet descriptor.
  * @prog_id: the id originally used for programming
  *
  * This is used to verify if the FD programming or invalidation
  * requested by SW to the HW is successful or not and take actions accordingly.
  **/
-static void i40e_fd_handle_status(struct i40e_ring *rx_ring, u32 qw, u8 prog_id)
+static void i40e_fd_handle_status(struct i40e_ring *rx_ring,
+                                 union i40e_rx_desc *rx_desc, u8 prog_id)
 {
-       struct pci_dev *pdev = rx_ring->vsi->back->pdev;
+       struct i40e_pf *pf = rx_ring->vsi->back;
+       struct pci_dev *pdev = pf->pdev;
+       u32 fcnt_prog, fcnt_avail;
        u32 error;
+       u64 qw;
 
+       qw = le64_to_cpu(rx_desc->wb.qword1.status_error_len);
        error = (qw & I40E_RX_PROG_STATUS_DESC_QW1_ERROR_MASK) >>
                I40E_RX_PROG_STATUS_DESC_QW1_ERROR_SHIFT;
 
-       /* for now just print the Status */
-       dev_info(&pdev->dev, "FD programming id %02x, Status %08x\n",
-                prog_id, error);
+       if (error == (0x1 << I40E_RX_PROG_STATUS_DESC_FD_TBL_FULL_SHIFT)) {
+               dev_warn(&pdev->dev, "ntuple filter loc = %d, could not be added\n",
+                        rx_desc->wb.qword0.hi_dword.fd_id);
+
+               /* filter programming failed most likely due to table full */
+               fcnt_prog = i40e_get_current_fd_count(pf);
+               fcnt_avail = pf->hw.fdir_shared_filter_count +
+                                                      pf->fdir_pf_filter_count;
+
+               /* If ATR is running fcnt_prog can quickly change,
+                * if we are very close to full, it makes sense to disable
+                * FD ATR/SB and then re-enable it when there is room.
+                */
+               if (fcnt_prog >= (fcnt_avail - I40E_FDIR_BUFFER_FULL_MARGIN)) {
+                       /* Turn off ATR first */
+                       if (pf->flags | I40E_FLAG_FD_ATR_ENABLED) {
+                               pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
+                               dev_warn(&pdev->dev, "FD filter space full, ATR for further flows will be turned off\n");
+                               pf->auto_disable_flags |=
+                                                      I40E_FLAG_FD_ATR_ENABLED;
+                               pf->flags |= I40E_FLAG_FDIR_REQUIRES_REINIT;
+                       } else if (pf->flags | I40E_FLAG_FD_SB_ENABLED) {
+                               pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
+                               dev_warn(&pdev->dev, "FD filter space full, new ntuple rules will not be added\n");
+                               pf->auto_disable_flags |=
+                                                       I40E_FLAG_FD_SB_ENABLED;
+                               pf->flags |= I40E_FLAG_FDIR_REQUIRES_REINIT;
+                       }
+               } else {
+                       dev_info(&pdev->dev, "FD filter programming error");
+               }
+       } else if (error ==
+                         (0x1 << I40E_RX_PROG_STATUS_DESC_NO_FD_ENTRY_SHIFT)) {
+               netdev_info(rx_ring->vsi->netdev, "ntuple filter loc = %d, could not be removed\n",
+                           rx_desc->wb.qword0.hi_dword.fd_id);
+       }
 }
 
 /**
                  I40E_RX_PROG_STATUS_DESC_QW1_PROGID_SHIFT;
 
        if (id == I40E_RX_PROG_STATUS_DESC_FD_FILTER_STATUS)
-               i40e_fd_handle_status(rx_ring, qw, id);
+               i40e_fd_handle_status(rx_ring, rx_desc, id);
 }
 
 /**
        if (!tx_ring->atr_sample_rate)
                return;
 
-       tx_ring->atr_count++;
-
        /* snag network header to get L4 type and address */
        hdr.network = skb_network_header(skb);
 
 
        th = (struct tcphdr *)(hdr.network + hlen);
 
+       /* Due to lack of space, no more new filters can be programmed */
+       if (th->syn && (pf->auto_disable_flags & I40E_FLAG_FD_ATR_ENABLED))
+               return;
+
+       tx_ring->atr_count++;
+
        /* sample on all syn/fin packets or once every atr sample rate */
        if (!th->fin && !th->syn && (tx_ring->atr_count < tx_ring->atr_sample_rate))
                return;