1164 lines · 26.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* Copyright (c) 2017 - 2018 Covalent IO, Inc. http://covalent.io */ #include <linux/skmsg.h>#include <linux/skbuff.h>#include <linux/scatterlist.h> #include <net/sock.h>#include <net/tcp.h>#include <net/tls.h> static bool sk_msg_try_coalesce_ok(struct sk_msg *msg, int elem_first_coalesce){	if (msg->sg.end > msg->sg.start &&	    elem_first_coalesce < msg->sg.end)		return true; 	if (msg->sg.end < msg->sg.start &&	    (elem_first_coalesce > msg->sg.start ||	     elem_first_coalesce < msg->sg.end))		return true; 	return false;} int sk_msg_alloc(struct sock *sk, struct sk_msg *msg, int len,		 int elem_first_coalesce){	struct page_frag *pfrag = sk_page_frag(sk);	int ret = 0; 	len -= msg->sg.size;	while (len > 0) {		struct scatterlist *sge;		u32 orig_offset;		int use, i; 		if (!sk_page_frag_refill(sk, pfrag))			return -ENOMEM; 		orig_offset = pfrag->offset;		use = min_t(int, len, pfrag->size - orig_offset);		if (!sk_wmem_schedule(sk, use))			return -ENOMEM; 		i = msg->sg.end;		sk_msg_iter_var_prev(i);		sge = &msg->sg.data[i]; 		if (sk_msg_try_coalesce_ok(msg, elem_first_coalesce) &&		    sg_page(sge) == pfrag->page &&		    sge->offset + sge->length == orig_offset) {			sge->length += use;		} else {			if (sk_msg_full(msg)) {				ret = -ENOSPC;				break;			} 			sge = &msg->sg.data[msg->sg.end];			sg_unmark_end(sge);			sg_set_page(sge, pfrag->page, use, orig_offset);			get_page(pfrag->page);			sk_msg_iter_next(msg, end);		} 		sk_mem_charge(sk, use);		msg->sg.size += use;		pfrag->offset += use;		len -= use;	} 	return ret;}EXPORT_SYMBOL_GPL(sk_msg_alloc); int sk_msg_clone(struct sock *sk, struct sk_msg *dst, struct sk_msg *src,		 u32 off, u32 len){	int i = src->sg.start;	struct scatterlist *sge = sk_msg_elem(src, i);	struct scatterlist *sgd = NULL;	u32 sge_len, sge_off; 	while (off) {		if (sge->length > off)			break;		off -= sge->length;		sk_msg_iter_var_next(i);		if (i == src->sg.end && off)			return -ENOSPC;		sge = sk_msg_elem(src, i);	} 	while (len) {		sge_len = sge->length - off;		if (sge_len > len)			sge_len = len; 		if (dst->sg.end)			sgd = sk_msg_elem(dst, dst->sg.end - 1); 		if (sgd &&		    (sg_page(sge) == sg_page(sgd)) &&		    (sg_virt(sge) + off == sg_virt(sgd) + sgd->length)) {			sgd->length += sge_len;			dst->sg.size += sge_len;		} else if (!sk_msg_full(dst)) {			sge_off = sge->offset + off;			sk_msg_page_add(dst, sg_page(sge), sge_len, sge_off);		} else {			return -ENOSPC;		} 		off = 0;		len -= sge_len;		sk_mem_charge(sk, sge_len);		sk_msg_iter_var_next(i);		if (i == src->sg.end && len)			return -ENOSPC;		sge = sk_msg_elem(src, i);	} 	return 0;}EXPORT_SYMBOL_GPL(sk_msg_clone); void sk_msg_return_zero(struct sock *sk, struct sk_msg *msg, int bytes){	int i = msg->sg.start; 	do {		struct scatterlist *sge = sk_msg_elem(msg, i); 		if (bytes < sge->length) {			sge->length -= bytes;			sge->offset += bytes;			sk_mem_uncharge(sk, bytes);			break;		} 		sk_mem_uncharge(sk, sge->length);		bytes -= sge->length;		sge->length = 0;		sge->offset = 0;		sk_msg_iter_var_next(i);	} while (bytes && i != msg->sg.end);	msg->sg.start = i;}EXPORT_SYMBOL_GPL(sk_msg_return_zero); void sk_msg_return(struct sock *sk, struct sk_msg *msg, int bytes){	int i = msg->sg.start; 	do {		struct scatterlist *sge = &msg->sg.data[i];		int uncharge = (bytes < sge->length) ? bytes : sge->length; 		sk_mem_uncharge(sk, uncharge);		bytes -= uncharge;		sk_msg_iter_var_next(i);	} while (i != msg->sg.end);}EXPORT_SYMBOL_GPL(sk_msg_return); static int sk_msg_free_elem(struct sock *sk, struct sk_msg *msg, u32 i,			    bool charge){	struct scatterlist *sge = sk_msg_elem(msg, i);	u32 len = sge->length; 	/* When the skb owns the memory we free it from consume_skb path. */	if (!msg->skb) {		if (charge)			sk_mem_uncharge(sk, len);		put_page(sg_page(sge));	}	memset(sge, 0, sizeof(*sge));	return len;} static int __sk_msg_free(struct sock *sk, struct sk_msg *msg, u32 i,			 bool charge){	struct scatterlist *sge = sk_msg_elem(msg, i);	int freed = 0; 	while (msg->sg.size) {		msg->sg.size -= sge->length;		freed += sk_msg_free_elem(sk, msg, i, charge);		sk_msg_iter_var_next(i);		sk_msg_check_to_free(msg, i, msg->sg.size);		sge = sk_msg_elem(msg, i);	}	consume_skb(msg->skb);	sk_msg_init(msg);	return freed;} int sk_msg_free_nocharge(struct sock *sk, struct sk_msg *msg){	return __sk_msg_free(sk, msg, msg->sg.start, false);}EXPORT_SYMBOL_GPL(sk_msg_free_nocharge); int sk_msg_free(struct sock *sk, struct sk_msg *msg){	return __sk_msg_free(sk, msg, msg->sg.start, true);}EXPORT_SYMBOL_GPL(sk_msg_free); static void __sk_msg_free_partial(struct sock *sk, struct sk_msg *msg,				  u32 bytes, bool charge){	struct scatterlist *sge;	u32 i = msg->sg.start; 	while (bytes) {		sge = sk_msg_elem(msg, i);		if (!sge->length)			break;		if (bytes < sge->length) {			if (charge)				sk_mem_uncharge(sk, bytes);			sge->length -= bytes;			sge->offset += bytes;			msg->sg.size -= bytes;			break;		} 		msg->sg.size -= sge->length;		bytes -= sge->length;		sk_msg_free_elem(sk, msg, i, charge);		sk_msg_iter_var_next(i);		sk_msg_check_to_free(msg, i, bytes);	}	msg->sg.start = i;} void sk_msg_free_partial(struct sock *sk, struct sk_msg *msg, u32 bytes){	__sk_msg_free_partial(sk, msg, bytes, true);}EXPORT_SYMBOL_GPL(sk_msg_free_partial); void sk_msg_free_partial_nocharge(struct sock *sk, struct sk_msg *msg,				  u32 bytes){	__sk_msg_free_partial(sk, msg, bytes, false);} void sk_msg_trim(struct sock *sk, struct sk_msg *msg, int len){	int trim = msg->sg.size - len;	u32 i = msg->sg.end; 	if (trim <= 0) {		WARN_ON(trim < 0);		return;	} 	sk_msg_iter_var_prev(i);	msg->sg.size = len;	while (msg->sg.data[i].length &&	       trim >= msg->sg.data[i].length) {		trim -= msg->sg.data[i].length;		sk_msg_free_elem(sk, msg, i, true);		sk_msg_iter_var_prev(i);		if (!trim)			goto out;	} 	msg->sg.data[i].length -= trim;	sk_mem_uncharge(sk, trim);	/* Adjust copybreak if it falls into the trimmed part of last buf */	if (msg->sg.curr == i && msg->sg.copybreak > msg->sg.data[i].length)		msg->sg.copybreak = msg->sg.data[i].length;out:	sk_msg_iter_var_next(i);	msg->sg.end = i; 	/* If we trim data a full sg elem before curr pointer update	 * copybreak and current so that any future copy operations	 * start at new copy location.	 * However trimed data that has not yet been used in a copy op	 * does not require an update.	 */	if (!msg->sg.size) {		msg->sg.curr = msg->sg.start;		msg->sg.copybreak = 0;	} else if (sk_msg_iter_dist(msg->sg.start, msg->sg.curr) >=		   sk_msg_iter_dist(msg->sg.start, msg->sg.end)) {		sk_msg_iter_var_prev(i);		msg->sg.curr = i;		msg->sg.copybreak = msg->sg.data[i].length;	}}EXPORT_SYMBOL_GPL(sk_msg_trim); int sk_msg_zerocopy_from_iter(struct sock *sk, struct iov_iter *from,			      struct sk_msg *msg, u32 bytes){	int i, maxpages, ret = 0, num_elems = sk_msg_elem_used(msg);	const int to_max_pages = MAX_MSG_FRAGS;	struct page *pages[MAX_MSG_FRAGS];	ssize_t orig, copied, use, offset; 	orig = msg->sg.size;	while (bytes > 0) {		i = 0;		maxpages = to_max_pages - num_elems;		if (maxpages == 0) {			ret = -EFAULT;			goto out;		} 		copied = iov_iter_get_pages(from, pages, bytes, maxpages,					    &offset);		if (copied <= 0) {			ret = -EFAULT;			goto out;		} 		iov_iter_advance(from, copied);		bytes -= copied;		msg->sg.size += copied; 		while (copied) {			use = min_t(int, copied, PAGE_SIZE - offset);			sg_set_page(&msg->sg.data[msg->sg.end],				    pages[i], use, offset);			sg_unmark_end(&msg->sg.data[msg->sg.end]);			sk_mem_charge(sk, use); 			offset = 0;			copied -= use;			sk_msg_iter_next(msg, end);			num_elems++;			i++;		}		/* When zerocopy is mixed with sk_msg_*copy* operations we		 * may have a copybreak set in this case clear and prefer		 * zerocopy remainder when possible.		 */		msg->sg.copybreak = 0;		msg->sg.curr = msg->sg.end;	}out:	/* Revert iov_iter updates, msg will need to use 'trim' later if it	 * also needs to be cleared.	 */	if (ret)		iov_iter_revert(from, msg->sg.size - orig);	return ret;}EXPORT_SYMBOL_GPL(sk_msg_zerocopy_from_iter); int sk_msg_memcopy_from_iter(struct sock *sk, struct iov_iter *from,			     struct sk_msg *msg, u32 bytes){	int ret = -ENOSPC, i = msg->sg.curr;	struct scatterlist *sge;	u32 copy, buf_size;	void *to; 	do {		sge = sk_msg_elem(msg, i);		/* This is possible if a trim operation shrunk the buffer */		if (msg->sg.copybreak >= sge->length) {			msg->sg.copybreak = 0;			sk_msg_iter_var_next(i);			if (i == msg->sg.end)				break;			sge = sk_msg_elem(msg, i);		} 		buf_size = sge->length - msg->sg.copybreak;		copy = (buf_size > bytes) ? bytes : buf_size;		to = sg_virt(sge) + msg->sg.copybreak;		msg->sg.copybreak += copy;		if (sk->sk_route_caps & NETIF_F_NOCACHE_COPY)			ret = copy_from_iter_nocache(to, copy, from);		else			ret = copy_from_iter(to, copy, from);		if (ret != copy) {			ret = -EFAULT;			goto out;		}		bytes -= copy;		if (!bytes)			break;		msg->sg.copybreak = 0;		sk_msg_iter_var_next(i);	} while (i != msg->sg.end);out:	msg->sg.curr = i;	return ret;}EXPORT_SYMBOL_GPL(sk_msg_memcopy_from_iter); int sk_msg_wait_data(struct sock *sk, struct sk_psock *psock, int flags,		     long timeo, int *err){	DEFINE_WAIT_FUNC(wait, woken_wake_function);	int ret = 0; 	if (sk->sk_shutdown & RCV_SHUTDOWN)		return 1; 	if (!timeo)		return ret; 	add_wait_queue(sk_sleep(sk), &wait);	sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);	ret = sk_wait_event(sk, &timeo,			    !list_empty(&psock->ingress_msg) ||			    !skb_queue_empty(&sk->sk_receive_queue), &wait);	sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);	remove_wait_queue(sk_sleep(sk), &wait);	return ret;}EXPORT_SYMBOL_GPL(sk_msg_wait_data); /* Receive sk_msg from psock->ingress_msg to @msg. */int sk_msg_recvmsg(struct sock *sk, struct sk_psock *psock, struct msghdr *msg,		   int len, int flags){	struct iov_iter *iter = &msg->msg_iter;	int peek = flags & MSG_PEEK;	struct sk_msg *msg_rx;	int i, copied = 0; 	msg_rx = sk_psock_peek_msg(psock);	while (copied != len) {		struct scatterlist *sge; 		if (unlikely(!msg_rx))			break; 		i = msg_rx->sg.start;		do {			struct page *page;			int copy; 			sge = sk_msg_elem(msg_rx, i);			copy = sge->length;			page = sg_page(sge);			if (copied + copy > len)				copy = len - copied;			copy = copy_page_to_iter(page, sge->offset, copy, iter);			if (!copy)				return copied ? copied : -EFAULT; 			copied += copy;			if (likely(!peek)) {				sge->offset += copy;				sge->length -= copy;				if (!msg_rx->skb)					sk_mem_uncharge(sk, copy);				msg_rx->sg.size -= copy; 				if (!sge->length) {					sk_msg_iter_var_next(i);					if (!msg_rx->skb)						put_page(page);				}			} else {				/* Lets not optimize peek case if copy_page_to_iter				 * didn't copy the entire length lets just break.				 */				if (copy != sge->length)					return copied;				sk_msg_iter_var_next(i);			} 			if (copied == len)				break;		} while (i != msg_rx->sg.end); 		if (unlikely(peek)) {			msg_rx = sk_psock_next_msg(psock, msg_rx);			if (!msg_rx)				break;			continue;		} 		msg_rx->sg.start = i;		if (!sge->length && msg_rx->sg.start == msg_rx->sg.end) {			msg_rx = sk_psock_dequeue_msg(psock);			kfree_sk_msg(msg_rx);		}		msg_rx = sk_psock_peek_msg(psock);	} 	return copied;}EXPORT_SYMBOL_GPL(sk_msg_recvmsg); static struct sk_msg *sk_psock_create_ingress_msg(struct sock *sk,

Lines 1–500 of 1164.