static inline void cxio_set_wq_in_error(struct t3_wq *wq)
 {
-       wq->queue->wq_in_err.err = 1;
+       wq->queue->wq_in_err.err |= 1;
+}
+
+static inline void cxio_disable_wq_db(struct t3_wq *wq)
+{
+       wq->queue->wq_in_err.err |= 2;
+}
+
+static inline void cxio_enable_wq_db(struct t3_wq *wq)
+{
+       wq->queue->wq_in_err.err &= ~2;
+}
+
+static inline int cxio_wq_db_enabled(struct t3_wq *wq)
+{
+       return !(wq->queue->wq_in_err.err & 2);
 }
 
 static inline struct t3_cqe *cxio_next_hw_cqe(struct t3_cq *cq)
 
 static LIST_HEAD(dev_list);
 static DEFINE_MUTEX(dev_mutex);
 
+static int disable_qp_db(int id, void *p, void *data)
+{
+       struct iwch_qp *qhp = p;
+
+       cxio_disable_wq_db(&qhp->wq);
+       return 0;
+}
+
+static int enable_qp_db(int id, void *p, void *data)
+{
+       struct iwch_qp *qhp = p;
+
+       if (data)
+               ring_doorbell(qhp->rhp->rdev.ctrl_qp.doorbell, qhp->wq.qpid);
+       cxio_enable_wq_db(&qhp->wq);
+       return 0;
+}
+
+static void disable_dbs(struct iwch_dev *rnicp)
+{
+       spin_lock_irq(&rnicp->lock);
+       idr_for_each(&rnicp->qpidr, disable_qp_db, NULL);
+       spin_unlock_irq(&rnicp->lock);
+}
+
+static void enable_dbs(struct iwch_dev *rnicp, int ring_db)
+{
+       spin_lock_irq(&rnicp->lock);
+       idr_for_each(&rnicp->qpidr, enable_qp_db,
+                    (void *)(unsigned long)ring_db);
+       spin_unlock_irq(&rnicp->lock);
+}
+
+static void iwch_db_drop_task(struct work_struct *work)
+{
+       struct iwch_dev *rnicp = container_of(work, struct iwch_dev,
+                                             db_drop_task.work);
+       enable_dbs(rnicp, 1);
+}
+
 static void rnic_init(struct iwch_dev *rnicp)
 {
        PDBG("%s iwch_dev %p\n", __func__,  rnicp);
        idr_init(&rnicp->qpidr);
        idr_init(&rnicp->mmidr);
        spin_lock_init(&rnicp->lock);
+       INIT_DELAYED_WORK(&rnicp->db_drop_task, iwch_db_drop_task);
 
        rnicp->attr.max_qps = T3_MAX_NUM_QP - 32;
        rnicp->attr.max_wrs = T3_MAX_QP_DEPTH;
        mutex_lock(&dev_mutex);
        list_for_each_entry_safe(dev, tmp, &dev_list, entry) {
                if (dev->rdev.t3cdev_p == tdev) {
+                       cancel_delayed_work_sync(&dev->db_drop_task);
                        list_del(&dev->entry);
                        iwch_unregister_device(dev);
                        cxio_rdev_close(&dev->rdev);
        struct cxio_rdev *rdev = tdev->ulp;
        struct iwch_dev *rnicp;
        struct ib_event event;
-       u32    portnum = port_id + 1;
+       u32 portnum = port_id + 1;
+       int dispatch = 0;
 
        if (!rdev)
                return;
        case OFFLOAD_STATUS_DOWN: {
                rdev->flags = CXIO_ERROR_FATAL;
                event.event  = IB_EVENT_DEVICE_FATAL;
+               dispatch = 1;
                break;
                }
        case OFFLOAD_PORT_DOWN: {
                event.event  = IB_EVENT_PORT_ERR;
+               dispatch = 1;
                break;
                }
        case OFFLOAD_PORT_UP: {
                event.event  = IB_EVENT_PORT_ACTIVE;
+               dispatch = 1;
+               break;
+               }
+       case OFFLOAD_DB_FULL: {
+               disable_dbs(rnicp);
+               break;
+               }
+       case OFFLOAD_DB_EMPTY: {
+               enable_dbs(rnicp, 1);
+               break;
+               }
+       case OFFLOAD_DB_DROP: {
+               unsigned long delay = 1000;
+               unsigned short r;
+
+               disable_dbs(rnicp);
+               get_random_bytes(&r, 2);
+               delay += r & 1023;
+
+               /*
+                * delay is between 1000-2023 usecs.
+                */
+               schedule_delayed_work(&rnicp->db_drop_task,
+                       usecs_to_jiffies(delay));
                break;
                }
        }
 
-       event.device = &rnicp->ibdev;
-       event.element.port_num = portnum;
-       ib_dispatch_event(&event);
+       if (dispatch) {
+               event.device = &rnicp->ibdev;
+               event.element.port_num = portnum;
+               ib_dispatch_event(&event);
+       }
 
        return;
 }
 
 #include <linux/list.h>
 #include <linux/spinlock.h>
 #include <linux/idr.h>
+#include <linux/workqueue.h>
 
 #include <rdma/ib_verbs.h>
 
        struct idr mmidr;
        spinlock_t lock;
        struct list_head entry;
+       struct delayed_work db_drop_task;
 };
 
 static inline struct iwch_dev *to_iwch_dev(struct ib_device *ibdev)
 
                ++(qhp->wq.sq_wptr);
        }
        spin_unlock_irqrestore(&qhp->lock, flag);
