Merge tag 'fs.close_range.v5.15' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.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         strcpy(req.service_path, pds->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         strcpy(req.service_path, pds->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;
308         bool found = false;
309
310         if (!ind_msg || !ind_msg->service_path[0] ||
311             strlen(ind_msg->service_path) > SERVREG_NAME_LENGTH)
312                 return;
313
314         mutex_lock(&pdr->list_lock);
315         list_for_each_entry(pds, &pdr->lookups, node) {
316                 if (strcmp(pds->service_path, ind_msg->service_path))
317                         continue;
318
319                 found = true;
320                 break;
321         }
322         mutex_unlock(&pdr->list_lock);
323
324         if (!found)
325                 return;
326
327         pr_info("PDR: Indication received from %s, state: 0x%x, trans-id: %d\n",
328                 ind_msg->service_path, ind_msg->curr_state,
329                 ind_msg->transaction_id);
330
331         ind = kzalloc(sizeof(*ind), GFP_KERNEL);
332         if (!ind)
333                 return;
334
335         ind->transaction_id = ind_msg->transaction_id;
336         ind->curr_state = ind_msg->curr_state;
337         ind->pds = pds;
338
339         mutex_lock(&pdr->list_lock);
340         list_add_tail(&ind->node, &pdr->indack_list);
341         mutex_unlock(&pdr->list_lock);
342
343         queue_work(pdr->indack_wq, &pdr->indack_work);
344 }
345
346 static const struct qmi_msg_handler qmi_indication_handler[] = {
347         {
348                 .type = QMI_INDICATION,
349                 .msg_id = SERVREG_STATE_UPDATED_IND_ID,
350                 .ei = servreg_state_updated_ind_ei,
351                 .decoded_size = sizeof(struct servreg_state_updated_ind),
352                 .fn = pdr_indication_cb,
353         },
354         {}
355 };
356
357 static int pdr_get_domain_list(struct servreg_get_domain_list_req *req,
358                                struct servreg_get_domain_list_resp *resp,
359                                struct pdr_handle *pdr)
360 {
361         struct qmi_txn txn;
362         int ret;
363
364         ret = qmi_txn_init(&pdr->locator_hdl, &txn,
365                            servreg_get_domain_list_resp_ei, resp);
366         if (ret < 0)
367                 return ret;
368
369         ret = qmi_send_request(&pdr->locator_hdl,
370                                &pdr->locator_addr,
371                                &txn, SERVREG_GET_DOMAIN_LIST_REQ,
372                                SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN,
373                                servreg_get_domain_list_req_ei,
374                                req);
375         if (ret < 0) {
376                 qmi_txn_cancel(&txn);
377                 return ret;
378         }
379
380         ret = qmi_txn_wait(&txn, 5 * HZ);
381         if (ret < 0) {
382                 pr_err("PDR: %s get domain list txn wait failed: %d\n",
383                        req->service_name, ret);
384                 return ret;
385         }
386
387         if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
388                 pr_err("PDR: %s get domain list failed: 0x%x\n",
389                        req->service_name, resp->resp.error);
390                 return -EREMOTEIO;
391         }
392
393         return 0;
394 }
395
396 static int pdr_locate_service(struct pdr_handle *pdr, struct pdr_service *pds)
397 {
398         struct servreg_get_domain_list_resp *resp;
399         struct servreg_get_domain_list_req req;
400         struct servreg_location_entry *entry;
401         int domains_read = 0;
402         int ret, i;
403
404         resp = kzalloc(sizeof(*resp), GFP_KERNEL);
405         if (!resp)
406                 return -ENOMEM;
407
408         /* Prepare req message */
409         strcpy(req.service_name, pds->service_name);
410         req.domain_offset_valid = true;
411         req.domain_offset = 0;
412
413         do {
414                 req.domain_offset = domains_read;
415                 ret = pdr_get_domain_list(&req, resp, pdr);
416                 if (ret < 0)
417                         goto out;
418
419                 for (i = domains_read; i < resp->domain_list_len; i++) {
420                         entry = &resp->domain_list[i];
421
422                         if (strnlen(entry->name, sizeof(entry->name)) == sizeof(entry->name))
423                                 continue;
424
425                         if (!strcmp(entry->name, pds->service_path)) {
426                                 pds->service_data_valid = entry->service_data_valid;
427                                 pds->service_data = entry->service_data;
428                                 pds->instance = entry->instance;
429                                 goto out;
430                         }
431                 }
432
433                 /* Update ret to indicate that the service is not yet found */
434                 ret = -ENXIO;
435
436                 /* Always read total_domains from the response msg */
437                 if (resp->domain_list_len > resp->total_domains)
438                         resp->domain_list_len = resp->total_domains;
439
440                 domains_read += resp->domain_list_len;
441         } while (domains_read < resp->total_domains);
442 out:
443         kfree(resp);
444         return ret;
445 }
446
447 static void pdr_notify_lookup_failure(struct pdr_handle *pdr,
448                                       struct pdr_service *pds,
449                                       int err)
450 {
451         pr_err("PDR: service lookup for %s failed: %d\n",
452                pds->service_name, err);
453
454         if (err == -ENXIO)
455                 return;
456
457         list_del(&pds->node);
458         pds->state = SERVREG_LOCATOR_ERR;
459         mutex_lock(&pdr->status_lock);
460         pdr->status(pds->state, pds->service_path, pdr->priv);
461         mutex_unlock(&pdr->status_lock);
462         kfree(pds);
463 }
464
465 static void pdr_locator_work(struct work_struct *work)
466 {
467         struct pdr_handle *pdr = container_of(work, struct pdr_handle,
468                                               locator_work);
469         struct pdr_service *pds, *tmp;
470         int ret = 0;
471
472         /* Bail out early if the SERVREG LOCATOR QMI service is not up */
473         mutex_lock(&pdr->lock);
474         if (!pdr->locator_init_complete) {
475                 mutex_unlock(&pdr->lock);
476                 pr_debug("PDR: SERVICE LOCATOR service not available\n");
477                 return;
478         }
479         mutex_unlock(&pdr->lock);
480
481         mutex_lock(&pdr->list_lock);
482         list_for_each_entry_safe(pds, tmp, &pdr->lookups, node) {
483                 if (!pds->need_locator_lookup)
484                         continue;
485
486                 ret = pdr_locate_service(pdr, pds);
487                 if (ret < 0) {
488                         pdr_notify_lookup_failure(pdr, pds, ret);
489                         continue;
490                 }
491
492                 ret = qmi_add_lookup(&pdr->notifier_hdl, pds->service, 1,
493                                      pds->instance);
494                 if (ret < 0) {
495                         pdr_notify_lookup_failure(pdr, pds, ret);
496                         continue;
497                 }
498
499                 pds->need_locator_lookup = false;
500         }
501         mutex_unlock(&pdr->list_lock);
502 }
503
504 /**
505  * pdr_add_lookup() - register a tracking request for a PD
506  * @pdr:                PDR client handle
507  * @service_name:       service name of the tracking request
508  * @service_path:       service path of the tracking request
509  *
510  * Registering a pdr lookup allows for tracking the life cycle of the PD.
511  *
512  * Return: pdr_service object on success, ERR_PTR on failure. -EALREADY is
513  * returned if a lookup is already in progress for the given service path.
514  */
515 struct pdr_service *pdr_add_lookup(struct pdr_handle *pdr,
516                                    const char *service_name,
517                                    const char *service_path)
518 {
519         struct pdr_service *pds, *tmp;
520         int ret;
521
522         if (IS_ERR_OR_NULL(pdr))
523                 return ERR_PTR(-EINVAL);
524
525         if (!service_name || strlen(service_name) > SERVREG_NAME_LENGTH ||
526             !service_path || strlen(service_path) > SERVREG_NAME_LENGTH)
527                 return ERR_PTR(-EINVAL);
528
529         pds = kzalloc(sizeof(*pds), GFP_KERNEL);
530         if (!pds)
531                 return ERR_PTR(-ENOMEM);
532
533         pds->service = SERVREG_NOTIFIER_SERVICE;
534         strcpy(pds->service_name, service_name);
535         strcpy(pds->service_path, service_path);
536         pds->need_locator_lookup = true;
537
538         mutex_lock(&pdr->list_lock);
539         list_for_each_entry(tmp, &pdr->lookups, node) {
540                 if (strcmp(tmp->service_path, service_path))
541                         continue;
542
543                 mutex_unlock(&pdr->list_lock);
544                 ret = -EALREADY;
545                 goto err;
546         }
547
548         list_add(&pds->node, &pdr->lookups);
549         mutex_unlock(&pdr->list_lock);
550
551         schedule_work(&pdr->locator_work);
552
553         return pds;
554 err:
555         kfree(pds);
556         return ERR_PTR(ret);
557 }
558 EXPORT_SYMBOL(pdr_add_lookup);
559
560 /**
561  * pdr_restart_pd() - restart PD
562  * @pdr:        PDR client handle
563  * @pds:        PD service handle
564  *
565  * Restarts the PD tracked by the PDR client handle for a given service path.
566  *
567  * Return: 0 on success, negative errno on failure.
568  */
569 int pdr_restart_pd(struct pdr_handle *pdr, struct pdr_service *pds)
570 {
571         struct servreg_restart_pd_resp resp;
572         struct servreg_restart_pd_req req = { 0 };
573         struct sockaddr_qrtr addr;
574         struct pdr_service *tmp;
575         struct qmi_txn txn;
576         int ret;
577
578         if (IS_ERR_OR_NULL(pdr) || IS_ERR_OR_NULL(pds))
579                 return -EINVAL;
580
581         mutex_lock(&pdr->list_lock);
582         list_for_each_entry(tmp, &pdr->lookups, node) {
583                 if (tmp != pds)
584                         continue;
585
586                 if (!pds->service_connected)
587                         break;
588
589                 /* Prepare req message */
590                 strcpy(req.service_path, pds->service_path);
591                 addr = pds->addr;
592                 break;
593         }
594         mutex_unlock(&pdr->list_lock);
595
596         if (!req.service_path[0])
597                 return -EINVAL;
598
599         ret = qmi_txn_init(&pdr->notifier_hdl, &txn,
600                            servreg_restart_pd_resp_ei,
601                            &resp);
602         if (ret < 0)
603                 return ret;
604
605         ret = qmi_send_request(&pdr->notifier_hdl, &addr,
606                                &txn, SERVREG_RESTART_PD_REQ,
607                                SERVREG_RESTART_PD_REQ_MAX_LEN,
608                                servreg_restart_pd_req_ei, &req);
609         if (ret < 0) {
610                 qmi_txn_cancel(&txn);
611                 return ret;
612         }
613
614         ret = qmi_txn_wait(&txn, 5 * HZ);
615         if (ret < 0) {
616                 pr_err("PDR: %s PD restart txn wait failed: %d\n",
617                        req.service_path, ret);
618                 return ret;
619         }
620
621         /* Check response if PDR is disabled */
622         if (resp.resp.result == QMI_RESULT_FAILURE_V01 &&
623             resp.resp.error == QMI_ERR_DISABLED_V01) {
624                 pr_err("PDR: %s PD restart is disabled: 0x%x\n",
625                        req.service_path, resp.resp.error);
626                 return -EOPNOTSUPP;
627         }
628
629         /* Check the response for other error case*/
630         if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
631                 pr_err("PDR: %s request for PD restart failed: 0x%x\n",
632                        req.service_path, resp.resp.error);
633                 return -EREMOTEIO;
634         }
635
636         return 0;
637 }
638 EXPORT_SYMBOL(pdr_restart_pd);
639
640 /**
641  * pdr_handle_alloc() - initialize the PDR client handle
642  * @status:     function to be called on PD state change
643  * @priv:       handle for client's use
644  *
645  * Initializes the PDR client handle to allow for tracking/restart of PDs.
646  *
647  * Return: pdr_handle object on success, ERR_PTR on failure.
648  */
649 struct pdr_handle *pdr_handle_alloc(void (*status)(int state,
650                                                    char *service_path,
651                                                    void *priv), void *priv)
652 {
653         struct pdr_handle *pdr;
654         int ret;
655
656         if (!status)
657                 return ERR_PTR(-EINVAL);
658
659         pdr = kzalloc(sizeof(*pdr), GFP_KERNEL);
660         if (!pdr)
661                 return ERR_PTR(-ENOMEM);
662
663         pdr->status = status;
664         pdr->priv = priv;
665
666         mutex_init(&pdr->status_lock);
667         mutex_init(&pdr->list_lock);
668         mutex_init(&pdr->lock);
669
670         INIT_LIST_HEAD(&pdr->lookups);
671         INIT_LIST_HEAD(&pdr->indack_list);
672
673         INIT_WORK(&pdr->locator_work, pdr_locator_work);
674         INIT_WORK(&pdr->notifier_work, pdr_notifier_work);
675         INIT_WORK(&pdr->indack_work, pdr_indack_work);
676
677         pdr->notifier_wq = create_singlethread_workqueue("pdr_notifier_wq");
678         if (!pdr->notifier_wq) {
679                 ret = -ENOMEM;
680                 goto free_pdr_handle;
681         }
682
683         pdr->indack_wq = alloc_ordered_workqueue("pdr_indack_wq", WQ_HIGHPRI);
684         if (!pdr->indack_wq) {
685                 ret = -ENOMEM;
686                 goto destroy_notifier;
687         }
688
689         ret = qmi_handle_init(&pdr->locator_hdl,
690                               SERVREG_GET_DOMAIN_LIST_RESP_MAX_LEN,
691                               &pdr_locator_ops, NULL);
692         if (ret < 0)
693                 goto destroy_indack;
694
695         ret = qmi_add_lookup(&pdr->locator_hdl, SERVREG_LOCATOR_SERVICE, 1, 1);
696         if (ret < 0)
697                 goto release_qmi_handle;
698
699         ret = qmi_handle_init(&pdr->notifier_hdl,
700                               SERVREG_STATE_UPDATED_IND_MAX_LEN,
701                               &pdr_notifier_ops,
702                               qmi_indication_handler);
703         if (ret < 0)
704                 goto release_qmi_handle;
705
706         return pdr;
707
708 release_qmi_handle:
709         qmi_handle_release(&pdr->locator_hdl);
710 destroy_indack:
711         destroy_workqueue(pdr->indack_wq);
712 destroy_notifier:
713         destroy_workqueue(pdr->notifier_wq);
714 free_pdr_handle:
715         kfree(pdr);
716
717         return ERR_PTR(ret);
718 }
719 EXPORT_SYMBOL(pdr_handle_alloc);
720
721 /**
722  * pdr_handle_release() - release the PDR client handle
723  * @pdr:        PDR client handle
724  *
725  * Cleans up pending tracking requests and releases the underlying qmi handles.
726  */
727 void pdr_handle_release(struct pdr_handle *pdr)
728 {
729         struct pdr_service *pds, *tmp;
730
731         if (IS_ERR_OR_NULL(pdr))
732                 return;
733
734         mutex_lock(&pdr->list_lock);
735         list_for_each_entry_safe(pds, tmp, &pdr->lookups, node) {
736                 list_del(&pds->node);
737                 kfree(pds);
738         }
739         mutex_unlock(&pdr->list_lock);
740
741         cancel_work_sync(&pdr->locator_work);
742         cancel_work_sync(&pdr->notifier_work);
743         cancel_work_sync(&pdr->indack_work);
744
745         destroy_workqueue(pdr->notifier_wq);
746         destroy_workqueue(pdr->indack_wq);
747
748         qmi_handle_release(&pdr->locator_hdl);
749         qmi_handle_release(&pdr->notifier_hdl);
750
751         kfree(pdr);
752 }
753 EXPORT_SYMBOL(pdr_handle_release);
754
755 MODULE_LICENSE("GPL v2");
756 MODULE_DESCRIPTION("Qualcomm Protection Domain Restart helpers");