Fixes bug in cpu_relax_vc()
[akaros.git] / kern / include / trace.h
1 /* Copyright (c) 2013 The Regents of the University of California
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * Simple ring-buffer tracing for in-kernel events.  The rings have a
6  * power-of-two number of slots, and each entry size will be rounded up to the
7  * nearest power of two.  Ring slot acquisition by default is thread-safe, but
8  * we provide racy helpers if you want a little less overhead.
9  *
10  * Users need to provide a contiguous memory buffer and the size of an event
11  * struct to init.  For example:
12  *
13  *              trace_ring_init(my_trace_ring_ptr, my_buf, buf_sz, event_sz);
14  *
15  * And then to store a trace, first get a slot, then fill it in:
16  *
17  *              struct my_trace_event *my_trace = get_trace_slot(my_trace_ring_ptr);
18  *              if (my_trace)   // only need to check if we aren't overwriting
19  *                      my_trace = whatever;
20  *
21  * Later, to process the traces, provide a function pointer to
22  * trace_ring_foreach().  This performs the func on all traces in the ring,
23  * including the unused:
24  *
25  *              void trace_handler(void *trace_event, void *data)
26  *              {
27  *                      whatever();
28  *              }
29  *              trace_ring_foreach(my_trace_ring_ptr, trace_handler, optional_blob);
30  *
31  * Rings can be racy or not, and can overwrite entries or not.  If you are not
32  * overwriting, the ring will stop giving you slots.  You need to reset the ring
33  * to get fresh slots again.  If you are overwriting, you don't need to check
34  * the return value of get_trace_slot_overwrite().
35  *
36  * Given there is overwrite, tr_next doesn't really tell us which ones were
37  * used.  So your handler should check for a flag or something.  Timestamps
38  * might help make sense of the data in these cases too. */
39
40 #ifndef ROS_INC_TRACE_H
41 #define ROS_INC_TRACE_H
42
43 #include <ros/common.h>
44
45 struct trace_ring {
46         void                                            *tr_buf;
47         size_t                                          tr_buf_sz;
48         unsigned int                            tr_event_sz_shift;
49         unsigned int                            tr_max;
50         unsigned long                           tr_next;
51 };
52
53 typedef void (*trace_handler_t)(void *event, void *blob);
54
55 static inline void *get_trace_slot(struct trace_ring *tr);
56 static inline void *get_trace_slot_overwrite(struct trace_ring *tr);
57 static inline void *get_trace_slot_racy(struct trace_ring *tr);
58 static inline void *get_trace_slot_overwrite_racy(struct trace_ring *tr);
59
60 void trace_ring_init(struct trace_ring *tr, void *buf, size_t buf_size,
61                      size_t event_size);
62 void trace_ring_reset(struct trace_ring *tr);
63 void trace_ring_reset_and_clear(struct trace_ring *tr);
64 void trace_ring_foreach(struct trace_ring *tr, trace_handler_t tr_func,
65                         void *data);
66
67 /* Inlined funcs, declared above */
68
69 /* Helper */
70 static inline void *__get_tr_slot(struct trace_ring *tr, unsigned long slot)
71 {
72         /* tr_max is a power of 2, we're ignoring the upper bits of my_slot */
73         slot &= tr->tr_max - 1;
74         /* event sizes are rounded up to the nearest power of 2 (sz_shift) */
75         return tr->tr_buf + (slot << tr->tr_event_sz_shift);
76 }
77
78 static inline void *get_trace_slot(struct trace_ring *tr)
79 {
80         /* Using syncs, instead of atomics, since we access tr_next as both atomic
81          * and 'normal'. */
82         unsigned long my_slot = __sync_fetch_and_add(&tr->tr_next, 1);
83         /* We can briefly go over, so long as we subtract back down to where we were
84          * before.  This will work so long as we don't have ~2^64 threads... */
85         if (my_slot >= tr->tr_max) {
86                 __sync_fetch_and_add(&tr->tr_next, -1);
87                 return 0;
88         }
89         return __get_tr_slot(tr, my_slot);
90 }
91
92 static inline void *get_trace_slot_overwrite(struct trace_ring *tr)
93 {
94         return __get_tr_slot(tr, __sync_fetch_and_add(&tr->tr_next, 1));
95 }
96
97 static inline void *get_trace_slot_racy(struct trace_ring *tr)
98 {
99         unsigned long my_slot = tr->tr_next;
100         if (my_slot >= tr->tr_max)
101                 return 0;
102         tr->tr_next++;
103         return __get_tr_slot(tr, my_slot);
104 }
105
106 static inline void *get_trace_slot_overwrite_racy(struct trace_ring *tr)
107 {
108         return __get_tr_slot(tr, tr->tr_next++);
109 }
110
111 #endif /* ROS_INC_TRACE_H */