diff options
author | Steven Rostedt <srostedt@redhat.com> | 2011-05-05 18:03:47 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2011-05-18 15:29:50 -0400 |
commit | 07fd5515f3b5c20704707f63e7f4485b534508a8 (patch) | |
tree | f018ec497f8c6b49a0fcfcd7a92a600e670f90f2 /kernel/trace | |
parent | 2b499381bc50ede01b3d8eab164ca2fad00655f0 (diff) |
ftrace: Free hash with call_rcu_sched()
When a hash is modified and might be in use, we need to perform
a schedule RCU operation on it, as the hashes will soon be used
directly in the function tracer callback.
Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel/trace')
-rw-r--r-- | kernel/trace/ftrace.c | 55 |
1 files changed, 28 insertions, 27 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index dcce0bf9c84d..92b6fdf49ae5 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -913,6 +913,7 @@ struct ftrace_hash { | |||
913 | unsigned long size_bits; | 913 | unsigned long size_bits; |
914 | struct hlist_head *buckets; | 914 | struct hlist_head *buckets; |
915 | unsigned long count; | 915 | unsigned long count; |
916 | struct rcu_head rcu; | ||
916 | }; | 917 | }; |
917 | 918 | ||
918 | /* | 919 | /* |
@@ -1058,6 +1059,21 @@ static void free_ftrace_hash(struct ftrace_hash *hash) | |||
1058 | kfree(hash); | 1059 | kfree(hash); |
1059 | } | 1060 | } |
1060 | 1061 | ||
1062 | static void __free_ftrace_hash_rcu(struct rcu_head *rcu) | ||
1063 | { | ||
1064 | struct ftrace_hash *hash; | ||
1065 | |||
1066 | hash = container_of(rcu, struct ftrace_hash, rcu); | ||
1067 | free_ftrace_hash(hash); | ||
1068 | } | ||
1069 | |||
1070 | static void free_ftrace_hash_rcu(struct ftrace_hash *hash) | ||
1071 | { | ||
1072 | if (!hash || hash == EMPTY_HASH) | ||
1073 | return; | ||
1074 | call_rcu_sched(&hash->rcu, __free_ftrace_hash_rcu); | ||
1075 | } | ||
1076 | |||
1061 | static struct ftrace_hash *alloc_ftrace_hash(int size_bits) | 1077 | static struct ftrace_hash *alloc_ftrace_hash(int size_bits) |
1062 | { | 1078 | { |
1063 | struct ftrace_hash *hash; | 1079 | struct ftrace_hash *hash; |
@@ -1122,7 +1138,8 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src) | |||
1122 | struct ftrace_func_entry *entry; | 1138 | struct ftrace_func_entry *entry; |
1123 | struct hlist_node *tp, *tn; | 1139 | struct hlist_node *tp, *tn; |
1124 | struct hlist_head *hhd; | 1140 | struct hlist_head *hhd; |
1125 | struct ftrace_hash *hash = *dst; | 1141 | struct ftrace_hash *old_hash; |
1142 | struct ftrace_hash *new_hash; | ||
1126 | unsigned long key; | 1143 | unsigned long key; |
1127 | int size = src->count; | 1144 | int size = src->count; |
1128 | int bits = 0; | 1145 | int bits = 0; |
@@ -1133,13 +1150,11 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src) | |||
1133 | * the empty_hash. | 1150 | * the empty_hash. |
1134 | */ | 1151 | */ |
1135 | if (!src->count) { | 1152 | if (!src->count) { |
1136 | free_ftrace_hash(*dst); | 1153 | free_ftrace_hash_rcu(*dst); |
1137 | *dst = EMPTY_HASH; | 1154 | rcu_assign_pointer(*dst, EMPTY_HASH); |
1138 | return 0; | 1155 | return 0; |
1139 | } | 1156 | } |
1140 | 1157 | ||
1141 | ftrace_hash_clear(hash); | ||
1142 | |||
1143 | /* | 1158 | /* |
1144 | * Make the hash size about 1/2 the # found | 1159 | * Make the hash size about 1/2 the # found |
1145 | */ | 1160 | */ |
@@ -1150,27 +1165,9 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src) | |||
1150 | if (bits > FTRACE_HASH_MAX_BITS) | 1165 | if (bits > FTRACE_HASH_MAX_BITS) |
1151 | bits = FTRACE_HASH_MAX_BITS; | 1166 | bits = FTRACE_HASH_MAX_BITS; |
1152 | 1167 | ||
1153 | /* We can't modify the empty_hash */ | 1168 | new_hash = alloc_ftrace_hash(bits); |
1154 | if (hash == EMPTY_HASH) { | 1169 | if (!new_hash) |
1155 | /* Create a new hash */ | 1170 | return -ENOMEM; |
1156 | *dst = alloc_ftrace_hash(bits); | ||
1157 | if (!*dst) { | ||
1158 | *dst = EMPTY_HASH; | ||
1159 | return -ENOMEM; | ||
1160 | } | ||
1161 | hash = *dst; | ||
1162 | } else { | ||
1163 | size = 1 << bits; | ||
1164 | |||
1165 | /* Use the old hash, but create new buckets */ | ||
1166 | hhd = kzalloc(sizeof(*hhd) * size, GFP_KERNEL); | ||
1167 | if (!hhd) | ||
1168 | return -ENOMEM; | ||
1169 | |||
1170 | kfree(hash->buckets); | ||
1171 | hash->buckets = hhd; | ||
1172 | hash->size_bits = bits; | ||
1173 | } | ||
1174 | 1171 | ||
1175 | size = 1 << src->size_bits; | 1172 | size = 1 << src->size_bits; |
1176 | for (i = 0; i < size; i++) { | 1173 | for (i = 0; i < size; i++) { |
@@ -1181,10 +1178,14 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src) | |||
1181 | else | 1178 | else |
1182 | key = 0; | 1179 | key = 0; |
1183 | remove_hash_entry(src, entry); | 1180 | remove_hash_entry(src, entry); |
1184 | __add_hash_entry(hash, entry); | 1181 | __add_hash_entry(new_hash, entry); |
1185 | } | 1182 | } |
1186 | } | 1183 | } |
1187 | 1184 | ||
1185 | old_hash = *dst; | ||
1186 | rcu_assign_pointer(*dst, new_hash); | ||
1187 | free_ftrace_hash_rcu(old_hash); | ||
1188 | |||
1188 | return 0; | 1189 | return 0; |
1189 | } | 1190 | } |
1190 | 1191 | ||