Commit 9fbd87d4 authored by Ursula Braun's avatar Ursula Braun Committed by David S. Miller

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: default avatarUrsula Braun <ursula.braun@de.ibm.com>
Signed-off-by: default avatarFrank Blaschka <frank.blaschka@de.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c75a312d
...@@ -453,14 +453,28 @@ static void iucv_sever_path(struct sock *sk, int with_user_data) ...@@ -453,14 +453,28 @@ static void iucv_sever_path(struct sock *sk, int with_user_data)
} }
} }
/* Send FIN through an IUCV socket for HIPER transport */
static int iucv_send_ctrl(struct sock *sk, u8 flags)
{
int err = 0;
int blen;
struct sk_buff *skb;
blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN;
skb = sock_alloc_send_skb(sk, blen, 1, &err);
if (skb) {
skb_reserve(skb, blen);
err = afiucv_hs_send(NULL, sk, skb, flags);
}
return err;
}
/* Close an IUCV socket */ /* Close an IUCV socket */
static void iucv_sock_close(struct sock *sk) static void iucv_sock_close(struct sock *sk)
{ {
struct iucv_sock *iucv = iucv_sk(sk); struct iucv_sock *iucv = iucv_sk(sk);
unsigned long timeo; unsigned long timeo;
int err = 0; int err = 0;
int blen;
struct sk_buff *skb;
lock_sock(sk); lock_sock(sk);
...@@ -471,14 +485,7 @@ static void iucv_sock_close(struct sock *sk) ...@@ -471,14 +485,7 @@ static void iucv_sock_close(struct sock *sk)
case IUCV_CONNECTED: case IUCV_CONNECTED:
if (iucv->transport == AF_IUCV_TRANS_HIPER) { if (iucv->transport == AF_IUCV_TRANS_HIPER) {
/* send fin */ err = iucv_send_ctrl(sk, AF_IUCV_FLAG_FIN);
blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN;
skb = sock_alloc_send_skb(sk, blen, 1, &err);
if (skb) {
skb_reserve(skb, blen);
err = afiucv_hs_send(NULL, sk, skb,
AF_IUCV_FLAG_FIN);
}
sk->sk_state = IUCV_DISCONN; sk->sk_state = IUCV_DISCONN;
sk->sk_state_change(sk); sk->sk_state_change(sk);
} }
...@@ -782,26 +789,6 @@ static int iucv_sock_autobind(struct sock *sk) ...@@ -782,26 +789,6 @@ static int iucv_sock_autobind(struct sock *sk)
return err; return err;
} }
static int afiucv_hs_connect(struct socket *sock)
{
struct sock *sk = sock->sk;
struct sk_buff *skb;
int blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN;
int err = 0;
/* send syn */
skb = sock_alloc_send_skb(sk, blen, 1, &err);
if (!skb) {
err = -ENOMEM;
goto done;
}
skb->dev = NULL;
skb_reserve(skb, blen);
err = afiucv_hs_send(NULL, sk, skb, AF_IUCV_FLAG_SYN);
done:
return err;
}
static int afiucv_path_connect(struct socket *sock, struct sockaddr *addr) static int afiucv_path_connect(struct socket *sock, struct sockaddr *addr)
{ {
struct sockaddr_iucv *sa = (struct sockaddr_iucv *) addr; struct sockaddr_iucv *sa = (struct sockaddr_iucv *) addr;
...@@ -882,7 +869,7 @@ static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr, ...@@ -882,7 +869,7 @@ static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr,
memcpy(iucv->dst_name, sa->siucv_name, 8); memcpy(iucv->dst_name, sa->siucv_name, 8);
if (iucv->transport == AF_IUCV_TRANS_HIPER) if (iucv->transport == AF_IUCV_TRANS_HIPER)
err = afiucv_hs_connect(sock); err = iucv_send_ctrl(sock->sk, AF_IUCV_FLAG_SYN);
else else
err = afiucv_path_connect(sock, addr); err = afiucv_path_connect(sock, addr);
if (err) if (err)
...@@ -1332,8 +1319,7 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock, ...@@ -1332,8 +1319,7 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
struct iucv_sock *iucv = iucv_sk(sk); struct iucv_sock *iucv = iucv_sk(sk);
unsigned int copied, rlen; unsigned int copied, rlen;
struct sk_buff *skb, *rskb, *cskb, *sskb; struct sk_buff *skb, *rskb, *cskb;
int blen;
int err = 0; int err = 0;
if ((sk->sk_state == IUCV_DISCONN) && if ((sk->sk_state == IUCV_DISCONN) &&
...@@ -1422,15 +1408,7 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock, ...@@ -1422,15 +1408,7 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
iucv_process_message_q(sk); iucv_process_message_q(sk);
if (atomic_read(&iucv->msg_recv) >= if (atomic_read(&iucv->msg_recv) >=
iucv->msglimit / 2) { iucv->msglimit / 2) {
/* send WIN to peer */ err = iucv_send_ctrl(sk, AF_IUCV_FLAG_WIN);
blen = sizeof(struct af_iucv_trans_hdr) +
ETH_HLEN;
sskb = sock_alloc_send_skb(sk, blen, 1, &err);
if (sskb) {
skb_reserve(sskb, blen);
err = afiucv_hs_send(NULL, sk, sskb,
AF_IUCV_FLAG_WIN);
}
if (err) { if (err) {
sk->sk_state = IUCV_DISCONN; sk->sk_state = IUCV_DISCONN;
sk->sk_state_change(sk); sk->sk_state_change(sk);
...@@ -2289,6 +2267,44 @@ static void afiucv_hs_callback_txnotify(struct sk_buff *skb, ...@@ -2289,6 +2267,44 @@ static void afiucv_hs_callback_txnotify(struct sk_buff *skb,
} }
} }
/*
* afiucv_netdev_event: handle netdev notifier chain events
*/
static int afiucv_netdev_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct net_device *event_dev = (struct net_device *)ptr;
struct hlist_node *node;
struct sock *sk;
struct iucv_sock *iucv;
switch (event) {
case NETDEV_REBOOT:
case NETDEV_GOING_DOWN:
sk_for_each(sk, node, &iucv_sk_list.head) {
iucv = iucv_sk(sk);
if ((iucv->hs_dev == event_dev) &&
(sk->sk_state == IUCV_CONNECTED)) {
if (event == NETDEV_GOING_DOWN)
iucv_send_ctrl(sk, AF_IUCV_FLAG_FIN);
sk->sk_state = IUCV_DISCONN;
sk->sk_state_change(sk);
}
}
break;
case NETDEV_DOWN:
case NETDEV_UNREGISTER:
default:
break;
}
return NOTIFY_DONE;
}
static struct notifier_block afiucv_netdev_notifier = {
.notifier_call = afiucv_netdev_event,
};
static const struct proto_ops iucv_sock_ops = { static const struct proto_ops iucv_sock_ops = {
.family = PF_IUCV, .family = PF_IUCV,
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -2388,7 +2404,8 @@ static int __init afiucv_init(void) ...@@ -2388,7 +2404,8 @@ static int __init afiucv_init(void)
err = afiucv_iucv_init(); err = afiucv_iucv_init();
if (err) if (err)
goto out_sock; goto out_sock;
} } else
register_netdevice_notifier(&afiucv_netdev_notifier);
dev_add_pack(&iucv_packet_type); dev_add_pack(&iucv_packet_type);
return 0; return 0;
...@@ -2409,7 +2426,8 @@ static void __exit afiucv_exit(void) ...@@ -2409,7 +2426,8 @@ static void __exit afiucv_exit(void)
driver_unregister(&af_iucv_driver); driver_unregister(&af_iucv_driver);
pr_iucv->iucv_unregister(&af_iucv_handler, 0); pr_iucv->iucv_unregister(&af_iucv_handler, 0);
symbol_put(iucv_if); symbol_put(iucv_if);
} } else
unregister_netdevice_notifier(&afiucv_netdev_notifier);
dev_remove_pack(&iucv_packet_type); dev_remove_pack(&iucv_packet_type);
sock_unregister(PF_IUCV); sock_unregister(PF_IUCV);
proto_unregister(&iucv_proto); proto_unregister(&iucv_proto);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment