CRIS: Merge machine dependent boot/compressed and boot/rescue
[safe/jmp/linux-2.6] / net / iucv / af_iucv.c
index 2255e3c..49e7865 100644 (file)
@@ -8,6 +8,9 @@
  *  Author(s): Jennifer Hunt <jenhunt@us.ibm.com>
  */
 
+#define KMSG_COMPONENT "af_iucv"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/list.h>
@@ -53,7 +56,7 @@ static int iucv_callback_connreq(struct iucv_path *, u8 ipvmid[8],
 static void iucv_callback_connrej(struct iucv_path *, u8 ipuser[16]);
 
 static struct iucv_sock_list iucv_sk_list = {
-       .lock = RW_LOCK_UNLOCKED,
+       .lock = __RW_LOCK_UNLOCKED(iucv_sk_list.lock),
        .autobind_name = ATOMIC_INIT(0)
 };
 
@@ -482,12 +485,30 @@ static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr,
        /* Create path. */
        iucv->path = iucv_path_alloc(IUCV_QUEUELEN_DEFAULT,
                                     IPRMDATA, GFP_KERNEL);
+       if (!iucv->path) {
+               err = -ENOMEM;
+               goto done;
+       }
        err = iucv_path_connect(iucv->path, &af_iucv_handler,
                                sa->siucv_user_id, NULL, user_data, sk);
        if (err) {
                iucv_path_free(iucv->path);
                iucv->path = NULL;
-               err = -ECONNREFUSED;
+               switch (err) {
+               case 0x0b:      /* Target communicator is not logged on */
+                       err = -ENETUNREACH;
+                       break;
+               case 0x0d:      /* Max connections for this guest exceeded */
+               case 0x0e:      /* Max connections for target guest exceeded */
+                       err = -EAGAIN;
+                       break;
+               case 0x0f:      /* Missing IUCV authorization */
+                       err = -EACCES;
+                       break;
+               default:
+                       err = -ECONNREFUSED;
+                       break;
+               }
                goto done;
        }
 
@@ -500,6 +521,13 @@ static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr,
                release_sock(sk);
                return -ECONNREFUSED;
        }
+
+       if (err) {
+               iucv_path_sever(iucv->path, NULL);
+               iucv_path_free(iucv->path);
+               iucv->path = NULL;
+       }
+
 done:
        release_sock(sk);
        return err;
@@ -612,6 +640,8 @@ static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
        struct iucv_sock *iucv = iucv_sk(sk);
        struct sk_buff *skb;
        struct iucv_message txmsg;
+       char user_id[9];
+       char appl_id[9];
        int err;
 
        err = sock_error(sk);
@@ -640,14 +670,22 @@ static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
                }
 
                txmsg.class = 0;
