Compiles with GCC
[akaros.git] / user / parlib / src / channel.c
1 /* Copyright (c) 2009 The Regents of the University  of California. 
2  * See the COPYRIGHT files at the top of this source tree for full 
3  * license information.
4  * 
5  * Kevin Klues <klueska@cs.berkeley.edu>    
6  */
7
8 #include <parlib.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <channel.h>
12 #include <ros/syscall.h>
13 #include <arch/arch.h>
14
15 void simulate_rsp(channel_t* ch) {
16         channel_t ch_server;
17         channel_msg_t msg;
18         ch_server.endpoint = ch->endpoint;
19         ch_server.type = CHANNEL_SERVER;
20         ch_server.ring_addr = ch->ring_addr;
21         ch_server.data_addr = ch->data_addr;    
22         BACK_RING_INIT(&(ch_server.ring_side.back), (ch_server.ring_addr), PGSIZE);
23         
24         channel_recvmsg(&ch_server, &msg);
25 }
26
27 error_t channel_create(pid_t server, channel_t* ch, channel_attr_t* ch_attr) {
28         error_t e;
29         void *COUNT(PGSIZE) ring_addr = NULL;
30         void *COUNT(PGSIZE) data_addr = NULL;
31         
32         /*
33          * Attempt to create two shared pages with the 'server' partition.
34          * One for holding our ring buffer, and one for holding our data.
35          * If there is an error at any point during this process, we need
36          * to cleanup and abort, returning the proper error code.  First we
37          * do the page for the ring buffer, deferring the data page till later.
38          */
39         e = sys_shared_page_alloc(&ring_addr, server, PG_RDWR, PG_RDWR);
40         if(e < 0) return e;
41         
42         /* 
43          * If we've made it to here, then we know the shared memory page
44          * for our ring buffer has been mapped successfully into both our own 
45          * address space as well as the server's.  We also know that the 
46          * server has been notified of its creation.  We still haven't mapped in 
47          * our data page, but we will do that after this page has been initialized
48          * properly.  We do this because we need to somehow synchronize the 
49          * the initilization of the ring buffer on this page with 
50          * the server being able to access it.  We do this by forcing the 
51          * initialization now on this end, and only accessing it on the server side
52          * after our data page has been created.
53          */
54         memset(ch, 0, sizeof(channel_t));
55         ch->endpoint = server;
56         ch->ring_addr = (channel_sring_t *COUNT(1)) TC(ring_addr);
57         ch->type = CHANNEL_CLIENT;
58                 
59         /*
60          * As the creator of this channel, we take on the responsibility of 
61          * setting up the ring buffer itself, as well as setting up our own front 
62          * ring so we can push requests (a.ka. messages) out for the server to 
63          * process.  It is the job of the server to setup its back ring so it can
64          * push responses (a.k.a acks) back for us to know the message has been 
65          * processed.
66          */
67         SHARED_RING_INIT(ch->ring_addr);
68         FRONT_RING_INIT(&(ch->ring_side.front), (ch->ring_addr), PGSIZE);
69         
70         /* 
71          * Now we map in the data page on both ends and add a pointer to it in 
72          * our channel struct.
73          */
74         e = sys_shared_page_alloc(&data_addr, server, PG_RDWR, PG_RDONLY);
75         if(e < 0) {
76                 sys_shared_page_free(ring_addr, server);
77                 return e;
78         }
79         ch->data_addr = data_addr;
80         
81         /* 
82          * Once both pages have been mapped, our data structures have been set up, 
83          * and everything is ready to go, we push an empty message out into the 
84          * ring indicating we are ready on our end.  This implicitly waits for the 
85          * server  to push a response indicating it has finished setting up the other 
86          * side of the channel.
87          */
88         channel_msg_t msg;
89         channel_sendmsg(ch, &msg);
90     
91     /*
92      * If everything has gone according to plan, both ends have set up 
93      * their respective ends of the channel and we can return successfully
94      */
95         return ESUCCESS;
96 }
97
98 error_t channel_destroy(channel_t* ch) {
99         sys_shared_page_free(ch->data_addr, ch->endpoint);
100         sys_shared_page_free((void *COUNT(PGSIZE)) TC(ch->ring_addr), ch->endpoint);
101         return ESUCCESS;
102 }
103
104 error_t channel_sendmsg(channel_t* ch, channel_msg_t* msg) {
105         /*
106          * As a first cut implementation, just copy the message handed to 
107          * us into our ring buffer and copy the buffer pointed to in 
108          * the message at the first address in our shared data page.
109          */
110         channel_msg_t* msg_copy;
111         msg_copy = RING_GET_REQUEST(&(ch->ring_side.front), 
112                                     ch->ring_side.front.req_prod_pvt++);
113         msg_copy->len = msg->len;
114         msg_copy->buf = 0;
115         memcpy(ch->data_addr + (size_t)(msg_copy->buf), msg->buf, msg->len);
116         
117         /*
118          * Now that we have copied the message properly, we push the request out
119          * and wait for a response from the server.
120          */
121         RING_PUSH_REQUESTS(&(ch->ring_side.front));
122         
123         while (!(RING_HAS_UNCONSUMED_RESPONSES(&(ch->ring_side.front))))
124                 cpu_relax();
125         RING_GET_RESPONSE(&(ch->ring_side.front), ch->ring_side.front.rsp_cons++);
126         
127         return ESUCCESS;
128 }
129
130 error_t channel_create_wait(channel_t* ch, channel_attr_t* ch_attr) {
131 #if 0
132         error_t e;
133         pid_t* client;
134         void *COUNT(PGSIZE) ring_addr = NULL;
135         void *COUNT(PGSIZE) data_addr = NULL;
136
137         /* 
138          * Set the type of the channel to the server
139          */
140         ch->type = CHANNEL_SERVER;
141         
142         /*
143          * Wait for the shared ring page to be established and set up
144          * the channel struct with its properties
145          */
146         sysevent_shared_page_alloc_wait(client, ring_addr);
147         ch->endpoint = *client;
148         ch->ring_addr = ring_addr;
149         
150         /*
151          * Now wait for the shared data page to be established and then set up
152          * the backring of the shared ring buffer.
153          */
154         sysevent_shared_page_alloc_wait(client, data_addr);
155         ch->data_addr = data_addr;      
156         BACK_RING_INIT(&(ch->ring_side.back), (ch->ring_addr), PGSIZE);
157         
158         /*
159          * If we've reached this point, then the creating side should already
160          * have a message sitting in the ring buffer waiting for use to 
161          * process.  Now we pull that message off and acknowledge it.
162          */
163         channel_msg_t msg;
164         channel_recvmsg(&ch, &msg);
165 #endif  
166         return ESUCCESS;
167 }
168
169 error_t channel_recvmsg(channel_t* ch, channel_msg_t* msg) {
170         /*
171          * First copy the data contained in the message from shared page pointed 
172          * to by the entry in the ring buffer to the msg struct passedinto this 
173          * function
174          */
175         channel_msg_t* msg_copy = RING_GET_REQUEST(&(ch->ring_side.back), 
176                                                   ch->ring_side.back.req_cons++);
177         msg->len = msg_copy->len; 
178         memcpy(msg->buf, ch->data_addr + (size_t)(msg_copy->buf), msg->len);
179         
180         /*
181          * Then acknowledge that its been serviced / in the process of being 
182          * serviced.
183          */     
184         channel_ack_t* ack = RING_GET_RESPONSE(&(ch->ring_side.back), 
185                                                ch->ring_side.back.rsp_prod_pvt++);      
186         RING_PUSH_RESPONSES(&(ch->ring_side.back));     
187
188         return ESUCCESS;
189 }