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