aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace/ftrace.c
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2011-05-05 18:03:47 -0400
committerSteven Rostedt <rostedt@goodmis.org>2011-05-18 15:29:50 -0400
commit07fd5515f3b5c20704707f63e7f4485b534508a8 (patch)
treef018ec497f8c6b49a0fcfcd7a92a600e670f90f2 /kernel/trace/ftrace.c
parent2b499381bc50ede01b3d8eab164ca2fad00655f0 (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/ftrace.c')
-rw-r--r--kernel/trace/ftrace.c55
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
1062static 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
1070static 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
1061static struct ftrace_hash *alloc_ftrace_hash(int size_bits) 1077static 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