diff options
Diffstat (limited to 'net/phonet/socket.c')
-rw-r--r-- | net/phonet/socket.c | 190 |
1 files changed, 189 insertions, 1 deletions
diff --git a/net/phonet/socket.c b/net/phonet/socket.c index 6e9848bf0370..aca8fba099e9 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c | |||
@@ -158,6 +158,7 @@ void pn_sock_unhash(struct sock *sk) | |||
158 | spin_lock_bh(&pnsocks.lock); | 158 | spin_lock_bh(&pnsocks.lock); |
159 | sk_del_node_init(sk); | 159 | sk_del_node_init(sk); |
160 | spin_unlock_bh(&pnsocks.lock); | 160 | spin_unlock_bh(&pnsocks.lock); |
161 | pn_sock_unbind_all_res(sk); | ||
161 | } | 162 | } |
162 | EXPORT_SYMBOL(pn_sock_unhash); | 163 | EXPORT_SYMBOL(pn_sock_unhash); |
163 | 164 | ||
@@ -281,7 +282,9 @@ static unsigned int pn_socket_poll(struct file *file, struct socket *sock, | |||
281 | if (!mask && sk->sk_state == TCP_CLOSE_WAIT) | 282 | if (!mask && sk->sk_state == TCP_CLOSE_WAIT) |
282 | return POLLHUP; | 283 | return POLLHUP; |
283 | 284 | ||
284 | if (sk->sk_state == TCP_ESTABLISHED && atomic_read(&pn->tx_credits)) | 285 | if (sk->sk_state == TCP_ESTABLISHED && |
286 | atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf && | ||
287 | atomic_read(&pn->tx_credits)) | ||
285 | mask |= POLLOUT | POLLWRNORM | POLLWRBAND; | 288 | mask |= POLLOUT | POLLWRNORM | POLLWRBAND; |
286 | 289 | ||
287 | return mask; | 290 | return mask; |
@@ -563,3 +566,188 @@ const struct file_operations pn_sock_seq_fops = { | |||
563 | .release = seq_release_net, | 566 | .release = seq_release_net, |
564 | }; | 567 | }; |
565 | #endif | 568 | #endif |
569 | |||
570 | static struct { | ||
571 | struct sock *sk[256]; | ||
572 | } pnres; | ||
573 | |||
574 | /* | ||
575 | * Find and hold socket based on resource. | ||
576 | */ | ||
577 | struct sock *pn_find_sock_by_res(struct net *net, u8 res) | ||
578 | { | ||
579 | struct sock *sk; | ||
580 | |||
581 | if (!net_eq(net, &init_net)) | ||
582 | return NULL; | ||
583 | |||
584 | rcu_read_lock(); | ||
585 | sk = rcu_dereference(pnres.sk[res]); | ||
586 | if (sk) | ||
587 | sock_hold(sk); | ||
588 | rcu_read_unlock(); | ||
589 | return sk; | ||
590 | } | ||
591 | |||
592 | static DEFINE_MUTEX(resource_mutex); | ||
593 | |||
594 | int pn_sock_bind_res(struct sock *sk, u8 res) | ||
595 | { | ||
596 | int ret = -EADDRINUSE; | ||
597 | |||
598 | if (!net_eq(sock_net(sk), &init_net)) | ||
599 | return -ENOIOCTLCMD; | ||
600 | if (!capable(CAP_SYS_ADMIN)) | ||
601 | return -EPERM; | ||
602 | if (pn_socket_autobind(sk->sk_socket)) | ||
603 | return -EAGAIN; | ||
604 | |||
605 | mutex_lock(&resource_mutex); | ||
606 | if (pnres.sk[res] == NULL) { | ||
607 | sock_hold(sk); | ||
608 | rcu_assign_pointer(pnres.sk[res], sk); | ||
609 | ret = 0; | ||
610 | } | ||
611 | mutex_unlock(&resource_mutex); | ||
612 | return ret; | ||
613 | } | ||
614 | |||
615 | int pn_sock_unbind_res(struct sock *sk, u8 res) | ||
616 | { | ||
617 | int ret = -ENOENT; | ||
618 | |||
619 | if (!capable(CAP_SYS_ADMIN)) | ||
620 | return -EPERM; | ||
621 | |||
622 | mutex_lock(&resource_mutex); | ||
623 | if (pnres.sk[res] == sk) { | ||
624 | rcu_assign_pointer(pnres.sk[res], NULL); | ||
625 | ret = 0; | ||
626 | } | ||
627 | mutex_unlock(&resource_mutex); | ||
628 | |||
629 | if (ret == 0) { | ||
630 | synchronize_rcu(); | ||
631 | sock_put(sk); | ||
632 | } | ||
633 | return ret; | ||
634 | } | ||
635 | |||
636 | void pn_sock_unbind_all_res(struct sock *sk) | ||
637 | { | ||
638 | unsigned res, match = 0; | ||
639 | |||
640 | mutex_lock(&resource_mutex); | ||
641 | for (res = 0; res < 256; res++) { | ||
642 | if (pnres.sk[res] == sk) { | ||
643 | rcu_assign_pointer(pnres.sk[res], NULL); | ||
644 | match++; | ||
645 | } | ||
646 | } | ||
647 | mutex_unlock(&resource_mutex); | ||
648 | |||
649 | if (match == 0) | ||
650 | return; | ||
651 | synchronize_rcu(); | ||
652 | while (match > 0) { | ||
653 | sock_put(sk); | ||
654 | match--; | ||
655 | } | ||
656 | } | ||
657 | |||
658 | #ifdef CONFIG_PROC_FS | ||
659 | static struct sock **pn_res_get_idx(struct seq_file *seq, loff_t pos) | ||
660 | { | ||
661 | struct net *net = seq_file_net(seq); | ||
662 | unsigned i; | ||
663 | |||
664 | if (!net_eq(net, &init_net)) | ||
665 | return NULL; | ||
666 | |||
667 | for (i = 0; i < 256; i++) { | ||
668 | if (pnres.sk[i] == NULL) | ||
669 | continue; | ||
670 | if (!pos) | ||
671 | return pnres.sk + i; | ||
672 | pos--; | ||
673 | } | ||
674 | return NULL; | ||
675 | } | ||
676 | |||
677 | static struct sock **pn_res_get_next(struct seq_file *seq, struct sock **sk) | ||
678 | { | ||
679 | struct net *net = seq_file_net(seq); | ||
680 | unsigned i; | ||
681 | |||
682 | BUG_ON(!net_eq(net, &init_net)); | ||
683 | |||
684 | for (i = (sk - pnres.sk) + 1; i < 256; i++) | ||
685 | if (pnres.sk[i]) | ||
686 | return pnres.sk + i; | ||
687 | return NULL; | ||
688 | } | ||
689 | |||
690 | static void *pn_res_seq_start(struct seq_file *seq, loff_t *pos) | ||
691 | __acquires(resource_mutex) | ||
692 | { | ||
693 | mutex_lock(&resource_mutex); | ||
694 | return *pos ? pn_res_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; | ||
695 | } | ||
696 | |||
697 | static void *pn_res_seq_next(struct seq_file *seq, void *v, loff_t *pos) | ||
698 | { | ||
699 | struct sock **sk; | ||
700 | |||
701 | if (v == SEQ_START_TOKEN) | ||
702 | sk = pn_res_get_idx(seq, 0); | ||
703 | else | ||
704 | sk = pn_res_get_next(seq, v); | ||
705 | (*pos)++; | ||
706 | return sk; | ||
707 | } | ||
708 | |||
709 | static void pn_res_seq_stop(struct seq_file *seq, void *v) | ||
710 | __releases(resource_mutex) | ||
711 | { | ||
712 | mutex_unlock(&resource_mutex); | ||
713 | } | ||
714 | |||
715 | static int pn_res_seq_show(struct seq_file *seq, void *v) | ||
716 | { | ||
717 | int len; | ||
718 | |||
719 | if (v == SEQ_START_TOKEN) | ||
720 | seq_printf(seq, "%s%n", "rs uid inode", &len); | ||
721 | else { | ||
722 | struct sock **psk = v; | ||
723 | struct sock *sk = *psk; | ||
724 | |||
725 | seq_printf(seq, "%02X %5d %lu%n", | ||
726 | (int) (psk - pnres.sk), sock_i_uid(sk), | ||
727 | sock_i_ino(sk), &len); | ||
728 | } | ||
729 | seq_printf(seq, "%*s\n", 63 - len, ""); | ||
730 | return 0; | ||
731 | } | ||
732 | |||
733 | static const struct seq_operations pn_res_seq_ops = { | ||
734 | .start = pn_res_seq_start, | ||
735 | .next = pn_res_seq_next, | ||
736 | .stop = pn_res_seq_stop, | ||
737 | .show = pn_res_seq_show, | ||
738 | }; | ||
739 | |||
740 | static int pn_res_open(struct inode *inode, struct file *file) | ||
741 | { | ||
742 | return seq_open_net(inode, file, &pn_res_seq_ops, | ||
743 | sizeof(struct seq_net_private)); | ||
744 | } | ||
745 | |||
746 | const struct file_operations pn_res_seq_fops = { | ||
747 | .owner = THIS_MODULE, | ||
748 | .open = pn_res_open, | ||
749 | .read = seq_read, | ||
750 | .llseek = seq_lseek, | ||
751 | .release = seq_release_net, | ||
752 | }; | ||
753 | #endif | ||