Skip to content

Commit

Permalink
qmimodem: Fix shared service creation logic
Browse files Browse the repository at this point in the history
qmi_service_create_shared() tries to find already created service of the
same type and if it fails to find one, start a creation of a new service.
This creation takes some time, so if while it is not complete, any new
calls to qmi_service_create_shared() will still fail to find a service of
that type and will start creation. This can easily lead to client ids
exhaustion and service creation failures.

Fix that by adding logic that delays responses to any shared service
creation requests after the first one, until that request either fails or
succeeds.

Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
  • Loading branch information
freemangordon committed Sep 5, 2022
1 parent 9a5628a commit 0e64709
Showing 1 changed file with 97 additions and 35 deletions.
132 changes: 97 additions & 35 deletions drivers/qmimodem/qmi.c
Expand Up @@ -260,6 +260,10 @@ static gboolean __service_compare_shared(gpointer key, gpointer value,
struct qmi_service *service = value;
uint8_t type = GPOINTER_TO_UINT(user_data);

/* ignore those that are in process of creation */
if (GPOINTER_TO_UINT(key) & 0x80000000)
return FALSE;

if (service->type == type)
return TRUE;

Expand Down Expand Up @@ -736,6 +740,10 @@ static void service_notify(gpointer key, gpointer value, gpointer user_data)
struct qmi_result *result = user_data;
GList *list;

/* ignore those that are in process of creation */
if (GPOINTER_TO_UINT(key) & 0x80000000)
return;

for (list = g_list_first(service->notify_list); list;
list = g_list_next(list)) {
struct qmi_notify *notify = list->data;
Expand Down Expand Up @@ -1970,12 +1978,58 @@ static void service_create_data_free(gpointer user_data)
g_free(data);
}

struct service_create_shared_data {
struct discovery super;
struct qmi_service *service;
struct qmi_device *device;
qmi_create_func_t func;
void *user_data;
qmi_destroy_func_t destroy;
guint timeout;
};

static gboolean service_create_shared_reply(gpointer user_data)
{
struct service_create_shared_data *data = user_data;

data->timeout = 0;
data->func(data->service, data->user_data);

__qmi_device_discovery_complete(data->device, &data->super);

return FALSE;
}

static void service_create_shared_pending_reply(struct qmi_device *device,
unsigned int type,
struct qmi_service *service)
{
gpointer key = GUINT_TO_POINTER(type | 0x80000000);
GList **shared = g_hash_table_lookup(device->service_list, key);
GList *l;

g_hash_table_steal(device->service_list, key);

for (l = *shared; l; l = l->next) {
struct service_create_shared_data *shared_data = l->data;

shared_data->service = qmi_service_ref(service);
shared_data->timeout = g_timeout_add(
0, service_create_shared_reply, shared_data);
}

g_list_free(*shared);
g_free(shared);
}

static gboolean service_create_reply(gpointer user_data)
{
struct service_create_data *data = user_data;
struct qmi_device *device = data->device;
struct qmi_request *req;

service_create_shared_pending_reply(device, data->type, NULL);

/* remove request from queues */
req = find_control_request(device, data->tid);

Expand Down Expand Up @@ -2042,6 +2096,8 @@ static void service_create_callback(uint16_t message, uint16_t length,
GUINT_TO_POINTER(hash_id), service);

done:
service_create_shared_pending_reply(device, data->type, service);

data->func(service, data->user_data);
qmi_service_unref(service);

Expand All @@ -2055,14 +2111,22 @@ static bool service_create(struct qmi_device *device,
struct service_create_data *data;
unsigned char client_req[] = { 0x01, 0x01, 0x00, type };
struct qmi_request *req;
GList **shared;
unsigned int type_val = type;
int i;

data = g_try_new0(struct service_create_data, 1);
if (!data)
if (!device->version_list)
return false;

if (!device->version_list)
shared = g_try_new0(GList *, 1);
if (!shared)
return NULL;

data = g_try_new0(struct service_create_data, 1);
if (!data) {
g_free(shared);
return false;
}

data->super.destroy = service_create_data_free;
data->device = device;
Expand Down Expand Up @@ -2091,19 +2155,13 @@ static bool service_create(struct qmi_device *device,

__qmi_device_discovery_started(device, &data->super);

/* Mark service creation as pending */
g_hash_table_insert(device->service_list,
GUINT_TO_POINTER(type_val | 0x80000000), shared);

return true;
}

struct service_create_shared_data {
struct discovery super;
struct qmi_service *service;
struct qmi_device *device;
qmi_create_func_t func;
void *user_data;
qmi_destroy_func_t destroy;
guint timeout;
};

static void service_create_shared_data_free(gpointer user_data)
{
struct service_create_shared_data *data = user_data;
Expand All @@ -2121,23 +2179,11 @@ static void service_create_shared_data_free(gpointer user_data)
g_free(data);
}

static gboolean service_create_shared_reply(gpointer user_data)
{
struct service_create_shared_data *data = user_data;

data->timeout = 0;
data->func(data->service, data->user_data);

__qmi_device_discovery_complete(data->device, &data->super);

return FALSE;
}

bool qmi_service_create_shared(struct qmi_device *device,
uint8_t type, qmi_create_func_t func,
void *user_data, qmi_destroy_func_t destroy)
bool qmi_service_create_shared(struct qmi_device *device, uint8_t type,
qmi_create_func_t func, void *user_data,
qmi_destroy_func_t destroy)
{
struct qmi_service *service;
gpointer service;
unsigned int type_val = type;

if (!device || !func)
Expand All @@ -2146,27 +2192,43 @@ bool qmi_service_create_shared(struct qmi_device *device,
if (type == QMI_SERVICE_CONTROL)
return false;

service = g_hash_table_find(device->service_list,
service = g_hash_table_lookup(device->service_list,
GUINT_TO_POINTER(type_val | 0x80000000));
if (!service) {
service = g_hash_table_find(device->service_list,
__service_compare_shared, GUINT_TO_POINTER(type_val));
} else
type_val |= 0x80000000;

if (service) {
struct service_create_shared_data *data;

data = g_try_new0(struct service_create_shared_data, 1);
if (!data)
return false;
return NULL;

data->super.destroy = service_create_shared_data_free;
data->service = qmi_service_ref(service);
data->device = device;
data->func = func;
data->user_data = user_data;
data->destroy = destroy;

data->timeout = g_timeout_add(0,
service_create_shared_reply, data);
if (!data)
return false;

if (!(type_val & 0x80000000)) {
data->service = qmi_service_ref(service);
data->timeout = g_timeout_add(
0, service_create_shared_reply, data);
} else {
GList **l = service;

*l = g_list_prepend(*l, data);
}

__qmi_device_discovery_started(device, &data->super);

return 0;
return true;
}

return service_create(device, type, func, user_data, destroy);
Expand Down

0 comments on commit 0e64709

Please sign in to comment.