Add extra version information framework
[deliverable/lttng-modules.git] / lttng-tracker-id.c
1 /*
2 * lttng-tracker-pid.c
3 *
4 * LTTng Process ID trackering.
5 *
6 * Copyright (C) 2014 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; only
11 * version 2.1 of the License.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include <linux/module.h>
24 #include <linux/slab.h>
25 #include <linux/err.h>
26 #include <linux/seq_file.h>
27 #include <linux/stringify.h>
28 #include <linux/hash.h>
29 #include <linux/rcupdate.h>
30
31 #include <wrapper/tracepoint.h>
32 #include <wrapper/rcu.h>
33 #include <wrapper/list.h>
34 #include <lttng-events.h>
35
36 /*
37 * Hash table is allocated and freed when there are no possible
38 * concurrent lookups (ensured by the alloc/free caller). However,
39 * there can be concurrent RCU lookups vs add/del operations.
40 *
41 * Concurrent updates of the PID hash table are forbidden: the caller
42 * must ensure mutual exclusion. This is currently done by holding the
43 * sessions_mutex across calls to create, destroy, add, and del
44 * functions of this API.
45 */
46 int lttng_id_tracker_get_node_id(const struct lttng_id_hash_node *node)
47 {
48 return node->id;
49 }
50
51 /*
52 * Lookup performed from RCU read-side critical section (RCU sched),
53 * protected by preemption off at the tracepoint call site.
54 * Return true if found, false if not found.
55 */
56 bool lttng_id_tracker_lookup(struct lttng_id_tracker_rcu *p, int id)
57 {
58 struct hlist_head *head;
59 struct lttng_id_hash_node *e;
60 uint32_t hash = hash_32(id, 32);
61
62 head = &p->id_hash[hash & (LTTNG_ID_TABLE_SIZE - 1)];
63 lttng_hlist_for_each_entry_rcu(e, head, hlist) {
64 if (id == e->id)
65 return true; /* Found */
66 }
67 return false;
68 }
69 EXPORT_SYMBOL_GPL(lttng_id_tracker_lookup);
70
71 static struct lttng_id_tracker_rcu *lttng_id_tracker_rcu_create(void)
72 {
73 struct lttng_id_tracker_rcu *tracker;
74
75 tracker = kzalloc(sizeof(struct lttng_id_tracker_rcu), GFP_KERNEL);
76 if (!tracker)
77 return NULL;
78 return tracker;
79 }
80
81 /*
82 * Tracker add and del operations support concurrent RCU lookups.
83 */
84 int lttng_id_tracker_add(struct lttng_id_tracker *lf, int id)
85 {
86 struct hlist_head *head;
87 struct lttng_id_hash_node *e;
88 struct lttng_id_tracker_rcu *p = lf->p;
89 uint32_t hash = hash_32(id, 32);
90 bool allocated = false;
91
92 if (!p) {
93 p = lttng_id_tracker_rcu_create();
94 if (!p)
95 return -ENOMEM;
96 allocated = true;
97 }
98 head = &p->id_hash[hash & (LTTNG_ID_TABLE_SIZE - 1)];
99 lttng_hlist_for_each_entry(e, head, hlist) {
100 if (id == e->id)
101 return -EEXIST;
102 }
103 e = kmalloc(sizeof(struct lttng_id_hash_node), GFP_KERNEL);
104 if (!e)
105 return -ENOMEM;
106 e->id = id;
107 hlist_add_head_rcu(&e->hlist, head);
108 if (allocated) {
109 rcu_assign_pointer(lf->p, p);
110 }
111 return 0;
112 }
113
114 static
115 void id_tracker_del_node_rcu(struct lttng_id_hash_node *e)
116 {
117 hlist_del_rcu(&e->hlist);
118 /*
119 * We choose to use a heavyweight synchronize on removal here,
120 * since removal of an ID from the tracker mask is a rare
121 * operation, and we don't want to use more cache lines than
122 * what we really need when doing the ID lookups, so we don't
123 * want to afford adding a rcu_head field to those pid hash
124 * node.
125 */
126 synchronize_trace();
127 kfree(e);
128 }
129
130 /*
131 * This removal is only used on destroy, so it does not need to support
132 * concurrent RCU lookups.
133 */
134 static
135 void id_tracker_del_node(struct lttng_id_hash_node *e)
136 {
137 hlist_del(&e->hlist);
138 kfree(e);
139 }
140
141 int lttng_id_tracker_del(struct lttng_id_tracker *lf, int id)
142 {
143 struct hlist_head *head;
144 struct lttng_id_hash_node *e;
145 struct lttng_id_tracker_rcu *p = lf->p;
146 uint32_t hash = hash_32(id, 32);
147
148 if (!p)
149 return -ENOENT;
150 head = &p->id_hash[hash & (LTTNG_ID_TABLE_SIZE - 1)];
151 /*
152 * No need of _safe iteration, because we stop traversal as soon
153 * as we remove the entry.
154 */
155 lttng_hlist_for_each_entry(e, head, hlist) {
156 if (id == e->id) {
157 id_tracker_del_node_rcu(e);
158 return 0;
159 }
160 }
161 return -ENOENT; /* Not found */
162 }
163
164 static void lttng_id_tracker_rcu_destroy(struct lttng_id_tracker_rcu *p)
165 {
166 int i;
167
168 if (!p)
169 return;
170 for (i = 0; i < LTTNG_ID_TABLE_SIZE; i++) {
171 struct hlist_head *head = &p->id_hash[i];
172 struct lttng_id_hash_node *e;
173 struct hlist_node *tmp;
174
175 lttng_hlist_for_each_entry_safe(e, tmp, head, hlist)
176 id_tracker_del_node(e);
177 }
178 kfree(p);
179 }
180
181 int lttng_id_tracker_empty_set(struct lttng_id_tracker *lf)
182 {
183 struct lttng_id_tracker_rcu *p, *oldp;
184
185 p = lttng_id_tracker_rcu_create();
186 if (!p)
187 return -ENOMEM;
188 oldp = lf->p;
189 rcu_assign_pointer(lf->p, p);
190 synchronize_trace();
191 lttng_id_tracker_rcu_destroy(oldp);
192 return 0;
193 }
194
195 void lttng_id_tracker_destroy(struct lttng_id_tracker *lf, bool rcu)
196 {
197 struct lttng_id_tracker_rcu *p = lf->p;
198
199 if (!lf->p)
200 return;
201 rcu_assign_pointer(lf->p, NULL);
202 if (rcu)
203 synchronize_trace();
204 lttng_id_tracker_rcu_destroy(p);
205 }
This page took 0.039937 seconds and 5 git commands to generate.