pdr_interface.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2020 The Linux Foundation. All rights reserved.
  4. */
  5. #include <linux/cleanup.h>
  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. #include "pdr_internal.h"
  12. struct pdr_service {
  13. char service_name[SERVREG_NAME_LENGTH + 1];
  14. char service_path[SERVREG_NAME_LENGTH + 1];
  15. struct sockaddr_qrtr addr;
  16. unsigned int instance;
  17. unsigned int service;
  18. u8 service_data_valid;
  19. u32 service_data;
  20. int state;
  21. bool need_notifier_register;
  22. bool need_notifier_remove;
  23. bool need_locator_lookup;
  24. bool service_connected;
  25. struct list_head node;
  26. };
  27. struct pdr_handle {
  28. struct qmi_handle locator_hdl;
  29. struct qmi_handle notifier_hdl;
  30. struct sockaddr_qrtr locator_addr;
  31. struct list_head lookups;
  32. struct list_head indack_list;
  33. /* control access to pdr lookup/indack lists */
  34. struct mutex list_lock;
  35. /* serialize pd status invocation */
  36. struct mutex status_lock;
  37. /* control access to the locator state */
  38. struct mutex lock;
  39. bool locator_init_complete;
  40. struct work_struct locator_work;
  41. struct work_struct notifier_work;
  42. struct work_struct indack_work;
  43. struct workqueue_struct *notifier_wq;
  44. struct workqueue_struct *indack_wq;
  45. void (*status)(int state, char *service_path, void *priv);
  46. void *priv;
  47. };
  48. struct pdr_list_node {
  49. enum servreg_service_state curr_state;
  50. u16 transaction_id;
  51. struct pdr_service *pds;
  52. struct list_head node;
  53. };
  54. static int pdr_locator_new_server(struct qmi_handle *qmi,
  55. struct qmi_service *svc)
  56. {
  57. struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
  58. locator_hdl);
  59. mutex_lock(&pdr->lock);
  60. /* Create a local client port for QMI communication */
  61. pdr->locator_addr.sq_family = AF_QIPCRTR;
  62. pdr->locator_addr.sq_node = svc->node;
  63. pdr->locator_addr.sq_port = svc->port;
  64. pdr->locator_init_complete = true;
  65. mutex_unlock(&pdr->lock);
  66. /* Service pending lookup requests */
  67. schedule_work(&pdr->locator_work);
  68. return 0;
  69. }
  70. static void pdr_locator_del_server(struct qmi_handle *qmi,
  71. struct qmi_service *svc)
  72. {
  73. struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
  74. locator_hdl);
  75. mutex_lock(&pdr->lock);
  76. pdr->locator_init_complete = false;
  77. pdr->locator_addr.sq_node = 0;
  78. pdr->locator_addr.sq_port = 0;
  79. mutex_unlock(&pdr->lock);
  80. }
  81. static const struct qmi_ops pdr_locator_ops = {
  82. .new_server = pdr_locator_new_server,
  83. .del_server = pdr_locator_del_server,
  84. };
  85. static int pdr_register_listener(struct pdr_handle *pdr,
  86. struct pdr_service *pds,
  87. bool enable)
  88. {
  89. struct servreg_register_listener_resp resp;
  90. struct servreg_register_listener_req req;
  91. struct qmi_txn txn;
  92. int ret;
  93. ret = qmi_txn_init(&pdr->notifier_hdl, &txn,
  94. servreg_register_listener_resp_ei,
  95. &resp);
  96. if (ret < 0)
  97. return ret;
  98. req.enable = enable;
  99. strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
  100. ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr,
  101. &txn, SERVREG_REGISTER_LISTENER_REQ,
  102. SERVREG_REGISTER_LISTENER_REQ_LEN,
  103. servreg_register_listener_req_ei,
  104. &req);
  105. if (ret < 0) {
  106. qmi_txn_cancel(&txn);
  107. return ret;
  108. }
  109. ret = qmi_txn_wait(&txn, 5 * HZ);
  110. if (ret < 0) {
  111. pr_err("PDR: %s register listener txn wait failed: %d\n",
  112. pds->service_path, ret);
  113. return ret;
  114. }
  115. if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
  116. pr_err("PDR: %s register listener failed: 0x%x\n",
  117. pds->service_path, resp.resp.error);
  118. return -EREMOTEIO;
  119. }
  120. pds->state = resp.curr_state;
  121. return 0;
  122. }
  123. static void pdr_notifier_work(struct work_struct *work)
  124. {
  125. struct pdr_handle *pdr = container_of(work, struct pdr_handle,
  126. notifier_work);
  127. struct pdr_service *pds;
  128. int ret;
  129. mutex_lock(&pdr->list_lock);
  130. list_for_each_entry(pds, &pdr->lookups, node) {
  131. if (pds->service_connected) {
  132. if (!pds->need_notifier_register)
  133. continue;
  134. pds->need_notifier_register = false;
  135. ret = pdr_register_listener(pdr, pds, true);
  136. if (ret < 0)
  137. pds->state = SERVREG_SERVICE_STATE_DOWN;
  138. } else {
  139. if (!pds->need_notifier_remove)
  140. continue;
  141. pds->need_notifier_remove = false;
  142. pds->state = SERVREG_SERVICE_STATE_DOWN;
  143. }
  144. mutex_lock(&pdr->status_lock);
  145. pdr->status(pds->state, pds->service_path, pdr->priv);
  146. mutex_unlock(&pdr->status_lock);
  147. }
  148. mutex_unlock(&pdr->list_lock);
  149. }
  150. static int pdr_notifier_new_server(struct qmi_handle *qmi,
  151. struct qmi_service *svc)
  152. {
  153. struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
  154. notifier_hdl);
  155. struct pdr_service *pds;
  156. mutex_lock(&pdr->list_lock);
  157. list_for_each_entry(pds, &pdr->lookups, node) {
  158. if (pds->service == svc->service &&
  159. pds->instance == svc->instance) {
  160. pds->service_connected = true;
  161. pds->need_notifier_register = true;
  162. pds->addr.sq_family = AF_QIPCRTR;
  163. pds->addr.sq_node = svc->node;
  164. pds->addr.sq_port = svc->port;
  165. queue_work(pdr->notifier_wq, &pdr->notifier_work);
  166. }
  167. }
  168. mutex_unlock(&pdr->list_lock);
  169. return 0;
  170. }
  171. static void pdr_notifier_del_server(struct qmi_handle *qmi,
  172. struct qmi_service *svc)
  173. {
  174. struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
  175. notifier_hdl);
  176. struct pdr_service *pds;
  177. mutex_lock(&pdr->list_lock);
  178. list_for_each_entry(pds, &pdr->lookups, node) {
  179. if (pds->service == svc->service &&
  180. pds->instance == svc->instance) {
  181. pds->service_connected = false;
  182. pds->need_notifier_remove = true;
  183. pds->addr.sq_node = 0;
  184. pds->addr.sq_port = 0;
  185. queue_work(pdr->notifier_wq, &pdr->notifier_work);
  186. }
  187. }
  188. mutex_unlock(&pdr->list_lock);
  189. }
  190. static const struct qmi_ops pdr_notifier_ops = {
  191. .new_server = pdr_notifier_new_server,
  192. .del_server = pdr_notifier_del_server,
  193. };
  194. static int pdr_send_indack_msg(struct pdr_handle *pdr, struct pdr_service *pds,
  195. u16 tid)
  196. {
  197. struct servreg_set_ack_resp resp;
  198. struct servreg_set_ack_req req;
  199. struct qmi_txn txn;
  200. int ret;
  201. ret = qmi_txn_init(&pdr->notifier_hdl, &txn, servreg_set_ack_resp_ei,
  202. &resp);
  203. if (ret < 0)
  204. return ret;
  205. req.transaction_id = tid;
  206. strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
  207. ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr,
  208. &txn, SERVREG_SET_ACK_REQ,
  209. SERVREG_SET_ACK_REQ_LEN,
  210. servreg_set_ack_req_ei,
  211. &req);
  212. /* Skip waiting for response */
  213. qmi_txn_cancel(&txn);
  214. return ret;
  215. }
  216. static void pdr_indack_work(struct work_struct *work)
  217. {
  218. struct pdr_handle *pdr = container_of(work, struct pdr_handle,
  219. indack_work);
  220. struct pdr_list_node *ind, *tmp;
  221. struct pdr_service *pds;
  222. list_for_each_entry_safe(ind, tmp, &pdr->indack_list, node) {
  223. pds = ind->pds;
  224. mutex_lock(&pdr->status_lock);
  225. pds->state = ind->curr_state;
  226. pdr->status(pds->state, pds->service_path, pdr->priv);
  227. mutex_unlock(&pdr->status_lock);
  228. /* Ack the indication after clients release the PD resources */
  229. pdr_send_indack_msg(pdr, pds, ind->transaction_id);
  230. mutex_lock(&pdr->list_lock);
  231. list_del(&ind->node);
  232. mutex_unlock(&pdr->list_lock);
  233. kfree(ind);
  234. }
  235. }
  236. static void pdr_indication_cb(struct qmi_handle *qmi,
  237. struct sockaddr_qrtr *sq,
  238. struct qmi_txn *txn, const void *data)
  239. {
  240. struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
  241. notifier_hdl);
  242. const struct servreg_state_updated_ind *ind_msg = data;
  243. struct pdr_list_node *ind;
  244. struct pdr_service *pds = NULL, *iter;
  245. if (!ind_msg || !ind_msg->service_path[0] ||
  246. strlen(ind_msg->service_path) > SERVREG_NAME_LENGTH)
  247. return;
  248. mutex_lock(&pdr->list_lock);
  249. list_for_each_entry(iter, &pdr->lookups, node) {
  250. if (strcmp(iter->service_path, ind_msg->service_path))
  251. continue;
  252. pds = iter;
  253. break;
  254. }
  255. mutex_unlock(&pdr->list_lock);
  256. if (!pds)
  257. return;
  258. pr_info("PDR: Indication received from %s, state: 0x%x, trans-id: %d\n",
  259. ind_msg->service_path, ind_msg->curr_state,
  260. ind_msg->transaction_id);
  261. ind = kzalloc_obj(*ind);
  262. if (!ind)
  263. return;
  264. ind->transaction_id = ind_msg->transaction_id;
  265. ind->curr_state = ind_msg->curr_state;
  266. ind->pds = pds;
  267. mutex_lock(&pdr->list_lock);
  268. list_add_tail(&ind->node, &pdr->indack_list);
  269. mutex_unlock(&pdr->list_lock);
  270. queue_work(pdr->indack_wq, &pdr->indack_work);
  271. }
  272. static const struct qmi_msg_handler qmi_indication_handler[] = {
  273. {
  274. .type = QMI_INDICATION,
  275. .msg_id = SERVREG_STATE_UPDATED_IND_ID,
  276. .ei = servreg_state_updated_ind_ei,
  277. .decoded_size = sizeof(struct servreg_state_updated_ind),
  278. .fn = pdr_indication_cb,
  279. },
  280. {}
  281. };
  282. static int pdr_get_domain_list(struct servreg_get_domain_list_req *req,
  283. struct servreg_get_domain_list_resp *resp,
  284. struct pdr_handle *pdr)
  285. {
  286. struct qmi_txn txn;
  287. int ret;
  288. ret = qmi_txn_init(&pdr->locator_hdl, &txn,
  289. servreg_get_domain_list_resp_ei, resp);
  290. if (ret < 0)
  291. return ret;
  292. mutex_lock(&pdr->lock);
  293. ret = qmi_send_request(&pdr->locator_hdl,
  294. &pdr->locator_addr,
  295. &txn, SERVREG_GET_DOMAIN_LIST_REQ,
  296. SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN,
  297. servreg_get_domain_list_req_ei,
  298. req);
  299. mutex_unlock(&pdr->lock);
  300. if (ret < 0) {
  301. qmi_txn_cancel(&txn);
  302. return ret;
  303. }
  304. ret = qmi_txn_wait(&txn, 5 * HZ);
  305. if (ret < 0) {
  306. pr_err("PDR: %s get domain list txn wait failed: %d\n",
  307. req->service_name, ret);
  308. return ret;
  309. }
  310. if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
  311. pr_err("PDR: %s get domain list failed: 0x%x\n",
  312. req->service_name, resp->resp.error);
  313. return -EREMOTEIO;
  314. }
  315. return 0;
  316. }
  317. static int pdr_locate_service(struct pdr_handle *pdr, struct pdr_service *pds)
  318. {
  319. struct servreg_get_domain_list_req req;
  320. struct servreg_location_entry *entry;
  321. int domains_read = 0;
  322. int ret, i;
  323. struct servreg_get_domain_list_resp *resp __free(kfree) = kzalloc_obj(*resp);
  324. if (!resp)
  325. return -ENOMEM;
  326. /* Prepare req message */
  327. strscpy(req.service_name, pds->service_name, sizeof(req.service_name));
  328. req.domain_offset_valid = true;
  329. req.domain_offset = 0;
  330. do {
  331. req.domain_offset = domains_read;
  332. ret = pdr_get_domain_list(&req, resp, pdr);
  333. if (ret < 0)
  334. return ret;
  335. for (i = 0; i < resp->domain_list_len; i++) {
  336. entry = &resp->domain_list[i];
  337. if (strnlen(entry->name, sizeof(entry->name)) == sizeof(entry->name))
  338. continue;
  339. if (!strcmp(entry->name, pds->service_path)) {
  340. pds->service_data_valid = entry->service_data_valid;
  341. pds->service_data = entry->service_data;
  342. pds->instance = entry->instance;
  343. return 0;
  344. }
  345. }
  346. /* Update ret to indicate that the service is not yet found */
  347. ret = -ENXIO;
  348. /* Always read total_domains from the response msg */
  349. if (resp->domain_list_len > resp->total_domains)
  350. resp->domain_list_len = resp->total_domains;
  351. domains_read += resp->domain_list_len;
  352. } while (domains_read < resp->total_domains);
  353. return ret;
  354. }
  355. static void pdr_notify_lookup_failure(struct pdr_handle *pdr,
  356. struct pdr_service *pds,
  357. int err)
  358. {
  359. pr_err("PDR: service lookup for %s failed: %d\n",
  360. pds->service_name, err);
  361. if (err == -ENXIO)
  362. return;
  363. list_del(&pds->node);
  364. pds->state = SERVREG_LOCATOR_ERR;
  365. mutex_lock(&pdr->status_lock);
  366. pdr->status(pds->state, pds->service_path, pdr->priv);
  367. mutex_unlock(&pdr->status_lock);
  368. kfree(pds);
  369. }
  370. static void pdr_locator_work(struct work_struct *work)
  371. {
  372. struct pdr_handle *pdr = container_of(work, struct pdr_handle,
  373. locator_work);
  374. struct pdr_service *pds, *tmp;
  375. int ret = 0;
  376. /* Bail out early if the SERVREG LOCATOR QMI service is not up */
  377. mutex_lock(&pdr->lock);
  378. if (!pdr->locator_init_complete) {
  379. mutex_unlock(&pdr->lock);
  380. pr_debug("PDR: SERVICE LOCATOR service not available\n");
  381. return;
  382. }
  383. mutex_unlock(&pdr->lock);
  384. mutex_lock(&pdr->list_lock);
  385. list_for_each_entry_safe(pds, tmp, &pdr->lookups, node) {
  386. if (!pds->need_locator_lookup)
  387. continue;
  388. ret = pdr_locate_service(pdr, pds);
  389. if (ret < 0) {
  390. pdr_notify_lookup_failure(pdr, pds, ret);
  391. continue;
  392. }
  393. ret = qmi_add_lookup(&pdr->notifier_hdl, pds->service, 1,
  394. pds->instance);
  395. if (ret < 0) {
  396. pdr_notify_lookup_failure(pdr, pds, ret);
  397. continue;
  398. }
  399. pds->need_locator_lookup = false;
  400. }
  401. mutex_unlock(&pdr->list_lock);
  402. }
  403. /**
  404. * pdr_add_lookup() - register a tracking request for a PD
  405. * @pdr: PDR client handle
  406. * @service_name: service name of the tracking request
  407. * @service_path: service path of the tracking request
  408. *
  409. * Registering a pdr lookup allows for tracking the life cycle of the PD.
  410. *
  411. * Return: pdr_service object on success, ERR_PTR on failure. -EALREADY is
  412. * returned if a lookup is already in progress for the given service path.
  413. */
  414. struct pdr_service *pdr_add_lookup(struct pdr_handle *pdr,
  415. const char *service_name,
  416. const char *service_path)
  417. {
  418. struct pdr_service *tmp;
  419. if (IS_ERR_OR_NULL(pdr))
  420. return ERR_PTR(-EINVAL);
  421. if (!service_name || strlen(service_name) > SERVREG_NAME_LENGTH ||
  422. !service_path || strlen(service_path) > SERVREG_NAME_LENGTH)
  423. return ERR_PTR(-EINVAL);
  424. struct pdr_service *pds __free(kfree) = kzalloc_obj(*pds);
  425. if (!pds)
  426. return ERR_PTR(-ENOMEM);
  427. pds->service = SERVREG_NOTIFIER_SERVICE;
  428. strscpy(pds->service_name, service_name, sizeof(pds->service_name));
  429. strscpy(pds->service_path, service_path, sizeof(pds->service_path));
  430. pds->need_locator_lookup = true;
  431. mutex_lock(&pdr->list_lock);
  432. list_for_each_entry(tmp, &pdr->lookups, node) {
  433. if (strcmp(tmp->service_path, service_path))
  434. continue;
  435. mutex_unlock(&pdr->list_lock);
  436. return ERR_PTR(-EALREADY);
  437. }
  438. list_add(&pds->node, &pdr->lookups);
  439. mutex_unlock(&pdr->list_lock);
  440. schedule_work(&pdr->locator_work);
  441. return_ptr(pds);
  442. }
  443. EXPORT_SYMBOL_GPL(pdr_add_lookup);
  444. /**
  445. * pdr_restart_pd() - restart PD
  446. * @pdr: PDR client handle
  447. * @pds: PD service handle
  448. *
  449. * Restarts the PD tracked by the PDR client handle for a given service path.
  450. *
  451. * Return: 0 on success, negative errno on failure.
  452. */
  453. int pdr_restart_pd(struct pdr_handle *pdr, struct pdr_service *pds)
  454. {
  455. struct servreg_restart_pd_resp resp;
  456. struct servreg_restart_pd_req req = { 0 };
  457. struct sockaddr_qrtr addr;
  458. struct pdr_service *tmp;
  459. struct qmi_txn txn;
  460. int ret;
  461. if (IS_ERR_OR_NULL(pdr) || IS_ERR_OR_NULL(pds))
  462. return -EINVAL;
  463. mutex_lock(&pdr->list_lock);
  464. list_for_each_entry(tmp, &pdr->lookups, node) {
  465. if (tmp != pds)
  466. continue;
  467. if (!pds->service_connected)
  468. break;
  469. /* Prepare req message */
  470. strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
  471. addr = pds->addr;
  472. break;
  473. }
  474. mutex_unlock(&pdr->list_lock);
  475. if (!req.service_path[0])
  476. return -EINVAL;
  477. ret = qmi_txn_init(&pdr->notifier_hdl, &txn,
  478. servreg_restart_pd_resp_ei,
  479. &resp);
  480. if (ret < 0)
  481. return ret;
  482. ret = qmi_send_request(&pdr->notifier_hdl, &addr,
  483. &txn, SERVREG_RESTART_PD_REQ,
  484. SERVREG_RESTART_PD_REQ_MAX_LEN,
  485. servreg_restart_pd_req_ei, &req);
  486. if (ret < 0) {
  487. qmi_txn_cancel(&txn);
  488. return ret;
  489. }
  490. ret = qmi_txn_wait(&txn, 5 * HZ);
  491. if (ret < 0) {
  492. pr_err("PDR: %s PD restart txn wait failed: %d\n",
  493. req.service_path, ret);
  494. return ret;
  495. }
  496. /* Check response if PDR is disabled */
  497. if (resp.resp.result == QMI_RESULT_FAILURE_V01 &&
  498. resp.resp.error == QMI_ERR_DISABLED_V01) {
  499. pr_err("PDR: %s PD restart is disabled: 0x%x\n",
  500. req.service_path, resp.resp.error);
  501. return -EOPNOTSUPP;
  502. }
  503. /* Check the response for other error case*/
  504. if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
  505. pr_err("PDR: %s request for PD restart failed: 0x%x\n",
  506. req.service_path, resp.resp.error);
  507. return -EREMOTEIO;
  508. }
  509. return 0;
  510. }
  511. EXPORT_SYMBOL_GPL(pdr_restart_pd);
  512. /**
  513. * pdr_handle_alloc() - initialize the PDR client handle
  514. * @status: function to be called on PD state change
  515. * @priv: handle for client's use
  516. *
  517. * Initializes the PDR client handle to allow for tracking/restart of PDs.
  518. *
  519. * Return: pdr_handle object on success, ERR_PTR on failure.
  520. */
  521. struct pdr_handle *pdr_handle_alloc(void (*status)(int state,
  522. char *service_path,
  523. void *priv), void *priv)
  524. {
  525. int ret;
  526. if (!status)
  527. return ERR_PTR(-EINVAL);
  528. struct pdr_handle *pdr __free(kfree) = kzalloc_obj(*pdr);
  529. if (!pdr)
  530. return ERR_PTR(-ENOMEM);
  531. pdr->status = status;
  532. pdr->priv = priv;
  533. mutex_init(&pdr->status_lock);
  534. mutex_init(&pdr->list_lock);
  535. mutex_init(&pdr->lock);
  536. INIT_LIST_HEAD(&pdr->lookups);
  537. INIT_LIST_HEAD(&pdr->indack_list);
  538. INIT_WORK(&pdr->locator_work, pdr_locator_work);
  539. INIT_WORK(&pdr->notifier_work, pdr_notifier_work);
  540. INIT_WORK(&pdr->indack_work, pdr_indack_work);
  541. pdr->notifier_wq = create_singlethread_workqueue("pdr_notifier_wq");
  542. if (!pdr->notifier_wq)
  543. return ERR_PTR(-ENOMEM);
  544. pdr->indack_wq = alloc_ordered_workqueue("pdr_indack_wq", WQ_HIGHPRI);
  545. if (!pdr->indack_wq) {
  546. ret = -ENOMEM;
  547. goto destroy_notifier;
  548. }
  549. ret = qmi_handle_init(&pdr->locator_hdl,
  550. SERVREG_GET_DOMAIN_LIST_RESP_MAX_LEN,
  551. &pdr_locator_ops, NULL);
  552. if (ret < 0)
  553. goto destroy_indack;
  554. ret = qmi_add_lookup(&pdr->locator_hdl, SERVREG_LOCATOR_SERVICE, 1, 1);
  555. if (ret < 0)
  556. goto release_qmi_handle;
  557. ret = qmi_handle_init(&pdr->notifier_hdl,
  558. SERVREG_STATE_UPDATED_IND_MAX_LEN,
  559. &pdr_notifier_ops,
  560. qmi_indication_handler);
  561. if (ret < 0)
  562. goto release_qmi_handle;
  563. return_ptr(pdr);
  564. release_qmi_handle:
  565. qmi_handle_release(&pdr->locator_hdl);
  566. destroy_indack:
  567. destroy_workqueue(pdr->indack_wq);
  568. destroy_notifier:
  569. destroy_workqueue(pdr->notifier_wq);
  570. return ERR_PTR(ret);
  571. }
  572. EXPORT_SYMBOL_GPL(pdr_handle_alloc);
  573. /**
  574. * pdr_handle_release() - release the PDR client handle
  575. * @pdr: PDR client handle
  576. *
  577. * Cleans up pending tracking requests and releases the underlying qmi handles.
  578. */
  579. void pdr_handle_release(struct pdr_handle *pdr)
  580. {
  581. struct pdr_service *pds, *tmp;
  582. if (IS_ERR_OR_NULL(pdr))
  583. return;
  584. mutex_lock(&pdr->list_lock);
  585. list_for_each_entry_safe(pds, tmp, &pdr->lookups, node) {
  586. list_del(&pds->node);
  587. kfree(pds);
  588. }
  589. mutex_unlock(&pdr->list_lock);
  590. cancel_work_sync(&pdr->locator_work);
  591. cancel_work_sync(&pdr->notifier_work);
  592. cancel_work_sync(&pdr->indack_work);
  593. destroy_workqueue(pdr->notifier_wq);
  594. destroy_workqueue(pdr->indack_wq);
  595. qmi_handle_release(&pdr->locator_hdl);
  596. qmi_handle_release(&pdr->notifier_hdl);
  597. kfree(pdr);
  598. }
  599. EXPORT_SYMBOL_GPL(pdr_handle_release);
  600. MODULE_LICENSE("GPL v2");
  601. MODULE_DESCRIPTION("Qualcomm Protection Domain Restart helpers");