xref: /openbmc/linux/drivers/soc/qcom/pdr_interface.c (revision 79f382b9)
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 ret;
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");
757