Merge branch 'arrakis'
[barrelfish] / include / barrelfish / ump_impl.h
1 /**
2  * \file
3  * \brief User-space messaging (UMP, formerly URPC) data transport implementation
4  */
5
6 /*
7  * Copyright (c) 2007, 2008, 2009, 2010, ETH Zurich.
8  * All rights reserved.
9  *
10  * This file is distributed under the terms in the attached LICENSE file.
11  * If you do not find this file, copies can be found by writing to:
12  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13  */
14
15 #ifndef UMP_IMPL_H
16 #define UMP_IMPL_H
17
18 #include <stddef.h>
19 #include <stdint.h>
20 #include <stdbool.h>
21 #include <sys/cdefs.h>
22
23 __BEGIN_DECLS
24
25 /**
26  * UMP message size is fixed to cache-line size (64 bytes on x86_64).
27  * FIXME: this should be different transports, not a compile-time constant
28  */
29 // XXX Should be from SKB or whatever.  On Arm this is variable and you need
30 // to read the coproc to see what size it is.
31 #if defined(__x86_64__) || defined(__i386__)
32 # if defined(__scc__)
33 #  define CACHELINE_BYTES 32
34 # else
35 #  define CACHELINE_BYTES 64
36 # endif
37 #elif defined(__arm__)
38 #  define CACHELINE_BYTES 32
39 #elif defined(__aarch64__)
40 // XXX: is this true?
41 #  define CACHELINE_BYTES 64
42 #else
43 # error set CACHELINE_BYTES appropriately
44 #endif
45
46 // Size of a UMP message in Bytes
47 // This needs to be such that ump_payload defined in params in flounder/UMP.hs
48 // and the size of the UMP headers fits into it. It also needs to be a multiple
49 // of a cache-line.
50 #define UMP_PAYLOAD_BYTES  64
51 #define UMP_PAYLOAD_WORDS  (UMP_PAYLOAD_BYTES / sizeof(uintptr_t) - 1)
52 #define UMP_MSG_WORDS      (UMP_PAYLOAD_WORDS + 1)
53 #define UMP_MSG_BYTES      (UMP_MSG_WORDS * sizeof(uintptr_t))
54
55 /// Default size of a unidirectional UMP message buffer, in bytes
56 #define DEFAULT_UMP_BUFLEN  (BASE_PAGE_SIZE / 2 / UMP_MSG_BYTES * UMP_MSG_BYTES)
57
58 // control word is 32-bit, because it must be possible to atomically write it
59 typedef uint32_t ump_control_t;
60 #define UMP_EPOCH_BITS  1
61 #define UMP_HEADER_BITS 31
62
63 struct ump_control {
64     ump_control_t epoch:UMP_EPOCH_BITS;
65     ump_control_t header:UMP_HEADER_BITS;
66 };
67
68 struct ump_message {
69     uintptr_t data[UMP_PAYLOAD_WORDS] __attribute__((aligned (CACHELINE_BYTES)));
70     union {
71         struct ump_control control;
72         uintptr_t raw;
73     } header;
74 };
75 STATIC_ASSERT((sizeof(struct ump_message)%CACHELINE_BYTES)==0, 
76                "Size of UMP message is not a multiple of cache-line size");
77
78 /// Type used for indices of UMP message slots
79 typedef uint16_t ump_index_t;
80 #define UMP_INDEX_BITS         (sizeof(ump_index_t) * NBBY)
81 #define UMP_INDEX_MASK         ((((uintptr_t)1) << UMP_INDEX_BITS) - 1)
82
83 /**
84  * UMP direction
85  */
86 enum ump_direction {
87     UMP_OUTGOING,
88     UMP_INCOMING
89 };
90
91 /**
92  * \brief State of a (one-way) UMP channel
93  */
94 struct ump_chan_state {
95     volatile struct ump_message *buf;  ///< Ring buffer
96     ump_index_t        pos;            ///< Current position
97     ump_index_t        bufmsgs;        ///< Buffer size in messages
98     bool               epoch;          ///< Next Message epoch
99     enum ump_direction dir;            ///< Channel direction
100 };
101
102 /// Cache-aligned size of a #ump_chan_state struct
103 #define UMP_CHAN_STATE_SIZE ROUND_UP(sizeof(struct ump_chan_state), CACHELINE_BYTES)
104
105
106 /**
107  * \brief Initialize UMP channel state
108  *
109  * The channel-state structure and buffer must already be allocated.
110  *
111  * \param       c       Pointer to channel-state structure to initialize.
112  * \param       buf     Pointer to ring buffer for the channel. Must be aligned to a cacheline.
113  * \param       size    Size (in bytes) of buffer. Must be multiple of #UMP_MSG_BYTES
114  * \param       dir     Channel direction.
115  */
116 static inline errval_t ump_chan_state_init(struct ump_chan_state *c,
117                                            volatile void *buf,
118                                            size_t size, enum ump_direction dir)
119 {
120     // check alignment and size of buffer.
121     if (size == 0 || (size % UMP_MSG_BYTES) != 0) {
122         return LIB_ERR_UMP_BUFSIZE_INVALID;
123     }
124
125     if (buf == NULL || (((uintptr_t)buf) % CACHELINE_BYTES) != 0) {
126         return LIB_ERR_UMP_BUFADDR_INVALID;
127     }
128
129     c->pos = 0;
130     c->buf = (volatile struct ump_message *) buf;
131     c->dir = dir;
132     c->bufmsgs = size / UMP_MSG_BYTES;
133     c->epoch = 1;
134
135     if(dir == UMP_INCOMING) {
136         ump_index_t i;
137         for(i = 0; i < c->bufmsgs; i++) {
138             c->buf[i].header.raw = 0;
139         }
140     }
141
142     return SYS_ERR_OK;
143 }
144
145 /**
146  * \brief Return pointer to a message if outstanding on 'c'.
147  *
148  * \param c     Pointer to UMP channel-state structure.
149  *
150  * \return Pointer to message if outstanding, or NULL.
151  */
152 static inline volatile struct ump_message *ump_impl_poll(struct ump_chan_state *c)
153 {
154     assert(c->dir == UMP_INCOMING);
155     ump_control_t ctrl_epoch =  c->buf[c->pos].header.control.epoch;
156     if (ctrl_epoch == c->epoch) {
157         return &c->buf[c->pos];
158     } else {
159         return NULL;
160     }
161 }
162
163 /**
164  * \brief Return pointer to a message if outstanding on 'c' and
165  * advance pointer.
166  *
167  * \param c     Pointer to UMP channel-state structure.
168  *
169  * \return Pointer to message if outstanding, or NULL.
170  */
171 static inline volatile struct ump_message *ump_impl_recv(struct ump_chan_state *c)
172 {
173     volatile struct ump_message *msg = ump_impl_poll(c);
174
175     if(msg != NULL) {
176         if (++c->pos == c->bufmsgs) {
177             c->pos = 0;
178             c->epoch = !c->epoch;
179         }
180         return msg;
181     } else {
182         return NULL;
183     }
184 }
185
186 /**
187  * \brief Determine next position for an outgoing message on a channel, and
188  *   advance send pointer.
189  *
190  * \param c     Pointer to UMP channel-state structure.
191  * \param ctrl  Pointer to storage for control word for next message, to be filled in
192  *
193  * \return Pointer to message if outstanding, or NULL.
194  */
195 static inline volatile struct ump_message *ump_impl_get_next(
196                             struct ump_chan_state *c, struct ump_control *ctrl)
197 {
198     assert(c->dir == UMP_OUTGOING);
199
200     // construct header
201     ctrl->epoch = c->epoch;
202
203     if(debug_notify_syscall) {
204         printf("ump_impl_get_next while forbidden from %p, %p, %p, %p, %p, %p, %p\n",
205                __builtin_return_address(0),
206                __builtin_return_address(1),
207                __builtin_return_address(2),
208                __builtin_return_address(3),
209                __builtin_return_address(4),
210                __builtin_return_address(5),
211                __builtin_return_address(6));
212     }
213
214     volatile struct ump_message *msg = &c->buf[c->pos];
215
216     // update pos
217     if (++c->pos == c->bufmsgs) {
218         c->pos = 0;
219         c->epoch = !c->epoch;
220     }
221
222     return msg;
223 }
224
225 __END_DECLS
226
227 #endif // UMP_IMPL_H