Skip to content

Commit 07fd551

Browse files
Steven Rostedtrostedt
authored andcommitted
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 <[email protected]> Signed-off-by: Steven Rostedt <[email protected]>
1 parent 2b49938 commit 07fd551

File tree

1 file changed

+28
-27
lines changed

1 file changed

+28
-27
lines changed

kernel/trace/ftrace.c

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,7 @@ struct ftrace_hash {
913913
unsigned long size_bits;
914914
struct hlist_head *buckets;
915915
unsigned long count;
916+
struct rcu_head rcu;
916917
};
917918

918919
/*
@@ -1058,6 +1059,21 @@ static void free_ftrace_hash(struct ftrace_hash *hash)
10581059
kfree(hash);
10591060
}
10601061

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+
10611077
static struct ftrace_hash *alloc_ftrace_hash(int size_bits)
10621078
{
10631079
struct ftrace_hash *hash;
@@ -1122,7 +1138,8 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src)
11221138
struct ftrace_func_entry *entry;
11231139
struct hlist_node *tp, *tn;
11241140
struct hlist_head *hhd;
1125-
struct ftrace_hash *hash = *dst;
1141+
struct ftrace_hash *old_hash;
1142+
struct ftrace_hash *new_hash;
11261143
unsigned long key;
11271144
int size = src->count;
11281145
int bits = 0;
@@ -1133,13 +1150,11 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src)
11331150
* the empty_hash.
11341151
*/
11351152
if (!src->count) {
1136-
free_ftrace_hash(*dst);
1137-
*dst = EMPTY_HASH;
1153+
free_ftrace_hash_rcu(*dst);
1154+
rcu_assign_pointer(*dst, EMPTY_HASH);
11381155
return 0;
11391156
}
11401157

1141-
ftrace_hash_clear(hash);
1142-
11431158
/*
11441159
* Make the hash size about 1/2 the # found
11451160
*/
@@ -1150,27 +1165,9 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src)
11501165
if (bits > FTRACE_HASH_MAX_BITS)
11511166
bits = FTRACE_HASH_MAX_BITS;
11521167

1153-
/* We can't modify the empty_hash */
1154-
if (hash == EMPTY_HASH) {
1155-
/* Create a new hash */
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-
}
1168+
new_hash = alloc_ftrace_hash(bits);
1169+
if (!new_hash)
1170+
return -ENOMEM;
11741171

11751172
size = 1 << src->size_bits;
11761173
for (i = 0; i < size; i++) {
@@ -1181,10 +1178,14 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src)
11811178
else
11821179
key = 0;
11831180
remove_hash_entry(src, entry);
1184-
__add_hash_entry(hash, entry);
1181+
__add_hash_entry(new_hash, entry);
11851182
}
11861183
}
11871184

1185+
old_hash = *dst;
1186+
rcu_assign_pointer(*dst, new_hash);
1187+
free_ftrace_hash_rcu(old_hash);
1188+
11881189
return 0;
11891190
}
11901191

0 commit comments

Comments
 (0)