GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / soc / qcom / pdr_interface.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2020 The Linux Foundation. All rights reserved.
4  */
5
6 #include <linux/kernel.h>
7 #include <linux/module.h>
8 #include <linux/slab.h>
9 #include <linux/string.h>
10 #include <linux/workqueue.h>
11
12 #include "pdr_internal.h"
13
14 struct pdr_service {
15         char service_name[SERVREG_NAME_LENGTH + 1];
16         char service_path[SERVREG_NAME_LENGTH + 1];
17
18         struct sockaddr_qrtr addr;
19
20         unsigned int instance;
21         unsigned int service;
22         u8 service_data_valid;
23         u32 service_data;
24         int state;
25
26         bool need_notifier_register;
27         bool need_notifier_remove;
28         bool need_locator_lookup;
29         bool service_connected;
30
31         struct list_head node;
32 };
33
34 struct pdr_handle {
35         struct qmi_handle locator_hdl;
36         struct qmi_handle notifier_hdl;
37
38         struct sockaddr_qrtr locator_addr;
39
40         struct list_head lookups;
41         struct list_head indack_list;
42
43         /* control access to pdr lookup/indack lists */
44         struct mutex list_lock;
45
46         /* serialize pd status invocation */
47         struct mutex status_lock;
48
49         /* control access to the locator state */
50         struct mutex lock;
51
52         bool locator_init_complete;
53
54         struct work_struct locator_work;
55         struct work_struct notifier_work;
56         struct work_struct indack_work;
57
58         struct workqueue_struct *notifier_wq;
59         struct workqueue_struct *indack_wq;
60
61         void (*status)(int state, char *service_path, void *priv);
62         void *priv;
63 };
64
65 struct pdr_list_node {
66         enum servreg_service_state curr_state;
67         u16 transaction_id;
68         struct pdr_service *pds;
69         struct list_head node;
70 };
71
72 static int pdr_locator_new_server(struct qmi_handle *qmi,
73                                   struct qmi_service *svc)
74 {
75         struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
76                                               locator_hdl);
77         struct pdr_service *pds;
78
79         /* Create a local client port for QMI communication */
80         pdr->locator_addr.sq_family = AF_QIPCRTR;
81         pdr->locator_addr.sq_node = svc->node;
82         pdr->locator_addr.sq_port = svc->port;
83
84         mutex_lock(&pdr->lock);
85         pdr->locator_init_complete = true;
86         mutex_unlock(&pdr->lock);
87
88         /* Service pending lookup requests */
89         mutex_lock(&pdr->list_lock);
90         list_for_each_entry(pds, &pdr->lookups, node) {
91                 if (pds->need_locator_lookup)
92                         schedule_work(&pdr->locator_work);
93         }
94         mutex_unlock(&pdr->list_lock);
95
96         return 0;
97 }
98
99 static void pdr_locator_del_server(struct qmi_handle *qmi,
100                                    struct qmi_service *svc)
101 {
102         struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
103                                               locator_hdl);
104
105         mutex_lock(&pdr->lock);
106         pdr->locator_init_complete = false;
107         mutex_unlock(&pdr->lock);
108
109         pdr->locator_addr.sq_node = 0;
110         pdr->locator_addr.sq_port = 0;
111 }
112
113 static const struct qmi_ops pdr_locator_ops = {
114         .new_server = pdr_locator_new_server,
115         .del_server = pdr_locator_del_server,
116 };
117
118 static int pdr_register_listener(struct pdr_handle *pdr,
119                                  struct pdr_service *pds,
120                                  bool enable)
121 {
122         struct servreg_register_listener_resp resp;
123         struct servreg_register_listener_req req;
124         struct qmi_txn txn;
125         int ret;
126
127         ret = qmi_txn_init(&pdr->notifier_hdl, &txn,
128                            servreg_register_listener_resp_ei,
129                            &resp);
130         if (ret < 0)
131                 return ret;
132
133         req.enable = enable;
134         strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
135
136         ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr,
137                                &txn, SERVREG_REGISTER_LISTENER_REQ,
138                                SERVREG_REGISTER_LISTENER_REQ_LEN,
139                                servreg_register_listener_req_ei,
140                                &req);
141         if (ret < 0) {
142                 qmi_txn_cancel(&txn);
143                 return ret;
144         }
145
146         ret = qmi_txn_wait(&txn, 5 * HZ);
147         if (ret < 0) {
148                 pr_err("PDR: %s register listener txn wait failed: %d\n",
149                        pds->service_path, ret);
150                 return ret;
151         }
152
153         if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
154                 pr_err("PDR: %s register listener failed: 0x%x\n",
155                        pds->service_path, resp.resp.error);
156                 return -EREMOTEIO;
157         }
158
159         pds->state = resp.curr_state;
160
161         return 0;
162 }
163
164 static void pdr_notifier_work(struct work_struct *work)
165 {
166         struct pdr_handle *pdr = container_of(work, struct pdr_handle,
167                                               notifier_work);
168         struct pdr_service *pds;
169         int ret;
170
171         mutex_lock(&pdr->list_lock);
172         list_for_each_entry(pds, &pdr->lookups, node) {
173                 if (pds->service_connected) {
174                         if (!pds->need_notifier_register)
175                                 continue;
176
177                         pds->need_notifier_register = false;
178                         ret = pdr_register_listener(pdr, pds, true);
179                         if (ret < 0)
180                                 pds->state = SERVREG_SERVICE_STATE_DOWN;
181                 } else {
182                         if (!pds->need_notifier_remove)
183                                 continue;
184
185                         pds->need_notifier_remove = false;
186                         pds->state = SERVREG_SERVICE_STATE_DOWN;
187                 }
188
189                 mutex_lock(&pdr->status_lock);
190                 pdr->status(pds->state, pds->service_path, pdr->priv);
191                 mutex_unlock(&pdr->status_lock);
192         }
193         mutex_unlock(&pdr->list_lock);
194 }
195
196 static int pdr_notifier_new_server(struct qmi_handle *qmi,
197                                    struct qmi_service *svc)
198 {
199         struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
200                                               notifier_hdl);
201         struct pdr_service *pds;
202
203         mutex_lock(&pdr->list_lock);
204         list_for_each_entry(pds, &pdr->lookups, node) {
205                 if (pds->service == svc->service &&
206                     pds->instance == svc->instance) {
207                         pds->service_connected = true;
208                         pds->need_notifier_register = true;
209                         pds->addr.sq_family = AF_QIPCRTR;
210                         pds->addr.sq_node = svc->node;
211                         pds->addr.sq_port = svc->port;
212                         queue_work(pdr->notifier_wq, &pdr->notifier_work);
213                 }
214         }
215         mutex_unlock(&pdr->list_lock);
216
217         return 0;
218 }
219
220 static void pdr_notifier_del_server(struct qmi_handle *qmi,
221                                     struct qmi_service *svc)
222 {
223         struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
224                                               notifier_hdl);
225         struct pdr_service *pds;
226
227         mutex_lock(&pdr->list_lock);
228         list_for_each_entry(pds, &pdr->lookups, node) {
229                 if (pds->service == svc->service &&
230                     pds->instance == svc->instance) {
231                         pds->service_connected = false;
232                         pds->need_notifier_remove = true;
233                         pds->addr.sq_node = 0;
234                         pds->addr.sq_port = 0;
235                         queue_work(pdr->notifier_wq, &pdr->notifier_work);
236                 }
237         }
238         mutex_unlock(&pdr->list_lock);
239 }
240
241 static const struct qmi_ops pdr_notifier_ops = {
242         .new_server = pdr_notifier_new_server,
243         .del_server = pdr_notifier_del_server,
244 };
245
246 static int pdr_send_indack_msg(struct pdr_handle *pdr, struct pdr_service *pds,
247                                u16 tid)
248 {
249         struct servreg_set_ack_resp resp;
250         struct servreg_set_ack_req req;
251         struct qmi_txn txn;
252         int ret;
253
254         ret = qmi_txn_init(&pdr->notifier_hdl, &txn, servreg_set_ack_resp_ei,
255                            &resp);
256         if (ret < 0)
257                 return ret;
258
259         req.transaction_id = tid;
260         strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
261
262         ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr,
263                                &txn, SERVREG_SET_ACK_REQ,
264                                SERVREG_SET_ACK_REQ_LEN,
265                                servreg_set_ack_req_ei,
266                                &req);
267
268         /* Skip waiting for response */
269         qmi_txn_cancel(&txn);
270         return ret;
271 }
272
273 static void pdr_indack_work(struct work_struct *work)
274 {
275         struct pdr_handle *pdr = container_of(work, struct pdr_handle,
276                                               indack_work);
277         struct pdr_list_node *ind, *tmp;
278         struct pdr_service *pds;
279
280         list_for_each_entry_safe(ind, tmp, &pdr->indack_list, node) {
281                 pds = ind->pds;
282
283                 mutex_lock(&pdr->status_lock);
284                 pds->state = ind->curr_state;
285                 pdr->status(pds->state, pds->service_path, pdr->priv);
286                 mutex_unlock(&pdr->status_lock);
287
288                 /* Ack the indication after clients release the PD resources */
289                 pdr_send_indack_msg(pdr, pds, ind->transaction_id);
290
291                 mutex_lock(&pdr->list_lock);
292                 list_del(&ind->node);
293                 mutex_unlock(&pdr->list_lock);
294
295                 kfree(ind);
296         }
297 }
298
299 static void pdr_indication_cb(struct qmi_handle *qmi,
300                               struct sockaddr_qrtr *sq,
301                               struct qmi_txn *txn, const void *data)
302 {
303         struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
304                                               notifier_hdl);
305         const struct servreg_state_updated_ind *ind_msg = data;
306         struct pdr_list_node *ind;
307         struct pdr_service *pds = NULL, *iter;
308
309         if (!ind_msg || !ind_msg->service_path[0] ||
310             strlen(ind_msg->service_path) > SERVREG_NAME_LENGTH)
311                 return;
312
313         mutex_lock(&pdr->list_lock);
314         list_for_each_entry(iter, &pdr->lookups, node) {
315                 if (strcmp(iter->service_path, ind_msg->service_path))
316                         continue;
317
318                 pds = iter;
319                 break;
320         }
321         mutex_unlock(&pdr->list_lock);
322
323         if (!pds)
324                 return;
325
326         pr_info("PDR: Indication received from %s, state: 0x%x, trans-id: %d\n",
327                 ind_msg->service_path, ind_msg->curr_state,
328                 ind_msg->transaction_id);
329
330         ind = kzalloc(sizeof(*ind), GFP_KERNEL);
331         if (!ind)
332                 return;
333
334         ind->transaction_id = ind_msg->transaction_id;
335         ind->curr_state = ind_msg->curr_state;
336         ind->pds = pds;
337
338         mutex_lock(&pdr->list_lock);
339         list_add_tail(&ind->node, &pdr->indack_list);
340         mutex_unlock(&pdr->list_lock);
341
342         queue_work(pdr->indack_wq, &pdr->indack_work);
343 }
344
345 static const struct qmi_msg_handler qmi_indication_handler[] = {
346         {
347                 .type = QMI_INDICATION,
348                 .msg_id = SERVREG_STATE_UPDATED_IND_ID,
349                 .ei = servreg_state_updated_ind_ei,
350                 .decoded_size = sizeof(struct servreg_state_updated_ind),
351                 .fn = pdr_indication_cb,
352         },
353         {}
354 };
355
356 static int pdr_get_domain_list(struct servreg_get_domain_list_req *req,
357                                struct servreg_get_domain_list_resp *resp,
358                                struct pdr_handle *pdr)
359 {
360         struct qmi_txn txn;
361         int ret;
362
363         ret = qmi_txn_init(&pdr->locator_hdl, &txn,
364                            servreg_get_domain_list_resp_ei, resp);
365         if (ret < 0)
366                 return ret;
367
368         ret = qmi_send_request(&pdr->locator_hdl,
369                                &pdr->locator_addr,
370                                &txn, SERVREG_GET_DOMAIN_LIST_REQ,
371                                SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN,
372                                servreg_get_domain_list_req_ei,
373                                req);
374         if (ret < 0) {
375                 qmi_txn_cancel(&txn);
376                 return ret;
377         }
378
379         ret = qmi_txn_wait(&txn, 5 * HZ);
380         if (ret < 0) {
381                 pr_err("PDR: %s get domain list txn wait failed: %d\n",
382                        req->service_name, ret);
383                 return ret;
384         }
385
386         if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
387                 pr_err("PDR: %s get domain list failed: 0x%x\n",
388                        req->service_name, resp->resp.error);
389                 return -EREMOTEIO;
390         }
391
392         return 0;
393 }
394
395 static int pdr_locate_service(struct pdr_handle *pdr, struct pdr_service *pds)
396 {
397         struct servreg_get_domain_list_resp *resp;
398         struct servreg_get_domain_list_req req;
399         struct servreg_location_entry *entry;
400         int domains_read = 0;
401         int ret, i;
402
403         resp = kzalloc(sizeof(*resp), GFP_KERNEL);
404         if (!resp)
405                 return -ENOMEM;
406
407         /* Prepare req message */
408         strscpy(req.service_name, pds->service_name, sizeof(req.service_name));
409         req.domain_offset_valid = true;
410         req.domain_offset = 0;
411
412         do {
413                 req.domain_offset = domains_read;
414                 ret = pdr_get_domain_list(&req, resp, pdr);
415                 if (ret < 0)
416                         goto out;
417
418                 for (i = domains_read; i < resp->domain_list_len; i++) {
419                         entry = &resp->domain_list[i];
420
421                         if (strnlen(entry->name, sizeof(entry->name)) == sizeof(entry->name))
422                                 continue;
423
424                         if (!strcmp(entry->name, pds->service_path)) {
425                                 pds->service_data_valid = entry->service_data_valid;
426                                 pds->service_data = entry->service_data;
427                                 pds->instance = entry->instance;
428                                 goto out;
429                         }
430                 }
431
432                 /* Update ret to indicate that the service is not yet found */
433                 ret = -ENXIO;
434
435                 /* Always read total_domains from the response msg */
436                 if (resp->domain_list_len > resp->total_domains)
437                         resp->domain_list_len = resp->total_domains;
438
439                 domains_read += resp->domain_list_len;
440         } while (domains_read < resp->total_domains);
441 out:
442         kfree(resp);
443         return ret;
444 }
445
446 static void pdr_notify_lookup_failure(struct pdr_handle *pdr,
447                                       struct pdr_service *pds,
448                                       int err)
449 {
450         pr_err("PDR: service lookup for %s failed: %d\n",
451                pds->service_name, err);
452
453         if (err == -ENXIO)
454                 return;
455
456         list_del(&pds->node);
457         pds->state = SERVREG_LOCATOR_ERR;
458         mutex_lock(&pdr->status_lock);
459         pdr->status(pds->state, pds->service_path, pdr->priv);
460         mutex_unlock(&pdr->status_lock);
461         kfree(pds);
462 }
463
464 static void pdr_locator_work(struct work_struct *work)
465 {
466         struct pdr_handle *pdr = container_of(work, struct pdr_handle,
467                                               locator_work);
468         struct pdr_service *pds, *tmp;
469         int ret = 0;
470
471         /* Bail out early if the SERVREG LOCATOR QMI service is not up */
472         mutex_lock(&pdr->lock);
473         if (!pdr->locator_init_complete) {
474                 mutex_unlock(&pdr->lock);
475                 pr_debug("PDR: SERVICE LOCATOR service not available\n");
476                 return;
477         }
478         mutex_unlock(&pdr->lock);
479
480         mutex_lock(&pdr->list_lock);
481         list_for_each_entry_safe(pds, tmp, &pdr->lookups, node) {
482                 if (!pds->need_locator_lookup)
483                         continue;
484
485                 ret = pdr_locate_service(pdr, pds);
486                 if (ret < 0) {
487                         pdr_notify_lookup_failure(pdr, pds, ret);
488                         continue;
489                 }
490
491                 ret = qmi_add_lookup(&pdr->notifier_hdl, pds->service, 1,
492                                      pds->instance);
493                 if (ret < 0) {
494                         pdr_notify_lookup_failure(pdr, pds, ret);
495                         continue;
496                 }
497
498                 pds->need_locator_lookup = false;
499         }
500         mutex_unlock(&pdr->list_lock);
501 }
502
503 /**
504  * pdr_add_lookup() - register a tracking request for a PD
505  * @pdr:                PDR client handle
506  * @service_name:       service name of the tracking request
507  * @service_path:       service path of the tracking request
508  *
509  * Registering a pdr lookup allows for tracking the life cycle of the PD.
510  *
511  * Return: pdr_service object on success, ERR_PTR on failure. -EALREADY is
512  * returned if a lookup is already in progress for the given service path.
513  */
514 struct pdr_service *pdr_add_lookup(struct pdr_handle *pdr,
515                                    const char *service_name,
516                                    const char *service_path)
517 {
518         struct pdr_service *pds, *tmp;
519         int ret;
520
521         if (IS_ERR_OR_NULL(pdr))
522                 return ERR_PTR(-EINVAL);
523
524         if (!service_name || strlen(service_name) > SERVREG_NAME_LENGTH ||
525             !service_path || strlen(service_path) > SERVREG_NAME_LENGTH)
526                 return ERR_PTR(-EINVAL);
527
528         pds = kzalloc(sizeof(*pds), GFP_KERNEL);
529         if (!pds)
530                 return ERR_PTR(-ENOMEM);
531
532         pds->service = SERVREG_NOTIFIER_SERVICE;
533         strscpy(pds->service_name, service_name, sizeof(pds->service_name));
534         strscpy(pds->service_path, service_path, sizeof(pds->service_path));
535         pds->need_locator_lookup = true;
536
537         mutex_lock(&pdr->list_lock);
538         list_for_each_entry(tmp, &pdr->lookups, node) {
539                 if (strcmp(tmp->service_path, service_path))
540                         continue;
541
542                 mutex_unlock(&pdr->list_lock);
543                 ret = -EALREADY;
544                 goto err;
545         }
546
547         list_add(&pds->node, &pdr->lookups);
548         mutex_unlock(&pdr->list_lock);
549
550         schedule_work(&pdr->locator_work);
551
552         return pds;
553 err:
554         kfree(pds);
555         return ERR_PTR(ret);
556 }
557 EXPORT_SYMBOL(pdr_add_lookup);
558
559 /**
560  * pdr_restart_pd() - restart PD
561  * @pdr:        PDR client handle
562  * @pds:        PD service handle
563  *
564  * Restarts the PD tracked by the PDR client handle for a given service path.
565  *
566  * Return: 0 on success, negative errno on failure.
567  */
568 int pdr_restart_pd(struct pdr_handle *pdr, struct pdr_service *pds)
569 {
570         struct servreg_restart_pd_resp resp;
571         struct servreg_restart_pd_req req = { 0 };
572         struct sockaddr_qrtr addr;
573         struct pdr_service *tmp;
574         struct qmi_txn txn;
575         int ret;
576
577         if (IS_ERR_OR_NULL(pdr) || IS_ERR_OR_NULL(pds))
578                 return -EINVAL;
579
580         mutex_lock(&pdr->list_lock);
581         list_for_each_entry(tmp, &pdr->lookups, node) {
582                 if (tmp != pds)
583                         continue;
584
585                 if (!pds->service_connected)
586                         break;
587
588                 /* Prepare req message */
589                 strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
590                 addr = pds->addr;
591                 break;
592         }
593         mutex_unlock(&pdr->list_lock);
594
595         if (!req.service_path[0])
596                 return -EINVAL;
597
598         ret = qmi_txn_init(&pdr->notifier_hdl, &txn,
599                            servreg_restart_pd_resp_ei,
600                            &resp);
601         if (ret < 0)
602                 return ret;
603
604         ret = qmi_send_request(&pdr->notifier_hdl, &addr,
605                                &txn, SERVREG_RESTART_PD_REQ,
606                                SERVREG_RESTART_PD_REQ_MAX_LEN,
607                                servreg_restart_pd_req_ei, &req);
608         if (ret < 0) {
609                 qmi_txn_cancel(&txn);
610                 return ret;
611         }
612
613         ret = qmi_txn_wait(&txn, 5 * HZ);
614         if (ret < 0) {
615                 pr_err("PDR: %s PD restart txn wait failed: %d\n",
616                        req.service_path, ret);
617                 return ret;
618         }
619
620         /* Check response if PDR is disabled */
621         if (resp.resp.result == QMI_RESULT_FAILURE_V01 &&
622             resp.resp.error == QMI_ERR_DISABLED_V01) {
623                 pr_err("PDR: %s PD restart is disabled: 0x%x\n",
624                        req.service_path, resp.resp.error);
625                 return -EOPNOTSUPP;
626         }
627
628         /* Check the response for other error case*/
629         if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
630                 pr_err("PDR: %s request for PD restart failed: 0x%x\n",
631                        req.service_path, resp.resp.error);
632                 return -EREMOTEIO;
633         }
634
635         return 0;
636 }
637 EXPORT_SYMBOL(pdr_restart_pd);
638
639 /**
640  * pdr_handle_alloc() - initialize the PDR client handle
641  * @status:     function to be called on PD state change
642  * @priv:       handle for client's use
643  *
644  * Initializes the PDR client handle to allow for tracking/restart of PDs.
645  *
646  * Return: pdr_handle object on success, ERR_PTR on failure.
647  */
648 struct pdr_handle *pdr_handle_alloc(void (*status)(int state,
649                                                    char *service_path,
650                                                    void *priv), void *priv)
651 {
652         struct pdr_handle *pdr;
653         int ret;
654
655         if (!status)
656                 return ERR_PTR(-EINVAL);
657
658         pdr = kzalloc(sizeof(*pdr), GFP_KERNEL);
659         if (!pdr)
660                 return ERR_PTR(-ENOMEM);
661
662         pdr->status = status;
663         pdr->priv = priv;
664
665         mutex_init(&pdr->status_lock);
666         mutex_init(&pdr->list_lock);
667         mutex_init(&pdr->lock);
668
669         INIT_LIST_HEAD(&pdr->lookups);
670         INIT_LIST_HEAD(&pdr->indack_list);
671
672         INIT_WORK(&pdr->locator_work, pdr_locator_work);
673         INIT_WORK(&pdr->notifier_work, pdr_notifier_work);
674         INIT_WORK(&pdr->indack_work, pdr_indack_work);
675
676         pdr->notifier_wq = create_singlethread_workqueue("pdr_notifier_wq");
677         if (!pdr->notifier_wq) {
678                 ret = -ENOMEM;
679                 goto free_pdr_handle;
680         }
681
682         pdr->indack_wq = alloc_ordered_workqueue("pdr_indack_wq", WQ_HIGHPRI);
683         if (!pdr->indack_wq) {
684                 ret = -ENOMEM;
685                 goto destroy_notifier;
686         }
687
688         ret = qmi_handle_init(&pdr->locator_hdl,
689                               SERVREG_GET_DOMAIN_LIST_RESP_MAX_LEN,
690                               &pdr_locator_ops, NULL);
691         if (ret < 0)
692                 goto destroy_indack;
693
694         ret = qmi_add_lookup(&pdr->locator_hdl, SERVREG_LOCATOR_SERVICE, 1, 1);
695         if (ret < 0)
696                 goto release_qmi_handle;
697
698         ret = qmi_handle_init(&pdr->notifier_hdl,
699                               SERVREG_STATE_UPDATED_IND_MAX_LEN,
700                               &pdr_notifier_ops,
701                               qmi_indication_handler);
702         if (ret < 0)
703                 goto release_qmi_handle;
704
705         return pdr;
706
707 release_qmi_handle:
708         qmi_handle_release(&pdr->locator_hdl);
709 destroy_indack:
710         destroy_workqueue(pdr->indack_wq);
711 destroy_notifier:
712         destroy_workqueue(pdr->notifier_wq);
713 free_pdr_handle:
714         kfree(pdr);
715
716         return ERR_PTR(ret);
717 }
718 EXPORT_SYMBOL(pdr_handle_alloc);
719
720 /**
721  * pdr_handle_release() - release the PDR client handle
722  * @pdr:        PDR client handle
723  *
724  * Cleans up pending tracking requests and releases the underlying qmi handles.
725  */
726 void pdr_handle_release(struct pdr_handle *pdr)
727 {
728         struct pdr_service *pds, *tmp;
729
730         if (IS_ERR_OR_NULL(pdr))
731                 return;
732
733         mutex_lock(&pdr->list_lock);
734         list_for_each_entry_safe(pds, tmp, &pdr->lookups, node) {
735                 list_del(&pds->node);
736                 kfree(pds);
737         }
738         mutex_unlock(&pdr->list_lock);
739
740         cancel_work_sync(&pdr->locator_work);
741         cancel_work_sync(&pdr->notifier_work);
742         cancel_work_sync(&pdr->indack_work);
743
744         destroy_workqueue(pdr->notifier_wq);
745         destroy_workqueue(pdr->indack_wq);
746
747         qmi_handle_release(&pdr->locator_hdl);
748         qmi_handle_release(&pdr->notifier_hdl);
749
750         kfree(pdr);
751 }
752 EXPORT_SYMBOL(pdr_handle_release);
753
754 MODULE_LICENSE("GPL v2");
755 MODULE_DESCRIPTION("Qualcomm Protection Domain Restart helpers");