Performance: define _LGPL_SOURCE in LGPL c files
[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
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 */
42 static
43 int 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;
70 error:
71 free(zeropage);
72 return ret;
73 }
74
75 struct 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]));
81 if (!table)
82 return NULL;
83 table->size = max_nb_obj;
84 return table;
85 }
86
87 static
88 struct shm_object *_shm_object_table_alloc_shm(struct shm_object_table *table,
89 size_t memory_map_size,
90 int stream_fd)
91 {
92 int shmfd, waitfd[2], ret, i;
93 struct shm_object *obj;
94 char *memory_map;
95
96 if (stream_fd < 0)
97 return NULL;
98 if (table->allocated_len >= table->size)
99 return NULL;
100 obj = &table->objects[table->allocated_len];
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 }
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 }
121 memcpy(obj->wait_fd, waitfd, sizeof(waitfd));
122
123 /* create shm */
124
125 shmfd = stream_fd;
126 ret = zero_file(shmfd, memory_map_size);
127 if (ret) {
128 PERROR("zero_file");
129 goto error_zero_file;
130 }
131 ret = ftruncate(shmfd, memory_map_size);
132 if (ret) {
133 PERROR("ftruncate");
134 goto error_ftruncate;
135 }
136 obj->shm_fd_ownership = 0;
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 }
146 obj->type = SHM_OBJECT_SHM;
147 obj->memory_map = memory_map;
148 obj->memory_map_size = memory_map_size;
149 obj->allocated_len = 0;
150 obj->index = table->allocated_len++;
151
152 return obj;
153
154 error_mmap:
155 error_ftruncate:
156 error_zero_file:
157 error_fcntl:
158 for (i = 0; i < 2; i++) {
159 ret = close(waitfd[i]);
160 if (ret) {
161 PERROR("close");
162 assert(0);
163 }
164 }
165 error_pipe:
166 return NULL;
167 }
168
169 static
170 struct 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;
175 int waitfd[2], i, ret;
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
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 */
207 obj->shm_fd = -1;
208 obj->shm_fd_ownership = 0;
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
218 error_fcntl:
219 for (i = 0; i < 2; i++) {
220 ret = close(waitfd[i]);
221 if (ret) {
222 PERROR("close");
223 assert(0);
224 }
225 }
226 error_pipe:
227 free(memory_map);
228 alloc_error:
229 return NULL;
230 }
231
232 struct shm_object *shm_object_table_alloc(struct shm_object_table *table,
233 size_t memory_map_size,
234 enum shm_object_type type,
235 int stream_fd)
236 {
237 switch (type) {
238 case SHM_OBJECT_SHM:
239 return _shm_object_table_alloc_shm(table, memory_map_size,
240 stream_fd);
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
249 struct 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)
252 {
253 struct shm_object *obj;
254 char *memory_map;
255 int ret;
256
257 if (table->allocated_len >= table->size)
258 return NULL;
259 /* streams _must_ be received in sequential order, else fail. */
260 if (stream_nr + 1 != table->allocated_len)
261 return NULL;
262
263 obj = &table->objects[table->allocated_len];
264
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;
268 obj->shm_fd = shm_fd;
269 obj->shm_fd_ownership = 1;
270
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
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 }
290 obj->type = SHM_OBJECT_SHM;
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
298 error_fcntl:
299 error_mmap:
300 return NULL;
301 }
302
303 /*
304 * Passing ownership of mem to object.
305 */
306 struct shm_object *shm_object_table_append_mem(struct shm_object_table *table,
307 void *mem, size_t memory_map_size, int wakeup_fd)
308 {
309 struct shm_object *obj;
310 int ret;
311
312 if (table->allocated_len >= table->size)
313 return NULL;
314 obj = &table->objects[table->allocated_len];
315
316 obj->wait_fd[0] = -1; /* read end is unset */
317 obj->wait_fd[1] = wakeup_fd;
318 obj->shm_fd = -1;
319 obj->shm_fd_ownership = 0;
320
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
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;
340
341 error_fcntl:
342 return NULL;
343 }
344
345 static
346 void shmp_object_destroy(struct shm_object *obj)
347 {
348 switch (obj->type) {
349 case SHM_OBJECT_SHM:
350 {
351 int ret, i;
352
353 ret = munmap(obj->memory_map, obj->memory_map_size);
354 if (ret) {
355 PERROR("umnmap");
356 assert(0);
357 }
358 if (obj->shm_fd_ownership) {
359 ret = close(obj->shm_fd);
360 if (ret) {
361 PERROR("close");
362 assert(0);
363 }
364 }
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 }
373 }
374 break;
375 }
376 case SHM_OBJECT_MEM:
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 }
389 free(obj->memory_map);
390 break;
391 }
392 default:
393 assert(0);
394 }
395 }
396
397 void 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 */
413 struct 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
426 void 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.048829 seconds and 5 git commands to generate.