diff options
author | Ira Weiny <ira.weiny@intel.com> | 2016-07-28 15:21:26 -0400 |
---|---|---|
committer | Doug Ledford <dledford@redhat.com> | 2016-08-02 22:46:21 -0400 |
commit | 2677a7680e773195a4fdabd163d756cac1b9abd7 (patch) | |
tree | d8640d01378b5d7ffa798f00cbcd29d686d9a1d4 | |
parent | 082b3532915395ea6620ba691138baf151a543b0 (diff) |
IB/hfi1: Fix memory leak during unexpected shutdown
During an unexpected shutdown, references to tid_rb_node were NULL'ed out
without properly being released.
Fix this by calling clear_tid_node in the mmu notifier remove callback
rather than after these callbacks are called.
Reviewed-by: Dean Luick <dean.luick@intel.com>
Signed-off-by: Ira Weiny <ira.weiny@intel.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
-rw-r--r-- | drivers/infiniband/hw/hfi1/user_exp_rcv.c | 44 |
1 files changed, 31 insertions, 13 deletions
diff --git a/drivers/infiniband/hw/hfi1/user_exp_rcv.c b/drivers/infiniband/hw/hfi1/user_exp_rcv.c index 8717e11fe3f5..64d26525435a 100644 --- a/drivers/infiniband/hw/hfi1/user_exp_rcv.c +++ b/drivers/infiniband/hw/hfi1/user_exp_rcv.c | |||
@@ -87,13 +87,15 @@ static u32 find_phys_blocks(struct page **, unsigned, struct tid_pageset *); | |||
87 | static int set_rcvarray_entry(struct file *, unsigned long, u32, | 87 | static int set_rcvarray_entry(struct file *, unsigned long, u32, |
88 | struct tid_group *, struct page **, unsigned); | 88 | struct tid_group *, struct page **, unsigned); |
89 | static int tid_rb_insert(void *, struct mmu_rb_node *); | 89 | static int tid_rb_insert(void *, struct mmu_rb_node *); |
90 | static void cacheless_tid_rb_remove(struct hfi1_filedata *fdata, | ||
91 | struct tid_rb_node *tnode); | ||
90 | static void tid_rb_remove(void *, struct mmu_rb_node *); | 92 | static void tid_rb_remove(void *, struct mmu_rb_node *); |
91 | static int tid_rb_invalidate(void *, struct mmu_rb_node *); | 93 | static int tid_rb_invalidate(void *, struct mmu_rb_node *); |
92 | static int program_rcvarray(struct file *, unsigned long, struct tid_group *, | 94 | static int program_rcvarray(struct file *, unsigned long, struct tid_group *, |
93 | struct tid_pageset *, unsigned, u16, struct page **, | 95 | struct tid_pageset *, unsigned, u16, struct page **, |
94 | u32 *, unsigned *, unsigned *); | 96 | u32 *, unsigned *, unsigned *); |
95 | static int unprogram_rcvarray(struct file *, u32, struct tid_group **); | 97 | static int unprogram_rcvarray(struct file *, u32, struct tid_group **); |
96 | static void clear_tid_node(struct hfi1_filedata *, struct tid_rb_node *); | 98 | static void clear_tid_node(struct hfi1_filedata *fd, struct tid_rb_node *node); |
97 | 99 | ||
98 | static struct mmu_rb_ops tid_rb_ops = { | 100 | static struct mmu_rb_ops tid_rb_ops = { |
99 | .insert = tid_rb_insert, | 101 | .insert = tid_rb_insert, |
@@ -899,14 +901,15 @@ static int unprogram_rcvarray(struct file *fp, u32 tidinfo, | |||
899 | node = fd->entry_to_rb[rcventry]; | 901 | node = fd->entry_to_rb[rcventry]; |
900 | if (!node || node->rcventry != (uctxt->expected_base + rcventry)) | 902 | if (!node || node->rcventry != (uctxt->expected_base + rcventry)) |
901 | return -EBADF; | 903 | return -EBADF; |
904 | |||
905 | if (grp) | ||
906 | *grp = node->grp; | ||
907 | |||
902 | if (!fd->handler) | 908 | if (!fd->handler) |
903 | tid_rb_remove(fd, &node->mmu); | 909 | cacheless_tid_rb_remove(fd, node); |
904 | else | 910 | else |
905 | hfi1_mmu_rb_remove(fd->handler, &node->mmu); | 911 | hfi1_mmu_rb_remove(fd->handler, &node->mmu); |
906 | 912 | ||
907 | if (grp) | ||
908 | *grp = node->grp; | ||
909 | clear_tid_node(fd, node); | ||
910 | return 0; | 913 | return 0; |
911 | } | 914 | } |
912 | 915 | ||
@@ -943,6 +946,10 @@ static void clear_tid_node(struct hfi1_filedata *fd, struct tid_rb_node *node) | |||
943 | kfree(node); | 946 | kfree(node); |
944 | } | 947 | } |
945 | 948 | ||
949 | /* | ||
950 | * As a simple helper for hfi1_user_exp_rcv_free, this function deals with | ||
951 | * clearing nodes in the non-cached case. | ||
952 | */ | ||
946 | static void unlock_exp_tids(struct hfi1_ctxtdata *uctxt, | 953 | static void unlock_exp_tids(struct hfi1_ctxtdata *uctxt, |
947 | struct exp_tid_set *set, | 954 | struct exp_tid_set *set, |
948 | struct hfi1_filedata *fd) | 955 | struct hfi1_filedata *fd) |
@@ -962,17 +969,20 @@ static void unlock_exp_tids(struct hfi1_ctxtdata *uctxt, | |||
962 | uctxt->expected_base]; | 969 | uctxt->expected_base]; |
963 | if (!node || node->rcventry != rcventry) | 970 | if (!node || node->rcventry != rcventry) |
964 | continue; | 971 | continue; |
965 | if (!fd->handler) | 972 | |
966 | tid_rb_remove(fd, &node->mmu); | 973 | cacheless_tid_rb_remove(fd, node); |
967 | else | ||
968 | hfi1_mmu_rb_remove(fd->handler, | ||
969 | &node->mmu); | ||
970 | clear_tid_node(fd, node); | ||
971 | } | 974 | } |
972 | } | 975 | } |
973 | } | 976 | } |
974 | } | 977 | } |
975 | 978 | ||
979 | /* | ||
980 | * Always return 0 from this function. A non-zero return indicates that the | ||
981 | * remove operation will be called and that memory should be unpinned. | ||
982 | * However, the driver cannot unpin out from under PSM. Instead, retain the | ||
983 | * memory (by returning 0) and inform PSM that the memory is going away. PSM | ||
984 | * will call back later when it has removed the memory from its list. | ||
985 | */ | ||
976 | static int tid_rb_invalidate(void *arg, struct mmu_rb_node *mnode) | 986 | static int tid_rb_invalidate(void *arg, struct mmu_rb_node *mnode) |
977 | { | 987 | { |
978 | struct hfi1_filedata *fdata = arg; | 988 | struct hfi1_filedata *fdata = arg; |
@@ -1027,12 +1037,20 @@ static int tid_rb_insert(void *arg, struct mmu_rb_node *node) | |||
1027 | return 0; | 1037 | return 0; |
1028 | } | 1038 | } |
1029 | 1039 | ||
1040 | static void cacheless_tid_rb_remove(struct hfi1_filedata *fdata, | ||
1041 | struct tid_rb_node *tnode) | ||
1042 | { | ||
1043 | u32 base = fdata->uctxt->expected_base; | ||
1044 | |||
1045 | fdata->entry_to_rb[tnode->rcventry - base] = NULL; | ||
1046 | clear_tid_node(fdata, tnode); | ||
1047 | } | ||
1048 | |||
1030 | static void tid_rb_remove(void *arg, struct mmu_rb_node *node) | 1049 | static void tid_rb_remove(void *arg, struct mmu_rb_node *node) |
1031 | { | 1050 | { |
1032 | struct hfi1_filedata *fdata = arg; | 1051 | struct hfi1_filedata *fdata = arg; |
1033 | struct tid_rb_node *tnode = | 1052 | struct tid_rb_node *tnode = |
1034 | container_of(node, struct tid_rb_node, mmu); | 1053 | container_of(node, struct tid_rb_node, mmu); |
1035 | u32 base = fdata->uctxt->expected_base; | ||
1036 | 1054 | ||
1037 | fdata->entry_to_rb[tnode->rcventry - base] = NULL; | 1055 | cacheless_tid_rb_remove(fdata, tnode); |
1038 | } | 1056 | } |