-       ring_doorbell(qhp->wq.doorbell, qhp->wq.qpid);
+       if (cxio_wq_db_enabled(&qhp->wq))
+               ring_doorbell(qhp->wq.doorbell, qhp->wq.qpid);
 
 out:
        if (err)
                num_wrs--;
        }
        spin_unlock_irqrestore(&qhp->lock, flag);
-       ring_doorbell(qhp->wq.doorbell, qhp->wq.qpid);
+       if (cxio_wq_db_enabled(&qhp->wq))
+               ring_doorbell(qhp->wq.doorbell, qhp->wq.qpid);
 
 out:
        if (err)
        ++(qhp->wq.sq_wptr);
        spin_unlock_irqrestore(&qhp->lock, flag);
 
-       ring_doorbell(qhp->wq.doorbell, qhp->wq.qpid);
+       if (cxio_wq_db_enabled(&qhp->wq))
+               ring_doorbell(qhp->wq.doorbell, qhp->wq.qpid);
 
        return err;
 }
 
        struct work_struct fatal_error_handler_task;
        struct work_struct link_fault_handler_task;
 
+       struct work_struct db_full_task;
+       struct work_struct db_empty_task;
+       struct work_struct db_drop_task;
+
        struct dentry *debugfs_root;
 
        struct mutex mdio_lock;
 int t3_get_desc(const struct sge_qset *qs, unsigned int qnum, unsigned int idx,
                unsigned char *data);
 irqreturn_t t3_sge_intr_msix(int irq, void *cookie);
+extern struct workqueue_struct *cxgb3_wq;
 
 int t3_get_edc_fw(struct cphy *phy, int edc_idx, int size);
 
 
 #include <linux/firmware.h>
 #include <linux/log2.h>
 #include <linux/stringify.h>
+#include <linux/sched.h>
 #include <asm/uaccess.h>
 
 #include "common.h"
  * will block keventd as it needs the rtnl lock, and we'll deadlock waiting
  * for our work to complete.  Get our own work queue to solve this.
  */
-static struct workqueue_struct *cxgb3_wq;
+struct workqueue_struct *cxgb3_wq;
 
 /**
  *     link_report - show link status and link speed/duplex
                      V_RRCPLCPUSIZE(6) | F_HASHTOEPLITZ, cpus, rspq_map);
 }
 
+static void ring_dbs(struct adapter *adap)
+{
+       int i, j;
+
+       for (i = 0; i < SGE_QSETS; i++) {
+               struct sge_qset *qs = &adap->sge.qs[i];
+
+               if (qs->adap)
+                       for (j = 0; j < SGE_TXQ_PER_SET; j++)
+                               t3_write_reg(adap, A_SG_KDOORBELL, F_SELEGRCNTX | V_EGRCNTX(qs->txq[j].cntxt_id));
+       }
+}
+
 static void init_napi(struct adapter *adap)
 {
        int i;
        spin_unlock_irq(&adapter->work_lock);
 }
 
+static void db_full_task(struct work_struct *work)
+{
+       struct adapter *adapter = container_of(work, struct adapter,
+                                              db_full_task);
+
+       cxgb3_event_notify(&adapter->tdev, OFFLOAD_DB_FULL, 0);
+}
+
+static void db_empty_task(struct work_struct *work)
+{
+       struct adapter *adapter = container_of(work, struct adapter,
+                                              db_empty_task);
+
+       cxgb3_event_notify(&adapter->tdev, OFFLOAD_DB_EMPTY, 0);
+}
+
+static void db_drop_task(struct work_struct *work)
+{
+       struct adapter *adapter = container_of(work, struct adapter,
+                                              db_drop_task);
+       unsigned long delay = 1000;
+       unsigned short r;
+
+       cxgb3_event_notify(&adapter->tdev, OFFLOAD_DB_DROP, 0);
+
+       /*
+        * Sleep a while before ringing the driver qset dbs.
+        * The delay is between 1000-2023 usecs.
+        */
+       get_random_bytes(&r, 2);
+       delay += r & 1023;
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       schedule_timeout(usecs_to_jiffies(delay));
+       ring_dbs(adapter);
+}
+
 /*
  * Processes external (PHY) interrupts in process context.
  */
        INIT_LIST_HEAD(&adapter->adapter_list);
        INIT_WORK(&adapter->ext_intr_handler_task, ext_intr_task);
        INIT_WORK(&adapter->fatal_error_handler_task, fatal_error_task);
