aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/ftrace.h18
-rw-r--r--kernel/trace/ftrace.c247
2 files changed, 265 insertions, 0 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index f0a0ecc63b5c..13918c4400ad 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -106,6 +106,24 @@ struct ftrace_func_command {
106/* asm/ftrace.h must be defined for archs supporting dynamic ftrace */ 106/* asm/ftrace.h must be defined for archs supporting dynamic ftrace */
107#include <asm/ftrace.h> 107#include <asm/ftrace.h>
108 108
109struct ftrace_hook_ops {
110 void (*func)(unsigned long ip,
111 unsigned long parent_ip,
112 void **data);
113 int (*callback)(unsigned long ip, void **data);
114 void (*free)(void **data);
115};
116
117extern int
118register_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops,
119 void *data);
120extern void
121unregister_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops,
122 void *data);
123extern void
124unregister_ftrace_function_hook_func(char *glob, struct ftrace_hook_ops *ops);
125extern void unregister_ftrace_function_hook_all(char *glob);
126
109enum { 127enum {
110 FTRACE_FL_FREE = (1 << 0), 128 FTRACE_FL_FREE = (1 << 0),
111 FTRACE_FL_FAILED = (1 << 1), 129 FTRACE_FL_FAILED = (1 << 1),
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 157d4f68b0e0..0b80e325f296 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -27,6 +27,7 @@
27#include <linux/sysctl.h> 27#include <linux/sysctl.h>
28#include <linux/ctype.h> 28#include <linux/ctype.h>
29#include <linux/list.h> 29#include <linux/list.h>
30#include <linux/hash.h>
30 31
31#include <asm/ftrace.h> 32#include <asm/ftrace.h>
32 33
@@ -1245,6 +1246,252 @@ static int __init ftrace_mod_cmd_init(void)
1245} 1246}
1246device_initcall(ftrace_mod_cmd_init); 1247device_initcall(ftrace_mod_cmd_init);
1247 1248
1249#define FTRACE_HASH_BITS 7
1250#define FTRACE_FUNC_HASHSIZE (1 << FTRACE_HASH_BITS)
1251static struct hlist_head ftrace_func_hash[FTRACE_FUNC_HASHSIZE] __read_mostly;
1252
1253struct ftrace_func_hook {
1254 struct hlist_node node;
1255 struct ftrace_hook_ops *ops;
1256 unsigned long flags;
1257 unsigned long ip;
1258 void *data;
1259 struct rcu_head rcu;
1260};
1261
1262static void
1263function_trace_hook_call(unsigned long ip, unsigned long parent_ip)
1264{
1265 struct ftrace_func_hook *entry;
1266 struct hlist_head *hhd;
1267 struct hlist_node *n;
1268 unsigned long key;
1269 int resched;
1270
1271 key = hash_long(ip, FTRACE_HASH_BITS);
1272
1273 hhd = &ftrace_func_hash[key];
1274
1275 if (hlist_empty(hhd))
1276 return;
1277
1278 /*
1279 * Disable preemption for these calls to prevent a RCU grace
1280 * period. This syncs the hash iteration and freeing of items
1281 * on the hash. rcu_read_lock is too dangerous here.
1282 */
1283 resched = ftrace_preempt_disable();
1284 hlist_for_each_entry_rcu(entry, n, hhd, node) {
1285 if (entry->ip == ip)
1286 entry->ops->func(ip, parent_ip, &entry->data);
1287 }
1288 ftrace_preempt_enable(resched);
1289}
1290
1291static struct ftrace_ops trace_hook_ops __read_mostly =
1292{
1293 .func = function_trace_hook_call,
1294};
1295
1296static int ftrace_hook_registered;
1297
1298static void __enable_ftrace_function_hook(void)
1299{
1300 int i;
1301
1302 if (ftrace_hook_registered)
1303 return;
1304
1305 for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) {
1306 struct hlist_head *hhd = &ftrace_func_hash[i];
1307 if (hhd->first)
1308 break;
1309 }
1310 /* Nothing registered? */
1311 if (i == FTRACE_FUNC_HASHSIZE)
1312 return;
1313
1314 __register_ftrace_function(&trace_hook_ops);
1315 ftrace_startup(0);
1316 ftrace_hook_registered = 1;
1317}
1318
1319static void __disable_ftrace_function_hook(void)
1320{
1321 int i;
1322
1323 if (!ftrace_hook_registered)
1324 return;
1325
1326 for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) {
1327 struct hlist_head *hhd = &ftrace_func_hash[i];
1328 if (hhd->first)
1329 return;
1330 }
1331
1332 /* no more funcs left */
1333 __unregister_ftrace_function(&trace_hook_ops);
1334 ftrace_shutdown(0);
1335 ftrace_hook_registered = 0;
1336}
1337
1338
1339static void ftrace_free_entry_rcu(struct rcu_head *rhp)
1340{
1341 struct ftrace_func_hook *entry =
1342 container_of(rhp, struct ftrace_func_hook, rcu);
1343
1344 if (entry->ops->free)
1345 entry->ops->free(&entry->data);
1346 kfree(entry);
1347}
1348
1349
1350int
1351register_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops,
1352 void *data)
1353{
1354 struct ftrace_func_hook *entry;
1355 struct ftrace_page *pg;
1356 struct dyn_ftrace *rec;
1357 unsigned long key;
1358 int type, len, not;
1359 int count = 0;
1360 char *search;
1361
1362 type = ftrace_setup_glob(glob, strlen(glob), &search, &not);
1363 len = strlen(search);
1364
1365 /* we do not support '!' for function hooks */
1366 if (WARN_ON(not))
1367 return -EINVAL;
1368
1369 mutex_lock(&ftrace_lock);
1370 do_for_each_ftrace_rec(pg, rec) {
1371
1372 if (rec->flags & FTRACE_FL_FAILED)
1373 continue;
1374
1375 if (!ftrace_match_record(rec, search, len, type))
1376 continue;
1377
1378 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
1379 if (!entry) {
1380 /* If we did not hook to any, then return error */
1381 if (!count)
1382 count = -ENOMEM;
1383 goto out_unlock;
1384 }
1385
1386 count++;
1387
1388 entry->data = data;
1389
1390 /*
1391 * The caller might want to do something special
1392 * for each function we find. We call the callback
1393 * to give the caller an opportunity to do so.
1394 */
1395 if (ops->callback) {
1396 if (ops->callback(rec->ip, &entry->data) < 0) {
1397 /* caller does not like this func */
1398 kfree(entry);
1399 continue;
1400 }
1401 }
1402
1403 entry->ops = ops;
1404 entry->ip = rec->ip;
1405
1406 key = hash_long(entry->ip, FTRACE_HASH_BITS);
1407 hlist_add_head_rcu(&entry->node, &ftrace_func_hash[key]);
1408
1409 } while_for_each_ftrace_rec();
1410 __enable_ftrace_function_hook();
1411
1412 out_unlock:
1413 mutex_unlock(&ftrace_lock);
1414
1415 return count;
1416}
1417
1418enum {
1419 HOOK_TEST_FUNC = 1,
1420 HOOK_TEST_DATA = 2
1421};
1422
1423static void
1424__unregister_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops,
1425 void *data, int flags)
1426{
1427 struct ftrace_func_hook *entry;
1428 struct hlist_node *n, *tmp;
1429 char str[KSYM_SYMBOL_LEN];
1430 int type = MATCH_FULL;
1431 int i, len = 0;
1432 char *search;
1433
1434 if (glob && (strcmp(glob, "*") || !strlen(glob)))
1435 glob = NULL;
1436 else {
1437 int not;
1438
1439 type = ftrace_setup_glob(glob, strlen(glob), &search, &not);
1440 len = strlen(search);
1441
1442 /* we do not support '!' for function hooks */
1443 if (WARN_ON(not))
1444 return;
1445 }
1446
1447 mutex_lock(&ftrace_lock);
1448 for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) {
1449 struct hlist_head *hhd = &ftrace_func_hash[i];
1450
1451 hlist_for_each_entry_safe(entry, n, tmp, hhd, node) {
1452
1453 /* break up if statements for readability */
1454 if ((flags & HOOK_TEST_FUNC) && entry->ops != ops)
1455 continue;
1456
1457 if ((flags & HOOK_TEST_DATA) && entry->data != data)
1458 continue;
1459
1460 /* do this last, since it is the most expensive */
1461 if (glob) {
1462 kallsyms_lookup(entry->ip, NULL, NULL,
1463 NULL, str);
1464 if (!ftrace_match(str, glob, len, type))
1465 continue;
1466 }
1467
1468 hlist_del(&entry->node);
1469 call_rcu(&entry->rcu, ftrace_free_entry_rcu);
1470 }
1471 }
1472 __disable_ftrace_function_hook();
1473 mutex_unlock(&ftrace_lock);
1474}
1475
1476void
1477unregister_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops,
1478 void *data)
1479{
1480 __unregister_ftrace_function_hook(glob, ops, data,
1481 HOOK_TEST_FUNC | HOOK_TEST_DATA);
1482}
1483
1484void
1485unregister_ftrace_function_hook_func(char *glob, struct ftrace_hook_ops *ops)
1486{
1487 __unregister_ftrace_function_hook(glob, ops, NULL, HOOK_TEST_FUNC);
1488}
1489
1490void unregister_ftrace_function_hook_all(char *glob)
1491{
1492 __unregister_ftrace_function_hook(glob, NULL, NULL, 0);
1493}
1494
1248static LIST_HEAD(ftrace_commands); 1495static LIST_HEAD(ftrace_commands);
1249static DEFINE_MUTEX(ftrace_cmd_mutex); 1496static DEFINE_MUTEX(ftrace_cmd_mutex);
1250 1497