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

Commit 28d64271 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller
Browse files

net: attempt high order allocations in sock_alloc_send_pskb()



Adding paged frags skbs to af_unix sockets introduced a performance
regression on large sends because of additional page allocations, even
if each skb could carry at least 100% more payload than before.

We can instruct sock_alloc_send_pskb() to attempt high order
allocations.

Most of the time, it does a single page allocation instead of 8.

I added an additional parameter to sock_alloc_send_pskb() to
let other users to opt-in for this new feature on followup patches.

Tested:

Before patch :

$ netperf -t STREAM_STREAM
STREAM STREAM TEST
Recv   Send    Send
Socket Socket  Message  Elapsed
Size   Size    Size     Time     Throughput
bytes  bytes   bytes    secs.    10^6bits/sec

 2304  212992  212992    10.00    46861.15

After patch :

$ netperf -t STREAM_STREAM
STREAM STREAM TEST
Recv   Send    Send
Socket Socket  Message  Elapsed
Size   Size    Size     Time     Throughput
bytes  bytes   bytes    secs.    10^6bits/sec

 2304  212992  212992    10.00    57981.11

Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e370a723
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -524,7 +524,7 @@ static inline struct sk_buff *macvtap_alloc_skb(struct sock *sk, size_t prepad,
		linear = len;

	skb = sock_alloc_send_pskb(sk, prepad + linear, len - linear, noblock,
				   err);
				   err, 0);
	if (!skb)
		return NULL;

+1 −1
Original line number Diff line number Diff line
@@ -949,7 +949,7 @@ static struct sk_buff *tun_alloc_skb(struct tun_file *tfile,
		linear = len;

	skb = sock_alloc_send_pskb(sk, prepad + linear, len - linear, noblock,
				   &err);
				   &err, 0);
	if (!skb)
		return ERR_PTR(err);

+2 −1
Original line number Diff line number Diff line
@@ -1539,7 +1539,8 @@ extern struct sk_buff *sock_alloc_send_pskb(struct sock *sk,
						      unsigned long header_len,
						      unsigned long data_len,
						      int noblock,
						      int *errcode);
						      int *errcode,
						      int max_page_order);
extern void *sock_kmalloc(struct sock *sk, int size,
			  gfp_t priority);
extern void sock_kfree_s(struct sock *sk, void *mem, int size);
+51 −49
Original line number Diff line number Diff line
@@ -1741,24 +1741,23 @@ static long sock_wait_for_wmem(struct sock *sk, long timeo)

struct sk_buff *sock_alloc_send_pskb(struct sock *sk, unsigned long header_len,
				     unsigned long data_len, int noblock,
				     int *errcode)
				     int *errcode, int max_page_order)
{
	struct sk_buff *skb;
	struct sk_buff *skb = NULL;
	unsigned long chunk;
	gfp_t gfp_mask;
	long timeo;
	int err;
	int npages = (data_len + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
	struct page *page;
	int i;

	err = -EMSGSIZE;
	if (npages > MAX_SKB_FRAGS)
		goto failure;

	gfp_mask = sk->sk_allocation;
	if (gfp_mask & __GFP_WAIT)
		gfp_mask |= __GFP_REPEAT;

	timeo = sock_sndtimeo(sk, noblock);
	while (1) {
	while (!skb) {
		err = sock_error(sk);
		if (err != 0)
			goto failure;
@@ -1767,50 +1766,52 @@ struct sk_buff *sock_alloc_send_pskb(struct sock *sk, unsigned long header_len,
		if (sk->sk_shutdown & SEND_SHUTDOWN)
			goto failure;

		if (atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf) {
			skb = alloc_skb(header_len, gfp_mask);
			if (skb) {
				int i;
		if (atomic_read(&sk->sk_wmem_alloc) >= sk->sk_sndbuf) {
			set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
			set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
			err = -EAGAIN;
			if (!timeo)
				goto failure;
			if (signal_pending(current))
				goto interrupted;
			timeo = sock_wait_for_wmem(sk, timeo);
			continue;
		}

				/* No pages, we're done... */
				if (!data_len)
					break;
		err = -ENOBUFS;
		gfp_mask = sk->sk_allocation;
		if (gfp_mask & __GFP_WAIT)
			gfp_mask |= __GFP_REPEAT;

		skb = alloc_skb(header_len, gfp_mask);
		if (!skb)
			goto failure;

		skb->truesize += data_len;
				skb_shinfo(skb)->nr_frags = npages;
				for (i = 0; i < npages; i++) {
					struct page *page;

					page = alloc_pages(sk->sk_allocation, 0);
					if (!page) {
						err = -ENOBUFS;
						skb_shinfo(skb)->nr_frags = i;
						kfree_skb(skb);
						goto failure;
					}
		for (i = 0; npages > 0; i++) {
			int order = max_page_order;

					__skb_fill_page_desc(skb, i,
							page, 0,
							(data_len >= PAGE_SIZE ?
							 PAGE_SIZE :
							 data_len));
					data_len -= PAGE_SIZE;
			while (order) {
				if (npages >= 1 << order) {
					page = alloc_pages(sk->sk_allocation |
							   __GFP_COMP | __GFP_NOWARN,
							   order);
					if (page)
						goto fill_page;
				}

				/* Full success... */
				break;
				order--;
			}
			err = -ENOBUFS;
			page = alloc_page(sk->sk_allocation);
			if (!page)
				goto failure;
fill_page:
			chunk = min_t(unsigned long, data_len,
				      PAGE_SIZE << order);
			skb_fill_page_desc(skb, i, page, 0, chunk);
			data_len -= chunk;
			npages -= 1 << order;
		}
		set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
		set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
		err = -EAGAIN;
		if (!timeo)
			goto failure;
		if (signal_pending(current))
			goto interrupted;
		timeo = sock_wait_for_wmem(sk, timeo);
	}

	skb_set_owner_w(skb, sk);
@@ -1819,6 +1820,7 @@ struct sk_buff *sock_alloc_send_pskb(struct sock *sk, unsigned long header_len,
interrupted:
	err = sock_intr_errno(timeo);
failure:
	kfree_skb(skb);
	*errcode = err;
	return NULL;
}
@@ -1827,7 +1829,7 @@ EXPORT_SYMBOL(sock_alloc_send_pskb);
struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size,
				    int noblock, int *errcode)
{
	return sock_alloc_send_pskb(sk, size, 0, noblock, errcode);
	return sock_alloc_send_pskb(sk, size, 0, noblock, errcode, 0);
}
EXPORT_SYMBOL(sock_alloc_send_skb);

+1 −1
Original line number Diff line number Diff line
@@ -2181,7 +2181,7 @@ static struct sk_buff *packet_alloc_skb(struct sock *sk, size_t prepad,
		linear = len;

	skb = sock_alloc_send_pskb(sk, prepad + linear, len - linear, noblock,
				   err);
				   err, 0);
	if (!skb)
		return NULL;

Loading