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