Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e5374399 authored by Eugene Crosser's avatar Eugene Crosser Committed by David S. Miller
Browse files

af_iucv: use paged SKBs for big outbound messages



When an outbound message is bigger than a page, allocate and fill
a paged SKB, and subsequently use IUCV send primitive with IPBUFLST
flag. This relaxes the pressure to allocate big contiguous kernel
buffers.

Signed-off-by: default avatarEugene Crosser <Eugene.Crosser@ru.ibm.com>
Signed-off-by: default avatarUrsula Braun <ubraun@linux.vnet.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 818d49ad
Loading
Loading
Loading
Loading
+77 −47
Original line number Diff line number Diff line
@@ -1033,6 +1033,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;
@@ -1110,20 +1111,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);
@@ -1148,49 +1160,67 @@ static int iucv_sock_sendmsg(struct socket *sock, struct msghdr *msg,
			atomic_dec(&iucv->msg_sent);
			goto fail;
		}
		goto release;
	}
	} else { /* Classic VM IUCV transport */
		skb_queue_tail(&iucv->send_skb_q, skb);

	if (((iucv->path->flags & IUCV_IPRMDATA) & iucv->flags)
	      && skb->len <= 7) {
		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 */
			/* 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 */
			/* 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
		err = pr_iucv->message_send(iucv->path, &txmsg, 0, 0,
					(void *) skb->data, skb->len);
		} 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);
		}
		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",
				pr_err(
		"Application %s on z/VM guest %s exceeds message limit\n",
					appl_id, user_id);
				err = -EAGAIN;
		} else
			} else {
				err = -EPIPE;
			}
			skb_unlink(skb, &iucv->send_skb_q);
			goto fail;
		}
	}

release:
	release_sock(sk);
	return len;