+
+       INIT_WORK(&adapter->db_full_task, db_full_task);
+       INIT_WORK(&adapter->db_empty_task, db_empty_task);
+       INIT_WORK(&adapter->db_drop_task, db_drop_task);
+
        INIT_DELAYED_WORK(&adapter->adap_check_task, t3_adap_check_task);
 
        for (i = 0; i < ai->nports0 + ai->nports1; ++i) {
 
        OFFLOAD_STATUS_UP,
        OFFLOAD_STATUS_DOWN,
        OFFLOAD_PORT_DOWN,
-       OFFLOAD_PORT_UP
+       OFFLOAD_PORT_UP,
+       OFFLOAD_DB_FULL,
+       OFFLOAD_DB_EMPTY,
+       OFFLOAD_DB_DROP
 };
 
 struct cxgb3_client {
 
 #define V_LOPIODRBDROPERR(x) ((x) << S_LOPIODRBDROPERR)
 #define F_LOPIODRBDROPERR    V_LOPIODRBDROPERR(1U)
 
+#define S_HIPRIORITYDBFULL    7
+#define V_HIPRIORITYDBFULL(x) ((x) << S_HIPRIORITYDBFULL)
+#define F_HIPRIORITYDBFULL    V_HIPRIORITYDBFULL(1U)
+
+#define S_HIPRIORITYDBEMPTY   6
+#define V_HIPRIORITYDBEMPTY(x) ((x) << S_HIPRIORITYDBEMPTY)
+#define F_HIPRIORITYDBEMPTY    V_HIPRIORITYDBEMPTY(1U)
+
+#define S_LOPRIORITYDBFULL    5
+#define V_LOPRIORITYDBFULL(x) ((x) << S_LOPRIORITYDBFULL)
+#define F_LOPRIORITYDBFULL    V_LOPRIORITYDBFULL(1U)
+
+#define S_LOPRIORITYDBEMPTY   4
+#define V_LOPRIORITYDBEMPTY(x) ((x) << S_LOPRIORITYDBEMPTY)
+#define F_LOPRIORITYDBEMPTY    V_LOPRIORITYDBEMPTY(1U)
+
 #define S_RSPQDISABLED    3
 #define V_RSPQDISABLED(x) ((x) << S_RSPQDISABLED)
 #define F_RSPQDISABLED    V_RSPQDISABLED(1U)
 
 #include "sge_defs.h"
 #include "t3_cpl.h"
 #include "firmware_exports.h"
+#include "cxgb3_offload.h"
 
 #define USE_GTS 0
 
        }
 
        if (status & (F_HIPIODRBDROPERR | F_LOPIODRBDROPERR))
-               CH_ALERT(adapter, "SGE dropped %s priority doorbell\n",
-                        status & F_HIPIODRBDROPERR ? "high" : "lo");
+               queue_work(cxgb3_wq, &adapter->db_drop_task);
+
+       if (status & (F_HIPRIORITYDBFULL | F_LOPRIORITYDBFULL))
+               queue_work(cxgb3_wq, &adapter->db_full_task);
+
+       if (status & (F_HIPRIORITYDBEMPTY | F_LOPRIORITYDBEMPTY))
+               queue_work(cxgb3_wq, &adapter->db_empty_task);
 
        t3_write_reg(adapter, A_SG_INT_CAUSE, status);
        if (status &  SGE_FATALERR)
 
                       F_IRPARITYERROR | V_ITPARITYERROR(M_ITPARITYERROR) | \
                       V_FLPARITYERROR(M_FLPARITYERROR) | F_LODRBPARITYERROR | \
                       F_HIDRBPARITYERROR | F_LORCQPARITYERROR | \
-                      F_HIRCQPARITYERROR)
+                      F_HIRCQPARITYERROR | F_LOPRIORITYDBFULL | \
+                      F_HIPRIORITYDBFULL | F_LOPRIORITYDBEMPTY | \
+                      F_HIPRIORITYDBEMPTY | F_HIPIODRBDROPERR | \
+                      F_LOPIODRBDROPERR)
 #define MC5_INTR_MASK (F_PARITYERR | F_ACTRGNFULL | F_UNKNOWNCMD | \
                       F_REQQPARERR | F_DISPQPARERR | F_DELACTEMPTY | \
                       F_NFASRCHFAIL)