staging/rdma/hfi1: Improve performance of user SDMA
authorMitko Haralanov <mitko.haralanov@intel.com>
Wed, 3 Feb 2016 22:37:06 +0000 (14:37 -0800)
committerDoug Ledford <dledford@redhat.com>
Fri, 11 Mar 2016 01:37:59 +0000 (20:37 -0500)
To facilitate locked page counting, the user SDMA
routines would maintain a list of io vectors, which
were freed in the completion callback and then unpin
the associated pages during the next call into the
kernel.

Since the size of this list was unbounded, doing this
was bad for performance because the driver ended up
spending too much time freeing the io vectors.

This commit changes how the io vector freeing is done
by moving the actual page unpinning in the callback and
maintaining a count of unpinned pages. This count can
then be used during the next call into the kernel to
update the mm->pinned_vm variable (since that requires
process context and the ability to sleep.)

Reviewed-by: Mike Marciniszyn <mike.marciniszyn@intel.com>
Signed-off-by: Mitko Haralanov <mitko.haralanov@intel.com>
Signed-off-by: Jubin John <jubin.john@intel.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
drivers/staging/rdma/hfi1/user_sdma.c
drivers/staging/rdma/hfi1/user_sdma.h

index 0c32eaf25afcaf9f102fda93f089ee87fe9dafe9..55c7e6a4eb1a10c5309030e28f5a5d79d70ca73f 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2015 Intel Corporation.
+ * Copyright(c) 2015, 2016 Intel Corporation.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -18,7 +18,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2015 Intel Corporation.
+ * Copyright(c) 2015, 2016 Intel Corporation.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -236,8 +236,6 @@ struct user_sdma_request {
        u64 seqcomp;
        u64 seqsubmitted;
        struct list_head txps;
-       spinlock_t txcmp_lock;  /* protect txcmp list */
-       struct list_head txcmp;
        unsigned long flags;
        /* status of the last txreq completed */
        int status;
@@ -381,14 +379,12 @@ int hfi1_user_sdma_alloc_queues(struct hfi1_ctxtdata *uctxt, struct file *fp)
                goto pq_reqs_nomem;
 
        INIT_LIST_HEAD(&pq->list);
-       INIT_LIST_HEAD(&pq->iovec_list);
        pq->dd = dd;
        pq->ctxt = uctxt->ctxt;
        pq->subctxt = fd->subctxt;
        pq->n_max_reqs = hfi1_sdma_comp_ring_size;
        pq->state = SDMA_PKT_Q_INACTIVE;
        atomic_set(&pq->n_reqs, 0);
-       spin_lock_init(&pq->iovec_lock);
        init_waitqueue_head(&pq->wait);
 
        iowait_init(&pq->busy, 0, NULL, defer_packet_queue,
@@ -444,7 +440,6 @@ int hfi1_user_sdma_free_queues(struct hfi1_filedata *fd)
 {
        struct hfi1_ctxtdata *uctxt = fd->uctxt;
        struct hfi1_user_sdma_pkt_q *pq;
-       struct user_sdma_iovec *iov;
        unsigned long flags;
 
        hfi1_cdbg(SDMA, "[%u:%u:%u] Freeing user SDMA queues", uctxt->dd->unit,
@@ -460,15 +455,6 @@ int hfi1_user_sdma_free_queues(struct hfi1_filedata *fd)
                wait_event_interruptible(
                        pq->wait,
                        (ACCESS_ONCE(pq->state) == SDMA_PKT_Q_INACTIVE));
-               /* Unpin any left over buffers. */
-               while (!list_empty(&pq->iovec_list)) {
-                       spin_lock_irqsave(&pq->iovec_lock, flags);
-                       iov = list_first_entry(&pq->iovec_list,
-                                              struct user_sdma_iovec, list);
-                       list_del_init(&iov->list);
-                       spin_unlock_irqrestore(&pq->iovec_lock, flags);
-                       unpin_vector_pages(iov);
-               }
                kfree(pq->reqs);
                kmem_cache_destroy(pq->txreq_cache);
                kfree(pq);
@@ -492,11 +478,10 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
        struct hfi1_user_sdma_pkt_q *pq = fd->pq;
        struct hfi1_user_sdma_comp_q *cq = fd->cq;
        struct hfi1_devdata *dd = pq->dd;
-       unsigned long idx = 0, flags;
+       unsigned long idx = 0, unpinned;
        u8 pcount = initial_pkt_count;
        struct sdma_req_info info;
        struct user_sdma_request *req;
-       struct user_sdma_iovec *ioptr;
        u8 opcode, sc, vl;
 
        if (iovec[idx].iov_len < sizeof(info) + sizeof(req->hdr)) {
@@ -515,13 +500,11 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
        }
 
        /* Process any completed vectors */
-       while (!list_empty(&pq->iovec_list)) {
-               spin_lock_irqsave(&pq->iovec_lock, flags);
-               ioptr = list_first_entry(&pq->iovec_list,
-                                        struct user_sdma_iovec, list);
-               list_del_init(&ioptr->list);
-               spin_unlock_irqrestore(&pq->iovec_lock, flags);
-               unpin_vector_pages(ioptr);
+       unpinned = xchg(&pq->unpinned, 0);
+       if (unpinned) {
+               down_write(&current->mm->mmap_sem);
+               current->mm->pinned_vm -= unpinned;
+               up_write(&current->mm->mmap_sem);
        }
 
        trace_hfi1_sdma_user_reqinfo(dd, uctxt->ctxt, fd->subctxt,
@@ -1075,10 +1058,6 @@ static int pin_vector_pages(struct user_sdma_request *req,
                unpin_vector_pages(iovec);
                return -EFAULT;
        }
-       /*
-        * Get a reference to the process's mm so we can use it when
-        * unpinning the io vectors.
-        */
        return 0;
 }
 
@@ -1368,7 +1347,7 @@ static void user_sdma_txreq_cb(struct sdma_txreq *txreq, int status,
        struct hfi1_user_sdma_pkt_q *pq;
        struct hfi1_user_sdma_comp_q *cq;
        u16 idx;
-       int i;
+       int i, j;
 
        if (!tx->req)
                return;
@@ -1379,15 +1358,19 @@ static void user_sdma_txreq_cb(struct sdma_txreq *txreq, int status,
 
        /*
         * If we have any io vectors associated with this txreq,
-        * check whether they need to be 'freed'. We can't free them
-        * here because the unpin function needs to be able to sleep.
+        * check whether they need to be 'freed'.
         */
        for (i = tx->idx; i >= 0; i--) {
                if (tx->iovecs[i].flags & TXREQ_FLAGS_IOVEC_LAST_PKT) {
-                       spin_lock(&pq->iovec_lock);
-                       list_add_tail(&tx->iovecs[i].vec->list,
-                                     &pq->iovec_list);
-                       spin_unlock(&pq->iovec_lock);
+                       struct user_sdma_iovec *vec =
+                               tx->iovecs[i].vec;
+
+                       for (j = 0; j < vec->npages; j++)
+                               put_page(vec->pages[j]);
+                       xadd(&pq->unpinned, vec->npages);
+                       kfree(vec->pages);
+                       vec->pages = NULL;
+                       vec->npages = 0;
                }
        }
 
index 317f0e8cffb669dc9508fdf5c271669d4e0b7340..7ef31a6b6dbe43ca3c486204bae5b0be100e9987 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2015 Intel Corporation.
+ * Copyright(c) 2015, 2016 Intel Corporation.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -18,7 +18,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2015 Intel Corporation.
+ * Copyright(c) 2015, 2016 Intel Corporation.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -69,8 +69,7 @@ struct hfi1_user_sdma_pkt_q {
        struct iowait busy;
        unsigned state;
        wait_queue_head_t wait;
-       struct list_head iovec_list;
-       spinlock_t iovec_lock;   /* protect iovec_list */
+       unsigned long unpinned;
 };
 
 struct hfi1_user_sdma_comp_q {