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 | ||
