diff options
-rw-r--r-- | drivers/i2c/i2c-core.c | 37 | ||||
-rw-r--r-- | include/trace/events/i2c.h | 150 |
2 files changed, 187 insertions, 0 deletions
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 98a5fd950f16..bdedbee85c01 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c | |||
@@ -48,10 +48,13 @@ | |||
48 | #include <linux/rwsem.h> | 48 | #include <linux/rwsem.h> |
49 | #include <linux/pm_runtime.h> | 49 | #include <linux/pm_runtime.h> |
50 | #include <linux/acpi.h> | 50 | #include <linux/acpi.h> |
51 | #include <linux/jump_label.h> | ||
51 | #include <asm/uaccess.h> | 52 | #include <asm/uaccess.h> |
52 | 53 | ||
53 | #include "i2c-core.h" | 54 | #include "i2c-core.h" |
54 | 55 | ||
56 | #define CREATE_TRACE_POINTS | ||
57 | #include <trace/events/i2c.h> | ||
55 | 58 | ||
56 | /* core_lock protects i2c_adapter_idr, and guarantees | 59 | /* core_lock protects i2c_adapter_idr, and guarantees |
57 | that device detection, deletion of detected devices, and attach_adapter | 60 | that device detection, deletion of detected devices, and attach_adapter |
@@ -62,6 +65,18 @@ static DEFINE_IDR(i2c_adapter_idr); | |||
62 | static struct device_type i2c_client_type; | 65 | static struct device_type i2c_client_type; |
63 | static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver); | 66 | static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver); |
64 | 67 | ||
68 | static struct static_key i2c_trace_msg = STATIC_KEY_INIT_FALSE; | ||
69 | |||
70 | void i2c_transfer_trace_reg(void) | ||
71 | { | ||
72 | static_key_slow_inc(&i2c_trace_msg); | ||
73 | } | ||
74 | |||
75 | void i2c_transfer_trace_unreg(void) | ||
76 | { | ||
77 | static_key_slow_dec(&i2c_trace_msg); | ||
78 | } | ||
79 | |||
65 | /* ------------------------------------------------------------------------- */ | 80 | /* ------------------------------------------------------------------------- */ |
66 | 81 | ||
67 | static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id, | 82 | static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id, |
@@ -1686,6 +1701,7 @@ static void __exit i2c_exit(void) | |||
1686 | class_compat_unregister(i2c_adapter_compat_class); | 1701 | class_compat_unregister(i2c_adapter_compat_class); |
1687 | #endif | 1702 | #endif |
1688 | bus_unregister(&i2c_bus_type); | 1703 | bus_unregister(&i2c_bus_type); |
1704 | tracepoint_synchronize_unregister(); | ||
1689 | } | 1705 | } |
1690 | 1706 | ||
1691 | /* We must initialize early, because some subsystems register i2c drivers | 1707 | /* We must initialize early, because some subsystems register i2c drivers |
@@ -1716,6 +1732,19 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) | |||
1716 | unsigned long orig_jiffies; | 1732 | unsigned long orig_jiffies; |
1717 | int ret, try; | 1733 | int ret, try; |
1718 | 1734 | ||
1735 | /* i2c_trace_msg gets enabled when tracepoint i2c_transfer gets | ||
1736 | * enabled. This is an efficient way of keeping the for-loop from | ||
1737 | * being executed when not needed. | ||
1738 | */ | ||
1739 | if (static_key_false(&i2c_trace_msg)) { | ||
1740 | int i; | ||
1741 | for (i = 0; i < num; i++) | ||
1742 | if (msgs[i].flags & I2C_M_RD) | ||
1743 | trace_i2c_read(adap, &msgs[i], i); | ||
1744 | else | ||
1745 | trace_i2c_write(adap, &msgs[i], i); | ||
1746 | } | ||
1747 | |||
1719 | /* Retry automatically on arbitration loss */ | 1748 | /* Retry automatically on arbitration loss */ |
1720 | orig_jiffies = jiffies; | 1749 | orig_jiffies = jiffies; |
1721 | for (ret = 0, try = 0; try <= adap->retries; try++) { | 1750 | for (ret = 0, try = 0; try <= adap->retries; try++) { |
@@ -1726,6 +1755,14 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) | |||
1726 | break; | 1755 | break; |
1727 | } | 1756 | } |
1728 | 1757 | ||
1758 | if (static_key_false(&i2c_trace_msg)) { | ||
1759 | int i; | ||
1760 | for (i = 0; i < ret; i++) | ||
1761 | if (msgs[i].flags & I2C_M_RD) | ||
1762 | trace_i2c_reply(adap, &msgs[i], i); | ||
1763 | trace_i2c_result(adap, i, ret); | ||
1764 | } | ||
1765 | |||
1729 | return ret; | 1766 | return ret; |
1730 | } | 1767 | } |
1731 | EXPORT_SYMBOL(__i2c_transfer); | 1768 | EXPORT_SYMBOL(__i2c_transfer); |
diff --git a/include/trace/events/i2c.h b/include/trace/events/i2c.h new file mode 100644 index 000000000000..4800207269a2 --- /dev/null +++ b/include/trace/events/i2c.h | |||
@@ -0,0 +1,150 @@ | |||
1 | /* I2C message transfer tracepoints | ||
2 | * | ||
3 | * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | #undef TRACE_SYSTEM | ||
12 | #define TRACE_SYSTEM i2c | ||
13 | |||
14 | #if !defined(_TRACE_I2C_H) || defined(TRACE_HEADER_MULTI_READ) | ||
15 | #define _TRACE_I2C_H | ||
16 | |||
17 | #include <linux/i2c.h> | ||
18 | #include <linux/tracepoint.h> | ||
19 | |||
20 | /* | ||
21 | * drivers/i2c/i2c-core.c | ||
22 | */ | ||
23 | extern void i2c_transfer_trace_reg(void); | ||
24 | extern void i2c_transfer_trace_unreg(void); | ||
25 | |||
26 | /* | ||
27 | * __i2c_transfer() write request | ||
28 | */ | ||
29 | TRACE_EVENT_FN(i2c_write, | ||
30 | TP_PROTO(const struct i2c_adapter *adap, const struct i2c_msg *msg, | ||
31 | int num), | ||
32 | TP_ARGS(adap, msg, num), | ||
33 | TP_STRUCT__entry( | ||
34 | __field(int, adapter_nr ) | ||
35 | __field(__u16, msg_nr ) | ||
36 | __field(__u16, addr ) | ||
37 | __field(__u16, flags ) | ||
38 | __field(__u16, len ) | ||
39 | __dynamic_array(__u8, buf, msg->len) ), | ||
40 | TP_fast_assign( | ||
41 | __entry->adapter_nr = adap->nr; | ||
42 | __entry->msg_nr = num; | ||
43 | __entry->addr = msg->addr; | ||
44 | __entry->flags = msg->flags; | ||
45 | __entry->len = msg->len; | ||
46 | memcpy(__get_dynamic_array(buf), msg->buf, msg->len); | ||
47 | ), | ||
48 | TP_printk("i2c-%d #%u a=%03x f=%04x l=%u [%*phD]", | ||
49 | __entry->adapter_nr, | ||
50 | __entry->msg_nr, | ||
51 | __entry->addr, | ||
52 | __entry->flags, | ||
53 | __entry->len, | ||
54 | __entry->len, __get_dynamic_array(buf) | ||
55 | ), | ||
56 | i2c_transfer_trace_reg, | ||
57 | i2c_transfer_trace_unreg); | ||
58 | |||
59 | /* | ||
60 | * __i2c_transfer() read request | ||
61 | */ | ||
62 | TRACE_EVENT_FN(i2c_read, | ||
63 | TP_PROTO(const struct i2c_adapter *adap, const struct i2c_msg *msg, | ||
64 | int num), | ||
65 | TP_ARGS(adap, msg, num), | ||
66 | TP_STRUCT__entry( | ||
67 | __field(int, adapter_nr ) | ||
68 | __field(__u16, msg_nr ) | ||
69 | __field(__u16, addr ) | ||
70 | __field(__u16, flags ) | ||
71 | __field(__u16, len ) | ||
72 | ), | ||
73 | TP_fast_assign( | ||
74 | __entry->adapter_nr = adap->nr; | ||
75 | __entry->msg_nr = num; | ||
76 | __entry->addr = msg->addr; | ||
77 | __entry->flags = msg->flags; | ||
78 | __entry->len = msg->len; | ||
79 | ), | ||
80 | TP_printk("i2c-%d #%u a=%03x f=%04x l=%u", | ||
81 | __entry->adapter_nr, | ||
82 | __entry->msg_nr, | ||
83 | __entry->addr, | ||
84 | __entry->flags, | ||
85 | __entry->len | ||
86 | ), | ||
87 | i2c_transfer_trace_reg, | ||
88 | i2c_transfer_trace_unreg); | ||
89 | |||
90 | /* | ||
91 | * __i2c_transfer() read reply | ||
92 | */ | ||
93 | TRACE_EVENT_FN(i2c_reply, | ||
94 | TP_PROTO(const struct i2c_adapter *adap, const struct i2c_msg *msg, | ||
95 | int num), | ||
96 | TP_ARGS(adap, msg, num), | ||
97 | TP_STRUCT__entry( | ||
98 | __field(int, adapter_nr ) | ||
99 | __field(__u16, msg_nr ) | ||
100 | __field(__u16, addr ) | ||
101 | __field(__u16, flags ) | ||
102 | __field(__u16, len ) | ||
103 | __dynamic_array(__u8, buf, msg->len) ), | ||
104 | TP_fast_assign( | ||
105 | __entry->adapter_nr = adap->nr; | ||
106 | __entry->msg_nr = num; | ||
107 | __entry->addr = msg->addr; | ||
108 | __entry->flags = msg->flags; | ||
109 | __entry->len = msg->len; | ||
110 | memcpy(__get_dynamic_array(buf), msg->buf, msg->len); | ||
111 | ), | ||
112 | TP_printk("i2c-%d #%u a=%03x f=%04x l=%u [%*phD]", | ||
113 | __entry->adapter_nr, | ||
114 | __entry->msg_nr, | ||
115 | __entry->addr, | ||
116 | __entry->flags, | ||
117 | __entry->len, | ||
118 | __entry->len, __get_dynamic_array(buf) | ||
119 | ), | ||
120 | i2c_transfer_trace_reg, | ||
121 | i2c_transfer_trace_unreg); | ||
122 | |||
123 | /* | ||
124 | * __i2c_transfer() result | ||
125 | */ | ||
126 | TRACE_EVENT_FN(i2c_result, | ||
127 | TP_PROTO(const struct i2c_adapter *adap, int num, int ret), | ||
128 | TP_ARGS(adap, num, ret), | ||
129 | TP_STRUCT__entry( | ||
130 | __field(int, adapter_nr ) | ||
131 | __field(__u16, nr_msgs ) | ||
132 | __field(__s16, ret ) | ||
133 | ), | ||
134 | TP_fast_assign( | ||
135 | __entry->adapter_nr = adap->nr; | ||
136 | __entry->nr_msgs = num; | ||
137 | __entry->ret = ret; | ||
138 | ), | ||
139 | TP_printk("i2c-%d n=%u ret=%d", | ||
140 | __entry->adapter_nr, | ||
141 | __entry->nr_msgs, | ||
142 | __entry->ret | ||
143 | ), | ||
144 | i2c_transfer_trace_reg, | ||
145 | i2c_transfer_trace_unreg); | ||
146 | |||
147 | #endif /* _TRACE_I2C_H */ | ||
148 | |||
149 | /* This part must be outside protection */ | ||
150 | #include <trace/define_trace.h> | ||