| From 224d71f910102c966cdcd782c97e096d5e26e4da Mon Sep 17 00:00:00 2001 |
| From: "Michael J. Ruhl" <michael.j.ruhl@intel.com> |
| Date: Thu, 4 May 2017 05:14:34 -0700 |
| Subject: IB/hfi1: Fix a subcontext memory leak |
| |
| From: Michael J. Ruhl <michael.j.ruhl@intel.com> |
| |
| commit 224d71f910102c966cdcd782c97e096d5e26e4da upstream. |
| |
| 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. |
| |
| 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> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/infiniband/hw/hfi1/file_ops.c | 5 +++- |
| drivers/infiniband/hw/hfi1/user_exp_rcv.c | 32 ++++++++++++++++-------------- |
| drivers/infiniband/hw/hfi1/user_exp_rcv.h | 1 |
| 3 files changed, 23 insertions(+), 15 deletions(-) |
| |
| --- a/drivers/infiniband/hw/hfi1/file_ops.c |
| +++ b/drivers/infiniband/hw/hfi1/file_ops.c |
| @@ -751,6 +751,9 @@ static int hfi1_file_close(struct inode |
| /* 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. |
| @@ -790,7 +793,7 @@ static int hfi1_file_close(struct inode |
| |
| 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; |
| --- a/drivers/infiniband/hw/hfi1/user_exp_rcv.c |
| +++ b/drivers/infiniband/hw/hfi1/user_exp_rcv.c |
| @@ -250,36 +250,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; |
| } |
| |
| --- a/drivers/infiniband/hw/hfi1/user_exp_rcv.h |
| +++ b/drivers/infiniband/hw/hfi1/user_exp_rcv.h |
| @@ -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 *); |