blob: a758a0902c174d829ec1f0dd9adefc4372e2967a [file] [log] [blame]
Chris Lew42ea9612017-10-04 15:58:16 -07001/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
Karthikeyan Ramasubramanian6a116d62016-09-16 16:05:32 -06002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/module.h>
14#include <linux/types.h>
15#include <linux/net.h>
16#include <linux/socket.h>
17#include <linux/errno.h>
18#include <linux/mm.h>
19#include <linux/poll.h>
20#include <linux/fcntl.h>
21#include <linux/gfp.h>
22#include <linux/msm_ipc.h>
23#include <linux/sched.h>
24#include <linux/thread_info.h>
25#include <linux/slab.h>
26#include <linux/kmemleak.h>
27#include <linux/ipc_logging.h>
28#include <linux/string.h>
29#include <linux/atomic.h>
30#include <linux/ipc_router.h>
31
32#include <net/sock.h>
33
34#include "ipc_router_private.h"
35#include "ipc_router_security.h"
36
37#define msm_ipc_sk(sk) ((struct msm_ipc_sock *)(sk))
38#define msm_ipc_sk_port(sk) ((struct msm_ipc_port *)(msm_ipc_sk(sk)->port))
39
40#ifndef SIZE_MAX
41#define SIZE_MAX ((size_t)-1)
42#endif
43
44static int sockets_enabled;
45static struct proto msm_ipc_proto;
46static const struct proto_ops msm_ipc_proto_ops;
47static RAW_NOTIFIER_HEAD(ipcrtr_af_init_chain);
48static DEFINE_MUTEX(ipcrtr_af_init_lock);
49
50static struct sk_buff_head *msm_ipc_router_build_msg(struct msghdr *m,
51 size_t total_len)
52{
53 struct sk_buff_head *msg_head;
54 struct sk_buff *msg;
55 int first = 1;
56 int last = 1;
57 size_t data_size = 0;
58 size_t alloc_size, align_size;
59 void *data;
60 size_t total_copied_size = 0, copied_size;
61
62 if (iov_iter_count(&m->msg_iter) == total_len)
63 data_size = total_len;
64
65 if (!data_size)
66 return NULL;
67 align_size = ALIGN_SIZE(data_size);
68
69 msg_head = kmalloc(sizeof(*msg_head), GFP_KERNEL);
70 if (!msg_head) {
71 IPC_RTR_ERR("%s: cannot allocate skb_head\n", __func__);
72 return NULL;
73 }
74 skb_queue_head_init(msg_head);
75
76 while (total_copied_size < total_len) {
77 alloc_size = data_size;
78 if (first)
79 alloc_size += IPC_ROUTER_HDR_SIZE;
80 if (last)
81 alloc_size += align_size;
82
83 msg = alloc_skb(alloc_size, GFP_KERNEL);
84 if (!msg) {
85 if (alloc_size <= (PAGE_SIZE / 2)) {
86 IPC_RTR_ERR("%s: cannot allocated skb\n",
87 __func__);
88 goto msg_build_failure;
89 }
90 data_size = data_size / 2;
91 last = 0;
92 continue;
93 }
94
95 if (first) {
96 skb_reserve(msg, IPC_ROUTER_HDR_SIZE);
97 first = 0;
98 }
99
100 data = skb_put(msg, data_size);
101 copied_size = copy_from_iter(msg->data, data_size,
102 &m->msg_iter);
103 if (copied_size != data_size) {
104 IPC_RTR_ERR("%s: copy_from_iter failed %zu %zu %zu\n",
105 __func__, alloc_size, data_size,
106 copied_size);
107 kfree_skb(msg);
108 goto msg_build_failure;
109 }
110 skb_queue_tail(msg_head, msg);
111 total_copied_size += data_size;
112 data_size = total_len - total_copied_size;
113 last = 1;
114 }
115 return msg_head;
116
117msg_build_failure:
118 while (!skb_queue_empty(msg_head)) {
119 msg = skb_dequeue(msg_head);
120 kfree_skb(msg);
121 }
122 kfree(msg_head);
123 return NULL;
124}
125
126static int msm_ipc_router_extract_msg(struct msghdr *m,
127 struct rr_packet *pkt)
128{
129 struct sockaddr_msm_ipc *addr;
130 struct rr_header_v1 *hdr;
131 struct sk_buff *temp;
132 union rr_control_msg *ctl_msg;
133 int offset = 0, data_len = 0, copy_len, copied_len;
134
135 if (!m || !pkt) {
136 IPC_RTR_ERR("%s: Invalid pointers passed\n", __func__);
137 return -EINVAL;
138 }
139 addr = (struct sockaddr_msm_ipc *)m->msg_name;
140
141 hdr = &pkt->hdr;
142 if (addr && (hdr->type == IPC_ROUTER_CTRL_CMD_RESUME_TX)) {
143 temp = skb_peek(pkt->pkt_fragment_q);
Chris Lew42ea9612017-10-04 15:58:16 -0700144 if (!temp || !temp->data) {
145 IPC_RTR_ERR("%s: Invalid skb\n", __func__);
146 return -EINVAL;
147 }
Karthikeyan Ramasubramanian6a116d62016-09-16 16:05:32 -0600148 ctl_msg = (union rr_control_msg *)(temp->data);
149 addr->family = AF_MSM_IPC;
150 addr->address.addrtype = MSM_IPC_ADDR_ID;
151 addr->address.addr.port_addr.node_id = ctl_msg->cli.node_id;
152 addr->address.addr.port_addr.port_id = ctl_msg->cli.port_id;
153 m->msg_namelen = sizeof(struct sockaddr_msm_ipc);
154 return offset;
155 }
156 if (addr && (hdr->type == IPC_ROUTER_CTRL_CMD_DATA)) {
157 addr->family = AF_MSM_IPC;
158 addr->address.addrtype = MSM_IPC_ADDR_ID;
159 addr->address.addr.port_addr.node_id = hdr->src_node_id;
160 addr->address.addr.port_addr.port_id = hdr->src_port_id;
161 m->msg_namelen = sizeof(struct sockaddr_msm_ipc);
162 }
163
164 data_len = hdr->size;
165 skb_queue_walk(pkt->pkt_fragment_q, temp) {
166 copy_len = data_len < temp->len ? data_len : temp->len;
167 copied_len = copy_to_iter(temp->data, copy_len, &m->msg_iter);
168 if (copy_len != copied_len) {
169 IPC_RTR_ERR("%s: Copy to user failed\n", __func__);
170 return -EFAULT;
171 }
172 offset += copy_len;
173 data_len -= copy_len;
174 }
175 return offset;
176}
177
178static int msm_ipc_router_create(struct net *net,
179 struct socket *sock,
180 int protocol,
181 int kern)
182{
183 struct sock *sk;
184 struct msm_ipc_port *port_ptr;
185
186 if (unlikely(protocol != 0)) {
187 IPC_RTR_ERR("%s: Protocol not supported\n", __func__);
188 return -EPROTONOSUPPORT;
189 }
190
191 switch (sock->type) {
192 case SOCK_DGRAM:
193 break;
194 default:
195 IPC_RTR_ERR("%s: Protocol type not supported\n", __func__);
196 return -EPROTOTYPE;
197 }
198
199 sk = sk_alloc(net, AF_MSM_IPC, GFP_KERNEL, &msm_ipc_proto, kern);
200 if (!sk) {
201 IPC_RTR_ERR("%s: sk_alloc failed\n", __func__);
202 return -ENOMEM;
203 }
204
205 sock->ops = &msm_ipc_proto_ops;
206 sock_init_data(sock, sk);
207 sk->sk_data_ready = NULL;
208 sk->sk_write_space = ipc_router_dummy_write_space;
209 sk->sk_rcvtimeo = DEFAULT_RCV_TIMEO;
210 sk->sk_sndtimeo = DEFAULT_SND_TIMEO;
211
212 port_ptr = msm_ipc_router_create_raw_port(sk, NULL, NULL);
213 if (!port_ptr) {
214 IPC_RTR_ERR("%s: port_ptr alloc failed\n", __func__);
215 sock_put(sk);
216 sock->sk = NULL;
217 return -ENOMEM;
218 }
219
220 port_ptr->check_send_permissions = msm_ipc_check_send_permissions;
221 msm_ipc_sk(sk)->port = port_ptr;
222 msm_ipc_sk(sk)->default_node_vote_info = NULL;
223
224 return 0;
225}
226
227int msm_ipc_router_bind(struct socket *sock, struct sockaddr *uaddr,
228 int uaddr_len)
229{
230 struct sockaddr_msm_ipc *addr = (struct sockaddr_msm_ipc *)uaddr;
231 struct sock *sk = sock->sk;
232 struct msm_ipc_port *port_ptr;
233 int ret;
234
235 if (!sk)
236 return -EINVAL;
237
238 if (!check_permissions()) {
239 IPC_RTR_ERR("%s: %s Do not have permissions\n",
240 __func__, current->comm);
241 return -EPERM;
242 }
243
244 if (!uaddr_len) {
245 IPC_RTR_ERR("%s: Invalid address length\n", __func__);
246 return -EINVAL;
247 }
248
249 if (addr->family != AF_MSM_IPC) {
250 IPC_RTR_ERR("%s: Address family is incorrect\n", __func__);
251 return -EAFNOSUPPORT;
252 }
253
254 if (addr->address.addrtype != MSM_IPC_ADDR_NAME) {
255 IPC_RTR_ERR("%s: Address type is incorrect\n", __func__);
256 return -EINVAL;
257 }
258
259 port_ptr = msm_ipc_sk_port(sk);
260 if (!port_ptr)
261 return -ENODEV;
262
263 if (!msm_ipc_sk(sk)->default_node_vote_info)
264 msm_ipc_sk(sk)->default_node_vote_info =
265 msm_ipc_load_default_node();
266 lock_sock(sk);
267
268 ret = msm_ipc_router_register_server(port_ptr, &addr->address);
269
270 release_sock(sk);
271 return ret;
272}
273
274static int ipc_router_connect(struct socket *sock, struct sockaddr *uaddr,
275 int uaddr_len, int flags)
276{
277 struct sockaddr_msm_ipc *addr = (struct sockaddr_msm_ipc *)uaddr;
278 struct sock *sk = sock->sk;
279 struct msm_ipc_port *port_ptr;
280 int ret;
281
282 if (!sk)
283 return -EINVAL;
284
285 if (uaddr_len <= 0) {
286 IPC_RTR_ERR("%s: Invalid address length\n", __func__);
287 return -EINVAL;
288 }
289
290 if (!addr) {
291 IPC_RTR_ERR("%s: Invalid address\n", __func__);
292 return -EINVAL;
293 }
294
295 if (addr->family != AF_MSM_IPC) {
296 IPC_RTR_ERR("%s: Address family is incorrect\n", __func__);
297 return -EAFNOSUPPORT;
298 }
299
300 port_ptr = msm_ipc_sk_port(sk);
301 if (!port_ptr)
302 return -ENODEV;
303
304 lock_sock(sk);
305 ret = ipc_router_set_conn(port_ptr, &addr->address);
306 release_sock(sk);
307 return ret;
308}
309
310static int msm_ipc_router_sendmsg(struct socket *sock,
311 struct msghdr *m, size_t total_len)
312{
313 struct sock *sk = sock->sk;
314 struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
315 struct sockaddr_msm_ipc *dest = (struct sockaddr_msm_ipc *)m->msg_name;
316 struct sk_buff_head *msg;
317 int ret;
318 struct msm_ipc_addr dest_addr = {0};
319 long timeout;
320
321 if (dest) {
322 if (m->msg_namelen < sizeof(*dest) ||
323 dest->family != AF_MSM_IPC)
324 return -EINVAL;
325 memcpy(&dest_addr, &dest->address, sizeof(dest_addr));
326 } else {
327 if (port_ptr->conn_status == NOT_CONNECTED)
328 return -EDESTADDRREQ;
329 if (port_ptr->conn_status < CONNECTION_RESET)
330 return -ENETRESET;
331 memcpy(&dest_addr.addr.port_addr, &port_ptr->dest_addr,
332 sizeof(struct msm_ipc_port_addr));
333 dest_addr.addrtype = MSM_IPC_ADDR_ID;
334 }
335
336 if (total_len > MAX_IPC_PKT_SIZE)
337 return -EINVAL;
338
339 lock_sock(sk);
340 timeout = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT);
341 msg = msm_ipc_router_build_msg(m, total_len);
342 if (!msg) {
343 IPC_RTR_ERR("%s: Msg build failure\n", __func__);
344 ret = -ENOMEM;
345 goto out_sendmsg;
346 }
347 kmemleak_not_leak(msg);
348
349 if (port_ptr->type == CLIENT_PORT)
350 wait_for_irsc_completion();
351 ret = msm_ipc_router_send_to(port_ptr, msg, &dest_addr, timeout);
352 if (ret != total_len) {
353 if (ret < 0) {
354 if (ret != -EAGAIN)
355 IPC_RTR_ERR("%s: Send_to failure %d\n",
356 __func__, ret);
357 msm_ipc_router_free_skb(msg);
358 } else if (ret >= 0) {
359 ret = -EFAULT;
360 }
361 }
362
363out_sendmsg:
364 release_sock(sk);
365 return ret;
366}
367
368static int msm_ipc_router_recvmsg(struct socket *sock,
369 struct msghdr *m, size_t buf_len, int flags)
370{
371 struct sock *sk = sock->sk;
372 struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
373 struct rr_packet *pkt;
374 long timeout;
375 int ret;
376
377 lock_sock(sk);
378 if (!buf_len) {
379 if (flags & MSG_PEEK)
380 ret = msm_ipc_router_get_curr_pkt_size(port_ptr);
381 else
382 ret = -EINVAL;
383 release_sock(sk);
384 return ret;
385 }
386 timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
387
388 ret = msm_ipc_router_rx_data_wait(port_ptr, timeout);
389 if (ret) {
390 release_sock(sk);
391 if (ret == -ENOMSG)
392 m->msg_namelen = 0;
393 return ret;
394 }
395
396 ret = msm_ipc_router_read(port_ptr, &pkt, buf_len);
397 if (ret <= 0 || !pkt) {
398 release_sock(sk);
399 return ret;
400 }
401
402 ret = msm_ipc_router_extract_msg(m, pkt);
403 release_pkt(pkt);
404 release_sock(sk);
405 return ret;
406}
407
408static int msm_ipc_router_ioctl(struct socket *sock,
409 unsigned int cmd, unsigned long arg)
410{
411 struct sock *sk = sock->sk;
412 struct msm_ipc_port *port_ptr;
413 struct server_lookup_args server_arg;
414 struct msm_ipc_server_info *srv_info = NULL;
415 unsigned int n;
416 size_t srv_info_sz = 0;
417 int ret;
418
419 if (!sk)
420 return -EINVAL;
421
422 lock_sock(sk);
423 port_ptr = msm_ipc_sk_port(sock->sk);
424 if (!port_ptr) {
425 release_sock(sk);
426 return -EINVAL;
427 }
428
429 switch (cmd) {
430 case IPC_ROUTER_IOCTL_GET_VERSION:
431 n = IPC_ROUTER_V1;
432 ret = put_user(n, (unsigned int *)arg);
433 break;
434
435 case IPC_ROUTER_IOCTL_GET_MTU:
436 n = (MAX_IPC_PKT_SIZE - IPC_ROUTER_HDR_SIZE);
437 ret = put_user(n, (unsigned int *)arg);
438 break;
439
440 case IPC_ROUTER_IOCTL_GET_CURR_PKT_SIZE:
441 ret = msm_ipc_router_get_curr_pkt_size(port_ptr);
442 break;
443
444 case IPC_ROUTER_IOCTL_LOOKUP_SERVER:
445 if (!msm_ipc_sk(sk)->default_node_vote_info)
446 msm_ipc_sk(sk)->default_node_vote_info =
447 msm_ipc_load_default_node();
448
449 ret = copy_from_user(&server_arg, (void *)arg,
450 sizeof(server_arg));
451 if (ret) {
452 ret = -EFAULT;
453 break;
454 }
455
456 if (server_arg.num_entries_in_array < 0) {
457 ret = -EINVAL;
458 break;
459 }
460 if (server_arg.num_entries_in_array) {
461 if (server_arg.num_entries_in_array >
462 (SIZE_MAX / sizeof(*srv_info))) {
463 IPC_RTR_ERR("%s: Integer Overflow %zu * %d\n",
464 __func__, sizeof(*srv_info),
465 server_arg.num_entries_in_array);
466 ret = -EINVAL;
467 break;
468 }
469 srv_info_sz = server_arg.num_entries_in_array *
470 sizeof(*srv_info);
471 srv_info = kmalloc(srv_info_sz, GFP_KERNEL);
472 if (!srv_info) {
473 ret = -ENOMEM;
474 break;
475 }
476 }
477 ret = msm_ipc_router_lookup_server_name
478 (&server_arg.port_name, srv_info,
479 server_arg.num_entries_in_array,
480 server_arg.lookup_mask);
481 if (ret < 0) {
482 IPC_RTR_ERR("%s: Server not found\n", __func__);
483 ret = -ENODEV;
484 kfree(srv_info);
485 break;
486 }
487 server_arg.num_entries_found = ret;
488
489 ret = copy_to_user((void *)arg, &server_arg,
490 sizeof(server_arg));
491
492 n = min(server_arg.num_entries_found,
493 server_arg.num_entries_in_array);
494
495 if (ret == 0 && n) {
496 ret = copy_to_user((void *)(arg + sizeof(server_arg)),
497 srv_info, n * sizeof(*srv_info));
498 }
499
500 if (ret)
501 ret = -EFAULT;
502 kfree(srv_info);
503 break;
504
505 case IPC_ROUTER_IOCTL_BIND_CONTROL_PORT:
506 ret = msm_ipc_router_bind_control_port(port_ptr);
507 break;
508
509 case IPC_ROUTER_IOCTL_CONFIG_SEC_RULES:
510 ret = msm_ipc_config_sec_rules((void *)arg);
511 if (ret != -EPERM)
512 port_ptr->type = IRSC_PORT;
513 break;
514
515 default:
516 ret = -EINVAL;
517 }
518 release_sock(sk);
519 return ret;
520}
521
522static unsigned int msm_ipc_router_poll(struct file *file, struct socket *sock,
523 poll_table *wait)
524{
525 struct sock *sk = sock->sk;
526 struct msm_ipc_port *port_ptr;
527 u32 mask = 0;
528
529 if (!sk)
530 return -EINVAL;
531
532 port_ptr = msm_ipc_sk_port(sk);
533 if (!port_ptr)
534 return -EINVAL;
535
536 poll_wait(file, &port_ptr->port_rx_wait_q, wait);
537
538 if (!list_empty(&port_ptr->port_rx_q))
539 mask |= (POLLRDNORM | POLLIN);
540
541 if (port_ptr->conn_status == CONNECTION_RESET)
542 mask |= (POLLHUP | POLLERR);
543
544 return mask;
545}
546
547static int msm_ipc_router_close(struct socket *sock)
548{
549 struct sock *sk = sock->sk;
Arun Kumar Neelakantam3fc03e02016-09-21 18:34:01 +0530550 struct msm_ipc_port *port_ptr;
Karthikeyan Ramasubramanian6a116d62016-09-16 16:05:32 -0600551 int ret;
552
Arun Kumar Neelakantam3fc03e02016-09-21 18:34:01 +0530553 if (!sk)
554 return -EINVAL;
555
Karthikeyan Ramasubramanian6a116d62016-09-16 16:05:32 -0600556 lock_sock(sk);
Arun Kumar Neelakantam3fc03e02016-09-21 18:34:01 +0530557 port_ptr = msm_ipc_sk_port(sk);
558 if (!port_ptr) {
559 release_sock(sk);
560 return -EINVAL;
561 }
Karthikeyan Ramasubramanian6a116d62016-09-16 16:05:32 -0600562 ret = msm_ipc_router_close_port(port_ptr);
563 msm_ipc_unload_default_node(msm_ipc_sk(sk)->default_node_vote_info);
564 release_sock(sk);
565 sock_put(sk);
566 sock->sk = NULL;
567
568 return ret;
569}
570
571/**
572 * register_ipcrtr_af_init_notifier() - Register for ipc router socket
573 * address family initialization callback
574 * @nb: Notifier block which will be notified when address family is
575 * initialized.
576 *
577 * Return: 0 on success, standard error code otherwise.
578 */
579int register_ipcrtr_af_init_notifier(struct notifier_block *nb)
580{
581 int ret;
582
583 if (!nb)
584 return -EINVAL;
585 mutex_lock(&ipcrtr_af_init_lock);
586 if (sockets_enabled)
587 nb->notifier_call(nb, IPCRTR_AF_INIT, NULL);
588 ret = raw_notifier_chain_register(&ipcrtr_af_init_chain, nb);
589 mutex_unlock(&ipcrtr_af_init_lock);
590 return ret;
591}
592EXPORT_SYMBOL(register_ipcrtr_af_init_notifier);
593
594/**
595 * unregister_ipcrtr_af_init_notifier() - Unregister for ipc router socket
596 * address family initialization callback
597 * @nb: Notifier block which will be notified once address family is
598 * initialized.
599 *
600 * Return: 0 on success, standard error code otherwise.
601 */
602int unregister_ipcrtr_af_init_notifier(struct notifier_block *nb)
603{
604 int ret;
605
606 if (!nb)
607 return -EINVAL;
608 ret = raw_notifier_chain_unregister(&ipcrtr_af_init_chain, nb);
609 return ret;
610}
611EXPORT_SYMBOL(unregister_ipcrtr_af_init_notifier);
612
613static const struct net_proto_family msm_ipc_family_ops = {
614 .owner = THIS_MODULE,
615 .family = AF_MSM_IPC,
616 .create = msm_ipc_router_create
617};
618
619static const struct proto_ops msm_ipc_proto_ops = {
620 .family = AF_MSM_IPC,
621 .owner = THIS_MODULE,
622 .release = msm_ipc_router_close,
623 .bind = msm_ipc_router_bind,
624 .connect = ipc_router_connect,
625 .socketpair = sock_no_socketpair,
626 .accept = sock_no_accept,
627 .getname = sock_no_getname,
628 .poll = msm_ipc_router_poll,
629 .ioctl = msm_ipc_router_ioctl,
630#ifdef CONFIG_COMPAT
631 .compat_ioctl = msm_ipc_router_ioctl,
632#endif
633 .listen = sock_no_listen,
634 .shutdown = sock_no_shutdown,
635 .setsockopt = sock_no_setsockopt,
636 .getsockopt = sock_no_getsockopt,
637#ifdef CONFIG_COMPAT
638 .compat_setsockopt = sock_no_setsockopt,
639 .compat_getsockopt = sock_no_getsockopt,
640#endif
641 .sendmsg = msm_ipc_router_sendmsg,
642 .recvmsg = msm_ipc_router_recvmsg,
643 .mmap = sock_no_mmap,
644 .sendpage = sock_no_sendpage,
645};
646
647static struct proto msm_ipc_proto = {
648 .name = "MSM_IPC",
649 .owner = THIS_MODULE,
650 .obj_size = sizeof(struct msm_ipc_sock),
651};
652
653int msm_ipc_router_init_sockets(void)
654{
655 int ret;
656
657 ret = proto_register(&msm_ipc_proto, 1);
658 if (ret) {
659 IPC_RTR_ERR("%s: Failed to register MSM_IPC protocol type\n",
660 __func__);
661 goto out_init_sockets;
662 }
663
664 ret = sock_register(&msm_ipc_family_ops);
665 if (ret) {
666 IPC_RTR_ERR("%s: Failed to register MSM_IPC socket type\n",
667 __func__);
668 proto_unregister(&msm_ipc_proto);
669 goto out_init_sockets;
670 }
671
672 mutex_lock(&ipcrtr_af_init_lock);
673 sockets_enabled = 1;
674 raw_notifier_call_chain(&ipcrtr_af_init_chain,
675 IPCRTR_AF_INIT, NULL);
676 mutex_unlock(&ipcrtr_af_init_lock);
677out_init_sockets:
678 return ret;
679}
680
681void msm_ipc_router_exit_sockets(void)
682{
683 if (!sockets_enabled)
684 return;
685
686 sock_unregister(msm_ipc_family_ops.family);
687 proto_unregister(&msm_ipc_proto);
688 mutex_lock(&ipcrtr_af_init_lock);
689 sockets_enabled = 0;
690 raw_notifier_call_chain(&ipcrtr_af_init_chain,
691 IPCRTR_AF_DEINIT, NULL);
692 mutex_unlock(&ipcrtr_af_init_lock);
693}