DeviceQueue: shared memory queue implementation
[barrelfish] / lib / devif / desc_queue.c
1 /*
2  * Copyright (c) 2016 ETH Zurich.
3  * All rights reserved.
4  *
5  * This file is distributed under the terms in the attached LICENSE file.
6  * If you do not find this file, copies can be found by writing to:
7  * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
8  */
9
10 #include <barrelfish/barrelfish.h>
11 #include <devif/queue_interface.h>
12 #include "desc_queue.h"
13 #include "dqi_debug.h"
14
15
16 struct __attribute__((aligned(DESCQ_ALIGNMENT))) desc {
17     regionid_t rid; // 4
18     bufferid_t bid; // 8
19     lpaddr_t addr; // 16
20     size_t len; // 24
21     uint64_t flags; // 32
22     uint8_t pad[32];
23 };
24
25 union __attribute__((aligned(DESCQ_ALIGNMENT))) pointer {
26     size_t value;
27     uint8_t pad[64];
28 };
29
30 struct descq {
31     // Shared memory of the queue
32     struct capref shm;
33     size_t slots;
34
35     size_t local_head;
36     size_t local_tail;
37     // Queue pointers
38     volatile union pointer* head;
39     volatile union pointer* tail;
40
41     // The queue itself
42     struct desc* descs;
43 };
44
45
46 /**
47  * @brief initialized a descriptor queue
48  *
49  * @param q                     Return pointer to the descriptor queue
50  * @param shm                   Cap of the shared memory of the queue
51  * @param slots                 Number of slots in the queue
52  *
53  * @returns error on failure or SYS_ERR_OK on success
54  */
55 errval_t descq_init(struct descq** q,
56                     struct capref shm,
57                     size_t slots)
58 {
59     errval_t err;
60     struct descq* tmp;
61     
62     // Init basic struct fields
63     tmp = malloc(sizeof(struct descq));
64     assert(tmp != NULL);
65
66     tmp->shm = shm;
67     tmp->slots = slots-2;
68
69     struct frame_identity id;
70     // Check if the frame is big enough
71     err = invoke_frame_identify(shm, &id);
72     if (err_is_fail(err)) {
73         free(tmp);
74         return DEVQ_ERR_DESCQ_INIT;
75     } 
76
77     if (id.bytes < DESCQ_ALIGNMENT*slots) {
78         free(tmp);
79         return DEVQ_ERR_DESCQ_INIT;
80     }
81
82     // TODO what about the non cache coherent case?
83     err = vspace_map_one_frame_attr((void**) &(tmp->head),
84                                     slots*DESCQ_ALIGNMENT, shm, 
85                                     VREGION_FLAGS_READ_WRITE, NULL, NULL);
86     if (err_is_fail(err)) {
87         free(tmp);
88         return DEVQ_ERR_DESCQ_INIT;
89     }
90
91     tmp->tail = tmp->head + 1;
92     tmp->descs = (struct desc*) tmp->head + 2;
93     tmp->tail->value = 0;
94     tmp->head->value = 0;    
95     tmp->local_head = 0;
96     tmp->local_tail = 0;
97
98     *q = tmp;
99     return SYS_ERR_OK;
100 }
101
102
103 /**
104  * @brief Destroys a descriptor queue and frees its resources
105  *
106  * @param q                     The descriptor queue
107  *
108  * @returns error on failure or SYS_ERR_OK on success
109  */
110 errval_t descq_destroy(struct descq* q)
111 {   
112     errval_t err;
113     err = vspace_unmap((void*) (q->descs));
114     if (err_is_fail(err)){
115         return err;
116     }
117     
118     free(q);
119
120     return SYS_ERR_OK;
121 }
122
123 /**
124  * @brief Enqueue a descriptor (as seperate fields) 
125  *        into the descriptor queue
126  *
127  * @param q                     The descriptor queue
128  * @param region_id             Region id of the enqueued buffer
129  * @param buffer_id             Buffer id of the buffer
130  * @param base                  Physical address of hte buffer
131  * @param len                   Lenght of the buffer
132  * @param misc_flags            Miscellaneous flags
133  *
134  * @returns error if queue is full or SYS_ERR_OK on success
135  */
136 errval_t descq_enqueue(struct descq* q,
137                        regionid_t region_id,
138                        bufferid_t buffer_id,
139                        lpaddr_t base,
140                        size_t len,
141                        uint64_t misc_flags)
142 {
143     if (descq_full(q)) {
144         return DEVQ_ERR_TX_FULL;
145     }
146     
147     size_t head = q->local_head;
148     q->descs[head].rid = region_id;
149     q->descs[head].bid = buffer_id;
150     q->descs[head].addr = base;
151     q->descs[head].len = len;
152     q->descs[head].flags = misc_flags;
153     
154     // only write local head
155     q->local_head = q->local_head + 1 % q->slots;
156
157     return SYS_ERR_OK;
158 }
159 /**
160  * @brief Dequeue a descriptor (as seperate fields) 
161  *        from the descriptor queue
162  *
163  * @param q                     The descriptor queue
164  * @param region_id             Return pointer to the region id of 
165  *                              the denqueued buffer
166  * @param buffer_id             Return pointer to the buffer id of the buffer
167  * @param base                  Return pointer to the physical address 
168  *                              of the buffer
169  * @param len                   Return pointer to the lenght of the buffer
170  * @param misc_flags            Return pointer to miscellaneous flags
171  *
172  * @returns error if queue is empty or SYS_ERR_OK on success
173  */
174 errval_t descq_dequeue(struct descq* q,
175                        regionid_t* region_id,
176                        bufferid_t* buffer_id,
177                        lpaddr_t* base,
178                        size_t* len,
179                        uint64_t* misc_flags)
180 {
181     if (descq_empty(q)) {
182         return DEVQ_ERR_RX_EMPTY;
183     }
184     
185     size_t tail = q->local_tail;
186     *region_id = q->descs[tail].rid;
187     *buffer_id = q->descs[tail].bid;
188     *base = q->descs[tail].addr;
189     *len = q->descs[tail].len;
190     *misc_flags = q->descs[tail].flags;
191     
192     q->local_tail = q->local_tail + 1 % q->slots;
193
194     return SYS_ERR_OK;
195 }
196
197 /**
198  * @brief Writes the local head pointer into the shared memory
199  *        making the state of the queue visible to the other end
200  *
201  * @param q                     The descriptor queue
202  *
203  */
204 void descq_writeout_head(struct descq* q)
205 {
206     q->head->value = q->local_head;
207 }
208
209 /**
210  * @brief Writes the local tail pointer into the shared memory
211  *        making the state of the queue visible to the other end
212  *
213  * @param q                     The descriptor queue
214  *
215  */
216 void descq_writeout_tail(struct descq* q)
217 {
218     q->tail->value = q->local_tail;
219 }
220
221 /**
222  * @brief Check if the descriptor queue is full
223  *
224  * @param q                     The descriptor queue
225  *
226  * @returns true if the queue is full, otherwise false
227  */
228 bool descq_full(struct descq* q)
229 {
230     size_t head = q->local_head;
231     size_t tail = q->tail->value;
232     if (head >= tail) {
233         return ((q->slots - (head - tail)) == 0);
234     } else {
235         return ((q->slots - (head + q->slots - tail)) == 0);
236     }
237 }
238 /**
239  * @brief Check if the descriptor queue is empty
240  *
241  * @param q                     The descriptor queue
242  *
243  * @returns true if the queue is empty, otherwise false
244  */
245 bool descq_empty(struct descq* q)
246 {
247     size_t head = q->head->value;
248     size_t tail = q->local_tail;
249     if (head >= tail) {
250         return ((head - tail) == 0);
251     } else {
252         return (((head + q->slots) - tail) == 0);
253     }
254 }
255 /**
256  * @brief Returns the number of occupied slots in the queue
257  *
258  * @param q                     The descriptor queue
259  *
260  * @returns the number of occupied slots
261  */
262 size_t descq_full_slots(struct descq* q)
263 {
264     size_t head = q->head->value;
265     size_t tail = q->local_tail;
266     if (head >= tail) {
267         return (head - tail);
268     } else {
269         return (head + q->slots) - tail;
270     }
271 }