c4c651e4b925543e373e9b016398636be4c03293
[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 <helper.h>
36 #include <ust-fd.h>
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 */
43 static
44 int 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;
71 error:
72 free(zeropage);
73 return ret;
74 }
75
76 struct 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]));
82 if (!table)
83 return NULL;
84 table->size = max_nb_obj;
85 return table;
86 }
87
88 static
89 struct shm_object *_shm_object_table_alloc_shm(struct shm_object_table *table,
90 size_t memory_map_size,
91 int stream_fd)
92 {
93 int shmfd, waitfd[2], ret, i;
94 struct shm_object *obj;
95 char *memory_map;
96
97 if (stream_fd < 0)
98 return NULL;
99 if (table->allocated_len >= table->size)
100 return NULL;
101 obj = &table->objects[table->allocated_len];
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 }
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 }
122 memcpy(obj->wait_fd, waitfd, sizeof(waitfd));
123
124 /* create shm */
125
126 shmfd = stream_fd;
127 ret = zero_file(shmfd, memory_map_size);
128 if (ret) {
129 PERROR("zero_file");
130 goto error_zero_file;
131 }
132 ret = ftruncate(shmfd, memory_map_size);
133 if (ret) {
134 PERROR("ftruncate");
135 goto error_ftruncate;
136 }
137 obj->shm_fd_ownership = 0;
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 }
147 obj->type = SHM_OBJECT_SHM;
148 obj->memory_map = memory_map;
149 obj->memory_map_size = memory_map_size;
150 obj->allocated_len = 0;
151 obj->index = table->allocated_len++;
152
153 return obj;
154
155 error_mmap:
156 error_ftruncate:
157 error_zero_file:
158 error_fcntl:
159 for (i = 0; i < 2; i++) {
160 ret = close(waitfd[i]);
161 if (ret) {
162 PERROR("close");
163 assert(0);
164 }
165 }
166 error_pipe:
167 return NULL;
168 }
169
170 static
171 struct 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;
176 int waitfd[2], i, ret;
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
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 */
208 obj->shm_fd = -1;
209 obj->shm_fd_ownership = 0;
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
219 error_fcntl:
220 for (i = 0; i < 2; i++) {
221 ret = close(waitfd[i]);
222 if (ret) {
223 PERROR("close");
224 assert(0);
225 }
226 }
227 error_pipe:
228 free(memory_map);
229 alloc_error:
230 return NULL;
231 }
232
233 struct shm_object *shm_object_table_alloc(struct shm_object_table *table,
234 size_t memory_map_size,
235 enum shm_object_type type,
236 int stream_fd)
237 {
238 switch (type) {
239 case SHM_OBJECT_SHM:
240 return _shm_object_table_alloc_shm(table, memory_map_size,
241 stream_fd);
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
250 struct 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)
253 {
254 struct shm_object *obj;
255 char *memory_map;
256 int ret;
257
258 if (table->allocated_len >= table->size)
259 return NULL;
260 /* streams _must_ be received in sequential order, else fail. */
261 if (stream_nr + 1 != table->allocated_len)
262 return NULL;
263
264 obj = &table->objects[table->allocated_len];
265
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;
269 obj->shm_fd = shm_fd;
270 obj->shm_fd_ownership = 1;
271
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
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 }
291 obj->type = SHM_OBJECT_SHM;
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
299 error_fcntl:
300 error_mmap:
301 return NULL;
302 }
303
304 /*
305 * Passing ownership of mem to object.
306 */
307 struct shm_object *shm_object_table_append_mem(struct shm_object_table *table,
308 void *mem, size_t memory_map_size, int wakeup_fd)
309 {
310 struct shm_object *obj;
311 int ret;
312
313 if (table->allocated_len >= table->size)
314 return NULL;
315 obj = &table->objects[table->allocated_len];
316
317 obj->wait_fd[0] = -1; /* read end is unset */
318 obj->wait_fd[1] = wakeup_fd;
319 obj->shm_fd = -1;
320 obj->shm_fd_ownership = 0;
321
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
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;
341
342 error_fcntl:
343 return NULL;
344 }
345
346 static
347 void shmp_object_destroy(struct shm_object *obj, int consumer)
348 {
349 switch (obj->type) {
350 case SHM_OBJECT_SHM:
351 {
352 int ret, i;
353
354 ret = munmap(obj->memory_map, obj->memory_map_size);
355 if (ret) {
356 PERROR("umnmap");
357 assert(0);
358 }
359
360 if (obj->shm_fd_ownership) {
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 }
378 }
379 }
380 for (i = 0; i < 2; i++) {
381 if (obj->wait_fd[i] < 0)
382 continue;
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 }
399 }
400 }
401 break;
402 }
403 case SHM_OBJECT_MEM:
404 {
405 int ret, i;
406
407 for (i = 0; i < 2; i++) {
408 if (obj->wait_fd[i] < 0)
409 continue;
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 }
426 }
427 }
428 free(obj->memory_map);
429 break;
430 }
431 default:
432 assert(0);
433 }
434 }
435
436 void shm_object_table_destroy(struct shm_object_table *table, int consumer)
437 {
438 int i;
439
440 for (i = 0; i < table->allocated_len; i++)
441 shmp_object_destroy(&table->objects[i], consumer);
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 */
452 struct 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
465 void 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.037562 seconds and 3 git commands to generate.