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