Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux...
[deliverable/linux.git] / net / iucv / af_iucv.c
index a0d1e36a284bca11a3cb6762bb7cb26e1ac194e5..02b45a8e8b35729a65c6d796136a846aa2df648b 100644 (file)
@@ -1036,6 +1036,7 @@ static int iucv_sock_sendmsg(struct socket *sock, struct msghdr *msg,
 {
        struct sock *sk = sock->sk;
        struct iucv_sock *iucv = iucv_sk(sk);
+       size_t headroom, linear;
        struct sk_buff *skb;
        struct iucv_message txmsg = {0};
        struct cmsghdr *cmsg;
@@ -1113,20 +1114,31 @@ static int iucv_sock_sendmsg(struct socket *sock, struct msghdr *msg,
         * this is fine for SOCK_SEQPACKET (unless we want to support
         * 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)
-               skb = sock_alloc_send_skb(sk,
-                       len + sizeof(struct af_iucv_trans_hdr) + ETH_HLEN,
-                       noblock, &err);
-       else
-               skb = sock_alloc_send_skb(sk, len, noblock, &err);
+       headroom = (iucv->transport == AF_IUCV_TRANS_HIPER)
+                  ? sizeof(struct af_iucv_trans_hdr) + ETH_HLEN : 0;
+       if (headroom + len < PAGE_SIZE) {
+               linear = len;
+       } else {
+               /* In nonlinear "classic" iucv skb,
+                * reserve space for iucv_array
+                */
+               if (iucv->transport != AF_IUCV_TRANS_HIPER)
+                       headroom += sizeof(struct iucv_array) *
+                                   (MAX_SKB_FRAGS + 1);
+               linear = PAGE_SIZE - headroom;
+       }
+       skb = sock_alloc_send_pskb(sk, headroom + linear, len - linear,
+                                  noblock, &err, 0);
        if (!skb)
                goto out;
-       if (iucv->transport == AF_IUCV_TRANS_HIPER)
-               skb_reserve(skb, sizeof(struct af_iucv_trans_hdr) + ETH_HLEN);
-       if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
-               err = -EFAULT;
+       if (headroom)
+               skb_reserve(skb, headroom);
+       skb_put(skb, linear);
+       skb->len = len;
+       skb->data_len = len - linear;
+       err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, len);
+       if (err)
                goto fail;
-       }
 
        /* wait if outstanding messages for iucv path has reached */
        timeo = sock_sndtimeo(sk, noblock);
@@ -1151,49 +1163,67 @@ static int iucv_sock_sendmsg(struct socket *sock, struct msghdr *msg,
                        atomic_dec(&iucv->msg_sent);
                        goto fail;
                }
-               goto release;
-       }
-       skb_queue_tail(&iucv->send_skb_q, skb);
-
-       if (((iucv->path->flags & IUCV_IPRMDATA) & iucv->flags)
-             && skb->len <= 7) {
-               err = iucv_send_iprm(iucv->path, &txmsg, skb);
+       } else { /* Classic VM IUCV transport */
+               skb_queue_tail(&iucv->send_skb_q, skb);
+
+               if (((iucv->path->flags & IUCV_IPRMDATA) & iucv->flags) &&
+                   skb->len <= 7) {
+                       err = iucv_send_iprm(iucv->path, &txmsg, skb);
+
+                       /* on success: there is no message_complete callback */
+                       /* for an IPRMDATA msg; remove skb from send queue   */
+                       if (err == 0) {
+                               skb_unlink(skb, &iucv->send_skb_q);
+                               kfree_skb(skb);
+                       }
 
-               /* on success: there is no message_complete callback
-                * for an IPRMDATA msg; remove skb from send queue */
-               if (err == 0) {
-                       skb_unlink(skb, &iucv->send_skb_q);
-                       kfree_skb(skb);
+                       /* this error should never happen since the     */
+                       /* IUCV_IPRMDATA path flag is set... sever path */
+                       if (err == 0x15) {
+                               pr_iucv->path_sever(iucv->path, NULL);
+                               skb_unlink(skb, &iucv->send_skb_q);
+                               err = -EPIPE;
+                               goto fail;
+                       }
+               } else if (skb_is_nonlinear(skb)) {
+                       struct iucv_array *iba = (struct iucv_array *)skb->head;
+                       int i;
+
+                       /* skip iucv_array lying in the headroom */
+                       iba[0].address = (u32)(addr_t)skb->data;
+                       iba[0].length = (u32)skb_headlen(skb);
+                       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+                               skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+                               iba[i + 1].address =
+                                       (u32)(addr_t)skb_frag_address(frag);
+                               iba[i + 1].length = (u32)skb_frag_size(frag);
+                       }
+                       err = pr_iucv->message_send(iucv->path, &txmsg,
+                                                   IUCV_IPBUFLST, 0,
+                                                   (void *)iba, skb->len);
+               } else { /* non-IPRM Linear skb */
+                       err = pr_iucv->message_send(iucv->path, &txmsg,
+                                       0, 0, (void *)skb->data, skb->len);
                }
-
-               /* this error should never happen since the
-                * IUCV_IPRMDATA path flag is set... sever path */
-               if (err == 0x15) {
-                       pr_iucv->path_sever(iucv->path, NULL);
+               if (err) {
+                       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",
+                                       appl_id, user_id);
+                               err = -EAGAIN;
+                       } else {
+                               err = -EPIPE;
+                       }
                        skb_unlink(skb, &iucv->send_skb_q);
-                       err = -EPIPE;
                        goto fail;
                }
