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