Imported Upstream version 0.1.0+git20131207+e452e83
[deb_libhybris.git] / hybris / common / hooks_shm.c
1 /*
2 * Copyright (c) 2012 Carsten Munk <carsten.munk@gmail.com>
3 * Copyright (c) 2013 Christophe Chapuis <chris.chapuis@gmail.com>
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 #include "hooks_shm.h"
20
21 #define _GNU_SOURCE
22
23 #include <stddef.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <pthread.h>
29 #include <errno.h>
30 #include <sys/ipc.h>
31 #include <sys/shm.h>
32 #include <sys/mman.h>
33
34 /* Debug */
35 #include "logging.h"
36 #define LOGD(message, ...) HYBRIS_DEBUG_LOG(HOOKS, message, ##__VA_ARGS__)
37
38 #define HYBRIS_DATA_SIZE 4000
39 #define HYBRIS_SHM_MASK 0xFF000000UL
40 #define HYBRIS_SHM_PATH "/hybris_shm_data"
41
42 /* Structure of a shared memory region */
43 typedef struct _hybris_shm_data_t {
44 pthread_mutex_t access_mutex;
45 int current_offset;
46 int max_offset;
47 unsigned char data;
48 } hybris_shm_data_t;
49
50 /* A helper to switch between the size of the data and the size of the shm object */
51 const int HYBRIS_SHM_DATA_HEADER_SIZE = (sizeof(hybris_shm_data_t) - sizeof(unsigned char));
52
53 /* pointer to the shared memory region */
54 static hybris_shm_data_t *_hybris_shm_data = NULL;
55
56 /* the SHM mem_id of the shared memory region */
57 static int _hybris_shm_fd = -1;
58
59 /* the size of the shared memory region that is currently mmap'ed to this process */
60 static int _current_mapped_size = 0;
61
62 /* forward-declare the internal static methods */
63 static void _release_shm(void);
64 static void _sync_mmap_with_shm(void);
65 static void _hybris_shm_init(void);
66 static void _hybris_shm_extend_region(void);
67
68 /*
69 * Detach the allocated memory region, and mark it for deletion
70 */
71 static void _release_shm(void)
72 {
73 if (_hybris_shm_data) {
74 munmap(_hybris_shm_data, _current_mapped_size); /* unmap from this process */
75 _hybris_shm_data = NULL; /* pointer is no more valid */
76 }
77 if (_hybris_shm_fd >= 0) {
78 close(_hybris_shm_fd); /* close the shm file descriptor */
79 _hybris_shm_fd = -1;
80 }
81 shm_unlink(HYBRIS_SHM_PATH); /* request the deletion of the shm region */
82 }
83
84 /*
85 * Synchronize the size of the mmap with the size of the shm region
86 */
87 static void _sync_mmap_with_shm()
88 {
89 if (_hybris_shm_fd >= 0 && _hybris_shm_data) {
90 if (_current_mapped_size < _hybris_shm_data->max_offset + HYBRIS_SHM_DATA_HEADER_SIZE) {
91 /* Note that mremap may change the address pointed by _hybris_shm_data.
92 * But as we never point directly into _hybris_shm_data, it's fine.
93 * */
94 _hybris_shm_data = (hybris_shm_data_t *)mremap( _hybris_shm_data, _current_mapped_size,
95 _hybris_shm_data->max_offset + HYBRIS_SHM_DATA_HEADER_SIZE,
96 MREMAP_MAYMOVE );
97
98 _current_mapped_size = _hybris_shm_data->max_offset + HYBRIS_SHM_DATA_HEADER_SIZE;
99 }
100 }
101 }
102
103 /*
104 * Initialize the shared memory region for hybris, in order to store
105 * pshared mutex, condition and rwlock
106 */
107 static void _hybris_shm_init()
108 {
109 if (_hybris_shm_fd < 0) {
110 const size_t size_to_map = HYBRIS_SHM_DATA_HEADER_SIZE + HYBRIS_DATA_SIZE; /* 4000 bytes for the data, plus the header size */
111
112 /* initialize or get shared memory segment */
113 _hybris_shm_fd = shm_open(HYBRIS_SHM_PATH, O_RDWR, 0660);
114 if (_hybris_shm_fd >= 0) {
115 /* Map the memory object */
116 _hybris_shm_data = (hybris_shm_data_t *)mmap( NULL, size_to_map,
117 PROT_READ | PROT_WRITE, MAP_SHARED,
118 _hybris_shm_fd, 0 );
119 _current_mapped_size = size_to_map;
120
121 _sync_mmap_with_shm();
122 }
123 else {
124 LOGD("Creating a new shared memory segment.");
125
126 _hybris_shm_fd = shm_open(HYBRIS_SHM_PATH, O_RDWR | O_CREAT, 0660);
127 if (_hybris_shm_fd >= 0) {
128 ftruncate( _hybris_shm_fd, size_to_map );
129 /* Map the memory object */
130 _hybris_shm_data = (hybris_shm_data_t *)mmap( NULL, size_to_map,
131 PROT_READ | PROT_WRITE, MAP_SHARED,
132 _hybris_shm_fd, 0 );
133 if (_hybris_shm_data == MAP_FAILED) {
134 HYBRIS_ERROR_LOG(HOOKS, "ERROR: mmap failed: %s\n", strerror(errno));
135 _release_shm();
136 }
137 else {
138 _current_mapped_size = size_to_map;
139 /* Initialize the memory object */
140 memset((void*)_hybris_shm_data, 0, size_to_map);
141 _hybris_shm_data->max_offset = HYBRIS_DATA_SIZE;
142
143 pthread_mutexattr_t attr;
144 pthread_mutexattr_init(&attr);
145 pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
146 pthread_mutex_init(&_hybris_shm_data->access_mutex, &attr);
147 pthread_mutexattr_destroy(&attr);
148
149 atexit(_release_shm);
150 }
151 }
152 else {
153 HYBRIS_ERROR_LOG(HOOKS, "ERROR: Couldn't create shared memory segment !");
154 }
155 }
156 }
157 }
158
159 /*
160 * Extend the SHM region's size
161 */
162 static void _hybris_shm_extend_region()
163 {
164 ftruncate( _hybris_shm_fd, _current_mapped_size + HYBRIS_DATA_SIZE );
165 _hybris_shm_data->max_offset += HYBRIS_DATA_SIZE;
166
167 _sync_mmap_with_shm();
168 }
169
170 /************ public functions *******************/
171
172 /*
173 * Determine if the pointer that has been extracted by hybris is
174 * pointing to an address in the shared memory
175 */
176 int hybris_is_pointer_in_shm(void *ptr)
177 {
178 if ((unsigned int)ptr >= HYBRIS_SHM_MASK)
179 return 1;
180
181 return 0;
182 }
183
184 /*
185 * Convert this offset pointer to the shared memory to an
186 * absolute pointer that can be used in user space
187 */
188 void *hybris_get_shmpointer(hybris_shm_pointer_t handle)
189 {
190 void *realpointer = NULL;
191 if (hybris_is_pointer_in_shm((void*)handle)) {
192 if (_hybris_shm_fd < 0) {
193 /* if we are not yet attached to any shm region, then do it now */
194 _hybris_shm_init();
195 }
196
197 pthread_mutex_lock(&_hybris_shm_data->access_mutex);
198
199 _sync_mmap_with_shm(); /* make sure our mmap is sync'ed */
200
201 if (_hybris_shm_data != NULL) {
202 unsigned int offset = handle & (~HYBRIS_SHM_MASK);
203 realpointer = &(_hybris_shm_data->data) + offset;
204
205 /* Be careful when activating this trace: this method is called *a lot* !
206 LOGD("handle = %x, offset = %d, realpointer = %x)", handle, offset, realpointer);
207 */
208 }
209
210 pthread_mutex_unlock(&_hybris_shm_data->access_mutex);
211 }
212
213 return realpointer;
214 }
215
216 /*
217 * Allocate a space in the shared memory region of hybris
218 */
219 hybris_shm_pointer_t hybris_shm_alloc(size_t size)
220 {
221 hybris_shm_pointer_t location = 0;
222
223 if (_hybris_shm_fd < 0) {
224 /* if we are not yet attached to any shm region, then do it now */
225 _hybris_shm_init();
226 }
227
228 pthread_mutex_lock(&_hybris_shm_data->access_mutex);
229
230 /* Make sure our mmap is sync'ed */
231 _sync_mmap_with_shm();
232
233 if (_hybris_shm_data == NULL || _hybris_shm_fd < 0)
234 return 0;
235
236 if (_hybris_shm_data->current_offset + size >= _hybris_shm_data->max_offset) {
237 /* the current buffer if full: extend it a little bit more */
238 _hybris_shm_extend_region();
239 _sync_mmap_with_shm();
240 }
241
242 /* there is now enough place in this pool */
243 location = _hybris_shm_data->current_offset | HYBRIS_SHM_MASK;
244 LOGD("Allocated a shared object (size = %d, at offset %d)", size, _hybris_shm_data->current_offset);
245
246 _hybris_shm_data->current_offset += size;
247
248 pthread_mutex_unlock(&_hybris_shm_data->access_mutex);
249
250 return location;
251 }