]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - net/iucv/af_iucv.c
net/af_iucv: build proper skbs for HiperTransport
[mirror_ubuntu-bionic-kernel.git] / net / iucv / af_iucv.c
index 148533169b1ddb6d20aea50523e6311188e80f13..fa79005808f192c083dce6b06d6862a36a87da6b 100644 (file)
@@ -13,6 +13,7 @@
 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
 
 #include <linux/module.h>
+#include <linux/netdevice.h>
 #include <linux/types.h>
 #include <linux/list.h>
 #include <linux/errno.h>
@@ -352,20 +353,33 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
                memcpy(&phs_hdr->iucv_hdr, imsg, sizeof(struct iucv_message));
 
        skb->dev = iucv->hs_dev;
-       if (!skb->dev)
-               return -ENODEV;
-       if (!(skb->dev->flags & IFF_UP) || !netif_carrier_ok(skb->dev))
-               return -ENETDOWN;
+       if (!skb->dev) {
+               err = -ENODEV;
+               goto err_free;
+       }
+
+       dev_hard_header(skb, skb->dev, ETH_P_AF_IUCV, NULL, NULL, skb->len);
+
+       if (!(skb->dev->flags & IFF_UP) || !netif_carrier_ok(skb->dev)) {
+               err = -ENETDOWN;
+               goto err_free;
+       }
        if (skb->len > skb->dev->mtu) {
-               if (sock->sk_type == SOCK_SEQPACKET)
-                       return -EMSGSIZE;
-               else
-                       skb_trim(skb, skb->dev->mtu);
+               if (sock->sk_type == SOCK_SEQPACKET) {
+                       err = -EMSGSIZE;
+                       goto err_free;
+               }
+               skb_trim(skb, skb->dev->mtu);
        }
        skb->protocol = cpu_to_be16(ETH_P_AF_IUCV);
+
+       __skb_header_release(skb);
        nskb = skb_clone(skb, GFP_ATOMIC);
-       if (!nskb)
-               return -ENOMEM;
+       if (!nskb) {
+               err = -ENOMEM;
+               goto err_free;
+       }
+
        skb_queue_tail(&iucv->send_skb_q, nskb);
        err = dev_queue_xmit(skb);
        if (net_xmit_eval(err)) {
@@ -376,6 +390,10 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
                WARN_ON(atomic_read(&iucv->msg_recv) < 0);
        }
        return net_xmit_eval(err);
+
+err_free:
+       kfree_skb(skb);
+       return err;
 }
 
 static struct sock *__iucv_get_sock_by_name(char *nm)
@@ -455,12 +473,14 @@ static void iucv_sever_path(struct sock *sk, int with_user_data)
 /* Send controlling flags through an IUCV socket for HIPER transport */
 static int iucv_send_ctrl(struct sock *sk, u8 flags)
 {
+       struct iucv_sock *iucv = iucv_sk(sk);
        int err = 0;
        int blen;
        struct sk_buff *skb;
        u8 shutdown = 0;
 
-       blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN;
+       blen = sizeof(struct af_iucv_trans_hdr) +
+              LL_RESERVED_SPACE(iucv->hs_dev);
        if (sk->sk_shutdown & SEND_SHUTDOWN) {
                /* controlling flags should be sent anyway */
                shutdown = sk->sk_shutdown;
@@ -1121,7 +1141,8 @@ static int iucv_sock_sendmsg(struct socket *sock, struct msghdr *msg,
         * segmented records using the MSG_EOR flag), but
         * for SOCK_STREAM we might want to improve it in future */
        if (iucv->transport == AF_IUCV_TRANS_HIPER) {
-               headroom = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN;
+               headroom = sizeof(struct af_iucv_trans_hdr) +
+                          LL_RESERVED_SPACE(iucv->hs_dev);
                linear = len;
        } else {
                if (len < PAGE_SIZE) {
@@ -1169,7 +1190,7 @@ static int iucv_sock_sendmsg(struct socket *sock, struct msghdr *msg,
                err = afiucv_hs_send(&txmsg, sk, skb, 0);
                if (err) {
                        atomic_dec(&iucv->msg_sent);
-                       goto fail;
+                       goto out;
                }
        } else { /* Classic VM IUCV transport */
                skb_queue_tail(&iucv->send_skb_q, skb);
@@ -2157,8 +2178,8 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
        struct sock *sk;
        struct iucv_sock *iucv;
        struct af_iucv_trans_hdr *trans_hdr;
+       int err = NET_RX_SUCCESS;
        char nullstring[8];
-       int err = 0;
 
        if (skb->len < (ETH_HLEN + sizeof(struct af_iucv_trans_hdr))) {
                WARN_ONCE(1, "AF_IUCV too short skb, len=%d, min=%d",
@@ -2256,7 +2277,7 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
                err = afiucv_hs_callback_rx(sk, skb);
                break;
        default:
-               ;
+               kfree_skb(skb);
        }
 
        return err;
@@ -2433,9 +2454,11 @@ static int afiucv_iucv_init(void)
        af_iucv_dev->driver = &af_iucv_driver;
        err = device_register(af_iucv_dev);
        if (err)
-               goto out_driver;
+               goto out_iucv_dev;
        return 0;
 
+out_iucv_dev:
+       put_device(af_iucv_dev);
 out_driver:
        driver_unregister(&af_iucv_driver);
 out_iucv:
@@ -2444,6 +2467,13 @@ out:
        return err;
 }
 
+static void afiucv_iucv_exit(void)
+{
+       device_unregister(af_iucv_dev);
+       driver_unregister(&af_iucv_driver);
+       pr_iucv->iucv_unregister(&af_iucv_handler, 0);
+}
+
 static int __init afiucv_init(void)
 {
        int err;
@@ -2477,11 +2507,18 @@ static int __init afiucv_init(void)
                err = afiucv_iucv_init();
                if (err)
                        goto out_sock;
-       } else
-               register_netdevice_notifier(&afiucv_netdev_notifier);
+       }
+
+       err = register_netdevice_notifier(&afiucv_netdev_notifier);
+       if (err)
+               goto out_notifier;
+
        dev_add_pack(&iucv_packet_type);
        return 0;
 
+out_notifier:
+       if (pr_iucv)
+               afiucv_iucv_exit();
 out_sock:
        sock_unregister(PF_IUCV);
 out_proto:
@@ -2495,12 +2532,11 @@ out:
 static void __exit afiucv_exit(void)
 {
        if (pr_iucv) {
-               device_unregister(af_iucv_dev);
-               driver_unregister(&af_iucv_driver);
-               pr_iucv->iucv_unregister(&af_iucv_handler, 0);
+               afiucv_iucv_exit();
                symbol_put(iucv_if);
-       } else
-               unregister_netdevice_notifier(&afiucv_netdev_notifier);
+       }
+
+       unregister_netdevice_notifier(&afiucv_netdev_notifier);
        dev_remove_pack(&iucv_packet_type);
        sock_unregister(PF_IUCV);
        proto_unregister(&iucv_proto);