aboutsummaryrefslogtreecommitdiffstats
path: root/net/phonet/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/phonet/socket.c')
-rw-r--r--net/phonet/socket.c289
1 files changed, 288 insertions, 1 deletions
diff --git a/net/phonet/socket.c b/net/phonet/socket.c
index 6e9848bf0370..25f746d20c1f 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}
162EXPORT_SYMBOL(pn_sock_unhash); 163EXPORT_SYMBOL(pn_sock_unhash);
163 164
@@ -224,6 +225,101 @@ static int pn_socket_autobind(struct socket *sock)
224 return 0; /* socket was already bound */ 225 return 0; /* socket was already bound */
225} 226}
226 227
228#ifdef CONFIG_PHONET_PIPECTRLR
229static int pn_socket_connect(struct socket *sock, struct sockaddr *addr,
230 int len, int flags)
231{
232 struct sock *sk = sock->sk;
233 struct sockaddr_pn *spn = (struct sockaddr_pn *)addr;
234 long timeo;
235 int err;
236
237 if (len < sizeof(struct sockaddr_pn))
238 return -EINVAL;
239 if (spn->spn_family != AF_PHONET)
240 return -EAFNOSUPPORT;
241
242 lock_sock(sk);
243
244 switch (sock->state) {
245 case SS_UNCONNECTED:
246 sk->sk_state = TCP_CLOSE;
247 break;
248 case SS_CONNECTING:
249 switch (sk->sk_state) {
250 case TCP_SYN_RECV:
251 sock->state = SS_CONNECTED;
252 err = -EISCONN;
253 goto out;
254 case TCP_CLOSE:
255 err = -EALREADY;
256 if (flags & O_NONBLOCK)
257 goto out;
258 goto wait_connect;
259 }
260 break;
261 case SS_CONNECTED:
262 switch (sk->sk_state) {
263 case TCP_SYN_RECV:
264 err = -EISCONN;
265 goto out;
266 case TCP_CLOSE:
267 sock->state = SS_UNCONNECTED;
268 break;
269 }
270 break;
271 case SS_DISCONNECTING:
272 case SS_FREE:
273 break;
274 }
275 sk->sk_state = TCP_CLOSE;
276 sk_stream_kill_queues(sk);
277
278 sock->state = SS_CONNECTING;
279 err = sk->sk_prot->connect(sk, addr, len);
280 if (err < 0) {
281 sock->state = SS_UNCONNECTED;
282 sk->sk_state = TCP_CLOSE;
283 goto out;
284 }
285
286 err = -EINPROGRESS;
287wait_connect:
288 if (sk->sk_state != TCP_SYN_RECV && (flags & O_NONBLOCK))
289 goto out;
290
291 timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
292 release_sock(sk);
293
294 err = -ERESTARTSYS;
295 timeo = wait_event_interruptible_timeout(*sk_sleep(sk),
296 sk->sk_state != TCP_CLOSE,
297 timeo);
298
299 lock_sock(sk);
300 if (timeo < 0)
301 goto out; /* -ERESTARTSYS */
302
303 err = -ETIMEDOUT;
304 if (timeo == 0 && sk->sk_state != TCP_SYN_RECV)
305 goto out;
306
307 if (sk->sk_state != TCP_SYN_RECV) {
308 sock->state = SS_UNCONNECTED;
309 err = sock_error(sk);
310 if (!err)
311 err = -ECONNREFUSED;
312 goto out;
313 }
314 sock->state = SS_CONNECTED;
315 err = 0;
316
317out:
318 release_sock(sk);
319 return err;
320}
321#endif
322
227static int pn_socket_accept(struct socket *sock, struct socket *newsock, 323static int pn_socket_accept(struct socket *sock, struct socket *newsock,
228 int flags) 324 int flags)
229{ 325{
@@ -281,7 +377,9 @@ static unsigned int pn_socket_poll(struct file *file, struct socket *sock,
281 if (!mask && sk->sk_state == TCP_CLOSE_WAIT) 377 if (!mask && sk->sk_state == TCP_CLOSE_WAIT)
282 return POLLHUP; 378 return POLLHUP;
283 379
284 if (sk->sk_state == TCP_ESTABLISHED && atomic_read(&pn->tx_credits)) 380 if (sk->sk_state == TCP_ESTABLISHED &&
381 atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf &&
382 atomic_read(&pn->tx_credits))
285 mask |= POLLOUT | POLLWRNORM | POLLWRBAND; 383 mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
286 384
287 return mask; 385 return mask;
@@ -390,7 +488,11 @@ const struct proto_ops phonet_stream_ops = {
390 .owner = THIS_MODULE, 488 .owner = THIS_MODULE,
391 .release = pn_socket_release, 489 .release = pn_socket_release,
392 .bind = pn_socket_bind, 490 .bind = pn_socket_bind,
491#ifdef CONFIG_PHONET_PIPECTRLR
492 .connect = pn_socket_connect,
493#else
393 .connect = sock_no_connect, 494 .connect = sock_no_connect,
495#endif
394 .socketpair = sock_no_socketpair, 496 .socketpair = sock_no_socketpair,
395 .accept = pn_socket_accept, 497 .accept = pn_socket_accept,
396 .getname = pn_socket_getname, 498 .getname = pn_socket_getname,
@@ -563,3 +665,188 @@ const struct file_operations pn_sock_seq_fops = {
563 .release = seq_release_net, 665 .release = seq_release_net,
564}; 666};
565#endif 667#endif
668
669static struct {
670 struct sock *sk[256];
671} pnres;
672
673/*
674 * Find and hold socket based on resource.
675 */
676struct sock *pn_find_sock_by_res(struct net *net, u8 res)
677{
678 struct sock *sk;
679
680 if (!net_eq(net, &init_net))
681 return NULL;
682
683 rcu_read_lock();
684 sk = rcu_dereference(pnres.sk[res]);
685 if (sk)
686 sock_hold(sk);
687 rcu_read_unlock();
688 return sk;
689}
690
691static DEFINE_MUTEX(resource_mutex);
692
693int pn_sock_bind_res(struct sock *sk, u8 res)
694{
695 int ret = -EADDRINUSE;
696
697 if (!net_eq(sock_net(sk), &init_net))
698 return -ENOIOCTLCMD;
699 if (!capable(CAP_SYS_ADMIN))
700 return -EPERM;
701 if (pn_socket_autobind(sk->sk_socket))
702 return -EAGAIN;
703
704 mutex_lock(&resource_mutex);
705 if (pnres.sk[res] == NULL) {
706 sock_hold(sk);
707 rcu_assign_pointer(pnres.sk[res], sk);
708 ret = 0;
709 }
710 mutex_unlock(&resource_mutex);
711 return ret;
712}
713
714int pn_sock_unbind_res(struct sock *sk, u8 res)
715{
716 int ret = -ENOENT;
717
718 if (!capable(CAP_SYS_ADMIN))
719 return -EPERM;
720
721 mutex_lock(&resource_mutex);
722 if (pnres.sk[res] == sk) {
723 rcu_assign_pointer(pnres.sk[res], NULL);
724 ret = 0;
725 }
726 mutex_unlock(&resource_mutex);
727
728 if (ret == 0) {
729 synchronize_rcu();
730 sock_put(sk);
731 }
732 return ret;
733}
734
735void pn_sock_unbind_all_res(struct sock *sk)
736{
737 unsigned res, match = 0;
738
739 mutex_lock(&resource_mutex);
740 for (res = 0; res < 256; res++) {
741 if (pnres.sk[res] == sk) {
742 rcu_assign_pointer(pnres.sk[res], NULL);
743 match++;
744 }
745 }
746 mutex_unlock(&resource_mutex);
747
748 if (match == 0)
749 return;
750 synchronize_rcu();
751 while (match > 0) {
752 sock_put(sk);
753 match--;
754 }
755}
756
757#ifdef CONFIG_PROC_FS
758static struct sock **pn_res_get_idx(struct seq_file *seq, loff_t pos)
759{
760 struct net *net = seq_file_net(seq);
761 unsigned i;
762
763 if (!net_eq(net, &init_net))
764 return NULL;
765
766 for (i = 0; i < 256; i++) {
767 if (pnres.sk[i] == NULL)
768 continue;
769 if (!pos)
770 return pnres.sk + i;
771 pos--;
772 }
773 return NULL;
774}
775
776static struct sock **pn_res_get_next(struct seq_file *seq, struct sock **sk)
777{
778 struct net *net = seq_file_net(seq);
779 unsigned i;
780
781 BUG_ON(!net_eq(net, &init_net));
782
783 for (i = (sk - pnres.sk) + 1; i < 256; i++)
784 if (pnres.sk[i])
785 return pnres.sk + i;
786 return NULL;
787}
788
789static void *pn_res_seq_start(struct seq_file *seq, loff_t *pos)
790 __acquires(resource_mutex)
791{
792 mutex_lock(&resource_mutex);
793 return *pos ? pn_res_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
794}
795
796static void *pn_res_seq_next(struct seq_file *seq, void *v, loff_t *pos)
797{
798 struct sock **sk;
799
800 if (v == SEQ_START_TOKEN)
801 sk = pn_res_get_idx(seq, 0);
802 else
803 sk = pn_res_get_next(seq, v);
804 (*pos)++;
805 return sk;
806}
807
808static void pn_res_seq_stop(struct seq_file *seq, void *v)
809 __releases(resource_mutex)
810{
811 mutex_unlock(&resource_mutex);
812}
813
814static int pn_res_seq_show(struct seq_file *seq, void *v)
815{
816 int len;
817
818 if (v == SEQ_START_TOKEN)
819 seq_printf(seq, "%s%n", "rs uid inode", &len);
820 else {
821 struct sock **psk = v;
822 struct sock *sk = *psk;
823
824 seq_printf(seq, "%02X %5d %lu%n",
825 (int) (psk - pnres.sk), sock_i_uid(sk),
826 sock_i_ino(sk), &len);
827 }
828 seq_printf(seq, "%*s\n", 63 - len, "");
829 return 0;
830}
831
832static const struct seq_operations pn_res_seq_ops = {
833 .start = pn_res_seq_start,
834 .next = pn_res_seq_next,
835 .stop = pn_res_seq_stop,
836 .show = pn_res_seq_show,
837};
838
839static int pn_res_open(struct inode *inode, struct file *file)
840{
841 return seq_open_net(inode, file, &pn_res_seq_ops,
842 sizeof(struct seq_net_private));
843}
844
845const struct file_operations pn_res_seq_fops = {
846 .owner = THIS_MODULE,
847 .open = pn_res_open,
848 .read = seq_read,
849 .llseek = seq_lseek,
850 .release = seq_release_net,
851};
852#endif