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