#include <linux/err.h>
 #include <linux/vmalloc.h>
+#include <linux/jhash.h>
 
 #include "qib.h"
 
                clear_bit(qpn & BITS_PER_PAGE_MASK, map->page);
 }
 
+static inline unsigned qpn_hash(struct qib_ibdev *dev, u32 qpn)
+{
+       return jhash_1word(qpn, dev->qp_rnd) &
+               (dev->qp_table_size - 1);
+}
+
+
 /*
  * Put the QP into the hash table.
  * The hash table holds a reference to the QP.
 static void insert_qp(struct qib_ibdev *dev, struct qib_qp *qp)
 {
        struct qib_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num);
-       unsigned n = qp->ibqp.qp_num % dev->qp_table_size;
        unsigned long flags;
+       unsigned n = qpn_hash(dev, qp->ibqp.qp_num);
 
        spin_lock_irqsave(&dev->qpt_lock, flags);
+       atomic_inc(&qp->refcount);
 
        if (qp->ibqp.qp_num == 0)
-               ibp->qp0 = qp;
+               rcu_assign_pointer(ibp->qp0, qp);
        else if (qp->ibqp.qp_num == 1)
-               ibp->qp1 = qp;
+               rcu_assign_pointer(ibp->qp1, qp);
        else {
                qp->next = dev->qp_table[n];
-               dev->qp_table[n] = qp;
+               rcu_assign_pointer(dev->qp_table[n], qp);
        }
-       atomic_inc(&qp->refcount);
 
        spin_unlock_irqrestore(&dev->qpt_lock, flags);
+       synchronize_rcu();
 }
 
 /*
 static void remove_qp(struct qib_ibdev *dev, struct qib_qp *qp)
 {
        struct qib_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num);
-       struct qib_qp *q, **qpp;
+       unsigned n = qpn_hash(dev, qp->ibqp.qp_num);
        unsigned long flags;
 
-       qpp = &dev->qp_table[qp->ibqp.qp_num % dev->qp_table_size];
-
        spin_lock_irqsave(&dev->qpt_lock, flags);
 
        if (ibp->qp0 == qp) {
-               ibp->qp0 = NULL;
                atomic_dec(&qp->refcount);
+               rcu_assign_pointer(ibp->qp0, NULL);
        } else if (ibp->qp1 == qp) {
-               ibp->qp1 = NULL;
                atomic_dec(&qp->refcount);
-       } else
+               rcu_assign_pointer(ibp->qp1, NULL);
+       } else {
+               struct qib_qp *q, **qpp;
+
+               qpp = &dev->qp_table[n];
                for (; (q = *qpp) != NULL; qpp = &q->next)
                        if (q == qp) {
-                               *qpp = qp->next;
-                               qp->next = NULL;
                                atomic_dec(&qp->refcount);
+                               rcu_assign_pointer(*qpp, qp->next);
+                               qp->next = NULL;
                                break;
                        }
+       }
 
        spin_unlock_irqrestore(&dev->qpt_lock, flags);
+       synchronize_rcu();
 }
 
 /**
 
                if (!qib_mcast_tree_empty(ibp))
                        qp_inuse++;
-               if (ibp->qp0)
+               rcu_read_lock();
+               if (rcu_dereference(ibp->qp0))
                        qp_inuse++;
-               if (ibp->qp1)
+               if (rcu_dereference(ibp->qp1))
                        qp_inuse++;
+               rcu_read_unlock();
        }
 
        spin_lock_irqsave(&dev->qpt_lock, flags);
        for (n = 0; n < dev->qp_table_size; n++) {
                qp = dev->qp_table[n];
-               dev->qp_table[n] = NULL;
+               rcu_assign_pointer(dev->qp_table[n], NULL);
 
                for (; qp; qp = qp->next)
                        qp_inuse++;
        }
        spin_unlock_irqrestore(&dev->qpt_lock, flags);
+       synchronize_rcu();
 
        return qp_inuse;
 }
  */
 struct qib_qp *qib_lookup_qpn(struct qib_ibport *ibp, u32 qpn)
 {
-       struct qib_ibdev *dev = &ppd_from_ibp(ibp)->dd->verbs_dev;
-       unsigned long flags;
-       struct qib_qp *qp;
+       struct qib_qp *qp = NULL;
 
-       spin_lock_irqsave(&dev->qpt_lock, flags);
+       if (unlikely(qpn <= 1)) {
+               rcu_read_lock();
+               if (qpn == 0)
+                       qp = rcu_dereference(ibp->qp0);
+               else
+                       qp = rcu_dereference(ibp->qp1);
+       } else {
+               struct qib_ibdev *dev = &ppd_from_ibp(ibp)->dd->verbs_dev;
+               unsigned n = qpn_hash(dev, qpn);
 
-       if (qpn == 0)
-               qp = ibp->qp0;
-       else if (qpn == 1)
-               qp = ibp->qp1;
-       else
-               for (qp = dev->qp_table[qpn % dev->qp_table_size]; qp;
-                    qp = qp->next)
+               rcu_read_lock();
+               for (qp = dev->qp_table[n]; rcu_dereference(qp); qp = qp->next)
                        if (qp->ibqp.qp_num == qpn)
                                break;
+       }
        if (qp)
-               atomic_inc(&qp->refcount);
+               if (unlikely(!atomic_inc_not_zero(&qp->refcount)))
+                       qp = NULL;
 
-       spin_unlock_irqrestore(&dev->qpt_lock, flags);
+       rcu_read_unlock();
        return qp;
 }
 
                        ret = ERR_PTR(-ENOMEM);
                        goto bail_swq;
                }
