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