IB/hfi1: Fix a subcontext memory leak
authorMichael J. Ruhl <michael.j.ruhl@intel.com>
Thu, 4 May 2017 12:14:34 +0000 (05:14 -0700)
committerDoug Ledford <dledford@redhat.com>
Thu, 4 May 2017 23:31:46 +0000 (19:31 -0400)
The only context that frees user_exp_rcv data structures is the last
context closed (from a sub-context set).  This leaks the allocations
from the other sub-contexts.  Separate the common frees from the
specific frees and call them at the appropriate time.

Using KEDR to check for memory leaks we get:

Before test:

[leak_check] Possible leaks: 25

After test:

[leak_check] Possible leaks: 31  (6 leaked data structures)

After patch applied (before and after test have the same value)

[leak_check] Possible leaks: 25

Each leak is 192 + 13440 + 6720 = 20352 bytes per sub-context.

Cc: stable@vger.kernel.org
Reviewed-by: Mike Marciniszyn <mike.marciniszyn@intel.com>
Signed-off-by: Michael J. Ruhl <michael.j.ruhl@intel.com>
Signed-off-by: Dennis Dalessandro <dennis.dalessandro@intel.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
drivers/infiniband/hw/hfi1/file_ops.c
drivers/infiniband/hw/hfi1/user_exp_rcv.c
drivers/infiniband/hw/hfi1/user_exp_rcv.h

index 3d9bce4..afdf3ef 100644 (file)
@@ -756,6 +756,9 @@ static int hfi1_file_close(struct inode *inode, struct file *fp)
        /* release the cpu */
        hfi1_put_proc_affinity(fdata->rec_cpu_num);
 
+       /* clean up rcv side */
+       hfi1_user_exp_rcv_free(fdata);
+
        /*
         * Clear any left over, unhandled events so the next process that
         * gets this context doesn't get confused.
@@ -795,7 +798,7 @@ static int hfi1_file_close(struct inode *inode, struct file *fp)
 
        dd->rcd[uctxt->ctxt] = NULL;
 
-       hfi1_user_exp_rcv_free(fdata);
+       hfi1_user_exp_rcv_grp_free(uctxt);
        hfi1_clear_ctxt_pkey(dd, uctxt->ctxt);
 
        uctxt->rcvwait_to = 0;
index 35c6e7e..c7f13df 100644 (file)
@@ -252,36 +252,40 @@ done:
        return ret;
 }
 
+void hfi1_user_exp_rcv_grp_free(struct hfi1_ctxtdata *uctxt)
+{
+       struct tid_group *grp, *gptr;
+
+       list_for_each_entry_safe(grp, gptr, &uctxt->tid_group_list.list,
+                                list) {
+               list_del_init(&grp->list);
+               kfree(grp);
+       }
+       hfi1_clear_tids(uctxt);
+}
+
 int hfi1_user_exp_rcv_free(struct hfi1_filedata *fd)
 {
        struct hfi1_ctxtdata *uctxt = fd->uctxt;
-       struct tid_group *grp, *gptr;
 
-       if (!test_bit(HFI1_CTXT_SETUP_DONE, &uctxt->event_flags))
-               return 0;
        /*
         * The notifier would have been removed when the process'es mm
         * was freed.
         */
-       if (fd->handler)
+       if (fd->handler) {
                hfi1_mmu_rb_unregister(fd->handler);
-
-       kfree(fd->invalid_tids);
-
-       if (!uctxt->cnt) {
+       } else {
                if (!EXP_TID_SET_EMPTY(uctxt->tid_full_list))
                        unlock_exp_tids(uctxt, &uctxt->tid_full_list, fd);
                if (!EXP_TID_SET_EMPTY(uctxt->tid_used_list))
                        unlock_exp_tids(uctxt, &uctxt->tid_used_list, fd);
-               list_for_each_entry_safe(grp, gptr, &uctxt->tid_group_list.list,
-                                        list) {
-                       list_del_init(&grp->list);
-                       kfree(grp);
-               }
-               hfi1_clear_tids(uctxt);
        }
 
+       kfree(fd->invalid_tids);
+       fd->invalid_tids = NULL;
+
        kfree(fd->entry_to_rb);
+       fd->entry_to_rb = NULL;
        return 0;
 }
 
index 9bc8d9f..d1d7d3d 100644 (file)
@@ -70,6 +70,7 @@
                (tid) |= EXP_TID_SET(field, (value));                   \
        } while (0)
 
+void hfi1_user_exp_rcv_grp_free(struct hfi1_ctxtdata *uctxt);
 int hfi1_user_exp_rcv_init(struct file *);
 int hfi1_user_exp_rcv_free(struct hfi1_filedata *);
 int hfi1_user_exp_rcv_setup(struct file *, struct hfi1_tid_info *);