IB/ehca: Fix memory leak in error path of ehca_get_dma_mr()
[safe/jmp/linux-2.6] / drivers / infiniband / hw / ehca / ehca_mrmw.c
index 0a5e221..98f2531 100644 (file)
@@ -39,6 +39,8 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <rdma/ib_umem.h>
+
 #include <asm/current.h>
 
 #include "ehca_iverbs.h"
@@ -53,9 +55,8 @@ static struct ehca_mr *ehca_mr_new(void)
 {
        struct ehca_mr *me;
 
-       me = kmem_cache_alloc(mr_cache, GFP_KERNEL);
+       me = kmem_cache_zalloc(mr_cache, GFP_KERNEL);
        if (me) {
-               memset(me, 0, sizeof(struct ehca_mr));
                spin_lock_init(&me->mrlock);
        } else
                ehca_gen_err("alloc failed");
@@ -72,9 +73,8 @@ static struct ehca_mw *ehca_mw_new(void)
 {
        struct ehca_mw *me;
 
-       me = kmem_cache_alloc(mw_cache, GFP_KERNEL);
+       me = kmem_cache_zalloc(mw_cache, GFP_KERNEL);
        if (me) {
-               memset(me, 0, sizeof(struct ehca_mw));
                spin_lock_init(&me->mwlock);
        } else
                ehca_gen_err("alloc failed");
@@ -111,6 +111,7 @@ struct ib_mr *ehca_get_dma_mr(struct ib_pd *pd, int mr_access_flags)
                                     &e_maxmr->ib.ib_mr.lkey,
                                     &e_maxmr->ib.ib_mr.rkey);
                if (ret) {
+                       ehca_mr_delete(e_maxmr);
                        ib_mr = ERR_PTR(ret);
                        goto get_dma_mr_exit0;
                }
@@ -240,10 +241,8 @@ reg_phys_mr_exit0:
 
 /*----------------------------------------------------------------------*/
 
-struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd,
-                              struct ib_umem *region,
-                              int mr_access_flags,
-                              struct ib_udata *udata)
+struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u64 virt,
+                              int mr_access_flags, struct ib_udata *udata)
 {
        struct ib_mr *ib_mr;
        struct ehca_mr *e_mr;
@@ -259,11 +258,7 @@ struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd,
                ehca_gen_err("bad pd=%p", pd);
                return ERR_PTR(-EFAULT);
        }
-       if (!region) {
-               ehca_err(pd->device, "bad input values: region=%p", region);
-               ib_mr = ERR_PTR(-EINVAL);
-               goto reg_user_mr_exit0;
-       }
+
        if (((mr_access_flags & IB_ACCESS_REMOTE_WRITE) &&
             !(mr_access_flags & IB_ACCESS_LOCAL_WRITE)) ||
            ((mr_access_flags & IB_ACCESS_REMOTE_ATOMIC) &&
@@ -277,17 +272,10 @@ struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd,
                ib_mr = ERR_PTR(-EINVAL);
                goto reg_user_mr_exit0;
        }
-       if (region->page_size != PAGE_SIZE) {
-               ehca_err(pd->device, "page size not supported, "
-                        "region->page_size=%x", region->page_size);
-               ib_mr = ERR_PTR(-EINVAL);
-               goto reg_user_mr_exit0;
-       }
 
-       if ((region->length == 0) ||
-           ((region->virt_base + region->length) < region->virt_base)) {
+       if (length == 0 || virt + length < virt) {
                ehca_err(pd->device, "bad input values: length=%lx "
-                        "virt_base=%lx", region->length, region->virt_base);
+                        "virt_base=%lx", length, virt);
                ib_mr = ERR_PTR(-EINVAL);
                goto reg_user_mr_exit0;
        }
@@ -299,40 +287,55 @@ struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd,
                goto reg_user_mr_exit0;
        }
 
+       e_mr->umem = ib_umem_get(pd->uobject->context, start, length,
+                                mr_access_flags);
+       if (IS_ERR(e_mr->umem)) {
+               ib_mr = (void *) e_mr->umem;
+               goto reg_user_mr_exit1;
+       }
+
+       if (e_mr->umem->page_size != PAGE_SIZE) {
+               ehca_err(pd->device, "page size not supported, "
+                        "e_mr->umem->page_size=%x", e_mr->umem->page_size);
+               ib_mr = ERR_PTR(-EINVAL);
+               goto reg_user_mr_exit2;
+       }
+
        /* determine number of MR pages */
