Commit | Line | Data |
---|---|---|
e0130fab MD |
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> | |
e0130fab MD |
28 | #include <linux/hash.h> |
29 | #include <linux/rcupdate.h> | |
30 | ||
241ae9a8 MD |
31 | #include <wrapper/tracepoint.h> |
32 | #include <wrapper/rcu.h> | |
33 | #include <wrapper/list.h> | |
34 | #include <lttng-events.h> | |
e0130fab MD |
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 | */ | |
6599a4eb | 46 | int lttng_id_tracker_get_node_id(const struct lttng_id_hash_node *node) |
7e6f9ef6 | 47 | { |
6599a4eb | 48 | return node->id; |
7e6f9ef6 | 49 | } |
e0130fab MD |
50 | |
51 | /* | |
52 | * Lookup performed from RCU read-side critical section (RCU sched), | |
53 | * protected by preemption off at the tracepoint call site. | |
6599a4eb | 54 | * Return true if found, false if not found. |
e0130fab | 55 | */ |
6599a4eb | 56 | bool lttng_id_tracker_lookup(struct lttng_id_tracker_rcu *p, int id) |
e0130fab MD |
57 | { |
58 | struct hlist_head *head; | |
6599a4eb MD |
59 | struct lttng_id_hash_node *e; |
60 | uint32_t hash = hash_32(id, 32); | |
e0130fab | 61 | |
6599a4eb | 62 | head = &p->id_hash[hash & (LTTNG_ID_TABLE_SIZE - 1)]; |
7a09dcb7 | 63 | lttng_hlist_for_each_entry_rcu(e, head, hlist) { |
6599a4eb MD |
64 | if (id == e->id) |
65 | return true; /* Found */ | |
e0130fab | 66 | } |
6599a4eb MD |
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; | |
e0130fab | 79 | } |
e0130fab MD |
80 | |
81 | /* | |
82 | * Tracker add and del operations support concurrent RCU lookups. | |
83 | */ | |
6599a4eb | 84 | int lttng_id_tracker_add(struct lttng_id_tracker *lf, int id) |
e0130fab MD |
85 | { |
86 | struct hlist_head *head; | |
6599a4eb MD |
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; | |
e0130fab | 91 | |
6599a4eb MD |
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)]; | |
f934e302 | 99 | lttng_hlist_for_each_entry(e, head, hlist) { |
6599a4eb | 100 | if (id == e->id) |
e0130fab MD |
101 | return -EEXIST; |
102 | } | |
6599a4eb | 103 | e = kmalloc(sizeof(struct lttng_id_hash_node), GFP_KERNEL); |
e0130fab MD |
104 | if (!e) |
105 | return -ENOMEM; | |
6599a4eb | 106 | e->id = id; |
e0130fab | 107 | hlist_add_head_rcu(&e->hlist, head); |
6599a4eb MD |
108 | if (allocated) { |
109 | rcu_assign_pointer(lf->p, p); | |
110 | } | |
e0130fab MD |
111 | return 0; |
112 | } | |
113 | ||
114 | static | |
6599a4eb | 115 | void id_tracker_del_node_rcu(struct lttng_id_hash_node *e) |
e0130fab MD |
116 | { |
117 | hlist_del_rcu(&e->hlist); | |
118 | /* | |
119 | * We choose to use a heavyweight synchronize on removal here, | |
6599a4eb | 120 | * since removal of an ID from the tracker mask is a rare |
e0130fab | 121 | * operation, and we don't want to use more cache lines than |
6599a4eb | 122 | * what we really need when doing the ID lookups, so we don't |
e0130fab MD |
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 | |
6599a4eb | 135 | void id_tracker_del_node(struct lttng_id_hash_node *e) |
e0130fab MD |
136 | { |
137 | hlist_del(&e->hlist); | |
138 | kfree(e); | |
139 | } | |
140 | ||
6599a4eb | 141 | int lttng_id_tracker_del(struct lttng_id_tracker *lf, int id) |
e0130fab MD |
142 | { |
143 | struct hlist_head *head; | |
6599a4eb MD |
144 | struct lttng_id_hash_node *e; |
145 | struct lttng_id_tracker_rcu *p = lf->p; | |
146 | uint32_t hash = hash_32(id, 32); | |
e0130fab | 147 | |
6599a4eb MD |
148 | if (!p) |
149 | return -ENOENT; | |
150 | head = &p->id_hash[hash & (LTTNG_ID_TABLE_SIZE - 1)]; | |
e0130fab MD |
151 | /* |
152 | * No need of _safe iteration, because we stop traversal as soon | |
153 | * as we remove the entry. | |
154 | */ | |
f934e302 | 155 | lttng_hlist_for_each_entry(e, head, hlist) { |
6599a4eb MD |
156 | if (id == e->id) { |
157 | id_tracker_del_node_rcu(e); | |
e0130fab MD |
158 | return 0; |
159 | } | |
160 | } | |
161 | return -ENOENT; /* Not found */ | |
162 | } | |
163 | ||
6599a4eb | 164 | static void lttng_id_tracker_rcu_destroy(struct lttng_id_tracker_rcu *p) |
e0130fab MD |
165 | { |
166 | int i; | |
167 | ||
6599a4eb MD |
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; | |
e0130fab MD |
173 | struct hlist_node *tmp; |
174 | ||
f934e302 | 175 | lttng_hlist_for_each_entry_safe(e, tmp, head, hlist) |
6599a4eb | 176 | id_tracker_del_node(e); |
e0130fab | 177 | } |
6599a4eb MD |
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); | |
e0130fab | 205 | } |