Hi Denis,
I realised last night that I was not freeing the user_data memory.
I would propose to modify your code to cover both discovery and shutdown. Unify
struct discover_data and struct shutdown_data into struct callback_data with a
union for the func. And then add the shutdown callback to the same queue.
It should also be possible to merge your struct discovery into the new struct
callback_data, so that we don't have to allocate two structures for each
callback.
Will I modify your patch with that?
Lukasz
On 28/03/17 20:25, Denis Kenzior wrote:
> There are various device & service discovery tasks that are initiated
> based on a qmi_device object. qmi_device object does not currently
> keep track of these tasks. Unfortunately the qmi_device object can
> go away at any time, and these tasks can become orphaned.
>
> The result of this can lead to crashes. E.g. a discovery task timeout fires
> after the qmi_device object has been destroyed. Since the object is no
> longer valid, any accesses to it will likely result in a SEGFAULT.
>
> This patch attempts to track all discovery tasks on the qmi_device
> object itself, so that they can be cleaned up properly. This patch does
> not handle the qmi_device_shutdown functionality.
> ---
> drivers/qmimodem/qmi.c | 149
> ++++++++++++++++++++++++++++++++++++++++---------
> 1 file changed, 124 insertions(+), 25 deletions(-)
>
> diff --git a/drivers/qmimodem/qmi.c b/drivers/qmimodem/qmi.c
> index 39fbb19..9b80455 100644
> --- a/drivers/qmimodem/qmi.c
> +++ b/drivers/qmimodem/qmi.c
> @@ -43,6 +43,11 @@
> typedef void (*qmi_message_func_t)(uint16_t message, uint16_t length,
> const void *buffer, void *user_data);
>
> +struct discovery {
> + void *discover_data;
> + qmi_destroy_func_t destroy;
> +};
> +
> struct qmi_device {
> int ref_count;
> int fd;
> @@ -53,6 +58,7 @@ struct qmi_device {
> GQueue *req_queue;
> GQueue *control_queue;
> GQueue *service_queue;
> + GQueue *discovery_queue;
> uint8_t next_control_tid;
> uint16_t next_service_tid;
> qmi_debug_func_t debug_func;
> @@ -213,6 +219,24 @@ static gint __request_compare(gconstpointer a,
> gconstpointer b)
> return req->tid - tid;
> }
>
> +static void __discovery_free(gpointer data, gpointer user_data)
> +{
> + struct discovery *d = data;
> + qmi_destroy_func_t destroy = d->destroy;
> +
> + destroy(d->discover_data);
> +}
> +
> +static gint __discovery_compare(gconstpointer a, gconstpointer b)
> +{
> + const struct discovery *d = a;
> +
> + if (d->discover_data == b)
> + return 0;
> +
> + return 1;
> +}
> +
> static void __notify_free(gpointer data, gpointer user_data)
> {
> struct qmi_notify *notify = data;
> @@ -844,6 +868,37 @@ static void read_watch_destroy(gpointer user_data)
> device->read_watch = 0;
> }
>
> +static void __qmi_device_discovery_started(struct qmi_device *device,
> + void *discover_data,
> + qmi_destroy_func_t destroy)
> +{
> + struct discovery *d;
> +
> + d = g_new0(struct discovery, 1);
> + d->discover_data = discover_data;
> + d->destroy = destroy;
> +
> + g_queue_push_tail(device->discovery_queue, d);
> +}
> +
> +static void __qmi_device_discovery_complete(struct qmi_device *device,
> + void *discover_data)
> +{
> + GList *list;
> + struct discovery *d;
> +
> + list = g_queue_find_custom(device->req_queue,
> + discover_data, __discovery_compare);
> + if (!list)
> + return;
> +
> + d = list->data;
> + g_queue_delete_link(device->discovery_queue, list);
> +
> + d->destroy(d->discover_data);
> + __discovery_free(d, NULL);
> +}
> +
> static void service_destroy(gpointer data)
> {
> struct qmi_service *service = data;
> @@ -897,6 +952,7 @@ struct qmi_device *qmi_device_new(int fd)
> device->req_queue = g_queue_new();
> device->control_queue = g_queue_new();
> device->service_queue = g_queue_new();
> + device->discovery_queue = g_queue_new();
>
> device->service_list = g_hash_table_new_full(g_direct_hash,
> g_direct_equal, NULL, service_destroy);
> @@ -933,6 +989,9 @@ void qmi_device_unref(struct qmi_device *device)
> g_queue_foreach(device->req_queue, __request_free, NULL);
> g_queue_free(device->req_queue);
>
> + g_queue_foreach(device->discovery_queue, __discovery_free, NULL);
> + g_queue_free(device->discovery_queue);
> +
> if (device->write_watch > 0)
> g_source_remove(device->write_watch);
>
> @@ -1000,6 +1059,21 @@ struct discover_data {
> guint timeout;
> };
>
> +static void discover_data_free(gpointer user_data)
> +{
> + struct discover_data *data = user_data;
> +
> + if (data->timeout) {
> + g_source_remove(data->timeout);
> + data->timeout = 0;
> + }
> +
> + if (data->destroy)
> + data->destroy(data->user_data);
> +
> + g_free(data);
> +}
> +
> static void discover_callback(uint16_t message, uint16_t length,
> const void *buffer, void *user_data)
> {
> @@ -1013,8 +1087,6 @@ static void discover_callback(uint16_t message,
> uint16_t length,
> uint8_t count;
> unsigned int i;
>
> - g_source_remove(data->timeout);
> -
> count = 0;
> list = NULL;
>
> @@ -1085,10 +1157,7 @@ done:
> if (data->func)
> data->func(count, list, data->user_data);
>
> - if (data->destroy)
> - data->destroy(data->user_data);
> -
> - g_free(data);
> + __qmi_device_discovery_complete(data->device, data);
> }
>
> static gboolean discover_reply(gpointer user_data)
> @@ -1102,10 +1171,7 @@ static gboolean discover_reply(gpointer user_data)
> data->func(device->version_count,
> device->version_list, data->user_data);
>
> - if (data->destroy)
> - data->destroy(data->user_data);
> -
> - g_free(data);
> + __qmi_device_discovery_complete(data->device, data);
>
> return FALSE;
> }
> @@ -1132,7 +1198,9 @@ bool qmi_device_discover(struct qmi_device *device,
> qmi_discover_func_t func,
> data->destroy = destroy;
>
> if (device->version_list) {
> - g_timeout_add_seconds(0, discover_reply, data);
> + data->timeout = g_timeout_add_seconds(0, discover_reply, data);
> + __qmi_device_discovery_started(device, data,
> + discover_data_free);
> return true;
> }
>
> @@ -1153,6 +1221,7 @@ bool qmi_device_discover(struct qmi_device *device,
> qmi_discover_func_t func,
> __request_submit(device, req, hdr->transaction);
>
> data->timeout = g_timeout_add_seconds(5, discover_reply, data);
> + __qmi_device_discovery_started(device, data, discover_data_free);
>
> return true;
> }
> @@ -1714,16 +1783,29 @@ struct service_create_data {
> guint timeout;
> };
>
> -static gboolean service_create_reply(gpointer user_data)
> +static void service_create_data_free(gpointer user_data)
> {
> struct service_create_data *data = user_data;
>
> - data->func(NULL, data->user_data);
> + if (data->timeout) {
> + g_source_remove(data->timeout);
> + data->timeout = 0;
> + }
>
> if (data->destroy)
> data->destroy(data->user_data);
>
> g_free(data);
> +}
> +
> +static gboolean service_create_reply(gpointer user_data)
> +{
> + struct service_create_data *data = user_data;
> +
> + data->timeout = 0;
> + data->func(NULL, data->user_data);
> +
> + __qmi_device_discovery_complete(data->device, data);
>
> return FALSE;
> }
> @@ -1739,8 +1821,6 @@ static void service_create_callback(uint16_t message,
> uint16_t length,
> uint16_t len;
> unsigned int hash_id;
>
> - g_source_remove(data->timeout);
> -
> result_code = tlv_get(buffer, length, 0x02, &len);
> if (!result_code)
> goto done;
> @@ -1782,13 +1862,9 @@ static void service_create_callback(uint16_t message,
> uint16_t length,
>
> done:
> data->func(service, data->user_data);
> -
> qmi_service_unref(service);
>
> - if (data->destroy)
> - data->destroy(data->user_data);
> -
> - g_free(data);
> + __qmi_device_discovery_complete(data->device, data);
> }
>
> static void service_create_discover(uint8_t count,
> @@ -1819,7 +1895,10 @@ static void service_create_discover(uint8_t count,
> if (data->timeout > 0)
> g_source_remove(data->timeout);
>
> - g_timeout_add_seconds(0, service_create_reply, data);
> + data->timeout = g_timeout_add_seconds(0,
> + service_create_reply, data);
> + __qmi_device_discovery_started(device, data,
> + service_create_data_free);
> return;
> }
>
> @@ -1864,6 +1943,8 @@ static bool service_create(struct qmi_device *device,
> bool shared,
>
> done:
> data->timeout = g_timeout_add_seconds(8, service_create_reply, data);
> + __qmi_device_discovery_started(device, data,
> + service_create_data_free);
>
> return true;
> }
> @@ -1883,16 +1964,21 @@ bool qmi_service_create(struct qmi_device *device,
>
> struct service_create_shared_data {
> 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)
> +static void service_create_shared_data_free(gpointer user_data)
> {
> struct service_create_shared_data *data = user_data;
>
> - data->func(data->service, data->user_data);
> + if (data->timeout) {
> + g_source_remove(data->timeout);
> + data->timeout = 0;
> + }
>
> qmi_service_unref(data->service);
>
> @@ -1900,6 +1986,16 @@ static gboolean service_create_shared_reply(gpointer
> user_data)
> data->destroy(data->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);
>
> return FALSE;
> }
> @@ -1927,12 +2023,15 @@ bool qmi_service_create_shared(struct qmi_device
> *device,
> return false;
>
> data->service = qmi_service_ref(service);
> -
> + data->device = device;
> data->func = func;
> data->user_data = user_data;
> data->destroy = destroy;
>
> - g_timeout_add(0, service_create_shared_reply, data);
> + data->timeout = g_timeout_add(0,
> + service_create_shared_reply, data);
> + __qmi_device_discovery_started(device, data,
> + service_create_shared_data_free);
>
> return 0;
> }
>
_______________________________________________
ofono mailing list
[email protected]
https://lists.ofono.org/mailman/listinfo/ofono