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