]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/shmc/lib/src/shmc.c
5b38bfe8dcb461aeecb47113f9e8f67eac737a9d
[l4.git] / l4 / pkg / shmc / lib / src / shmc.c
1 /*
2  * (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
3  *               Alexander Warg <warg@os.inf.tu-dresden.de>
4  *     economic rights: Technische Universität Dresden (Germany)
5  * This file is part of TUD:OS and distributed under the terms of the
6  * GNU Lesser General Public License 2.1.
7  * Please see the COPYING-LGPL-2.1 file for details.
8  */
9 #include <l4/shmc/shmc.h>
10
11 #include <l4/sys/err.h>
12 #include <l4/sys/factory.h>
13 #include <l4/sys/task.h>
14 #include <l4/re/env.h>
15 #include <l4/re/c/util/cap_alloc.h>
16 #include <l4/re/c/rm.h>
17 #include <l4/re/c/mem_alloc.h>
18 #include <l4/re/c/namespace.h>
19 #include <l4/sys/debugger.h>
20
21 #include <l4/util/util.h>
22 #include <l4/util/atomic.h>
23
24 #include <string.h>
25 #include <stdio.h>
26
27 /* Head of a shared memory data memory, which has a size of multiple pages
28  * No task local data must be in here (pointers, caps)
29  */
30 typedef struct {
31   l4_umword_t  lock;         // lock for handling chunks
32   l4_addr_t    _first_chunk; // offset to first chunk
33 } shared_mem_t;
34
35 enum {
36   SHMAREA_LOCK_FREE, SHMAREA_LOCK_TAKEN,
37 };
38
39 enum {
40   MAX_SIZE = (~0UL) >> 1,
41 };
42
43 static inline l4shmc_chunk_desc_t *
44 chunk_get(l4_addr_t o, void *shm_local_addr)
45 {
46   return (l4shmc_chunk_desc_t *)(o + (l4_addr_t)shm_local_addr);
47 }
48
49 L4_CV long
50 l4shmc_create(const char *shm_name, l4_umword_t shm_size)
51 {
52   shared_mem_t *s;
53   l4re_ds_t shm_ds = L4_INVALID_CAP;
54   l4re_namespace_t shm_cap;
55   long r;
56
57   if (shm_size > MAX_SIZE)
58     return -L4_ENOMEM;
59
60   shm_cap = l4re_env_get_cap(shm_name);
61   if (l4_is_invalid_cap(shm_cap))
62     return -L4_ENOENT;
63
64   if (l4_is_invalid_cap(shm_ds = l4re_util_cap_alloc()))
65     return -L4_ENOMEM;
66
67   if ((r = l4re_ma_alloc(shm_size, shm_ds, 0)))
68     goto out_shm_free_cap;
69
70   if ((r = l4re_rm_attach((void **)&s, shm_size, L4RE_RM_SEARCH_ADDR, shm_ds,
71                           0, L4_PAGESHIFT)))
72     goto out_shm_free_mem;
73
74   s->_first_chunk = 0;
75   s->lock = SHMAREA_LOCK_FREE;
76
77   if ((r = l4re_ns_register_obj_srv(shm_cap, "shm", shm_ds, L4RE_NS_REGISTER_RW)))
78     goto out_shm_free_mem;
79
80   l4re_rm_detach_unmap((l4_addr_t)s, L4RE_THIS_TASK_CAP);
81
82   return 0;
83
84 out_shm_free_mem:
85   l4re_ma_free(shm_ds);
86 out_shm_free_cap:
87   l4re_util_cap_free(shm_ds);
88   return r;
89 }
90
91
92 L4_CV long
93 l4shmc_attach_to(const char *shm_name, l4_umword_t timeout_ms,
94                  l4shmc_area_t *shmarea)
95 {
96   long r = -L4_ENOENT;
97   l4re_namespace_t nssrv;
98
99   strncpy(shmarea->_name, shm_name, sizeof(shmarea->_name));
100   shmarea->_name[sizeof(shmarea->_name) - 1] = 0;
101   shmarea->_local_addr = 0;
102
103   if (l4_is_invalid_cap(shmarea->_shm_ds = l4re_util_cap_alloc()))
104     return -L4_ENOMEM;
105
106   nssrv = l4re_env_get_cap(shm_name);
107   if (l4_is_invalid_cap(nssrv))
108     {
109       printf("shm: did not find '%s' namespace\n", shm_name);
110       goto out_free_cap;
111     }
112
113   if ((r = l4re_ns_query_to_srv(nssrv, "shm", shmarea->_shm_ds, timeout_ms)))
114     {
115       printf("shm: did not find shm-ds 'shm' (%ld)\n", r);
116       goto out_free_cap;
117     }
118
119   r = l4shmc_area_size(shmarea);
120   if (r < 0)
121     {
122       r = -L4_ENOMEM;
123       goto out_free_cap;
124     }
125   shmarea->_size = r;
126
127   if ((r = l4re_rm_attach(&shmarea->_local_addr, shmarea->_size,
128                           L4RE_RM_SEARCH_ADDR, shmarea->_shm_ds,
129                           0, L4_PAGESHIFT)))
130     goto out_free_cap;
131
132   return L4_EOK;
133
134 out_free_cap:
135   l4re_util_cap_free(shmarea->_shm_ds);
136   return r;
137 }
138
139 L4_CV long
140 l4shmc_area_overhead(void)
141 {
142   return sizeof(shared_mem_t);
143 }
144
145 L4_CV long
146 l4shmc_chunk_overhead(void)
147 {
148   return sizeof(l4shmc_chunk_desc_t);
149 }
150
151 static long next_chunk(l4shmc_area_t *shmarea, l4_addr_t offs)
152 {
153   shared_mem_t *shm_addr = (shared_mem_t *)shmarea->_local_addr;
154   volatile l4shmc_chunk_desc_t *p;
155   l4_addr_t next;
156
157   if (offs == 0)
158     {
159       next = shm_addr->_first_chunk;
160     }
161   else
162     {
163       p = chunk_get(offs, shmarea->_local_addr);
164       next = p->_next;
165     }
166   if (next == 0)
167     return 0;
168   if (next >= shmarea->_size || next + sizeof(*p) >= shmarea->_size || next <= offs)
169     return -L4_EIO;
170   if (next % sizeof(l4_addr_t) != 0)
171     return -L4_EINVAL;
172   p = chunk_get(next, shmarea->_local_addr);
173   if (p->_magic != L4SHMC_CHUNK_MAGIC)
174     return -L4_EIO;
175   return next;
176 }
177
178 L4_CV long
179 l4shmc_iterate_chunk(l4shmc_area_t *shmarea, const char **chunk_name, long offs)
180 {
181   if (offs < 0)
182     return -L4_EINVAL;
183   offs = next_chunk(shmarea, offs);
184   if (offs > 0)
185     {
186       l4shmc_chunk_desc_t *p = chunk_get(offs, shmarea->_local_addr);
187       *chunk_name =  p->_name;
188     }
189   return offs;
190 }
191
192 L4_CV long
193 l4shmc_add_chunk(l4shmc_area_t *shmarea,
194                 const char *chunk_name,
195                 l4_umword_t chunk_capacity,
196                 l4shmc_chunk_t *chunk)
197 {
198   shared_mem_t *shm_addr = (shared_mem_t *)shmarea->_local_addr;
199
200   l4shmc_chunk_desc_t *p = NULL;
201   l4shmc_chunk_desc_t *prev = NULL;
202   l4_addr_t offs = 0;
203   long ret;
204
205   if (chunk_capacity >> (sizeof(chunk_capacity) * 8 - 1))
206     return -L4_ENOMEM;
207
208   while (!l4util_cmpxchg(&shm_addr->lock, SHMAREA_LOCK_FREE,
209                          SHMAREA_LOCK_TAKEN))
210     l4_sleep(1);
211   asm volatile ("" : : : "memory");
212   while ((ret = next_chunk(shmarea, offs)) > 0)
213     {
214       p = chunk_get(ret, shmarea->_local_addr);
215       if (strcmp(p->_name, chunk_name) == 0)
216         {
217           ret = -L4_EEXIST;
218           goto out_free_lock;
219         }
220       offs = ret;
221     }
222   if (ret < 0)
223      goto out_free_lock;
224   if (offs == 0)
225     offs = sizeof(shared_mem_t);
226   else
227     {
228       l4_addr_t n = p->_offset + p->_capacity + sizeof(*p);
229       if (n <= offs || n >= shmarea->_size)
230         {
231           ret = -L4_EIO;
232           goto out_free_lock;
233         }
234       offs = n;
235       prev = p;
236     }
237
238   if (offs + chunk_capacity + sizeof(*p) > (unsigned long)shmarea->_size)
239     {
240       ret = -L4_ENOMEM;
241       goto out_free_lock; // no more free memory in this shm
242     }
243   p = chunk_get(offs, shmarea->_local_addr);
244   p->_offset = offs;
245   p->_next = 0;
246   p->_capacity = chunk_capacity;
247   p->_size   = 0;
248   p->_status = L4SHMC_CHUNK_CLEAR;
249   p->_magic  = L4SHMC_CHUNK_MAGIC;
250   strncpy(p->_name, chunk_name, sizeof(p->_name));
251   p->_name[sizeof(p->_name) - 1] = 0;
252   // Ensure that other CPUs have correct data before inserting chunk
253   __sync_synchronize();
254
255   if (prev)
256     prev->_next = offs;
257   else
258     shm_addr->_first_chunk = offs;
259
260   __sync_synchronize();
261   shm_addr->lock = SHMAREA_LOCK_FREE;
262
263   chunk->_chunk    = p;
264   chunk->_shm      = shmarea;
265   chunk->_sig      = NULL;
266   chunk->_capacity = chunk_capacity;
267
268   return L4_EOK;
269 out_free_lock:
270   shm_addr->lock = SHMAREA_LOCK_FREE;
271   return ret;
272 }
273
274 L4_CV long
275 l4shmc_area_size_free(l4shmc_area_t *shmarea)
276 {
277   long ret;
278   l4_addr_t offs = 0;
279   while ((ret = next_chunk(shmarea, offs)) > 0)
280     offs = ret;
281   if (ret < 0)
282     return ret;
283   ret = shmarea->_size - offs;
284   return ret > 0 ? ret : 0;
285 }
286
287 L4_CV long
288 l4shmc_add_signal(l4shmc_area_t *shmarea,
289                  const char *signal_name,
290                  l4shmc_signal_t *signal)
291 {
292   /* Now that the chunk is allocated in the shm, lets get the UIRQ
293    * and register it */
294   long r;
295   l4re_namespace_t tmp;
296   char b[L4SHMC_NAME_STRINGLEN + L4SHMC_SIGNAL_NAME_SIZE + 5]; // strings + "sig-"
297
298   tmp = l4re_env_get_cap(shmarea->_name);
299   if (l4_is_invalid_cap(tmp))
300     return -L4_ENOENT;
301
302   signal->_sigcap = l4re_util_cap_alloc();
303   if (l4_is_invalid_cap(signal->_sigcap))
304     return -L4_ENOMEM;
305
306   if ((r = l4_error(l4_factory_create_irq(l4re_env()->factory,
307                                           signal->_sigcap))))
308     goto out_free_cap;
309
310   snprintf(b, sizeof(b) - 1, "sig-%s", signal_name);
311   b[sizeof(b) - 1] = 0;
312
313   r = l4re_ns_register_obj_srv(tmp, b, signal->_sigcap, 0);
314   if (r)
315     goto out_unmap_irq;
316
317   return L4_EOK;
318
319 out_unmap_irq:
320   l4_task_unmap(L4_BASE_TASK_CAP,
321                 l4_obj_fpage(signal->_sigcap, 0, L4_FPAGE_RWX),
322                 L4_FP_ALL_SPACES);
323 out_free_cap:
324   l4re_util_cap_free(signal->_sigcap);
325   return r;
326 }
327
328 L4_CV long
329 l4shmc_get_chunk_to(l4shmc_area_t *shmarea,
330                     const char *chunk_name,
331                     l4_umword_t timeout_ms,
332                     l4shmc_chunk_t *chunk)
333 {
334   l4_kernel_clock_t try_until = l4re_kip()->clock + (timeout_ms * 1000);
335   long ret;
336
337   do
338     {
339       l4_addr_t offs = 0;
340       while ((ret = next_chunk(shmarea, offs)) > 0)
341         {
342           l4shmc_chunk_desc_t *p;
343           offs = ret;
344           p = chunk_get(offs, shmarea->_local_addr);
345           if (!strcmp(p->_name, chunk_name))
346             { // found it!
347                chunk->_shm      = shmarea;
348                chunk->_chunk    = p;
349                chunk->_sig      = NULL;
350                chunk->_capacity = p->_capacity;
351                if (chunk->_capacity > shmarea->_size ||
352                    chunk->_capacity + offs > shmarea->_size)
353                   return -L4_EIO;
354                return L4_EOK;
355             }
356         }
357       if (ret < 0)
358         return ret;
359
360       if (!timeout_ms)
361         break;
362
363       l4_sleep(100);
364     }
365   while (l4re_kip()->clock < try_until);
366
367   return -L4_ENOENT;
368 }
369
370 L4_CV long
371 l4shmc_attach_signal_to(l4shmc_area_t *shmarea,
372                        const char *signal_name,
373                        l4_cap_idx_t thread,
374                        l4_umword_t timeout_ms,
375                        l4shmc_signal_t *signal)
376 {
377   long r = L4_EOK;
378
379   r = l4shmc_get_signal_to(shmarea, signal_name, timeout_ms, signal);
380   if (r)
381     goto out;
382
383   if ((r = l4_error(l4_irq_attach(signal->_sigcap,
384                                   (l4_umword_t)signal, thread))))
385     {
386       printf("Error on irq_attach(): %ld (sigcap %lx, sig_id %lx, thread %lx\n",
387              r, signal->_sigcap, l4_debugger_global_id(signal->_sigcap),
388              thread);
389       l4re_util_cap_free(signal->_sigcap);
390     }
391
392 out:
393   return r;
394 }
395
396 L4_CV long
397 l4shmc_get_signal_to(l4shmc_area_t *shmarea,
398                     const char *signal_name,
399                     l4_umword_t timeout_ms,
400                     l4shmc_signal_t *signal)
401 {
402   char b[L4SHMC_NAME_STRINGLEN + L4SHMC_SIGNAL_NAME_SIZE + 5]; // strings + "sig-"
403   l4re_namespace_t ns;
404   l4_cpu_time_t timeout;
405
406   ns = l4re_env_get_cap(shmarea->_name);
407   if (l4_is_invalid_cap(ns))
408     return -L4_ENOENT;
409
410   signal->_sigcap = l4re_util_cap_alloc();
411   if (l4_is_invalid_cap(signal->_sigcap))
412     return -L4_ENOMEM;
413
414   snprintf(b, sizeof(b) - 1, "sig-%s", signal_name);
415   b[sizeof(b) - 1] = 0;
416
417   timeout = l4re_kip()->clock + timeout_ms * 1000;
418   while (1)
419     {
420       int e = l4re_ns_query_to_srv(ns, b, signal->_sigcap, timeout_ms);
421       if (e != -L4_ENOENT)
422         {
423           if (e)
424             l4re_util_cap_free(signal->_sigcap);
425           return e;
426         }
427       if (l4re_kip()->clock < timeout)
428         l4_sleep(100);
429       else
430         break;
431     }
432
433   return -L4_ENOENT;
434 }
435
436
437
438 L4_CV long
439 l4shmc_connect_chunk_signal(l4shmc_chunk_t *chunk,
440                             l4shmc_signal_t *signal)
441 {
442   chunk->_sig = signal;
443   return L4_EOK;
444 }
445
446 L4_CV long
447 l4shmc_enable_signal(l4shmc_signal_t *s)
448 {
449   return l4_error(l4_irq_unmask(s->_sigcap));
450 }
451
452 L4_CV long
453 l4shmc_enable_chunk(l4shmc_chunk_t *p)
454 {
455   return l4shmc_enable_signal(p->_sig);
456 }
457
458 L4_CV long
459 l4shmc_wait_any_to(l4_timeout_t timeout, l4shmc_signal_t **p)
460 {
461   l4_umword_t l;
462   long r;
463
464   if ((r = l4_ipc_error(l4_ipc_wait(l4_utcb(), &l, timeout), l4_utcb())))
465     return r;
466
467   *p = (l4shmc_signal_t *)l;
468
469   return L4_EOK;
470 }
471
472 L4_CV long
473 l4shmc_wait_signal_to(l4shmc_signal_t *s, l4_timeout_t timeout)
474 {
475   long r;
476
477   if ((r = l4_ipc_error(l4_irq_receive(s->_sigcap, timeout), l4_utcb())))
478     return r;
479
480   return L4_EOK;
481 }
482
483 L4_CV long
484 l4shmc_wait_chunk_to(l4shmc_chunk_t *p, l4_timeout_t to)
485 {
486   return l4shmc_wait_signal_to(p->_sig, to);
487 }