Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(164)

Side by Side Diff: kernel/port.c

Issue 1437453002: [kernel][ports] Add basic ports functionality (Closed) Base URL: https://github.com/travisg/lk.git@master
Patch Set: travis review Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « include/kernel/port.h ('k') | kernel/rules.mk » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright (c) 2015 Carlos Pizano-Uribe cpu@chromium.org
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 /**
25 * @file
26 * @brief Port object functions
27 * @defgroup event Events
28 *
29 */
30
31 #include <kernel/port.h>
32 #include <debug.h>
33 #include <list.h>
34 #include <malloc.h>
35 #include <assert.h>
36 #include <string.h>
37 #include <err.h>
38 #include <kernel/thread.h>
39
40 #define WRITEPORT_MAGIC 'prtw'
41 #define READPORT_MAGIC 'prtr'
42 #define PORTGROUP_MAGIC 'prtg'
43
44 #define PORT_BUFF_SIZE 8
45 #define PORT_BUFF_SIZE_BIG 64
46
47 #define RESCHEDULE_POLICY true
48
49 typedef struct {
50 uint max;
51 uint head;
52 uint tail;
53 port_packet_t packet[1];
54 } port_buf_t;
55
56 typedef struct {
57 int magic;
58 struct list_node node;
59 port_buf_t* buf;
60 struct list_node rp_list;
61 char name[PORT_NAME_LEN];
62 } write_port_t;
63
64 // todo (cpu) add the |mode| bit to the write port and check
65 // that unicast ports have only one read port.
66
67 typedef struct {
68 int magic;
69 wait_queue_t wait;
70 struct list_node rp_list;
71 } port_group_t;
72
73 typedef struct {
74 int magic;
75 struct list_node node;
76 port_buf_t* buf;
77 void* ctx;
78 wait_queue_t wait;
79 write_port_t* wport;
80 port_group_t* gport;
81 } read_port_t;
82
83
84 static struct list_node write_port_list;
85
86
87 static port_buf_t* make_buf(uint pk_count)
88 {
89 uint size = sizeof(port_buf_t) + ((pk_count - 1) * sizeof(port_packet_t));
90 port_buf_t* buf = (port_buf_t*) malloc(size);
91 if (!buf)
92 return NULL;
93 buf->max = pk_count;
94 buf->head = buf->tail = 0;
95 return buf;
96 }
97
98 static int buf_write(const port_packet_t* packets, size_t count, port_buf_t* buf )
99 {
100 // todo (cpu). circular buffer write.
101 return 0;
102 }
103
104 static int buf_read(port_buf_t* buf, port_result_t* pr)
105 {
106 // todo (cpu). circular buffer read.
107 return 0;
108 }
109
110 // must be called before any use of ports.
111 void port_init(void)
112 {
113 list_initialize(&write_port_list);
114 }
115
116 status_t port_create(const char* name, port_mode_t mode, port_t* port)
117 {
118 if (!name || !port)
119 return ERR_INVALID_ARGS;
120
121 // only unicast ports can have a large buffer.
122 if (mode & PORT_MODE_BROADCAST) {
123 if (mode & PORT_MODE_BIG_BUFFER)
124 return ERR_INVALID_ARGS;
125 }
126
127 // lookup for existing port, return that if found.
128 write_port_t* wp = NULL;
129 THREAD_LOCK(state1);
130 list_for_every_entry(&write_port_list, wp, write_port_t, node) {
131 if (strcmp(wp->name, name) == 0) {
132 THREAD_UNLOCK(state1);
133 *port = (void*)wp;
134 return NO_ERROR;
135 }
136 }
137 THREAD_UNLOCK(state1);
138
139 // not found, create the write port and the circular buffer.
140 wp = malloc(sizeof(write_port_t));
141 if (!wp)
142 return ERR_NO_MEMORY;
143
144 memset(wp, 0, sizeof(write_port_t));
145 wp->magic = WRITEPORT_MAGIC;
146 strlcpy(wp->name, name, sizeof(wp->name));
147 list_initialize(&wp->rp_list);
148
149 uint size = mode & PORT_MODE_BIG_BUFFER ? PORT_BUFF_SIZE_BIG : PORT_BUFF_SI ZE;
150 wp->buf = make_buf(size);
151 if (!wp->buf) {
152 free(wp);
153 return ERR_NO_MEMORY;
154 }
155
156 // race condtion! a port with the same name could have been created
157 // by another thread at is point.
158 THREAD_LOCK(state2);
159 list_add_tail(&write_port_list, &wp->node);
160 THREAD_UNLOCK(state2);
161
162 *port = (void*)wp;
163 return NO_ERROR;
164 }
165
166 status_t port_open(const char* name, void* ctx, port_t* port)
167 {
168 if (!name || !port)
169 return ERR_INVALID_ARGS;
170
171 // assume success; create the read port and buffer now.
172 read_port_t* rp = malloc(sizeof(read_port_t));
173 if (!rp)
174 return ERR_NO_MEMORY;
175
176 memset(rp, 0, sizeof(read_port_t));
177 rp->magic = READPORT_MAGIC;
178 wait_queue_init(&rp->wait);
179 rp->ctx = ctx;
180
181 // |buf| might not be needed, but we always allocate outside the lock.
182 port_buf_t* buf = make_buf(PORT_BUFF_SIZE);
183
184 // find the named write port and associate it with read port.
185 status_t rc = ERR_NOT_FOUND;
186
187 THREAD_LOCK(state);
188 write_port_t* wp = NULL;
189 list_for_every_entry(&write_port_list, wp, write_port_t, node) {
190 if (strcmp(wp->name, name) == 0) {
191 // found; add read port to write port list.
cpu_(ooo_6.6-7.5) 2015/11/11 00:36:31 note that the list_add_tail was not unrolled previ
192 rp->wport = wp;
193 if (wp->buf) {
194 // this is the first read port; transfer the circular buffer.
195 list_add_tail(&wp->rp_list, &rp->node);
196 rp->buf = wp->buf;
197 wp->buf = NULL;
198 rc = NO_ERROR;
199 } else if (buf) {
200 // not first read port; use the new (small) circular buffer.
201 list_add_tail(&wp->rp_list, &rp->node);
202 rp->buf = buf;
203 buf = NULL;
204 rc = NO_ERROR;
205 } else {
206 // |buf| allocation failed and the buffer was needed.
cpu_(ooo_6.6-7.5) 2015/11/11 00:36:31 note the test asked for line 182 is done in line 1
207 rc = ERR_NO_MEMORY;
208 }
209 break;
210 }
211 }
212 THREAD_UNLOCK(state);
213
214 if (buf)
215 free(buf);
216
217 if (rc == NO_ERROR) {
218 *port = (void*)rp;
219 } else {
220 free(rp);
221 }
222 return rc;
223 }
224
225 status_t port_group(port_t* ports, size_t count, port_t* group)
226 {
227 // assume success; create port group now.
228 port_group_t* pg = malloc(sizeof(port_group_t));
229 if (!pg)
230 return ERR_NO_MEMORY;
231
232 memset(pg, 0, sizeof(port_group_t));
233 pg->magic = PORTGROUP_MAGIC;
234 wait_queue_init(&pg->wait);
235 list_initialize(&pg->rp_list);
236
237 status_t rc = NO_ERROR;
238
239 THREAD_LOCK(state);
240 for (size_t ix = 0; ix != count; ix++) {
241 read_port_t* rp = (read_port_t*)ports[ix];
242 if ((rp->magic != READPORT_MAGIC) || rp->gport) {
243 // wrong type of port, or port already part of a group,
244 // in any case, undo the changes to the previous read ports.
245 for (size_t jx = 0; jx != ix; jx++) {
246 ((read_port_t*)ports[jx])->gport = NULL;
247 }
248 rc = ERR_BAD_HANDLE;
249 break;
250 }
251 // link port group and read port.
252 rp->gport = pg;
253 list_add_tail(&pg->rp_list, &rp->node);
254 }
255 THREAD_UNLOCK(state);
256
cpu_(ooo_6.6-7.5) 2015/11/11 00:36:31 redid this so there are less unlocks and to match
257 if (rc == NO_ERROR) {
258 *group = (port_t*)pg;
259 } else {
260 free(pg);
261 }
262 return rc;
263 }
264
265 status_t port_write(port_t port, const port_packet_t* pk, size_t count)
266 {
267 write_port_t* wp = (write_port_t*)port;
268 THREAD_LOCK(state);
269 if (wp->magic != WRITEPORT_MAGIC) {
270 // wrong port type.
271 THREAD_UNLOCK(state);
272 return ERR_BAD_HANDLE;
273 }
274
275 if (wp->buf) {
276 // there are no read ports, just write to the buffer.
277 buf_write(pk, count, wp->buf);
278 } else {
279 // there are read ports. for each, write and attempt to wake a thread
280 // from the port group or from the read port itself.
281 read_port_t* rp;
282 list_for_every_entry(&wp->rp_list, rp, read_port_t, node) {
283 buf_write(pk, count, rp->buf);
284
285 int count = 0;
286 if (rp->gport) {
287 count = wait_queue_wake_one(&rp->gport->wait, RESCHEDULE_POLICY, NO_ERROR);
288 }
289 if (!count) {
290 wait_queue_wake_one(&rp->wait, RESCHEDULE_POLICY, NO_ERROR);
291 }
292 }
293 }
294
295 THREAD_UNLOCK(state);
296 return NO_ERROR;
297 }
298
299 static inline status_t read_no_lock(read_port_t* rp, lk_time_t timeout, port_res ult_t* result)
300 {
301 int read = buf_read(rp->buf, result);
302 if (read > 0) {
303 result->ctx = rp->ctx;
304 return NO_ERROR;
305 } else if (read < 0) {
306 return (status_t)read;
307 }
308 // early return allows compiler to elide the rest for the group read case.
309 if (!timeout)
310 return ERR_TIMED_OUT;
311
312 status_t rc = wait_queue_block(&rp->wait, timeout);
313 if (rc != NO_ERROR)
314 return rc;
315 // recursive tail call is usually optimized away with a goto.
316 return read_no_lock(rp, timeout, result);
317 }
318
319 status_t port_read(port_t port, lk_time_t timeout, port_result_t* result)
320 {
321 status_t rc = ERR_GENERIC;
322 read_port_t* rp = (read_port_t*)port;
323
324 THREAD_LOCK(state);
325 if (rp->magic == READPORT_MAGIC) {
326 // dealing with a single port.
327 rc = read_no_lock(rp, timeout, result);
328 } else if (rp->magic == PORTGROUP_MAGIC) {
329 // dealing with a port group.
330 port_group_t* pg = (port_group_t*)port;
331 do {
332 // read each port with no timeout.
333 list_for_every_entry(&pg->rp_list, rp, read_port_t, node) {
334 rc = read_no_lock(rp, 0, result);
335 if (rc != ERR_TIMED_OUT)
336 goto read_exit;
337 }
338 // no data, block on the group waitqueue.
339 rc = wait_queue_block(&pg->wait, timeout);
340 } while (rc == NO_ERROR);
341 } else {
342 // wrong port type.
343 rc = ERR_BAD_HANDLE;
344 }
345
346 read_exit:
347 THREAD_UNLOCK(state);
348 return rc;
349 }
350
351 status_t port_destroy(port_t port)
352 {
353 // todo (cpu)
354 return NO_ERROR;
355 }
356
357 status_t port_close(port_t port)
358 {
359 // todo (cpu)
360 return NO_ERROR;
361 }
362
OLDNEW
« no previous file with comments | « include/kernel/port.h ('k') | kernel/rules.mk » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698