a2e1f819574b1e151165f369e8cbb50b047339a4
[lttng-ust.git] / libcounter / shm.c
1 /*
2 * libcounter/shm.c
3 *
4 * Copyright (C) 2005-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; only
9 * version 2.1 of the License.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #define _LGPL_SOURCE
22 #include <config.h>
23 #include "shm.h"
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <sys/mman.h>
27 #include <sys/types.h>
28 #include <sys/stat.h> /* For mode constants */
29 #include <fcntl.h> /* For O_* constants */
30 #include <assert.h>
31 #include <stdio.h>
32 #include <signal.h>
33 #include <dirent.h>
34 #include <lttng/align.h>
35 #include <limits.h>
36 #include <stdbool.h>
37 #include <stdint.h>
38 #ifdef HAVE_LIBNUMA
39 #include <numa.h>
40 #include <numaif.h>
41 #endif
42 #include <helper.h>
43 #include <ust-fd.h>
44 #include "../libringbuffer/mmap.h"
45
46 /*
47 * Ensure we have the required amount of space available by writing 0
48 * into the entire buffer. Not doing so can trigger SIGBUS when going
49 * beyond the available shm space.
50 */
51 static
52 int zero_file(int fd, size_t len)
53 {
54 ssize_t retlen;
55 size_t written = 0;
56 char *zeropage;
57 long pagelen;
58 int ret;
59
60 pagelen = sysconf(_SC_PAGESIZE);
61 if (pagelen < 0)
62 return (int) pagelen;
63 zeropage = calloc(pagelen, 1);
64 if (!zeropage)
65 return -ENOMEM;
66
67 while (len > written) {
68 do {
69 retlen = write(fd, zeropage,
70 min_t(size_t, pagelen, len - written));
71 } while (retlen == -1UL && errno == EINTR);
72 if (retlen < 0) {
73 ret = (int) retlen;
74 goto error;
75 }
76 written += retlen;
77 }
78 ret = 0;
79 error:
80 free(zeropage);
81 return ret;
82 }
83
84 struct lttng_counter_shm_object_table *lttng_counter_shm_object_table_create(size_t max_nb_obj)
85 {
86 struct lttng_counter_shm_object_table *table;
87
88 table = zmalloc(sizeof(struct lttng_counter_shm_object_table) +
89 max_nb_obj * sizeof(table->objects[0]));
90 if (!table)
91 return NULL;
92 table->size = max_nb_obj;
93 return table;
94 }
95
96 static
97 struct lttng_counter_shm_object *_lttng_counter_shm_object_table_alloc_shm(struct lttng_counter_shm_object_table *table,
98 size_t memory_map_size,
99 int cpu_fd)
100 {
101 int shmfd, ret;
102 struct lttng_counter_shm_object *obj;
103 char *memory_map;
104
105 if (cpu_fd < 0)
106 return NULL;
107 if (table->allocated_len >= table->size)
108 return NULL;
109 obj = &table->objects[table->allocated_len];
110
111 /* create shm */
112
113 shmfd = cpu_fd;
114 ret = zero_file(shmfd, memory_map_size);
115 if (ret) {
116 PERROR("zero_file");
117 goto error_zero_file;
118 }
119 ret = ftruncate(shmfd, memory_map_size);
120 if (ret) {
121 PERROR("ftruncate");
122 goto error_ftruncate;
123 }
124 /*
125 * Also ensure the file metadata is synced with the storage by using
126 * fsync(2).
127 */
128 ret = fsync(shmfd);
129 if (ret) {
130 PERROR("fsync");
131 goto error_fsync;
132 }
133 obj->shm_fd_ownership = 0;
134 obj->shm_fd = shmfd;
135
136 /* memory_map: mmap */
137 memory_map = mmap(NULL, memory_map_size, PROT_READ | PROT_WRITE,
138 MAP_SHARED | LTTNG_MAP_POPULATE, shmfd, 0);
139 if (memory_map == MAP_FAILED) {
140 PERROR("mmap");
141 goto error_mmap;
142 }
143 obj->type = LTTNG_COUNTER_SHM_OBJECT_SHM;
144 obj->memory_map = memory_map;
145 obj->memory_map_size = memory_map_size;
146 obj->allocated_len = 0;
147 obj->index = table->allocated_len++;
148
149 return obj;
150
151 error_mmap:
152 error_fsync:
153 error_ftruncate:
154 error_zero_file:
155 return NULL;
156 }
157
158 static
159 struct lttng_counter_shm_object *_lttng_counter_shm_object_table_alloc_mem(struct lttng_counter_shm_object_table *table,
160 size_t memory_map_size)
161 {
162 struct lttng_counter_shm_object *obj;
163 void *memory_map;
164
165 if (table->allocated_len >= table->size)
166 return NULL;
167 obj = &table->objects[table->allocated_len];
168
169 memory_map = zmalloc(memory_map_size);
170 if (!memory_map)
171 goto alloc_error;
172
173 /* no shm_fd */
174 obj->shm_fd = -1;
175 obj->shm_fd_ownership = 0;
176
177 obj->type = LTTNG_COUNTER_SHM_OBJECT_MEM;
178 obj->memory_map = memory_map;
179 obj->memory_map_size = memory_map_size;
180 obj->allocated_len = 0;
181 obj->index = table->allocated_len++;
182
183 return obj;
184
185 alloc_error:
186 return NULL;
187 }
188
189 /*
190 * libnuma prints errors on the console even for numa_available().
191 * Work-around this limitation by using get_mempolicy() directly to
192 * check whether the kernel supports mempolicy.
193 */
194 #ifdef HAVE_LIBNUMA
195 static bool lttng_is_numa_available(void)
196 {
197 int ret;
198
199 ret = get_mempolicy(NULL, NULL, 0, NULL, 0);
200 if (ret && errno == ENOSYS) {
201 return false;
202 }
203 return numa_available() > 0;
204 }
205 #endif
206
207 struct lttng_counter_shm_object *lttng_counter_shm_object_table_alloc(struct lttng_counter_shm_object_table *table,
208 size_t memory_map_size,
209 enum lttng_counter_shm_object_type type,
210 int cpu_fd,
211 int cpu)
212 {
213 struct lttng_counter_shm_object *shm_object;
214 #ifdef HAVE_LIBNUMA
215 int oldnode = 0, node;
216 bool numa_avail;
217
218 numa_avail = lttng_is_numa_available();
219 if (numa_avail) {
220 oldnode = numa_preferred();
221 if (cpu >= 0) {
222 node = numa_node_of_cpu(cpu);
223 if (node >= 0)
224 numa_set_preferred(node);
225 }
226 if (cpu < 0 || node < 0)
227 numa_set_localalloc();
228 }
229 #endif /* HAVE_LIBNUMA */
230 switch (type) {
231 case LTTNG_COUNTER_SHM_OBJECT_SHM:
232 shm_object = _lttng_counter_shm_object_table_alloc_shm(table, memory_map_size,
233 cpu_fd);
234 break;
235 case LTTNG_COUNTER_SHM_OBJECT_MEM:
236 shm_object = _lttng_counter_shm_object_table_alloc_mem(table, memory_map_size);
237 break;
238 default:
239 assert(0);
240 }
241 #ifdef HAVE_LIBNUMA
242 if (numa_avail)
243 numa_set_preferred(oldnode);
244 #endif /* HAVE_LIBNUMA */
245 return shm_object;
246 }
247
248 struct lttng_counter_shm_object *lttng_counter_shm_object_table_append_shm(struct lttng_counter_shm_object_table *table,
249 int shm_fd,
250 size_t memory_map_size)
251 {
252 struct lttng_counter_shm_object *obj;
253 char *memory_map;
254
255 if (table->allocated_len >= table->size)
256 return NULL;
257
258 obj = &table->objects[table->allocated_len];
259
260 obj->shm_fd = shm_fd;
261 obj->shm_fd_ownership = 1;
262
263 /* memory_map: mmap */
264 memory_map = mmap(NULL, memory_map_size, PROT_READ | PROT_WRITE,
265 MAP_SHARED | LTTNG_MAP_POPULATE, shm_fd, 0);
266 if (memory_map == MAP_FAILED) {
267 PERROR("mmap");
268 goto error_mmap;
269 }
270 obj->type = LTTNG_COUNTER_SHM_OBJECT_SHM;
271 obj->memory_map = memory_map;
272 obj->memory_map_size = memory_map_size;
273 obj->allocated_len = memory_map_size;
274 obj->index = table->allocated_len++;
275
276 return obj;
277
278 error_mmap:
279 return NULL;
280 }
281
282 /*
283 * Passing ownership of mem to object.
284 */
285 struct lttng_counter_shm_object *lttng_counter_shm_object_table_append_mem(struct lttng_counter_shm_object_table *table,
286 void *mem, size_t memory_map_size)
287 {
288 struct lttng_counter_shm_object *obj;
289
290 if (table->allocated_len >= table->size)
291 return NULL;
292 obj = &table->objects[table->allocated_len];
293
294 obj->shm_fd = -1;
295 obj->shm_fd_ownership = 0;
296
297 obj->type = LTTNG_COUNTER_SHM_OBJECT_MEM;
298 obj->memory_map = mem;
299 obj->memory_map_size = memory_map_size;
300 obj->allocated_len = memory_map_size;
301 obj->index = table->allocated_len++;
302
303 return obj;
304
305 return NULL;
306 }
307
308 static
309 void lttng_counter_shmp_object_destroy(struct lttng_counter_shm_object *obj, int consumer)
310 {
311 switch (obj->type) {
312 case LTTNG_COUNTER_SHM_OBJECT_SHM:
313 {
314 int ret;
315
316 ret = munmap(obj->memory_map, obj->memory_map_size);
317 if (ret) {
318 PERROR("umnmap");
319 assert(0);
320 }
321
322 if (obj->shm_fd_ownership) {
323 /* Delete FDs only if called from app (not consumer). */
324 if (!consumer) {
325 lttng_ust_lock_fd_tracker();
326 ret = close(obj->shm_fd);
327 if (!ret) {
328 lttng_ust_delete_fd_from_tracker(obj->shm_fd);
329 } else {
330 PERROR("close");
331 assert(0);
332 }
333 lttng_ust_unlock_fd_tracker();
334 } else {
335 ret = close(obj->shm_fd);
336 if (ret) {
337 PERROR("close");
338 assert(0);
339 }
340 }
341 }
342 break;
343 }
344 case LTTNG_COUNTER_SHM_OBJECT_MEM:
345 {
346 free(obj->memory_map);
347 break;
348 }
349 default:
350 assert(0);
351 }
352 }
353
354 void lttng_counter_shm_object_table_destroy(struct lttng_counter_shm_object_table *table, int consumer)
355 {
356 int i;
357
358 for (i = 0; i < table->allocated_len; i++)
359 lttng_counter_shmp_object_destroy(&table->objects[i], consumer);
360 free(table);
361 }
362
363 /*
364 * lttng_counter_zalloc_shm - allocate memory within a shm object.
365 *
366 * Shared memory is already zeroed by shmget.
367 * *NOT* multithread-safe (should be protected by mutex).
368 * Returns a -1, -1 tuple on error.
369 */
370 struct lttng_counter_shm_ref lttng_counter_zalloc_shm(struct lttng_counter_shm_object *obj, size_t len)
371 {
372 struct lttng_counter_shm_ref ref;
373 struct lttng_counter_shm_ref shm_ref_error = { -1, -1 };
374
375 if (obj->memory_map_size - obj->allocated_len < len)
376 return shm_ref_error;
377 ref.index = obj->index;
378 ref.offset = obj->allocated_len;
379 obj->allocated_len += len;
380 return ref;
381 }
382
383 void lttng_counter_align_shm(struct lttng_counter_shm_object *obj, size_t align)
384 {
385 size_t offset_len = offset_align(obj->allocated_len, align);
386 obj->allocated_len += offset_len;
387 }
This page took 0.035158 seconds and 3 git commands to generate.