pdr_interface.c 18 KB

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