diff options
Diffstat (limited to 'sys/contrib')
21 files changed, 881 insertions, 313 deletions
diff --git a/sys/contrib/vchiq/interface/vchi/vchi.h b/sys/contrib/vchiq/interface/vchi/vchi.h index f1b9d1c..c80b255 100644 --- a/sys/contrib/vchiq/interface/vchi/vchi.h +++ b/sys/contrib/vchiq/interface/vchi/vchi.h @@ -220,7 +220,12 @@ extern int32_t vchi_service_use( const VCHI_SERVICE_HANDLE_T handle ); // Routine to decrement ref count on a named service extern int32_t vchi_service_release( const VCHI_SERVICE_HANDLE_T handle ); -// Routine to send a message accross a service +// Routine to set a control option for a named service +extern int32_t vchi_service_set_option( const VCHI_SERVICE_HANDLE_T handle, + VCHI_SERVICE_OPTION_T option, + int value); + +// Routine to send a message across a service extern int32_t vchi_msg_queue( VCHI_SERVICE_HANDLE_T handle, const void *data, uint32_t data_size, diff --git a/sys/contrib/vchiq/interface/vchi/vchi_common.h b/sys/contrib/vchiq/interface/vchi/vchi_common.h index 9e6c00e..d535a72 100644 --- a/sys/contrib/vchiq/interface/vchi/vchi_common.h +++ b/sys/contrib/vchiq/interface/vchi/vchi_common.h @@ -110,7 +110,19 @@ typedef enum VCHI_CALLBACK_REASON_MAX } VCHI_CALLBACK_REASON_T; -//Calback used by all services / bulk transfers +// service control options +typedef enum +{ + VCHI_SERVICE_OPTION_MIN, + + VCHI_SERVICE_OPTION_TRACE, + VCHI_SERVICE_OPTION_SYNCHRONOUS, + + VCHI_SERVICE_OPTION_MAX +} VCHI_SERVICE_OPTION_T; + + +//Callback used by all services / bulk transfers typedef void (*VCHI_CALLBACK_T)( void *callback_param, //my service local param VCHI_CALLBACK_REASON_T reason, void *handle ); //for transmitting msg's only diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq.h b/sys/contrib/vchiq/interface/vchiq_arm/vchiq.h index f87dcbd..ad398ba 100644 --- a/sys/contrib/vchiq/interface/vchiq_arm/vchiq.h +++ b/sys/contrib/vchiq/interface/vchiq_arm/vchiq.h @@ -38,4 +38,3 @@ #include "vchiq_util.h" #endif - diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_2835_arm.c b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_2835_arm.c index 2f8ed43..c826999 100644 --- a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_2835_arm.c +++ b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_2835_arm.c @@ -61,6 +61,7 @@ MALLOC_DEFINE(M_VCPAGELIST, "vcpagelist", "VideoCore pagelist memory"); #include "vchiq_arm.h" #include "vchiq_2835.h" #include "vchiq_connected.h" +#include "vchiq_killable.h" #define MAX_FRAGMENTS (VCHIQ_NUM_CURRENT_BULKS * 2) diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_arm.c b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_arm.c index d534a7f..556ddf6 100644 --- a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_arm.c +++ b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_arm.c @@ -1,4 +1,5 @@ /** + * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. * Copyright (c) 2010-2012 Broadcom. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -102,13 +103,15 @@ typedef struct user_service_struct { VCHIQ_SERVICE_T *service; void *userdata; VCHIQ_INSTANCE_T instance; - int is_vchi; - int dequeue_pending; + char is_vchi; + char dequeue_pending; + char close_pending; int message_available_pos; int msg_insert; int msg_remove; struct semaphore insert_event; struct semaphore remove_event; + struct semaphore close_event; VCHIQ_HEADER_T * msg_queue[MSG_QUEUE_SIZE]; } USER_SERVICE_T; @@ -131,11 +134,15 @@ struct vchiq_instance_struct { int closing; int pid; int mark; + int use_close_delivered; + int trace; struct list_head bulk_waiter_list; struct mutex bulk_waiter_list_mutex; - struct proc_dir_entry *proc_entry; +#ifdef notyet + VCHIQ_DEBUGFS_NODE_T proc_entry; +#endif }; typedef struct dump_context_struct { @@ -165,7 +172,9 @@ static const char *const ioctl_names[] = { "USE_SERVICE", "RELEASE_SERVICE", "SET_SERVICE_OPTION", - "DUMP_PHYS_MEM" + "DUMP_PHYS_MEM", + "LIB_VERSION", + "CLOSE_DELIVERED" }; vchiq_static_assert((sizeof(ioctl_names)/sizeof(ioctl_names[0])) == @@ -232,10 +241,13 @@ add_completion(VCHIQ_INSTANCE_T instance, VCHIQ_REASON_T reason, completion->service_userdata = user_service->service; completion->bulk_userdata = bulk_userdata; - if (reason == VCHIQ_SERVICE_CLOSED) + if (reason == VCHIQ_SERVICE_CLOSED) { /* Take an extra reference, to be held until this CLOSED notification is delivered. */ lock_service(user_service->service); + if (instance->use_close_delivered) + user_service->close_pending = 1; + } /* A write barrier is needed here to ensure that the entire completion record is written out before the insert point. */ @@ -282,10 +294,10 @@ service_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, return VCHIQ_SUCCESS; vchiq_log_trace(vchiq_arm_log_level, - "service_callback - service %lx(%d), handle %x, reason %d, header %lx, " + "service_callback - service %lx(%d,%p), reason %d, header %lx, " "instance %lx, bulk_userdata %lx", (unsigned long)user_service, - service->localport, service->handle, + service->localport, user_service->userdata, reason, (unsigned long)header, (unsigned long)instance, (unsigned long)bulk_userdata); @@ -377,6 +389,28 @@ user_service_free(void *userdata) /**************************************************************************** * +* close_delivered +* +***************************************************************************/ +static void close_delivered(USER_SERVICE_T *user_service) +{ + vchiq_log_info(vchiq_arm_log_level, + "close_delivered(handle=%x)", + user_service->service->handle); + + if (user_service->close_pending) { + /* Allow the underlying service to be culled */ + unlock_service(user_service->service); + + /* Wake the user-thread blocked in close_ or remove_service */ + up(&user_service->close_event); + + user_service->close_pending = 0; + } +} + +/**************************************************************************** +* * vchiq_ioctl * ***************************************************************************/ @@ -496,14 +530,16 @@ vchiq_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, user_service->service = service; user_service->userdata = userdata; user_service->instance = instance; - user_service->is_vchi = args.is_vchi; + user_service->is_vchi = (args.is_vchi != 0); user_service->dequeue_pending = 0; + user_service->close_pending = 0; user_service->message_available_pos = instance->completion_remove - 1; user_service->msg_insert = 0; user_service->msg_remove = 0; _sema_init(&user_service->insert_event, 0); _sema_init(&user_service->remove_event, 0); + _sema_init(&user_service->close_event, 0); if (args.is_open) { status = vchiq_open_service_internal @@ -543,8 +579,24 @@ vchiq_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, #endif service = find_service_for_instance(instance, handle); - if (service != NULL) - status = vchiq_close_service(service->handle); + if (service != NULL) { + USER_SERVICE_T *user_service = + (USER_SERVICE_T *)service->base.userdata; + /* close_pending is false on first entry, and when the + wait in vchiq_close_service has been interrupted. */ + if (!user_service->close_pending) { + status = vchiq_close_service(service->handle); + if (status != VCHIQ_SUCCESS) + break; + } + + /* close_pending is true once the underlying service + has been closed until the client library calls the + CLOSE_DELIVERED ioctl, signalling close_event. */ + if (user_service->close_pending && + down_interruptible(&user_service->close_event)) + status = VCHIQ_RETRY; + } else ret = -EINVAL; } break; @@ -559,8 +611,24 @@ vchiq_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, #endif service = find_service_for_instance(instance, handle); - if (service != NULL) - status = vchiq_remove_service(service->handle); + if (service != NULL) { + USER_SERVICE_T *user_service = + (USER_SERVICE_T *)service->base.userdata; + /* close_pending is false on first entry, and when the + wait in vchiq_close_service has been interrupted. */ + if (!user_service->close_pending) { + status = vchiq_remove_service(service->handle); + if (status != VCHIQ_SUCCESS) + break; + } + + /* close_pending is true once the underlying service + has been closed until the client library calls the + CLOSE_DELIVERED ioctl, signalling close_event. */ + if (user_service->close_pending && + down_interruptible(&user_service->close_event)) + status = VCHIQ_RETRY; + } else ret = -EINVAL; } break; @@ -824,8 +892,9 @@ vchiq_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, completion->header = msgbuf; } - if (completion->reason == - VCHIQ_SERVICE_CLOSED) + if ((completion->reason == + VCHIQ_SERVICE_CLOSED) && + !instance->use_close_delivered) unlock_service(service1); if (copy_to_user((void __user *)( @@ -1007,6 +1076,29 @@ vchiq_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, #endif } break; + case VCHIQ_IOC_LIB_VERSION: { + unsigned int lib_version = (unsigned int)arg; + + if (lib_version < VCHIQ_VERSION_MIN) + ret = -EINVAL; + else if (lib_version >= VCHIQ_VERSION_CLOSE_DELIVERED) + instance->use_close_delivered = 1; + } break; + + case VCHIQ_IOC_CLOSE_DELIVERED: { + VCHIQ_SERVICE_HANDLE_T handle; + memcpy(&handle, (const void*)arg, sizeof(handle)); + + service = find_closed_service_for_instance(instance, handle); + if (service != NULL) { + USER_SERVICE_T *user_service = + (USER_SERVICE_T *)service->base.userdata; + close_delivered(user_service); + } + else + ret = -EINVAL; + } break; + default: ret = -ENOTTY; break; @@ -1209,7 +1301,15 @@ vchiq_close(struct cdev *dev, int flags __unused, int fmt __unused, (MAX_COMPLETIONS - 1)]; service1 = completion->service_userdata; if (completion->reason == VCHIQ_SERVICE_CLOSED) + { + USER_SERVICE_T *user_service = + service->base.userdata; + + /* Wake any blocked user-thread */ + if (instance->use_close_delivered) + up(&user_service->close_event); unlock_service(service1); + } instance->completion_remove++; } @@ -1704,7 +1804,7 @@ vchiq_arm_init_state(VCHIQ_STATE_T *state, VCHIQ_ARM_STATE_T *arm_state) ** VC_RESUME_FAILED - Currently unused - no mechanism to fail resume exists. */ -inline void +void set_suspend_state(VCHIQ_ARM_STATE_T *arm_state, enum vc_suspend_status new_state) { @@ -1725,6 +1825,7 @@ set_suspend_state(VCHIQ_ARM_STATE_T *arm_state, complete_all(&arm_state->vc_resume_complete); break; case VC_SUSPEND_IDLE: + /* TODO: reinit_completion */ INIT_COMPLETION(arm_state->vc_suspend_complete); break; case VC_SUSPEND_REQUESTED: @@ -1741,7 +1842,7 @@ set_suspend_state(VCHIQ_ARM_STATE_T *arm_state, } } -inline void +void set_resume_state(VCHIQ_ARM_STATE_T *arm_state, enum vc_resume_status new_state) { @@ -1753,6 +1854,7 @@ set_resume_state(VCHIQ_ARM_STATE_T *arm_state, case VC_RESUME_FAILED: break; case VC_RESUME_IDLE: + /* TODO: reinit_completion */ INIT_COMPLETION(arm_state->vc_resume_complete); break; case VC_RESUME_REQUESTED: @@ -1815,6 +1917,7 @@ block_resume(VCHIQ_ARM_STATE_T *arm_state) * (which only happens when blocked_count hits 0) then those threads * will have to wait until next time around */ if (arm_state->blocked_count) { + /* TODO: reinit_completion */ INIT_COMPLETION(arm_state->blocked_blocker); write_unlock_bh(&arm_state->susp_res_lock); vchiq_log_info(vchiq_susp_log_level, "%s wait for previously " @@ -1860,6 +1963,7 @@ block_resume(VCHIQ_ARM_STATE_T *arm_state) write_lock_bh(&arm_state->susp_res_lock); resume_count++; } + /* TODO: reinit_completion */ INIT_COMPLETION(arm_state->resume_blocker); arm_state->resume_blocked = 1; diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_arm.h b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_arm.h index e514a7f..c7e9ae7 100644 --- a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_arm.h +++ b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_arm.h @@ -1,4 +1,5 @@ /** + * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. * Copyright (c) 2010-2012 Broadcom. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -153,6 +154,9 @@ vchiq_check_resume(VCHIQ_STATE_T *state); extern void vchiq_check_suspend(VCHIQ_STATE_T *state); +VCHIQ_STATUS_T +vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle); + extern VCHIQ_STATUS_T vchiq_platform_suspend(VCHIQ_STATE_T *state); @@ -180,21 +184,32 @@ vchiq_use_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, extern VCHIQ_STATUS_T vchiq_release_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service); -void +#ifdef notyet +extern VCHIQ_DEBUGFS_NODE_T * +vchiq_instance_get_debugfs_node(VCHIQ_INSTANCE_T instance); +#endif + +extern int +vchiq_instance_get_use_count(VCHIQ_INSTANCE_T instance); + +extern int +vchiq_instance_get_pid(VCHIQ_INSTANCE_T instance); + +extern int +vchiq_instance_get_trace(VCHIQ_INSTANCE_T instance); + +extern void +vchiq_instance_set_trace(VCHIQ_INSTANCE_T instance, int trace); + +extern void set_suspend_state(VCHIQ_ARM_STATE_T *arm_state, enum vc_suspend_status new_state); -void +extern void set_resume_state(VCHIQ_ARM_STATE_T *arm_state, enum vc_resume_status new_state); -void +extern void start_suspend_timer(VCHIQ_ARM_STATE_T *arm_state); -extern int vchiq_proc_init(void); -extern void vchiq_proc_deinit(void); -extern struct proc_dir_entry *vchiq_proc_top(void); -extern struct proc_dir_entry *vchiq_clients_top(void); - - #endif /* VCHIQ_ARM_H */ diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_cfg.h b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_cfg.h index 493c86c..d2797db 100644 --- a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_cfg.h +++ b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_cfg.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2012 Broadcom. All rights reserved. + * Copyright (c) 2010-2014 Broadcom. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,11 +36,20 @@ #define VCHIQ_MAGIC VCHIQ_MAKE_FOURCC('V', 'C', 'H', 'I') /* The version of VCHIQ - change with any non-trivial change */ -#define VCHIQ_VERSION 6 +#define VCHIQ_VERSION 8 /* The minimum compatible version - update to match VCHIQ_VERSION with any ** incompatible change */ #define VCHIQ_VERSION_MIN 3 +/* The version that introduced the VCHIQ_IOC_LIB_VERSION ioctl */ +#define VCHIQ_VERSION_LIB_VERSION 7 + +/* The version that introduced the VCHIQ_IOC_CLOSE_DELIVERED ioctl */ +#define VCHIQ_VERSION_CLOSE_DELIVERED 7 + +/* The version that made it safe to use SYNCHRONOUS mode */ +#define VCHIQ_VERSION_SYNCHRONOUS_MODE 8 + #define VCHIQ_MAX_STATES 1 #define VCHIQ_MAX_SERVICES 4096 #define VCHIQ_MAX_SLOTS 128 diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_connected.c b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_connected.c index 0bc6c58..928e454 100644 --- a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_connected.c +++ b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_connected.c @@ -33,6 +33,7 @@ #include "vchiq_connected.h" #include "vchiq_core.h" +#include "vchiq_killable.h" #define MAX_CALLBACKS 10 diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_connected.h b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_connected.h index e4cfdcc..863b3e3 100644 --- a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_connected.h +++ b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_connected.h @@ -48,4 +48,3 @@ void vchiq_add_connected_callback(VCHIQ_CONNECTED_CALLBACK_T callback); void vchiq_call_connected_callbacks(void); #endif /* VCHIQ_CONNECTED_H */ - diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_core.c b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_core.c index 633fd86..2ded208 100644 --- a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_core.c +++ b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_core.c @@ -32,6 +32,7 @@ */ #include "vchiq_core.h" +#include "vchiq_killable.h" #define VCHIQ_SLOT_HANDLER_STACK 8192 @@ -47,9 +48,12 @@ #define SLOT_QUEUE_INDEX_FROM_POS(pos) \ ((int)((unsigned int)(pos) / VCHIQ_SLOT_SIZE)) - #define BULK_INDEX(x) (x & (VCHIQ_NUM_SERVICE_BULKS - 1)) +#define SRVTRACE_LEVEL(srv) \ + (((srv) && (srv)->trace) ? VCHIQ_LOG_TRACE : vchiq_core_msg_log_level) +#define SRVTRACE_ENABLED(srv, lev) \ + (((srv) && (srv)->trace) || (vchiq_core_msg_log_level >= (lev))) struct vchiq_open_payload { int fourcc; @@ -62,6 +66,13 @@ struct vchiq_openack_payload { short version; }; +enum +{ + QMFLAGS_IS_BLOCKING = (1 << 0), + QMFLAGS_NO_MUTEX_LOCK = (1 << 1), + QMFLAGS_NO_MUTEX_UNLOCK = (1 << 2) +}; + /* we require this for consistency between endpoints */ vchiq_static_assert(sizeof(VCHIQ_HEADER_T) == 8); vchiq_static_assert(IS_POW2(sizeof(VCHIQ_HEADER_T))); @@ -231,6 +242,31 @@ find_service_for_instance(VCHIQ_INSTANCE_T instance, } VCHIQ_SERVICE_T * +find_closed_service_for_instance(VCHIQ_INSTANCE_T instance, + VCHIQ_SERVICE_HANDLE_T handle) { + VCHIQ_SERVICE_T *service; + + spin_lock(&service_spinlock); + service = handle_to_service(handle); + if (service && + ((service->srvstate == VCHIQ_SRVSTATE_FREE) || + (service->srvstate == VCHIQ_SRVSTATE_CLOSED)) && + (service->handle == handle) && + (service->instance == instance)) { + BUG_ON(service->ref_count == 0); + service->ref_count++; + } else + service = NULL; + spin_unlock(&service_spinlock); + + if (!service) + vchiq_log_info(vchiq_core_log_level, + "Invalid service handle 0x%x", handle); + + return service; +} + +VCHIQ_SERVICE_T * next_service_by_instance(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance, int *pidx) { @@ -726,7 +762,7 @@ process_free_queue(VCHIQ_STATE_T *state) static VCHIQ_STATUS_T queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, int msgid, const VCHIQ_ELEMENT_T *elements, - int count, int size, int is_blocking) + int count, int size, int flags) { VCHIQ_SHARED_STATE_T *local; VCHIQ_SERVICE_QUOTA_T *service_quota = NULL; @@ -741,7 +777,7 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, WARN_ON(!(stride <= VCHIQ_SLOT_SIZE)); - if ((type != VCHIQ_MSG_RESUME) && + if (!(flags & QMFLAGS_NO_MUTEX_LOCK) && (lmutex_lock_interruptible(&state->slot_mutex) != 0)) return VCHIQ_RETRY; @@ -749,6 +785,8 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, int tx_end_index; BUG_ON(!service); + BUG_ON((flags & (QMFLAGS_NO_MUTEX_LOCK | + QMFLAGS_NO_MUTEX_UNLOCK)) != 0); if (service->closing) { /* The service has been closed */ @@ -824,12 +862,16 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, spin_unlock("a_spinlock); } - header = reserve_space(state, stride, is_blocking); + header = reserve_space(state, stride, flags & QMFLAGS_IS_BLOCKING); if (!header) { if (service) VCHIQ_SERVICE_STATS_INC(service, slot_stalls); - lmutex_unlock(&state->slot_mutex); + /* In the event of a failure, return the mutex to the + state it was in */ + if (!(flags & QMFLAGS_NO_MUTEX_LOCK)) + lmutex_unlock(&state->slot_mutex); + return VCHIQ_RETRY; } @@ -847,6 +889,8 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, VCHIQ_MSG_DSTPORT(msgid)); BUG_ON(!service); + BUG_ON((flags & (QMFLAGS_NO_MUTEX_LOCK | + QMFLAGS_NO_MUTEX_UNLOCK)) != 0); for (i = 0, pos = 0; i < (unsigned int)count; pos += elements[i++].size) @@ -861,11 +905,11 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, return VCHIQ_ERROR; } if (i == 0) { - if (vchiq_core_msg_log_level >= - VCHIQ_LOG_INFO) + if (SRVTRACE_ENABLED(service, + VCHIQ_LOG_INFO)) vchiq_log_dump_mem("Sent", 0, header->data + pos, - min(64, + min(64u, elements[0].size)); } } @@ -928,7 +972,7 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, ? service->base.fourcc : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); - vchiq_log_info(vchiq_core_msg_log_level, + vchiq_log_info(SRVTRACE_LEVEL(service), "Sent Msg %s(%u) to %c%c%c%c s:%u d:%d len:%d", msg_type_str(VCHIQ_MSG_TYPE(msgid)), VCHIQ_MSG_TYPE(msgid), @@ -948,7 +992,7 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, if (service && (type == VCHIQ_MSG_CLOSE)) vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSESENT); - if (VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_PAUSE) + if (!(flags & QMFLAGS_NO_MUTEX_UNLOCK)) lmutex_unlock(&state->slot_mutex); remote_event_signal(&state->remote->trigger); @@ -1013,7 +1057,7 @@ queue_message_sync(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, VCHIQ_LOG_TRACE) vchiq_log_dump_mem("Sent Sync", 0, header->data + pos, - min(64, + min(64u, elements[0].size)); } } @@ -1320,11 +1364,11 @@ resolve_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue) vchiq_transfer_bulk(bulk); lmutex_unlock(&state->bulk_transfer_mutex); - if (vchiq_core_msg_log_level >= VCHIQ_LOG_INFO) { + if (SRVTRACE_ENABLED(service, VCHIQ_LOG_INFO)) { const char *header = (queue == &service->bulk_tx) ? "Send Bulk to" : "Recv Bulk from"; if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED) - vchiq_log_info(vchiq_core_msg_log_level, + vchiq_log_info(SRVTRACE_LEVEL(service), "%s %c%c%c%c d:%d len:%d %x<->%x", header, VCHIQ_FOURCC_AS_4CHARS( @@ -1334,7 +1378,7 @@ resolve_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue) (unsigned int)bulk->data, (unsigned int)bulk->remote_data); else - vchiq_log_info(vchiq_core_msg_log_level, + vchiq_log_info(SRVTRACE_LEVEL(service), "%s %c%c%c%c d:%d ABORTED - tx len:%d," " rx len:%d %x<->%x", header, @@ -1381,7 +1425,7 @@ abort_outstanding_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue) if (queue->process != queue->local_insert) { vchiq_complete_bulk(bulk); - vchiq_log_info(vchiq_core_msg_log_level, + vchiq_log_info(SRVTRACE_LEVEL(service), "%s %c%c%c%c d:%d ABORTED - tx len:%d, " "rx len:%d", is_tx ? "Send Bulk to" : "Recv Bulk from", @@ -1488,10 +1532,10 @@ parse_open(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header) if (service) { /* A matching service exists */ - short v = payload->version; + short version = payload->version; short version_min = payload->version_min; if ((service->version < version_min) || - (v < service->version_min)) { + (version < service->version_min)) { /* Version mismatch */ vchiq_loud_error_header(); vchiq_loud_error("%d: service %d (%c%c%c%c) " @@ -1500,12 +1544,13 @@ parse_open(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header) state->id, service->localport, VCHIQ_FOURCC_AS_4CHARS(fourcc), service->version, service->version_min, - v, version_min); + version, version_min); vchiq_loud_error_footer(); unlock_service(service); + service = NULL; goto fail_open; } - service->peer_version = v; + service->peer_version = version; if (service->srvstate == VCHIQ_SRVSTATE_LISTENING) { struct vchiq_openack_payload ack_payload = { @@ -1516,8 +1561,14 @@ parse_open(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header) sizeof(ack_payload) }; + if (state->version_common < + VCHIQ_VERSION_SYNCHRONOUS_MODE) + service->sync = 0; + /* Acknowledge the OPEN */ - if (service->sync) { + if (service->sync && + (state->version_common >= + VCHIQ_VERSION_SYNCHRONOUS_MODE)) { if (queue_message_sync(state, NULL, VCHIQ_MAKE_MSG( VCHIQ_MSG_OPENACK, @@ -1631,9 +1682,11 @@ parse_rx_slots(VCHIQ_STATE_T *state) case VCHIQ_MSG_BULK_RX_DONE: case VCHIQ_MSG_BULK_TX_DONE: service = find_service_by_port(state, localport); - if ((!service || service->remoteport != remoteport) && - (localport == 0) && - (type == VCHIQ_MSG_CLOSE)) { + if ((!service || + ((service->remoteport != remoteport) && + (service->remoteport != VCHIQ_PORT_FREE))) && + (localport == 0) && + (type == VCHIQ_MSG_CLOSE)) { /* This could be a CLOSE from a client which hadn't yet received the OPENACK - look for the connected service */ @@ -1665,13 +1718,13 @@ parse_rx_slots(VCHIQ_STATE_T *state) break; } - if (vchiq_core_msg_log_level >= VCHIQ_LOG_INFO) { + if (SRVTRACE_ENABLED(service, VCHIQ_LOG_INFO)) { int svc_fourcc; svc_fourcc = service ? service->base.fourcc : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); - vchiq_log_info(vchiq_core_msg_log_level, + vchiq_log_info(SRVTRACE_LEVEL(service), "Rcvd Msg %s(%u) from %c%c%c%c s:%d d:%d " "len:%d", msg_type_str(type), type, @@ -1741,7 +1794,7 @@ parse_rx_slots(VCHIQ_STATE_T *state) service->remoteport); break; case VCHIQ_MSG_DATA: - vchiq_log_trace(vchiq_core_log_level, + vchiq_log_info(vchiq_core_log_level, "%d: prs DATA@%x,%x (%d->%d)", state->id, (unsigned int)header, size, remoteport, localport); @@ -1769,6 +1822,8 @@ parse_rx_slots(VCHIQ_STATE_T *state) vchiq_log_info(vchiq_core_log_level, "%d: prs CONNECT@%x", state->id, (unsigned int)header); + state->version_common = ((VCHIQ_SLOT_ZERO_T *) + state->slot_data)->version; up(&state->connect); break; case VCHIQ_MSG_BULK_RX: @@ -1922,7 +1977,8 @@ parse_rx_slots(VCHIQ_STATE_T *state) /* Send a PAUSE in response */ if (queue_message(state, NULL, VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0), - NULL, 0, 0, 0) == VCHIQ_RETRY) + NULL, 0, 0, QMFLAGS_NO_MUTEX_UNLOCK) + == VCHIQ_RETRY) goto bail_not_ready; if (state->is_master) pause_bulks(state); @@ -2021,7 +2077,9 @@ slot_handler_func(void *v) pause_bulks(state); if (queue_message(state, NULL, VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0), - NULL, 0, 0, 0) != VCHIQ_RETRY) { + NULL, 0, 0, + QMFLAGS_NO_MUTEX_UNLOCK) + != VCHIQ_RETRY) { vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSE_SENT); } else { @@ -2039,7 +2097,8 @@ slot_handler_func(void *v) case VCHIQ_CONNSTATE_RESUMING: if (queue_message(state, NULL, VCHIQ_MAKE_MSG(VCHIQ_MSG_RESUME, 0, 0), - NULL, 0, 0, 0) != VCHIQ_RETRY) { + NULL, 0, 0, QMFLAGS_NO_MUTEX_LOCK) + != VCHIQ_RETRY) { if (state->is_master) resume_bulks(state); vchiq_set_conn_state(state, @@ -2162,6 +2221,7 @@ sync_func(void *v) service->remoteport = remoteport; vchiq_set_service_state(service, VCHIQ_SRVSTATE_OPENSYNC); + service->sync = 1; up(&service->remove_event); } release_message_sync(state, header); @@ -2341,6 +2401,9 @@ vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero, return VCHIQ_ERROR; } + if (VCHIQ_VERSION < slot_zero->version) + slot_zero->version = VCHIQ_VERSION; + if (is_master) { local = &slot_zero->master; remote = &slot_zero->slave; @@ -2517,6 +2580,7 @@ vchiq_add_service_internal(VCHIQ_STATE_T *state, service->auto_close = 1; service->sync = 0; service->closing = 0; + service->trace = 0; atomic_set(&service->poll_flags, 0); service->version = params->version; service->version_min = params->version_min; @@ -2647,8 +2711,9 @@ vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id) vchiq_use_service_internal(service); status = queue_message(service->state, NULL, VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN, service->localport, 0), - &body, 1, sizeof(payload), 1); + &body, 1, sizeof(payload), QMFLAGS_IS_BLOCKING); if (status == VCHIQ_SUCCESS) { + /* Wait for the ACK/NAK */ if (down_interruptible(&service->remove_event) != 0) { status = VCHIQ_RETRY; vchiq_release_service_internal(service); @@ -2675,7 +2740,18 @@ release_service_messages(VCHIQ_SERVICE_T *service) int slot_last = state->remote->slot_last; int i; - /* Release any claimed messages */ + /* Release any claimed messages aimed at this service */ + + if (service->sync) { + VCHIQ_HEADER_T *header = + (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, + state->remote->slot_sync); + if (VCHIQ_MSG_DSTPORT(header->msgid) == service->localport) + release_message_sync(state, header); + + return; + } + for (i = state->remote->slot_first; i <= slot_last; i++) { VCHIQ_SLOT_INFO_T *slot_info = SLOT_INFO_FROM_INDEX(state, i); @@ -2873,17 +2949,31 @@ vchiq_close_service_internal(VCHIQ_SERVICE_T *service, int close_recvd) (VCHIQ_MSG_CLOSE, service->localport, VCHIQ_MSG_DSTPORT(service->remoteport)), - NULL, 0, 0, 0); + NULL, 0, 0, QMFLAGS_NO_MUTEX_UNLOCK); if (status == VCHIQ_SUCCESS) { - if (!close_recvd) + if (!close_recvd) { + /* Change the state while the mutex is + still held */ + vchiq_set_service_state(service, + VCHIQ_SRVSTATE_CLOSESENT); + lmutex_unlock(&state->slot_mutex); + if (service->sync) + lmutex_unlock(&state->sync_mutex); break; + } } else if (service->srvstate == VCHIQ_SRVSTATE_OPENSYNC) { lmutex_unlock(&state->sync_mutex); break; } else break; + /* Change the state while the mutex is still held */ + vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSERECVD); + lmutex_unlock(&state->slot_mutex); + if (service->sync) + lmutex_unlock(&state->sync_mutex); + status = close_service_complete(service, VCHIQ_SRVSTATE_CLOSERECVD); break; @@ -2990,7 +3080,7 @@ vchiq_connect_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance) if (state->conn_state == VCHIQ_CONNSTATE_DISCONNECTED) { if (queue_message(state, NULL, VCHIQ_MAKE_MSG(VCHIQ_MSG_CONNECT, 0, 0), NULL, 0, - 0, 1) == VCHIQ_RETRY) + 0, QMFLAGS_IS_BLOCKING) == VCHIQ_RETRY) return VCHIQ_RETRY; vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTING); @@ -3276,6 +3366,16 @@ vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, service->localport, service->remoteport, dir_char, size, (unsigned int)bulk->data, (unsigned int)userdata); + /* The slot mutex must be held when the service is being closed, so + claim it here to ensure that isn't happening */ + if (lmutex_lock_interruptible(&state->slot_mutex) != 0) { + status = VCHIQ_RETRY; + goto cancel_bulk_error_exit; + } + + if (service->srvstate != VCHIQ_SRVSTATE_OPEN) + goto unlock_both_error_exit; + if (state->is_master) { queue->local_insert++; if (resolve_bulks(service, queue)) @@ -3289,14 +3389,17 @@ vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, status = queue_message(state, NULL, VCHIQ_MAKE_MSG(dir_msgtype, service->localport, service->remoteport), - &element, 1, sizeof(payload), 1); + &element, 1, sizeof(payload), + QMFLAGS_IS_BLOCKING | + QMFLAGS_NO_MUTEX_LOCK | + QMFLAGS_NO_MUTEX_UNLOCK); if (status != VCHIQ_SUCCESS) { - vchiq_complete_bulk(bulk); - goto unlock_error_exit; + goto unlock_both_error_exit; } queue->local_insert++; } + lmutex_unlock(&state->slot_mutex); lmutex_unlock(&service->bulk_mutex); vchiq_log_trace(vchiq_core_log_level, @@ -3320,6 +3423,10 @@ waiting: return status; +unlock_both_error_exit: + lmutex_unlock(&state->slot_mutex); +cancel_bulk_error_exit: + vchiq_complete_bulk(bulk); unlock_error_exit: lmutex_unlock(&service->bulk_mutex); @@ -3530,6 +3637,11 @@ vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T handle, } break; + case VCHIQ_SERVICE_OPTION_TRACE: + service->trace = value; + status = VCHIQ_SUCCESS; + break; + default: break; } diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_core.h b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_core.h index fb50e85..754c9f6 100644 --- a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_core.h +++ b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_core.h @@ -295,6 +295,7 @@ typedef struct vchiq_service_struct { char auto_close; char sync; char closing; + char trace; atomic_t poll_flags; short version; short version_min; @@ -402,6 +403,7 @@ struct vchiq_state_struct { int initialised; VCHIQ_CONNSTATE_T conn_state; int is_master; + short version_common; VCHIQ_SHARED_STATE_T *local; VCHIQ_SHARED_STATE_T *remote; @@ -606,6 +608,10 @@ find_service_for_instance(VCHIQ_INSTANCE_T instance, VCHIQ_SERVICE_HANDLE_T handle); extern VCHIQ_SERVICE_T * +find_closed_service_for_instance(VCHIQ_INSTANCE_T instance, + VCHIQ_SERVICE_HANDLE_T handle); + +extern VCHIQ_SERVICE_T * next_service_by_instance(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance, int *pidx); diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_debugfs.c b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_debugfs.c new file mode 100644 index 0000000..7e03213 --- /dev/null +++ b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_debugfs.c @@ -0,0 +1,383 @@ +/** + * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. + * Copyright (c) 2010-2012 Broadcom. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include <linux/debugfs.h> +#include "vchiq_core.h" +#include "vchiq_arm.h" +#include "vchiq_debugfs.h" + +#ifdef CONFIG_DEBUG_FS + +/**************************************************************************** +* +* log category entries +* +***************************************************************************/ +#define DEBUGFS_WRITE_BUF_SIZE 256 + +#define VCHIQ_LOG_ERROR_STR "error" +#define VCHIQ_LOG_WARNING_STR "warning" +#define VCHIQ_LOG_INFO_STR "info" +#define VCHIQ_LOG_TRACE_STR "trace" + + +/* Top-level debug info */ +struct vchiq_debugfs_info { + /* Global 'vchiq' debugfs entry used by all instances */ + struct dentry *vchiq_cfg_dir; + + /* one entry per client process */ + struct dentry *clients; + + /* log categories */ + struct dentry *log_categories; +}; + +static struct vchiq_debugfs_info debugfs_info; + +/* Log category debugfs entries */ +struct vchiq_debugfs_log_entry { + const char *name; + int *plevel; + struct dentry *dir; +}; + +static struct vchiq_debugfs_log_entry vchiq_debugfs_log_entries[] = { + { "core", &vchiq_core_log_level }, + { "msg", &vchiq_core_msg_log_level }, + { "sync", &vchiq_sync_log_level }, + { "susp", &vchiq_susp_log_level }, + { "arm", &vchiq_arm_log_level }, +}; +static int n_log_entries = + sizeof(vchiq_debugfs_log_entries)/sizeof(vchiq_debugfs_log_entries[0]); + + +static struct dentry *vchiq_clients_top(void); +static struct dentry *vchiq_debugfs_top(void); + +static int debugfs_log_show(struct seq_file *f, void *offset) +{ + int *levp = f->private; + char *log_value = NULL; + + switch (*levp) { + case VCHIQ_LOG_ERROR: + log_value = VCHIQ_LOG_ERROR_STR; + break; + case VCHIQ_LOG_WARNING: + log_value = VCHIQ_LOG_WARNING_STR; + break; + case VCHIQ_LOG_INFO: + log_value = VCHIQ_LOG_INFO_STR; + break; + case VCHIQ_LOG_TRACE: + log_value = VCHIQ_LOG_TRACE_STR; + break; + default: + break; + } + + seq_printf(f, "%s\n", log_value ? log_value : "(null)"); + + return 0; +} + +static int debugfs_log_open(struct inode *inode, struct file *file) +{ + return single_open(file, debugfs_log_show, inode->i_private); +} + +static int debugfs_log_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct seq_file *f = (struct seq_file *)file->private_data; + int *levp = f->private; + char kbuf[DEBUGFS_WRITE_BUF_SIZE + 1]; + + memset(kbuf, 0, DEBUGFS_WRITE_BUF_SIZE + 1); + if (count >= DEBUGFS_WRITE_BUF_SIZE) + count = DEBUGFS_WRITE_BUF_SIZE; + + if (copy_from_user(kbuf, buffer, count) != 0) + return -EFAULT; + kbuf[count - 1] = 0; + + if (strncmp("error", kbuf, strlen("error")) == 0) + *levp = VCHIQ_LOG_ERROR; + else if (strncmp("warning", kbuf, strlen("warning")) == 0) + *levp = VCHIQ_LOG_WARNING; + else if (strncmp("info", kbuf, strlen("info")) == 0) + *levp = VCHIQ_LOG_INFO; + else if (strncmp("trace", kbuf, strlen("trace")) == 0) + *levp = VCHIQ_LOG_TRACE; + else + *levp = VCHIQ_LOG_DEFAULT; + + *ppos += count; + + return count; +} + +static const struct file_operations debugfs_log_fops = { + .owner = THIS_MODULE, + .open = debugfs_log_open, + .write = debugfs_log_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* create an entry under <debugfs>/vchiq/log for each log category */ +static int vchiq_debugfs_create_log_entries(struct dentry *top) +{ + struct dentry *dir; + size_t i; + int ret = 0; + dir = debugfs_create_dir("log", vchiq_debugfs_top()); + if (!dir) + return -ENOMEM; + debugfs_info.log_categories = dir; + + for (i = 0; i < n_log_entries; i++) { + void *levp = (void *)vchiq_debugfs_log_entries[i].plevel; + dir = debugfs_create_file(vchiq_debugfs_log_entries[i].name, + 0644, + debugfs_info.log_categories, + levp, + &debugfs_log_fops); + if (!dir) { + ret = -ENOMEM; + break; + } + + vchiq_debugfs_log_entries[i].dir = dir; + } + return ret; +} + +static int debugfs_usecount_show(struct seq_file *f, void *offset) +{ + VCHIQ_INSTANCE_T instance = f->private; + int use_count; + + use_count = vchiq_instance_get_use_count(instance); + seq_printf(f, "%d\n", use_count); + + return 0; +} + +static int debugfs_usecount_open(struct inode *inode, struct file *file) +{ + return single_open(file, debugfs_usecount_show, inode->i_private); +} + +static const struct file_operations debugfs_usecount_fops = { + .owner = THIS_MODULE, + .open = debugfs_usecount_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int debugfs_trace_show(struct seq_file *f, void *offset) +{ + VCHIQ_INSTANCE_T instance = f->private; + int trace; + + trace = vchiq_instance_get_trace(instance); + seq_printf(f, "%s\n", trace ? "Y" : "N"); + + return 0; +} + +static int debugfs_trace_open(struct inode *inode, struct file *file) +{ + return single_open(file, debugfs_trace_show, inode->i_private); +} + +static int debugfs_trace_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct seq_file *f = (struct seq_file *)file->private_data; + VCHIQ_INSTANCE_T instance = f->private; + char firstchar; + + if (copy_from_user(&firstchar, buffer, 1) != 0) + return -EFAULT; + + switch (firstchar) { + case 'Y': + case 'y': + case '1': + vchiq_instance_set_trace(instance, 1); + break; + case 'N': + case 'n': + case '0': + vchiq_instance_set_trace(instance, 0); + break; + default: + break; + } + + *ppos += count; + + return count; +} + +static const struct file_operations debugfs_trace_fops = { + .owner = THIS_MODULE, + .open = debugfs_trace_open, + .write = debugfs_trace_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* add an instance (process) to the debugfs entries */ +int vchiq_debugfs_add_instance(VCHIQ_INSTANCE_T instance) +{ + char pidstr[16]; + struct dentry *top, *use_count, *trace; + struct dentry *clients = vchiq_clients_top(); + + snprintf(pidstr, sizeof(pidstr), "%d", + vchiq_instance_get_pid(instance)); + + top = debugfs_create_dir(pidstr, clients); + if (!top) + goto fail_top; + + use_count = debugfs_create_file("use_count", + 0444, top, + instance, + &debugfs_usecount_fops); + if (!use_count) + goto fail_use_count; + + trace = debugfs_create_file("trace", + 0644, top, + instance, + &debugfs_trace_fops); + if (!trace) + goto fail_trace; + + vchiq_instance_get_debugfs_node(instance)->dentry = top; + + return 0; + +fail_trace: + debugfs_remove(use_count); +fail_use_count: + debugfs_remove(top); +fail_top: + return -ENOMEM; +} + +void vchiq_debugfs_remove_instance(VCHIQ_INSTANCE_T instance) +{ + VCHIQ_DEBUGFS_NODE_T *node = vchiq_instance_get_debugfs_node(instance); + debugfs_remove_recursive(node->dentry); +} + + +int vchiq_debugfs_init(void) +{ + BUG_ON(debugfs_info.vchiq_cfg_dir != NULL); + + debugfs_info.vchiq_cfg_dir = debugfs_create_dir("vchiq", NULL); + if (debugfs_info.vchiq_cfg_dir == NULL) + goto fail; + + debugfs_info.clients = debugfs_create_dir("clients", + vchiq_debugfs_top()); + if (!debugfs_info.clients) + goto fail; + + if (vchiq_debugfs_create_log_entries(vchiq_debugfs_top()) != 0) + goto fail; + + return 0; + +fail: + vchiq_debugfs_deinit(); + vchiq_log_error(vchiq_arm_log_level, + "%s: failed to create debugfs directory", + __func__); + + return -ENOMEM; +} + +/* remove all the debugfs entries */ +void vchiq_debugfs_deinit(void) +{ + debugfs_remove_recursive(vchiq_debugfs_top()); +} + +static struct dentry *vchiq_clients_top(void) +{ + return debugfs_info.clients; +} + +static struct dentry *vchiq_debugfs_top(void) +{ + BUG_ON(debugfs_info.vchiq_cfg_dir == NULL); + return debugfs_info.vchiq_cfg_dir; +} + +#else /* CONFIG_DEBUG_FS */ + +int vchiq_debugfs_init(void) +{ + return 0; +} + +void vchiq_debugfs_deinit(void) +{ +} + +int vchiq_debugfs_add_instance(VCHIQ_INSTANCE_T instance) +{ + return 0; +} + +void vchiq_debugfs_remove_instance(VCHIQ_INSTANCE_T instance) +{ +} + +#endif /* CONFIG_DEBUG_FS */ diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_debugfs.h b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_debugfs.h new file mode 100644 index 0000000..4d6a378 --- /dev/null +++ b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_debugfs.h @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef VCHIQ_DEBUGFS_H +#define VCHIQ_DEBUGFS_H + +#include "vchiq_core.h" + +typedef struct vchiq_debugfs_node_struct +{ + struct dentry *dentry; +} VCHIQ_DEBUGFS_NODE_T; + +int vchiq_debugfs_init(void); + +void vchiq_debugfs_deinit(void); + +int vchiq_debugfs_add_instance(VCHIQ_INSTANCE_T instance); + +void vchiq_debugfs_remove_instance(VCHIQ_INSTANCE_T instance); + +#endif /* VCHIQ_DEBUGFS_H */ diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_if.h b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_if.h index 6a95a67..a5fb128 100644 --- a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_if.h +++ b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_if.h @@ -74,7 +74,8 @@ typedef enum { VCHIQ_SERVICE_OPTION_AUTOCLOSE, VCHIQ_SERVICE_OPTION_SLOT_QUOTA, VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA, - VCHIQ_SERVICE_OPTION_SYNCHRONOUS + VCHIQ_SERVICE_OPTION_SYNCHRONOUS, + VCHIQ_SERVICE_OPTION_TRACE } VCHIQ_SERVICE_OPTION_T; typedef struct vchiq_header_struct { diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_ioctl.h b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_ioctl.h index 13103c5..617479e 100644 --- a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_ioctl.h +++ b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_ioctl.h @@ -123,6 +123,8 @@ typedef struct { _IOW(VCHIQ_IOC_MAGIC, 14, VCHIQ_SET_SERVICE_OPTION_T) #define VCHIQ_IOC_DUMP_PHYS_MEM \ _IOW(VCHIQ_IOC_MAGIC, 15, VCHIQ_DUMP_MEM_T) -#define VCHIQ_IOC_MAX 15 +#define VCHIQ_IOC_LIB_VERSION _IO(VCHIQ_IOC_MAGIC, 16) +#define VCHIQ_IOC_CLOSE_DELIVERED _IO(VCHIQ_IOC_MAGIC, 17) +#define VCHIQ_IOC_MAX 17 #endif diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_kern_lib.c b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_kern_lib.c index 6edbb67..1f849a0 100644 --- a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_kern_lib.c +++ b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_kern_lib.c @@ -35,6 +35,7 @@ #include "vchiq_core.h" #include "vchiq_arm.h" +#include "vchiq_killable.h" /* ---- Public Variables ------------------------------------------------- */ diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_killable.h b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_killable.h new file mode 100644 index 0000000..9385dbb --- /dev/null +++ b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_killable.h @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2010-2012 Broadcom. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef VCHIQ_KILLABLE_H +#define VCHIQ_KILLABLE_H + +#ifdef notyet +#include <linux/mutex.h> +#include <linux/semaphore.h> + +#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGTRAP) | sigmask(SIGSTOP) | sigmask(SIGCONT)) + +static inline int __must_check down_interruptible_killable(struct semaphore *sem) +{ + /* Allow interception of killable signals only. We don't want to be interrupted by harmless signals like SIGALRM */ + int ret; + sigset_t blocked, oldset; + siginitsetinv(&blocked, SHUTDOWN_SIGS); + sigprocmask(SIG_SETMASK, &blocked, &oldset); + ret = down_interruptible(sem); + sigprocmask(SIG_SETMASK, &oldset, NULL); + return ret; +} +#define down_interruptible down_interruptible_killable + + +static inline int __must_check mutex_lock_interruptible_killable(struct mutex *lock) +{ + /* Allow interception of killable signals only. We don't want to be interrupted by harmless signals like SIGALRM */ + int ret; + sigset_t blocked, oldset; + siginitsetinv(&blocked, SHUTDOWN_SIGS); + sigprocmask(SIG_SETMASK, &blocked, &oldset); + ret = mutex_lock_interruptible(lock); + sigprocmask(SIG_SETMASK, &oldset, NULL); + return ret; +} +#define mutex_lock_interruptible mutex_lock_interruptible_killable + +#endif + +#endif diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_proc.c b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_proc.c deleted file mode 100644 index 863c285..0000000 --- a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_proc.c +++ /dev/null @@ -1,240 +0,0 @@ -/** - * Copyright (c) 2010-2012 Broadcom. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The names of the above-listed copyright holders may not be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2, as published by the Free - * Software Foundation. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - -#include <linux/proc_fs.h> -#include "vchiq_core.h" -#include "vchiq_arm.h" - -struct vchiq_proc_info { - /* Global 'vc' proc entry used by all instances */ - struct proc_dir_entry *vc_cfg_dir; - - /* one entry per client process */ - struct proc_dir_entry *clients; - - /* log categories */ - struct proc_dir_entry *log_categories; -}; - -static struct vchiq_proc_info proc_info; - -struct proc_dir_entry *vchiq_proc_top(void) -{ - BUG_ON(proc_info.vc_cfg_dir == NULL); - return proc_info.vc_cfg_dir; -} - -/**************************************************************************** -* -* log category entries -* -***************************************************************************/ -#define PROC_WRITE_BUF_SIZE 256 - -#define VCHIQ_LOG_ERROR_STR "error" -#define VCHIQ_LOG_WARNING_STR "warning" -#define VCHIQ_LOG_INFO_STR "info" -#define VCHIQ_LOG_TRACE_STR "trace" - -static int log_cfg_read(char *buffer, - char **start, - off_t off, - int count, - int *eof, - void *data) -{ - int len = 0; - char *log_value = NULL; - - switch (*((int *)data)) { - case VCHIQ_LOG_ERROR: - log_value = VCHIQ_LOG_ERROR_STR; - break; - case VCHIQ_LOG_WARNING: - log_value = VCHIQ_LOG_WARNING_STR; - break; - case VCHIQ_LOG_INFO: - log_value = VCHIQ_LOG_INFO_STR; - break; - case VCHIQ_LOG_TRACE: - log_value = VCHIQ_LOG_TRACE_STR; - break; - default: - break; - } - - len += snprintf(buffer + len, count - len, - "%s\n", - log_value ? log_value : "(null)"); - - return len; -} - - -static int log_cfg_write(struct file *file, - const char __user *buffer, - unsigned long count, - void *data) -{ - int *log_module = data; - char kbuf[PROC_WRITE_BUF_SIZE + 1]; - - (void)file; - - memset(kbuf, 0, PROC_WRITE_BUF_SIZE + 1); - if (count >= PROC_WRITE_BUF_SIZE) - count = PROC_WRITE_BUF_SIZE; - - if (copy_from_user(kbuf, - buffer, - count) != 0) - return -EFAULT; - kbuf[count - 1] = 0; - - if (strncmp("error", kbuf, strlen("error")) == 0) - *log_module = VCHIQ_LOG_ERROR; - else if (strncmp("warning", kbuf, strlen("warning")) == 0) - *log_module = VCHIQ_LOG_WARNING; - else if (strncmp("info", kbuf, strlen("info")) == 0) - *log_module = VCHIQ_LOG_INFO; - else if (strncmp("trace", kbuf, strlen("trace")) == 0) - *log_module = VCHIQ_LOG_TRACE; - else - *log_module = VCHIQ_LOG_DEFAULT; - - return count; -} - -/* Log category proc entries */ -struct vchiq_proc_log_entry { - const char *name; - int *plevel; - struct proc_dir_entry *dir; -}; - -static struct vchiq_proc_log_entry vchiq_proc_log_entries[] = { - { "core", &vchiq_core_log_level }, - { "msg", &vchiq_core_msg_log_level }, - { "sync", &vchiq_sync_log_level }, - { "susp", &vchiq_susp_log_level }, - { "arm", &vchiq_arm_log_level }, -}; -static int n_log_entries = - sizeof(vchiq_proc_log_entries)/sizeof(vchiq_proc_log_entries[0]); - -/* create an entry under /proc/vc/log for each log category */ -static int vchiq_proc_create_log_entries(struct proc_dir_entry *top) -{ - struct proc_dir_entry *dir; - size_t i; - int ret = 0; - - dir = proc_mkdir("log", proc_info.vc_cfg_dir); - if (!dir) - return -ENOMEM; - proc_info.log_categories = dir; - - for (i = 0; i < n_log_entries; i++) { - dir = create_proc_entry(vchiq_proc_log_entries[i].name, - 0644, - proc_info.log_categories); - if (!dir) { - ret = -ENOMEM; - break; - } - - dir->read_proc = &log_cfg_read; - dir->write_proc = &log_cfg_write; - dir->data = (void *)vchiq_proc_log_entries[i].plevel; - - vchiq_proc_log_entries[i].dir = dir; - } - return ret; -} - - -int vchiq_proc_init(void) -{ - BUG_ON(proc_info.vc_cfg_dir != NULL); - - proc_info.vc_cfg_dir = proc_mkdir("vc", NULL); - if (proc_info.vc_cfg_dir == NULL) - goto fail; - - proc_info.clients = proc_mkdir("clients", - proc_info.vc_cfg_dir); - if (!proc_info.clients) - goto fail; - - if (vchiq_proc_create_log_entries(proc_info.vc_cfg_dir) != 0) - goto fail; - - return 0; - -fail: - vchiq_proc_deinit(); - vchiq_log_error(vchiq_arm_log_level, - "%s: failed to create proc directory", - __func__); - - return -ENOMEM; -} - -/* remove all the proc entries */ -void vchiq_proc_deinit(void) -{ - /* log category entries */ - if (proc_info.log_categories) { - size_t i; - for (i = 0; i < n_log_entries; i++) - if (vchiq_proc_log_entries[i].dir) - remove_proc_entry( - vchiq_proc_log_entries[i].name, - proc_info.log_categories); - - remove_proc_entry(proc_info.log_categories->name, - proc_info.vc_cfg_dir); - } - if (proc_info.clients) - remove_proc_entry(proc_info.clients->name, - proc_info.vc_cfg_dir); - if (proc_info.vc_cfg_dir) - remove_proc_entry(proc_info.vc_cfg_dir->name, NULL); -} - -struct proc_dir_entry *vchiq_clients_top(void) -{ - return proc_info.clients; -} - diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_shim.c b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_shim.c index 94ad46e..cc8ef2e 100644 --- a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_shim.c +++ b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_shim.c @@ -403,6 +403,7 @@ int32_t vchi_held_msg_release(VCHI_HELD_MSG_T *message) return 0; } +EXPORT_SYMBOL(vchi_held_msg_release); /*********************************************************** * Name: vchi_msg_hold @@ -448,13 +449,12 @@ int32_t vchi_msg_hold(VCHI_SERVICE_HANDLE_T handle, return 0; } +EXPORT_SYMBOL(vchi_msg_hold); /*********************************************************** * Name: vchi_initialise * * Arguments: VCHI_INSTANCE_T *instance_handle - * VCHI_CONNECTION_T **connections - * const uint32_t num_connections * * Description: Initialises the hardware but does not transmit anything * When run as a Host App this will be called twice hence the need @@ -725,6 +725,36 @@ int32_t vchi_service_destroy(const VCHI_SERVICE_HANDLE_T handle) } EXPORT_SYMBOL(vchi_service_destroy); +int32_t vchi_service_set_option(const VCHI_SERVICE_HANDLE_T handle, + VCHI_SERVICE_OPTION_T option, + int value) +{ + int32_t ret = -1; + SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; + VCHIQ_SERVICE_OPTION_T vchiq_option; + switch (option) { + case VCHI_SERVICE_OPTION_TRACE: + vchiq_option = VCHIQ_SERVICE_OPTION_TRACE; + break; + case VCHI_SERVICE_OPTION_SYNCHRONOUS: + vchiq_option = VCHIQ_SERVICE_OPTION_SYNCHRONOUS; + break; + default: + service = NULL; + break; + } + if (service) { + VCHIQ_STATUS_T status = + vchiq_set_service_option(service->handle, + vchiq_option, + value); + + ret = vchiq_status_to_vchi(status); + } + return ret; +} +EXPORT_SYMBOL(vchi_service_set_option); + int32_t vchi_get_peer_version( const VCHI_SERVICE_HANDLE_T handle, short *peer_version ) { int32_t ret = -1; diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_util.c b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_util.c index d972d3b..67270b3 100644 --- a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_util.c +++ b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_util.c @@ -45,6 +45,7 @@ int vchiu_queue_init(VCHIU_QUEUE_T *queue, int size) queue->size = size; queue->read = 0; queue->write = 0; + queue->initialized = 1; _sema_init(&queue->pop, 0); _sema_init(&queue->push, 0); @@ -75,6 +76,9 @@ int vchiu_queue_is_full(VCHIU_QUEUE_T *queue) void vchiu_queue_push(VCHIU_QUEUE_T *queue, VCHIQ_HEADER_T *header) { + if (!queue->initialized) + return; + while (queue->write == queue->read + queue->size) { if (down_interruptible(&queue->pop) != 0) { flush_signals(current); diff --git a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_util.h b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_util.h index ce49037..b63f1aa 100644 --- a/sys/contrib/vchiq/interface/vchiq_arm/vchiq_util.h +++ b/sys/contrib/vchiq/interface/vchiq_arm/vchiq_util.h @@ -44,6 +44,7 @@ typedef struct { int size; int read; int write; + int initialized; struct semaphore pop; struct semaphore push; @@ -63,4 +64,3 @@ extern VCHIQ_HEADER_T *vchiu_queue_peek(VCHIU_QUEUE_T *queue); extern VCHIQ_HEADER_T *vchiu_queue_pop(VCHIU_QUEUE_T *queue); #endif - |