]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c
vchiq_arm: Avoid use of mutex in add_completion
[mirror_ubuntu-zesty-kernel.git] / drivers / staging / vc04_services / interface / vchiq_arm / vchiq_core.c
index 028e90bc1cdcc3f305ce5b97b88dd5c6c4613a50..d6757ee263fb61a689c0d38c0dbb65c57a8e39bb 100644 (file)
@@ -464,8 +464,8 @@ remote_event_pollall(VCHIQ_STATE_T *state)
 ** enough for a header. This relies on header size being a power of two, which
 ** has been verified earlier by a static assertion. */
 
-static inline size_t
-calc_stride(size_t size)
+static inline unsigned int
+calc_stride(unsigned int size)
 {
        /* Allow room for the header */
        size += sizeof(VCHIQ_HEADER_T);
@@ -544,7 +544,7 @@ request_poll(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, int poll_type)
 /* Called from queue_message, by the slot handler and application threads,
 ** with slot_mutex held */
 static VCHIQ_HEADER_T *
-reserve_space(VCHIQ_STATE_T *state, size_t space, int is_blocking)
+reserve_space(VCHIQ_STATE_T *state, int space, int is_blocking)
 {
        VCHIQ_SHARED_STATE_T *local = state->local;
        int tx_pos = state->local_tx_pos;
@@ -607,15 +607,15 @@ process_free_queue(VCHIQ_STATE_T *state)
        BITSET_T service_found[BITSET_SIZE(VCHIQ_MAX_SERVICES)];
        int slot_queue_available;
 
-       /* Use a read memory barrier to ensure that any state that may have
-       ** been modified by another thread is not masked by stale prefetched
-       ** values. */
-       rmb();
-
        /* Find slots which have been freed by the other side, and return them
        ** to the available queue. */
        slot_queue_available = state->slot_queue_available;
 
+       /* Use a memory barrier to ensure that any state that may have been
+       ** modified by another thread is not masked by stale prefetched
+       ** values. */
+       mb();
+
        while (slot_queue_available != local->slot_queue_recycle) {
                unsigned int pos;
                int slot_index = local->slot_queue[slot_queue_available++ &
@@ -623,6 +623,8 @@ process_free_queue(VCHIQ_STATE_T *state)
                char *data = (char *)SLOT_DATA_FROM_INDEX(state, slot_index);
                int data_found = 0;
 
+               rmb();
+
                vchiq_log_trace(vchiq_core_log_level, "%d: pfq %d=%pK %x %x",
                        state->id, slot_index, data,
                        local->slot_queue_recycle, slot_queue_available);
@@ -721,71 +723,25 @@ process_free_queue(VCHIQ_STATE_T *state)
                                up(&state->data_quota_event);
                }
 
+               mb();
+
                state->slot_queue_available = slot_queue_available;
                up(&state->slot_available_event);
        }
 }
 
-static ssize_t
-memcpy_copy_callback(
-       void *context, void *dest,
-       size_t offset, size_t maxsize)
-{
-       void *src = context;
-
-       memcpy(dest + offset, src + offset, maxsize);
-       return maxsize;
-}
-
-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)
-{
-       size_t pos = 0;
-
-       while (pos < size) {
-               ssize_t callback_result;
-               size_t max_bytes = size - pos;
-
-               callback_result =
-                       copy_callback(context, dest + pos,
-                                     pos, max_bytes);
-
-               if (callback_result < 0)
-                       return callback_result;
-
-               if (!callback_result)
-                       return -EIO;
-
-               if (callback_result > max_bytes)
-                       return -EIO;
-
-               pos += callback_result;
-       }
-
-       return size;
-}
-
 /* Called by the slot handler and application threads */
 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)
