Implement file-backed ring buffer
[lttng-ust.git] / libringbuffer / shm.c
1 /*
2 * libringbuffer/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 #include "shm.h"
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <sys/mman.h>
25 #include <sys/types.h>
26 #include <sys/stat.h> /* For mode constants */
27 #include <fcntl.h> /* For O_* constants */
28 #include <assert.h>
29 #include <stdio.h>
30 #include <signal.h>
31 #include <dirent.h>
32 #include <lttng/align.h>
33 #include <limits.h>
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 */
41 static
42 int 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;
69 error:
70 free(zeropage);
71 return ret;
72 }
73
74 struct 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]));
80 if (!table)
81 return NULL;
82 table->size = max_nb_obj;
83 return table;
84 }
85
86 static
87 int 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
125 error_shm_release:
126 ret = close(shmfd);
127 if (ret) {
128 PERROR("close");
129 assert(0);
130 }
131 error_shm_open:
132 return -1;
133 }
134
135 static
136 int 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
141 static
142 struct shm_object *_shm_object_table_alloc_shm(struct shm_object_table *table,
143 size_t memory_map_size,
144 const char *shm_path)
145 {
146 int shmfd, waitfd[2], ret, i, sigblocked = 0;
147 struct shm_object *obj;
148 char *memory_map;
149 sigset_t all_sigs, orig_sigs;
150
151 if (table->allocated_len >= table->size)
152 return NULL;
153 obj = &table->objects[table->allocated_len];
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 }
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 }
174 memcpy(obj->wait_fd, waitfd, sizeof(waitfd));
175
176 /* shm_fd: create shm */
177
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 }
189 sigblocked = 1;
190
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);
202 }
203 if (shmfd < 0)
204 goto error_shm_open;
205
206 sigblocked = 0;
207 ret = pthread_sigmask(SIG_SETMASK, &orig_sigs, NULL);
208 if (ret == -1) {
209 PERROR("pthread_sigmask");
210 goto error_sigmask_release;
211 }
212 ret = zero_file(shmfd, memory_map_size);
213 if (ret) {
214 PERROR("zero_file");
215 goto error_zero_file;
216 }
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 }
231 obj->type = SHM_OBJECT_SHM;
232 obj->memory_map = memory_map;
233 obj->memory_map_size = memory_map_size;
234 obj->allocated_len = 0;
235 obj->index = table->allocated_len++;
236
237 return obj;
238
239 error_mmap:
240 error_ftruncate:
241 error_zero_file:
242 error_sigmask_release:
243 ret = close(shmfd);
244 if (ret) {
245 PERROR("close");
246 assert(0);
247 }
248 if (shm_path) {
249 ret = unlink(shm_path);
250 if (ret) {
251 PERROR("ret");
252 }
253 }
254 error_shm_open:
255 if (sigblocked) {
256 ret = pthread_sigmask(SIG_SETMASK, &orig_sigs, NULL);
257 if (ret == -1) {
258 PERROR("pthread_sigmask");
259 }
260 }
261 error_pthread_sigmask:
262 error_fcntl:
263 for (i = 0; i < 2; i++) {
264 ret = close(waitfd[i]);
265 if (ret) {
266 PERROR("close");
267 assert(0);
268 }
269 }
270 error_pipe:
271 return NULL;
272 }
273
274 static
275 struct 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;
280 int waitfd[2], i, ret;
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
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 */
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
322 error_fcntl:
323 for (i = 0; i < 2; i++) {
324 ret = close(waitfd[i]);
325 if (ret) {
326 PERROR("close");
327 assert(0);
328 }
329 }
330 error_pipe:
331 free(memory_map);
332 alloc_error:
333 return NULL;
334 }
335
336 struct shm_object *shm_object_table_alloc(struct shm_object_table *table,
337 size_t memory_map_size,
338 enum shm_object_type type,
339 const char *shm_path)
340 {
341 switch (type) {
342 case SHM_OBJECT_SHM:
343 return _shm_object_table_alloc_shm(table, memory_map_size,
344 shm_path);
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
353 struct 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)
356 {
357 struct shm_object *obj;
358 char *memory_map;
359 int ret;
360
361 if (table->allocated_len >= table->size)
362 return NULL;
363 /* streams _must_ be received in sequential order, else fail. */
364 if (stream_nr + 1 != table->allocated_len)
365 return NULL;
366
367 obj = &table->objects[table->allocated_len];
368
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;
372 obj->shm_fd = shm_fd;
373
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
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 }
393 obj->type = SHM_OBJECT_SHM;
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
401 error_fcntl:
402 error_mmap:
403 return NULL;
404 }
405
406 /*
407 * Passing ownership of mem to object.
408 */
409 struct shm_object *shm_object_table_append_mem(struct shm_object_table *table,
410 void *mem, size_t memory_map_size, int wakeup_fd)
411 {
412 struct shm_object *obj;
413 int ret;
414
415 if (table->allocated_len >= table->size)
416 return NULL;
417 obj = &table->objects[table->allocated_len];
418
419 obj->wait_fd[0] = -1; /* read end is unset */
420 obj->wait_fd[1] = wakeup_fd;
421 obj->shm_fd = -1;
422
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
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;
442
443 error_fcntl:
444 return NULL;
445 }
446
447 static
448 void shmp_object_destroy(struct shm_object *obj)
449 {
450 switch (obj->type) {
451 case SHM_OBJECT_SHM:
452 {
453 int ret, i;
454
455 ret = munmap(obj->memory_map, obj->memory_map_size);
456 if (ret) {
457 PERROR("umnmap");
458 assert(0);
459 }
460 ret = close(obj->shm_fd);
461 if (ret) {
462 PERROR("close");
463 assert(0);
464 }
465 if (obj->shm_path[0]) {
466 ret = unlink(obj->shm_path);
467 if (ret) {
468 PERROR("ret");
469 }
470 }
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 }
479 }
480 break;
481 }
482 case SHM_OBJECT_MEM:
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 }
495 free(obj->memory_map);
496 break;
497 }
498 default:
499 assert(0);
500 }
501 }
502
503 void 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 */
519 struct 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
532 void 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.039821 seconds and 4 git commands to generate.