+               memcpy(&txmsg.class, skb->data, skb->len >= 4 ? 4 : skb->len);
                txmsg.tag = iucv->send_tag++;
                memcpy(skb->cb, &txmsg.tag, 4);
                skb_queue_tail(&iucv->send_skb_q, skb);
                err = iucv_message_send(iucv->path, &txmsg, 0, 0,
                                        (void *) skb->data, skb->len);
                if (err) {
-                       if (err == 3)
-                               printk(KERN_ERR "AF_IUCV msg limit exceeded\n");
+                       if (err == 3) {
+                               user_id[8] = 0;
+                               memcpy(user_id, iucv->dst_user_id, 8);
+                               appl_id[8] = 0;
+                               memcpy(appl_id, iucv->dst_name, 8);
+                               pr_err("Application %s on z/VM guest %s"
+                                      " exceeds message limit\n",
+                                      user_id, appl_id);
+                       }
                        skb_unlink(skb, &iucv->send_skb_q);
                        err = -EPIPE;
                        goto fail;
@@ -1004,12 +1042,14 @@ static int iucv_callback_connreq(struct iucv_path *path,
        ASCEBC(user_data, sizeof(user_data));
        if (sk->sk_state != IUCV_LISTEN) {
                err = iucv_path_sever(path, user_data);
+               iucv_path_free(path);
                goto fail;
        }
 
        /* Check for backlog size */
        if (sk_acceptq_is_full(sk)) {
                err = iucv_path_sever(path, user_data);
+               iucv_path_free(path);
                goto fail;
        }
 
@@ -1017,6 +1057,7 @@ static int iucv_callback_connreq(struct iucv_path *path,
        nsk = iucv_sock_alloc(NULL, SOCK_STREAM, GFP_ATOMIC);
        if (!nsk) {
                err = iucv_path_sever(path, user_data);
+               iucv_path_free(path);
                goto fail;
        }
 
@@ -1040,6 +1081,8 @@ static int iucv_callback_connreq(struct iucv_path *path,
        err = iucv_path_accept(path, &af_iucv_handler, nuser_data, nsk);
        if (err) {
                err = iucv_path_sever(path, user_data);
+               iucv_path_free(path);
+               iucv_sock_kill(nsk);
                goto fail;
        }
 
@@ -1094,6 +1137,8 @@ static void iucv_callback_rx(struct iucv_path *path, struct iucv_message *msg)
 
 save_message:
        save_msg = kzalloc(sizeof(struct sock_msg_q), GFP_ATOMIC | GFP_DMA);
+       if (!save_msg)
+               return;
        save_msg->path = path;
        save_msg->msg = *msg;
 
@@ -1106,24 +1151,29 @@ static void iucv_callback_txdone(struct iucv_path *path,
                                 struct iucv_message *msg)
 {
        struct sock *sk = path->private;
-       struct sk_buff *this;
+       struct sk_buff *this = NULL;
        struct sk_buff_head *list = &iucv_sk(sk)->send_skb_q;
        struct sk_buff *list_skb = list->next;
        unsigned long flags;
 
-       if (list_skb) {
+       if (!skb_queue_empty(list)) {
                spin_lock_irqsave(&list->lock, flags);
 
-               do {
-                       this = list_skb;
+               while (list_skb != (struct sk_buff *)list) {
+                       if (!memcmp(&msg->tag, list_skb->cb, 4)) {
+                               this = list_skb;
+                               break;
+                       }
                        list_skb = list_skb->next;
-               } while (memcmp(&msg->tag, this->cb, 4) && list_skb);
+               }
+               if (this)
+                       __skb_unlink(this, list);
 
                spin_unlock_irqrestore(&list->lock, flags);
 
-               skb_unlink(this, &iucv_sk(sk)->send_skb_q);
                kfree_skb(this);
        }
+       BUG_ON(!this);
 
        if (sk->sk_state == IUCV_CLOSING) {
                if (skb_queue_empty(&iucv_sk(sk)->send_skb_q)) {
@@ -1177,13 +1227,14 @@ static int __init afiucv_init(void)
        int err;
 
        if (!MACHINE_IS_VM) {
-               printk(KERN_ERR "AF_IUCV connection needs VM as base\n");
+               pr_err("The af_iucv module cannot be loaded"
+                      " without z/VM\n");
                err = -EPROTONOSUPPORT;
                goto out;
        }
        cpcmd("QUERY USERID", iucv_userid, sizeof(iucv_userid), &err);
        if (unlikely(err)) {
-               printk(KERN_ERR "AF_IUCV needs the VM userid\n");
+               WARN_ON(err);
                err = -EPROTONOSUPPORT;
                goto out;
        }
@@ -1197,7 +1248,6 @@ static int __init afiucv_init(void)
        err = sock_register(&iucv_sock_family_ops);
        if (err)
                goto out_proto;
-       printk(KERN_INFO "AF_IUCV lowlevel driver initialized\n");
        return 0;
 
 out_proto:
@@ -1213,8 +1263,6 @@ static void __exit afiucv_exit(void)
        sock_unregister(PF_IUCV);
        proto_unregister(&iucv_proto);
        iucv_unregister(&af_iucv_handler, 0);
-
-       printk(KERN_INFO "AF_IUCV lowlevel driver unloaded\n");
 }
 
 module_init(afiucv_init);