port: set shm size with ftruncate before writing
[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 #define _LGPL_SOURCE
22 #include "shm.h"
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <sys/mman.h>
26 #include <sys/types.h>
27 #include <sys/stat.h> /* For mode constants */
28 #include <fcntl.h> /* For O_* constants */
29 #include <assert.h>
30 #include <stdio.h>
31 #include <signal.h>
32 #include <dirent.h>
33 #include <lttng/align.h>
34 #include <limits.h>
35 #include <stdbool.h>
36 #include <stdint.h>
37 #ifdef HAVE_LIBNUMA
38 #include <numa.h>
39 #include <numaif.h>
40 #endif
41 #include <helper.h>
42 #include <ust-fd.h>
43 #include "mmap.h"
44
45 /*
46 * Ensure we have the required amount of space available by writing 0
47 * into the entire buffer. Not doing so can trigger SIGBUS when going
48 * beyond the available shm space.
49 */
50 static
51 int zero_file(int fd, size_t len)
52 {
53 ssize_t retlen;
54 size_t written = 0;
55 char *zeropage;
56 long pagelen;
57 int ret;
58
59 pagelen = sysconf(_SC_PAGESIZE);
60 if (pagelen < 0)
61 return (int) pagelen;
62 zeropage = calloc(pagelen, 1);
63 if (!zeropage)
64 return -ENOMEM;
65
66 while (len > written) {
67 do {
68 retlen = write(fd, zeropage,
69 min_t(size_t, pagelen, len - written));
70 } while (retlen == -1UL && errno == EINTR);
71 if (retlen < 0) {
72 ret = (int) retlen;
73 goto error;
74 }
75 written += retlen;
76 }
77 ret = 0;
78 error:
79 free(zeropage);
80 return ret;
81 }
82
83 struct shm_object_table *shm_object_table_create(size_t max_nb_obj)
84 {
85 struct shm_object_table *table;
86
87 table = zmalloc(sizeof(struct shm_object_table) +
88 max_nb_obj * sizeof(table->objects[0]));
89 if (!table)
90 return NULL;
91 table->size = max_nb_obj;
92 return table;
93 }
94
95 static
96 struct shm_object *_shm_object_table_alloc_shm(struct shm_object_table *table,
97 size_t memory_map_size,
98 int stream_fd)
99 {
100 int shmfd, waitfd[2], ret, i;
101 struct shm_object *obj;
102 char *memory_map;
103
104 if (stream_fd < 0)
105 return NULL;
106 if (table->allocated_len >= table->size)
107 return NULL;
108 obj = &table->objects[table->allocated_len];
109
110 /* wait_fd: create pipe */
111 ret = pipe(waitfd);
112 if (ret < 0) {
113 PERROR("pipe");
114 goto error_pipe;
115 }
116 for (i = 0; i < 2; i++) {
117 ret = fcntl(waitfd[i], F_SETFD, FD_CLOEXEC);
118 if (ret < 0) {
119 PERROR("fcntl");
120 goto error_fcntl;
121 }
122 }
123 /* The write end of the pipe needs to be non-blocking */
124 ret = fcntl(waitfd[1], F_SETFL, O_NONBLOCK);
125 if (ret < 0) {
126 PERROR("fcntl");
127 goto error_fcntl;
128 }
129 memcpy(obj->wait_fd, waitfd, sizeof(waitfd));
130
131 /*
132 * Set POSIX shared memory object size
133 *
134 * First, use ftruncate() to set its size, some implementations won't
135 * allow writes past the size set by ftruncate.
136 * Then, use write() to fill it with zeros, this allows us to fully
137 * allocate it and detect a shortage of shm space without dealing with
138 * a SIGBUS.
139 */
140
141 shmfd = stream_fd;
142 ret = ftruncate(shmfd, memory_map_size);
143 if (ret) {
144 PERROR("ftruncate");
145 goto error_ftruncate;
146 }
147 ret = zero_file(shmfd, memory_map_size);
148 if (ret) {
149 PERROR("zero_file");
150 goto error_zero_file;
151 }
152 /*
153 * Also ensure the file metadata is synced with the storage by using
154 * fsync(2).
155 */
156 ret = fsync(shmfd);
157 if (ret) {
158 PERROR("fsync");
159 goto error_fsync;
160 }
161 obj->shm_fd_ownership = 0;
162 obj->shm_fd = shmfd;
163
164 /* memory_map: mmap */
165 memory_map = mmap(NULL, memory_map_size, PROT_READ | PROT_WRITE,
166 MAP_SHARED | LTTNG_MAP_POPULATE, shmfd, 0);
167 if (memory_map == MAP_FAILED) {
168 PERROR("mmap");
169 goto error_mmap;
170 }
171 obj->type = SHM_OBJECT_SHM;
172 obj->memory_map = memory_map;
173 obj->memory_map_size = memory_map_size;
174 obj->allocated_len = 0;
175 obj->index = table->allocated_len++;
176
177 return obj;
178
179 error_mmap:
180 error_fsync:
181 error_ftruncate:
182 error_zero_file:
183 error_fcntl:
184 for (i = 0; i < 2; i++) {
185 ret = close(waitfd[i]);
186 if (ret) {
187 PERROR("close");
188 assert(0);
189 }
190 }
191 error_pipe:
192 return NULL;
193 }
194
195 static
196 struct shm_object *_shm_object_table_alloc_mem(struct shm_object_table *table,
197 size_t memory_map_size)
198 {
199 struct shm_object *obj;
200 void *memory_map;
201 int waitfd[2], i, ret;
202
203 if (table->allocated_len >= table->size)
204 return NULL;
205 obj = &table->objects[table->allocated_len];
206
207 memory_map = zmalloc(memory_map_size);
208 if (!memory_map)
209 goto alloc_error;
210
211 /* wait_fd: create pipe */
212 ret = pipe(waitfd);
213 if (ret < 0) {
214 PERROR("pipe");
215 goto error_pipe;
216 }
217 for (i = 0; i < 2; i++) {
218 ret = fcntl(waitfd[i], F_SETFD, FD_CLOEXEC);
219 if (ret < 0) {
220 PERROR("fcntl");
221 goto error_fcntl;
222 }
223 }
224 /* The write end of the pipe needs to be non-blocking */
225 ret = fcntl(waitfd[1], F_SETFL, O_NONBLOCK);
226 if (ret < 0) {
227 PERROR("fcntl");
228 goto error_fcntl;
229 }
230 memcpy(obj->wait_fd, waitfd, sizeof(waitfd));
231
232 /* no shm_fd */
233 obj->shm_fd = -1;
234 obj->shm_fd_ownership = 0;
235
236 obj->type = SHM_OBJECT_MEM;
237 obj->memory_map = memory_map;
238 obj->memory_map_size = memory_map_size;
239 obj->allocated_len = 0;
240 obj->index = table->allocated_len++;
241
242 return obj;
243
244 error_fcntl:
245 for (i = 0; i < 2; i++) {
246 ret = close(waitfd[i]);
247 if (ret) {
248 PERROR("close");
249 assert(0);
250 }
251 }
252 error_pipe:
253 free(memory_map);
254 alloc_error:
255 return NULL;
256 }
257
258 /*
259 * libnuma prints errors on the console even for numa_available().
260 * Work-around this limitation by using get_mempolicy() directly to
261 * check whether the kernel supports mempolicy.
262 */
263 #ifdef HAVE_LIBNUMA
264 static bool lttng_is_numa_available(void)
265 {
266 int ret;
267
268 ret = get_mempolicy(NULL, NULL, 0, NULL, 0);
269 if (ret && errno == ENOSYS) {
270 return false;
271 }
272 return numa_available() > 0;
273 }
274 #endif
275
276 struct shm_object *shm_object_table_alloc(struct shm_object_table *table,
277 size_t memory_map_size,
278 enum shm_object_type type,
279 int stream_fd,
280 int cpu)
281 {
282 struct shm_object *shm_object;
283 #ifdef HAVE_LIBNUMA
284 int oldnode = 0, node;
285 bool numa_avail;
286
287 numa_avail = lttng_is_numa_available();
288 if (numa_avail) {
289 oldnode = numa_preferred();
290 if (cpu >= 0) {
291 node = numa_node_of_cpu(cpu);
292 if (node >= 0)
293 numa_set_preferred(node);
294 }
295 if (cpu < 0 || node < 0)
296 numa_set_localalloc();
297 }
298 #endif /* HAVE_LIBNUMA */
299 switch (type) {
300 case SHM_OBJECT_SHM:
301 shm_object = _shm_object_table_alloc_shm(table, memory_map_size,
302 stream_fd);
303 break;
304 case SHM_OBJECT_MEM:
305 shm_object = _shm_object_table_alloc_mem(table, memory_map_size);
306 break;
307 default:
308 assert(0);
309 }
310 #ifdef HAVE_LIBNUMA
311 if (numa_avail)
312 numa_set_preferred(oldnode);
313 #endif /* HAVE_LIBNUMA */
314 return shm_object;
315 }
316
317 struct shm_object *shm_object_table_append_shm(struct shm_object_table *table,
318 int shm_fd, int wakeup_fd, uint32_t stream_nr,
319 size_t memory_map_size)
320 {
321 struct shm_object *obj;
322 char *memory_map;
323 int ret;
324
325 if (table->allocated_len >= table->size)
326 return NULL;
327 /* streams _must_ be received in sequential order, else fail. */
328 if (stream_nr + 1 != table->allocated_len)
329 return NULL;
330
331 obj = &table->objects[table->allocated_len];
332
333 /* wait_fd: set write end of the pipe. */
334 obj->wait_fd[0] = -1; /* read end is unset */
335 obj->wait_fd[1] = wakeup_fd;
336 obj->shm_fd = shm_fd;
337 obj->shm_fd_ownership = 1;
338
339 /* The write end of the pipe needs to be non-blocking */
340 ret = fcntl(obj->wait_fd[1], F_SETFL, O_NONBLOCK);
341 if (ret < 0) {
342 PERROR("fcntl");
343 goto error_fcntl;
344 }
345
346 /* memory_map: mmap */
347 memory_map = mmap(NULL, memory_map_size, PROT_READ | PROT_WRITE,
348 MAP_SHARED | LTTNG_MAP_POPULATE, shm_fd, 0);
349 if (memory_map == MAP_FAILED) {
350 PERROR("mmap");
351 goto error_mmap;
352 }
353 obj->type = SHM_OBJECT_SHM;
354 obj->memory_map = memory_map;
355 obj->memory_map_size = memory_map_size;
356 obj->allocated_len = memory_map_size;
357 obj->index = table->allocated_len++;
358
359 return obj;
360
361 error_fcntl:
362 error_mmap:
363 return NULL;
364 }
365
366 /*
367 * Passing ownership of mem to object.
368 */
369 struct shm_object *shm_object_table_append_mem(struct shm_object_table *table,
370 void *mem, size_t memory_map_size, int wakeup_fd)
371 {
372 struct shm_object *obj;
373 int ret;
374
375 if (table->allocated_len >= table->size)
376 return NULL;
377 obj = &table->objects[table->allocated_len];
378
379 obj->wait_fd[0] = -1; /* read end is unset */
380 obj->wait_fd[1] = wakeup_fd;
381 obj->shm_fd = -1;
382 obj->shm_fd_ownership = 0;
383
384 ret = fcntl(obj->wait_fd[1], F_SETFD, FD_CLOEXEC);
385 if (ret < 0) {
386 PERROR("fcntl");
387 goto error_fcntl;
388 }
389 /* The write end of the pipe needs to be non-blocking */
390 ret = fcntl(obj->wait_fd[1], F_SETFL, O_NONBLOCK);
391 if (ret < 0) {
392 PERROR("fcntl");
393 goto error_fcntl;
394 }
395
396 obj->type = SHM_OBJECT_MEM;
397 obj->memory_map = mem;
398 obj->memory_map_size = memory_map_size;
399 obj->allocated_len = memory_map_size;
400 obj->index = table->allocated_len++;
401
402 return obj;
403
404 error_fcntl:
405 return NULL;
406 }
407
408 static
409 void shmp_object_destroy(struct shm_object *obj, int consumer)
410 {
411 switch (obj->type) {
412 case SHM_OBJECT_SHM:
413 {
414 int ret, i;
415
416 ret = munmap(obj->memory_map, obj->memory_map_size);
417 if (ret) {
418 PERROR("umnmap");
419 assert(0);
420 }
421
422 if (obj->shm_fd_ownership) {
423 /* Delete FDs only if called from app (not consumer). */
424 if (!consumer) {
425 lttng_ust_lock_fd_tracker();
426 ret = close(obj->shm_fd);
427 if (!ret) {
428 lttng_ust_delete_fd_from_tracker(obj->shm_fd);
429 } else {
430 PERROR("close");
431 assert(0);
432 }
433 lttng_ust_unlock_fd_tracker();
434 } else {
435 ret = close(obj->shm_fd);
436 if (ret) {
437 PERROR("close");
438 assert(0);
439 }
440 }
441 }
442 for (i = 0; i < 2; i++) {
443 if (obj->wait_fd[i] < 0)
444 continue;
445 if (!consumer) {
446 lttng_ust_lock_fd_tracker();
447 ret = close(obj->wait_fd[i]);
448 if (!ret) {
449 lttng_ust_delete_fd_from_tracker(obj->wait_fd[i]);
450 } else {
451 PERROR("close");
452 assert(0);
453 }
454 lttng_ust_unlock_fd_tracker();
455 } else {
456 ret = close(obj->wait_fd[i]);
457 if (ret) {
458 PERROR("close");
459 assert(0);
460 }
461 }
462 }
463 break;
464 }
465 case SHM_OBJECT_MEM:
466 {
467 int ret, i;
468
469 for (i = 0; i < 2; i++) {
470 if (obj->wait_fd[i] < 0)
471 continue;
472 if (!consumer) {
473 lttng_ust_lock_fd_tracker();
474 ret = close(obj->wait_fd[i]);
475 if (!ret) {
476 lttng_ust_delete_fd_from_tracker(obj->wait_fd[i]);
477 } else {
478 PERROR("close");
479 assert(0);
480 }
481 lttng_ust_unlock_fd_tracker();
482 } else {
483 ret = close(obj->wait_fd[i]);
484 if (ret) {
485 PERROR("close");
486 assert(0);
487 }
488 }
489 }
490 free(obj->memory_map);
491 break;
492 }
493 default:
494 assert(0);
495 }
496 }
497
498 void shm_object_table_destroy(struct shm_object_table *table, int consumer)
499 {
500 int i;
501
502 for (i = 0; i < table->allocated_len; i++)
503 shmp_object_destroy(&table->objects[i], consumer);
504 free(table);
505 }
506
507 /*
508 * zalloc_shm - allocate memory within a shm object.
509 *
510 * Shared memory is already zeroed by shmget.
511 * *NOT* multithread-safe (should be protected by mutex).
512 * Returns a -1, -1 tuple on error.
513 */
514 struct shm_ref zalloc_shm(struct shm_object *obj, size_t len)
515 {
516 struct shm_ref ref;
517 struct shm_ref shm_ref_error = { -1, -1 };
518
519 if (obj->memory_map_size - obj->allocated_len < len)
520 return shm_ref_error;
521 ref.index = obj->index;
522 ref.offset = obj->allocated_len;
523 obj->allocated_len += len;
524 return ref;
525 }
526
527 void align_shm(struct shm_object *obj, size_t align)
528 {
529 size_t offset_len = lttng_ust_offset_align(obj->allocated_len, align);
530 obj->allocated_len += offset_len;
531 }
This page took 0.040258 seconds and 5 git commands to generate.