1/**
2 * \file
3 * \brief e1000 continuation management
4 *
5 * This file provides a generic way of managing continuation for messages
6 * of different types
7 */
8
9/*
10 * Copyright (c) 2007, 2008, 2009, 2010, ETH Zurich.
11 * All rights reserved.
12 *
13 * This file is distributed under the terms in the attached LICENSE file.
14 * If you do not find this file, copies can be found by writing to:
15 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
16 */
17
18#ifndef CONTMNG_C_
19#define CONTMNG_C_
20
21#include <string.h>
22#include <barrelfish/barrelfish.h>
23
24#include <contmng/contmng.h>
25
26/* QUEUE_DEBUG enables the debug statements
27 * NOTE: This option does not compile for all architecture/machine due to
28 * dependance on disp_name and dispatch.h
29 */
30//#define QUEUE_DEBUG 1
31
32static void cont_queue_send_next_message(struct cont_queue *q);
33
34
35#ifdef QUEUE_DEBUG
36#include <dispatch.h>
37static void qprintf_mac(struct cont_queue *q, char *msg)
38{
39    if (q->debug) {
40        printf("CQ: %s [%d:%.*s] [%d] [%d] [%d] qqqqqq\n",
41                msg, disp_get_core_id(),
42                64, q->name, q->head, q->tail, q->running);
43    }
44}
45#define qprintf(x...) qprintf_mac(x)
46
47#else
48#define qprintf(x...) ((void)0)
49#endif /* QUEUE_DEBUG */
50
51
52//****************************************************************************
53/************** Generic continuation queue implementation *******************/
54/* WARN: use only when your responses contains only integers */
55
56
57/* allocates the memory for continuation queue
58    It includes the memory for MAX_QUEUE_SIZE of elements also */
59struct cont_queue *create_cont_q(char *name)
60{
61    struct cont_queue *ptr = NULL;
62    ptr = (struct cont_queue *) malloc(sizeof(struct cont_queue));
63    if (ptr == NULL) {
64        printf("ERROR: malloc failed in create_cont_q\n");
65        abort();
66        /* FIXME: introduce new error and return the error */
67    }
68    memset(ptr, 0, sizeof(struct cont_queue));
69    if (name != NULL) {
70        strncpy(ptr->name, name, 63);
71    }
72    return ptr;
73}/* end function: create_cont_q */
74
75
76
77// Tells how many slots are actually used
78int queue_used_slots(struct cont_queue *q)
79{
80    uint64_t nhead = q->head % MAX_QUEUE_SIZE;
81    uint64_t ntail = q->tail % MAX_QUEUE_SIZE;
82    if ( nhead >= ntail) {
83        return (nhead - ntail);
84    } else {
85        return (ntail + (MAX_QUEUE_SIZE - nhead));
86    }
87} // end function: queue_used_slots
88
89
90
91/* Tells if queue has enough space to add more events,
92 * or if the producer should pause for a while */
93int queue_free_slots(struct cont_queue *q)
94{
95
96    if (((q->head + 1) % MAX_QUEUE_SIZE) > q->tail) {
97        return (MAX_QUEUE_SIZE -
98                    (((q->head + 1) % MAX_QUEUE_SIZE) - q->tail)
99               );
100    } else {
101        return q->tail - ((q->head + 1) % MAX_QUEUE_SIZE);
102    }
103} // end function
104
105
106int is_enough_space_in_queue(struct cont_queue *q)
107{
108    if (queue_free_slots(q) > (MAX_QUEUE_SIZE/10)) {
109        return true;
110    } else {
111        return false;
112    }
113} // end function: is_enough_space_in_queue
114
115
116bool can_enqueue_cont_q(struct cont_queue *q)
117{
118    if (((q->head + 1) % MAX_QUEUE_SIZE) == q->tail) {
119        return false;
120    } else {
121        return true;
122    }
123} // end function: can_enqueue_cont_q
124
125/* Adds element to the queue */
126void enqueue_cont_q(struct cont_queue *q, struct q_entry *entry)
127{
128
129    if (((q->head + 1) % MAX_QUEUE_SIZE) == q->tail)
130    {
131        qprintf(q, "ERROR: Queue full");
132        printf("ERROR:  Queue [%s] is full\n", q->name);
133        printf("ERROR: %.*s\n", 64, q->name);
134        printf("ERROR: CQ: head [%d], tail [%d] qqqqqq\n", q->head, q->tail);
135        /* __builtin_return_address is not supported in ARM toolchain */
136
137#ifdef QUEUE_DEBUG
138        printf("callstack: %p %p %p %p\n",
139	     __builtin_return_address(0),
140	     __builtin_return_address(1),
141	     __builtin_return_address(2),
142	     __builtin_return_address(3));
143#endif // QUEUE_DEBUG
144
145        // Following two lines are there to force the seg-fault of the domain
146        // as abort was showing some strange behaviour
147//        int *p = NULL;
148//        *p = 43;
149        abort();
150//        return CONT_ERR_NO_MORE_SLOTS;
151    }
152
153    /* Copying the structure */
154    q->qelist[q->head].binding_ptr = entry->binding_ptr;
155    q->qelist[q->head].cap = entry->cap;
156    q->qelist[q->head].handler = entry->handler;
157    q->qelist[q->head].state = 0;
158    q->qelist[q->head].fname = entry->fname;
159
160    for(int i = 0; i < MAX_PARAMS; ++i) {
161        q->qelist[q->head].plist[i] = entry->plist[i];
162    }
163
164    q->head = (q->head + 1) % MAX_QUEUE_SIZE;
165
166    // If no continuations are running, then execute this one directly
167    if (((q->tail + 1) % MAX_QUEUE_SIZE) == q->head) {
168//    if (q->running == 0) {
169        q->running = 1;
170        qprintf(q, "directly-sending");
171        cont_queue_send_next_message(q);
172    }
173
174    //otherwise continuation function will trigger sending next queue element
175} // end function: enqueue_cont_q
176
177
178// called from continuation registered with flounder
179//    WARN: should not be called directly
180void cont_queue_callback(void *arg)
181{
182    struct cont_queue *q = (struct cont_queue *)arg;
183
184    q->tail = (q->tail + 1) % MAX_QUEUE_SIZE;
185    qprintf(q, "from-continuation");
186    cont_queue_send_next_message(q);
187} /* end function: cont_queue_callback */
188
189
190/* Sends the top of queue msg
191    NOTE: this function does not increment the tail.  It calls handler,
192        which registers "cont_queue_callback" as callback with flounder,
193        and that callback function increments the tail!!!
194            complicated? huh.. :-P */
195void cont_queue_send_next_message(struct cont_queue *q)
196{
197//    qprintf(q, "sending-msg");
198
199    if(q->head == q->tail){
200        q->running = 0;
201        qprintf(q, "Queue-empty-Recursion-End!!");
202        return;
203    }
204    errval_t err = q->qelist[q->tail].handler(q->qelist[q->tail]);
205    if (err_is_fail(err)) {
206        if (err == FLOUNDER_ERR_TX_BUSY ) {
207            qprintf(q, "sending:FLO-BUSY");
208        } else {
209            qprintf(q, "sending:FLO FAIL");
210            USER_PANIC_ERR(err, "cont_queue_send_next_message ");
211        }
212    }
213    qprintf(q, "sending-msg done: ");
214} /* end function: cont_queue_send_next_message */
215
216void cont_queue_show_queue(struct cont_queue *q)
217{
218
219    int len = 0;
220    len = q->head - q->tail;
221    printf("queue [%s] len [%d]==> head [%d] - tail [%d]\n",
222            q->name, len, q->head, q->tail);
223
224    /*
225    int i = 0;
226    int idx = 0;
227    idx = q->tail;
228    while (idx != q->head){
229        printf("elem %d: [%s], state %u\n", idx, q->qelist[idx].fname,
230                q->qelist[idx].history);
231        idx = (idx + 1) % MAX_QUEUE_SIZE;
232    }
233
234    printf("Showing elements which are already sent!!\n");
235    idx = q->tail;
236    for (i = 0; i < 10; ++i){
237        idx = (idx - 1);
238        if (idx < 0) {
239            idx = MAX_QUEUE_SIZE - 1;
240        }
241        printf("elem %d: [%s], state %d\n", idx, q->qelist[idx].fname,
242                q->qelist[idx].history);
243    }
244    */
245
246} // end function: cont_queue_show_queue
247
248
249/* Following function has nothing to do with cont_queue_management,
250 * and is to be used for debugging.  Ideally this function should not exist. */
251void show_binary_blob (void *data, int len)
252{
253	printf("\nBlob_%d[", len);
254	uint8_t *ptr = data;
255	for (int i = 0 ; i < len; ++i){
256		printf("0x%x,", ptr[i]);
257	}
258	printf("]\n");
259}
260
261#endif // CONTMNG_C_
262