aboutsummaryrefslogtreecommitdiffstats
path: root/net/iucv
diff options
context:
space:
mode:
authorUrsula Braun <ursula.braun@de.ibm.com>2012-03-06 21:06:23 -0500
committerDavid S. Miller <davem@davemloft.net>2012-03-08 01:52:24 -0500
commit9fbd87d413921f36d2f55cee1d082323e6eb1d5f (patch)
treeef81716bfbb06f0376f51c0c6bca661efb4decfd /net/iucv
parentc75a312d8b2c97943014261d71c6ef2622b6d0d9 (diff)
af_iucv: handle netdev events
In case of transport through HiperSockets the underlying network interface may switch to DOWN state or the underlying network device may recover. In both cases the socket must change to IUCV_DISCONN state. If the interface goes down, af_iucv has a chance to notify its connection peer in addition. Signed-off-by: Ursula Braun <ursula.braun@de.ibm.com> Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/iucv')
-rw-r--r--net/iucv/af_iucv.c106
1 files changed, 62 insertions, 44 deletions
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index 98d1f0ba7fe9..31537c5eb17e 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -453,14 +453,28 @@ static void iucv_sever_path(struct sock *sk, int with_user_data)
453 } 453 }
454} 454}
455 455
456/* Send FIN through an IUCV socket for HIPER transport */
457static int iucv_send_ctrl(struct sock *sk, u8 flags)
458{
459 int err = 0;
460 int blen;
461 struct sk_buff *skb;
462
463 blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN;
464 skb = sock_alloc_send_skb(sk, blen, 1, &err);
465 if (skb) {
466 skb_reserve(skb, blen);
467 err = afiucv_hs_send(NULL, sk, skb, flags);
468 }
469 return err;
470}
471
456/* Close an IUCV socket */ 472/* Close an IUCV socket */
457static void iucv_sock_close(struct sock *sk) 473static void iucv_sock_close(struct sock *sk)
458{ 474{
459 struct iucv_sock *iucv = iucv_sk(sk); 475 struct iucv_sock *iucv = iucv_sk(sk);
460 unsigned long timeo; 476 unsigned long timeo;
461 int err = 0; 477 int err = 0;
462 int blen;
463 struct sk_buff *skb;
464 478
465 lock_sock(sk); 479 lock_sock(sk);
466 480
@@ -471,14 +485,7 @@ static void iucv_sock_close(struct sock *sk)
471 485
472 case IUCV_CONNECTED: 486 case IUCV_CONNECTED:
473 if (iucv->transport == AF_IUCV_TRANS_HIPER) { 487 if (iucv->transport == AF_IUCV_TRANS_HIPER) {
474 /* send fin */ 488 err = iucv_send_ctrl(sk, AF_IUCV_FLAG_FIN);
475 blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN;
476 skb = sock_alloc_send_skb(sk, blen, 1, &err);
477 if (skb) {
478 skb_reserve(skb, blen);
479 err = afiucv_hs_send(NULL, sk, skb,
480 AF_IUCV_FLAG_FIN);
481 }
482 sk->sk_state = IUCV_DISCONN; 489 sk->sk_state = IUCV_DISCONN;
483 sk->sk_state_change(sk); 490 sk->sk_state_change(sk);
484 } 491 }
@@ -782,26 +789,6 @@ static int iucv_sock_autobind(struct sock *sk)
782 return err; 789 return err;
783} 790}
784 791
785static int afiucv_hs_connect(struct socket *sock)
786{
787 struct sock *sk = sock->sk;
788 struct sk_buff *skb;
789 int blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN;
790 int err = 0;
791
792 /* send syn */
793 skb = sock_alloc_send_skb(sk, blen, 1, &err);
794 if (!skb) {
795 err = -ENOMEM;
796 goto done;
797 }
798 skb->dev = NULL;
799 skb_reserve(skb, blen);
800 err = afiucv_hs_send(NULL, sk, skb, AF_IUCV_FLAG_SYN);
801done:
802 return err;
803}
804
805static int afiucv_path_connect(struct socket *sock, struct sockaddr *addr) 792static int afiucv_path_connect(struct socket *sock, struct sockaddr *addr)
806{ 793{
807 struct sockaddr_iucv *sa = (struct sockaddr_iucv *) addr; 794 struct sockaddr_iucv *sa = (struct sockaddr_iucv *) addr;
@@ -882,7 +869,7 @@ static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr,
882 memcpy(iucv->dst_name, sa->siucv_name, 8); 869 memcpy(iucv->dst_name, sa->siucv_name, 8);
883 870
884 if (iucv->transport == AF_IUCV_TRANS_HIPER) 871 if (iucv->transport == AF_IUCV_TRANS_HIPER)
885 err = afiucv_hs_connect(sock); 872 err = iucv_send_ctrl(sock->sk, AF_IUCV_FLAG_SYN);
886 else 873 else
887 err = afiucv_path_connect(sock, addr); 874 err = afiucv_path_connect(sock, addr);
888 if (err) 875 if (err)
@@ -1332,8 +1319,7 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
1332 struct sock *sk = sock->sk; 1319 struct sock *sk = sock->sk;
1333 struct iucv_sock *iucv = iucv_sk(sk); 1320 struct iucv_sock *iucv = iucv_sk(sk);
1334 unsigned int copied, rlen; 1321 unsigned int copied, rlen;
1335 struct sk_buff *skb, *rskb, *cskb, *sskb; 1322 struct sk_buff *skb, *rskb, *cskb;
1336 int blen;
1337 int err = 0; 1323 int err = 0;
1338 1324
1339 if ((sk->sk_state == IUCV_DISCONN) && 1325 if ((sk->sk_state == IUCV_DISCONN) &&
@@ -1422,15 +1408,7 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
1422 iucv_process_message_q(sk); 1408 iucv_process_message_q(sk);
1423 if (atomic_read(&iucv->msg_recv) >= 1409 if (atomic_read(&iucv->msg_recv) >=
1424 iucv->msglimit / 2) { 1410 iucv->msglimit / 2) {
1425 /* send WIN to peer */ 1411 err = iucv_send_ctrl(sk, AF_IUCV_FLAG_WIN);
1426 blen = sizeof(struct af_iucv_trans_hdr) +
1427 ETH_HLEN;
1428 sskb = sock_alloc_send_skb(sk, blen, 1, &err);
1429 if (sskb) {
1430 skb_reserve(sskb, blen);
1431 err = afiucv_hs_send(NULL, sk, sskb,
1432 AF_IUCV_FLAG_WIN);
1433 }
1434 if (err) { 1412 if (err) {
1435 sk->sk_state = IUCV_DISCONN; 1413 sk->sk_state = IUCV_DISCONN;
1436 sk->sk_state_change(sk); 1414 sk->sk_state_change(sk);
@@ -2289,6 +2267,44 @@ out_unlock:
2289 } 2267 }
2290 2268
2291} 2269}
2270
2271/*
2272 * afiucv_netdev_event: handle netdev notifier chain events
2273 */
2274static int afiucv_netdev_event(struct notifier_block *this,
2275 unsigned long event, void *ptr)
2276{
2277 struct net_device *event_dev = (struct net_device *)ptr;
2278 struct hlist_node *node;
2279 struct sock *sk;
2280 struct iucv_sock *iucv;
2281
2282 switch (event) {
2283 case NETDEV_REBOOT:
2284 case NETDEV_GOING_DOWN:
2285 sk_for_each(sk, node, &iucv_sk_list.head) {
2286 iucv = iucv_sk(sk);
2287 if ((iucv->hs_dev == event_dev) &&
2288 (sk->sk_state == IUCV_CONNECTED)) {
2289 if (event == NETDEV_GOING_DOWN)
2290 iucv_send_ctrl(sk, AF_IUCV_FLAG_FIN);
2291 sk->sk_state = IUCV_DISCONN;
2292 sk->sk_state_change(sk);
2293 }
2294 }
2295 break;
2296 case NETDEV_DOWN:
2297 case NETDEV_UNREGISTER:
2298 default:
2299 break;
2300 }
2301 return NOTIFY_DONE;
2302}
2303
2304static struct notifier_block afiucv_netdev_notifier = {
2305 .notifier_call = afiucv_netdev_event,
2306};
2307
2292static const struct proto_ops iucv_sock_ops = { 2308static const struct proto_ops iucv_sock_ops = {
2293 .family = PF_IUCV, 2309 .family = PF_IUCV,
2294 .owner = THIS_MODULE, 2310 .owner = THIS_MODULE,
@@ -2388,7 +2404,8 @@ static int __init afiucv_init(void)
2388 err = afiucv_iucv_init(); 2404 err = afiucv_iucv_init();
2389 if (err) 2405 if (err)
2390 goto out_sock; 2406 goto out_sock;
2391 } 2407 } else
2408 register_netdevice_notifier(&afiucv_netdev_notifier);
2392 dev_add_pack(&iucv_packet_type); 2409 dev_add_pack(&iucv_packet_type);
2393 return 0; 2410 return 0;
2394 2411
@@ -2409,7 +2426,8 @@ static void __exit afiucv_exit(void)
2409 driver_unregister(&af_iucv_driver); 2426 driver_unregister(&af_iucv_driver);
2410 pr_iucv->iucv_unregister(&af_iucv_handler, 0); 2427 pr_iucv->iucv_unregister(&af_iucv_handler, 0);
2411 symbol_put(iucv_if); 2428 symbol_put(iucv_if);
2412 } 2429 } else
2430 unregister_netdevice_notifier(&afiucv_netdev_notifier);
2413 dev_remove_pack(&iucv_packet_type); 2431 dev_remove_pack(&iucv_packet_type);
2414 sock_unregister(PF_IUCV); 2432 sock_unregister(PF_IUCV);
2415 proto_unregister(&iucv_proto); 2433 proto_unregister(&iucv_proto);