-       } else
-               err = pr_iucv->message_send(iucv->path, &txmsg, 0, 0,
-                                       (void *) skb->data, skb->len);
-       if (err) {
-               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",
-                               appl_id, user_id);
-                       err = -EAGAIN;
-               } else
-                       err = -EPIPE;
-               skb_unlink(skb, &iucv->send_skb_q);
-               goto fail;
        }
 
-release:
        release_sock(sk);
        return len;
 
@@ -1204,42 +1234,32 @@ out:
        return err;
 }
 
-/* iucv_fragment_skb() - Fragment a single IUCV message into multiple skb's
- *
- * Locking: must be called with message_q.lock held
- */
-static int iucv_fragment_skb(struct sock *sk, struct sk_buff *skb, int len)
+static struct sk_buff *alloc_iucv_recv_skb(unsigned long len)
 {
-       int dataleft, size, copied = 0;
-       struct sk_buff *nskb;
-
-       dataleft = len;
-       while (dataleft) {
-               if (dataleft >= sk->sk_rcvbuf / 4)
-                       size = sk->sk_rcvbuf / 4;
-               else
-                       size = dataleft;
-
-               nskb = alloc_skb(size, GFP_ATOMIC | GFP_DMA);
-               if (!nskb)
-                       return -ENOMEM;
-
-               /* copy target class to control buffer of new skb */
-               IUCV_SKB_CB(nskb)->class = IUCV_SKB_CB(skb)->class;
-
-               /* copy data fragment */
-               memcpy(nskb->data, skb->data + copied, size);
-               copied += size;
-               dataleft -= size;
-
-               skb_reset_transport_header(nskb);
-               skb_reset_network_header(nskb);
-               nskb->len = size;
+       size_t headroom, linear;
+       struct sk_buff *skb;
+       int err;
 
-               skb_queue_tail(&iucv_sk(sk)->backlog_skb_q, nskb);
+       if (len < PAGE_SIZE) {
+               headroom = 0;
+               linear = len;
+       } else {
+               headroom = sizeof(struct iucv_array) * (MAX_SKB_FRAGS + 1);
+               linear = PAGE_SIZE - headroom;
+       }
+       skb = alloc_skb_with_frags(headroom + linear, len - linear,
+                                  0, &err, GFP_ATOMIC | GFP_DMA);
+       WARN_ONCE(!skb,
+                 "alloc of recv iucv skb len=%lu failed with errcode=%d\n",
+                 len, err);
+       if (skb) {
+               if (headroom)
+                       skb_reserve(skb, headroom);
+               skb_put(skb, linear);
+               skb->len = len;
+               skb->data_len = len - linear;
        }
-
-       return 0;
+       return skb;
 }
 
 /* iucv_process_message() - Receive a single outstanding IUCV message
@@ -1266,31 +1286,32 @@ static void iucv_process_message(struct sock *sk, struct sk_buff *skb,
                        skb->len = 0;
                }
        } else {
-               rc = pr_iucv->message_receive(path, msg,
+               if (skb_is_nonlinear(skb)) {
+                       struct iucv_array *iba = (struct iucv_array *)skb->head;
+                       int i;
+
+                       iba[0].address = (u32)(addr_t)skb->data;
+                       iba[0].length = (u32)skb_headlen(skb);
+                       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+                               skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+                               iba[i + 1].address =
+                                       (u32)(addr_t)skb_frag_address(frag);
+                               iba[i + 1].length = (u32)skb_frag_size(frag);
+                       }
+                       rc = pr_iucv->message_receive(path, msg,
+                                             IUCV_IPBUFLST,
+                                             (void *)iba, len, NULL);
+               } else {
+                       rc = pr_iucv->message_receive(path, msg,
                                              msg->flags & IUCV_IPRMDATA,
                                              skb->data, len, NULL);
+               }
                if (rc) {
                        kfree_skb(skb);
                        return;
                }
-               /* we need to fragment iucv messages for SOCK_STREAM only;
-                * for SOCK_SEQPACKET, it is only relevant if we support
-                * record segmentation using MSG_EOR (see also recvmsg()) */
-               if (sk->sk_type == SOCK_STREAM &&
-                   skb->truesize >= sk->sk_rcvbuf / 4) {
-                       rc = iucv_fragment_skb(sk, skb, len);
-                       kfree_skb(skb);
-                       skb = NULL;
-                       if (rc) {
-                               pr_iucv->path_sever(path, NULL);
-                               return;
-                       }
-                       skb = skb_dequeue(&iucv_sk(sk)->backlog_skb_q);
-               } else {
-                       skb_reset_transport_header(skb);
-                       skb_reset_network_header(skb);
-                       skb->len = len;
-               }
+               WARN_ON_ONCE(skb->len != len);
        }
 
        IUCV_SKB_CB(skb)->offset = 0;
@@ -1309,7 +1330,7 @@ static void iucv_process_message_q(struct sock *sk)
        struct sock_msg_q *p, *n;
 
        list_for_each_entry_safe(p, n, &iucv->message_q.list, list) {
-               skb = alloc_skb(iucv_msg_length(&p->msg), GFP_ATOMIC | GFP_DMA);
+               skb = alloc_iucv_recv_skb(iucv_msg_length(&p->msg));
                if (!skb)
                        break;
                iucv_process_message(sk, skb, p->path, &p->msg);
@@ -1804,7 +1825,7 @@ static void iucv_callback_rx(struct iucv_path *path, struct iucv_message *msg)
        if (len > sk->sk_rcvbuf)
                goto save_message;
 
-       skb = alloc_skb(iucv_msg_length(msg), GFP_ATOMIC | GFP_DMA);
+       skb = alloc_iucv_recv_skb(iucv_msg_length(msg));
        if (!skb)
                goto save_message;
 
This page took 0.028711 seconds and 5 git commands to generate.