-       num_pages_mr = (((region->virt_base % PAGE_SIZE) + region->length +
-                        PAGE_SIZE - 1) / PAGE_SIZE);
-       num_pages_4k = (((region->virt_base % EHCA_PAGESIZE) + region->length +
-                        EHCA_PAGESIZE - 1) / EHCA_PAGESIZE);
+       num_pages_mr = (((virt % PAGE_SIZE) + length + PAGE_SIZE - 1) /
+                       PAGE_SIZE);
+       num_pages_4k = (((virt % EHCA_PAGESIZE) + length + EHCA_PAGESIZE - 1) /
+                       EHCA_PAGESIZE);
 
        /* register MR on HCA */
        pginfo.type       = EHCA_MR_PGI_USER;
        pginfo.num_pages  = num_pages_mr;
        pginfo.num_4k     = num_pages_4k;
-       pginfo.region     = region;
-       pginfo.next_4k    = region->offset / EHCA_PAGESIZE;
+       pginfo.region     = e_mr->umem;
+       pginfo.next_4k    = e_mr->umem->offset / EHCA_PAGESIZE;
        pginfo.next_chunk = list_prepare_entry(pginfo.next_chunk,
-                                              (&region->chunk_list),
+                                              (&e_mr->umem->chunk_list),
                                               list);
 
-       ret = ehca_reg_mr(shca, e_mr, (u64*)region->virt_base,
-                         region->length, mr_access_flags, e_pd, &pginfo,
-                         &e_mr->ib.ib_mr.lkey, &e_mr->ib.ib_mr.rkey);
+       ret = ehca_reg_mr(shca, e_mr, (u64*) virt, length, mr_access_flags, e_pd,
+                         &pginfo, &e_mr->ib.ib_mr.lkey, &e_mr->ib.ib_mr.rkey);
        if (ret) {
                ib_mr = ERR_PTR(ret);
-               goto reg_user_mr_exit1;
+               goto reg_user_mr_exit2;
        }
 
        /* successful registration of all pages */
        return &e_mr->ib.ib_mr;
 
+reg_user_mr_exit2:
+       ib_umem_release(e_mr->umem);
 reg_user_mr_exit1:
        ehca_mr_delete(e_mr);
 reg_user_mr_exit0:
        if (IS_ERR(ib_mr))
-               ehca_err(pd->device, "rc=%lx pd=%p region=%p mr_access_flags=%x"
+               ehca_err(pd->device, "rc=%lx pd=%p mr_access_flags=%x"
                         " udata=%p",
-                        PTR_ERR(ib_mr), pd, region, mr_access_flags, udata);
+                        PTR_ERR(ib_mr), pd, mr_access_flags, udata);
        return ib_mr;
 } /* end ehca_reg_user_mr() */
 
@@ -598,6 +601,9 @@ int ehca_dereg_mr(struct ib_mr *mr)
                goto dereg_mr_exit0;
        }
 
+       if (e_mr->umem)
+               ib_umem_release(e_mr->umem);
+
        /* successful deregistration */
        ehca_mr_delete(e_mr);
 
@@ -1013,7 +1019,7 @@ int ehca_reg_mr_rpages(struct ehca_shca *shca,
        u32 i;
        u64 *kpage;
 
-       kpage = ehca_alloc_fw_ctrlblock();
+       kpage = ehca_alloc_fw_ctrlblock(GFP_KERNEL);
        if (!kpage) {
                ehca_err(&shca->ib_device, "kpage alloc failed");
                ret = -ENOMEM;
@@ -1124,7 +1130,7 @@ inline int ehca_rereg_mr_rereg1(struct ehca_shca *shca,
        ehca_mrmw_map_acl(acl, &hipz_acl);
        ehca_mrmw_set_pgsize_hipz_acl(&hipz_acl);
 
-       kpage = ehca_alloc_fw_ctrlblock();
+       kpage = ehca_alloc_fw_ctrlblock(GFP_KERNEL);
        if (!kpage) {
                ehca_err(&shca->ib_device, "kpage alloc failed");
                ret = -ENOMEM;
@@ -2045,13 +2051,10 @@ int ehca_mrmw_map_hrc_alloc(const u64 hipz_rc)
        switch (hipz_rc) {
        case H_SUCCESS:              /* successful completion */
                return 0;
-       case H_ADAPTER_PARM:         /* invalid adapter handle */
-       case H_RT_PARM:              /* invalid resource type */
        case H_NOT_ENOUGH_RESOURCES: /* insufficient resources */
-       case H_MLENGTH_PARM:         /* invalid memory length */
-       case H_MEM_ACCESS_PARM:      /* invalid access controls */
        case H_CONSTRAINED:          /* resource constraint */
-               return -EINVAL;
+       case H_NO_MEM:
+               return -ENOMEM;
        case H_BUSY:                 /* long busy */
                return -EBUSY;
        default: