summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>2022-09-16 12:31:03 +0300
committerDenis Kenzior <denkenz@gmail.com>2022-09-19 11:44:18 -0500
commit918f8c1c88b2fe970f1574acbb238c829dcdd64b (patch)
treecb80a407685a17703f0123024d23a0e6927fff57
parentbd691e1a02fe5ad756987ad427df618acbbae70b (diff)
downloadofono-918f8c1c88b2fe970f1574acbb238c829dcdd64b.tar.gz
qmimodem: Fix shared service creation logic
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.
-rw-r--r--drivers/qmimodem/qmi.c130
1 files changed, 96 insertions, 34 deletions
diff --git a/drivers/qmimodem/qmi.c b/drivers/qmimodem/qmi.c
index fc4534a2..e66e8d8f 100644
--- a/drivers/qmimodem/qmi.c
+++ b/drivers/qmimodem/qmi.c
@@ -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;
@@ -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;
@@ -1967,12 +1975,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);
@@ -2039,6 +2093,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);
@@ -2052,15 +2108,23 @@ 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 false;
+ 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;
data->type = type;
@@ -2088,19 +2152,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;
@@ -2118,23 +2176,11 @@ static void service_create_shared_data_free(gpointer user_data)
g_free(data);
}
-static gboolean service_create_shared_reply(gpointer user_data)
+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 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)
-{
- struct qmi_service *service;
+ gpointer service;
unsigned int type_val = type;
if (!device || !func)
@@ -2143,8 +2189,14 @@ 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;
@@ -2153,17 +2205,27 @@ bool qmi_service_create_shared(struct qmi_device *device,
return false;
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);