+       int msgid, const VCHIQ_ELEMENT_T *elements,
+       int count, int size, int flags)
 {
        VCHIQ_SHARED_STATE_T *local;
        VCHIQ_SERVICE_QUOTA_T *service_quota = NULL;
        VCHIQ_HEADER_T *header;
        int type = VCHIQ_MSG_TYPE(msgid);
 
-       size_t stride;
+       unsigned int stride;
 
        local = state->local;
 
@@ -891,7 +847,7 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service,
        }
 
        if (type == VCHIQ_MSG_DATA) {
-               ssize_t callback_result;
+               int i, pos;
                int tx_end_index;
                int slot_use_count;
 
@@ -905,23 +861,27 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service,
                BUG_ON((flags & (QMFLAGS_NO_MUTEX_LOCK |
                                 QMFLAGS_NO_MUTEX_UNLOCK)) != 0);
 
-               callback_result =
-                       copy_message_data(copy_callback, context,
-                                         header->data, size);
-
-               if (callback_result < 0) {
-                       mutex_unlock(&state->slot_mutex);
-                       VCHIQ_SERVICE_STATS_INC(service,
+               for (i = 0, pos = 0; i < (unsigned int)count;
+                       pos += elements[i++].size)
+                       if (elements[i].size) {
+                               if (vchiq_copy_from_user
+                                       (header->data + pos, elements[i].data,
+                                       (size_t) elements[i].size) !=
+                                       VCHIQ_SUCCESS) {
+                                       mutex_unlock(&state->slot_mutex);
+                                       VCHIQ_SERVICE_STATS_INC(service,
                                                error_count);
-                       return VCHIQ_ERROR;
-               }
-
-               if (SRVTRACE_ENABLED(service,
-                                    VCHIQ_LOG_INFO))
-                       vchiq_log_dump_mem("Sent", 0,
-                                          header->data,
-                                          min((size_t)64,
-                                              (size_t)callback_result));
+                                       return VCHIQ_ERROR;
+                               }
+                               if (i == 0) {
+                                       if (SRVTRACE_ENABLED(service,
+                                                       VCHIQ_LOG_INFO))
+                                               vchiq_log_dump_mem("Sent", 0,
+                                                       header->data + pos,
+                                                       min(64u,
+                                                       elements[0].size));
+                               }
+                       }
 
                spin_lock(&quota_spinlock);
                service_quota->message_use_count++;
@@ -963,17 +923,9 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service,
                        header, size, VCHIQ_MSG_SRCPORT(msgid),
                        VCHIQ_MSG_DSTPORT(msgid));
                if (size != 0) {
-                       /* It is assumed for now that this code path
-                        * only happens from calls inside this file.
-                        *
-                        * External callers are through the vchiq_queue_message
-                        * path which always sets the type to be VCHIQ_MSG_DATA
-                        *
-                        * At first glance this appears to be correct but
-                        * more review is needed.
-                        */
-                       copy_message_data(copy_callback, context,
-                                         header->data, size);
+                       WARN_ON(!((count == 1) && (size == elements[0].size)));
+                       memcpy(header->data, elements[0].data,
+                               elements[0].size);
                }
                VCHIQ_STATS_INC(state, ctrl_tx_count);
        }
@@ -1019,16 +971,11 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service,
 /* Called by the slot handler and application threads */
 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)
+       int msgid, const VCHIQ_ELEMENT_T *elements,
+       int count, int size, int is_blocking)
 {
        VCHIQ_SHARED_STATE_T *local;
        VCHIQ_HEADER_T *header;
-       ssize_t callback_result;
 
        local = state->local;
 
@@ -1051,34 +998,50 @@ queue_message_sync(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service,
                                state->id, oldmsgid);
        }
 
