pdr_interface.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741
  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(sizeof(*ind), GFP_KERNEL);
  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(sizeof(*resp),
  324. GFP_KERNEL);
  325. if (!resp)
  326. return -ENOMEM;
  327. /* Prepare req message */
  328. strscpy(req.service_name, pds->service_name, sizeof(req.service_name));
  329. req.domain_offset_valid = true;
  330. req.domain_offset = 0;
  331. do {
  332. req.domain_offset = domains_read;
  333. ret = pdr_get_domain_list(&req, resp, pdr);
  334. if (ret < 0)
  335. return ret;
  336. for (i = 0; i < resp->domain_list_len; i++) {
  337. entry = &resp->domain_list[i];
  338. if (strnlen(entry->name, sizeof(entry->name)) == sizeof(entry->name))
  339. continue;
  340. if (!strcmp(entry->name, pds->service_path)) {
  341. pds->service_data_valid = entry->service_data_valid;
  342. pds->service_data = entry->service_data;
  343. pds->instance = entry->instance;
  344. return 0;
  345. }
  346. }
  347. /* Update ret to indicate that the service is not yet found */
  348. ret = -ENXIO;
  349. /* Always read total_domains from the response msg */
  350. if (resp->domain_list_len > resp->total_domains)
  351. resp->domain_list_len = resp->total_domains;
  352. domains_read += resp->domain_list_len;
  353. } while (domains_read < resp->total_domains);
  354. return ret;
  355. }
  356. static void pdr_notify_lookup_failure(struct pdr_handle *pdr,
  357. struct pdr_service *pds,
  358. int err)
  359. {
  360. pr_err("PDR: service lookup for %s failed: %d\n",
  361. pds->service_name, err);
  362. if (err == -ENXIO)
  363. return;
  364. list_del(&pds->node);
  365. pds->state = SERVREG_LOCATOR_ERR;
  366. mutex_lock(&pdr->status_lock);
  367. pdr->status(pds->state, pds->service_path, pdr->priv);
  368. mutex_unlock(&pdr->status_lock);
  369. kfree(pds);
  370. }
  371. static void pdr_locator_work(struct work_struct *work)
  372. {
  373. struct pdr_handle *pdr = container_of(work, struct pdr_handle,
  374. locator_work);
  375. struct pdr_service *pds, *tmp;
  376. int ret = 0;
  377. /* Bail out early if the SERVREG LOCATOR QMI service is not up */
  378. mutex_lock(&pdr->lock);
  379. if (!pdr->locator_init_complete) {
  380. mutex_unlock(&pdr->lock);
  381. pr_debug("PDR: SERVICE LOCATOR service not available\n");
  382. return;
  383. }
  384. mutex_unlock(&pdr->lock);
  385. mutex_lock(&pdr->list_lock);
  386. list_for_each_entry_safe(pds, tmp, &pdr->lookups, node) {
  387. if (!pds->need_locator_lookup)
  388. continue;
  389. ret = pdr_locate_service(pdr, pds);
  390. if (ret < 0) {
  391. pdr_notify_lookup_failure(pdr, pds, ret);
  392. continue;
  393. }
  394. ret = qmi_add_lookup(&pdr->notifier_hdl, pds->service, 1,
  395. pds->instance);
  396. if (ret < 0) {
  397. pdr_notify_lookup_failure(pdr, pds, ret);
  398. continue;
  399. }
  400. pds->need_locator_lookup = false;
  401. }
  402. mutex_unlock(&pdr->list_lock);
  403. }
  404. /**
  405. * pdr_add_lookup() - register a tracking request for a PD
  406. * @pdr: PDR client handle
  407. * @service_name: service name of the tracking request
  408. * @service_path: service path of the tracking request
  409. *
  410. * Registering a pdr lookup allows for tracking the life cycle of the PD.
  411. *
  412. * Return: pdr_service object on success, ERR_PTR on failure. -EALREADY is
  413. * returned if a lookup is already in progress for the given service path.
  414. */
  415. struct pdr_service *pdr_add_lookup(struct pdr_handle *pdr,
  416. const char *service_name,
  417. const char *service_path)
  418. {
  419. struct pdr_service *tmp;
  420. if (IS_ERR_OR_NULL(pdr))
  421. return ERR_PTR(-EINVAL);
  422. if (!service_name || strlen(service_name) > SERVREG_NAME_LENGTH ||
  423. !service_path || strlen(service_path) > SERVREG_NAME_LENGTH)
  424. return ERR_PTR(-EINVAL);
  425. struct pdr_service *pds __free(kfree) = kzalloc(sizeof(*pds), GFP_KERNEL);
  426. if (!pds)
  427. return ERR_PTR(-ENOMEM);
  428. pds->service = SERVREG_NOTIFIER_SERVICE;
  429. strscpy(pds->service_name, service_name, sizeof(pds->service_name));
  430. strscpy(pds->service_path, service_path, sizeof(pds->service_path));
  431. pds->need_locator_lookup = true;
  432. mutex_lock(&pdr->list_lock);
  433. list_for_each_entry(tmp, &pdr->lookups, node) {
  434. if (strcmp(tmp->service_path, service_path))
  435. continue;
  436. mutex_unlock(&pdr->list_lock);
  437. return ERR_PTR(-EALREADY);
  438. }
  439. list_add(&pds->node, &pdr->lookups);
  440. mutex_unlock(&pdr->list_lock);
  441. schedule_work(&pdr->locator_work);
  442. return_ptr(pds);
  443. }
  444. EXPORT_SYMBOL_GPL(pdr_add_lookup);
  445. /**
  446. * pdr_restart_pd() - restart PD
  447. * @pdr: PDR client handle
  448. * @pds: PD service handle
  449. *
  450. * Restarts the PD tracked by the PDR client handle for a given service path.
  451. *
  452. * Return: 0 on success, negative errno on failure.
  453. */
  454. int pdr_restart_pd(struct pdr_handle *pdr, struct pdr_service *pds)
  455. {
  456. struct servreg_restart_pd_resp resp;
  457. struct servreg_restart_pd_req req = { 0 };
  458. struct sockaddr_qrtr addr;
  459. struct pdr_service *tmp;
  460. struct qmi_txn txn;
  461. int ret;
  462. if (IS_ERR_OR_NULL(pdr) || IS_ERR_OR_NULL(pds))
  463. return -EINVAL;
  464. mutex_lock(&pdr->list_lock);
  465. list_for_each_entry(tmp, &pdr->lookups, node) {
  466. if (tmp != pds)
  467. continue;
  468. if (!pds->service_connected)
  469. break;
  470. /* Prepare req message */
  471. strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
  472. addr = pds->addr;
  473. break;
  474. }
  475. mutex_unlock(&pdr->list_lock);
  476. if (!req.service_path[0])
  477. return -EINVAL;
  478. ret = qmi_txn_init(&pdr->notifier_hdl, &txn,
  479. servreg_restart_pd_resp_ei,
  480. &resp);
  481. if (ret < 0)
  482. return ret;
  483. ret = qmi_send_request(&pdr->notifier_hdl, &addr,
  484. &txn, SERVREG_RESTART_PD_REQ,
  485. SERVREG_RESTART_PD_REQ_MAX_LEN,
  486. servreg_restart_pd_req_ei, &req);
  487. if (ret < 0) {
  488. qmi_txn_cancel(&txn);
  489. return ret;
  490. }
  491. ret = qmi_txn_wait(&txn, 5 * HZ);
  492. if (ret < 0) {
  493. pr_err("PDR: %s PD restart txn wait failed: %d\n",
  494. req.service_path, ret);
  495. return ret;
  496. }
  497. /* Check response if PDR is disabled */
  498. if (resp.resp.result == QMI_RESULT_FAILURE_V01 &&
  499. resp.resp.error == QMI_ERR_DISABLED_V01) {
  500. pr_err("PDR: %s PD restart is disabled: 0x%x\n",
  501. req.service_path, resp.resp.error);
  502. return -EOPNOTSUPP;
  503. }
  504. /* Check the response for other error case*/
  505. if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
  506. pr_err("PDR: %s request for PD restart failed: 0x%x\n",
  507. req.service_path, resp.resp.error);
  508. return -EREMOTEIO;
  509. }
  510. return 0;
  511. }
  512. EXPORT_SYMBOL_GPL(pdr_restart_pd);
  513. /**
  514. * pdr_handle_alloc() - initialize the PDR client handle
  515. * @status: function to be called on PD state change
  516. * @priv: handle for client's use
  517. *
  518. * Initializes the PDR client handle to allow for tracking/restart of PDs.
  519. *
  520. * Return: pdr_handle object on success, ERR_PTR on failure.
  521. */
  522. struct pdr_handle *pdr_handle_alloc(void (*status)(int state,
  523. char *service_path,
  524. void *priv), void *priv)
  525. {
  526. int ret;
  527. if (!status)
  528. return ERR_PTR(-EINVAL);
  529. struct pdr_handle *pdr __free(kfree) = kzalloc(sizeof(*pdr), GFP_KERNEL);
  530. if (!pdr)
  531. return ERR_PTR(-ENOMEM);
  532. pdr->status = status;
  533. pdr->priv = priv;
  534. mutex_init(&pdr->status_lock);
  535. mutex_init(&pdr->list_lock);
  536. mutex_init(&pdr->lock);
  537. INIT_LIST_HEAD(&pdr->lookups);
  538. INIT_LIST_HEAD(&pdr->indack_list);
  539. INIT_WORK(&pdr->locator_work, pdr_locator_work);
  540. INIT_WORK(&pdr->notifier_work, pdr_notifier_work);
  541. INIT_WORK(&pdr->indack_work, pdr_indack_work);
  542. pdr->notifier_wq = create_singlethread_workqueue("pdr_notifier_wq");
  543. if (!pdr->notifier_wq)
  544. return ERR_PTR(-ENOMEM);
  545. pdr->indack_wq = alloc_ordered_workqueue("pdr_indack_wq", WQ_HIGHPRI);
  546. if (!pdr->indack_wq) {
  547. ret = -ENOMEM;
  548. goto destroy_notifier;
  549. }
  550. ret = qmi_handle_init(&pdr->locator_hdl,
  551. SERVREG_GET_DOMAIN_LIST_RESP_MAX_LEN,
  552. &pdr_locator_ops, NULL);
  553. if (ret < 0)
  554. goto destroy_indack;
  555. ret = qmi_add_lookup(&pdr->locator_hdl, SERVREG_LOCATOR_SERVICE, 1, 1);
  556. if (ret < 0)
  557. goto release_qmi_handle;
  558. ret = qmi_handle_init(&pdr->notifier_hdl,
  559. SERVREG_STATE_UPDATED_IND_MAX_LEN,
  560. &pdr_notifier_ops,
  561. qmi_indication_handler);
  562. if (ret < 0)
  563. goto release_qmi_handle;
  564. return_ptr(pdr);
  565. release_qmi_handle:
  566. qmi_handle_release(&pdr->locator_hdl);
  567. destroy_indack:
  568. destroy_workqueue(pdr->indack_wq);
  569. destroy_notifier:
  570. destroy_workqueue(pdr->notifier_wq);
  571. return ERR_PTR(ret);
  572. }
  573. EXPORT_SYMBOL_GPL(pdr_handle_alloc);
  574. /**
  575. * pdr_handle_release() - release the PDR client handle
  576. * @pdr: PDR client handle
  577. *
  578. * Cleans up pending tracking requests and releases the underlying qmi handles.
  579. */
  580. void pdr_handle_release(struct pdr_handle *pdr)
  581. {
  582. struct pdr_service *pds, *tmp;
  583. if (IS_ERR_OR_NULL(pdr))
  584. return;
  585. mutex_lock(&pdr->list_lock);
  586. list_for_each_entry_safe(pds, tmp, &pdr->lookups, node) {
  587. list_del(&pds->node);
  588. kfree(pds);
  589. }
  590. mutex_unlock(&pdr->list_lock);
  591. cancel_work_sync(&pdr->locator_work);
  592. cancel_work_sync(&pdr->notifier_work);
  593. cancel_work_sync(&pdr->indack_work);
  594. destroy_workqueue(pdr->notifier_wq);
  595. destroy_workqueue(pdr->indack_wq);
  596. qmi_handle_release(&pdr->locator_hdl);
  597. qmi_handle_release(&pdr->notifier_hdl);
  598. kfree(pdr);
  599. }
  600. EXPORT_SYMBOL_GPL(pdr_handle_release);
  601. MODULE_LICENSE("GPL v2");
  602. MODULE_DESCRIPTION("Qualcomm Protection Domain Restart helpers");