diff options
Diffstat (limited to 'net/phonet/socket.c')
-rw-r--r-- | net/phonet/socket.c | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/net/phonet/socket.c b/net/phonet/socket.c index 7c91f739f138..4c29a23e9007 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c | |||
@@ -565,3 +565,91 @@ const struct file_operations pn_sock_seq_fops = { | |||
565 | .release = seq_release_net, | 565 | .release = seq_release_net, |
566 | }; | 566 | }; |
567 | #endif | 567 | #endif |
568 | |||
569 | static struct { | ||
570 | struct sock *sk[256]; | ||
571 | } pnres; | ||
572 | |||
573 | /* | ||
574 | * Find and hold socket based on resource. | ||
575 | */ | ||
576 | struct sock *pn_find_sock_by_res(struct net *net, u8 res) | ||
577 | { | ||
578 | struct sock *sk; | ||
579 | |||
580 | if (!net_eq(net, &init_net)) | ||
581 | return NULL; | ||
582 | |||
583 | rcu_read_lock(); | ||
584 | sk = rcu_dereference(pnres.sk[res]); | ||
585 | if (sk) | ||
586 | sock_hold(sk); | ||
587 | rcu_read_unlock(); | ||
588 | return sk; | ||
589 | } | ||
590 | |||
591 | static DEFINE_MUTEX(resource_mutex); | ||
592 | |||
593 | int pn_sock_bind_res(struct sock *sk, u8 res) | ||
594 | { | ||
595 | int ret = -EADDRINUSE; | ||
596 | |||
597 | if (!net_eq(sock_net(sk), &init_net)) | ||
598 | return -ENOIOCTLCMD; | ||
599 | if (!capable(CAP_SYS_ADMIN)) | ||
600 | return -EPERM; | ||
601 | if (pn_socket_autobind(sk->sk_socket)) | ||
602 | return -EAGAIN; | ||
603 | |||
604 | mutex_lock(&resource_mutex); | ||
605 | if (pnres.sk[res] == NULL) { | ||
606 | sock_hold(sk); | ||
607 | rcu_assign_pointer(pnres.sk[res], sk); | ||
608 | ret = 0; | ||
609 | } | ||
610 | mutex_unlock(&resource_mutex); | ||
611 | return ret; | ||
612 | } | ||
613 | |||
614 | int pn_sock_unbind_res(struct sock *sk, u8 res) | ||
615 | { | ||
616 | int ret = -ENOENT; | ||
617 | |||
618 | if (!capable(CAP_SYS_ADMIN)) | ||
619 | return -EPERM; | ||
620 | |||
621 | mutex_lock(&resource_mutex); | ||
622 | if (pnres.sk[res] == sk) { | ||
623 | rcu_assign_pointer(pnres.sk[res], NULL); | ||
624 | ret = 0; | ||
625 | } | ||
626 | mutex_unlock(&resource_mutex); | ||
627 | |||
628 | if (ret == 0) { | ||
629 | synchronize_rcu(); | ||
630 | sock_put(sk); | ||
631 | } | ||
632 | return ret; | ||
633 | } | ||
634 | |||
635 | void pn_sock_unbind_all_res(struct sock *sk) | ||
636 | { | ||
637 | unsigned res, match = 0; | ||
638 | |||
639 | mutex_lock(&resource_mutex); | ||
640 | for (res = 0; res < 256; res++) { | ||
641 | if (pnres.sk[res] == sk) { | ||
642 | rcu_assign_pointer(pnres.sk[res], NULL); | ||
643 | match++; | ||
644 | } | ||
645 | } | ||
646 | mutex_unlock(&resource_mutex); | ||
647 | |||
648 | if (match == 0) | ||
649 | return; | ||
650 | synchronize_rcu(); | ||
651 | while (match > 0) { | ||
652 | sock_put(sk); | ||
653 | match--; | ||
654 | } | ||
655 | } | ||