-       vchiq_log_info(vchiq_sync_log_level,
-                      "%d: qms %s@%pK,%x (%d->%d)", state->id,
-                      msg_type_str(VCHIQ_MSG_TYPE(msgid)),
-                      header, size, VCHIQ_MSG_SRCPORT(msgid),
-                      VCHIQ_MSG_DSTPORT(msgid));
-
-       callback_result =
-               copy_message_data(copy_callback, context,
-                                 header->data, size);
+       if (service) {
+               int i, pos;
 
-       if (callback_result < 0) {
-               mutex_unlock(&state->slot_mutex);
-               VCHIQ_SERVICE_STATS_INC(service,
-                                       error_count);
-               return VCHIQ_ERROR;
-       }
+               vchiq_log_info(vchiq_sync_log_level,
+                       "%d: qms %s@%pK,%x (%d->%d)", state->id,
+                       msg_type_str(VCHIQ_MSG_TYPE(msgid)),
+                       header, size, VCHIQ_MSG_SRCPORT(msgid),
+                       VCHIQ_MSG_DSTPORT(msgid));
 
-       if (service) {
-               if (SRVTRACE_ENABLED(service,
-                                    VCHIQ_LOG_INFO))
-                       vchiq_log_dump_mem("Sent", 0,
-                                          header->data,
-                                          min((size_t)64,
-                                              (size_t)callback_result));
+               for (i = 0, pos = 0; i < (unsigned int)count;
+                       pos += elements[i++].size)
+                       if (elements[i].size) {
+                               if (vchiq_copy_from_user
+                                       (header->data + pos, elements[i].data,
+                                       (size_t) elements[i].size) !=
+                                       VCHIQ_SUCCESS) {
+                                       mutex_unlock(&state->sync_mutex);
+                                       VCHIQ_SERVICE_STATS_INC(service,
+                                               error_count);
+                                       return VCHIQ_ERROR;
+                               }
+                               if (i == 0) {
+                                       if (vchiq_sync_log_level >=
+                                               VCHIQ_LOG_TRACE)
+                                               vchiq_log_dump_mem("Sent Sync",
+                                                       0, header->data + pos,
+                                                       min(64u,
+                                                       elements[0].size));
+                               }
+                       }
 
                VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count);
                VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size);
        } else {
+               vchiq_log_info(vchiq_sync_log_level,
+                       "%d: qms %s@%pK,%x (%d->%d)", state->id,
+                       msg_type_str(VCHIQ_MSG_TYPE(msgid)),
+                       header, size, VCHIQ_MSG_SRCPORT(msgid),
+                       VCHIQ_MSG_DSTPORT(msgid));
+               if (size != 0) {
+                       WARN_ON(!((count == 1) && (size == elements[0].size)));
+                       memcpy(header->data, elements[0].data,
+                               elements[0].size);
+               }
                VCHIQ_STATS_INC(state, ctrl_tx_count);
        }
 
@@ -1191,16 +1154,11 @@ notify_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue,
                                VCHIQ_MSG_BULK_RX_DONE : VCHIQ_MSG_BULK_TX_DONE;
                        int msgid = VCHIQ_MAKE_MSG(msgtype, service->localport,
                                service->remoteport);
+                       VCHIQ_ELEMENT_T element = { &bulk->actual, 4 };
                        /* Only reply to non-dummy bulk requests */
                        if (bulk->remote_data) {
-                               status = queue_message(
-                                               service->state,
-                                               NULL,
-                                               msgid,
-                                               memcpy_copy_callback,
-                                               &bulk->actual,
-                                               4,
-                                               0);
+                               status = queue_message(service->state, NULL,
+                                       msgid, &element, 1, 4, 0);
                                if (status != VCHIQ_SUCCESS)
                                        break;
                        }
@@ -1560,6 +1518,10 @@ parse_open(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header)
                                struct vchiq_openack_payload ack_payload = {
                                        service->version
                                };
+                               VCHIQ_ELEMENT_T body = {
+                                       &ack_payload,
+                                       sizeof(ack_payload)
+                               };
 
                                if (state->version_common <
                                    VCHIQ_VERSION_SYNCHRONOUS_MODE)
