Commit 3c27a36f authored by Marcelo Diop-Gonzalez's avatar Marcelo Diop-Gonzalez Committed by Greg Kroah-Hartman

staging: vc04_services: use kref + RCU to reference count services

Currently reference counts are implemented by locking service_spinlock
and then incrementing the service's ->ref_count field, calling
kfree() when the last reference has been dropped. But at the same
time, there's code in multiple places that dereferences pointers
to services without having a reference, so there could be a race there.

It should be possible to avoid taking any lock in unlock_service()
or service_release() because we are setting a single array element
to NULL, and on service creation, a mutex is locked before looking
for a NULL spot to put the new service in.

Using a struct kref and RCU-delaying the freeing of services fixes
this race condition while still making it possible to skip
grabbing a reference in many places. Also it avoids the need to
acquire a single spinlock when e.g. taking a reference on
state->services[i] when somebody else is in the middle of taking
a reference on state->services[j].
Signed-off-by: default avatarMarcelo Diop-Gonzalez <marcgonzalez@google.com>
Link: https://lore.kernel.org/r/3bf6f1ec6ace64d7072025505e165b8dd18b25ca.1581532523.git.marcgonzalez@google.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 0e35fa61
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/rcupdate.h>
#include <soc/bcm2835/raspberrypi-firmware.h> #include <soc/bcm2835/raspberrypi-firmware.h>
#include "vchiq_core.h" #include "vchiq_core.h"
...@@ -2096,10 +2097,12 @@ int vchiq_dump_platform_instances(void *dump_context) ...@@ -2096,10 +2097,12 @@ int vchiq_dump_platform_instances(void *dump_context)
/* There is no list of instances, so instead scan all services, /* There is no list of instances, so instead scan all services,
marking those that have been dumped. */ marking those that have been dumped. */
rcu_read_lock();
for (i = 0; i < state->unused_service; i++) { for (i = 0; i < state->unused_service; i++) {
struct vchiq_service *service = state->services[i]; struct vchiq_service *service;
struct vchiq_instance *instance; struct vchiq_instance *instance;
service = rcu_dereference(state->services[i]);
if (!service || service->base.callback != service_callback) if (!service || service->base.callback != service_callback)
continue; continue;
...@@ -2107,18 +2110,26 @@ int vchiq_dump_platform_instances(void *dump_context) ...@@ -2107,18 +2110,26 @@ int vchiq_dump_platform_instances(void *dump_context)
if (instance) if (instance)
instance->mark = 0; instance->mark = 0;
} }
rcu_read_unlock();
for (i = 0; i < state->unused_service; i++) { for (i = 0; i < state->unused_service; i++) {
struct vchiq_service *service = state->services[i]; struct vchiq_service *service;
struct vchiq_instance *instance; struct vchiq_instance *instance;
int err; int err;
if (!service || service->base.callback != service_callback) rcu_read_lock();
service = rcu_dereference(state->services[i]);
if (!service || service->base.callback != service_callback) {
rcu_read_unlock();
continue; continue;
}
instance = service->instance; instance = service->instance;
if (!instance || instance->mark) if (!instance || instance->mark) {
rcu_read_unlock();
continue; continue;
}
rcu_read_unlock();
len = snprintf(buf, sizeof(buf), len = snprintf(buf, sizeof(buf),
"Instance %pK: pid %d,%s completions %d/%d", "Instance %pK: pid %d,%s completions %d/%d",
...@@ -2128,7 +2139,6 @@ int vchiq_dump_platform_instances(void *dump_context) ...@@ -2128,7 +2139,6 @@ int vchiq_dump_platform_instances(void *dump_context)
instance->completion_insert - instance->completion_insert -
instance->completion_remove, instance->completion_remove,
MAX_COMPLETIONS); MAX_COMPLETIONS);
err = vchiq_dump(dump_context, buf, len + 1); err = vchiq_dump(dump_context, buf, len + 1);
if (err) if (err)
return err; return err;
...@@ -2585,8 +2595,10 @@ vchiq_dump_service_use_state(struct vchiq_state *state) ...@@ -2585,8 +2595,10 @@ vchiq_dump_service_use_state(struct vchiq_state *state)
if (active_services > MAX_SERVICES) if (active_services > MAX_SERVICES)
only_nonzero = 1; only_nonzero = 1;
rcu_read_lock();
for (i = 0; i < active_services; i++) { for (i = 0; i < active_services; i++) {
struct vchiq_service *service_ptr = state->services[i]; struct vchiq_service *service_ptr =
rcu_dereference(state->services[i]);
if (!service_ptr) if (!service_ptr)
continue; continue;
...@@ -2604,6 +2616,7 @@ vchiq_dump_service_use_state(struct vchiq_state *state) ...@@ -2604,6 +2616,7 @@ vchiq_dump_service_use_state(struct vchiq_state *state)
if (found >= MAX_SERVICES) if (found >= MAX_SERVICES)
break; break;
} }
rcu_read_unlock();
read_unlock_bh(&arm_state->susp_res_lock); read_unlock_bh(&arm_state->susp_res_lock);
......
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright (c) 2010-2012 Broadcom. All rights reserved. */ /* Copyright (c) 2010-2012 Broadcom. All rights reserved. */
#include <linux/kref.h>
#include <linux/rcupdate.h>
#include "vchiq_core.h" #include "vchiq_core.h"
#define VCHIQ_SLOT_HANDLER_STACK 8192 #define VCHIQ_SLOT_HANDLER_STACK 8192
...@@ -54,7 +57,6 @@ int vchiq_core_log_level = VCHIQ_LOG_DEFAULT; ...@@ -54,7 +57,6 @@ int vchiq_core_log_level = VCHIQ_LOG_DEFAULT;
int vchiq_core_msg_log_level = VCHIQ_LOG_DEFAULT; int vchiq_core_msg_log_level = VCHIQ_LOG_DEFAULT;
int vchiq_sync_log_level = VCHIQ_LOG_DEFAULT; int vchiq_sync_log_level = VCHIQ_LOG_DEFAULT;
static DEFINE_SPINLOCK(service_spinlock);
DEFINE_SPINLOCK(bulk_waiter_spinlock); DEFINE_SPINLOCK(bulk_waiter_spinlock);
static DEFINE_SPINLOCK(quota_spinlock); static DEFINE_SPINLOCK(quota_spinlock);
...@@ -136,44 +138,41 @@ find_service_by_handle(unsigned int handle) ...@@ -136,44 +138,41 @@ find_service_by_handle(unsigned int handle)
{ {
struct vchiq_service *service; struct vchiq_service *service;
spin_lock(&service_spinlock); rcu_read_lock();
service = handle_to_service(handle); service = handle_to_service(handle);
if (service && service->srvstate != VCHIQ_SRVSTATE_FREE && if (service && service->srvstate != VCHIQ_SRVSTATE_FREE &&
service->handle == handle) { service->handle == handle &&
WARN_ON(service->ref_count == 0); kref_get_unless_zero(&service->ref_count)) {
service->ref_count++; service = rcu_pointer_handoff(service);
} else rcu_read_unlock();
service = NULL; return service;
spin_unlock(&service_spinlock); }
rcu_read_unlock();
if (!service) vchiq_log_info(vchiq_core_log_level,
vchiq_log_info(vchiq_core_log_level, "Invalid service handle 0x%x", handle);
"Invalid service handle 0x%x", handle); return NULL;
return service;
} }
struct vchiq_service * struct vchiq_service *
find_service_by_port(struct vchiq_state *state, int localport) find_service_by_port(struct vchiq_state *state, int localport)
{ {
struct vchiq_service *service = NULL;
if ((unsigned int)localport <= VCHIQ_PORT_MAX) { if ((unsigned int)localport <= VCHIQ_PORT_MAX) {
spin_lock(&service_spinlock); struct vchiq_service *service;
service = state->services[localport];
if (service && service->srvstate != VCHIQ_SRVSTATE_FREE) {
WARN_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 port %d", localport);
return service; rcu_read_lock();
service = rcu_dereference(state->services[localport]);
if (service && service->srvstate != VCHIQ_SRVSTATE_FREE &&
kref_get_unless_zero(&service->ref_count)) {
service = rcu_pointer_handoff(service);
rcu_read_unlock();
return service;
}
rcu_read_unlock();
}
vchiq_log_info(vchiq_core_log_level,
"Invalid port %d", localport);
return NULL;
} }
struct vchiq_service * struct vchiq_service *
...@@ -182,22 +181,20 @@ find_service_for_instance(struct vchiq_instance *instance, ...@@ -182,22 +181,20 @@ find_service_for_instance(struct vchiq_instance *instance,
{ {
struct vchiq_service *service; struct vchiq_service *service;
spin_lock(&service_spinlock); rcu_read_lock();
service = handle_to_service(handle); service = handle_to_service(handle);
if (service && service->srvstate != VCHIQ_SRVSTATE_FREE && if (service && service->srvstate != VCHIQ_SRVSTATE_FREE &&
service->handle == handle && service->handle == handle &&
service->instance == instance) { service->instance == instance &&
WARN_ON(service->ref_count == 0); kref_get_unless_zero(&service->ref_count)) {
service->ref_count++; service = rcu_pointer_handoff(service);
} else rcu_read_unlock();
service = NULL; return service;
spin_unlock(&service_spinlock); }
rcu_read_unlock();
if (!service) vchiq_log_info(vchiq_core_log_level,
vchiq_log_info(vchiq_core_log_level, "Invalid service handle 0x%x", handle);
"Invalid service handle 0x%x", handle); return NULL;
return service;
} }
struct vchiq_service * struct vchiq_service *
...@@ -206,23 +203,21 @@ find_closed_service_for_instance(struct vchiq_instance *instance, ...@@ -206,23 +203,21 @@ find_closed_service_for_instance(struct vchiq_instance *instance,
{ {
struct vchiq_service *service; struct vchiq_service *service;
spin_lock(&service_spinlock); rcu_read_lock();
service = handle_to_service(handle); service = handle_to_service(handle);
if (service && if (service &&
(service->srvstate == VCHIQ_SRVSTATE_FREE || (service->srvstate == VCHIQ_SRVSTATE_FREE ||
service->srvstate == VCHIQ_SRVSTATE_CLOSED) && service->srvstate == VCHIQ_SRVSTATE_CLOSED) &&
service->handle == handle && service->handle == handle &&
service->instance == instance) { service->instance == instance &&
WARN_ON(service->ref_count == 0); kref_get_unless_zero(&service->ref_count)) {
service->ref_count++; service = rcu_pointer_handoff(service);
} else rcu_read_unlock();
service = NULL; return service;
spin_unlock(&service_spinlock); }
rcu_read_unlock();
if (!service) vchiq_log_info(vchiq_core_log_level,
vchiq_log_info(vchiq_core_log_level, "Invalid service handle 0x%x", handle);
"Invalid service handle 0x%x", handle);
return service; return service;
} }
...@@ -233,19 +228,19 @@ next_service_by_instance(struct vchiq_state *state, struct vchiq_instance *insta ...@@ -233,19 +228,19 @@ next_service_by_instance(struct vchiq_state *state, struct vchiq_instance *insta
struct vchiq_service *service = NULL; struct vchiq_service *service = NULL;
int idx = *pidx; int idx = *pidx;
spin_lock(&service_spinlock); rcu_read_lock();
while (idx < state->unused_service) { while (idx < state->unused_service) {
struct vchiq_service *srv = state->services[idx++]; struct vchiq_service *srv;
srv = rcu_dereference(state->services[idx++]);
if (srv && srv->srvstate != VCHIQ_SRVSTATE_FREE && if (srv && srv->srvstate != VCHIQ_SRVSTATE_FREE &&
srv->instance == instance) { srv->instance == instance &&
service = srv; kref_get_unless_zero(&srv->ref_count)) {
WARN_ON(service->ref_count == 0); service = rcu_pointer_handoff(srv);
service->ref_count++;
break; break;
} }
} }
spin_unlock(&service_spinlock); rcu_read_unlock();
*pidx = idx; *pidx = idx;
...@@ -255,43 +250,34 @@ next_service_by_instance(struct vchiq_state *state, struct vchiq_instance *insta ...@@ -255,43 +250,34 @@ next_service_by_instance(struct vchiq_state *state, struct vchiq_instance *insta
void void
lock_service(struct vchiq_service *service) lock_service(struct vchiq_service *service)
{ {
spin_lock(&service_spinlock); if (!service) {
WARN_ON(!service); WARN(1, "%s service is NULL\n", __func__);
if (service) { return;
WARN_ON(service->ref_count == 0);
service->ref_count++;
} }
spin_unlock(&service_spinlock); kref_get(&service->ref_count);
}
static void service_release(struct kref *kref)
{
struct vchiq_service *service =
container_of(kref, struct vchiq_service, ref_count);
struct vchiq_state *state = service->state;
WARN_ON(service->srvstate != VCHIQ_SRVSTATE_FREE);
rcu_assign_pointer(state->services[service->localport], NULL);
if (service->userdata_term)
service->userdata_term(service->base.userdata);
kfree_rcu(service, rcu);
} }
void void
unlock_service(struct vchiq_service *service) unlock_service(struct vchiq_service *service)
{ {
spin_lock(&service_spinlock);
if (!service) { if (!service) {
WARN(1, "%s: service is NULL\n", __func__); WARN(1, "%s: service is NULL\n", __func__);
goto unlock; return;
}
if (!service->ref_count) {
WARN(1, "%s: ref_count is zero\n", __func__);
goto unlock;
}
service->ref_count--;
if (!service->ref_count) {
struct vchiq_state *state = service->state;
WARN_ON(service->srvstate != VCHIQ_SRVSTATE_FREE);
state->services[service->localport] = NULL;
} else {
service = NULL;
} }
unlock: kref_put(&service->ref_count, service_release);
spin_unlock(&service_spinlock);
if (service && service->userdata_term)
service->userdata_term(service->base.userdata);
kfree(service);
} }
int int
...@@ -310,9 +296,14 @@ vchiq_get_client_id(unsigned int handle) ...@@ -310,9 +296,14 @@ vchiq_get_client_id(unsigned int handle)
void * void *
vchiq_get_service_userdata(unsigned int handle) vchiq_get_service_userdata(unsigned int handle)
{ {
struct vchiq_service *service = handle_to_service(handle); void *userdata;
struct vchiq_service *service;
return service ? service->base.userdata : NULL; rcu_read_lock();
service = handle_to_service(handle);
userdata = service ? service->base.userdata : NULL;
rcu_read_unlock();
return userdata;
} }
static void static void
...@@ -460,19 +451,23 @@ get_listening_service(struct vchiq_state *state, int fourcc) ...@@ -460,19 +451,23 @@ get_listening_service(struct vchiq_state *state, int fourcc)
WARN_ON(fourcc == VCHIQ_FOURCC_INVALID); WARN_ON(fourcc == VCHIQ_FOURCC_INVALID);
rcu_read_lock();
for (i = 0; i < state->unused_service; i++) { for (i = 0; i < state->unused_service; i++) {
struct vchiq_service *service = state->services[i]; struct vchiq_service *service;
service = rcu_dereference(state->services[i]);
if (service && if (service &&
service->public_fourcc == fourcc && service->public_fourcc == fourcc &&
(service->srvstate == VCHIQ_SRVSTATE_LISTENING || (service->srvstate == VCHIQ_SRVSTATE_LISTENING ||
(service->srvstate == VCHIQ_SRVSTATE_OPEN && (service->srvstate == VCHIQ_SRVSTATE_OPEN &&
service->remoteport == VCHIQ_PORT_FREE))) { service->remoteport == VCHIQ_PORT_FREE)) &&
lock_service(service); kref_get_unless_zero(&service->ref_count)) {
service = rcu_pointer_handoff(service);
rcu_read_unlock();
return service; return service;
} }
} }
rcu_read_unlock();
return NULL; return NULL;
} }
...@@ -482,15 +477,20 @@ get_connected_service(struct vchiq_state *state, unsigned int port) ...@@ -482,15 +477,20 @@ get_connected_service(struct vchiq_state *state, unsigned int port)
{ {
int i; int i;
rcu_read_lock();
for (i = 0; i < state->unused_service; i++) { for (i = 0; i < state->unused_service; i++) {
struct vchiq_service *service = state->services[i]; struct vchiq_service *service =
rcu_dereference(state->services[i]);
if (service && service->srvstate == VCHIQ_SRVSTATE_OPEN && if (service && service->srvstate == VCHIQ_SRVSTATE_OPEN &&
service->remoteport == port) { service->remoteport == port &&
lock_service(service); kref_get_unless_zero(&service->ref_count)) {
service = rcu_pointer_handoff(service);
rcu_read_unlock();
return service; return service;
} }
} }
rcu_read_unlock();
return NULL; return NULL;
} }
...@@ -2260,7 +2260,7 @@ vchiq_add_service_internal(struct vchiq_state *state, ...@@ -2260,7 +2260,7 @@ vchiq_add_service_internal(struct vchiq_state *state,
vchiq_userdata_term userdata_term) vchiq_userdata_term userdata_term)
{ {
struct vchiq_service *service; struct vchiq_service *service;
struct vchiq_service **pservice = NULL; struct vchiq_service __rcu **pservice = NULL;
struct vchiq_service_quota *service_quota; struct vchiq_service_quota *service_quota;
int i; int i;
...@@ -2272,7 +2272,7 @@ vchiq_add_service_internal(struct vchiq_state *state, ...@@ -2272,7 +2272,7 @@ vchiq_add_service_internal(struct vchiq_state *state,
service->base.callback = params->callback; service->base.callback = params->callback;
service->base.userdata = params->userdata; service->base.userdata = params->userdata;
service->handle = VCHIQ_SERVICE_HANDLE_INVALID; service->handle = VCHIQ_SERVICE_HANDLE_INVALID;
service->ref_count = 1; kref_init(&service->ref_count);
service->srvstate = VCHIQ_SRVSTATE_FREE; service->srvstate = VCHIQ_SRVSTATE_FREE;
service->userdata_term = userdata_term; service->userdata_term = userdata_term;
service->localport = VCHIQ_PORT_FREE; service->localport = VCHIQ_PORT_FREE;
...@@ -2298,7 +2298,7 @@ vchiq_add_service_internal(struct vchiq_state *state, ...@@ -2298,7 +2298,7 @@ vchiq_add_service_internal(struct vchiq_state *state,
mutex_init(&service->bulk_mutex); mutex_init(&service->bulk_mutex);
memset(&service->stats, 0, sizeof(service->stats)); memset(&service->stats, 0, sizeof(service->stats));
/* Although it is perfectly possible to use service_spinlock /* Although it is perfectly possible to use a spinlock
** to protect the creation of services, it is overkill as it ** to protect the creation of services, it is overkill as it
** disables interrupts while the array is searched. ** disables interrupts while the array is searched.
** The only danger is of another thread trying to create a ** The only danger is of another thread trying to create a
...@@ -2316,17 +2316,17 @@ vchiq_add_service_internal(struct vchiq_state *state, ...@@ -2316,17 +2316,17 @@ vchiq_add_service_internal(struct vchiq_state *state,
if (srvstate == VCHIQ_SRVSTATE_OPENING) { if (srvstate == VCHIQ_SRVSTATE_OPENING) {
for (i = 0; i < state->unused_service; i++) { for (i = 0; i < state->unused_service; i++) {
struct vchiq_service *srv = state->services[i]; if (!rcu_access_pointer(state->services[i])) {
if (!srv) {
pservice = &state->services[i]; pservice = &state->services[i];
break; break;
} }
} }
} else { } else {
rcu_read_lock();
for (i = (state->unused_service - 1); i >= 0; i--) { for (i = (state->unused_service - 1); i >= 0; i--) {
struct vchiq_service *srv = state->services[i]; struct vchiq_service *srv;
srv = rcu_dereference(state->services[i]);
if (!srv) if (!srv)
pservice = &state->services[i]; pservice = &state->services[i];
else if ((srv->public_fourcc == params->fourcc) else if ((srv->public_fourcc == params->fourcc)
...@@ -2339,6 +2339,7 @@ vchiq_add_service_internal(struct vchiq_state *state, ...@@ -2339,6 +2339,7 @@ vchiq_add_service_internal(struct vchiq_state *state,
break; break;
} }
} }
rcu_read_unlock();
} }
if (pservice) { if (pservice) {
...@@ -2350,7 +2351,7 @@ vchiq_add_service_internal(struct vchiq_state *state, ...@@ -2350,7 +2351,7 @@ vchiq_add_service_internal(struct vchiq_state *state,
(state->id * VCHIQ_MAX_SERVICES) | (state->id * VCHIQ_MAX_SERVICES) |
service->localport; service->localport;
handle_seq += VCHIQ_MAX_STATES * VCHIQ_MAX_SERVICES; handle_seq += VCHIQ_MAX_STATES * VCHIQ_MAX_SERVICES;
*pservice = service; rcu_assign_pointer(*pservice, service);
if (pservice == &state->services[state->unused_service]) if (pservice == &state->services[state->unused_service])
state->unused_service++; state->unused_service++;
} }
...@@ -2416,10 +2417,10 @@ vchiq_open_service_internal(struct vchiq_service *service, int client_id) ...@@ -2416,10 +2417,10 @@ vchiq_open_service_internal(struct vchiq_service *service, int client_id)
(service->srvstate != VCHIQ_SRVSTATE_OPENSYNC)) { (service->srvstate != VCHIQ_SRVSTATE_OPENSYNC)) {
if (service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) if (service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT)
vchiq_log_error(vchiq_core_log_level, vchiq_log_error(vchiq_core_log_level,
"%d: osi - srvstate = %s (ref %d)", "%d: osi - srvstate = %s (ref %u)",
service->state->id, service->state->id,
srvstate_names[service->srvstate], srvstate_names[service->srvstate],
service->ref_count); kref_read(&service->ref_count));
status = VCHIQ_ERROR; status = VCHIQ_ERROR;
VCHIQ_SERVICE_STATS_INC(service, error_count); VCHIQ_SERVICE_STATS_INC(service, error_count);
vchiq_release_service_internal(service); vchiq_release_service_internal(service);
...@@ -3425,10 +3426,13 @@ int vchiq_dump_service_state(void *dump_context, struct vchiq_service *service) ...@@ -3425,10 +3426,13 @@ int vchiq_dump_service_state(void *dump_context, struct vchiq_service *service)
char buf[80]; char buf[80];
int len; int len;
int err; int err;
unsigned int ref_count;
/*Don't include the lock just taken*/
ref_count = kref_read(&service->ref_count) - 1;
len = scnprintf(buf, sizeof(buf), "Service %u: %s (ref %u)", len = scnprintf(buf, sizeof(buf), "Service %u: %s (ref %u)",
service->localport, srvstate_names[service->srvstate], service->localport, srvstate_names[service->srvstate],
service->ref_count - 1); /*Don't include the lock just taken*/ ref_count);
if (service->srvstate != VCHIQ_SRVSTATE_FREE) { if (service->srvstate != VCHIQ_SRVSTATE_FREE) {
char remoteport[30]; char remoteport[30];
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/kref.h>
#include <linux/rcupdate.h>
#include <linux/wait.h> #include <linux/wait.h>
#include "vchiq_cfg.h" #include "vchiq_cfg.h"
...@@ -251,7 +253,8 @@ struct vchiq_slot_info { ...@@ -251,7 +253,8 @@ struct vchiq_slot_info {
struct vchiq_service { struct vchiq_service {
struct vchiq_service_base base; struct vchiq_service_base base;
unsigned int handle; unsigned int handle;
unsigned int ref_count; struct kref ref_count;
struct rcu_head rcu;
int srvstate; int srvstate;
vchiq_userdata_term userdata_term; vchiq_userdata_term userdata_term;
unsigned int localport; unsigned int localport;
...@@ -464,7 +467,7 @@ struct vchiq_state { ...@@ -464,7 +467,7 @@ struct vchiq_state {
int error_count; int error_count;
} stats; } stats;
struct vchiq_service *services[VCHIQ_MAX_SERVICES]; struct vchiq_service __rcu *services[VCHIQ_MAX_SERVICES];
struct vchiq_service_quota service_quotas[VCHIQ_MAX_SERVICES]; struct vchiq_service_quota service_quotas[VCHIQ_MAX_SERVICES];
struct vchiq_slot_info slot_info[VCHIQ_MAX_SLOTS]; struct vchiq_slot_info slot_info[VCHIQ_MAX_SLOTS];
...@@ -545,12 +548,13 @@ request_poll(struct vchiq_state *state, struct vchiq_service *service, ...@@ -545,12 +548,13 @@ request_poll(struct vchiq_state *state, struct vchiq_service *service,
static inline struct vchiq_service * static inline struct vchiq_service *
handle_to_service(unsigned int handle) handle_to_service(unsigned int handle)
{ {
int idx = handle & (VCHIQ_MAX_SERVICES - 1);
struct vchiq_state *state = vchiq_states[(handle / VCHIQ_MAX_SERVICES) & struct vchiq_state *state = vchiq_states[(handle / VCHIQ_MAX_SERVICES) &
(VCHIQ_MAX_STATES - 1)]; (VCHIQ_MAX_STATES - 1)];
if (!state) if (!state)
return NULL; return NULL;
return rcu_dereference(state->services[idx]);
return state->services[handle & (VCHIQ_MAX_SERVICES - 1)];
} }
extern struct vchiq_service * extern struct vchiq_service *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment