|
@@ -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 */
|
|
|
static void iucv_sock_close(struct sock *sk)
|
|
|
{
|
|
|
struct iucv_sock *iucv = iucv_sk(sk);
|
|
|
unsigned long timeo;
|
|
|
int err = 0;
|
|
|
- int blen;
|
|
|
- struct sk_buff *skb;
|
|
|
|
|
|
lock_sock(sk);
|
|
|
|
|
@@ -471,14 +485,7 @@ static void iucv_sock_close(struct sock *sk)
|
|
|
|
|
|
case IUCV_CONNECTED:
|
|
|
if (iucv->transport == AF_IUCV_TRANS_HIPER) {
|
|
|
- /* send 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);
|
|
|
- }
|
|
|
+ err = iucv_send_ctrl(sk, AF_IUCV_FLAG_FIN);
|
|
|
sk->sk_state = IUCV_DISCONN;
|
|
|
sk->sk_state_change(sk);
|
|
|
}
|
|
@@ -782,26 +789,6 @@ static int iucv_sock_autobind(struct sock *sk)
|
|
|
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)
|
|
|
{
|
|
|
struct sockaddr_iucv *sa = (struct sockaddr_iucv *) addr;
|
|
@@ -882,7 +869,7 @@ static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr,
|
|
|
memcpy(iucv->dst_name, sa->siucv_name, 8);
|
|
|
|
|
|
if (iucv->transport == AF_IUCV_TRANS_HIPER)
|
|
|
- err = afiucv_hs_connect(sock);
|
|
|
+ err = iucv_send_ctrl(sock->sk, AF_IUCV_FLAG_SYN);
|
|
|
else
|
|
|
err = afiucv_path_connect(sock, addr);
|
|
|
if (err)
|
|
@@ -1332,8 +1319,7 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
|
|
|
struct sock *sk = sock->sk;
|
|
|
struct iucv_sock *iucv = iucv_sk(sk);
|
|
|
unsigned int copied, rlen;
|
|
|
- struct sk_buff *skb, *rskb, *cskb, *sskb;
|
|
|
- int blen;
|
|
|
+ struct sk_buff *skb, *rskb, *cskb;
|
|
|
int err = 0;
|
|
|
|
|
|
if ((sk->sk_state == IUCV_DISCONN) &&
|
|
@@ -1422,15 +1408,7 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
|
|
|
iucv_process_message_q(sk);
|
|
|
if (atomic_read(&iucv->msg_recv) >=
|
|
|
iucv->msglimit / 2) {
|
|
|
- /* send WIN to peer */
|
|
|
- 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);
|
|
|
- }
|
|
|
+ err = iucv_send_ctrl(sk, AF_IUCV_FLAG_WIN);
|
|
|
if (err) {
|
|
|
sk->sk_state = IUCV_DISCONN;
|
|
|
sk->sk_state_change(sk);
|
|
@@ -2289,6 +2267,44 @@ out_unlock:
|
|
|
}
|
|
|
|
|
|
}
|
|
|
+
|
|
|
+/*
|
|
|
+ * 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 = {
|
|
|
.family = PF_IUCV,
|
|
|
.owner = THIS_MODULE,
|
|
@@ -2388,7 +2404,8 @@ static int __init afiucv_init(void)
|
|
|
err = afiucv_iucv_init();
|
|
|
if (err)
|
|
|
goto out_sock;
|
|
|
- }
|
|
|
+ } else
|
|
|
+ register_netdevice_notifier(&afiucv_netdev_notifier);
|
|
|
dev_add_pack(&iucv_packet_type);
|
|
|
return 0;
|
|
|
|
|
@@ -2409,7 +2426,8 @@ static void __exit afiucv_exit(void)
|
|
|
driver_unregister(&af_iucv_driver);
|
|
|
pr_iucv->iucv_unregister(&af_iucv_handler, 0);
|
|
|
symbol_put(iucv_if);
|
|
|
- }
|
|
|
+ } else
|
|
|
+ unregister_netdevice_notifier(&afiucv_netdev_notifier);
|
|
|
dev_remove_pack(&iucv_packet_type);
|
|
|
sock_unregister(PF_IUCV);
|
|
|
proto_unregister(&iucv_proto);
|