Introduce file descriptor tracker
[lttng-ust.git] / libringbuffer / shm.c
CommitLineData
1d498196
MD
1/*
2 * libringbuffer/shm.c
3 *
e92f3e28 4 * Copyright (C) 2005-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
1d498196 5 *
e92f3e28
MD
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
1d498196
MD
19 */
20
3fbec7dc 21#define _LGPL_SOURCE
1d498196
MD
22#include "shm.h"
23#include <unistd.h>
24#include <fcntl.h>
25#include <sys/mman.h>
a9ff648c 26#include <sys/types.h>
1d498196
MD
27#include <sys/stat.h> /* For mode constants */
28#include <fcntl.h> /* For O_* constants */
29#include <assert.h>
8da6cd6d
MD
30#include <stdio.h>
31#include <signal.h>
32#include <dirent.h>
4318ae1b 33#include <lttng/align.h>
96e80018 34#include <limits.h>
3a81f31d 35#include <helper.h>
6548fca4 36#include <ust-fd.h>
3a81f31d
MD
37
38/*
39 * Ensure we have the required amount of space available by writing 0
40 * into the entire buffer. Not doing so can trigger SIGBUS when going
41 * beyond the available shm space.
42 */
43static
44int zero_file(int fd, size_t len)
45{
46 ssize_t retlen;
47 size_t written = 0;
48 char *zeropage;
49 long pagelen;
50 int ret;
51
52 pagelen = sysconf(_SC_PAGESIZE);
53 if (pagelen < 0)
54 return (int) pagelen;
55 zeropage = calloc(pagelen, 1);
56 if (!zeropage)
57 return -ENOMEM;
58
59 while (len > written) {
60 do {
61 retlen = write(fd, zeropage,
62 min_t(size_t, pagelen, len - written));
63 } while (retlen == -1UL && errno == EINTR);
64 if (retlen < 0) {
65 ret = (int) retlen;
66 goto error;
67 }
68 written += retlen;
69 }
70 ret = 0;
71error:
72 free(zeropage);
73 return ret;
74}
1d498196
MD
75
76struct shm_object_table *shm_object_table_create(size_t max_nb_obj)
77{
78 struct shm_object_table *table;
79
80 table = zmalloc(sizeof(struct shm_object_table) +
81 max_nb_obj * sizeof(table->objects[0]));
74d48abe
MD
82 if (!table)
83 return NULL;
1d498196
MD
84 table->size = max_nb_obj;
85 return table;
86}
87
74d81a6c
MD
88static
89struct shm_object *_shm_object_table_alloc_shm(struct shm_object_table *table,
a9ff648c 90 size_t memory_map_size,
5ea386c3 91 int stream_fd)
1d498196 92{
5ea386c3 93 int shmfd, waitfd[2], ret, i;
1d498196
MD
94 struct shm_object *obj;
95 char *memory_map;
96
5ea386c3
MD
97 if (stream_fd < 0)
98 return NULL;
1d498196
MD
99 if (table->allocated_len >= table->size)
100 return NULL;
7a9c21bd 101 obj = &table->objects[table->allocated_len];
1d498196
MD
102
103 /* wait_fd: create pipe */
104 ret = pipe(waitfd);
105 if (ret < 0) {
106 PERROR("pipe");
107 goto error_pipe;
108 }
109 for (i = 0; i < 2; i++) {
110 ret = fcntl(waitfd[i], F_SETFD, FD_CLOEXEC);
111 if (ret < 0) {
112 PERROR("fcntl");
113 goto error_fcntl;
114 }
115 }
5d61a504
MD
116 /* The write end of the pipe needs to be non-blocking */
117 ret = fcntl(waitfd[1], F_SETFL, O_NONBLOCK);
118 if (ret < 0) {
119 PERROR("fcntl");
120 goto error_fcntl;
121 }
7a9c21bd 122 memcpy(obj->wait_fd, waitfd, sizeof(waitfd));
1d498196 123
5ea386c3 124 /* create shm */
a9ff648c 125
5ea386c3 126 shmfd = stream_fd;
3a81f31d
MD
127 ret = zero_file(shmfd, memory_map_size);
128 if (ret) {
129 PERROR("zero_file");
130 goto error_zero_file;
131 }
1d498196
MD
132 ret = ftruncate(shmfd, memory_map_size);
133 if (ret) {
134 PERROR("ftruncate");
135 goto error_ftruncate;
136 }
5ea386c3 137 obj->shm_fd_ownership = 0;
1d498196
MD
138 obj->shm_fd = shmfd;
139
140 /* memory_map: mmap */
141 memory_map = mmap(NULL, memory_map_size, PROT_READ | PROT_WRITE,
142 MAP_SHARED, shmfd, 0);
143 if (memory_map == MAP_FAILED) {
144 PERROR("mmap");
145 goto error_mmap;
146 }
74d81a6c 147 obj->type = SHM_OBJECT_SHM;
1d498196
MD
148 obj->memory_map = memory_map;
149 obj->memory_map_size = memory_map_size;
150 obj->allocated_len = 0;
dc613eb9 151 obj->index = table->allocated_len++;
7a9c21bd 152
1d498196
MD
153 return obj;
154
155error_mmap:
156error_ftruncate:
3a81f31d 157error_zero_file:
1d498196
MD
158error_fcntl:
159 for (i = 0; i < 2; i++) {
160 ret = close(waitfd[i]);
161 if (ret) {
162 PERROR("close");
163 assert(0);
164 }
165 }
166error_pipe:
1d498196 167 return NULL;
1d498196
MD
168}
169
74d81a6c
MD
170static
171struct shm_object *_shm_object_table_alloc_mem(struct shm_object_table *table,
172 size_t memory_map_size)
173{
174 struct shm_object *obj;
175 void *memory_map;
ff0f5728 176 int waitfd[2], i, ret;
74d81a6c
MD
177
178 if (table->allocated_len >= table->size)
179 return NULL;
180 obj = &table->objects[table->allocated_len];
181
182 memory_map = zmalloc(memory_map_size);
183 if (!memory_map)
184 goto alloc_error;
185
ff0f5728
MD
186 /* wait_fd: create pipe */
187 ret = pipe(waitfd);
188 if (ret < 0) {
189 PERROR("pipe");
190 goto error_pipe;
191 }
192 for (i = 0; i < 2; i++) {
193 ret = fcntl(waitfd[i], F_SETFD, FD_CLOEXEC);
194 if (ret < 0) {
195 PERROR("fcntl");
196 goto error_fcntl;
197 }
198 }
199 /* The write end of the pipe needs to be non-blocking */
200 ret = fcntl(waitfd[1], F_SETFL, O_NONBLOCK);
201 if (ret < 0) {
202 PERROR("fcntl");
203 goto error_fcntl;
204 }
205 memcpy(obj->wait_fd, waitfd, sizeof(waitfd));
206
207 /* no shm_fd */
74d81a6c 208 obj->shm_fd = -1;
5ea386c3 209 obj->shm_fd_ownership = 0;
74d81a6c
MD
210
211 obj->type = SHM_OBJECT_MEM;
212 obj->memory_map = memory_map;
213 obj->memory_map_size = memory_map_size;
214 obj->allocated_len = 0;
215 obj->index = table->allocated_len++;
216
217 return obj;
218
ff0f5728
MD
219error_fcntl:
220 for (i = 0; i < 2; i++) {
221 ret = close(waitfd[i]);
222 if (ret) {
223 PERROR("close");
224 assert(0);
225 }
226 }
227error_pipe:
228 free(memory_map);
74d81a6c
MD
229alloc_error:
230 return NULL;
231}
232
233struct shm_object *shm_object_table_alloc(struct shm_object_table *table,
234 size_t memory_map_size,
a9ff648c 235 enum shm_object_type type,
5ea386c3 236 int stream_fd)
74d81a6c
MD
237{
238 switch (type) {
239 case SHM_OBJECT_SHM:
a9ff648c 240 return _shm_object_table_alloc_shm(table, memory_map_size,
5ea386c3 241 stream_fd);
74d81a6c
MD
242 case SHM_OBJECT_MEM:
243 return _shm_object_table_alloc_mem(table, memory_map_size);
244 default:
245 assert(0);
246 }
247 return NULL;
248}
249
250struct shm_object *shm_object_table_append_shm(struct shm_object_table *table,
251 int shm_fd, int wakeup_fd, uint32_t stream_nr,
252 size_t memory_map_size)
193183fb
MD
253{
254 struct shm_object *obj;
255 char *memory_map;
74d81a6c 256 int ret;
193183fb
MD
257
258 if (table->allocated_len >= table->size)
259 return NULL;
74d81a6c
MD
260 /* streams _must_ be received in sequential order, else fail. */
261 if (stream_nr + 1 != table->allocated_len)
262 return NULL;
263
193183fb
MD
264 obj = &table->objects[table->allocated_len];
265
74d81a6c
MD
266 /* wait_fd: set write end of the pipe. */
267 obj->wait_fd[0] = -1; /* read end is unset */
268 obj->wait_fd[1] = wakeup_fd;
193183fb 269 obj->shm_fd = shm_fd;
5ea386c3 270 obj->shm_fd_ownership = 1;
193183fb 271
74d81a6c
MD
272 ret = fcntl(obj->wait_fd[1], F_SETFD, FD_CLOEXEC);
273 if (ret < 0) {
274 PERROR("fcntl");
275 goto error_fcntl;
276 }
277 /* The write end of the pipe needs to be non-blocking */
278 ret = fcntl(obj->wait_fd[1], F_SETFL, O_NONBLOCK);
279 if (ret < 0) {
280 PERROR("fcntl");
281 goto error_fcntl;
282 }
283
193183fb
MD
284 /* memory_map: mmap */
285 memory_map = mmap(NULL, memory_map_size, PROT_READ | PROT_WRITE,
286 MAP_SHARED, shm_fd, 0);
287 if (memory_map == MAP_FAILED) {
288 PERROR("mmap");
289 goto error_mmap;
290 }
74d81a6c 291 obj->type = SHM_OBJECT_SHM;
193183fb
MD
292 obj->memory_map = memory_map;
293 obj->memory_map_size = memory_map_size;
294 obj->allocated_len = memory_map_size;
295 obj->index = table->allocated_len++;
296
297 return obj;
298
74d81a6c 299error_fcntl:
193183fb
MD
300error_mmap:
301 return NULL;
302}
303
74d81a6c
MD
304/*
305 * Passing ownership of mem to object.
306 */
307struct shm_object *shm_object_table_append_mem(struct shm_object_table *table,
ff0f5728 308 void *mem, size_t memory_map_size, int wakeup_fd)
74d81a6c
MD
309{
310 struct shm_object *obj;
ff0f5728 311 int ret;
74d81a6c
MD
312
313 if (table->allocated_len >= table->size)
314 return NULL;
315 obj = &table->objects[table->allocated_len];
316
ff0f5728
MD
317 obj->wait_fd[0] = -1; /* read end is unset */
318 obj->wait_fd[1] = wakeup_fd;
74d81a6c 319 obj->shm_fd = -1;
5ea386c3 320 obj->shm_fd_ownership = 0;
74d81a6c 321
ff0f5728
MD
322 ret = fcntl(obj->wait_fd[1], F_SETFD, FD_CLOEXEC);
323 if (ret < 0) {
324 PERROR("fcntl");
325 goto error_fcntl;
326 }
327 /* The write end of the pipe needs to be non-blocking */
328 ret = fcntl(obj->wait_fd[1], F_SETFL, O_NONBLOCK);
329 if (ret < 0) {
330 PERROR("fcntl");
331 goto error_fcntl;
332 }
333
74d81a6c
MD
334 obj->type = SHM_OBJECT_MEM;
335 obj->memory_map = mem;
336 obj->memory_map_size = memory_map_size;
337 obj->allocated_len = memory_map_size;
338 obj->index = table->allocated_len++;
339
340 return obj;
ff0f5728
MD
341
342error_fcntl:
343 return NULL;
74d81a6c
MD
344}
345
1d498196 346static
6548fca4 347void shmp_object_destroy(struct shm_object *obj, int consumer)
1d498196 348{
74d81a6c
MD
349 switch (obj->type) {
350 case SHM_OBJECT_SHM:
351 {
352 int ret, i;
1d498196 353
7a784989
MD
354 ret = munmap(obj->memory_map, obj->memory_map_size);
355 if (ret) {
356 PERROR("umnmap");
357 assert(0);
358 }
6548fca4 359
5ea386c3 360 if (obj->shm_fd_ownership) {
6548fca4
MD
361 /* Delete FDs only if called from app (not consumer). */
362 if (!consumer) {
363 lttng_ust_lock_fd_tracker();
364 ret = close(obj->shm_fd);
365 if (!ret) {
366 lttng_ust_delete_fd_from_tracker(obj->shm_fd);
367 } else {
368 PERROR("close");
369 assert(0);
370 }
371 lttng_ust_unlock_fd_tracker();
372 } else {
373 ret = close(obj->shm_fd);
374 if (ret) {
375 PERROR("close");
376 assert(0);
377 }
a9ff648c
MD
378 }
379 }
74d81a6c
MD
380 for (i = 0; i < 2; i++) {
381 if (obj->wait_fd[i] < 0)
382 continue;
6548fca4
MD
383 if (!consumer) {
384 lttng_ust_lock_fd_tracker();
385 ret = close(obj->wait_fd[i]);
386 if (!ret) {
387 lttng_ust_delete_fd_from_tracker(obj->wait_fd[i]);
388 } else {
389 PERROR("close");
390 assert(0);
391 }
392 lttng_ust_unlock_fd_tracker();
393 } else {
394 ret = close(obj->wait_fd[i]);
395 if (ret) {
396 PERROR("close");
397 assert(0);
398 }
74d81a6c 399 }
1d498196 400 }
74d81a6c
MD
401 break;
402 }
403 case SHM_OBJECT_MEM:
ff0f5728
MD
404 {
405 int ret, i;
406
407 for (i = 0; i < 2; i++) {
408 if (obj->wait_fd[i] < 0)
409 continue;
6548fca4
MD
410 if (!consumer) {
411 lttng_ust_lock_fd_tracker();
412 ret = close(obj->wait_fd[i]);
413 if (!ret) {
414 lttng_ust_delete_fd_from_tracker(obj->wait_fd[i]);
415 } else {
416 PERROR("close");
417 assert(0);
418 }
419 lttng_ust_unlock_fd_tracker();
420 } else {
421 ret = close(obj->wait_fd[i]);
422 if (ret) {
423 PERROR("close");
424 assert(0);
425 }
ff0f5728
MD
426 }
427 }
74d81a6c
MD
428 free(obj->memory_map);
429 break;
ff0f5728 430 }
74d81a6c
MD
431 default:
432 assert(0);
1d498196
MD
433 }
434}
435
6548fca4 436void shm_object_table_destroy(struct shm_object_table *table, int consumer)
1d498196
MD
437{
438 int i;
439
440 for (i = 0; i < table->allocated_len; i++)
6548fca4 441 shmp_object_destroy(&table->objects[i], consumer);
1d498196
MD
442 free(table);
443}
444
445/*
446 * zalloc_shm - allocate memory within a shm object.
447 *
448 * Shared memory is already zeroed by shmget.
449 * *NOT* multithread-safe (should be protected by mutex).
450 * Returns a -1, -1 tuple on error.
451 */
452struct shm_ref zalloc_shm(struct shm_object *obj, size_t len)
453{
454 struct shm_ref ref;
455 struct shm_ref shm_ref_error = { -1, -1 };
456
457 if (obj->memory_map_size - obj->allocated_len < len)
458 return shm_ref_error;
459 ref.index = obj->index;
460 ref.offset = obj->allocated_len;
461 obj->allocated_len += len;
462 return ref;
463}
464
465void align_shm(struct shm_object *obj, size_t align)
466{
467 size_t offset_len = offset_align(obj->allocated_len, align);
468 obj->allocated_len += offset_len;
469}
This page took 0.0646 seconds and 4 git commands to generate.