Implement file-backed ring buffer
[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
21#include "shm.h"
22#include <unistd.h>
23#include <fcntl.h>
24#include <sys/mman.h>
a9ff648c 25#include <sys/types.h>
1d498196
MD
26#include <sys/stat.h> /* For mode constants */
27#include <fcntl.h> /* For O_* constants */
28#include <assert.h>
8da6cd6d
MD
29#include <stdio.h>
30#include <signal.h>
31#include <dirent.h>
4318ae1b 32#include <lttng/align.h>
96e80018 33#include <limits.h>
3a81f31d
MD
34#include <helper.h>
35
36/*
37 * Ensure we have the required amount of space available by writing 0
38 * into the entire buffer. Not doing so can trigger SIGBUS when going
39 * beyond the available shm space.
40 */
41static
42int zero_file(int fd, size_t len)
43{
44 ssize_t retlen;
45 size_t written = 0;
46 char *zeropage;
47 long pagelen;
48 int ret;
49
50 pagelen = sysconf(_SC_PAGESIZE);
51 if (pagelen < 0)
52 return (int) pagelen;
53 zeropage = calloc(pagelen, 1);
54 if (!zeropage)
55 return -ENOMEM;
56
57 while (len > written) {
58 do {
59 retlen = write(fd, zeropage,
60 min_t(size_t, pagelen, len - written));
61 } while (retlen == -1UL && errno == EINTR);
62 if (retlen < 0) {
63 ret = (int) retlen;
64 goto error;
65 }
66 written += retlen;
67 }
68 ret = 0;
69error:
70 free(zeropage);
71 return ret;
72}
1d498196
MD
73
74struct shm_object_table *shm_object_table_create(size_t max_nb_obj)
75{
76 struct shm_object_table *table;
77
78 table = zmalloc(sizeof(struct shm_object_table) +
79 max_nb_obj * sizeof(table->objects[0]));
74d48abe
MD
80 if (!table)
81 return NULL;
1d498196
MD
82 table->size = max_nb_obj;
83 return table;
84}
85
a9ff648c
MD
86static
87int create_posix_shm(void)
88{
89 char tmp_name[NAME_MAX] = "/ust-shm-tmp-XXXXXX";
90 int shmfd, ret;
91
92 /*
93 * Allocate shm, and immediately unlink its shm oject, keeping
94 * only the file descriptor as a reference to the object. If it
95 * already exists (caused by short race window during which the
96 * global object exists in a concurrent shm_open), simply retry.
97 * We specifically do _not_ use the / at the beginning of the
98 * pathname so that some OS implementations can keep it local to
99 * the process (POSIX leaves this implementation-defined).
100 */
101 do {
102 /*
103 * Using mktemp filename with O_CREAT | O_EXCL open
104 * flags.
105 */
106 (void) mktemp(tmp_name);
107 if (tmp_name[0] == '\0') {
108 PERROR("mktemp");
109 goto error_shm_open;
110 }
111 shmfd = shm_open(tmp_name,
112 O_CREAT | O_EXCL | O_RDWR, 0700);
113 } while (shmfd < 0 && (errno == EEXIST || errno == EACCES));
114 if (shmfd < 0) {
115 PERROR("shm_open");
116 goto error_shm_open;
117 }
118 ret = shm_unlink(tmp_name);
119 if (ret < 0 && errno != ENOENT) {
120 PERROR("shm_unlink");
121 goto error_shm_release;
122 }
123 return shmfd;
124
125error_shm_release:
126 ret = close(shmfd);
127 if (ret) {
128 PERROR("close");
129 assert(0);
130 }
131error_shm_open:
132 return -1;
133}
134
135static
136int create_shared_file(const char *shm_path)
137{
138 return open(shm_path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
139}
140
74d81a6c
MD
141static
142struct shm_object *_shm_object_table_alloc_shm(struct shm_object_table *table,
a9ff648c
MD
143 size_t memory_map_size,
144 const char *shm_path)
1d498196 145{
5a61337d 146 int shmfd, waitfd[2], ret, i, sigblocked = 0;
1d498196
MD
147 struct shm_object *obj;
148 char *memory_map;
8da6cd6d 149 sigset_t all_sigs, orig_sigs;
1d498196
MD
150
151 if (table->allocated_len >= table->size)
152 return NULL;
7a9c21bd 153 obj = &table->objects[table->allocated_len];
1d498196
MD
154
155 /* wait_fd: create pipe */
156 ret = pipe(waitfd);
157 if (ret < 0) {
158 PERROR("pipe");
159 goto error_pipe;
160 }
161 for (i = 0; i < 2; i++) {
162 ret = fcntl(waitfd[i], F_SETFD, FD_CLOEXEC);
163 if (ret < 0) {
164 PERROR("fcntl");
165 goto error_fcntl;
166 }
167 }
5d61a504
MD
168 /* The write end of the pipe needs to be non-blocking */
169 ret = fcntl(waitfd[1], F_SETFL, O_NONBLOCK);
170 if (ret < 0) {
171 PERROR("fcntl");
172 goto error_fcntl;
173 }
7a9c21bd 174 memcpy(obj->wait_fd, waitfd, sizeof(waitfd));
1d498196
MD
175
176 /* shm_fd: create shm */
177
8da6cd6d
MD
178 /*
179 * Theoretically, we could leak a shm if the application crashes
180 * between open and unlink. Disable signals on this thread for
181 * increased safety against this scenario.
182 */
183 sigfillset(&all_sigs);
184 ret = pthread_sigmask(SIG_BLOCK, &all_sigs, &orig_sigs);
185 if (ret == -1) {
186 PERROR("pthread_sigmask");
187 goto error_pthread_sigmask;
188 }
5a61337d 189 sigblocked = 1;
8da6cd6d 190
a9ff648c
MD
191
192 if (!shm_path) {
193 obj->shm_path[0] = '\0';
194 shmfd = create_posix_shm();
195 } else {
196 strncpy(obj->shm_path, shm_path,
197 sizeof(obj->shm_path));
198 obj->shm_path[sizeof(obj->shm_path) - 1] = '\0';
199
200 /* Path should already exist, but could fail. */
201 shmfd = create_shared_file(shm_path);
a8803897 202 }
a9ff648c
MD
203 if (shmfd < 0)
204 goto error_shm_open;
205
5a61337d 206 sigblocked = 0;
8da6cd6d
MD
207 ret = pthread_sigmask(SIG_SETMASK, &orig_sigs, NULL);
208 if (ret == -1) {
209 PERROR("pthread_sigmask");
5a61337d 210 goto error_sigmask_release;
8da6cd6d 211 }
3a81f31d
MD
212 ret = zero_file(shmfd, memory_map_size);
213 if (ret) {
214 PERROR("zero_file");
215 goto error_zero_file;
216 }
1d498196
MD
217 ret = ftruncate(shmfd, memory_map_size);
218 if (ret) {
219 PERROR("ftruncate");
220 goto error_ftruncate;
221 }
222 obj->shm_fd = shmfd;
223
224 /* memory_map: mmap */
225 memory_map = mmap(NULL, memory_map_size, PROT_READ | PROT_WRITE,
226 MAP_SHARED, shmfd, 0);
227 if (memory_map == MAP_FAILED) {
228 PERROR("mmap");
229 goto error_mmap;
230 }
74d81a6c 231 obj->type = SHM_OBJECT_SHM;
1d498196
MD
232 obj->memory_map = memory_map;
233 obj->memory_map_size = memory_map_size;
234 obj->allocated_len = 0;
dc613eb9 235 obj->index = table->allocated_len++;
7a9c21bd 236
1d498196
MD
237 return obj;
238
239error_mmap:
240error_ftruncate:
3a81f31d 241error_zero_file:
5a61337d 242error_sigmask_release:
1d498196
MD
243 ret = close(shmfd);
244 if (ret) {
245 PERROR("close");
246 assert(0);
247 }
a9ff648c
MD
248 if (shm_path) {
249 ret = unlink(shm_path);
250 if (ret) {
251 PERROR("ret");
252 }
253 }
1d498196 254error_shm_open:
5a61337d
MD
255 if (sigblocked) {
256 ret = pthread_sigmask(SIG_SETMASK, &orig_sigs, NULL);
257 if (ret == -1) {
258 PERROR("pthread_sigmask");
259 }
260 }
8da6cd6d 261error_pthread_sigmask:
1d498196
MD
262error_fcntl:
263 for (i = 0; i < 2; i++) {
264 ret = close(waitfd[i]);
265 if (ret) {
266 PERROR("close");
267 assert(0);
268 }
269 }
270error_pipe:
1d498196 271 return NULL;
1d498196
MD
272}
273
74d81a6c
MD
274static
275struct shm_object *_shm_object_table_alloc_mem(struct shm_object_table *table,
276 size_t memory_map_size)
277{
278 struct shm_object *obj;
279 void *memory_map;
ff0f5728 280 int waitfd[2], i, ret;
74d81a6c
MD
281
282 if (table->allocated_len >= table->size)
283 return NULL;
284 obj = &table->objects[table->allocated_len];
285
286 memory_map = zmalloc(memory_map_size);
287 if (!memory_map)
288 goto alloc_error;
289
ff0f5728
MD
290 /* wait_fd: create pipe */
291 ret = pipe(waitfd);
292 if (ret < 0) {
293 PERROR("pipe");
294 goto error_pipe;
295 }
296 for (i = 0; i < 2; i++) {
297 ret = fcntl(waitfd[i], F_SETFD, FD_CLOEXEC);
298 if (ret < 0) {
299 PERROR("fcntl");
300 goto error_fcntl;
301 }
302 }
303 /* The write end of the pipe needs to be non-blocking */
304 ret = fcntl(waitfd[1], F_SETFL, O_NONBLOCK);
305 if (ret < 0) {
306 PERROR("fcntl");
307 goto error_fcntl;
308 }
309 memcpy(obj->wait_fd, waitfd, sizeof(waitfd));
310
311 /* no shm_fd */
74d81a6c
MD
312 obj->shm_fd = -1;
313
314 obj->type = SHM_OBJECT_MEM;
315 obj->memory_map = memory_map;
316 obj->memory_map_size = memory_map_size;
317 obj->allocated_len = 0;
318 obj->index = table->allocated_len++;
319
320 return obj;
321
ff0f5728
MD
322error_fcntl:
323 for (i = 0; i < 2; i++) {
324 ret = close(waitfd[i]);
325 if (ret) {
326 PERROR("close");
327 assert(0);
328 }
329 }
330error_pipe:
331 free(memory_map);
74d81a6c
MD
332alloc_error:
333 return NULL;
334}
335
336struct shm_object *shm_object_table_alloc(struct shm_object_table *table,
337 size_t memory_map_size,
a9ff648c
MD
338 enum shm_object_type type,
339 const char *shm_path)
74d81a6c
MD
340{
341 switch (type) {
342 case SHM_OBJECT_SHM:
a9ff648c
MD
343 return _shm_object_table_alloc_shm(table, memory_map_size,
344 shm_path);
74d81a6c
MD
345 case SHM_OBJECT_MEM:
346 return _shm_object_table_alloc_mem(table, memory_map_size);
347 default:
348 assert(0);
349 }
350 return NULL;
351}
352
353struct shm_object *shm_object_table_append_shm(struct shm_object_table *table,
354 int shm_fd, int wakeup_fd, uint32_t stream_nr,
355 size_t memory_map_size)
193183fb
MD
356{
357 struct shm_object *obj;
358 char *memory_map;
74d81a6c 359 int ret;
193183fb
MD
360
361 if (table->allocated_len >= table->size)
362 return NULL;
74d81a6c
MD
363 /* streams _must_ be received in sequential order, else fail. */
364 if (stream_nr + 1 != table->allocated_len)
365 return NULL;
366
193183fb
MD
367 obj = &table->objects[table->allocated_len];
368
74d81a6c
MD
369 /* wait_fd: set write end of the pipe. */
370 obj->wait_fd[0] = -1; /* read end is unset */
371 obj->wait_fd[1] = wakeup_fd;
193183fb
MD
372 obj->shm_fd = shm_fd;
373
74d81a6c
MD
374 ret = fcntl(obj->wait_fd[1], F_SETFD, FD_CLOEXEC);
375 if (ret < 0) {
376 PERROR("fcntl");
377 goto error_fcntl;
378 }
379 /* The write end of the pipe needs to be non-blocking */
380 ret = fcntl(obj->wait_fd[1], F_SETFL, O_NONBLOCK);
381 if (ret < 0) {
382 PERROR("fcntl");
383 goto error_fcntl;
384 }
385
193183fb
MD
386 /* memory_map: mmap */
387 memory_map = mmap(NULL, memory_map_size, PROT_READ | PROT_WRITE,
388 MAP_SHARED, shm_fd, 0);
389 if (memory_map == MAP_FAILED) {
390 PERROR("mmap");
391 goto error_mmap;
392 }
74d81a6c 393 obj->type = SHM_OBJECT_SHM;
193183fb
MD
394 obj->memory_map = memory_map;
395 obj->memory_map_size = memory_map_size;
396 obj->allocated_len = memory_map_size;
397 obj->index = table->allocated_len++;
398
399 return obj;
400
74d81a6c 401error_fcntl:
193183fb
MD
402error_mmap:
403 return NULL;
404}
405
74d81a6c
MD
406/*
407 * Passing ownership of mem to object.
408 */
409struct shm_object *shm_object_table_append_mem(struct shm_object_table *table,
ff0f5728 410 void *mem, size_t memory_map_size, int wakeup_fd)
74d81a6c
MD
411{
412 struct shm_object *obj;
ff0f5728 413 int ret;
74d81a6c
MD
414
415 if (table->allocated_len >= table->size)
416 return NULL;
417 obj = &table->objects[table->allocated_len];
418
ff0f5728
MD
419 obj->wait_fd[0] = -1; /* read end is unset */
420 obj->wait_fd[1] = wakeup_fd;
74d81a6c
MD
421 obj->shm_fd = -1;
422
ff0f5728
MD
423 ret = fcntl(obj->wait_fd[1], F_SETFD, FD_CLOEXEC);
424 if (ret < 0) {
425 PERROR("fcntl");
426 goto error_fcntl;
427 }
428 /* The write end of the pipe needs to be non-blocking */
429 ret = fcntl(obj->wait_fd[1], F_SETFL, O_NONBLOCK);
430 if (ret < 0) {
431 PERROR("fcntl");
432 goto error_fcntl;
433 }
434
74d81a6c
MD
435 obj->type = SHM_OBJECT_MEM;
436 obj->memory_map = mem;
437 obj->memory_map_size = memory_map_size;
438 obj->allocated_len = memory_map_size;
439 obj->index = table->allocated_len++;
440
441 return obj;
ff0f5728
MD
442
443error_fcntl:
444 return NULL;
74d81a6c
MD
445}
446
1d498196
MD
447static
448void shmp_object_destroy(struct shm_object *obj)
449{
74d81a6c
MD
450 switch (obj->type) {
451 case SHM_OBJECT_SHM:
452 {
453 int ret, i;
1d498196 454
7a784989
MD
455 ret = munmap(obj->memory_map, obj->memory_map_size);
456 if (ret) {
457 PERROR("umnmap");
458 assert(0);
459 }
ef9ff354
MD
460 ret = close(obj->shm_fd);
461 if (ret) {
462 PERROR("close");
463 assert(0);
464 }
a9ff648c
MD
465 if (obj->shm_path[0]) {
466 ret = unlink(obj->shm_path);
467 if (ret) {
468 PERROR("ret");
469 }
470 }
74d81a6c
MD
471 for (i = 0; i < 2; i++) {
472 if (obj->wait_fd[i] < 0)
473 continue;
474 ret = close(obj->wait_fd[i]);
475 if (ret) {
476 PERROR("close");
477 assert(0);
478 }
1d498196 479 }
74d81a6c
MD
480 break;
481 }
482 case SHM_OBJECT_MEM:
ff0f5728
MD
483 {
484 int ret, i;
485
486 for (i = 0; i < 2; i++) {
487 if (obj->wait_fd[i] < 0)
488 continue;
489 ret = close(obj->wait_fd[i]);
490 if (ret) {
491 PERROR("close");
492 assert(0);
493 }
494 }
74d81a6c
MD
495 free(obj->memory_map);
496 break;
ff0f5728 497 }
74d81a6c
MD
498 default:
499 assert(0);
1d498196
MD
500 }
501}
502
503void shm_object_table_destroy(struct shm_object_table *table)
504{
505 int i;
506
507 for (i = 0; i < table->allocated_len; i++)
508 shmp_object_destroy(&table->objects[i]);
509 free(table);
510}
511
512/*
513 * zalloc_shm - allocate memory within a shm object.
514 *
515 * Shared memory is already zeroed by shmget.
516 * *NOT* multithread-safe (should be protected by mutex).
517 * Returns a -1, -1 tuple on error.
518 */
519struct shm_ref zalloc_shm(struct shm_object *obj, size_t len)
520{
521 struct shm_ref ref;
522 struct shm_ref shm_ref_error = { -1, -1 };
523
524 if (obj->memory_map_size - obj->allocated_len < len)
525 return shm_ref_error;
526 ref.index = obj->index;
527 ref.offset = obj->allocated_len;
528 obj->allocated_len += len;
529 return ref;
530}
531
532void align_shm(struct shm_object *obj, size_t align)
533{
534 size_t offset_len = offset_align(obj->allocated_len, align);
535 obj->allocated_len += offset_len;
536}
This page took 0.049461 seconds and 4 git commands to generate.