IB/ehca: Use WQE offset instead of WQE addr for pending work reqs
authorHoang-Nam Nguyen <hnguyen@linux.vnet.ibm.com>
Mon, 20 Nov 2006 22:54:12 +0000 (23:54 +0100)
committerRoland Dreier <rolandd@cisco.com>
Wed, 29 Nov 2006 23:33:08 +0000 (15:33 -0800)
This is a patch for ehca to fix a bug in prepare_sqe_to_rts(), which
used WQE address to iterate pending work requests.  This might cause
an access violation since the queue pages can not be assumed to follow
each other consecutively.  Thus, this patch introduces a few queue
functions to determine WQE offset based on its address and uses WQE
offset to iterate the pending work requests.

Signed-off-by: Hoang-Nam Nguyen <hnguyen@de.ibm.com>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
drivers/infiniband/hw/ehca/ehca_main.c
drivers/infiniband/hw/ehca/ehca_qp.c
drivers/infiniband/hw/ehca/ipz_pt_fn.c
drivers/infiniband/hw/ehca/ipz_pt_fn.h

index 01f5aa9cb56d1ba3e58b2667a50dcf4d78787de2..3d1c1c535038c0ce0460ad1f63a87037e401501b 100644 (file)
@@ -52,7 +52,7 @@
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_AUTHOR("Christoph Raisch <raisch@de.ibm.com>");
 MODULE_DESCRIPTION("IBM eServer HCA InfiniBand Device Driver");
-MODULE_VERSION("SVNEHCA_0018");
+MODULE_VERSION("SVNEHCA_0019");
 
 int ehca_open_aqp1     = 0;
 int ehca_debug_level   = 0;
@@ -790,7 +790,7 @@ int __init ehca_module_init(void)
        int ret;
 
        printk(KERN_INFO "eHCA Infiniband Device Driver "
-                        "(Rel.: SVNEHCA_0018)\n");
+                        "(Rel.: SVNEHCA_0019)\n");
        idr_init(&ehca_qp_idr);
        idr_init(&ehca_cq_idr);
        spin_lock_init(&ehca_qp_idr_lock);
index cf3e50ee2d0628c2e0daf477791708479c0c9b75..8682aa50c707bf665f9ac3ce59a95cdeedae4867 100644 (file)
@@ -732,8 +732,7 @@ static int prepare_sqe_rts(struct ehca_qp *my_qp, struct ehca_shca *shca,
        u64 h_ret;
        struct ipz_queue *squeue;
        void *bad_send_wqe_p, *bad_send_wqe_v;
-       void *squeue_start_p, *squeue_end_p;
-       void *squeue_start_v, *squeue_end_v;
+       u64 q_ofs;
        struct ehca_wqe *wqe;
        int qp_num = my_qp->ib_qp.qp_num;
 
@@ -755,26 +754,23 @@ static int prepare_sqe_rts(struct ehca_qp *my_qp, struct ehca_shca *shca,
        if (ehca_debug_level)
                ehca_dmp(bad_send_wqe_v, 32, "qp_num=%x bad_wqe", qp_num);
        squeue = &my_qp->ipz_squeue;
-       squeue_start_p = (void*)virt_to_abs(ipz_qeit_calc(squeue, 0L));
-       squeue_end_p = squeue_start_p+squeue->queue_length;
-       squeue_start_v = abs_to_virt((u64)squeue_start_p);
-       squeue_end_v = abs_to_virt((u64)squeue_end_p);
-       ehca_dbg(&shca->ib_device, "qp_num=%x squeue_start_v=%p squeue_end_v=%p",
-                qp_num, squeue_start_v, squeue_end_v);
+       if (ipz_queue_abs_to_offset(squeue, (u64)bad_send_wqe_p, &q_ofs)) {
+               ehca_err(&shca->ib_device, "failed to get wqe offset qp_num=%x"
+                        " bad_send_wqe_p=%p", qp_num, bad_send_wqe_p);
+               return -EFAULT;
+       }
 
        /* loop sets wqe's purge bit */
-       wqe = (struct ehca_wqe*)bad_send_wqe_v;
+       wqe = (struct ehca_wqe*)ipz_qeit_calc(squeue, q_ofs);
        *bad_wqe_cnt = 0;
        while (wqe->optype != 0xff && wqe->wqef != 0xff) {
                if (ehca_debug_level)
                        ehca_dmp(wqe, 32, "qp_num=%x wqe", qp_num);
                wqe->nr_of_data_seg = 0; /* suppress data access */
                wqe->wqef = WQEF_PURGE; /* WQE to be purged */
-               wqe = (struct ehca_wqe*)((u8*)wqe+squeue->qe_size);
+               q_ofs = ipz_queue_advance_offset(squeue, q_ofs);
+               wqe = (struct ehca_wqe*)ipz_qeit_calc(squeue, q_ofs);
                *bad_wqe_cnt = (*bad_wqe_cnt)+1;
-               if ((void*)wqe >= squeue_end_v) {
-                       wqe = squeue_start_v;
-               }
        }
        /*
         * bad wqe will be reprocessed and ignored when pol_cq() is called,
index e028ff1588ccb3390307a11cda6e1fba435797df..bf7a40088f61ad57f7830cc620306c1867286c11 100644 (file)
@@ -70,6 +70,19 @@ void *ipz_qeit_eq_get_inc(struct ipz_queue *queue)
        return ret;
 }
 
+int ipz_queue_abs_to_offset(struct ipz_queue *queue, u64 addr, u64 *q_offset)
+{
+       int i;
+       for (i = 0; i < queue->queue_length / queue->pagesize; i++) {
+               u64 page = (u64)virt_to_abs(queue->queue_pages[i]);
+               if (addr >= page && addr < page + queue->pagesize) {
+                       *q_offset = addr - page + i * queue->pagesize;
+                       return 0;
+               }
+       }
+       return -EINVAL;
+}
+
 int ipz_queue_ctor(struct ipz_queue *queue,
                   const u32 nr_of_pages,
                   const u32 pagesize, const u32 qe_size, const u32 nr_of_sg)
index 2f13509d5257a1436d3ffa5a0f4892541958a083..dc3bda2634b7dfcbd5fd3db3e791249c1d0cbe22 100644 (file)
@@ -150,6 +150,21 @@ static inline void *ipz_qeit_reset(struct ipz_queue *queue)
        return ipz_qeit_get(queue);
 }
 
+/*
+ * return the q_offset corresponding to an absolute address
+ */
+int ipz_queue_abs_to_offset(struct ipz_queue *queue, u64 addr, u64 *q_offset);
+
+/*
+ * return the next queue offset. don't modify the queue.
+ */
+static inline u64 ipz_queue_advance_offset(struct ipz_queue *queue, u64 offset)
+{
+       offset += queue->qe_size;
+       if (offset >= queue->queue_length) offset = 0;
+       return offset;
+}
+
 /* struct generic page table */
 struct ipz_pt {
        u64 entries[EHCA_PT_ENTRIES];