1 /**
2 * Copyright (c) 2010-2012 Broadcom. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions, and the following disclaimer,
9 * without modification.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The names of the above-listed copyright holders may not be used
14 * to endorse or promote products derived from this software without
15 * specific prior written permission.
16 *
17 * ALTERNATIVELY, this software may be distributed under the terms of the
18 * GNU General Public License ("GPL") version 2, as published by the Free
19 * Software Foundation.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include "vchiq_core.h"
35 #include "vchiq_killable.h"
36
37 #define VCHIQ_SLOT_HANDLER_STACK 8192
38
39 #define HANDLE_STATE_SHIFT 12
40
41 #define SLOT_INFO_FROM_INDEX(state, index) (state->slot_info + (index))
42 #define SLOT_DATA_FROM_INDEX(state, index) (state->slot_data + (index))
43 #define SLOT_INDEX_FROM_DATA(state, data) \
44 (((unsigned int)((char *)data - (char *)state->slot_data)) / \
45 VCHIQ_SLOT_SIZE)
46 #define SLOT_INDEX_FROM_INFO(state, info) \
47 ((unsigned int)(info - state->slot_info))
48 #define SLOT_QUEUE_INDEX_FROM_POS(pos) \
49 ((int)((unsigned int)(pos) / VCHIQ_SLOT_SIZE))
50
51 #define BULK_INDEX(x) (x & (VCHIQ_NUM_SERVICE_BULKS - 1))
52
53 #define SRVTRACE_LEVEL(srv) \
54 (((srv) && (srv)->trace) ? VCHIQ_LOG_TRACE : vchiq_core_msg_log_level)
55 #define SRVTRACE_ENABLED(srv, lev) \
56 (((srv) && (srv)->trace) || (vchiq_core_msg_log_level >= (lev)))
57
58 struct vchiq_open_payload {
59 int fourcc;
60 int client_id;
61 short version;
62 short version_min;
63 };
64
65 struct vchiq_openack_payload {
66 short version;
67 };
68
69 enum
70 {
71 QMFLAGS_IS_BLOCKING = (1 << 0),
72 QMFLAGS_NO_MUTEX_LOCK = (1 << 1),
73 QMFLAGS_NO_MUTEX_UNLOCK = (1 << 2)
74 };
75
76 /* we require this for consistency between endpoints */
77 vchiq_static_assert(sizeof(VCHIQ_HEADER_T) == 8);
78 vchiq_static_assert(IS_POW2(sizeof(VCHIQ_HEADER_T)));
79 vchiq_static_assert(IS_POW2(VCHIQ_NUM_CURRENT_BULKS));
80 vchiq_static_assert(IS_POW2(VCHIQ_NUM_SERVICE_BULKS));
81 vchiq_static_assert(IS_POW2(VCHIQ_MAX_SERVICES));
82 vchiq_static_assert(VCHIQ_VERSION >= VCHIQ_VERSION_MIN);
83
84 /* Run time control of log level, based on KERN_XXX level. */
85 int vchiq_core_log_level = VCHIQ_LOG_DEFAULT;
86 int vchiq_core_msg_log_level = VCHIQ_LOG_DEFAULT;
87 int vchiq_sync_log_level = VCHIQ_LOG_DEFAULT;
88
89 static atomic_t pause_bulks_count = ATOMIC_INIT(0);
90
91 static DEFINE_SPINLOCK(service_spinlock);
92 DEFINE_SPINLOCK(bulk_waiter_spinlock);
93 DEFINE_SPINLOCK(quota_spinlock);
94
95 void
vchiq_core_initialize(void)96 vchiq_core_initialize(void)
97 {
98 spin_lock_init(&service_spinlock);
99 spin_lock_init(&bulk_waiter_spinlock);
100 spin_lock_init("a_spinlock);
101 }
102
103 VCHIQ_STATE_T *vchiq_states[VCHIQ_MAX_STATES];
104 static unsigned int handle_seq;
105
106 static const char *const srvstate_names[] = {
107 "FREE",
108 "HIDDEN",
109 "LISTENING",
110 "OPENING",
111 "OPEN",
112 "OPENSYNC",
113 "CLOSESENT",
114 "CLOSERECVD",
115 "CLOSEWAIT",
116 "CLOSED"
117 };
118
119 static const char *const reason_names[] = {
120 "SERVICE_OPENED",
121 "SERVICE_CLOSED",
122 "MESSAGE_AVAILABLE",
123 "BULK_TRANSMIT_DONE",
124 "BULK_RECEIVE_DONE",
125 "BULK_TRANSMIT_ABORTED",
126 "BULK_RECEIVE_ABORTED"
127 };
128
129 static const char *const conn_state_names[] = {
130 "DISCONNECTED",
131 "CONNECTING",
132 "CONNECTED",
133 "PAUSING",
134 "PAUSE_SENT",
135 "PAUSED",
136 "RESUMING",
137 "PAUSE_TIMEOUT",
138 "RESUME_TIMEOUT"
139 };
140
141
142 static void
143 release_message_sync(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header);
144
msg_type_str(unsigned int msg_type)145 static const char *msg_type_str(unsigned int msg_type)
146 {
147 switch (msg_type) {
148 case VCHIQ_MSG_PADDING: return "PADDING";
149 case VCHIQ_MSG_CONNECT: return "CONNECT";
150 case VCHIQ_MSG_OPEN: return "OPEN";
151 case VCHIQ_MSG_OPENACK: return "OPENACK";
152 case VCHIQ_MSG_CLOSE: return "CLOSE";
153 case VCHIQ_MSG_DATA: return "DATA";
154 case VCHIQ_MSG_BULK_RX: return "BULK_RX";
155 case VCHIQ_MSG_BULK_TX: return "BULK_TX";
156 case VCHIQ_MSG_BULK_RX_DONE: return "BULK_RX_DONE";
157 case VCHIQ_MSG_BULK_TX_DONE: return "BULK_TX_DONE";
158 case VCHIQ_MSG_PAUSE: return "PAUSE";
159 case VCHIQ_MSG_RESUME: return "RESUME";
160 case VCHIQ_MSG_REMOTE_USE: return "REMOTE_USE";
161 case VCHIQ_MSG_REMOTE_RELEASE: return "REMOTE_RELEASE";
162 case VCHIQ_MSG_REMOTE_USE_ACTIVE: return "REMOTE_USE_ACTIVE";
163 }
164 return "???";
165 }
166
167 static inline void
vchiq_set_service_state(VCHIQ_SERVICE_T * service,int newstate)168 vchiq_set_service_state(VCHIQ_SERVICE_T *service, int newstate)
169 {
170 vchiq_log_info(vchiq_core_log_level, "%d: srv:%d %s->%s",
171 service->state->id, service->localport,
172 srvstate_names[service->srvstate],
173 srvstate_names[newstate]);
174 service->srvstate = newstate;
175 }
176
177 VCHIQ_SERVICE_T *
find_service_by_handle(VCHIQ_SERVICE_HANDLE_T handle)178 find_service_by_handle(VCHIQ_SERVICE_HANDLE_T handle)
179 {
180 VCHIQ_SERVICE_T *service;
181
182 spin_lock(&service_spinlock);
183 service = handle_to_service(handle);
184 if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE) &&
185 (service->handle == handle)) {
186 BUG_ON(service->ref_count == 0);
187 service->ref_count++;
188 } else
189 service = NULL;
190 spin_unlock(&service_spinlock);
191
192 if (!service)
193 vchiq_log_info(vchiq_core_log_level,
194 "Invalid service handle 0x%x", handle);
195
196 return service;
197 }
198
199 VCHIQ_SERVICE_T *
find_service_by_port(VCHIQ_STATE_T * state,int localport)200 find_service_by_port(VCHIQ_STATE_T *state, int localport)
201 {
202 VCHIQ_SERVICE_T *service = NULL;
203 if ((unsigned int)localport <= VCHIQ_PORT_MAX) {
204 spin_lock(&service_spinlock);
205 service = state->services[localport];
206 if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE)) {
207 BUG_ON(service->ref_count == 0);
208 service->ref_count++;
209 } else
210 service = NULL;
211 spin_unlock(&service_spinlock);
212 }
213
214 if (!service)
215 vchiq_log_info(vchiq_core_log_level,
216 "Invalid port %d", localport);
217
218 return service;
219 }
220
221 VCHIQ_SERVICE_T *
find_service_for_instance(VCHIQ_INSTANCE_T instance,VCHIQ_SERVICE_HANDLE_T handle)222 find_service_for_instance(VCHIQ_INSTANCE_T instance,
223 VCHIQ_SERVICE_HANDLE_T handle) {
224 VCHIQ_SERVICE_T *service;
225
226 spin_lock(&service_spinlock);
227 service = handle_to_service(handle);
228 if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE) &&
229 (service->handle == handle) &&
230 (service->instance == instance)) {
231 BUG_ON(service->ref_count == 0);
232 service->ref_count++;
233 } else
234 service = NULL;
235 spin_unlock(&service_spinlock);
236
237 if (!service)
238 vchiq_log_info(vchiq_core_log_level,
239 "Invalid service handle 0x%x", handle);
240
241 return service;
242 }
243
244 VCHIQ_SERVICE_T *
find_closed_service_for_instance(VCHIQ_INSTANCE_T instance,VCHIQ_SERVICE_HANDLE_T handle)245 find_closed_service_for_instance(VCHIQ_INSTANCE_T instance,
246 VCHIQ_SERVICE_HANDLE_T handle) {
247 VCHIQ_SERVICE_T *service;
248
249 spin_lock(&service_spinlock);
250 service = handle_to_service(handle);
251 if (service &&
252 ((service->srvstate == VCHIQ_SRVSTATE_FREE) ||
253 (service->srvstate == VCHIQ_SRVSTATE_CLOSED)) &&
254 (service->handle == handle) &&
255 (service->instance == instance)) {
256 BUG_ON(service->ref_count == 0);
257 service->ref_count++;
258 } else
259 service = NULL;
260 spin_unlock(&service_spinlock);
261
262 if (!service)
263 vchiq_log_info(vchiq_core_log_level,
264 "Invalid service handle 0x%x", handle);
265
266 return service;
267 }
268
269 VCHIQ_SERVICE_T *
next_service_by_instance(VCHIQ_STATE_T * state,VCHIQ_INSTANCE_T instance,int * pidx)270 next_service_by_instance(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance,
271 int *pidx)
272 {
273 VCHIQ_SERVICE_T *service = NULL;
274 int idx = *pidx;
275
276 spin_lock(&service_spinlock);
277 while (idx < state->unused_service) {
278 VCHIQ_SERVICE_T *srv = state->services[idx++];
279 if (srv && (srv->srvstate != VCHIQ_SRVSTATE_FREE) &&
280 (srv->instance == instance)) {
281 service = srv;
282 BUG_ON(service->ref_count == 0);
283 service->ref_count++;
284 break;
285 }
286 }
287 spin_unlock(&service_spinlock);
288
289 *pidx = idx;
290
291 return service;
292 }
293
294 void
lock_service(VCHIQ_SERVICE_T * service)295 lock_service(VCHIQ_SERVICE_T *service)
296 {
297 spin_lock(&service_spinlock);
298 BUG_ON(!service || (service->ref_count == 0));
299 if (service)
300 service->ref_count++;
301 spin_unlock(&service_spinlock);
302 }
303
304 void
unlock_service(VCHIQ_SERVICE_T * service)305 unlock_service(VCHIQ_SERVICE_T *service)
306 {
307 VCHIQ_STATE_T *state = service->state;
308 spin_lock(&service_spinlock);
309 BUG_ON(!service || (service->ref_count == 0));
310 if (service && service->ref_count) {
311 service->ref_count--;
312 if (!service->ref_count) {
313 BUG_ON(service->srvstate != VCHIQ_SRVSTATE_FREE);
314 state->services[service->localport] = NULL;
315
316 _sema_destroy(&service->remove_event);
317 _sema_destroy(&service->bulk_remove_event);
318 lmutex_destroy(&service->bulk_mutex);
319 } else
320 service = NULL;
321 }
322 spin_unlock(&service_spinlock);
323
324 if (service && service->userdata_term)
325 service->userdata_term(service->base.userdata);
326
327 kfree(service);
328 }
329
330 int
vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T handle)331 vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T handle)
332 {
333 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
334 int id;
335
336 id = service ? service->client_id : 0;
337 if (service)
338 unlock_service(service);
339
340 return id;
341 }
342
343 void *
vchiq_get_service_userdata(VCHIQ_SERVICE_HANDLE_T handle)344 vchiq_get_service_userdata(VCHIQ_SERVICE_HANDLE_T handle)
345 {
346 VCHIQ_SERVICE_T *service = handle_to_service(handle);
347
348 return service ? service->base.userdata : NULL;
349 }
350
351 int
vchiq_get_service_fourcc(VCHIQ_SERVICE_HANDLE_T handle)352 vchiq_get_service_fourcc(VCHIQ_SERVICE_HANDLE_T handle)
353 {
354 VCHIQ_SERVICE_T *service = handle_to_service(handle);
355
356 return service ? service->base.fourcc : 0;
357 }
358
359 static void
mark_service_closing_internal(VCHIQ_SERVICE_T * service,int sh_thread)360 mark_service_closing_internal(VCHIQ_SERVICE_T *service, int sh_thread)
361 {
362 VCHIQ_STATE_T *state = service->state;
363 VCHIQ_SERVICE_QUOTA_T *service_quota;
364
365 service->closing = 1;
366
367 /* Synchronise with other threads. */
368 lmutex_lock(&state->recycle_mutex);
369 lmutex_unlock(&state->recycle_mutex);
370 if (!sh_thread || (state->conn_state != VCHIQ_CONNSTATE_PAUSE_SENT)) {
371 /* If we're pausing then the slot_mutex is held until resume
372 * by the slot handler. Therefore don't try to acquire this
373 * mutex if we're the slot handler and in the pause sent state.
374 * We don't need to in this case anyway. */
375 lmutex_lock(&state->slot_mutex);
376 lmutex_unlock(&state->slot_mutex);
377 }
378
379 /* Unblock any sending thread. */
380 service_quota = &state->service_quotas[service->localport];
381 up(&service_quota->quota_event);
382 }
383
384 static void
mark_service_closing(VCHIQ_SERVICE_T * service)385 mark_service_closing(VCHIQ_SERVICE_T *service)
386 {
387 mark_service_closing_internal(service, 0);
388 }
389
390 static inline VCHIQ_STATUS_T
make_service_callback(VCHIQ_SERVICE_T * service,VCHIQ_REASON_T reason,VCHIQ_HEADER_T * header,void * bulk_userdata)391 make_service_callback(VCHIQ_SERVICE_T *service, VCHIQ_REASON_T reason,
392 VCHIQ_HEADER_T *header, void *bulk_userdata)
393 {
394 VCHIQ_STATUS_T status;
395 vchiq_log_trace(vchiq_core_log_level, "%d: callback:%d (%s, %p, %p)",
396 service->state->id, service->localport, reason_names[reason],
397 header, bulk_userdata);
398 status = service->base.callback(reason, header, service->handle,
399 bulk_userdata);
400 if (status == VCHIQ_ERROR) {
401 vchiq_log_warning(vchiq_core_log_level,
402 "%d: ignoring ERROR from callback to service %x",
403 service->state->id, service->handle);
404 status = VCHIQ_SUCCESS;
405 }
406 return status;
407 }
408
409 inline void
vchiq_set_conn_state(VCHIQ_STATE_T * state,VCHIQ_CONNSTATE_T newstate)410 vchiq_set_conn_state(VCHIQ_STATE_T *state, VCHIQ_CONNSTATE_T newstate)
411 {
412 VCHIQ_CONNSTATE_T oldstate = state->conn_state;
413 vchiq_log_info(vchiq_core_log_level, "%d: %s->%s", state->id,
414 conn_state_names[oldstate],
415 conn_state_names[newstate]);
416 state->conn_state = newstate;
417 vchiq_platform_conn_state_changed(state, oldstate, newstate);
418 }
419
420 #define ACTUAL_EVENT_SEM_ADDR(ref,offset)\
421 ((struct semaphore *)(((size_t) ref) + ((size_t) offset)))
422 static inline void
remote_event_create(VCHIQ_STATE_T * ref,REMOTE_EVENT_T * event)423 remote_event_create(VCHIQ_STATE_T *ref, REMOTE_EVENT_T *event)
424 {
425 event->armed = 0;
426 /* Don't clear the 'fired' flag because it may already have been set
427 ** by the other side. */
428 _sema_init(ACTUAL_EVENT_SEM_ADDR(ref,event->event), 0);
429 }
430
431 __unused static inline void
remote_event_destroy(REMOTE_EVENT_T * event)432 remote_event_destroy(REMOTE_EVENT_T *event)
433 {
434 (void)event;
435 }
436
437 static inline int
remote_event_wait(VCHIQ_STATE_T * ref,REMOTE_EVENT_T * event)438 remote_event_wait(VCHIQ_STATE_T *ref, REMOTE_EVENT_T *event)
439 {
440 if (!event->fired) {
441 event->armed = 1;
442 #if defined(__aarch64__)
443 dsb(sy);
444 #else
445 dsb();
446 #endif
447
448 if (!event->fired) {
449 if (down_interruptible(ACTUAL_EVENT_SEM_ADDR(ref,event->event)) != 0) {
450 event->armed = 0;
451 return 0;
452 }
453 }
454 event->armed = 0;
455 wmb();
456 }
457
458 event->fired = 0;
459 return 1;
460 }
461
462 static inline void
remote_event_signal_local(VCHIQ_STATE_T * ref,REMOTE_EVENT_T * event)463 remote_event_signal_local(VCHIQ_STATE_T *ref, REMOTE_EVENT_T *event)
464 {
465 /*
466 * Mirror
467 * https://github.com/raspberrypi/linux/commit/a50c4c9a65779ca835746b5fd79d3d5278afbdbe
468 * for extra safety
469 */
470 event->fired = 1;
471 event->armed = 0;
472 up(ACTUAL_EVENT_SEM_ADDR(ref,event->event));
473 }
474
475 static inline void
remote_event_poll(VCHIQ_STATE_T * ref,REMOTE_EVENT_T * event)476 remote_event_poll(VCHIQ_STATE_T *ref, REMOTE_EVENT_T *event)
477 {
478 if (event->fired && event->armed)
479 remote_event_signal_local(ref,event);
480 }
481
482 void
remote_event_pollall(VCHIQ_STATE_T * state)483 remote_event_pollall(VCHIQ_STATE_T *state)
484 {
485 remote_event_poll(state , &state->local->sync_trigger);
486 remote_event_poll(state , &state->local->sync_release);
487 remote_event_poll(state , &state->local->trigger);
488 remote_event_poll(state , &state->local->recycle);
489 }
490
491 /* Round up message sizes so that any space at the end of a slot is always big
492 ** enough for a header. This relies on header size being a power of two, which
493 ** has been verified earlier by a static assertion. */
494
495 static inline size_t
calc_stride(size_t size)496 calc_stride(size_t size)
497 {
498 /* Allow room for the header */
499 size += sizeof(VCHIQ_HEADER_T);
500
501 /* Round up */
502 return (size + sizeof(VCHIQ_HEADER_T) - 1) & ~(sizeof(VCHIQ_HEADER_T)
503 - 1);
504 }
505
506 /* Called by the slot handler thread */
507 static VCHIQ_SERVICE_T *
get_listening_service(VCHIQ_STATE_T * state,int fourcc)508 get_listening_service(VCHIQ_STATE_T *state, int fourcc)
509 {
510 int i;
511
512 WARN_ON(fourcc == VCHIQ_FOURCC_INVALID);
513
514 for (i = 0; i < state->unused_service; i++) {
515 VCHIQ_SERVICE_T *service = state->services[i];
516 if (service &&
517 (service->public_fourcc == fourcc) &&
518 ((service->srvstate == VCHIQ_SRVSTATE_LISTENING) ||
519 ((service->srvstate == VCHIQ_SRVSTATE_OPEN) &&
520 (service->remoteport == VCHIQ_PORT_FREE)))) {
521 lock_service(service);
522 return service;
523 }
524 }
525
526 return NULL;
527 }
528
529 /* Called by the slot handler thread */
530 static VCHIQ_SERVICE_T *
get_connected_service(VCHIQ_STATE_T * state,unsigned int port)531 get_connected_service(VCHIQ_STATE_T *state, unsigned int port)
532 {
533 int i;
534 for (i = 0; i < state->unused_service; i++) {
535 VCHIQ_SERVICE_T *service = state->services[i];
536 if (service && (service->srvstate == VCHIQ_SRVSTATE_OPEN)
537 && (service->remoteport == port)) {
538 lock_service(service);
539 return service;
540 }
541 }
542 return NULL;
543 }
544
545 inline void
request_poll(VCHIQ_STATE_T * state,VCHIQ_SERVICE_T * service,int poll_type)546 request_poll(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, int poll_type)
547 {
548 uint32_t value;
549
550 if (service) {
551 do {
552 value = atomic_read(&service->poll_flags);
553 } while (atomic_cmpxchg(&service->poll_flags, value,
554 value | (1 << poll_type)) != value);
555
556 do {
557 value = atomic_read(&state->poll_services[
558 service->localport>>5]);
559 } while (atomic_cmpxchg(
560 &state->poll_services[service->localport>>5],
561 value, value | (1 << (service->localport & 0x1f)))
562 != value);
563 }
564
565 state->poll_needed = 1;
566 wmb();
567
568 /* ... and ensure the slot handler runs. */
569 remote_event_signal_local(state, &state->local->trigger);
570 }
571
572 /* Called from queue_message, by the slot handler and application threads,
573 ** with slot_mutex held */
574 static VCHIQ_HEADER_T *
reserve_space(VCHIQ_STATE_T * state,size_t space,int is_blocking)575 reserve_space(VCHIQ_STATE_T *state, size_t space, int is_blocking)
576 {
577 VCHIQ_SHARED_STATE_T *local = state->local;
578 int tx_pos = state->local_tx_pos;
579 int slot_space = VCHIQ_SLOT_SIZE - (tx_pos & VCHIQ_SLOT_MASK);
580
581 if (space > slot_space) {
582 VCHIQ_HEADER_T *header;
583 /* Fill the remaining space with padding */
584 WARN_ON(state->tx_data == NULL);
585 header = (VCHIQ_HEADER_T *)
586 (state->tx_data + (tx_pos & VCHIQ_SLOT_MASK));
587 header->msgid = VCHIQ_MSGID_PADDING;
588 header->size = slot_space - sizeof(VCHIQ_HEADER_T);
589
590 tx_pos += slot_space;
591 }
592
593 /* If necessary, get the next slot. */
594 if ((tx_pos & VCHIQ_SLOT_MASK) == 0) {
595 int slot_index;
596
597 /* If there is no free slot... */
598
599 if (down_trylock(&state->slot_available_event) != 0) {
600 /* ...wait for one. */
601
602 VCHIQ_STATS_INC(state, slot_stalls);
603
604 /* But first, flush through the last slot. */
605 state->local_tx_pos = tx_pos;
606 local->tx_pos = tx_pos;
607 remote_event_signal(&state->remote->trigger);
608
609 if (!is_blocking ||
610 (down_interruptible(
611 &state->slot_available_event) != 0))
612 return NULL; /* No space available */
613 }
614
615 BUG_ON(tx_pos ==
616 (state->slot_queue_available * VCHIQ_SLOT_SIZE));
617
618 slot_index = local->slot_queue[
619 SLOT_QUEUE_INDEX_FROM_POS(tx_pos) &
620 VCHIQ_SLOT_QUEUE_MASK];
621 state->tx_data =
622 (char *)SLOT_DATA_FROM_INDEX(state, slot_index);
623 }
624
625 state->local_tx_pos = tx_pos + space;
626
627 return (VCHIQ_HEADER_T *)(state->tx_data + (tx_pos & VCHIQ_SLOT_MASK));
628 }
629
630 /* Called by the recycle thread. */
631 static void
process_free_queue(VCHIQ_STATE_T * state)632 process_free_queue(VCHIQ_STATE_T *state)
633 {
634 VCHIQ_SHARED_STATE_T *local = state->local;
635 VCHI_BITSET_T service_found[VCHI_BITSET_SIZE(VCHIQ_MAX_SERVICES)];
636 int slot_queue_available;
637
638 /* Find slots which have been freed by the other side, and return them
639 ** to the available queue. */
640 slot_queue_available = state->slot_queue_available;
641
642 /* Use a memory barrier to ensure that any state that may have been
643 ** modified by another thread is not masked by stale prefetched
644 ** values. */
645 mb();
646
647 while (slot_queue_available != local->slot_queue_recycle) {
648 unsigned int pos;
649 int slot_index = local->slot_queue[slot_queue_available++ &
650 VCHIQ_SLOT_QUEUE_MASK];
651 char *data = (char *)SLOT_DATA_FROM_INDEX(state, slot_index);
652 int data_found = 0;
653
654 rmb();
655
656 vchiq_log_trace(vchiq_core_log_level, "%d: pfq %d=%p %x %x",
657 state->id, slot_index, data,
658 local->slot_queue_recycle, slot_queue_available);
659
660 /* Initialise the bitmask for services which have used this
661 ** slot */
662 VCHI_BITSET_ZERO(service_found);
663
664 pos = 0;
665
666 while (pos < VCHIQ_SLOT_SIZE) {
667 VCHIQ_HEADER_T *header =
668 (VCHIQ_HEADER_T *)(data + pos);
669 int msgid = header->msgid;
670 if (VCHIQ_MSG_TYPE(msgid) == VCHIQ_MSG_DATA) {
671 int port = VCHIQ_MSG_SRCPORT(msgid);
672 VCHIQ_SERVICE_QUOTA_T *service_quota =
673 &state->service_quotas[port];
674 int count;
675 spin_lock("a_spinlock);
676 count = service_quota->message_use_count;
677 if (count > 0)
678 service_quota->message_use_count =
679 count - 1;
680 spin_unlock("a_spinlock);
681
682 if (count == service_quota->message_quota)
683 /* Signal the service that it
684 ** has dropped below its quota
685 */
686 up(&service_quota->quota_event);
687 else if (count == 0) {
688 vchiq_log_error(vchiq_core_log_level,
689 "service %d "
690 "message_use_count=%d "
691 "(header %p, msgid %x, "
692 "header->msgid %x, "
693 "header->size %x)",
694 port,
695 service_quota->
696 message_use_count,
697 header, msgid,
698 header->msgid,
699 header->size);
700 WARN(1, "invalid message use count\n");
701 }
702 if (!VCHI_BITSET_IS_SET(service_found, port)) {
703 /* Set the found bit for this service */
704 VCHI_BITSET_SET(service_found, port);
705
706 spin_lock("a_spinlock);
707 count = service_quota->slot_use_count;
708 if (count > 0)
709 service_quota->slot_use_count =
710 count - 1;
711 spin_unlock("a_spinlock);
712
713 if (count > 0) {
714 /* Signal the service in case
715 ** it has dropped below its
716 ** quota */
717 up(&service_quota->quota_event);
718 vchiq_log_trace(
719 vchiq_core_log_level,
720 "%d: pfq:%d %x@%p - "
721 "slot_use->%d",
722 state->id, port,
723 header->size,
724 header,
725 count - 1);
726 } else {
727 vchiq_log_error(
728 vchiq_core_log_level,
729 "service %d "
730 "slot_use_count"
731 "=%d (header %p"
732 ", msgid %x, "
733 "header->msgid"
734 " %x, header->"
735 "size %x)",
736 port, count,
737 header,
738 msgid,
739 header->msgid,
740 header->size);
741 WARN(1, "bad slot use count\n");
742 }
743 }
744
745 data_found = 1;
746 }
747
748 pos += calc_stride(header->size);
749 if (pos > VCHIQ_SLOT_SIZE) {
750 vchiq_log_error(vchiq_core_log_level,
751 "pfq - pos %x: header %p, msgid %x, "
752 "header->msgid %x, header->size %x",
753 pos, header, msgid,
754 header->msgid, header->size);
755 WARN(1, "invalid slot position\n");
756 }
757 }
758
759 if (data_found) {
760 int count;
761 spin_lock("a_spinlock);
762 count = state->data_use_count;
763 if (count > 0)
764 state->data_use_count =
765 count - 1;
766 spin_unlock("a_spinlock);
767 if (count == state->data_quota)
768 up(&state->data_quota_event);
769 }
770
771 mb();
772
773 state->slot_queue_available = slot_queue_available;
774 up(&state->slot_available_event);
775 }
776 }
777
778 static ssize_t
memcpy_copy_callback(void * context,void * dest,size_t offset,size_t maxsize)779 memcpy_copy_callback(
780 void *context, void *dest,
781 size_t offset, size_t maxsize)
782 {
783 void *src = context;
784
785 memcpy((uint8_t *)dest + offset, (uint8_t *)src + offset, maxsize);
786 return maxsize;
787 }
788
789 static ssize_t
copy_message_data(ssize_t (* copy_callback)(void * context,void * dest,size_t offset,size_t maxsize),void * context,void * dest,size_t size)790 copy_message_data(
791 ssize_t (*copy_callback)(void *context, void *dest,
792 size_t offset, size_t maxsize),
793 void *context,
794 void *dest,
795 size_t size)
796 {
797 size_t pos = 0;
798
799 while (pos < size) {
800 ssize_t callback_result;
801 size_t max_bytes = size - pos;
802
803 callback_result =
804 copy_callback(context, (uint8_t *)dest + pos,
805 pos, max_bytes);
806
807 if (callback_result < 0)
808 return callback_result;
809
810 if (!callback_result)
811 return -EIO;
812
813 if (callback_result > max_bytes)
814 return -EIO;
815
816 pos += callback_result;
817 }
818
819 return size;
820 }
821
822 /* Called by the slot handler and application threads */
823 static VCHIQ_STATUS_T
queue_message(VCHIQ_STATE_T * state,VCHIQ_SERVICE_T * service,int msgid,ssize_t (* copy_callback)(void * context,void * dest,size_t offset,size_t maxsize),void * context,size_t size,int flags)824 queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service,
825 int msgid,
826 ssize_t (*copy_callback)(void *context, void *dest,
827 size_t offset, size_t maxsize),
828 void *context,
829 size_t size,
830 int flags)
831 {
832 VCHIQ_SHARED_STATE_T *local;
833 VCHIQ_SERVICE_QUOTA_T *service_quota = NULL;
834 VCHIQ_HEADER_T *header;
835 int type = VCHIQ_MSG_TYPE(msgid);
836
837 size_t stride;
838
839 local = state->local;
840
841 stride = calc_stride(size);
842
843 WARN_ON(!(stride <= VCHIQ_SLOT_SIZE));
844
845 if (!(flags & QMFLAGS_NO_MUTEX_LOCK) &&
846 (lmutex_lock_interruptible(&state->slot_mutex) != 0))
847 return VCHIQ_RETRY;
848
849 if (type == VCHIQ_MSG_DATA) {
850 int tx_end_index;
851
852 BUG_ON(!service);
853 BUG_ON((flags & (QMFLAGS_NO_MUTEX_LOCK |
854 QMFLAGS_NO_MUTEX_UNLOCK)) != 0);
855
856 if (service->closing) {
857 /* The service has been closed */
858 lmutex_unlock(&state->slot_mutex);
859 return VCHIQ_ERROR;
860 }
861
862 service_quota = &state->service_quotas[service->localport];
863
864 spin_lock("a_spinlock);
865
866 /* Ensure this service doesn't use more than its quota of
867 ** messages or slots */
868 tx_end_index = SLOT_QUEUE_INDEX_FROM_POS(
869 state->local_tx_pos + stride - 1);
870
871 /* Ensure data messages don't use more than their quota of
872 ** slots */
873 while ((tx_end_index != state->previous_data_index) &&
874 (state->data_use_count == state->data_quota)) {
875 VCHIQ_STATS_INC(state, data_stalls);
876 spin_unlock("a_spinlock);
877 lmutex_unlock(&state->slot_mutex);
878
879 if (down_interruptible(&state->data_quota_event)
880 != 0)
881 return VCHIQ_RETRY;
882
883 lmutex_lock(&state->slot_mutex);
884 spin_lock("a_spinlock);
885 tx_end_index = SLOT_QUEUE_INDEX_FROM_POS(
886 state->local_tx_pos + stride - 1);
887 if ((tx_end_index == state->previous_data_index) ||
888 (state->data_use_count < state->data_quota)) {
889 /* Pass the signal on to other waiters */
890 up(&state->data_quota_event);
891 break;
892 }
893 }
894
895 while ((service_quota->message_use_count ==
896 service_quota->message_quota) ||
897 ((tx_end_index != service_quota->previous_tx_index) &&
898 (service_quota->slot_use_count ==
899 service_quota->slot_quota))) {
900 spin_unlock("a_spinlock);
901 vchiq_log_trace(vchiq_core_log_level,
902 "%d: qm:%d %s,%zx - quota stall "
903 "(msg %d, slot %d)",
904 state->id, service->localport,
905 msg_type_str(type), size,
906 service_quota->message_use_count,
907 service_quota->slot_use_count);
908 VCHIQ_SERVICE_STATS_INC(service, quota_stalls);
909 lmutex_unlock(&state->slot_mutex);
910 if (down_interruptible(&service_quota->quota_event)
911 != 0)
912 return VCHIQ_RETRY;
913 if (service->closing)
914 return VCHIQ_ERROR;
915 if (lmutex_lock_interruptible(&state->slot_mutex) != 0)
916 return VCHIQ_RETRY;
917 if (service->srvstate != VCHIQ_SRVSTATE_OPEN) {
918 /* The service has been closed */
919 lmutex_unlock(&state->slot_mutex);
920 return VCHIQ_ERROR;
921 }
922 spin_lock("a_spinlock);
923 tx_end_index = SLOT_QUEUE_INDEX_FROM_POS(
924 state->local_tx_pos + stride - 1);
925 }
926
927 spin_unlock("a_spinlock);
928 }
929
930 header = reserve_space(state, stride, flags & QMFLAGS_IS_BLOCKING);
931
932 if (!header) {
933 if (service)
934 VCHIQ_SERVICE_STATS_INC(service, slot_stalls);
935 /* In the event of a failure, return the mutex to the
936 state it was in */
937 if (!(flags & QMFLAGS_NO_MUTEX_LOCK))
938 lmutex_unlock(&state->slot_mutex);
939
940 return VCHIQ_RETRY;
941 }
942
943 if (type == VCHIQ_MSG_DATA) {
944 ssize_t callback_result;
945 int tx_end_index;
946 int slot_use_count;
947
948 vchiq_log_info(vchiq_core_log_level,
949 "%d: qm %s@%p,%zx (%d->%d)",
950 state->id,
951 msg_type_str(VCHIQ_MSG_TYPE(msgid)),
952 header, size,
953 VCHIQ_MSG_SRCPORT(msgid),
954 VCHIQ_MSG_DSTPORT(msgid));
955
956 BUG_ON(!service);
957 BUG_ON((flags & (QMFLAGS_NO_MUTEX_LOCK |
958 QMFLAGS_NO_MUTEX_UNLOCK)) != 0);
959
960 callback_result =
961 copy_message_data(copy_callback, context,
962 header->data, size);
963 if (callback_result < 0) {
964 lmutex_unlock(&state->slot_mutex);
965 VCHIQ_SERVICE_STATS_INC(service,
966 error_count);
967 return VCHIQ_ERROR;
968 }
969
970 if (SRVTRACE_ENABLED(service,
971 VCHIQ_LOG_INFO))
972 vchiq_log_dump_mem("Sent", 0,
973 header->data,
974 min((size_t)64,
975 (size_t)callback_result));
976
977 spin_lock("a_spinlock);
978 service_quota->message_use_count++;
979
980 tx_end_index =
981 SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos - 1);
982
983 /* If this transmission can't fit in the last slot used by any
984 ** service, the data_use_count must be increased. */
985 if (tx_end_index != state->previous_data_index) {
986 state->previous_data_index = tx_end_index;
987 state->data_use_count++;
988 }
989
990 /* If this isn't the same slot last used by this service,
991 ** the service's slot_use_count must be increased. */
992 if (tx_end_index != service_quota->previous_tx_index) {
993 service_quota->previous_tx_index = tx_end_index;
994 slot_use_count = ++service_quota->slot_use_count;
995 } else {
996 slot_use_count = 0;
997 }
998
999 spin_unlock("a_spinlock);
1000
1001 if (slot_use_count)
1002 vchiq_log_trace(vchiq_core_log_level,
1003 "%d: qm:%d %s,%zx - slot_use->%d (hdr %p)",
1004 state->id, service->localport,
1005 msg_type_str(VCHIQ_MSG_TYPE(msgid)), size,
1006 slot_use_count, header);
1007
1008 VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count);
1009 VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size);
1010 } else {
1011 vchiq_log_info(vchiq_core_log_level,
1012 "%d: qm %s@%p,%zx (%d->%d)", state->id,
1013 msg_type_str(VCHIQ_MSG_TYPE(msgid)),
1014 header, size,
1015 VCHIQ_MSG_SRCPORT(msgid),
1016 VCHIQ_MSG_DSTPORT(msgid));
1017 if (size != 0) {
1018 /* It is assumed for now that this code path
1019 * only happens from calls inside this file.
1020 *
1021 * External callers are through the vchiq_queue_message
1022 * path which always sets the type to be VCHIQ_MSG_DATA
1023 *
1024 * At first glance this appears to be correct but
1025 * more review is needed.
1026 */
1027 copy_message_data(copy_callback, context,
1028 header->data, size);
1029 }
1030 VCHIQ_STATS_INC(state, ctrl_tx_count);
1031 }
1032
1033 header->msgid = msgid;
1034 header->size = size;
1035
1036 {
1037 int svc_fourcc;
1038
1039 svc_fourcc = service
1040 ? service->base.fourcc
1041 : VCHIQ_MAKE_FOURCC('?', '?', '?', '?');
1042
1043 vchiq_log_info(SRVTRACE_LEVEL(service),
1044 "Sent Msg %s(%u) to %c%c%c%c s:%u d:%d len:%zu",
1045 msg_type_str(VCHIQ_MSG_TYPE(msgid)),
1046 VCHIQ_MSG_TYPE(msgid),
1047 VCHIQ_FOURCC_AS_4CHARS(svc_fourcc),
1048 VCHIQ_MSG_SRCPORT(msgid),
1049 VCHIQ_MSG_DSTPORT(msgid),
1050 size);
1051 }
1052
1053 /* Make sure the new header is visible to the peer. */
1054 wmb();
1055
1056 /* Make the new tx_pos visible to the peer. */
1057 local->tx_pos = state->local_tx_pos;
1058 wmb();
1059
1060 if (service && (type == VCHIQ_MSG_CLOSE))
1061 vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSESENT);
1062
1063 if (!(flags & QMFLAGS_NO_MUTEX_UNLOCK))
1064 lmutex_unlock(&state->slot_mutex);
1065
1066 remote_event_signal(&state->remote->trigger);
1067
1068 return VCHIQ_SUCCESS;
1069 }
1070
1071 /* Called by the slot handler and application threads */
1072 static VCHIQ_STATUS_T
queue_message_sync(VCHIQ_STATE_T * state,VCHIQ_SERVICE_T * service,int msgid,ssize_t (* copy_callback)(void * context,void * dest,size_t offset,size_t maxsize),void * context,int size,int is_blocking)1073 queue_message_sync(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service,
1074 int msgid,
1075 ssize_t (*copy_callback)(void *context, void *dest,
1076 size_t offset, size_t maxsize),
1077 void *context,
1078 int size,
1079 int is_blocking)
1080 {
1081 VCHIQ_SHARED_STATE_T *local;
1082 VCHIQ_HEADER_T *header;
1083 ssize_t callback_result;
1084
1085 local = state->local;
1086
1087 if ((VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_RESUME) &&
1088 (lmutex_lock_interruptible(&state->sync_mutex) != 0))
1089 return VCHIQ_RETRY;
1090
1091 remote_event_wait(state, &local->sync_release);
1092
1093 rmb();
1094
1095 header = (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state,
1096 local->slot_sync);
1097
1098 {
1099 int oldmsgid = header->msgid;
1100 if (oldmsgid != VCHIQ_MSGID_PADDING)
1101 vchiq_log_error(vchiq_core_log_level,
1102 "%d: qms - msgid %x, not PADDING",
1103 state->id, oldmsgid);
1104 }
1105
1106
1107 vchiq_log_info(vchiq_sync_log_level,
1108 "%d: qms %s@%p,%x (%d->%d)", state->id,
1109 msg_type_str(VCHIQ_MSG_TYPE(msgid)),
1110 header, size, VCHIQ_MSG_SRCPORT(msgid),
1111 VCHIQ_MSG_DSTPORT(msgid));
1112
1113 callback_result =
1114 copy_message_data(copy_callback, context,
1115 header->data, size);
1116
1117 if (callback_result < 0) {
1118 lmutex_unlock(&state->sync_mutex);
1119 VCHIQ_SERVICE_STATS_INC(service,
1120 error_count);
1121 return VCHIQ_ERROR;
1122 }
1123
1124 if (service) {
1125 if (SRVTRACE_ENABLED(service,
1126 VCHIQ_LOG_INFO))
1127 vchiq_log_dump_mem("Sent Sync", 0,
1128 header->data,
1129 min((size_t)64,
1130 (size_t)callback_result));
1131
1132 VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count);
1133 VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size);
1134 } else {
1135 VCHIQ_STATS_INC(state, ctrl_tx_count);
1136 }
1137
1138 header->size = size;
1139 header->msgid = msgid;
1140
1141 if (vchiq_sync_log_level >= VCHIQ_LOG_TRACE) {
1142 int svc_fourcc;
1143
1144 svc_fourcc = service
1145 ? service->base.fourcc
1146 : VCHIQ_MAKE_FOURCC('?', '?', '?', '?');
1147
1148 vchiq_log_trace(vchiq_sync_log_level,
1149 "Sent Sync Msg %s(%u) to %c%c%c%c s:%u d:%d len:%d",
1150 msg_type_str(VCHIQ_MSG_TYPE(msgid)),
1151 VCHIQ_MSG_TYPE(msgid),
1152 VCHIQ_FOURCC_AS_4CHARS(svc_fourcc),
1153 VCHIQ_MSG_SRCPORT(msgid),
1154 VCHIQ_MSG_DSTPORT(msgid),
1155 size);
1156 }
1157
1158 remote_event_signal(&state->remote->sync_trigger);
1159
1160 if (VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_PAUSE)
1161 lmutex_unlock(&state->sync_mutex);
1162
1163 return VCHIQ_SUCCESS;
1164 }
1165
1166 static inline void
claim_slot(VCHIQ_SLOT_INFO_T * slot)1167 claim_slot(VCHIQ_SLOT_INFO_T *slot)
1168 {
1169 slot->use_count++;
1170 }
1171
1172 static void
release_slot(VCHIQ_STATE_T * state,VCHIQ_SLOT_INFO_T * slot_info,VCHIQ_HEADER_T * header,VCHIQ_SERVICE_T * service)1173 release_slot(VCHIQ_STATE_T *state, VCHIQ_SLOT_INFO_T *slot_info,
1174 VCHIQ_HEADER_T *header, VCHIQ_SERVICE_T *service)
1175 {
1176 int release_count;
1177
1178 lmutex_lock(&state->recycle_mutex);
1179
1180 if (header) {
1181 int msgid = header->msgid;
1182 if (((msgid & VCHIQ_MSGID_CLAIMED) == 0) ||
1183 (service && service->closing)) {
1184 lmutex_unlock(&state->recycle_mutex);
1185 return;
1186 }
1187
1188 /* Rewrite the message header to prevent a double
1189 ** release */
1190 header->msgid = msgid & ~VCHIQ_MSGID_CLAIMED;
1191 }
1192
1193 release_count = slot_info->release_count;
1194 slot_info->release_count = ++release_count;
1195
1196 if (release_count == slot_info->use_count) {
1197 int slot_queue_recycle;
1198 /* Add to the freed queue */
1199
1200 /* A read barrier is necessary here to prevent speculative
1201 ** fetches of remote->slot_queue_recycle from overtaking the
1202 ** mutex. */
1203 rmb();
1204
1205 slot_queue_recycle = state->remote->slot_queue_recycle;
1206 state->remote->slot_queue[slot_queue_recycle &
1207 VCHIQ_SLOT_QUEUE_MASK] =
1208 SLOT_INDEX_FROM_INFO(state, slot_info);
1209 state->remote->slot_queue_recycle = slot_queue_recycle + 1;
1210 vchiq_log_info(vchiq_core_log_level,
1211 "%d: release_slot %d - recycle->%x",
1212 state->id, SLOT_INDEX_FROM_INFO(state, slot_info),
1213 state->remote->slot_queue_recycle);
1214
1215 /* A write barrier is necessary, but remote_event_signal
1216 ** contains one. */
1217 remote_event_signal(&state->remote->recycle);
1218 }
1219
1220 lmutex_unlock(&state->recycle_mutex);
1221 }
1222
1223 /* Called by the slot handler - don't hold the bulk mutex */
1224 static VCHIQ_STATUS_T
notify_bulks(VCHIQ_SERVICE_T * service,VCHIQ_BULK_QUEUE_T * queue,int retry_poll)1225 notify_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue,
1226 int retry_poll)
1227 {
1228 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
1229
1230 vchiq_log_trace(vchiq_core_log_level,
1231 "%d: nb:%d %cx - p=%x rn=%x r=%x",
1232 service->state->id, service->localport,
1233 (queue == &service->bulk_tx) ? 't' : 'r',
1234 queue->process, queue->remote_notify, queue->remove);
1235
1236 if (service->state->is_master) {
1237 while (queue->remote_notify != queue->process) {
1238 VCHIQ_BULK_T *bulk =
1239 &queue->bulks[BULK_INDEX(queue->remote_notify)];
1240 int msgtype = (bulk->dir == VCHIQ_BULK_TRANSMIT) ?
1241 VCHIQ_MSG_BULK_RX_DONE : VCHIQ_MSG_BULK_TX_DONE;
1242 int msgid = VCHIQ_MAKE_MSG(msgtype, service->localport,
1243 service->remoteport);
1244 /* Only reply to non-dummy bulk requests */
1245 if (bulk->remote_data) {
1246 status = queue_message(
1247 service->state,
1248 NULL,
1249 msgid,
1250 memcpy_copy_callback,
1251 &bulk->actual,
1252 4,
1253 0);
1254 if (status != VCHIQ_SUCCESS)
1255 break;
1256 }
1257 queue->remote_notify++;
1258 }
1259 } else {
1260 queue->remote_notify = queue->process;
1261 }
1262
1263 if (status == VCHIQ_SUCCESS) {
1264 while (queue->remove != queue->remote_notify) {
1265 VCHIQ_BULK_T *bulk =
1266 &queue->bulks[BULK_INDEX(queue->remove)];
1267
1268 /* Only generate callbacks for non-dummy bulk
1269 ** requests, and non-terminated services */
1270 if (bulk->data && service->instance) {
1271 if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED) {
1272 if (bulk->dir == VCHIQ_BULK_TRANSMIT) {
1273 VCHIQ_SERVICE_STATS_INC(service,
1274 bulk_tx_count);
1275 VCHIQ_SERVICE_STATS_ADD(service,
1276 bulk_tx_bytes,
1277 bulk->actual);
1278 } else {
1279 VCHIQ_SERVICE_STATS_INC(service,
1280 bulk_rx_count);
1281 VCHIQ_SERVICE_STATS_ADD(service,
1282 bulk_rx_bytes,
1283 bulk->actual);
1284 }
1285 } else {
1286 VCHIQ_SERVICE_STATS_INC(service,
1287 bulk_aborted_count);
1288 }
1289 if (bulk->mode == VCHIQ_BULK_MODE_BLOCKING) {
1290 struct bulk_waiter *waiter;
1291 spin_lock(&bulk_waiter_spinlock);
1292 waiter = bulk->userdata;
1293 if (waiter) {
1294 waiter->actual = bulk->actual;
1295 up(&waiter->event);
1296 }
1297 spin_unlock(&bulk_waiter_spinlock);
1298 } else if (bulk->mode ==
1299 VCHIQ_BULK_MODE_CALLBACK) {
1300 VCHIQ_REASON_T reason = (bulk->dir ==
1301 VCHIQ_BULK_TRANSMIT) ?
1302 ((bulk->actual ==
1303 VCHIQ_BULK_ACTUAL_ABORTED) ?
1304 VCHIQ_BULK_TRANSMIT_ABORTED :
1305 VCHIQ_BULK_TRANSMIT_DONE) :
1306 ((bulk->actual ==
1307 VCHIQ_BULK_ACTUAL_ABORTED) ?
1308 VCHIQ_BULK_RECEIVE_ABORTED :
1309 VCHIQ_BULK_RECEIVE_DONE);
1310 status = make_service_callback(service,
1311 reason, NULL, bulk->userdata);
1312 if (status == VCHIQ_RETRY)
1313 break;
1314 }
1315 }
1316
1317 queue->remove++;
1318 up(&service->bulk_remove_event);
1319 }
1320 if (!retry_poll)
1321 status = VCHIQ_SUCCESS;
1322 }
1323
1324 if (status == VCHIQ_RETRY)
1325 request_poll(service->state, service,
1326 (queue == &service->bulk_tx) ?
1327 VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY);
1328
1329 return status;
1330 }
1331
1332 /* Called by the slot handler thread */
1333 static void
poll_services(VCHIQ_STATE_T * state)1334 poll_services(VCHIQ_STATE_T *state)
1335 {
1336 int group, i;
1337
1338 for (group = 0; group < VCHI_BITSET_SIZE(state->unused_service); group++) {
1339 uint32_t flags;
1340 flags = atomic_xchg(&state->poll_services[group], 0);
1341 for (i = 0; flags; i++) {
1342 if (flags & (1 << i)) {
1343 VCHIQ_SERVICE_T *service =
1344 find_service_by_port(state,
1345 (group<<5) + i);
1346 uint32_t service_flags;
1347 flags &= ~(1 << i);
1348 if (!service)
1349 continue;
1350 service_flags =
1351 atomic_xchg(&service->poll_flags, 0);
1352 if (service_flags &
1353 (1 << VCHIQ_POLL_REMOVE)) {
1354 vchiq_log_info(vchiq_core_log_level,
1355 "%d: ps - remove %d<->%d",
1356 state->id, service->localport,
1357 service->remoteport);
1358
1359 /* Make it look like a client, because
1360 it must be removed and not left in
1361 the LISTENING state. */
1362 service->public_fourcc =
1363 VCHIQ_FOURCC_INVALID;
1364
1365 if (vchiq_close_service_internal(
1366 service, 0/*!close_recvd*/) !=
1367 VCHIQ_SUCCESS)
1368 request_poll(state, service,
1369 VCHIQ_POLL_REMOVE);
1370 } else if (service_flags &
1371 (1 << VCHIQ_POLL_TERMINATE)) {
1372 vchiq_log_info(vchiq_core_log_level,
1373 "%d: ps - terminate %d<->%d",
1374 state->id, service->localport,
1375 service->remoteport);
1376 if (vchiq_close_service_internal(
1377 service, 0/*!close_recvd*/) !=
1378 VCHIQ_SUCCESS)
1379 request_poll(state, service,
1380 VCHIQ_POLL_TERMINATE);
1381 }
1382 if (service_flags & (1 << VCHIQ_POLL_TXNOTIFY))
1383 notify_bulks(service,
1384 &service->bulk_tx,
1385 1/*retry_poll*/);
1386 if (service_flags & (1 << VCHIQ_POLL_RXNOTIFY))
1387 notify_bulks(service,
1388 &service->bulk_rx,
1389 1/*retry_poll*/);
1390 unlock_service(service);
1391 }
1392 }
1393 }
1394 }
1395
1396 /* Called by the slot handler or application threads, holding the bulk mutex. */
1397 static int
resolve_bulks(VCHIQ_SERVICE_T * service,VCHIQ_BULK_QUEUE_T * queue)1398 resolve_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue)
1399 {
1400 VCHIQ_STATE_T *state = service->state;
1401 int resolved = 0;
1402 int rc;
1403
1404 while ((queue->process != queue->local_insert) &&
1405 (queue->process != queue->remote_insert)) {
1406 VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->process)];
1407
1408 vchiq_log_trace(vchiq_core_log_level,
1409 "%d: rb:%d %cx - li=%x ri=%x p=%x",
1410 state->id, service->localport,
1411 (queue == &service->bulk_tx) ? 't' : 'r',
1412 queue->local_insert, queue->remote_insert,
1413 queue->process);
1414
1415 WARN_ON(!((int)(queue->local_insert - queue->process) > 0));
1416 WARN_ON(!((int)(queue->remote_insert - queue->process) > 0));
1417
1418 rc = lmutex_lock_interruptible(&state->bulk_transfer_mutex);
1419 if (rc != 0)
1420 break;
1421
1422 vchiq_transfer_bulk(bulk);
1423 lmutex_unlock(&state->bulk_transfer_mutex);
1424
1425 if (SRVTRACE_ENABLED(service, VCHIQ_LOG_INFO)) {
1426 const char *header = (queue == &service->bulk_tx) ?
1427 "Send Bulk to" : "Recv Bulk from";
1428 if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED)
1429 vchiq_log_info(SRVTRACE_LEVEL(service),
1430 "%s %c%c%c%c d:%d len:%d %p<->%p",
1431 header,
1432 VCHIQ_FOURCC_AS_4CHARS(
1433 service->base.fourcc),
1434 service->remoteport,
1435 bulk->size,
1436 bulk->data,
1437 bulk->remote_data);
1438 else
1439 vchiq_log_info(SRVTRACE_LEVEL(service),
1440 "%s %c%c%c%c d:%d ABORTED - tx len:%d,"
1441 " rx len:%d %p<->%p",
1442 header,
1443 VCHIQ_FOURCC_AS_4CHARS(
1444 service->base.fourcc),
1445 service->remoteport,
1446 bulk->size,
1447 bulk->remote_size,
1448 bulk->data,
1449 bulk->remote_data);
1450 }
1451
1452 vchiq_complete_bulk(bulk);
1453 queue->process++;
1454 resolved++;
1455 }
1456 return resolved;
1457 }
1458
1459 /* Called with the bulk_mutex held */
1460 static void
abort_outstanding_bulks(VCHIQ_SERVICE_T * service,VCHIQ_BULK_QUEUE_T * queue)1461 abort_outstanding_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue)
1462 {
1463 int is_tx = (queue == &service->bulk_tx);
1464 vchiq_log_trace(vchiq_core_log_level,
1465 "%d: aob:%d %cx - li=%x ri=%x p=%x",
1466 service->state->id, service->localport, is_tx ? 't' : 'r',
1467 queue->local_insert, queue->remote_insert, queue->process);
1468
1469 WARN_ON(!((int)(queue->local_insert - queue->process) >= 0));
1470 WARN_ON(!((int)(queue->remote_insert - queue->process) >= 0));
1471
1472 while ((queue->process != queue->local_insert) ||
1473 (queue->process != queue->remote_insert)) {
1474 VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->process)];
1475
1476 if (queue->process == queue->remote_insert) {
1477 /* fabricate a matching dummy bulk */
1478 bulk->remote_data = NULL;
1479 bulk->remote_size = 0;
1480 queue->remote_insert++;
1481 }
1482
1483 if (queue->process != queue->local_insert) {
1484 vchiq_complete_bulk(bulk);
1485
1486 vchiq_log_info(SRVTRACE_LEVEL(service),
1487 "%s %c%c%c%c d:%d ABORTED - tx len:%d, "
1488 "rx len:%d",
1489 is_tx ? "Send Bulk to" : "Recv Bulk from",
1490 VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc),
1491 service->remoteport,
1492 bulk->size,
1493 bulk->remote_size);
1494 } else {
1495 /* fabricate a matching dummy bulk */
1496 bulk->data = NULL;
1497 bulk->size = 0;
1498 bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED;
1499 bulk->dir = is_tx ? VCHIQ_BULK_TRANSMIT :
1500 VCHIQ_BULK_RECEIVE;
1501 queue->local_insert++;
1502 }
1503
1504 queue->process++;
1505 }
1506 }
1507
1508 /* Called from the slot handler thread */
1509 static void
pause_bulks(VCHIQ_STATE_T * state)1510 pause_bulks(VCHIQ_STATE_T *state)
1511 {
1512 if (unlikely(atomic_inc_return(&pause_bulks_count) != 1)) {
1513 WARN_ON_ONCE(1);
1514 atomic_set(&pause_bulks_count, 1);
1515 return;
1516 }
1517
1518 /* Block bulk transfers from all services */
1519 lmutex_lock(&state->bulk_transfer_mutex);
1520 }
1521
1522 /* Called from the slot handler thread */
1523 static void
resume_bulks(VCHIQ_STATE_T * state)1524 resume_bulks(VCHIQ_STATE_T *state)
1525 {
1526 int i;
1527 if (unlikely(atomic_dec_return(&pause_bulks_count) != 0)) {
1528 WARN_ON_ONCE(1);
1529 atomic_set(&pause_bulks_count, 0);
1530 return;
1531 }
1532
1533 /* Allow bulk transfers from all services */
1534 lmutex_unlock(&state->bulk_transfer_mutex);
1535
1536 if (state->deferred_bulks == 0)
1537 return;
1538
1539 /* Deal with any bulks which had to be deferred due to being in
1540 * paused state. Don't try to match up to number of deferred bulks
1541 * in case we've had something come and close the service in the
1542 * interim - just process all bulk queues for all services */
1543 vchiq_log_info(vchiq_core_log_level, "%s: processing %d deferred bulks",
1544 __func__, state->deferred_bulks);
1545
1546 for (i = 0; i < state->unused_service; i++) {
1547 VCHIQ_SERVICE_T *service = state->services[i];
1548 int resolved_rx = 0;
1549 int resolved_tx = 0;
1550 if (!service || (service->srvstate != VCHIQ_SRVSTATE_OPEN))
1551 continue;
1552
1553 lmutex_lock(&service->bulk_mutex);
1554 resolved_rx = resolve_bulks(service, &service->bulk_rx);
1555 resolved_tx = resolve_bulks(service, &service->bulk_tx);
1556 lmutex_unlock(&service->bulk_mutex);
1557 if (resolved_rx)
1558 notify_bulks(service, &service->bulk_rx, 1);
1559 if (resolved_tx)
1560 notify_bulks(service, &service->bulk_tx, 1);
1561 }
1562 state->deferred_bulks = 0;
1563 }
1564
1565 static int
parse_open(VCHIQ_STATE_T * state,VCHIQ_HEADER_T * header)1566 parse_open(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header)
1567 {
1568 VCHIQ_SERVICE_T *service = NULL;
1569 int msgid, size;
1570 unsigned int localport, remoteport;
1571
1572 msgid = header->msgid;
1573 size = header->size;
1574 //int type = VCHIQ_MSG_TYPE(msgid);
1575 localport = VCHIQ_MSG_DSTPORT(msgid);
1576 remoteport = VCHIQ_MSG_SRCPORT(msgid);
1577 if (size >= sizeof(struct vchiq_open_payload)) {
1578 const struct vchiq_open_payload *payload =
1579 (struct vchiq_open_payload *)header->data;
1580 unsigned int fourcc;
1581
1582 fourcc = payload->fourcc;
1583 vchiq_log_info(vchiq_core_log_level,
1584 "%d: prs OPEN@%p (%d->'%c%c%c%c')",
1585 state->id, header,
1586 localport,
1587 VCHIQ_FOURCC_AS_4CHARS(fourcc));
1588
1589 service = get_listening_service(state, fourcc);
1590
1591 if (service) {
1592 /* A matching service exists */
1593 short version = payload->version;
1594 short version_min = payload->version_min;
1595 if ((service->version < version_min) ||
1596 (version < service->version_min)) {
1597 /* Version mismatch */
1598 vchiq_loud_error_header();
1599 vchiq_loud_error("%d: service %d (%c%c%c%c) "
1600 "version mismatch - local (%d, min %d)"
1601 " vs. remote (%d, min %d)",
1602 state->id, service->localport,
1603 VCHIQ_FOURCC_AS_4CHARS(fourcc),
1604 service->version, service->version_min,
1605 version, version_min);
1606 vchiq_loud_error_footer();
1607 unlock_service(service);
1608 service = NULL;
1609 goto fail_open;
1610 }
1611 service->peer_version = version;
1612
1613 if (service->srvstate == VCHIQ_SRVSTATE_LISTENING) {
1614 struct vchiq_openack_payload ack_payload = {
1615 service->version
1616 };
1617
1618 if (state->version_common <
1619 VCHIQ_VERSION_SYNCHRONOUS_MODE)
1620 service->sync = 0;
1621
1622 /* Acknowledge the OPEN */
1623 if (service->sync &&
1624 (state->version_common >=
1625 VCHIQ_VERSION_SYNCHRONOUS_MODE)) {
1626 if (queue_message_sync(
1627 state,
1628 NULL,
1629 VCHIQ_MAKE_MSG(
1630 VCHIQ_MSG_OPENACK,
1631 service->localport,
1632 remoteport),
1633 memcpy_copy_callback,
1634 &ack_payload,
1635 sizeof(ack_payload),
1636 0) == VCHIQ_RETRY)
1637 goto bail_not_ready;
1638 } else {
1639 if (queue_message(state,
1640 NULL,
1641 VCHIQ_MAKE_MSG(
1642 VCHIQ_MSG_OPENACK,
1643 service->localport,
1644 remoteport),
1645 memcpy_copy_callback,
1646 &ack_payload,
1647 sizeof(ack_payload),
1648 0) == VCHIQ_RETRY)
1649 goto bail_not_ready;
1650 }
1651
1652 /* The service is now open */
1653 vchiq_set_service_state(service,
1654 service->sync ? VCHIQ_SRVSTATE_OPENSYNC
1655 : VCHIQ_SRVSTATE_OPEN);
1656 }
1657
1658 service->remoteport = remoteport;
1659 service->client_id = ((int *)header->data)[1];
1660 if (make_service_callback(service, VCHIQ_SERVICE_OPENED,
1661 NULL, NULL) == VCHIQ_RETRY) {
1662 /* Bail out if not ready */
1663 service->remoteport = VCHIQ_PORT_FREE;
1664 goto bail_not_ready;
1665 }
1666
1667 /* Success - the message has been dealt with */
1668 unlock_service(service);
1669 return 1;
1670 }
1671 }
1672
1673 fail_open:
1674 /* No available service, or an invalid request - send a CLOSE */
1675 if (queue_message(state, NULL,
1676 VCHIQ_MAKE_MSG(VCHIQ_MSG_CLOSE, 0, VCHIQ_MSG_SRCPORT(msgid)),
1677 NULL, 0, 0, 0) == VCHIQ_RETRY)
1678 goto bail_not_ready;
1679
1680 return 1;
1681
1682 bail_not_ready:
1683 if (service)
1684 unlock_service(service);
1685
1686 return 0;
1687 }
1688
1689 /* Called by the slot handler thread */
1690 static void
parse_rx_slots(VCHIQ_STATE_T * state)1691 parse_rx_slots(VCHIQ_STATE_T *state)
1692 {
1693 VCHIQ_SHARED_STATE_T *remote = state->remote;
1694 VCHIQ_SERVICE_T *service = NULL;
1695 int tx_pos;
1696 DEBUG_INITIALISE(state->local)
1697
1698 tx_pos = remote->tx_pos;
1699
1700 while (state->rx_pos != tx_pos) {
1701 VCHIQ_HEADER_T *header;
1702 int msgid, size;
1703 int type;
1704 unsigned int localport, remoteport;
1705
1706 DEBUG_TRACE(PARSE_LINE);
1707 if (!state->rx_data) {
1708 int rx_index;
1709 WARN_ON(!((state->rx_pos & VCHIQ_SLOT_MASK) == 0));
1710 rx_index = remote->slot_queue[
1711 SLOT_QUEUE_INDEX_FROM_POS(state->rx_pos) &
1712 VCHIQ_SLOT_QUEUE_MASK];
1713 state->rx_data = (char *)SLOT_DATA_FROM_INDEX(state,
1714 rx_index);
1715 state->rx_info = SLOT_INFO_FROM_INDEX(state, rx_index);
1716
1717 /* Initialise use_count to one, and increment
1718 ** release_count at the end of the slot to avoid
1719 ** releasing the slot prematurely. */
1720 state->rx_info->use_count = 1;
1721 state->rx_info->release_count = 0;
1722 }
1723
1724 header = (VCHIQ_HEADER_T *)(state->rx_data +
1725 (state->rx_pos & VCHIQ_SLOT_MASK));
1726 DEBUG_VALUE(PARSE_HEADER, (size_t)header);
1727 msgid = header->msgid;
1728 DEBUG_VALUE(PARSE_MSGID, msgid);
1729 size = header->size;
1730 type = VCHIQ_MSG_TYPE(msgid);
1731 localport = VCHIQ_MSG_DSTPORT(msgid);
1732 remoteport = VCHIQ_MSG_SRCPORT(msgid);
1733
1734 if (type != VCHIQ_MSG_DATA)
1735 VCHIQ_STATS_INC(state, ctrl_rx_count);
1736
1737 switch (type) {
1738 case VCHIQ_MSG_OPENACK:
1739 case VCHIQ_MSG_CLOSE:
1740 case VCHIQ_MSG_DATA:
1741 case VCHIQ_MSG_BULK_RX:
1742 case VCHIQ_MSG_BULK_TX:
1743 case VCHIQ_MSG_BULK_RX_DONE:
1744 case VCHIQ_MSG_BULK_TX_DONE:
1745 service = find_service_by_port(state, localport);
1746 if ((!service ||
1747 ((service->remoteport != remoteport) &&
1748 (service->remoteport != VCHIQ_PORT_FREE))) &&
1749 (localport == 0) &&
1750 (type == VCHIQ_MSG_CLOSE)) {
1751 /* This could be a CLOSE from a client which
1752 hadn't yet received the OPENACK - look for
1753 the connected service */
1754 if (service)
1755 unlock_service(service);
1756 service = get_connected_service(state,
1757 remoteport);
1758 if (service)
1759 vchiq_log_warning(vchiq_core_log_level,
1760 "%d: prs %s@%p (%d->%d) - "
1761 "found connected service %d",
1762 state->id, msg_type_str(type),
1763 header,
1764 remoteport, localport,
1765 service->localport);
1766 }
1767
1768 if (!service) {
1769 vchiq_log_error(vchiq_core_log_level,
1770 "%d: prs %s@%p (%d->%d) - " /* XXX */
1771 "invalid/closed service %d",
1772 state->id, msg_type_str(type),
1773 header,
1774 remoteport, localport, localport);
1775 goto skip_message;
1776 }
1777 break;
1778 default:
1779 break;
1780 }
1781
1782 if (SRVTRACE_ENABLED(service, VCHIQ_LOG_INFO)) {
1783 int svc_fourcc;
1784
1785 svc_fourcc = service
1786 ? service->base.fourcc
1787 : VCHIQ_MAKE_FOURCC('?', '?', '?', '?');
1788 vchiq_log_info(SRVTRACE_LEVEL(service),
1789 "Rcvd Msg %s(%u) from %c%c%c%c s:%d d:%d "
1790 "len:%d",
1791 msg_type_str(type), type,
1792 VCHIQ_FOURCC_AS_4CHARS(svc_fourcc),
1793 remoteport, localport, size);
1794 if (size > 0)
1795 vchiq_log_dump_mem("Rcvd", 0, header->data,
1796 min(16, size));
1797 }
1798
1799 if (((size_t)header & VCHIQ_SLOT_MASK) + calc_stride(size)
1800 > VCHIQ_SLOT_SIZE) {
1801 vchiq_log_error(vchiq_core_log_level,
1802 "header %p (msgid %x) - size %x too big for "
1803 "slot",
1804 header, (unsigned int)msgid,
1805 (unsigned int)size);
1806 WARN(1, "oversized for slot\n");
1807 }
1808
1809 switch (type) {
1810 case VCHIQ_MSG_OPEN:
1811 WARN_ON(!(VCHIQ_MSG_DSTPORT(msgid) == 0));
1812 if (!parse_open(state, header))
1813 goto bail_not_ready;
1814 break;
1815 case VCHIQ_MSG_OPENACK:
1816 if (size >= sizeof(struct vchiq_openack_payload)) {
1817 const struct vchiq_openack_payload *payload =
1818 (struct vchiq_openack_payload *)
1819 header->data;
1820 service->peer_version = payload->version;
1821 }
1822 vchiq_log_info(vchiq_core_log_level,
1823 "%d: prs OPENACK@%p,%x (%d->%d) v:%d",
1824 state->id, header, size,
1825 remoteport, localport, service->peer_version);
1826 if (service->srvstate ==
1827 VCHIQ_SRVSTATE_OPENING) {
1828 service->remoteport = remoteport;
1829 vchiq_set_service_state(service,
1830 VCHIQ_SRVSTATE_OPEN);
1831 up(&service->remove_event);
1832 } else
1833 vchiq_log_error(vchiq_core_log_level,
1834 "OPENACK received in state %s",
1835 srvstate_names[service->srvstate]);
1836 break;
1837 case VCHIQ_MSG_CLOSE:
1838 WARN_ON(size != 0); /* There should be no data */
1839
1840 vchiq_log_info(vchiq_core_log_level,
1841 "%d: prs CLOSE@%p (%d->%d)",
1842 state->id, header,
1843 remoteport, localport);
1844
1845 mark_service_closing_internal(service, 1);
1846
1847 if (vchiq_close_service_internal(service,
1848 1/*close_recvd*/) == VCHIQ_RETRY)
1849 goto bail_not_ready;
1850
1851 vchiq_log_info(vchiq_core_log_level,
1852 "Close Service %c%c%c%c s:%u d:%d",
1853 VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc),
1854 service->localport,
1855 service->remoteport);
1856 break;
1857 case VCHIQ_MSG_DATA:
1858 vchiq_log_info(vchiq_core_log_level,
1859 "%d: prs DATA@%p,%x (%d->%d)",
1860 state->id, header, size,
1861 remoteport, localport);
1862
1863 if ((service->remoteport == remoteport)
1864 && (service->srvstate ==
1865 VCHIQ_SRVSTATE_OPEN)) {
1866 header->msgid = msgid | VCHIQ_MSGID_CLAIMED;
1867 claim_slot(state->rx_info);
1868 DEBUG_TRACE(PARSE_LINE);
1869 if (make_service_callback(service,
1870 VCHIQ_MESSAGE_AVAILABLE, header,
1871 NULL) == VCHIQ_RETRY) {
1872 DEBUG_TRACE(PARSE_LINE);
1873 goto bail_not_ready;
1874 }
1875 VCHIQ_SERVICE_STATS_INC(service, ctrl_rx_count);
1876 VCHIQ_SERVICE_STATS_ADD(service, ctrl_rx_bytes,
1877 size);
1878 } else {
1879 VCHIQ_STATS_INC(state, error_count);
1880 }
1881 break;
1882 case VCHIQ_MSG_CONNECT:
1883 vchiq_log_info(vchiq_core_log_level,
1884 "%d: prs CONNECT@%p",
1885 state->id, header);
1886 state->version_common = ((VCHIQ_SLOT_ZERO_T *)
1887 state->slot_data)->version;
1888 up(&state->connect);
1889 break;
1890 /*
1891 * XXXMDC Apparently nothing uses this
1892 * https://github.com/raspberrypi/linux/commit/14f4d72fb799a9b3170a45ab80d4a3ddad541960
1893 * but taking out the master bits is a whole new job
1894 */
1895 case VCHIQ_MSG_BULK_RX:
1896 case VCHIQ_MSG_BULK_TX:
1897 WARN_ON(1);
1898 break;
1899 #if 0
1900 {
1901 VCHIQ_BULK_QUEUE_T *queue;
1902 WARN_ON(!state->is_master);
1903 queue = (type == VCHIQ_MSG_BULK_RX) ?
1904 &service->bulk_tx : &service->bulk_rx;
1905 if ((service->remoteport == remoteport)
1906 && (service->srvstate ==
1907 VCHIQ_SRVSTATE_OPEN)) {
1908 VCHIQ_BULK_T *bulk;
1909 int resolved = 0;
1910
1911 DEBUG_TRACE(PARSE_LINE);
1912 if (lmutex_lock_interruptible(
1913 &service->bulk_mutex) != 0) {
1914 DEBUG_TRACE(PARSE_LINE);
1915 goto bail_not_ready;
1916 }
1917
1918 WARN_ON(!(queue->remote_insert < queue->remove +
1919 VCHIQ_NUM_SERVICE_BULKS));
1920 bulk = &queue->bulks[
1921 BULK_INDEX(queue->remote_insert)];
1922 bulk->remote_data =
1923 (void *)((int *)header->data)[0];
1924 bulk->remote_size = ((int *)header->data)[1];
1925 wmb();
1926
1927 vchiq_log_info(vchiq_core_log_level,
1928 "%d: prs %s@%p (%d->%d) %x@%p",
1929 state->id, msg_type_str(type),
1930 header,
1931 remoteport, localport,
1932 bulk->remote_size,
1933 bulk->remote_data);
1934
1935 queue->remote_insert++;
1936
1937 if (atomic_read(&pause_bulks_count)) {
1938 state->deferred_bulks++;
1939 vchiq_log_info(vchiq_core_log_level,
1940 "%s: deferring bulk (%d)",
1941 __func__,
1942 state->deferred_bulks);
1943 if (state->conn_state !=
1944 VCHIQ_CONNSTATE_PAUSE_SENT)
1945 vchiq_log_error(
1946 vchiq_core_log_level,
1947 "%s: bulks paused in "
1948 "unexpected state %s",
1949 __func__,
1950 conn_state_names[
1951 state->conn_state]);
1952 } else if (state->conn_state ==
1953 VCHIQ_CONNSTATE_CONNECTED) {
1954 DEBUG_TRACE(PARSE_LINE);
1955 resolved = resolve_bulks(service,
1956 queue);
1957 }
1958
1959 lmutex_unlock(&service->bulk_mutex);
1960 if (resolved)
1961 notify_bulks(service, queue,
1962 1//retry_poll
1963 );
1964 }
1965 }
1966 #endif
1967 case VCHIQ_MSG_BULK_RX_DONE:
1968 case VCHIQ_MSG_BULK_TX_DONE:
1969 WARN_ON(state->is_master);
1970 if ((service->remoteport == remoteport)
1971 && (service->srvstate !=
1972 VCHIQ_SRVSTATE_FREE)) {
1973 VCHIQ_BULK_QUEUE_T *queue;
1974 VCHIQ_BULK_T *bulk;
1975
1976 queue = (type == VCHIQ_MSG_BULK_RX_DONE) ?
1977 &service->bulk_rx : &service->bulk_tx;
1978
1979 DEBUG_TRACE(PARSE_LINE);
1980 if (lmutex_lock_interruptible(
1981 &service->bulk_mutex) != 0) {
1982 DEBUG_TRACE(PARSE_LINE);
1983 goto bail_not_ready;
1984 }
1985 if ((int)(queue->remote_insert -
1986 queue->local_insert) >= 0) {
1987 vchiq_log_error(vchiq_core_log_level,
1988 "%d: prs %s@%p (%d->%d) "
1989 "unexpected (ri=%d,li=%d)",
1990 state->id, msg_type_str(type),
1991 header,
1992 remoteport, localport,
1993 queue->remote_insert,
1994 queue->local_insert);
1995 lmutex_unlock(&service->bulk_mutex);
1996 break;
1997 }
1998
1999 BUG_ON(queue->process == queue->local_insert);
2000 BUG_ON(queue->process != queue->remote_insert);
2001
2002 bulk = &queue->bulks[
2003 BULK_INDEX(queue->remote_insert)];
2004 bulk->actual = *(int *)header->data;
2005 queue->remote_insert++;
2006
2007 vchiq_log_info(vchiq_core_log_level,
2008 "%d: prs %s@%p (%d->%d) %x@%p",
2009 state->id, msg_type_str(type),
2010 header,
2011 remoteport, localport,
2012 bulk->actual, bulk->data);
2013
2014 vchiq_log_trace(vchiq_core_log_level,
2015 "%d: prs:%d %cx li=%x ri=%x p=%x",
2016 state->id, localport,
2017 (type == VCHIQ_MSG_BULK_RX_DONE) ?
2018 'r' : 't',
2019 queue->local_insert,
2020 queue->remote_insert, queue->process);
2021
2022 DEBUG_TRACE(PARSE_LINE);
2023 WARN_ON(queue->process == queue->local_insert);
2024 vchiq_complete_bulk(bulk);
2025 queue->process++;
2026 lmutex_unlock(&service->bulk_mutex);
2027 DEBUG_TRACE(PARSE_LINE);
2028 notify_bulks(service, queue, 1/*retry_poll*/);
2029 DEBUG_TRACE(PARSE_LINE);
2030 }
2031 break;
2032 case VCHIQ_MSG_PADDING:
2033 vchiq_log_trace(vchiq_core_log_level,
2034 "%d: prs PADDING@%p,%x",
2035 state->id, header, size);
2036 break;
2037 case VCHIQ_MSG_PAUSE:
2038 /* If initiated, signal the application thread */
2039 vchiq_log_trace(vchiq_core_log_level,
2040 "%d: prs PAUSE@%p,%x",
2041 state->id, header, size);
2042 if (state->conn_state == VCHIQ_CONNSTATE_PAUSED) {
2043 vchiq_log_error(vchiq_core_log_level,
2044 "%d: PAUSE received in state PAUSED",
2045 state->id);
2046 break;
2047 }
2048 if (state->conn_state != VCHIQ_CONNSTATE_PAUSE_SENT) {
2049 /* Send a PAUSE in response */
2050 if (queue_message(state, NULL,
2051 VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0),
2052 NULL, 0, 0, QMFLAGS_NO_MUTEX_UNLOCK)
2053 == VCHIQ_RETRY)
2054 goto bail_not_ready;
2055 if (state->is_master)
2056 pause_bulks(state);
2057 }
2058 /* At this point slot_mutex is held */
2059 vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSED);
2060 vchiq_platform_paused(state);
2061 break;
2062 case VCHIQ_MSG_RESUME:
2063 vchiq_log_trace(vchiq_core_log_level,
2064 "%d: prs RESUME@%p,%x",
2065 state->id, header, size);
2066 /* Release the slot mutex */
2067 lmutex_unlock(&state->slot_mutex);
2068 if (state->is_master)
2069 resume_bulks(state);
2070 vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED);
2071 vchiq_platform_resumed(state);
2072 break;
2073
2074 case VCHIQ_MSG_REMOTE_USE:
2075 vchiq_on_remote_use(state);
2076 break;
2077 case VCHIQ_MSG_REMOTE_RELEASE:
2078 vchiq_on_remote_release(state);
2079 break;
2080 case VCHIQ_MSG_REMOTE_USE_ACTIVE:
2081 vchiq_on_remote_use_active(state);
2082 break;
2083
2084 default:
2085 vchiq_log_error(vchiq_core_log_level,
2086 "%d: prs invalid msgid %x@%p,%x",
2087 state->id, msgid, header, size);
2088 WARN(1, "invalid message\n");
2089 break;
2090 }
2091
2092 skip_message:
2093 if (service) {
2094 unlock_service(service);
2095 service = NULL;
2096 }
2097
2098 state->rx_pos += calc_stride(size);
2099
2100 DEBUG_TRACE(PARSE_LINE);
2101 /* Perform some housekeeping when the end of the slot is
2102 ** reached. */
2103 if ((state->rx_pos & VCHIQ_SLOT_MASK) == 0) {
2104 /* Remove the extra reference count. */
2105 release_slot(state, state->rx_info, NULL, NULL);
2106 state->rx_data = NULL;
2107 }
2108 }
2109
2110 bail_not_ready:
2111 if (service)
2112 unlock_service(service);
2113 }
2114
2115 /* Called by the slot handler thread */
2116 int slot_handler_func(void *v);
2117 int
slot_handler_func(void * v)2118 slot_handler_func(void *v)
2119 {
2120 VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v;
2121 VCHIQ_SHARED_STATE_T *local = state->local;
2122 DEBUG_INITIALISE(local)
2123
2124 while (1) {
2125 DEBUG_COUNT(SLOT_HANDLER_COUNT);
2126 DEBUG_TRACE(SLOT_HANDLER_LINE);
2127 remote_event_wait(state, &local->trigger);
2128
2129 rmb();
2130
2131 DEBUG_TRACE(SLOT_HANDLER_LINE);
2132 if (state->poll_needed) {
2133 /* Check if we need to suspend - may change our
2134 * conn_state */
2135 vchiq_platform_check_suspend(state);
2136
2137 state->poll_needed = 0;
2138
2139 /* Handle service polling and other rare conditions here
2140 ** out of the mainline code */
2141 switch (state->conn_state) {
2142 case VCHIQ_CONNSTATE_CONNECTED:
2143 /* Poll the services as requested */
2144 poll_services(state);
2145 break;
2146
2147 case VCHIQ_CONNSTATE_PAUSING:
2148 if (state->is_master)
2149 pause_bulks(state);
2150 if (queue_message(state, NULL,
2151 VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0),
2152 NULL, 0, 0,
2153 QMFLAGS_NO_MUTEX_UNLOCK)
2154 != VCHIQ_RETRY) {
2155 vchiq_set_conn_state(state,
2156 VCHIQ_CONNSTATE_PAUSE_SENT);
2157 } else {
2158 if (state->is_master)
2159 resume_bulks(state);
2160 /* Retry later */
2161 state->poll_needed = 1;
2162 }
2163 break;
2164
2165 case VCHIQ_CONNSTATE_PAUSED:
2166 vchiq_platform_resume(state);
2167 break;
2168
2169 case VCHIQ_CONNSTATE_RESUMING:
2170 if (queue_message(state, NULL,
2171 VCHIQ_MAKE_MSG(VCHIQ_MSG_RESUME, 0, 0),
2172 NULL, 0, 0, QMFLAGS_NO_MUTEX_LOCK)
2173 != VCHIQ_RETRY) {
2174 if (state->is_master)
2175 resume_bulks(state);
2176 vchiq_set_conn_state(state,
2177 VCHIQ_CONNSTATE_CONNECTED);
2178 vchiq_platform_resumed(state);
2179 } else {
2180 /* This should really be impossible,
2181 ** since the PAUSE should have flushed
2182 ** through outstanding messages. */
2183 vchiq_log_error(vchiq_core_log_level,
2184 "Failed to send RESUME "
2185 "message");
2186 BUG();
2187 }
2188 break;
2189
2190 case VCHIQ_CONNSTATE_PAUSE_TIMEOUT:
2191 case VCHIQ_CONNSTATE_RESUME_TIMEOUT:
2192 vchiq_platform_handle_timeout(state);
2193 break;
2194 default:
2195 break;
2196 }
2197
2198
2199 }
2200
2201 DEBUG_TRACE(SLOT_HANDLER_LINE);
2202 parse_rx_slots(state);
2203 }
2204 return 0;
2205 }
2206
2207
2208 /* Called by the recycle thread */
2209 int recycle_func(void *v);
2210 int
recycle_func(void * v)2211 recycle_func(void *v)
2212 {
2213 VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v;
2214 VCHIQ_SHARED_STATE_T *local = state->local;
2215
2216 while (1) {
2217 remote_event_wait(state, &local->recycle);
2218 process_free_queue(state);
2219 }
2220 return 0;
2221 }
2222
2223
2224 /* Called by the sync thread */
2225 int sync_func(void *v);
2226 int
sync_func(void * v)2227 sync_func(void *v)
2228 {
2229 VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v;
2230 VCHIQ_SHARED_STATE_T *local = state->local;
2231 VCHIQ_HEADER_T *header = (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state,
2232 state->remote->slot_sync);
2233
2234 while (1) {
2235 VCHIQ_SERVICE_T *service;
2236 int msgid, size;
2237 int type;
2238 unsigned int localport, remoteport;
2239
2240 remote_event_wait(state, &local->sync_trigger);
2241
2242 rmb();
2243
2244 msgid = header->msgid;
2245 size = header->size;
2246 type = VCHIQ_MSG_TYPE(msgid);
2247 localport = VCHIQ_MSG_DSTPORT(msgid);
2248 remoteport = VCHIQ_MSG_SRCPORT(msgid);
2249
2250 service = find_service_by_port(state, localport);
2251
2252 if (!service) {
2253 vchiq_log_error(vchiq_sync_log_level,
2254 "%d: sf %s@%p (%d->%d) - "
2255 "invalid/closed service %d",
2256 state->id, msg_type_str(type),
2257 header,
2258 remoteport, localport, localport);
2259 release_message_sync(state, header);
2260 continue;
2261 }
2262
2263 if (vchiq_sync_log_level >= VCHIQ_LOG_TRACE) {
2264 int svc_fourcc;
2265
2266 svc_fourcc = service
2267 ? service->base.fourcc
2268 : VCHIQ_MAKE_FOURCC('?', '?', '?', '?');
2269 vchiq_log_trace(vchiq_sync_log_level,
2270 "Rcvd Msg %s from %c%c%c%c s:%d d:%d len:%d",
2271 msg_type_str(type),
2272 VCHIQ_FOURCC_AS_4CHARS(svc_fourcc),
2273 remoteport, localport, size);
2274 if (size > 0)
2275 vchiq_log_dump_mem("Rcvd", 0, header->data,
2276 min(16, size));
2277 }
2278
2279 switch (type) {
2280 case VCHIQ_MSG_OPENACK:
2281 if (size >= sizeof(struct vchiq_openack_payload)) {
2282 const struct vchiq_openack_payload *payload =
2283 (struct vchiq_openack_payload *)
2284 header->data;
2285 service->peer_version = payload->version;
2286 }
2287 vchiq_log_info(vchiq_sync_log_level,
2288 "%d: sf OPENACK@%p,%x (%d->%d) v:%d",
2289 state->id, header, size,
2290 remoteport, localport, service->peer_version);
2291 if (service->srvstate == VCHIQ_SRVSTATE_OPENING) {
2292 service->remoteport = remoteport;
2293 vchiq_set_service_state(service,
2294 VCHIQ_SRVSTATE_OPENSYNC);
2295 service->sync = 1;
2296 up(&service->remove_event);
2297 }
2298 release_message_sync(state, header);
2299 break;
2300
2301 case VCHIQ_MSG_DATA:
2302 vchiq_log_trace(vchiq_sync_log_level,
2303 "%d: sf DATA@%p,%x (%d->%d)",
2304 state->id, header, size,
2305 remoteport, localport);
2306
2307 if ((service->remoteport == remoteport) &&
2308 (service->srvstate ==
2309 VCHIQ_SRVSTATE_OPENSYNC)) {
2310 if (make_service_callback(service,
2311 VCHIQ_MESSAGE_AVAILABLE, header,
2312 NULL) == VCHIQ_RETRY)
2313 vchiq_log_error(vchiq_sync_log_level,
2314 "synchronous callback to "
2315 "service %d returns "
2316 "VCHIQ_RETRY",
2317 localport);
2318 }
2319 break;
2320
2321 default:
2322 vchiq_log_error(vchiq_sync_log_level,
2323 "%d: sf unexpected msgid %x@%p,%x",
2324 state->id, msgid, header, size);
2325 release_message_sync(state, header);
2326 break;
2327 }
2328
2329 unlock_service(service);
2330 }
2331
2332 return 0;
2333 }
2334
2335
2336 static void
init_bulk_queue(VCHIQ_BULK_QUEUE_T * queue)2337 init_bulk_queue(VCHIQ_BULK_QUEUE_T *queue)
2338 {
2339 queue->local_insert = 0;
2340 queue->remote_insert = 0;
2341 queue->process = 0;
2342 queue->remote_notify = 0;
2343 queue->remove = 0;
2344 }
2345
2346
2347 inline const char *
get_conn_state_name(VCHIQ_CONNSTATE_T conn_state)2348 get_conn_state_name(VCHIQ_CONNSTATE_T conn_state)
2349 {
2350 return conn_state_names[conn_state];
2351 }
2352
2353
2354 VCHIQ_SLOT_ZERO_T *
vchiq_init_slots(void * mem_base,int mem_size)2355 vchiq_init_slots(void *mem_base, int mem_size)
2356 {
2357 int mem_align = (int)((VCHIQ_SLOT_SIZE - (long)mem_base) & VCHIQ_SLOT_MASK);
2358 VCHIQ_SLOT_ZERO_T *slot_zero =
2359 (VCHIQ_SLOT_ZERO_T *)((char *)mem_base + mem_align);
2360 int num_slots = (mem_size - mem_align)/VCHIQ_SLOT_SIZE;
2361 int first_data_slot = VCHIQ_SLOT_ZERO_SLOTS;
2362
2363 /* Ensure there is enough memory to run an absolutely minimum system */
2364 num_slots -= first_data_slot;
2365
2366 if (num_slots < 4) {
2367 vchiq_log_error(vchiq_core_log_level,
2368 "vchiq_init_slots - insufficient memory %x bytes",
2369 mem_size);
2370 return NULL;
2371 }
2372
2373 memset(slot_zero, 0, sizeof(VCHIQ_SLOT_ZERO_T));
2374
2375 slot_zero->magic = VCHIQ_MAGIC;
2376 slot_zero->version = VCHIQ_VERSION;
2377 slot_zero->version_min = VCHIQ_VERSION_MIN;
2378 slot_zero->slot_zero_size = sizeof(VCHIQ_SLOT_ZERO_T);
2379 slot_zero->slot_size = VCHIQ_SLOT_SIZE;
2380 slot_zero->max_slots = VCHIQ_MAX_SLOTS;
2381 slot_zero->max_slots_per_side = VCHIQ_MAX_SLOTS_PER_SIDE;
2382
2383 slot_zero->master.slot_sync = first_data_slot;
2384 slot_zero->master.slot_first = first_data_slot + 1;
2385 slot_zero->master.slot_last = first_data_slot + (num_slots/2) - 1;
2386 slot_zero->slave.slot_sync = first_data_slot + (num_slots/2);
2387 slot_zero->slave.slot_first = first_data_slot + (num_slots/2) + 1;
2388 slot_zero->slave.slot_last = first_data_slot + num_slots - 1;
2389
2390 return slot_zero;
2391 }
2392
2393 VCHIQ_STATUS_T
vchiq_init_state(VCHIQ_STATE_T * state,VCHIQ_SLOT_ZERO_T * slot_zero,int is_master)2394 vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero,
2395 int is_master)
2396 {
2397 VCHIQ_SHARED_STATE_T *local;
2398 VCHIQ_SHARED_STATE_T *remote;
2399 VCHIQ_STATUS_T status;
2400 char threadname[10];
2401 static int id;
2402 int i;
2403
2404 /* Check the input configuration */
2405
2406 if (slot_zero->magic != VCHIQ_MAGIC) {
2407 vchiq_loud_error_header();
2408 vchiq_loud_error("Invalid VCHIQ magic value found.");
2409 vchiq_loud_error("slot_zero=%p: magic=%x (expected %x)",
2410 slot_zero, slot_zero->magic, VCHIQ_MAGIC);
2411 vchiq_loud_error_footer();
2412 return VCHIQ_ERROR;
2413 }
2414
2415 vchiq_log_warning(vchiq_core_log_level,
2416 "local ver %d (min %d), remote ver %d.",
2417 VCHIQ_VERSION, VCHIQ_VERSION_MIN,
2418 slot_zero->version);
2419
2420 if (slot_zero->version < VCHIQ_VERSION_MIN) {
2421 vchiq_loud_error_header();
2422 vchiq_loud_error("Incompatible VCHIQ versions found.");
2423 vchiq_loud_error("slot_zero=%p: VideoCore version=%d "
2424 "(minimum %d)",
2425 slot_zero, slot_zero->version,
2426 VCHIQ_VERSION_MIN);
2427 vchiq_loud_error("Restart with a newer VideoCore image.");
2428 vchiq_loud_error_footer();
2429 return VCHIQ_ERROR;
2430 }
2431
2432 if (VCHIQ_VERSION < slot_zero->version_min) {
2433 vchiq_loud_error_header();
2434 vchiq_loud_error("Incompatible VCHIQ versions found.");
2435 vchiq_loud_error("slot_zero=%p: version=%d (VideoCore "
2436 "minimum %d)",
2437 slot_zero, VCHIQ_VERSION,
2438 slot_zero->version_min);
2439 vchiq_loud_error("Restart with a newer kernel.");
2440 vchiq_loud_error_footer();
2441 return VCHIQ_ERROR;
2442 }
2443
2444 if ((slot_zero->slot_zero_size != sizeof(VCHIQ_SLOT_ZERO_T)) ||
2445 (slot_zero->slot_size != VCHIQ_SLOT_SIZE) ||
2446 (slot_zero->max_slots != VCHIQ_MAX_SLOTS) ||
2447 (slot_zero->max_slots_per_side != VCHIQ_MAX_SLOTS_PER_SIDE)) {
2448 vchiq_loud_error_header();
2449 if (slot_zero->slot_zero_size != sizeof(VCHIQ_SLOT_ZERO_T))
2450 vchiq_loud_error("slot_zero=%p: slot_zero_size=%x "
2451 "(expected %zx)",
2452 slot_zero,
2453 slot_zero->slot_zero_size,
2454 sizeof(VCHIQ_SLOT_ZERO_T));
2455 if (slot_zero->slot_size != VCHIQ_SLOT_SIZE)
2456 vchiq_loud_error("slot_zero=%p: slot_size=%d "
2457 "(expected %d",
2458 slot_zero, slot_zero->slot_size,
2459 VCHIQ_SLOT_SIZE);
2460 if (slot_zero->max_slots != VCHIQ_MAX_SLOTS)
2461 vchiq_loud_error("slot_zero=%p: max_slots=%d "
2462 "(expected %d)",
2463 slot_zero, slot_zero->max_slots,
2464 VCHIQ_MAX_SLOTS);
2465 if (slot_zero->max_slots_per_side != VCHIQ_MAX_SLOTS_PER_SIDE)
2466 vchiq_loud_error("slot_zero=%p: max_slots_per_side=%d "
2467 "(expected %d)",
2468 slot_zero,
2469 slot_zero->max_slots_per_side,
2470 VCHIQ_MAX_SLOTS_PER_SIDE);
2471 vchiq_loud_error_footer();
2472 return VCHIQ_ERROR;
2473 }
2474
2475 if (VCHIQ_VERSION < slot_zero->version)
2476 slot_zero->version = VCHIQ_VERSION;
2477
2478 if (is_master) {
2479 local = &slot_zero->master;
2480 remote = &slot_zero->slave;
2481 } else {
2482 local = &slot_zero->slave;
2483 remote = &slot_zero->master;
2484 }
2485
2486 if (local->initialised) {
2487 vchiq_loud_error_header();
2488 if (remote->initialised)
2489 vchiq_loud_error("local state has already been "
2490 "initialised");
2491 else
2492 vchiq_loud_error("master/slave mismatch - two %ss",
2493 is_master ? "master" : "slave");
2494 vchiq_loud_error_footer();
2495 return VCHIQ_ERROR;
2496 }
2497
2498 memset(state, 0, sizeof(VCHIQ_STATE_T));
2499
2500 state->id = id++;
2501 state->is_master = is_master;
2502
2503 /*
2504 initialize shared state pointers
2505 */
2506
2507 state->local = local;
2508 state->remote = remote;
2509 state->slot_data = (VCHIQ_SLOT_T *)slot_zero;
2510
2511 /*
2512 initialize events and mutexes
2513 */
2514
2515 _sema_init(&state->connect, 0);
2516 lmutex_init(&state->mutex);
2517 _sema_init(&state->trigger_event, 0);
2518 _sema_init(&state->recycle_event, 0);
2519 _sema_init(&state->sync_trigger_event, 0);
2520 _sema_init(&state->sync_release_event, 0);
2521
2522 lmutex_init(&state->slot_mutex);
2523 lmutex_init(&state->recycle_mutex);
2524 lmutex_init(&state->sync_mutex);
2525 lmutex_init(&state->bulk_transfer_mutex);
2526
2527 _sema_init(&state->slot_available_event, 0);
2528 _sema_init(&state->slot_remove_event, 0);
2529 _sema_init(&state->data_quota_event, 0);
2530
2531 state->slot_queue_available = 0;
2532
2533 for (i = 0; i < VCHIQ_MAX_SERVICES; i++) {
2534 VCHIQ_SERVICE_QUOTA_T *service_quota =
2535 &state->service_quotas[i];
2536 _sema_init(&service_quota->quota_event, 0);
2537 }
2538
2539 for (i = local->slot_first; i <= local->slot_last; i++) {
2540 local->slot_queue[state->slot_queue_available++] = i;
2541 up(&state->slot_available_event);
2542 }
2543
2544 state->default_slot_quota = state->slot_queue_available/2;
2545 state->default_message_quota =
2546 min((unsigned short)(state->default_slot_quota * 256),
2547 (unsigned short)~0);
2548
2549 state->previous_data_index = -1;
2550 state->data_use_count = 0;
2551 state->data_quota = state->slot_queue_available - 1;
2552
2553 local->trigger.event = offsetof(VCHIQ_STATE_T, trigger_event);
2554 remote_event_create(state, &local->trigger);
2555 local->tx_pos = 0;
2556
2557 local->recycle.event = offsetof(VCHIQ_STATE_T, recycle_event);
2558 remote_event_create(state, &local->recycle);
2559 local->slot_queue_recycle = state->slot_queue_available;
2560
2561 local->sync_trigger.event = offsetof(VCHIQ_STATE_T, sync_trigger_event);
2562 remote_event_create(state, &local->sync_trigger);
2563
2564 local->sync_release.event = offsetof(VCHIQ_STATE_T, sync_release_event);
2565 remote_event_create(state, &local->sync_release);
2566
2567 /* At start-of-day, the slot is empty and available */
2568 ((VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, local->slot_sync))->msgid
2569 = VCHIQ_MSGID_PADDING;
2570 remote_event_signal_local(state, &local->sync_release);
2571
2572 local->debug[DEBUG_ENTRIES] = DEBUG_MAX;
2573
2574 status = vchiq_platform_init_state(state);
2575
2576 /*
2577 bring up slot handler thread
2578 */
2579 snprintf(threadname, sizeof(threadname), "VCHIQ-%d", state->id);
2580 state->slot_handler_thread = vchiq_thread_create(&slot_handler_func,
2581 (void *)state,
2582 threadname);
2583
2584 if (state->slot_handler_thread == NULL) {
2585 vchiq_loud_error_header();
2586 vchiq_loud_error("couldn't create thread %s", threadname);
2587 vchiq_loud_error_footer();
2588 return VCHIQ_ERROR;
2589 }
2590 set_user_nice(state->slot_handler_thread, -19);
2591 wake_up_process(state->slot_handler_thread);
2592
2593 snprintf(threadname, sizeof(threadname), "VCHIQr-%d", state->id);
2594 state->recycle_thread = vchiq_thread_create(&recycle_func,
2595 (void *)state,
2596 threadname);
2597 if (state->recycle_thread == NULL) {
2598 vchiq_loud_error_header();
2599 vchiq_loud_error("couldn't create thread %s", threadname);
2600 vchiq_loud_error_footer();
2601 return VCHIQ_ERROR;
2602 }
2603 set_user_nice(state->recycle_thread, -19);
2604 wake_up_process(state->recycle_thread);
2605
2606 snprintf(threadname, sizeof(threadname), "VCHIQs-%d", state->id);
2607 state->sync_thread = vchiq_thread_create(&sync_func,
2608 (void *)state,
2609 threadname);
2610 if (state->sync_thread == NULL) {
2611 vchiq_loud_error_header();
2612 vchiq_loud_error("couldn't create thread %s", threadname);
2613 vchiq_loud_error_footer();
2614 return VCHIQ_ERROR;
2615 }
2616 set_user_nice(state->sync_thread, -20);
2617 wake_up_process(state->sync_thread);
2618
2619 BUG_ON(state->id >= VCHIQ_MAX_STATES);
2620 vchiq_states[state->id] = state;
2621
2622 /* Indicate readiness to the other side */
2623 local->initialised = 1;
2624
2625 return status;
2626 }
2627
2628 /* Called from application thread when a client or server service is created. */
2629 VCHIQ_SERVICE_T *
vchiq_add_service_internal(VCHIQ_STATE_T * state,const VCHIQ_SERVICE_PARAMS_T * params,int srvstate,VCHIQ_INSTANCE_T instance,VCHIQ_USERDATA_TERM_T userdata_term)2630 vchiq_add_service_internal(VCHIQ_STATE_T *state,
2631 const VCHIQ_SERVICE_PARAMS_T *params, int srvstate,
2632 VCHIQ_INSTANCE_T instance, VCHIQ_USERDATA_TERM_T userdata_term)
2633 {
2634 VCHIQ_SERVICE_T *service;
2635
2636 service = kmalloc(sizeof(VCHIQ_SERVICE_T), GFP_KERNEL);
2637 if (service) {
2638 service->base.fourcc = params->fourcc;
2639 service->base.callback = params->callback;
2640 service->base.userdata = params->userdata;
2641 service->handle = VCHIQ_SERVICE_HANDLE_INVALID;
2642 service->ref_count = 1;
2643 service->srvstate = VCHIQ_SRVSTATE_FREE;
2644 service->userdata_term = userdata_term;
2645 service->localport = VCHIQ_PORT_FREE;
2646 service->remoteport = VCHIQ_PORT_FREE;
2647
2648 service->public_fourcc = (srvstate == VCHIQ_SRVSTATE_OPENING) ?
2649 VCHIQ_FOURCC_INVALID : params->fourcc;
2650 service->client_id = 0;
2651 service->auto_close = 1;
2652 service->sync = 0;
2653 service->closing = 0;
2654 service->trace = 0;
2655 atomic_set(&service->poll_flags, 0);
2656 service->version = params->version;
2657 service->version_min = params->version_min;
2658 service->state = state;
2659 service->instance = instance;
2660 service->service_use_count = 0;
2661 init_bulk_queue(&service->bulk_tx);
2662 init_bulk_queue(&service->bulk_rx);
2663 _sema_init(&service->remove_event, 0);
2664 _sema_init(&service->bulk_remove_event, 0);
2665 lmutex_init(&service->bulk_mutex);
2666 memset(&service->stats, 0, sizeof(service->stats));
2667 } else {
2668 vchiq_log_error(vchiq_core_log_level,
2669 "Out of memory");
2670 }
2671
2672 if (service) {
2673 VCHIQ_SERVICE_T **pservice = NULL;
2674 int i;
2675
2676 /* Although it is perfectly possible to use service_spinlock
2677 ** to protect the creation of services, it is overkill as it
2678 ** disables interrupts while the array is searched.
2679 ** The only danger is of another thread trying to create a
2680 ** service - service deletion is safe.
2681 ** Therefore it is preferable to use state->mutex which,
2682 ** although slower to claim, doesn't block interrupts while
2683 ** it is held.
2684 */
2685
2686 lmutex_lock(&state->mutex);
2687
2688 /* Prepare to use a previously unused service */
2689 if (state->unused_service < VCHIQ_MAX_SERVICES)
2690 pservice = &state->services[state->unused_service];
2691
2692 if (srvstate == VCHIQ_SRVSTATE_OPENING) {
2693 for (i = 0; i < state->unused_service; i++) {
2694 VCHIQ_SERVICE_T *srv = state->services[i];
2695 if (!srv) {
2696 pservice = &state->services[i];
2697 break;
2698 }
2699 }
2700 } else {
2701 for (i = (state->unused_service - 1); i >= 0; i--) {
2702 VCHIQ_SERVICE_T *srv = state->services[i];
2703 if (!srv)
2704 pservice = &state->services[i];
2705 else if ((srv->public_fourcc == params->fourcc)
2706 && ((srv->instance != instance) ||
2707 (srv->base.callback !=
2708 params->callback))) {
2709 /* There is another server using this
2710 ** fourcc which doesn't match. */
2711 pservice = NULL;
2712 break;
2713 }
2714 }
2715 }
2716
2717 if (pservice) {
2718 service->localport = (pservice - state->services);
2719 if (!handle_seq)
2720 handle_seq = VCHIQ_MAX_STATES *
2721 VCHIQ_MAX_SERVICES;
2722 service->handle = handle_seq |
2723 (state->id * VCHIQ_MAX_SERVICES) |
2724 service->localport;
2725 handle_seq += VCHIQ_MAX_STATES * VCHIQ_MAX_SERVICES;
2726 *pservice = service;
2727 if (pservice == &state->services[state->unused_service])
2728 state->unused_service++;
2729 }
2730
2731 lmutex_unlock(&state->mutex);
2732
2733 if (!pservice) {
2734 _sema_destroy(&service->remove_event);
2735 _sema_destroy(&service->bulk_remove_event);
2736 lmutex_destroy(&service->bulk_mutex);
2737
2738 kfree(service);
2739 service = NULL;
2740 }
2741 }
2742
2743 if (service) {
2744 VCHIQ_SERVICE_QUOTA_T *service_quota =
2745 &state->service_quotas[service->localport];
2746 service_quota->slot_quota = state->default_slot_quota;
2747 service_quota->message_quota = state->default_message_quota;
2748 if (service_quota->slot_use_count == 0)
2749 service_quota->previous_tx_index =
2750 SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos)
2751 - 1;
2752
2753 /* Bring this service online */
2754 vchiq_set_service_state(service, srvstate);
2755
2756 vchiq_log_info(vchiq_core_msg_log_level,
2757 "%s Service %c%c%c%c SrcPort:%d",
2758 (srvstate == VCHIQ_SRVSTATE_OPENING)
2759 ? "Open" : "Add",
2760 VCHIQ_FOURCC_AS_4CHARS(params->fourcc),
2761 service->localport);
2762 }
2763
2764 /* Don't unlock the service - leave it with a ref_count of 1. */
2765
2766 return service;
2767 }
2768
2769 VCHIQ_STATUS_T
vchiq_open_service_internal(VCHIQ_SERVICE_T * service,int client_id)2770 vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id)
2771 {
2772 struct vchiq_open_payload payload = {
2773 service->base.fourcc,
2774 client_id,
2775 service->version,
2776 service->version_min
2777 };
2778 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
2779
2780 service->client_id = client_id;
2781 vchiq_use_service_internal(service);
2782 status = queue_message(service->state,
2783 NULL,
2784 VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN,
2785 service->localport,
2786 0),
2787 memcpy_copy_callback,
2788 &payload,
2789 sizeof(payload),
2790 QMFLAGS_IS_BLOCKING);
2791 if (status == VCHIQ_SUCCESS) {
2792 /* Wait for the ACK/NAK */
2793 if (down_interruptible(&service->remove_event) != 0) {
2794 status = VCHIQ_RETRY;
2795 vchiq_release_service_internal(service);
2796 } else if ((service->srvstate != VCHIQ_SRVSTATE_OPEN) &&
2797 (service->srvstate != VCHIQ_SRVSTATE_OPENSYNC)) {
2798 if (service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT)
2799 vchiq_log_error(vchiq_core_log_level,
2800 "%d: osi - srvstate = %s (ref %d)",
2801 service->state->id,
2802 srvstate_names[service->srvstate],
2803 service->ref_count);
2804 status = VCHIQ_ERROR;
2805 VCHIQ_SERVICE_STATS_INC(service, error_count);
2806 vchiq_release_service_internal(service);
2807 }
2808 }
2809 return status;
2810 }
2811
2812 static void
release_service_messages(VCHIQ_SERVICE_T * service)2813 release_service_messages(VCHIQ_SERVICE_T *service)
2814 {
2815 VCHIQ_STATE_T *state = service->state;
2816 int slot_last = state->remote->slot_last;
2817 int i;
2818
2819 /* Release any claimed messages aimed at this service */
2820
2821 if (service->sync) {
2822 VCHIQ_HEADER_T *header =
2823 (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state,
2824 state->remote->slot_sync);
2825 if (VCHIQ_MSG_DSTPORT(header->msgid) == service->localport)
2826 release_message_sync(state, header);
2827
2828 return;
2829 }
2830
2831 for (i = state->remote->slot_first; i <= slot_last; i++) {
2832 VCHIQ_SLOT_INFO_T *slot_info =
2833 SLOT_INFO_FROM_INDEX(state, i);
2834 if (slot_info->release_count != slot_info->use_count) {
2835 char *data =
2836 (char *)SLOT_DATA_FROM_INDEX(state, i);
2837 unsigned int pos, end;
2838
2839 end = VCHIQ_SLOT_SIZE;
2840 if (data == state->rx_data)
2841 /* This buffer is still being read from - stop
2842 ** at the current read position */
2843 end = state->rx_pos & VCHIQ_SLOT_MASK;
2844
2845 pos = 0;
2846
2847 while (pos < end) {
2848 VCHIQ_HEADER_T *header =
2849 (VCHIQ_HEADER_T *)(data + pos);
2850 int msgid = header->msgid;
2851 int port = VCHIQ_MSG_DSTPORT(msgid);
2852 if ((port == service->localport) &&
2853 (msgid & VCHIQ_MSGID_CLAIMED)) {
2854 vchiq_log_info(vchiq_core_log_level,
2855 " fsi - hdr %p",
2856 header);
2857 release_slot(state, slot_info, header,
2858 NULL);
2859 }
2860 pos += calc_stride(header->size);
2861 if (pos > VCHIQ_SLOT_SIZE) {
2862 vchiq_log_error(vchiq_core_log_level,
2863 "fsi - pos %x: header %p, "
2864 "msgid %x, header->msgid %x, "
2865 "header->size %x",
2866 pos, header,
2867 msgid, header->msgid,
2868 header->size);
2869 WARN(1, "invalid slot position\n");
2870 }
2871 }
2872 }
2873 }
2874 }
2875
2876 static int
do_abort_bulks(VCHIQ_SERVICE_T * service)2877 do_abort_bulks(VCHIQ_SERVICE_T *service)
2878 {
2879 VCHIQ_STATUS_T status;
2880
2881 /* Abort any outstanding bulk transfers */
2882 if (lmutex_lock_interruptible(&service->bulk_mutex) != 0)
2883 return 0;
2884 abort_outstanding_bulks(service, &service->bulk_tx);
2885 abort_outstanding_bulks(service, &service->bulk_rx);
2886 lmutex_unlock(&service->bulk_mutex);
2887
2888 status = notify_bulks(service, &service->bulk_tx, 0/*!retry_poll*/);
2889 if (status == VCHIQ_SUCCESS)
2890 status = notify_bulks(service, &service->bulk_rx,
2891 0/*!retry_poll*/);
2892 return (status == VCHIQ_SUCCESS);
2893 }
2894
2895 static VCHIQ_STATUS_T
close_service_complete(VCHIQ_SERVICE_T * service,int failstate)2896 close_service_complete(VCHIQ_SERVICE_T *service, int failstate)
2897 {
2898 VCHIQ_STATUS_T status;
2899 int is_server = (service->public_fourcc != VCHIQ_FOURCC_INVALID);
2900 int newstate;
2901
2902 switch (service->srvstate) {
2903 case VCHIQ_SRVSTATE_OPEN:
2904 case VCHIQ_SRVSTATE_CLOSESENT:
2905 case VCHIQ_SRVSTATE_CLOSERECVD:
2906 if (is_server) {
2907 if (service->auto_close) {
2908 service->client_id = 0;
2909 service->remoteport = VCHIQ_PORT_FREE;
2910 newstate = VCHIQ_SRVSTATE_LISTENING;
2911 } else
2912 newstate = VCHIQ_SRVSTATE_CLOSEWAIT;
2913 } else
2914 newstate = VCHIQ_SRVSTATE_CLOSED;
2915 vchiq_set_service_state(service, newstate);
2916 break;
2917 case VCHIQ_SRVSTATE_LISTENING:
2918 break;
2919 default:
2920 vchiq_log_error(vchiq_core_log_level,
2921 "close_service_complete(%x) called in state %s",
2922 service->handle, srvstate_names[service->srvstate]);
2923 WARN(1, "close_service_complete in unexpected state\n");
2924 return VCHIQ_ERROR;
2925 }
2926
2927 status = make_service_callback(service,
2928 VCHIQ_SERVICE_CLOSED, NULL, NULL);
2929
2930 if (status != VCHIQ_RETRY) {
2931 int uc = service->service_use_count;
2932 int i;
2933 /* Complete the close process */
2934 for (i = 0; i < uc; i++)
2935 /* cater for cases where close is forced and the
2936 ** client may not close all it's handles */
2937 vchiq_release_service_internal(service);
2938
2939 service->client_id = 0;
2940 service->remoteport = VCHIQ_PORT_FREE;
2941
2942 if (service->srvstate == VCHIQ_SRVSTATE_CLOSED)
2943 vchiq_free_service_internal(service);
2944 else if (service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) {
2945 if (is_server)
2946 service->closing = 0;
2947
2948 up(&service->remove_event);
2949 }
2950 } else
2951 vchiq_set_service_state(service, failstate);
2952
2953 return status;
2954 }
2955
2956 /* Called by the slot handler */
2957 VCHIQ_STATUS_T
vchiq_close_service_internal(VCHIQ_SERVICE_T * service,int close_recvd)2958 vchiq_close_service_internal(VCHIQ_SERVICE_T *service, int close_recvd)
2959 {
2960 VCHIQ_STATE_T *state = service->state;
2961 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
2962 int is_server = (service->public_fourcc != VCHIQ_FOURCC_INVALID);
2963
2964 vchiq_log_info(vchiq_core_log_level, "%d: csi:%d,%d (%s)",
2965 service->state->id, service->localport, close_recvd,
2966 srvstate_names[service->srvstate]);
2967
2968 switch (service->srvstate) {
2969 case VCHIQ_SRVSTATE_CLOSED:
2970 case VCHIQ_SRVSTATE_HIDDEN:
2971 case VCHIQ_SRVSTATE_LISTENING:
2972 case VCHIQ_SRVSTATE_CLOSEWAIT:
2973 if (close_recvd)
2974 vchiq_log_error(vchiq_core_log_level,
2975 "vchiq_close_service_internal(1) called "
2976 "in state %s",
2977 srvstate_names[service->srvstate]);
2978 else if (is_server) {
2979 if (service->srvstate == VCHIQ_SRVSTATE_LISTENING) {
2980 status = VCHIQ_ERROR;
2981 } else {
2982 service->client_id = 0;
2983 service->remoteport = VCHIQ_PORT_FREE;
2984 if (service->srvstate ==
2985 VCHIQ_SRVSTATE_CLOSEWAIT)
2986 vchiq_set_service_state(service,
2987 VCHIQ_SRVSTATE_LISTENING);
2988 }
2989 up(&service->remove_event);
2990 } else
2991 vchiq_free_service_internal(service);
2992 break;
2993 case VCHIQ_SRVSTATE_OPENING:
2994 if (close_recvd) {
2995 /* The open was rejected - tell the user */
2996 vchiq_set_service_state(service,
2997 VCHIQ_SRVSTATE_CLOSEWAIT);
2998 up(&service->remove_event);
2999 } else {
3000 /* Shutdown mid-open - let the other side know */
3001 status = queue_message(state, service,
3002 VCHIQ_MAKE_MSG
3003 (VCHIQ_MSG_CLOSE,
3004 service->localport,
3005 VCHIQ_MSG_DSTPORT(service->remoteport)),
3006 NULL, 0, 0, 0);
3007 }
3008 break;
3009
3010 case VCHIQ_SRVSTATE_OPENSYNC:
3011 lmutex_lock(&state->sync_mutex);
3012 /* Drop through */
3013
3014 case VCHIQ_SRVSTATE_OPEN:
3015 if (state->is_master || close_recvd) {
3016 if (!do_abort_bulks(service))
3017 status = VCHIQ_RETRY;
3018 }
3019
3020 release_service_messages(service);
3021
3022 if (status == VCHIQ_SUCCESS)
3023 status = queue_message(state, service,
3024 VCHIQ_MAKE_MSG
3025 (VCHIQ_MSG_CLOSE,
3026 service->localport,
3027 VCHIQ_MSG_DSTPORT(service->remoteport)),
3028 NULL, 0, 0, QMFLAGS_NO_MUTEX_UNLOCK);
3029
3030 if (status == VCHIQ_SUCCESS) {
3031 if (!close_recvd) {
3032 /* Change the state while the mutex is
3033 still held */
3034 vchiq_set_service_state(service,
3035 VCHIQ_SRVSTATE_CLOSESENT);
3036 lmutex_unlock(&state->slot_mutex);
3037 if (service->sync)
3038 lmutex_unlock(&state->sync_mutex);
3039 break;
3040 }
3041 } else if (service->srvstate == VCHIQ_SRVSTATE_OPENSYNC) {
3042 lmutex_unlock(&state->sync_mutex);
3043 break;
3044 } else
3045 break;
3046
3047 /* Change the state while the mutex is still held */
3048 vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSERECVD);
3049 lmutex_unlock(&state->slot_mutex);
3050 if (service->sync)
3051 lmutex_unlock(&state->sync_mutex);
3052
3053 status = close_service_complete(service,
3054 VCHIQ_SRVSTATE_CLOSERECVD);
3055 break;
3056
3057 case VCHIQ_SRVSTATE_CLOSESENT:
3058 if (!close_recvd)
3059 /* This happens when a process is killed mid-close */
3060 break;
3061
3062 if (!state->is_master) {
3063 if (!do_abort_bulks(service)) {
3064 status = VCHIQ_RETRY;
3065 break;
3066 }
3067 }
3068
3069 if (status == VCHIQ_SUCCESS)
3070 status = close_service_complete(service,
3071 VCHIQ_SRVSTATE_CLOSERECVD);
3072 break;
3073
3074 case VCHIQ_SRVSTATE_CLOSERECVD:
3075 if (!close_recvd && is_server)
3076 /* Force into LISTENING mode */
3077 vchiq_set_service_state(service,
3078 VCHIQ_SRVSTATE_LISTENING);
3079 status = close_service_complete(service,
3080 VCHIQ_SRVSTATE_CLOSERECVD);
3081 break;
3082
3083 default:
3084 vchiq_log_error(vchiq_core_log_level,
3085 "vchiq_close_service_internal(%d) called in state %s",
3086 close_recvd, srvstate_names[service->srvstate]);
3087 break;
3088 }
3089
3090 return status;
3091 }
3092
3093 /* Called from the application process upon process death */
3094 void
vchiq_terminate_service_internal(VCHIQ_SERVICE_T * service)3095 vchiq_terminate_service_internal(VCHIQ_SERVICE_T *service)
3096 {
3097 VCHIQ_STATE_T *state = service->state;
3098
3099 vchiq_log_info(vchiq_core_log_level, "%d: tsi - (%d<->%d)",
3100 state->id, service->localport, service->remoteport);
3101
3102 mark_service_closing(service);
3103
3104 /* Mark the service for removal by the slot handler */
3105 request_poll(state, service, VCHIQ_POLL_REMOVE);
3106 }
3107
3108 /* Called from the slot handler */
3109 void
vchiq_free_service_internal(VCHIQ_SERVICE_T * service)3110 vchiq_free_service_internal(VCHIQ_SERVICE_T *service)
3111 {
3112 VCHIQ_STATE_T *state = service->state;
3113
3114 vchiq_log_info(vchiq_core_log_level, "%d: fsi - (%d)",
3115 state->id, service->localport);
3116
3117 switch (service->srvstate) {
3118 case VCHIQ_SRVSTATE_OPENING:
3119 case VCHIQ_SRVSTATE_CLOSED:
3120 case VCHIQ_SRVSTATE_HIDDEN:
3121 case VCHIQ_SRVSTATE_LISTENING:
3122 case VCHIQ_SRVSTATE_CLOSEWAIT:
3123 break;
3124 default:
3125 vchiq_log_error(vchiq_core_log_level,
3126 "%d: fsi - (%d) in state %s",
3127 state->id, service->localport,
3128 srvstate_names[service->srvstate]);
3129 return;
3130 }
3131
3132 vchiq_set_service_state(service, VCHIQ_SRVSTATE_FREE);
3133
3134 up(&service->remove_event);
3135
3136 /* Release the initial lock */
3137 unlock_service(service);
3138 }
3139
3140 VCHIQ_STATUS_T
vchiq_connect_internal(VCHIQ_STATE_T * state,VCHIQ_INSTANCE_T instance)3141 vchiq_connect_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance)
3142 {
3143 VCHIQ_SERVICE_T *service;
3144 int i;
3145
3146 /* Find all services registered to this client and enable them. */
3147 i = 0;
3148 while ((service = next_service_by_instance(state, instance,
3149 &i)) != NULL) {
3150 if (service->srvstate == VCHIQ_SRVSTATE_HIDDEN)
3151 vchiq_set_service_state(service,
3152 VCHIQ_SRVSTATE_LISTENING);
3153 unlock_service(service);
3154 }
3155
3156 if (state->conn_state == VCHIQ_CONNSTATE_DISCONNECTED) {
3157 if (queue_message(state, NULL,
3158 VCHIQ_MAKE_MSG(VCHIQ_MSG_CONNECT, 0, 0), NULL, 0,
3159 0, QMFLAGS_IS_BLOCKING) == VCHIQ_RETRY)
3160 return VCHIQ_RETRY;
3161
3162 vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTING);
3163 }
3164
3165 if (state->conn_state == VCHIQ_CONNSTATE_CONNECTING) {
3166 if (down_interruptible(&state->connect) != 0)
3167 return VCHIQ_RETRY;
3168
3169 vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED);
3170 up(&state->connect);
3171 }
3172
3173 return VCHIQ_SUCCESS;
3174 }
3175
3176 VCHIQ_STATUS_T
vchiq_shutdown_internal(VCHIQ_STATE_T * state,VCHIQ_INSTANCE_T instance)3177 vchiq_shutdown_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance)
3178 {
3179 VCHIQ_SERVICE_T *service;
3180 int i;
3181
3182 /* Find all services registered to this client and enable them. */
3183 i = 0;
3184 while ((service = next_service_by_instance(state, instance,
3185 &i)) != NULL) {
3186 (void)vchiq_remove_service(service->handle);
3187 unlock_service(service);
3188 }
3189
3190 return VCHIQ_SUCCESS;
3191 }
3192
3193 VCHIQ_STATUS_T
vchiq_pause_internal(VCHIQ_STATE_T * state)3194 vchiq_pause_internal(VCHIQ_STATE_T *state)
3195 {
3196 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
3197
3198 switch (state->conn_state) {
3199 case VCHIQ_CONNSTATE_CONNECTED:
3200 /* Request a pause */
3201 vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSING);
3202 request_poll(state, NULL, 0);
3203 break;
3204 default:
3205 vchiq_log_error(vchiq_core_log_level,
3206 "vchiq_pause_internal in state %s\n",
3207 conn_state_names[state->conn_state]);
3208 status = VCHIQ_ERROR;
3209 VCHIQ_STATS_INC(state, error_count);
3210 break;
3211 }
3212
3213 return status;
3214 }
3215
3216 VCHIQ_STATUS_T
vchiq_resume_internal(VCHIQ_STATE_T * state)3217 vchiq_resume_internal(VCHIQ_STATE_T *state)
3218 {
3219 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
3220
3221 if (state->conn_state == VCHIQ_CONNSTATE_PAUSED) {
3222 vchiq_set_conn_state(state, VCHIQ_CONNSTATE_RESUMING);
3223 request_poll(state, NULL, 0);
3224 } else {
3225 status = VCHIQ_ERROR;
3226 VCHIQ_STATS_INC(state, error_count);
3227 }
3228
3229 return status;
3230 }
3231
3232 VCHIQ_STATUS_T
vchiq_close_service(VCHIQ_SERVICE_HANDLE_T handle)3233 vchiq_close_service(VCHIQ_SERVICE_HANDLE_T handle)
3234 {
3235 /* Unregister the service */
3236 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
3237 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
3238
3239 if (!service)
3240 return VCHIQ_ERROR;
3241
3242 vchiq_log_info(vchiq_core_log_level,
3243 "%d: close_service:%d",
3244 service->state->id, service->localport);
3245
3246 if ((service->srvstate == VCHIQ_SRVSTATE_FREE) ||
3247 (service->srvstate == VCHIQ_SRVSTATE_LISTENING) ||
3248 (service->srvstate == VCHIQ_SRVSTATE_HIDDEN)) {
3249 unlock_service(service);
3250 return VCHIQ_ERROR;
3251 }
3252
3253 mark_service_closing(service);
3254
3255 if (current == service->state->slot_handler_thread) {
3256 status = vchiq_close_service_internal(service,
3257 0/*!close_recvd*/);
3258 BUG_ON(status == VCHIQ_RETRY);
3259 } else {
3260 /* Mark the service for termination by the slot handler */
3261 request_poll(service->state, service, VCHIQ_POLL_TERMINATE);
3262 }
3263
3264 while (1) {
3265 if (down_interruptible(&service->remove_event) != 0) {
3266 status = VCHIQ_RETRY;
3267 break;
3268 }
3269
3270 if ((service->srvstate == VCHIQ_SRVSTATE_FREE) ||
3271 (service->srvstate == VCHIQ_SRVSTATE_LISTENING) ||
3272 (service->srvstate == VCHIQ_SRVSTATE_OPEN))
3273 break;
3274
3275 vchiq_log_warning(vchiq_core_log_level,
3276 "%d: close_service:%d - waiting in state %s",
3277 service->state->id, service->localport,
3278 srvstate_names[service->srvstate]);
3279 }
3280
3281 if ((status == VCHIQ_SUCCESS) &&
3282 (service->srvstate != VCHIQ_SRVSTATE_FREE) &&
3283 (service->srvstate != VCHIQ_SRVSTATE_LISTENING))
3284 status = VCHIQ_ERROR;
3285
3286 unlock_service(service);
3287
3288 return status;
3289 }
3290
3291 VCHIQ_STATUS_T
vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T handle)3292 vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T handle)
3293 {
3294 /* Unregister the service */
3295 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
3296 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
3297
3298 if (!service)
3299 return VCHIQ_ERROR;
3300
3301 vchiq_log_info(vchiq_core_log_level,
3302 "%d: remove_service:%d",
3303 service->state->id, service->localport);
3304
3305 if (service->srvstate == VCHIQ_SRVSTATE_FREE) {
3306 unlock_service(service);
3307 return VCHIQ_ERROR;
3308 }
3309
3310 mark_service_closing(service);
3311
3312 if ((service->srvstate == VCHIQ_SRVSTATE_HIDDEN) ||
3313 (current == service->state->slot_handler_thread)) {
3314 /* Make it look like a client, because it must be removed and
3315 not left in the LISTENING state. */
3316 service->public_fourcc = VCHIQ_FOURCC_INVALID;
3317
3318 status = vchiq_close_service_internal(service,
3319 0/*!close_recvd*/);
3320 BUG_ON(status == VCHIQ_RETRY);
3321 } else {
3322 /* Mark the service for removal by the slot handler */
3323 request_poll(service->state, service, VCHIQ_POLL_REMOVE);
3324 }
3325 while (1) {
3326 if (down_interruptible(&service->remove_event) != 0) {
3327 status = VCHIQ_RETRY;
3328 break;
3329 }
3330
3331 if ((service->srvstate == VCHIQ_SRVSTATE_FREE) ||
3332 (service->srvstate == VCHIQ_SRVSTATE_OPEN))
3333 break;
3334
3335 vchiq_log_warning(vchiq_core_log_level,
3336 "%d: remove_service:%d - waiting in state %s",
3337 service->state->id, service->localport,
3338 srvstate_names[service->srvstate]);
3339 }
3340
3341 if ((status == VCHIQ_SUCCESS) &&
3342 (service->srvstate != VCHIQ_SRVSTATE_FREE))
3343 status = VCHIQ_ERROR;
3344
3345 unlock_service(service);
3346
3347 return status;
3348 }
3349
3350
3351 /* This function may be called by kernel threads or user threads.
3352 * User threads may receive VCHIQ_RETRY to indicate that a signal has been
3353 * received and the call should be retried after being returned to user
3354 * context.
3355 * When called in blocking mode, the userdata field points to a bulk_waiter
3356 * structure.
3357 */
3358 VCHIQ_STATUS_T
vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle,VCHI_MEM_HANDLE_T memhandle,void * offset,int size,void * userdata,VCHIQ_BULK_MODE_T mode,VCHIQ_BULK_DIR_T dir)3359 vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle,
3360 VCHI_MEM_HANDLE_T memhandle, void *offset, int size, void *userdata,
3361 VCHIQ_BULK_MODE_T mode, VCHIQ_BULK_DIR_T dir)
3362 {
3363 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
3364 VCHIQ_BULK_QUEUE_T *queue;
3365 VCHIQ_BULK_T *bulk;
3366 VCHIQ_STATE_T *state;
3367 struct bulk_waiter *bulk_waiter = NULL;
3368 const char dir_char = (dir == VCHIQ_BULK_TRANSMIT) ? 't' : 'r';
3369 const int dir_msgtype = (dir == VCHIQ_BULK_TRANSMIT) ?
3370 VCHIQ_MSG_BULK_TX : VCHIQ_MSG_BULK_RX;
3371 VCHIQ_STATUS_T status = VCHIQ_ERROR;
3372
3373 if (!service ||
3374 (service->srvstate != VCHIQ_SRVSTATE_OPEN) ||
3375 ((memhandle == VCHI_MEM_HANDLE_INVALID) && (offset == NULL)) ||
3376 (vchiq_check_service(service) != VCHIQ_SUCCESS))
3377 goto error_exit;
3378
3379 switch (mode) {
3380 case VCHIQ_BULK_MODE_NOCALLBACK:
3381 case VCHIQ_BULK_MODE_CALLBACK:
3382 break;
3383 case VCHIQ_BULK_MODE_BLOCKING:
3384 bulk_waiter = (struct bulk_waiter *)userdata;
3385 _sema_init(&bulk_waiter->event, 0);
3386 bulk_waiter->actual = 0;
3387 bulk_waiter->bulk = NULL;
3388 break;
3389 case VCHIQ_BULK_MODE_WAITING:
3390 bulk_waiter = (struct bulk_waiter *)userdata;
3391 bulk = bulk_waiter->bulk;
3392 goto waiting;
3393 default:
3394 goto error_exit;
3395 }
3396
3397 state = service->state;
3398
3399 queue = (dir == VCHIQ_BULK_TRANSMIT) ?
3400 &service->bulk_tx : &service->bulk_rx;
3401
3402 if (lmutex_lock_interruptible(&service->bulk_mutex) != 0) {
3403 status = VCHIQ_RETRY;
3404 goto error_exit;
3405 }
3406
3407 if (queue->local_insert == queue->remove + VCHIQ_NUM_SERVICE_BULKS) {
3408 VCHIQ_SERVICE_STATS_INC(service, bulk_stalls);
3409 do {
3410 lmutex_unlock(&service->bulk_mutex);
3411 if (down_interruptible(&service->bulk_remove_event)
3412 != 0) {
3413 status = VCHIQ_RETRY;
3414 goto error_exit;
3415 }
3416 if (lmutex_lock_interruptible(&service->bulk_mutex)
3417 != 0) {
3418 status = VCHIQ_RETRY;
3419 goto error_exit;
3420 }
3421 } while (queue->local_insert == queue->remove +
3422 VCHIQ_NUM_SERVICE_BULKS);
3423 }
3424
3425 bulk = &queue->bulks[BULK_INDEX(queue->local_insert)];
3426
3427 bulk->mode = mode;
3428 bulk->dir = dir;
3429 bulk->userdata = userdata;
3430 bulk->size = size;
3431 bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED;
3432
3433 if (vchiq_prepare_bulk_data(bulk, memhandle, offset, size, dir) !=
3434 VCHIQ_SUCCESS)
3435 goto unlock_error_exit;
3436
3437 wmb();
3438
3439 vchiq_log_info(vchiq_core_log_level,
3440 "%d: bt (%d->%d) %cx %x@%p %p",
3441 state->id,
3442 service->localport, service->remoteport, dir_char,
3443 size, bulk->data, userdata);
3444
3445 /* The slot mutex must be held when the service is being closed, so
3446 claim it here to ensure that isn't happening */
3447 if (lmutex_lock_interruptible(&state->slot_mutex) != 0) {
3448 status = VCHIQ_RETRY;
3449 goto cancel_bulk_error_exit;
3450 }
3451
3452 if (service->srvstate != VCHIQ_SRVSTATE_OPEN)
3453 goto unlock_both_error_exit;
3454
3455 if (state->is_master) {
3456 queue->local_insert++;
3457 if (resolve_bulks(service, queue))
3458 request_poll(state, service,
3459 (dir == VCHIQ_BULK_TRANSMIT) ?
3460 VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY);
3461 } else {
3462 uint32_t payload[2] = { (uint32_t)(uintptr_t)bulk->data, bulk->size };
3463
3464 status = queue_message(state,
3465 NULL,
3466 VCHIQ_MAKE_MSG(dir_msgtype,
3467 service->localport,
3468 service->remoteport),
3469 memcpy_copy_callback,
3470 &payload,
3471 sizeof(payload),
3472 QMFLAGS_IS_BLOCKING |
3473 QMFLAGS_NO_MUTEX_LOCK |
3474 QMFLAGS_NO_MUTEX_UNLOCK);
3475 if (status != VCHIQ_SUCCESS) {
3476 goto unlock_both_error_exit;
3477 }
3478 queue->local_insert++;
3479 }
3480
3481 lmutex_unlock(&state->slot_mutex);
3482 lmutex_unlock(&service->bulk_mutex);
3483
3484 vchiq_log_trace(vchiq_core_log_level,
3485 "%d: bt:%d %cx li=%x ri=%x p=%x",
3486 state->id,
3487 service->localport, dir_char,
3488 queue->local_insert, queue->remote_insert, queue->process);
3489
3490 waiting:
3491 unlock_service(service);
3492
3493 status = VCHIQ_SUCCESS;
3494
3495 if (bulk_waiter) {
3496 bulk_waiter->bulk = bulk;
3497 if (down_interruptible(&bulk_waiter->event) != 0)
3498 status = VCHIQ_RETRY;
3499 else if (bulk_waiter->actual == VCHIQ_BULK_ACTUAL_ABORTED)
3500 status = VCHIQ_ERROR;
3501 }
3502
3503 return status;
3504
3505 unlock_both_error_exit:
3506 lmutex_unlock(&state->slot_mutex);
3507 cancel_bulk_error_exit:
3508 vchiq_complete_bulk(bulk);
3509 unlock_error_exit:
3510 lmutex_unlock(&service->bulk_mutex);
3511
3512 error_exit:
3513 if (service)
3514 unlock_service(service);
3515 return status;
3516 }
3517
3518 VCHIQ_STATUS_T
vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle,ssize_t (* copy_callback)(void * context,void * dest,size_t offset,size_t maxsize),void * context,size_t size)3519 vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle,
3520 ssize_t (*copy_callback)(void *context, void *dest,
3521 size_t offset, size_t maxsize),
3522 void *context,
3523 size_t size)
3524 {
3525 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
3526 VCHIQ_STATUS_T status = VCHIQ_ERROR;
3527
3528 if (!service ||
3529 (vchiq_check_service(service) != VCHIQ_SUCCESS))
3530 goto error_exit;
3531
3532 if (!size) {
3533 VCHIQ_SERVICE_STATS_INC(service, error_count);
3534 goto error_exit;
3535
3536 }
3537
3538 if (size > VCHIQ_MAX_MSG_SIZE) {
3539 VCHIQ_SERVICE_STATS_INC(service, error_count);
3540 goto error_exit;
3541 }
3542
3543 switch (service->srvstate) {
3544 case VCHIQ_SRVSTATE_OPEN:
3545 status = queue_message(service->state, service,
3546 VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA,
3547 service->localport,
3548 service->remoteport),
3549 copy_callback, context, size, 1);
3550 break;
3551 case VCHIQ_SRVSTATE_OPENSYNC:
3552 status = queue_message_sync(service->state, service,
3553 VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA,
3554 service->localport,
3555 service->remoteport),
3556 copy_callback, context, size, 1);
3557 break;
3558 default:
3559 status = VCHIQ_ERROR;
3560 break;
3561 }
3562
3563 error_exit:
3564 if (service)
3565 unlock_service(service);
3566
3567 return status;
3568 }
3569
3570 void
vchiq_release_message(VCHIQ_SERVICE_HANDLE_T handle,VCHIQ_HEADER_T * header)3571 vchiq_release_message(VCHIQ_SERVICE_HANDLE_T handle, VCHIQ_HEADER_T *header)
3572 {
3573 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
3574 VCHIQ_SHARED_STATE_T *remote;
3575 VCHIQ_STATE_T *state;
3576 int slot_index;
3577
3578 if (!service)
3579 return;
3580
3581 state = service->state;
3582 remote = state->remote;
3583
3584 slot_index = SLOT_INDEX_FROM_DATA(state, (void *)header);
3585
3586 if ((slot_index >= remote->slot_first) &&
3587 (slot_index <= remote->slot_last)) {
3588 int msgid = header->msgid;
3589 if (msgid & VCHIQ_MSGID_CLAIMED) {
3590 VCHIQ_SLOT_INFO_T *slot_info =
3591 SLOT_INFO_FROM_INDEX(state, slot_index);
3592
3593 release_slot(state, slot_info, header, service);
3594 }
3595 } else if (slot_index == remote->slot_sync)
3596 release_message_sync(state, header);
3597
3598 unlock_service(service);
3599 }
3600
3601 static void
release_message_sync(VCHIQ_STATE_T * state,VCHIQ_HEADER_T * header)3602 release_message_sync(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header)
3603 {
3604 header->msgid = VCHIQ_MSGID_PADDING;
3605 remote_event_signal(&state->remote->sync_release);
3606 }
3607
3608 VCHIQ_STATUS_T
vchiq_get_peer_version(VCHIQ_SERVICE_HANDLE_T handle,short * peer_version)3609 vchiq_get_peer_version(VCHIQ_SERVICE_HANDLE_T handle, short *peer_version)
3610 {
3611 VCHIQ_STATUS_T status = VCHIQ_ERROR;
3612 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
3613
3614 if (!service ||
3615 (vchiq_check_service(service) != VCHIQ_SUCCESS) ||
3616 !peer_version)
3617 goto exit;
3618 *peer_version = service->peer_version;
3619 status = VCHIQ_SUCCESS;
3620
3621 exit:
3622 if (service)
3623 unlock_service(service);
3624 return status;
3625 }
3626
3627 VCHIQ_STATUS_T
vchiq_get_config(VCHIQ_INSTANCE_T instance,int config_size,VCHIQ_CONFIG_T * pconfig)3628 vchiq_get_config(VCHIQ_INSTANCE_T instance,
3629 int config_size, VCHIQ_CONFIG_T *pconfig)
3630 {
3631 VCHIQ_CONFIG_T config;
3632
3633 (void)instance;
3634
3635 config.max_msg_size = VCHIQ_MAX_MSG_SIZE;
3636 config.bulk_threshold = VCHIQ_MAX_MSG_SIZE;
3637 config.max_outstanding_bulks = VCHIQ_NUM_SERVICE_BULKS;
3638 config.max_services = VCHIQ_MAX_SERVICES;
3639 config.version = VCHIQ_VERSION;
3640 config.version_min = VCHIQ_VERSION_MIN;
3641
3642 if (config_size > sizeof(VCHIQ_CONFIG_T))
3643 return VCHIQ_ERROR;
3644
3645 memcpy(pconfig, &config,
3646 min(config_size, (int)(sizeof(VCHIQ_CONFIG_T))));
3647
3648 return VCHIQ_SUCCESS;
3649 }
3650
3651 VCHIQ_STATUS_T
vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T handle,VCHIQ_SERVICE_OPTION_T option,int value)3652 vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T handle,
3653 VCHIQ_SERVICE_OPTION_T option, int value)
3654 {
3655 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
3656 VCHIQ_STATUS_T status = VCHIQ_ERROR;
3657
3658 if (service) {
3659 switch (option) {
3660 case VCHIQ_SERVICE_OPTION_AUTOCLOSE:
3661 service->auto_close = value;
3662 status = VCHIQ_SUCCESS;
3663 break;
3664
3665 case VCHIQ_SERVICE_OPTION_SLOT_QUOTA: {
3666 VCHIQ_SERVICE_QUOTA_T *service_quota =
3667 &service->state->service_quotas[
3668 service->localport];
3669 if (value == 0)
3670 value = service->state->default_slot_quota;
3671 if ((value >= service_quota->slot_use_count) &&
3672 (value < (unsigned short)~0)) {
3673 service_quota->slot_quota = value;
3674 if ((value >= service_quota->slot_use_count) &&
3675 (service_quota->message_quota >=
3676 service_quota->message_use_count)) {
3677 /* Signal the service that it may have
3678 ** dropped below its quota */
3679 up(&service_quota->quota_event);
3680 }
3681 status = VCHIQ_SUCCESS;
3682 }
3683 } break;
3684
3685 case VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA: {
3686 VCHIQ_SERVICE_QUOTA_T *service_quota =
3687 &service->state->service_quotas[
3688 service->localport];
3689 if (value == 0)
3690 value = service->state->default_message_quota;
3691 if ((value >= service_quota->message_use_count) &&
3692 (value < (unsigned short)~0)) {
3693 service_quota->message_quota = value;
3694 if ((value >=
3695 service_quota->message_use_count) &&
3696 (service_quota->slot_quota >=
3697 service_quota->slot_use_count))
3698 /* Signal the service that it may have
3699 ** dropped below its quota */
3700 up(&service_quota->quota_event);
3701 status = VCHIQ_SUCCESS;
3702 }
3703 } break;
3704
3705 case VCHIQ_SERVICE_OPTION_SYNCHRONOUS:
3706 if ((service->srvstate == VCHIQ_SRVSTATE_HIDDEN) ||
3707 (service->srvstate ==
3708 VCHIQ_SRVSTATE_LISTENING)) {
3709 service->sync = value;
3710 status = VCHIQ_SUCCESS;
3711 }
3712 break;
3713
3714 case VCHIQ_SERVICE_OPTION_TRACE:
3715 service->trace = value;
3716 status = VCHIQ_SUCCESS;
3717 break;
3718
3719 default:
3720 break;
3721 }
3722 unlock_service(service);
3723 }
3724
3725 return status;
3726 }
3727
3728 static void
vchiq_dump_shared_state(void * dump_context,VCHIQ_STATE_T * state,VCHIQ_SHARED_STATE_T * shared,const char * label)3729 vchiq_dump_shared_state(void *dump_context, VCHIQ_STATE_T *state,
3730 VCHIQ_SHARED_STATE_T *shared, const char *label)
3731 {
3732 static const char *const debug_names[] = {
3733 "<entries>",
3734 "SLOT_HANDLER_COUNT",
3735 "SLOT_HANDLER_LINE",
3736 "PARSE_LINE",
3737 "PARSE_HEADER",
3738 "PARSE_MSGID",
3739 "AWAIT_COMPLETION_LINE",
3740 "DEQUEUE_MESSAGE_LINE",
3741 "SERVICE_CALLBACK_LINE",
3742 "MSG_QUEUE_FULL_COUNT",
3743 "COMPLETION_QUEUE_FULL_COUNT"
3744 };
3745 int i;
3746
3747 char buf[80];
3748 int len;
3749 len = snprintf(buf, sizeof(buf),
3750 " %s: slots %d-%d tx_pos=%x recycle=%x",
3751 label, shared->slot_first, shared->slot_last,
3752 shared->tx_pos, shared->slot_queue_recycle);
3753 vchiq_dump(dump_context, buf, len + 1);
3754
3755 len = snprintf(buf, sizeof(buf),
3756 " Slots claimed:");
3757 vchiq_dump(dump_context, buf, len + 1);
3758
3759 for (i = shared->slot_first; i <= shared->slot_last; i++) {
3760 VCHIQ_SLOT_INFO_T slot_info = *SLOT_INFO_FROM_INDEX(state, i);
3761 if (slot_info.use_count != slot_info.release_count) {
3762 len = snprintf(buf, sizeof(buf),
3763 " %d: %d/%d", i, slot_info.use_count,
3764 slot_info.release_count);
3765 vchiq_dump(dump_context, buf, len + 1);
3766 }
3767 }
3768
3769 for (i = 1; i < shared->debug[DEBUG_ENTRIES]; i++) {
3770 len = snprintf(buf, sizeof(buf), " DEBUG: %s = %d(%x)",
3771 debug_names[i], shared->debug[i], shared->debug[i]);
3772 vchiq_dump(dump_context, buf, len + 1);
3773 }
3774 }
3775
3776 void
vchiq_dump_state(void * dump_context,VCHIQ_STATE_T * state)3777 vchiq_dump_state(void *dump_context, VCHIQ_STATE_T *state)
3778 {
3779 char buf[80];
3780 int len;
3781 int i;
3782
3783 len = snprintf(buf, sizeof(buf), "State %d: %s", state->id,
3784 conn_state_names[state->conn_state]);
3785 vchiq_dump(dump_context, buf, len + 1);
3786
3787 len = snprintf(buf, sizeof(buf),
3788 " tx_pos=%x(@%p), rx_pos=%x(@%p)",
3789 state->local->tx_pos,
3790 state->tx_data +
3791 (state->local_tx_pos & VCHIQ_SLOT_MASK),
3792 state->rx_pos,
3793 state->rx_data +
3794 (state->rx_pos & VCHIQ_SLOT_MASK));
3795 vchiq_dump(dump_context, buf, len + 1);
3796
3797 len = snprintf(buf, sizeof(buf),
3798 " Version: %d (min %d)",
3799 VCHIQ_VERSION, VCHIQ_VERSION_MIN);
3800 vchiq_dump(dump_context, buf, len + 1);
3801
3802 if (VCHIQ_ENABLE_STATS) {
3803 len = snprintf(buf, sizeof(buf),
3804 " Stats: ctrl_tx_count=%d, ctrl_rx_count=%d, "
3805 "error_count=%d",
3806 state->stats.ctrl_tx_count, state->stats.ctrl_rx_count,
3807 state->stats.error_count);
3808 vchiq_dump(dump_context, buf, len + 1);
3809 }
3810
3811 len = snprintf(buf, sizeof(buf),
3812 " Slots: %d available (%d data), %d recyclable, %d stalls "
3813 "(%d data)",
3814 ((state->slot_queue_available * VCHIQ_SLOT_SIZE) -
3815 state->local_tx_pos) / VCHIQ_SLOT_SIZE,
3816 state->data_quota - state->data_use_count,
3817 state->local->slot_queue_recycle - state->slot_queue_available,
3818 state->stats.slot_stalls, state->stats.data_stalls);
3819 vchiq_dump(dump_context, buf, len + 1);
3820
3821 vchiq_dump_platform_state(dump_context);
3822
3823 vchiq_dump_shared_state(dump_context, state, state->local, "Local");
3824 vchiq_dump_shared_state(dump_context, state, state->remote, "Remote");
3825
3826 vchiq_dump_platform_instances(dump_context);
3827
3828 for (i = 0; i < state->unused_service; i++) {
3829 VCHIQ_SERVICE_T *service = find_service_by_port(state, i);
3830
3831 if (service) {
3832 vchiq_dump_service_state(dump_context, service);
3833 unlock_service(service);
3834 }
3835 }
3836 }
3837
3838 void
vchiq_dump_service_state(void * dump_context,VCHIQ_SERVICE_T * service)3839 vchiq_dump_service_state(void *dump_context, VCHIQ_SERVICE_T *service)
3840 {
3841 char buf[120];
3842 int len;
3843
3844 len = snprintf(buf, sizeof(buf), "Service %d: %s (ref %u)",
3845 service->localport, srvstate_names[service->srvstate],
3846 service->ref_count - 1); /*Don't include the lock just taken*/
3847
3848 if (service->srvstate != VCHIQ_SRVSTATE_FREE) {
3849 char remoteport[30];
3850 VCHIQ_SERVICE_QUOTA_T *service_quota =
3851 &service->state->service_quotas[service->localport];
3852 int fourcc = service->base.fourcc;
3853 int tx_pending, rx_pending;
3854 if (service->remoteport != VCHIQ_PORT_FREE) {
3855 int len2 = snprintf(remoteport, sizeof(remoteport),
3856 "%d", service->remoteport);
3857 if (service->public_fourcc != VCHIQ_FOURCC_INVALID)
3858 snprintf(remoteport + len2,
3859 sizeof(remoteport) - len2,
3860 " (client %8x)", service->client_id);
3861 } else
3862 strcpy(remoteport, "n/a");
3863
3864 len += snprintf(buf + len, sizeof(buf) - len,
3865 " '%c%c%c%c' remote %s (msg use %d/%d, slot use %d/%d)",
3866 VCHIQ_FOURCC_AS_4CHARS(fourcc),
3867 remoteport,
3868 service_quota->message_use_count,
3869 service_quota->message_quota,
3870 service_quota->slot_use_count,
3871 service_quota->slot_quota);
3872
3873 vchiq_dump(dump_context, buf, len + 1);
3874
3875 tx_pending = service->bulk_tx.local_insert -
3876 service->bulk_tx.remote_insert;
3877
3878 rx_pending = service->bulk_rx.local_insert -
3879 service->bulk_rx.remote_insert;
3880
3881 len = snprintf(buf, sizeof(buf),
3882 " Bulk: tx_pending=%d (size %d),"
3883 " rx_pending=%d (size %d)",
3884 tx_pending,
3885 tx_pending ? service->bulk_tx.bulks[
3886 BULK_INDEX(service->bulk_tx.remove)].size : 0,
3887 rx_pending,
3888 rx_pending ? service->bulk_rx.bulks[
3889 BULK_INDEX(service->bulk_rx.remove)].size : 0);
3890
3891 if (VCHIQ_ENABLE_STATS) {
3892 vchiq_dump(dump_context, buf, len + 1);
3893
3894 len = snprintf(buf, sizeof(buf),
3895 " Ctrl: tx_count=%d, tx_bytes=%ju, "
3896 "rx_count=%d, rx_bytes=%ju",
3897 service->stats.ctrl_tx_count,
3898 (uintmax_t) service->stats.ctrl_tx_bytes,
3899 service->stats.ctrl_rx_count,
3900 (uintmax_t) service->stats.ctrl_rx_bytes);
3901 vchiq_dump(dump_context, buf, len + 1);
3902
3903 len = snprintf(buf, sizeof(buf),
3904 " Bulk: tx_count=%d, tx_bytes=%ju, "
3905 "rx_count=%d, rx_bytes=%ju",
3906 service->stats.bulk_tx_count,
3907 (uintmax_t) service->stats.bulk_tx_bytes,
3908 service->stats.bulk_rx_count,
3909 (uintmax_t) service->stats.bulk_rx_bytes);
3910 vchiq_dump(dump_context, buf, len + 1);
3911
3912 len = snprintf(buf, sizeof(buf),
3913 " %d quota stalls, %d slot stalls, "
3914 "%d bulk stalls, %d aborted, %d errors",
3915 service->stats.quota_stalls,
3916 service->stats.slot_stalls,
3917 service->stats.bulk_stalls,
3918 service->stats.bulk_aborted_count,
3919 service->stats.error_count);
3920 }
3921 }
3922
3923 vchiq_dump(dump_context, buf, len + 1);
3924
3925 if (service->srvstate != VCHIQ_SRVSTATE_FREE)
3926 vchiq_dump_platform_service_state(dump_context, service);
3927 }
3928
3929
3930 void
vchiq_loud_error_header(void)3931 vchiq_loud_error_header(void)
3932 {
3933 vchiq_log_error(vchiq_core_log_level,
3934 "============================================================"
3935 "================");
3936 vchiq_log_error(vchiq_core_log_level,
3937 "============================================================"
3938 "================");
3939 vchiq_log_error(vchiq_core_log_level, "=====");
3940 }
3941
3942 void
vchiq_loud_error_footer(void)3943 vchiq_loud_error_footer(void)
3944 {
3945 vchiq_log_error(vchiq_core_log_level, "=====");
3946 vchiq_log_error(vchiq_core_log_level,
3947 "============================================================"
3948 "================");
3949 vchiq_log_error(vchiq_core_log_level,
3950 "============================================================"
3951 "================");
3952 }
3953
3954
vchiq_send_remote_use(VCHIQ_STATE_T * state)3955 VCHIQ_STATUS_T vchiq_send_remote_use(VCHIQ_STATE_T *state)
3956 {
3957 VCHIQ_STATUS_T status = VCHIQ_RETRY;
3958 if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED)
3959 status = queue_message(state, NULL,
3960 VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_USE, 0, 0),
3961 NULL, 0, 0, 0);
3962 return status;
3963 }
3964
vchiq_send_remote_release(VCHIQ_STATE_T * state)3965 VCHIQ_STATUS_T vchiq_send_remote_release(VCHIQ_STATE_T *state)
3966 {
3967 VCHIQ_STATUS_T status = VCHIQ_RETRY;
3968 if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED)
3969 status = queue_message(state, NULL,
3970 VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_RELEASE, 0, 0),
3971 NULL, 0, 0, 0);
3972 return status;
3973 }
3974
vchiq_send_remote_use_active(VCHIQ_STATE_T * state)3975 VCHIQ_STATUS_T vchiq_send_remote_use_active(VCHIQ_STATE_T *state)
3976 {
3977 VCHIQ_STATUS_T status = VCHIQ_RETRY;
3978 if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED)
3979 status = queue_message(state, NULL,
3980 VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_USE_ACTIVE, 0, 0),
3981 NULL, 0, 0, 0);
3982 return status;
3983 }
3984
vchiq_log_dump_mem(const char * label,uint32_t addr,const void * voidMem,size_t numBytes)3985 void vchiq_log_dump_mem(const char *label, uint32_t addr, const void *voidMem,
3986 size_t numBytes)
3987 {
3988 const uint8_t *mem = (const uint8_t *)voidMem;
3989 size_t offset;
3990 char lineBuf[100];
3991 char *s;
3992
3993 while (numBytes > 0) {
3994 s = lineBuf;
3995
3996 for (offset = 0; offset < 16; offset++) {
3997 if (offset < numBytes)
3998 s += snprintf(s, 4, "%02x ", mem[offset]);
3999 else
4000 s += snprintf(s, 4, " ");
4001 }
4002
4003 for (offset = 0; offset < 16; offset++) {
4004 if (offset < numBytes) {
4005 uint8_t ch = mem[offset];
4006
4007 if ((ch < ' ') || (ch > '~'))
4008 ch = '.';
4009 *s++ = (char)ch;
4010 }
4011 }
4012 *s++ = '\0';
4013
4014 if ((label != NULL) && (*label != '\0'))
4015 vchiq_log_trace(VCHIQ_LOG_TRACE,
4016 "%s: %08x: %s", label, addr, lineBuf);
4017 else
4018 vchiq_log_trace(VCHIQ_LOG_TRACE,
4019 "%08x: %s", addr, lineBuf);
4020
4021 addr += 16;
4022 mem += 16;
4023 if (numBytes > 16)
4024 numBytes -= 16;
4025 else
4026 numBytes = 0;
4027 }
4028 }
4029