af_iucv: remove IUCV-pathes completely
authorUrsula Braun <ursula.braun@de.ibm.com>
Wed, 8 Feb 2012 00:19:45 +0000 (00:19 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 8 Feb 2012 23:50:19 +0000 (18:50 -0500)
A SEVER is missing in the callback of a receiving SEVERED. This may
inhibit z/VM to remove the corresponding IUCV-path completely.
This patch adds a SEVER in iucv_callback_connrej (together with
additional locking.

Signed-off-by: Ursula Braun <ursula.braun@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/iucv/af_iucv.c

index d5c5b8fd1d01e50f2c9a928809184b28e77ea4bd..3ce0259499edff36c4508d09eff660a85d0d772b 100644 (file)
@@ -90,6 +90,7 @@ do {                                                                  \
 
 static void iucv_sock_kill(struct sock *sk);
 static void iucv_sock_close(struct sock *sk);
+static void iucv_sever_path(struct sock *, int);
 
 static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
        struct packet_type *pt, struct net_device *orig_dev);
@@ -181,11 +182,7 @@ static int afiucv_pm_freeze(struct device *dev)
                case IUCV_DISCONN:
                case IUCV_CLOSING:
                case IUCV_CONNECTED:
-                       if (iucv->path) {
-                               err = pr_iucv->path_sever(iucv->path, NULL);
-                               iucv_path_free(iucv->path);
-                               iucv->path = NULL;
-                       }
+                       iucv_sever_path(sk, 0);
                        break;
                case IUCV_OPEN:
                case IUCV_BOUND:
@@ -194,6 +191,8 @@ static int afiucv_pm_freeze(struct device *dev)
                default:
                        break;
                }
+               skb_queue_purge(&iucv->send_skb_q);
+               skb_queue_purge(&iucv->backlog_skb_q);
        }
        read_unlock(&iucv_sk_list.lock);
        return err;
@@ -447,10 +446,29 @@ static void iucv_sock_kill(struct sock *sk)
        sock_put(sk);
 }
 
+/* Terminate an IUCV path */
+static void iucv_sever_path(struct sock *sk, int with_user_data)
+{
+       unsigned char user_data[16];
+       struct iucv_sock *iucv = iucv_sk(sk);
+       struct iucv_path *path = iucv->path;
+
+       if (iucv->path) {
+               iucv->path = NULL;
+               if (with_user_data) {
+                       low_nmcpy(user_data, iucv->src_name);
+                       high_nmcpy(user_data, iucv->dst_name);
+                       ASCEBC(user_data, sizeof(user_data));
+                       pr_iucv->path_sever(path, user_data);
+               } else
+                       pr_iucv->path_sever(path, NULL);
+               iucv_path_free(path);
+       }
+}
+
 /* Close an IUCV socket */
 static void iucv_sock_close(struct sock *sk)
 {
-       unsigned char user_data[16];
        struct iucv_sock *iucv = iucv_sk(sk);
        unsigned long timeo;
        int err, blen;
@@ -494,25 +512,14 @@ static void iucv_sock_close(struct sock *sk)
                sk->sk_state = IUCV_CLOSED;
                sk->sk_state_change(sk);
 
-               if (iucv->path) {
-                       low_nmcpy(user_data, iucv->src_name);
-                       high_nmcpy(user_data, iucv->dst_name);
-                       ASCEBC(user_data, sizeof(user_data));
-                       pr_iucv->path_sever(iucv->path, user_data);
-                       iucv_path_free(iucv->path);
-                       iucv->path = NULL;
-               }
-
                sk->sk_err = ECONNRESET;
                sk->sk_state_change(sk);
 
                iucv_skb_queue_purge(&iucv->send_skb_q);
                skb_queue_purge(&iucv->backlog_skb_q);
-               break;
 
-       default:
-               /* nothing to do here */
-               break;
+       default:   /* fall through */
+               iucv_sever_path(sk, 1);
        }
 
        /* mark socket for deletion by iucv_sock_kill() */
@@ -894,11 +901,8 @@ static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr,
        if (sk->sk_state == IUCV_DISCONN || sk->sk_state == IUCV_CLOSED)
                err = -ECONNREFUSED;
 
-       if (err && iucv->transport == AF_IUCV_TRANS_IUCV) {
-               pr_iucv->path_sever(iucv->path, NULL);
-               iucv_path_free(iucv->path);
-               iucv->path = NULL;
-       }
+       if (err && iucv->transport == AF_IUCV_TRANS_IUCV)
+               iucv_sever_path(sk, 0);
 
 done:
        release_sock(sk);
@@ -1565,13 +1569,6 @@ static int iucv_sock_release(struct socket *sock)
 
        iucv_sock_close(sk);
 
-       /* Unregister with IUCV base support */
-       if (iucv_sk(sk)->path) {
-               pr_iucv->path_sever(iucv_sk(sk)->path, NULL);
-               iucv_path_free(iucv_sk(sk)->path);
-               iucv_sk(sk)->path = NULL;
-       }
-
        sock_orphan(sk);
        iucv_sock_kill(sk);
        return err;
@@ -1750,8 +1747,7 @@ static int iucv_callback_connreq(struct iucv_path *path,
        path->msglim = iucv->msglimit;
        err = pr_iucv->path_accept(path, &af_iucv_handler, nuser_data, nsk);
        if (err) {
-               err = pr_iucv->path_sever(path, user_data);
-               iucv_path_free(path);
+               iucv_sever_path(nsk, 1);
                iucv_sock_kill(nsk);
                goto fail;
        }
@@ -1828,6 +1824,7 @@ static void iucv_callback_txdone(struct iucv_path *path,
        struct sk_buff *list_skb = list->next;
        unsigned long flags;
 
+       bh_lock_sock(sk);
        if (!skb_queue_empty(list)) {
                spin_lock_irqsave(&list->lock, flags);
 
@@ -1849,7 +1846,6 @@ static void iucv_callback_txdone(struct iucv_path *path,
                        iucv_sock_wake_msglim(sk);
                }
        }
-       BUG_ON(!this);
 
        if (sk->sk_state == IUCV_CLOSING) {
                if (skb_queue_empty(&iucv_sk(sk)->send_skb_q)) {
@@ -1857,6 +1853,7 @@ static void iucv_callback_txdone(struct iucv_path *path,
                        sk->sk_state_change(sk);
                }
        }
+       bh_unlock_sock(sk);
 
 }
 
@@ -1864,9 +1861,15 @@ static void iucv_callback_connrej(struct iucv_path *path, u8 ipuser[16])
 {
        struct sock *sk = path->private;
 
+       if (sk->sk_state == IUCV_CLOSED)
+               return;
+
+       bh_lock_sock(sk);
+       iucv_sever_path(sk, 1);
        sk->sk_state = IUCV_DISCONN;
 
        sk->sk_state_change(sk);
+       bh_unlock_sock(sk);
 }
 
 /* called if the other communication side shuts down its RECV direction;