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