IB/hfi1: Remove duplicate definitions of num_user_pages() function
[platform/kernel/linux-rpi.git] / drivers / infiniband / hw / hfi1 / user_sdma.c
index 0749689..2837407 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2015, 2016 Intel Corporation.
+ * Copyright(c) 2015 - 2017 Intel Corporation.
  *
  * This file is provided under a dual BSD/GPLv2 license.  When using or
  * redistributing this file, you may do so under either license.
@@ -94,43 +94,13 @@ MODULE_PARM_DESC(sdma_comp_size, "Size of User SDMA completion ring. Default: 12
 /* Number of BTH.PSN bits used for sequence number in expected rcvs */
 #define BTH_SEQ_MASK 0x7ffull
 
-/*
- * Define fields in the KDETH header so we can update the header
- * template.
- */
-#define KDETH_OFFSET_SHIFT        0
-#define KDETH_OFFSET_MASK         0x7fff
-#define KDETH_OM_SHIFT            15
-#define KDETH_OM_MASK             0x1
-#define KDETH_TID_SHIFT           16
-#define KDETH_TID_MASK            0x3ff
-#define KDETH_TIDCTRL_SHIFT       26
-#define KDETH_TIDCTRL_MASK        0x3
-#define KDETH_INTR_SHIFT          28
-#define KDETH_INTR_MASK           0x1
-#define KDETH_SH_SHIFT            29
-#define KDETH_SH_MASK             0x1
-#define KDETH_HCRC_UPPER_SHIFT    16
-#define KDETH_HCRC_UPPER_MASK     0xff
-#define KDETH_HCRC_LOWER_SHIFT    24
-#define KDETH_HCRC_LOWER_MASK     0xff
-
 #define AHG_KDETH_INTR_SHIFT 12
 #define AHG_KDETH_SH_SHIFT   13
+#define AHG_KDETH_ARRAY_SIZE  9
 
 #define PBC2LRH(x) ((((x) & 0xfff) << 2) - 4)
 #define LRH2PBC(x) ((((x) >> 2) + 1) & 0xfff)
 
-#define KDETH_GET(val, field)                                          \
-       (((le32_to_cpu((val))) >> KDETH_##field##_SHIFT) & KDETH_##field##_MASK)
-#define KDETH_SET(dw, field, val) do {                                 \
-               u32 dwval = le32_to_cpu(dw);                            \
-               dwval &= ~(KDETH_##field##_MASK << KDETH_##field##_SHIFT); \
-               dwval |= (((val) & KDETH_##field##_MASK) << \
-                         KDETH_##field##_SHIFT);                       \
-               dw = cpu_to_le32(dwval);                                \
-       } while (0)
-
 #define AHG_HEADER_SET(arr, idx, dw, bit, width, value)                        \
        do {                                                            \
                if ((idx) < ARRAY_SIZE((arr)))                          \
@@ -141,22 +111,10 @@ MODULE_PARM_DESC(sdma_comp_size, "Size of User SDMA completion ring. Default: 12
                        return -ERANGE;                                 \
        } while (0)
 
-/* KDETH OM multipliers and switch over point */
-#define KDETH_OM_SMALL     4
-#define KDETH_OM_LARGE     64
-#define KDETH_OM_MAX_SIZE  (1 << ((KDETH_OM_LARGE / KDETH_OM_SMALL) + 1))
-
 /* Tx request flag bits */
 #define TXREQ_FLAGS_REQ_ACK   BIT(0)      /* Set the ACK bit in the header */
 #define TXREQ_FLAGS_REQ_DISABLE_SH BIT(1) /* Disable header suppression */
 
-/* SDMA request flag bits */
-#define SDMA_REQ_FOR_THREAD 1
-#define SDMA_REQ_SEND_DONE  2
-#define SDMA_REQ_HAVE_AHG   3
-#define SDMA_REQ_HAS_ERROR  4
-#define SDMA_REQ_DONE_ERROR 5
-
 #define SDMA_PKT_Q_INACTIVE BIT(0)
 #define SDMA_PKT_Q_ACTIVE   BIT(1)
 #define SDMA_PKT_Q_DEFERRED BIT(2)
@@ -203,25 +161,41 @@ struct evict_data {
 };
 
 struct user_sdma_request {
-       struct sdma_req_info info;
-       struct hfi1_user_sdma_pkt_q *pq;
-       struct hfi1_user_sdma_comp_q *cq;
        /* This is the original header from user space */
        struct hfi1_pkt_header hdr;
+
+       /* Read mostly fields */
+       struct hfi1_user_sdma_pkt_q *pq ____cacheline_aligned_in_smp;
+       struct hfi1_user_sdma_comp_q *cq;
        /*
         * Pointer to the SDMA engine for this request.
         * Since different request could be on different VLs,
         * each request will need it's own engine pointer.
         */
        struct sdma_engine *sde;
-       u8 ahg_idx;
-       u32 ahg[9];
+       struct sdma_req_info info;
+       /* TID array values copied from the tid_iov vector */
+       u32 *tids;
+       /* total length of the data in the request */
+       u32 data_len;
+       /* number of elements copied to the tids array */
+       u16 n_tids;
        /*
-        * KDETH.Offset (Eager) field
-        * We need to remember the initial value so the headers
-        * can be updated properly.
+        * We copy the iovs for this request (based on
+        * info.iovcnt). These are only the data vectors
         */
-       u32 koffset;
+       u8 data_iovs;
+       s8 ahg_idx;
+
+       /* Writeable fields shared with interrupt */
+       u64 seqcomp ____cacheline_aligned_in_smp;
+       u64 seqsubmitted;
+       /* status of the last txreq completed */
+       int status;
+
+       /* Send side fields */
+       struct list_head txps ____cacheline_aligned_in_smp;
+       u64 seqnum;
        /*
         * KDETH.OFFSET (TID) field
         * The offset can cover multiple packets, depending on the
@@ -229,35 +203,21 @@ struct user_sdma_request {
         */
        u32 tidoffset;
        /*
-        * KDETH.OM
-        * Remember this because the header template always sets it
-        * to 0.
-        */
-       u8 omfactor;
-       /*
-        * We copy the iovs for this request (based on
-        * info.iovcnt). These are only the data vectors
+        * KDETH.Offset (Eager) field
+        * We need to remember the initial value so the headers
+        * can be updated properly.
         */
-       unsigned data_iovs;
-       /* total length of the data in the request */
-       u32 data_len;
+       u32 koffset;
+       u32 sent;
+       /* TID index copied from the tid_iov vector */
+       u16 tididx;
        /* progress index moving along the iovs array */
-       unsigned iov_idx;
+       u8 iov_idx;
+       u8 done;
+       u8 has_error;
+
        struct user_sdma_iovec iovs[MAX_VECTORS_PER_REQ];
-       /* number of elements copied to the tids array */
-       u16 n_tids;
-       /* TID array values copied from the tid_iov vector */
-       u32 *tids;
-       u16 tididx;
-       u32 sent;
-       u64 seqnum;
-       u64 seqcomp;
-       u64 seqsubmitted;
-       struct list_head txps;
-       unsigned long flags;
-       /* status of the last txreq completed */
-       int status;
-};
+} ____cacheline_aligned_in_smp;
 
 /*
  * A single txreq could span up to 3 physical pages when the MTU
@@ -284,39 +244,43 @@ struct user_sdma_txreq {
        hfi1_cdbg(SDMA, "[%u:%u:%u] " fmt, (pq)->dd->unit, (pq)->ctxt, \
                 (pq)->subctxt, ##__VA_ARGS__)
 
-static int user_sdma_send_pkts(struct user_sdma_request *, unsigned);
-static int num_user_pages(const struct iovec *);
-static void user_sdma_txreq_cb(struct sdma_txreq *, int);
-static inline void pq_update(struct hfi1_user_sdma_pkt_q *);
-static void user_sdma_free_request(struct user_sdma_request *, bool);
-static int pin_vector_pages(struct user_sdma_request *,
-                           struct user_sdma_iovec *);
-static void unpin_vector_pages(struct mm_struct *, struct page **, unsigned,
-                              unsigned);
-static int check_header_template(struct user_sdma_request *,
-                                struct hfi1_pkt_header *, u32, u32);
-static int set_txreq_header(struct user_sdma_request *,
-                           struct user_sdma_txreq *, u32);
-static int set_txreq_header_ahg(struct user_sdma_request *,
-                               struct user_sdma_txreq *, u32);
-static inline void set_comp_state(struct hfi1_user_sdma_pkt_q *,
-                                 struct hfi1_user_sdma_comp_q *,
-                                 u16, enum hfi1_sdma_comp_state, int);
-static inline u32 set_pkt_bth_psn(__be32, u8, u32);
+static int user_sdma_send_pkts(struct user_sdma_request *req,
+                              unsigned maxpkts);
+static void user_sdma_txreq_cb(struct sdma_txreq *txreq, int status);
+static inline void pq_update(struct hfi1_user_sdma_pkt_q *pq);
+static void user_sdma_free_request(struct user_sdma_request *req, bool unpin);
+static int pin_vector_pages(struct user_sdma_request *req,
+                           struct user_sdma_iovec *iovec);
+static void unpin_vector_pages(struct mm_struct *mm, struct page **pages,
+                              unsigned start, unsigned npages);
+static int check_header_template(struct user_sdma_request *req,
+                                struct hfi1_pkt_header *hdr, u32 lrhlen,
+                                u32 datalen);
+static int set_txreq_header(struct user_sdma_request *req,
+                           struct user_sdma_txreq *tx, u32 datalen);
+static int set_txreq_header_ahg(struct user_sdma_request *req,
+                               struct user_sdma_txreq *tx, u32 len);
+static inline void set_comp_state(struct hfi1_user_sdma_pkt_q *pq,
+                                 struct hfi1_user_sdma_comp_q *cq,
+                                 u16 idx, enum hfi1_sdma_comp_state state,
+                                 int ret);
+static inline u32 set_pkt_bth_psn(__be32 bthpsn, u8 expct, u32 frags);
 static inline u32 get_lrh_len(struct hfi1_pkt_header, u32 len);
 
 static int defer_packet_queue(
-       struct sdma_engine *,
-       struct iowait *,
-       struct sdma_txreq *,
-       unsigned seq);
-static void activate_packet_queue(struct iowait *, int);
-static bool sdma_rb_filter(struct mmu_rb_node *, unsigned long, unsigned long);
-static int sdma_rb_insert(void *, struct mmu_rb_node *);
+       struct sdma_engine *sde,
+       struct iowait *wait,
+       struct sdma_txreq *txreq,
+       uint seq,
+       bool pkts_sent);
+static void activate_packet_queue(struct iowait *wait, int reason);
+static bool sdma_rb_filter(struct mmu_rb_node *node, unsigned long addr,
+                          unsigned long len);
+static int sdma_rb_insert(void *arg, struct mmu_rb_node *mnode);
 static int sdma_rb_evict(void *arg, struct mmu_rb_node *mnode,
                         void *arg2, bool *stop);
-static void sdma_rb_remove(void *, struct mmu_rb_node *);
-static int sdma_rb_invalidate(void *, struct mmu_rb_node *);
+static void sdma_rb_remove(void *arg, struct mmu_rb_node *mnode);
+static int sdma_rb_invalidate(void *arg, struct mmu_rb_node *mnode);
 
 static struct mmu_rb_ops sdma_rb_ops = {
        .filter = sdma_rb_filter,
@@ -330,7 +294,8 @@ static int defer_packet_queue(
        struct sdma_engine *sde,
        struct iowait *wait,
        struct sdma_txreq *txreq,
-       unsigned seq)
+       uint seq,
+       bool pkts_sent)
 {
        struct hfi1_user_sdma_pkt_q *pq =
                container_of(wait, struct hfi1_user_sdma_pkt_q, busy);
@@ -350,7 +315,7 @@ static int defer_packet_queue(
        xchg(&pq->state, SDMA_PKT_Q_DEFERRED);
        write_seqlock(&dev->iowait_lock);
        if (list_empty(&pq->busy.list))
-               list_add_tail(&pq->busy.list, &sde->dmawait);
+               iowait_queue(pkts_sent, &pq->busy, &sde->dmawait);
        write_sequnlock(&dev->iowait_lock);
        return -EBUSY;
 eagain:
@@ -365,54 +330,27 @@ static void activate_packet_queue(struct iowait *wait, int reason)
        wake_up(&wait->wait_dma);
 };
 
-static void sdma_kmem_cache_ctor(void *obj)
+int hfi1_user_sdma_alloc_queues(struct hfi1_ctxtdata *uctxt,
+                               struct hfi1_filedata *fd)
 {
-       struct user_sdma_txreq *tx = obj;
-
-       memset(tx, 0, sizeof(*tx));
-}
-
-int hfi1_user_sdma_alloc_queues(struct hfi1_ctxtdata *uctxt, struct file *fp)
-{
-       struct hfi1_filedata *fd;
-       int ret = 0;
+       int ret = -ENOMEM;
        char buf[64];
        struct hfi1_devdata *dd;
        struct hfi1_user_sdma_comp_q *cq;
        struct hfi1_user_sdma_pkt_q *pq;
-       unsigned long flags;
 
-       if (!uctxt || !fp) {
-               ret = -EBADF;
-               goto done;
-       }
+       if (!uctxt || !fd)
+               return -EBADF;
 
-       fd = fp->private_data;
-
-       if (!hfi1_sdma_comp_ring_size) {
-               ret = -EINVAL;
-               goto done;
-       }
+       if (!hfi1_sdma_comp_ring_size)
+               return -EINVAL;
 
        dd = uctxt->dd;
 
        pq = kzalloc(sizeof(*pq), GFP_KERNEL);
        if (!pq)
-               goto pq_nomem;
-
-       pq->reqs = kcalloc(hfi1_sdma_comp_ring_size,
-                          sizeof(*pq->reqs),
-                          GFP_KERNEL);
-       if (!pq->reqs)
-               goto pq_reqs_nomem;
-
-       pq->req_in_use = kcalloc(BITS_TO_LONGS(hfi1_sdma_comp_ring_size),
-                                sizeof(*pq->req_in_use),
-                                GFP_KERNEL);
-       if (!pq->req_in_use)
-               goto pq_reqs_no_in_use;
+               return -ENOMEM;
 
-       INIT_LIST_HEAD(&pq->list);
        pq->dd = dd;
        pq->ctxt = uctxt->ctxt;
        pq->subctxt = fd->subctxt;
@@ -426,19 +364,32 @@ int hfi1_user_sdma_alloc_queues(struct hfi1_ctxtdata *uctxt, struct file *fp)
        iowait_init(&pq->busy, 0, NULL, defer_packet_queue,
                    activate_packet_queue, NULL);
        pq->reqidx = 0;
+
+       pq->reqs = kcalloc(hfi1_sdma_comp_ring_size,
+                          sizeof(*pq->reqs),
+                          GFP_KERNEL);
+       if (!pq->reqs)
+               goto pq_reqs_nomem;
+
+       pq->req_in_use = kcalloc(BITS_TO_LONGS(hfi1_sdma_comp_ring_size),
+                                sizeof(*pq->req_in_use),
+                                GFP_KERNEL);
+       if (!pq->req_in_use)
+               goto pq_reqs_no_in_use;
+
        snprintf(buf, 64, "txreq-kmem-cache-%u-%u-%u", dd->unit, uctxt->ctxt,
                 fd->subctxt);
        pq->txreq_cache = kmem_cache_create(buf,
-                              sizeof(struct user_sdma_txreq),
+                                           sizeof(struct user_sdma_txreq),
                                            L1_CACHE_BYTES,
                                            SLAB_HWCACHE_ALIGN,
-                                           sdma_kmem_cache_ctor);
+                                           NULL);
        if (!pq->txreq_cache) {
                dd_dev_err(dd, "[%u] Failed to allocate TxReq cache\n",
                           uctxt->ctxt);
                goto pq_txreq_nomem;
        }
-       fd->pq = pq;
+
        cq = kzalloc(sizeof(*cq), GFP_KERNEL);
        if (!cq)
                goto cq_nomem;
@@ -449,20 +400,21 @@ int hfi1_user_sdma_alloc_queues(struct hfi1_ctxtdata *uctxt, struct file *fp)
                goto cq_comps_nomem;
 
        cq->nentries = hfi1_sdma_comp_ring_size;
-       fd->cq = cq;
 
        ret = hfi1_mmu_rb_register(pq, pq->mm, &sdma_rb_ops, dd->pport->hfi1_wq,
                                   &pq->handler);
        if (ret) {
                dd_dev_err(dd, "Failed to register with MMU %d", ret);
-               goto done;
+               goto pq_mmu_fail;
        }
 
-       spin_lock_irqsave(&uctxt->sdma_qlock, flags);
-       list_add(&pq->list, &uctxt->sdma_queues);
-       spin_unlock_irqrestore(&uctxt->sdma_qlock, flags);
-       goto done;
+       fd->pq = pq;
+       fd->cq = cq;
 
+       return 0;
+
+pq_mmu_fail:
+       vfree(cq->comps);
 cq_comps_nomem:
        kfree(cq);
 cq_nomem:
@@ -473,18 +425,14 @@ pq_reqs_no_in_use:
        kfree(pq->reqs);
 pq_reqs_nomem:
        kfree(pq);
-       fd->pq = NULL;
-pq_nomem:
-       ret = -ENOMEM;
-done:
+
        return ret;
 }
 
-int hfi1_user_sdma_free_queues(struct hfi1_filedata *fd)
+int hfi1_user_sdma_free_queues(struct hfi1_filedata *fd,
+                              struct hfi1_ctxtdata *uctxt)
 {
-       struct hfi1_ctxtdata *uctxt = fd->uctxt;
        struct hfi1_user_sdma_pkt_q *pq;
-       unsigned long flags;
 
        hfi1_cdbg(SDMA, "[%u:%u:%u] Freeing user SDMA queues", uctxt->dd->unit,
                  uctxt->ctxt, fd->subctxt);
@@ -492,10 +440,6 @@ int hfi1_user_sdma_free_queues(struct hfi1_filedata *fd)
        if (pq) {
                if (pq->handler)
                        hfi1_mmu_rb_unregister(pq->handler);
-               spin_lock_irqsave(&uctxt->sdma_qlock, flags);
-               if (!list_empty(&pq->list))
-                       list_del_init(&pq->list);
-               spin_unlock_irqrestore(&uctxt->sdma_qlock, flags);
                iowait_sdma_drain(&pq->busy);
                /* Wait until all requests have been freed. */
                wait_event_interruptible(
@@ -536,11 +480,11 @@ static u8 dlid_to_selector(u16 dlid)
        return mapping[hash];
 }
 
-int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
-                                  unsigned long dim, unsigned long *count)
+int hfi1_user_sdma_process_request(struct hfi1_filedata *fd,
+                                  struct iovec *iovec, unsigned long dim,
+                                  unsigned long *count)
 {
        int ret = 0, i;
-       struct hfi1_filedata *fd = fp->private_data;
        struct hfi1_ctxtdata *uctxt = fd->uctxt;
        struct hfi1_user_sdma_pkt_q *pq = fd->pq;
        struct hfi1_user_sdma_comp_q *cq = fd->cq;
@@ -550,6 +494,8 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
        struct sdma_req_info info;
        struct user_sdma_request *req;
        u8 opcode, sc, vl;
+       u16 pkey;
+       u32 slid;
        int req_queued = 0;
        u16 dlid;
        u32 selector;
@@ -611,11 +557,20 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
        hfi1_cdbg(SDMA, "[%u:%u:%u] Using req/comp entry %u\n", dd->unit,
                  uctxt->ctxt, fd->subctxt, info.comp_idx);
        req = pq->reqs + info.comp_idx;
-       memset(req, 0, sizeof(*req));
        req->data_iovs = req_iovcnt(info.ctrl) - 1; /* subtract header vector */
+       req->data_len  = 0;
        req->pq = pq;
        req->cq = cq;
        req->status = -1;
+       req->ahg_idx = -1;
+       req->iov_idx = 0;
+       req->sent = 0;
+       req->seqnum = 0;
+       req->seqcomp = 0;
+       req->seqsubmitted = 0;
+       req->tids = NULL;
+       req->done = 0;
+       req->has_error = 0;
        INIT_LIST_HEAD(&req->txps);
 
        memcpy(&req->info, &info, sizeof(info));
@@ -674,8 +629,9 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
        }
 
        /* Checking P_KEY for requests from user-space */
-       if (egress_pkey_check(dd->pport, req->hdr.lrh, req->hdr.bth, sc,
-                             PKEY_CHECK_INVALID)) {
+       pkey = (u16)be32_to_cpu(req->hdr.bth[0]);
+       slid = be16_to_cpu(req->hdr.lrh[3]);
+       if (egress_pkey_check(dd->pport, slid, pkey, sc, PKEY_CHECK_INVALID)) {
                ret = -EINVAL;
                goto free_req;
        }
@@ -704,12 +660,14 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
 
        /* Save all the IO vector structures */
        for (i = 0; i < req->data_iovs; i++) {
+               req->iovs[i].offset = 0;
                INIT_LIST_HEAD(&req->iovs[i].list);
                memcpy(&req->iovs[i].iov,
                       iovec + idx++,
                       sizeof(req->iovs[i].iov));
                ret = pin_vector_pages(req, &req->iovs[i]);
                if (ret) {
+                       req->data_iovs = i;
                        req->status = ret;
                        goto free_req;
                }
@@ -752,6 +710,7 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
                }
                req->tids = tmp;
                req->n_tids = ntids;
+               req->tididx = 0;
                idx++;
        }
 
@@ -766,14 +725,8 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
        }
 
        /* We don't need an AHG entry if the request contains only one packet */
-       if (req->info.npkts > 1 && HFI1_CAP_IS_USET(SDMA_AHG)) {
-               int ahg = sdma_ahg_alloc(req->sde);
-
-               if (likely(ahg >= 0)) {
-                       req->ahg_idx = (u8)ahg;
-                       set_bit(SDMA_REQ_HAVE_AHG, &req->flags);
-               }
-       }
+       if (req->info.npkts > 1 && HFI1_CAP_IS_USET(SDMA_AHG))
+               req->ahg_idx = sdma_ahg_alloc(req->sde);
 
        set_comp_state(pq, cq, info.comp_idx, QUEUED, 0);
        atomic_inc(&pq->n_reqs);
@@ -800,12 +753,12 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
         * request have been submitted to the SDMA engine. However, it
         * will not wait for send completions.
         */
-       while (!test_bit(SDMA_REQ_SEND_DONE, &req->flags)) {
+       while (req->seqsubmitted != req->info.npkts) {
                ret = user_sdma_send_pkts(req, pcount);
                if (ret < 0) {
                        if (ret != -EBUSY) {
                                req->status = ret;
-                               set_bit(SDMA_REQ_DONE_ERROR, &req->flags);
+                               WRITE_ONCE(req->has_error, 1);
                                if (ACCESS_ONCE(req->seqcomp) ==
                                    req->seqsubmitted - 1)
                                        goto free_req;
@@ -893,6 +846,84 @@ static inline u32 get_lrh_len(struct hfi1_pkt_header hdr, u32 len)
        return ((sizeof(hdr) - sizeof(hdr.pbc)) + 4 + len);
 }
 
+static int user_sdma_txadd_ahg(struct user_sdma_request *req,
+                              struct user_sdma_txreq *tx,
+                              u32 datalen)
+{
+       int ret;
+       u16 pbclen = le16_to_cpu(req->hdr.pbc[0]);
+       u32 lrhlen = get_lrh_len(req->hdr, pad_len(datalen));
+       struct hfi1_user_sdma_pkt_q *pq = req->pq;
+
+       /*
+        * Copy the request header into the tx header
+        * because the HW needs a cacheline-aligned
+        * address.
+        * This copy can be optimized out if the hdr
+        * member of user_sdma_request were also
+        * cacheline aligned.
+        */
+       memcpy(&tx->hdr, &req->hdr, sizeof(tx->hdr));
+       if (PBC2LRH(pbclen) != lrhlen) {
+               pbclen = (pbclen & 0xf000) | LRH2PBC(lrhlen);
+               tx->hdr.pbc[0] = cpu_to_le16(pbclen);
+       }
+       ret = check_header_template(req, &tx->hdr, lrhlen, datalen);
+       if (ret)
+               return ret;
+       ret = sdma_txinit_ahg(&tx->txreq, SDMA_TXREQ_F_AHG_COPY,
+                             sizeof(tx->hdr) + datalen, req->ahg_idx,
+                             0, NULL, 0, user_sdma_txreq_cb);
+       if (ret)
+               return ret;
+       ret = sdma_txadd_kvaddr(pq->dd, &tx->txreq, &tx->hdr, sizeof(tx->hdr));
+       if (ret)
+               sdma_txclean(pq->dd, &tx->txreq);
+       return ret;
+}
+
+static int user_sdma_txadd(struct user_sdma_request *req,
+                          struct user_sdma_txreq *tx,
+                          struct user_sdma_iovec *iovec, u32 datalen,
+                          u32 *queued_ptr, u32 *data_sent_ptr,
+                          u64 *iov_offset_ptr)
+{
+       int ret;
+       unsigned int pageidx, len;
+       unsigned long base, offset;
+       u64 iov_offset = *iov_offset_ptr;
+       u32 queued = *queued_ptr, data_sent = *data_sent_ptr;
+       struct hfi1_user_sdma_pkt_q *pq = req->pq;
+
+       base = (unsigned long)iovec->iov.iov_base;
+       offset = offset_in_page(base + iovec->offset + iov_offset);
+       pageidx = (((iovec->offset + iov_offset + base) - (base & PAGE_MASK)) >>
+                  PAGE_SHIFT);
+       len = offset + req->info.fragsize > PAGE_SIZE ?
+               PAGE_SIZE - offset : req->info.fragsize;
+       len = min((datalen - queued), len);
+       ret = sdma_txadd_page(pq->dd, &tx->txreq, iovec->pages[pageidx],
+                             offset, len);
+       if (ret) {
+               SDMA_DBG(req, "SDMA txreq add page failed %d\n", ret);
+               return ret;
+       }
+       iov_offset += len;
+       queued += len;
+       data_sent += len;
+       if (unlikely(queued < datalen && pageidx == iovec->npages &&
+                    req->iov_idx < req->data_iovs - 1)) {
+               iovec->offset += iov_offset;
+               iovec = &req->iovs[++req->iov_idx];
+               iov_offset = 0;
+       }
+
+       *queued_ptr = queued;
+       *data_sent_ptr = data_sent;
+       *iov_offset_ptr = iov_offset;
+       return ret;
+}
+
 static int user_sdma_send_pkts(struct user_sdma_request *req, unsigned maxpkts)
 {
        int ret = 0, count;
@@ -907,10 +938,8 @@ static int user_sdma_send_pkts(struct user_sdma_request *req, unsigned maxpkts)
        pq = req->pq;
 
        /* If tx completion has reported an error, we are done. */
-       if (test_bit(SDMA_REQ_HAS_ERROR, &req->flags)) {
-               set_bit(SDMA_REQ_DONE_ERROR, &req->flags);
+       if (READ_ONCE(req->has_error))
                return -EFAULT;
-       }
 
        /*
         * Check if we might have sent the entire request already
@@ -933,10 +962,8 @@ static int user_sdma_send_pkts(struct user_sdma_request *req, unsigned maxpkts)
                 * with errors. If so, we are not going to process any
                 * more packets from this request.
                 */
-               if (test_bit(SDMA_REQ_HAS_ERROR, &req->flags)) {
-                       set_bit(SDMA_REQ_DONE_ERROR, &req->flags);
+               if (READ_ONCE(req->has_error))
                        return -EFAULT;
-               }
 
                tx = kmem_cache_alloc(pq->txreq_cache, GFP_KERNEL);
                if (!tx)
@@ -991,41 +1018,11 @@ static int user_sdma_send_pkts(struct user_sdma_request *req, unsigned maxpkts)
                        }
                }
 
-               if (test_bit(SDMA_REQ_HAVE_AHG, &req->flags)) {
+               if (req->ahg_idx >= 0) {
                        if (!req->seqnum) {
-                               u16 pbclen = le16_to_cpu(req->hdr.pbc[0]);
-                               u32 lrhlen = get_lrh_len(req->hdr,
-                                                        pad_len(datalen));
-                               /*
-                                * Copy the request header into the tx header
-                                * because the HW needs a cacheline-aligned
-                                * address.
-                                * This copy can be optimized out if the hdr
-                                * member of user_sdma_request were also
-                                * cacheline aligned.
-                                */
-                               memcpy(&tx->hdr, &req->hdr, sizeof(tx->hdr));
-                               if (PBC2LRH(pbclen) != lrhlen) {
-                                       pbclen = (pbclen & 0xf000) |
-                                               LRH2PBC(lrhlen);
-                                       tx->hdr.pbc[0] = cpu_to_le16(pbclen);
-                               }
-                               ret = check_header_template(req, &tx->hdr,
-                                                           lrhlen, datalen);
+                               ret = user_sdma_txadd_ahg(req, tx, datalen);
                                if (ret)
                                        goto free_tx;
-                               ret = sdma_txinit_ahg(&tx->txreq,
-                                                     SDMA_TXREQ_F_AHG_COPY,
-                                                     sizeof(tx->hdr) + datalen,
-                                                     req->ahg_idx, 0, NULL, 0,
-                                                     user_sdma_txreq_cb);
-                               if (ret)
-                                       goto free_tx;
-                               ret = sdma_txadd_kvaddr(pq->dd, &tx->txreq,
-                                                       &tx->hdr,
-                                                       sizeof(tx->hdr));
-                               if (ret)
-                                       goto free_txreq;
                        } else {
                                int changes;
 
@@ -1033,11 +1030,6 @@ static int user_sdma_send_pkts(struct user_sdma_request *req, unsigned maxpkts)
                                                               datalen);
                                if (changes < 0)
                                        goto free_tx;
-                               sdma_txinit_ahg(&tx->txreq,
-                                               SDMA_TXREQ_F_USE_AHG,
-                                               datalen, req->ahg_idx, changes,
-                                               req->ahg, sizeof(req->hdr),
-                                               user_sdma_txreq_cb);
                        }
                } else {
                        ret = sdma_txinit(&tx->txreq, 0, sizeof(req->hdr) +
@@ -1061,35 +1053,10 @@ static int user_sdma_send_pkts(struct user_sdma_request *req, unsigned maxpkts)
                 */
                while (queued < datalen &&
                       (req->sent + data_sent) < req->data_len) {
-                       unsigned long base, offset;
-                       unsigned pageidx, len;
-
-                       base = (unsigned long)iovec->iov.iov_base;
-                       offset = offset_in_page(base + iovec->offset +
-                                               iov_offset);
-                       pageidx = (((iovec->offset + iov_offset +
-                                    base) - (base & PAGE_MASK)) >> PAGE_SHIFT);
-                       len = offset + req->info.fragsize > PAGE_SIZE ?
-                               PAGE_SIZE - offset : req->info.fragsize;
-                       len = min((datalen - queued), len);
-                       ret = sdma_txadd_page(pq->dd, &tx->txreq,
-                                             iovec->pages[pageidx],
-                                             offset, len);
-                       if (ret) {
-                               SDMA_DBG(req, "SDMA txreq add page failed %d\n",
-                                        ret);
+                       ret = user_sdma_txadd(req, tx, iovec, datalen,
+                                             &queued, &data_sent, &iov_offset);
+                       if (ret)
                                goto free_txreq;
-                       }
-                       iov_offset += len;
-                       queued += len;
-                       data_sent += len;
-                       if (unlikely(queued < datalen &&
-                                    pageidx == iovec->npages &&
-                                    req->iov_idx < req->data_iovs - 1)) {
-                               iovec->offset += iov_offset;
-                               iovec = &req->iovs[++req->iov_idx];
-                               iov_offset = 0;
-                       }
                }
                /*
                 * The txreq was submitted successfully so we can update
@@ -1114,14 +1081,14 @@ dosend:
        ret = sdma_send_txlist(req->sde, &pq->busy, &req->txps, &count);
        req->seqsubmitted += count;
        if (req->seqsubmitted == req->info.npkts) {
-               set_bit(SDMA_REQ_SEND_DONE, &req->flags);
+               WRITE_ONCE(req->done, 1);
                /*
                 * The txreq has already been submitted to the HW queue
                 * so we can free the AHG entry now. Corruption will not
                 * happen due to the sequential manner in which
                 * descriptors are processed.
                 */
-               if (test_bit(SDMA_REQ_HAVE_AHG, &req->flags))
+               if (req->ahg_idx >= 0)
                        sdma_ahg_free(req->sde, req->ahg_idx);
        }
        return ret;
@@ -1133,19 +1100,6 @@ free_tx:
        return ret;
 }
 
-/*
- * How many pages in this iovec element?
- */
-static inline int num_user_pages(const struct iovec *iov)
-{
-       const unsigned long addr  = (unsigned long)iov->iov_base;
-       const unsigned long len   = iov->iov_len;
-       const unsigned long spage = addr & PAGE_MASK;
-       const unsigned long epage = (addr + len - 1) & PAGE_MASK;
-
-       return 1 + ((epage - spage) >> PAGE_SHIFT);
-}
-
 static u32 sdma_cache_evict(struct hfi1_user_sdma_pkt_q *pq, u32 npages)
 {
        struct evict_data evict_data;
@@ -1156,22 +1110,82 @@ static u32 sdma_cache_evict(struct hfi1_user_sdma_pkt_q *pq, u32 npages)
        return evict_data.cleared;
 }
 
+static int pin_sdma_pages(struct user_sdma_request *req,
+                         struct user_sdma_iovec *iovec,
+                         struct sdma_mmu_node *node,
+                         int npages)
+{
+       int pinned, cleared;
+       struct page **pages;
+       struct hfi1_user_sdma_pkt_q *pq = req->pq;
+
+       pages = kcalloc(npages, sizeof(*pages), GFP_KERNEL);
+       if (!pages) {
+               SDMA_DBG(req, "Failed page array alloc");
+               return -ENOMEM;
+       }
+       memcpy(pages, node->pages, node->npages * sizeof(*pages));
+
+       npages -= node->npages;
+retry:
+       if (!hfi1_can_pin_pages(pq->dd, pq->mm,
+                               atomic_read(&pq->n_locked), npages)) {
+               cleared = sdma_cache_evict(pq, npages);
+               if (cleared >= npages)
+                       goto retry;
+       }
+       pinned = hfi1_acquire_user_pages(pq->mm,
+                                        ((unsigned long)iovec->iov.iov_base +
+                                        (node->npages * PAGE_SIZE)), npages, 0,
+                                        pages + node->npages);
+       if (pinned < 0) {
+               kfree(pages);
+               return pinned;
+       }
+       if (pinned != npages) {
+               unpin_vector_pages(pq->mm, pages, node->npages, pinned);
+               return -EFAULT;
+       }
+       kfree(node->pages);
+       node->rb.len = iovec->iov.iov_len;
+       node->pages = pages;
+       atomic_add(pinned, &pq->n_locked);
+       return pinned;
+}
+
+static void unpin_sdma_pages(struct sdma_mmu_node *node)
+{
+       if (node->npages) {
+               unpin_vector_pages(node->pq->mm, node->pages, 0, node->npages);
+               atomic_sub(node->npages, &node->pq->n_locked);
+       }
+}
+
 static int pin_vector_pages(struct user_sdma_request *req,
                            struct user_sdma_iovec *iovec)
 {
-       int ret = 0, pinned, npages, cleared;
-       struct page **pages;
+       int ret = 0, pinned, npages;
        struct hfi1_user_sdma_pkt_q *pq = req->pq;
        struct sdma_mmu_node *node = NULL;
        struct mmu_rb_node *rb_node;
-
-       rb_node = hfi1_mmu_rb_extract(pq->handler,
-                                     (unsigned long)iovec->iov.iov_base,
-                                     iovec->iov.iov_len);
-       if (rb_node)
+       struct iovec *iov;
+       bool extracted;
+
+       extracted =
+               hfi1_mmu_rb_remove_unless_exact(pq->handler,
+                                               (unsigned long)
+                                               iovec->iov.iov_base,
+                                               iovec->iov.iov_len, &rb_node);
+       if (rb_node) {
                node = container_of(rb_node, struct sdma_mmu_node, rb);
-       else
-               rb_node = NULL;
+               if (!extracted) {
+                       atomic_inc(&node->refcount);
+                       iovec->pages = node->pages;
+                       iovec->npages = node->npages;
+                       iovec->node = node;
+                       return 0;
+               }
+       }
 
        if (!node) {
                node = kzalloc(sizeof(*node), GFP_KERNEL);
@@ -1183,46 +1197,16 @@ static int pin_vector_pages(struct user_sdma_request *req,
                atomic_set(&node->refcount, 0);
        }
 
-       npages = num_user_pages(&iovec->iov);
+       iov = &iovec->iov;
+       npages = num_user_pages((unsigned long)iov->iov_base, iov->iov_len);
        if (node->npages < npages) {
-               pages = kcalloc(npages, sizeof(*pages), GFP_KERNEL);
-               if (!pages) {
-                       SDMA_DBG(req, "Failed page array alloc");
-                       ret = -ENOMEM;
-                       goto bail;
-               }
-               memcpy(pages, node->pages, node->npages * sizeof(*pages));
-
-               npages -= node->npages;
-
-retry:
-               if (!hfi1_can_pin_pages(pq->dd, pq->mm,
-                                       atomic_read(&pq->n_locked), npages)) {
-                       cleared = sdma_cache_evict(pq, npages);
-                       if (cleared >= npages)
-                               goto retry;
-               }
-               pinned = hfi1_acquire_user_pages(pq->mm,
-                       ((unsigned long)iovec->iov.iov_base +
-                        (node->npages * PAGE_SIZE)), npages, 0,
-                       pages + node->npages);
+               pinned = pin_sdma_pages(req, iovec, node, npages);
                if (pinned < 0) {
-                       kfree(pages);
                        ret = pinned;
                        goto bail;
                }
-               if (pinned != npages) {
-                       unpin_vector_pages(pq->mm, pages, node->npages,
-                                          pinned);
-                       ret = -EFAULT;
-                       goto bail;
-               }
-               kfree(node->pages);
-               node->rb.len = iovec->iov.iov_len;
-               node->pages = pages;
                node->npages += pinned;
                npages = node->npages;
-               atomic_add(pinned, &pq->n_locked);
        }
        iovec->pages = node->pages;
        iovec->npages = npages;
@@ -1230,14 +1214,12 @@ retry:
 
        ret = hfi1_mmu_rb_insert(req->pq->handler, &node->rb);
        if (ret) {
-               atomic_sub(node->npages, &pq->n_locked);
                iovec->node = NULL;
                goto bail;
        }
        return 0;
 bail:
-       if (rb_node)
-               unpin_vector_pages(pq->mm, node->pages, 0, node->npages);
+       unpin_sdma_pages(node);
        kfree(node);
        return ret;
 }
@@ -1323,6 +1305,7 @@ static int set_txreq_header(struct user_sdma_request *req,
 {
        struct hfi1_user_sdma_pkt_q *pq = req->pq;
        struct hfi1_pkt_header *hdr = &tx->hdr;
+       u8 omfactor; /* KDETH.OM */
        u16 pbclen;
        int ret;
        u32 tidval = 0, lrhlen = get_lrh_len(*hdr, pad_len(datalen));
@@ -1400,8 +1383,9 @@ static int set_txreq_header(struct user_sdma_request *req,
                        }
                        tidval = req->tids[req->tididx];
                }
-               req->omfactor = EXP_TID_GET(tidval, LEN) * PAGE_SIZE >=
-                       KDETH_OM_MAX_SIZE ? KDETH_OM_LARGE : KDETH_OM_SMALL;
+               omfactor = EXP_TID_GET(tidval, LEN) * PAGE_SIZE >=
+                       KDETH_OM_MAX_SIZE ? KDETH_OM_LARGE_SHIFT :
+                       KDETH_OM_SMALL_SHIFT;
                /* Set KDETH.TIDCtrl based on value for this TID. */
                KDETH_SET(hdr->kdeth.ver_tid_offset, TIDCTRL,
                          EXP_TID_GET(tidval, CTRL));
@@ -1416,12 +1400,12 @@ static int set_txreq_header(struct user_sdma_request *req,
                 * transfer.
                 */
                SDMA_DBG(req, "TID offset %ubytes %uunits om%u",
-                        req->tidoffset, req->tidoffset / req->omfactor,
-                        req->omfactor != KDETH_OM_SMALL);
+                        req->tidoffset, req->tidoffset >> omfactor,
+                        omfactor != KDETH_OM_SMALL_SHIFT);
                KDETH_SET(hdr->kdeth.ver_tid_offset, OFFSET,
-                         req->tidoffset / req->omfactor);
+                         req->tidoffset >> omfactor);
                KDETH_SET(hdr->kdeth.ver_tid_offset, OM,
-                         req->omfactor != KDETH_OM_SMALL);
+                         omfactor != KDETH_OM_SMALL_SHIFT);
        }
 done:
        trace_hfi1_sdma_user_header(pq->dd, pq->ctxt, pq->subctxt,
@@ -1430,20 +1414,22 @@ done:
 }
 
 static int set_txreq_header_ahg(struct user_sdma_request *req,
-                               struct user_sdma_txreq *tx, u32 len)
+                               struct user_sdma_txreq *tx, u32 datalen)
 {
+       u32 ahg[AHG_KDETH_ARRAY_SIZE];
        int diff = 0;
+       u8 omfactor; /* KDETH.OM */
        struct hfi1_user_sdma_pkt_q *pq = req->pq;
        struct hfi1_pkt_header *hdr = &req->hdr;
        u16 pbclen = le16_to_cpu(hdr->pbc[0]);
-       u32 val32, tidval = 0, lrhlen = get_lrh_len(*hdr, pad_len(len));
+       u32 val32, tidval = 0, lrhlen = get_lrh_len(*hdr, pad_len(datalen));
 
        if (PBC2LRH(pbclen) != lrhlen) {
                /* PBC.PbcLengthDWs */
-               AHG_HEADER_SET(req->ahg, diff, 0, 0, 12,
+               AHG_HEADER_SET(ahg, diff, 0, 0, 12,
                               cpu_to_le16(LRH2PBC(lrhlen)));
                /* LRH.PktLen (we need the full 16 bits due to byte swap) */
-               AHG_HEADER_SET(req->ahg, diff, 3, 0, 16,
+               AHG_HEADER_SET(ahg, diff, 3, 0, 16,
                               cpu_to_be16(lrhlen >> 2));
        }
 
@@ -1455,13 +1441,12 @@ static int set_txreq_header_ahg(struct user_sdma_request *req,
                (HFI1_CAP_IS_KSET(EXTENDED_PSN) ? 0x7fffffff : 0xffffff);
        if (unlikely(tx->flags & TXREQ_FLAGS_REQ_ACK))
                val32 |= 1UL << 31;
-       AHG_HEADER_SET(req->ahg, diff, 6, 0, 16, cpu_to_be16(val32 >> 16));
-       AHG_HEADER_SET(req->ahg, diff, 6, 16, 16, cpu_to_be16(val32 & 0xffff));
+       AHG_HEADER_SET(ahg, diff, 6, 0, 16, cpu_to_be16(val32 >> 16));
+       AHG_HEADER_SET(ahg, diff, 6, 16, 16, cpu_to_be16(val32 & 0xffff));
        /* KDETH.Offset */
-       AHG_HEADER_SET(req->ahg, diff, 15, 0, 16,
+       AHG_HEADER_SET(ahg, diff, 15, 0, 16,
                       cpu_to_le16(req->koffset & 0xffff));
-       AHG_HEADER_SET(req->ahg, diff, 15, 16, 16,
-                      cpu_to_le16(req->koffset >> 16));
+       AHG_HEADER_SET(ahg, diff, 15, 16, 16, cpu_to_le16(req->koffset >> 16));
        if (req_opcode(req->info.ctrl) == EXPECTED) {
                __le16 val;
 
@@ -1479,19 +1464,19 @@ static int set_txreq_header_ahg(struct user_sdma_request *req,
                         * we have to check again.
                         */
                        if (++req->tididx > req->n_tids - 1 ||
-                           !req->tids[req->tididx]) {
+                           !req->tids[req->tididx])
                                return -EINVAL;
-                       }
                        tidval = req->tids[req->tididx];
                }
-               req->omfactor = ((EXP_TID_GET(tidval, LEN) *
+               omfactor = ((EXP_TID_GET(tidval, LEN) *
                                  PAGE_SIZE) >=
-                                KDETH_OM_MAX_SIZE) ? KDETH_OM_LARGE :
-                       KDETH_OM_SMALL;
+                                KDETH_OM_MAX_SIZE) ? KDETH_OM_LARGE_SHIFT :
+                                KDETH_OM_SMALL_SHIFT;
                /* KDETH.OM and KDETH.OFFSET (TID) */
-               AHG_HEADER_SET(req->ahg, diff, 7, 0, 16,
-                              ((!!(req->omfactor - KDETH_OM_SMALL)) << 15 |
-                               ((req->tidoffset / req->omfactor) & 0x7fff)));
+               AHG_HEADER_SET(ahg, diff, 7, 0, 16,
+                              ((!!(omfactor - KDETH_OM_SMALL_SHIFT)) << 15 |
+                               ((req->tidoffset >> omfactor)
+                                & 0x7fff)));
                /* KDETH.TIDCtrl, KDETH.TID, KDETH.Intr, KDETH.SH */
                val = cpu_to_le16(((EXP_TID_GET(tidval, CTRL) & 0x3) << 10) |
                                   (EXP_TID_GET(tidval, IDX) & 0x3ff));
@@ -1508,12 +1493,20 @@ static int set_txreq_header_ahg(struct user_sdma_request *req,
                                             AHG_KDETH_INTR_SHIFT));
                }
 
-               AHG_HEADER_SET(req->ahg, diff, 7, 16, 14, val);
+               AHG_HEADER_SET(ahg, diff, 7, 16, 14, val);
        }
+       if (diff < 0)
+               return diff;
 
        trace_hfi1_sdma_user_header_ahg(pq->dd, pq->ctxt, pq->subctxt,
                                        req->info.comp_idx, req->sde->this_idx,
-                                       req->ahg_idx, req->ahg, diff, tidval);
+                                       req->ahg_idx, ahg, diff, tidval);
+       sdma_txinit_ahg(&tx->txreq,
+                       SDMA_TXREQ_F_USE_AHG,
+                       datalen, req->ahg_idx, diff,
+                       ahg, sizeof(req->hdr),
+                       user_sdma_txreq_cb);
+
        return diff;
 }
 
@@ -1542,7 +1535,7 @@ static void user_sdma_txreq_cb(struct sdma_txreq *txreq, int status)
        if (status != SDMA_TXREQ_S_OK) {
                SDMA_DBG(req, "SDMA completion with error %d",
                         status);
-               set_bit(SDMA_REQ_HAS_ERROR, &req->flags);
+               WRITE_ONCE(req->has_error, 1);
        }
 
        req->seqcomp = tx->seqnum;
@@ -1561,8 +1554,8 @@ static void user_sdma_txreq_cb(struct sdma_txreq *txreq, int status)
                if (status != SDMA_TXREQ_S_OK)
                        req->status = status;
                if (req->seqcomp == (ACCESS_ONCE(req->seqsubmitted) - 1) &&
-                   (test_bit(SDMA_REQ_SEND_DONE, &req->flags) ||
-                    test_bit(SDMA_REQ_DONE_ERROR, &req->flags))) {
+                   (READ_ONCE(req->done) ||
+                    READ_ONCE(req->has_error))) {
                        user_sdma_free_request(req, false);
                        pq_update(pq);
                        set_comp_state(pq, cq, idx, ERROR, req->status);
@@ -1672,10 +1665,7 @@ static void sdma_rb_remove(void *arg, struct mmu_rb_node *mnode)
        struct sdma_mmu_node *node =
                container_of(mnode, struct sdma_mmu_node, rb);
 
-       atomic_sub(node->npages, &node->pq->n_locked);
-
-       unpin_vector_pages(node->pq->mm, node->pages, 0, node->npages);
-
+       unpin_sdma_pages(node);
        kfree(node);
 }