@@ -1569,28 +1531,21 @@ parse_open(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header)
                                if (service->sync &&
                                    (state->version_common >=
                                     VCHIQ_VERSION_SYNCHRONOUS_MODE)) {
-                                       if (queue_message_sync(
-                                               state,
-                                               NULL,
+                                       if (queue_message_sync(state, NULL,
                                                VCHIQ_MAKE_MSG(
                                                        VCHIQ_MSG_OPENACK,
                                                        service->localport,
                                                        remoteport),
-                                               memcpy_copy_callback,
-                                               &ack_payload,
-                                               sizeof(ack_payload),
+                                               &body, 1, sizeof(ack_payload),
                                                0) == VCHIQ_RETRY)
                                                goto bail_not_ready;
                                } else {
-                                       if (queue_message(state,
-                                                       NULL,
-                                                       VCHIQ_MAKE_MSG(
+                                       if (queue_message(state, NULL,
+                                               VCHIQ_MAKE_MSG(
                                                        VCHIQ_MSG_OPENACK,
                                                        service->localport,
                                                        remoteport),
-                                               memcpy_copy_callback,
-                                               &ack_payload,
-                                               sizeof(ack_payload),
+                                               &body, 1, sizeof(ack_payload),
                                                0) == VCHIQ_RETRY)
                                                goto bail_not_ready;
                                }
@@ -2680,19 +2635,14 @@ vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id)
                service->version,
                service->version_min
        };
+       VCHIQ_ELEMENT_T body = { &payload, sizeof(payload) };
        VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
 
        service->client_id = client_id;
        vchiq_use_service_internal(service);
-       status = queue_message(service->state,
-                              NULL,
-                              VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN,
-                                             service->localport,
-                                             0),
-                              memcpy_copy_callback,
-                              &payload,
-                              sizeof(payload),
-                              QMFLAGS_IS_BLOCKING);
+       status = queue_message(service->state, NULL,
+               VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN, service->localport, 0),
+               &body, 1, sizeof(payload), QMFLAGS_IS_BLOCKING);
        if (status == VCHIQ_SUCCESS) {
                /* Wait for the ACK/NAK */
                if (down_interruptible(&service->remove_event) != 0) {
@@ -3360,18 +3310,15 @@ vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle,
                                VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY);
        } else {
                int payload[2] = { (int)(long)bulk->data, bulk->size };
+               VCHIQ_ELEMENT_T element = { payload, sizeof(payload) };
 
-               status = queue_message(state,
-                                      NULL,
-                                      VCHIQ_MAKE_MSG(dir_msgtype,
-                                                     service->localport,
-                                                     service->remoteport),
-                                      memcpy_copy_callback,
-                                      &payload,
-                                      sizeof(payload),
-                                      QMFLAGS_IS_BLOCKING |
-                                      QMFLAGS_NO_MUTEX_LOCK |
-                                      QMFLAGS_NO_MUTEX_UNLOCK);
+               status = queue_message(state, NULL,
+                       VCHIQ_MAKE_MSG(dir_msgtype,
+                               service->localport, service->remoteport),
+                       &element, 1, sizeof(payload),
+                       QMFLAGS_IS_BLOCKING |
+                       QMFLAGS_NO_MUTEX_LOCK |
+                       QMFLAGS_NO_MUTEX_UNLOCK);
                if (status != VCHIQ_SUCCESS) {
                        goto unlock_both_error_exit;
                }
@@ -3417,22 +3364,26 @@ error_exit:
 
 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)
+       const VCHIQ_ELEMENT_T *elements, unsigned int count)
 {
        VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
        VCHIQ_STATUS_T status = VCHIQ_ERROR;
 
+       unsigned int size = 0;
+       unsigned int i;
+
        if (!service ||
                (vchiq_check_service(service) != VCHIQ_SUCCESS))
                goto error_exit;
 
-       if (!size) {
-               VCHIQ_SERVICE_STATS_INC(service, error_count);
-               goto error_exit;
-
+       for (i = 0; i < (unsigned int)count; i++) {
+               if (elements[i].size) {
+                       if (elements[i].data == NULL) {
+                               VCHIQ_SERVICE_STATS_INC(service, error_count);
+                               goto error_exit;
+                       }
+                       size += elements[i].size;
+               }
        }
 
        if (size > VCHIQ_MAX_MSG_SIZE) {
@@ -3446,14 +3397,14 @@ vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle,
                                VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA,
                                        service->localport,
                                        service->remoteport),
-                               copy_callback, context, size, 1);
+                               elements, count, size, 1);
                break;
        case VCHIQ_SRVSTATE_OPENSYNC:
                status = queue_message_sync(service->state, service,
                                VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA,
                                        service->localport,
                                        service->remoteport),
-                               copy_callback, context, size, 1);
+                               elements, count, size, 1);
                break;
        default:
                status = VCHIQ_ERROR;