+               RCU_INIT_POINTER(qp->next, NULL);
                if (init_attr->srq)
                        sz = 0;
                else {
 
 #include <linux/utsname.h>
 #include <linux/rculist.h>
 #include <linux/mm.h>
+#include <linux/random.h>
 
 #include "qib.h"
 #include "qib_common.h"
 
-static unsigned int ib_qib_qp_table_size = 251;
+static unsigned int ib_qib_qp_table_size = 256;
 module_param_named(qp_table_size, ib_qib_qp_table_size, uint, S_IRUGO);
 MODULE_PARM_DESC(qp_table_size, "QP table size");
 
                if (atomic_dec_return(&mcast->refcount) <= 1)
                        wake_up(&mcast->wait);
        } else {
-               qp = qib_lookup_qpn(ibp, qp_num);
-               if (!qp)
-                       goto drop;
+               if (rcd->lookaside_qp) {
+                       if (rcd->lookaside_qpn != qp_num) {
+                               if (atomic_dec_and_test(
+                                       &rcd->lookaside_qp->refcount))
+                                       wake_up(
+                                        &rcd->lookaside_qp->wait);
+                                       rcd->lookaside_qp = NULL;
+                               }
+               }
+               if (!rcd->lookaside_qp) {
+                       qp = qib_lookup_qpn(ibp, qp_num);
+                       if (!qp)
+                               goto drop;
+                       rcd->lookaside_qp = qp;
+                       rcd->lookaside_qpn = qp_num;
+               } else
+                       qp = rcd->lookaside_qp;
                ibp->n_unicast_rcv++;
                qib_qp_rcv(rcd, hdr, lnh == QIB_LRH_GRH, data, tlen, qp);
-               /*
-                * Notify qib_destroy_qp() if it is waiting
-                * for us to finish.
-                */
-               if (atomic_dec_and_test(&qp->refcount))
-                       wake_up(&qp->wait);
        }
        return;
 
        ibp->z_excessive_buffer_overrun_errors =
                cntrs.excessive_buffer_overrun_errors;
        ibp->z_vl15_dropped = cntrs.vl15_dropped;
+       RCU_INIT_POINTER(ibp->qp0, NULL);
+       RCU_INIT_POINTER(ibp->qp1, NULL);
 }
 
 /**
        int ret;
 
        dev->qp_table_size = ib_qib_qp_table_size;
-       dev->qp_table = kzalloc(dev->qp_table_size * sizeof *dev->qp_table,
+       get_random_bytes(&dev->qp_rnd, sizeof(dev->qp_rnd));
+       dev->qp_table = kmalloc(dev->qp_table_size * sizeof *dev->qp_table,
                                GFP_KERNEL);
        if (!dev->qp_table) {
                ret = -ENOMEM;
                goto err_qpt;
        }
+       for (i = 0; i < dev->qp_table_size; i++)
+               RCU_INIT_POINTER(dev->qp_table[i], NULL);
 
        for (i = 0; i < dd->num_pports; i++)
                init_ibport(ppd + i);