linux/drivers/soc/qcom/pdr_interface.c
<<
>>
Prefs
   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
  14struct 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
  34struct 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
  65struct 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
  72static 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
  99static 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
 113static const struct qmi_ops pdr_locator_ops = {
 114        .new_server = pdr_locator_new_server,
 115        .del_server = pdr_locator_del_server,
 116};
 117
 118static 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
 164static 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
 196static 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
 220static 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
 241static const struct qmi_ops pdr_notifier_ops = {
 242        .new_server = pdr_notifier_new_server,
 243        .del_server = pdr_notifier_del_server,
 244};
 245
 246static 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
 273static 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
 299static 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
 346static 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
 357static 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
 396static 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);
 442out:
 443        kfree(resp);
 444        return ret;
 445}
 446
 447static 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
 465static 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 */
 515struct 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;
 554err:
 555        kfree(pds);
 556        return ERR_PTR(ret);
 557}
 558EXPORT_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 */
 569int 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}
 638EXPORT_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 */
 649struct 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
 708release_qmi_handle:
 709        qmi_handle_release(&pdr->locator_hdl);
 710destroy_indack:
 711        destroy_workqueue(pdr->indack_wq);
 712destroy_notifier:
 713        destroy_workqueue(pdr->notifier_wq);
 714free_pdr_handle:
 715        kfree(pdr);
 716
 717        return ERR_PTR(ret);
 718}
 719EXPORT_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 */
 727void 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}
 753EXPORT_SYMBOL(pdr_handle_release);
 754
 755MODULE_LICENSE("GPL v2");
 756MODULE_DESCRIPTION("Qualcomm Protection Domain Restart helpers");
 757