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