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