Skip to contentSkip to conversation

VM deadlock prevention core -v3

RFC PATCH In review Peter Zijlstra 3 participants 6 messages Aug 10, 2006 – Aug 11, 2006

Subsystems (10)
lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 VM deadlock prevention core -v3

Hi,

So I try again, please tell me if I'm still on crack and should go detox. However if you do so, I kindly request some words on the how and why of it.

so I try to map to net_device in several fashions:

1) netdev_alloc_skb()
- has an argument with the actual incoming device

2) free_skb_pages()
- uses: skb->input_dev ?: skb->dev

this device is pinned by virtue of netdev_wait_memalloc(), which will wait until all skb->memalloc skbuffs are destroyed. This will delay module unload under severe memory pressure, I think this is acceptable as one has other problems at that point.

3) inet_sock_destruct(), sk_set_memalloc()
- both use: ip_dev_find(inet_sk(sk)->rcv_saddr))

if the later two methods do not yield the same net_device the first has, weird and wonderfull stuff will happen.

Why, currently the sole purpose is to be able to limit the number of memalloc skbs per device (and for me to learn some).

I suspect something as simple as a bridge device will destroy this, with that I suspect (3) will return the bride device instead of the actual input device.

So, if I'm still busted, is there any hope for this approach?

If not, I'll have to go do global skb memalloc accounting (largesmp fanboys need not worry, this will only happen under severe load, at that point the box will have other issues) and fudge the per deviceness.

---

The core of the VM deadlock avoidance framework.

Quoted 1 line

From the 'user' side of things it provides a function to mark a 'struct sock'

as SOCK_MEMALLOC, meaning this socket may dip into the memalloc reserves on the receive side.

Quoted 1 line

From the net_device side of things, the extra 'struct net_device *' argument

to {,__}netdev_alloc_skb() is used to attribute/account the memalloc usage. When netdev_alloc_skb() finds it cannot allocate a struct sk_buff the regular way it will grab some memory from the memalloc reserve.

Drivers that have been converted to the netdev_alloc_skb() family will automatically receive this feature.

Network paths will drop !SOCK_MEMALLOC packets ASAP when reserve is being used.

Memalloc sk_buff allocations are not done from the SLAB but are done using alloc_pages(). sk_buff::memalloc records this exception so that kfree_skbmem() can do the right thing. NOTE this does not play very nice with skb_clone()

---
 include/linux/gfp.h       |    3 
 include/linux/mmzone.h    |    1 
 include/linux/netdevice.h |    7 ++
 include/linux/skbuff.h    |    3 
 include/net/sock.h        |    8 ++
 mm/page_alloc.c           |   46 ++++++++++++-
 net/core/dev.c            |   97 ++++++++++++++++++++++++++++
 net/core/skbuff.c         |  155 +++++++++++++++++++++++++++++++++++++++++++---
 net/core/sock.c           |   25 +++++++
 net/ethernet/eth.c        |    1 
 net/ipv4/af_inet.c        |    8 ++
 net/ipv4/icmp.c           |    3 
 net/ipv4/tcp_ipv4.c       |    3 
 net/ipv4/udp.c            |    8 ++
 14 files changed, 355 insertions(+), 13 deletions(-)
include/linux/gfp.h +2 -1
Index: linux-2.6/include/linux/gfp.h
===================================================================
@@ -46,6 +46,7 @@ struct vm_area_struct;
 #define __GFP_ZERO	((__force gfp_t)0x8000u)/* Return zeroed page on success */
 #define __GFP_NOMEMALLOC ((__force gfp_t)0x10000u) /* Don't use emergency reserves */
 #define __GFP_HARDWALL   ((__force gfp_t)0x20000u) /* Enforce hardwall cpuset memory allocs */
+#define __GFP_MEMALLOC  ((__force gfp_t)0x40000u) /* Use emergency reserves */
 
 #define __GFP_BITS_SHIFT 20	/* Room for 20 __GFP_FOO bits */
 #define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))
@@ -54,7 +55,7 @@ struct vm_area_struct;
 #define GFP_LEVEL_MASK (__GFP_WAIT|__GFP_HIGH|__GFP_IO|__GFP_FS| \
 			__GFP_COLD|__GFP_NOWARN|__GFP_REPEAT| \
 			__GFP_NOFAIL|__GFP_NORETRY|__GFP_NO_GROW|__GFP_COMP| \
-			__GFP_NOMEMALLOC|__GFP_HARDWALL)
+			__GFP_NOMEMALLOC|__GFP_HARDWALL|__GFP_MEMALLOC)
 
 /* This equals 0, but use constants in case they ever change */
 #define GFP_NOWAIT	(GFP_ATOMIC & ~__GFP_HIGH)
include/linux/mmzone.h +1 -0
Index: linux-2.6/include/linux/mmzone.h
===================================================================
@@ -420,6 +420,7 @@ int percpu_pagelist_fraction_sysctl_hand
 					void __user *, size_t *, loff_t *);
 int sysctl_min_unmapped_ratio_sysctl_handler(struct ctl_table *, int,
 			struct file *, void __user *, size_t *, loff_t *);
+int adjust_memalloc_reserve(int bytes);
 
 #include <linux/topology.h>
 /* Returns the number of the current Node. */
include/linux/netdevice.h +7 -0
Index: linux-2.6/include/linux/netdevice.h
===================================================================
@@ -409,6 +409,12 @@ struct net_device
 	struct Qdisc		*qdisc_sleeping;
 	struct list_head	qdisc_list;
 	unsigned long		tx_queue_len;	/* Max frames per queue allowed */
+	int			rx_reserve;
+	atomic_t		rx_reserve_used;
+
+	int			memalloc_socks;
+	unsigned long		memalloc_reserve;
+	spinlock_t		memalloc_lock;
 
 	/* Partially transmitted GSO packet. */
 	struct sk_buff		*gso_skb;
@@ -576,6 +582,7 @@ extern struct net_device	*__dev_get_by_n
 extern int		dev_alloc_name(struct net_device *dev, const char *name);
 extern int		dev_open(struct net_device *dev);
 extern int		dev_close(struct net_device *dev);
+extern int 		dev_adjust_memalloc(struct net_device *dev, int a);
 extern int		dev_queue_xmit(struct sk_buff *skb);
 extern int		register_netdevice(struct net_device *dev);
 extern int		unregister_netdevice(struct net_device *dev);
include/linux/skbuff.h +2 -1
Index: linux-2.6/include/linux/skbuff.h
===================================================================
@@ -282,7 +282,8 @@ struct sk_buff {
 				nfctinfo:3;
 	__u8			pkt_type:3,
 				fclone:2,
-				ipvs_property:1;
+				ipvs_property:1,
+				memalloc:1;
 	__be16			protocol;
 
 	void			(*destructor)(struct sk_buff *skb);
include/net/sock.h +8 -0
Index: linux-2.6/include/net/sock.h
===================================================================
@@ -391,6 +391,7 @@ enum sock_flags {
 	SOCK_RCVTSTAMP, /* %SO_TIMESTAMP setting */
 	SOCK_LOCALROUTE, /* route locally only, %SO_DONTROUTE setting */
 	SOCK_QUEUE_SHRUNK, /* write queue has been shrunk recently */
+	SOCK_MEMALLOC, /* protocol can use memalloc reserve */
 };
 
 static inline void sock_copy_flags(struct sock *nsk, struct sock *osk)
@@ -413,6 +414,13 @@ static inline int sock_flag(struct sock 
 	return test_bit(flag, &sk->sk_flags);
 }
 
+static inline int sk_is_memalloc(struct sock *sk)
+{
+	return sock_flag(sk, SOCK_MEMALLOC);
+}
+
+extern int sk_set_memalloc(struct sock *sk);
+
 static inline void sk_acceptq_removed(struct sock *sk)
 {
 	sk->sk_ack_backlog--;
mm/page_alloc.c +43 -3

70 lines not on this page · raw message

net/core/skbuff.c +148 -7

250 lines not on this page · raw message

net/ethernet/eth.c +1 -0
Index: linux-2.6/net/ethernet/eth.c
===================================================================
@@ -275,6 +275,7 @@ void ether_setup(struct net_device *dev)
 	dev->mtu		= ETH_DATA_LEN;
 	dev->addr_len		= ETH_ALEN;
 	dev->tx_queue_len	= 1000;	/* Ethernet wants good queues */	
+	dev->rx_reserve		= 64;
 	dev->flags		= IFF_BROADCAST|IFF_MULTICAST;
 	
 	memset(dev->broadcast,0xFF, ETH_ALEN);
net/ipv4/icmp.c +3 -0
Index: linux-2.6/net/ipv4/icmp.c
===================================================================
@@ -938,6 +938,9 @@ int icmp_rcv(struct sk_buff *skb)
 			goto error;
 	}
 
+	if (unlikely(skb->memalloc))
+		goto drop;
+
 	if (!pskb_pull(skb, sizeof(struct icmphdr)))
 		goto error;
 
net/ipv4/tcp_ipv4.c +3 -0
Index: linux-2.6/net/ipv4/tcp_ipv4.c
===================================================================
@@ -1093,6 +1093,9 @@ int tcp_v4_rcv(struct sk_buff *skb)
 	if (!sk)
 		goto no_tcp_socket;
 
+	if (unlikely(skb->memalloc && !sk_is_memalloc(sk)))
+		goto discard_and_relse;
+
 process:
 	if (sk->sk_state == TCP_TIME_WAIT)
 		goto do_time_wait;
net/ipv4/udp.c +7 -1
Index: linux-2.6/net/ipv4/udp.c
===================================================================
@@ -1136,7 +1136,12 @@ int udp_rcv(struct sk_buff *skb)
 	sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, skb->dev->ifindex);
 
 	if (sk != NULL) {
-		int ret = udp_queue_rcv_skb(sk, skb);
+		int ret;
+
+		if (unlikely(skb->memalloc && !sk_is_memalloc(sk)))
+			goto drop_noncritical;
+
+		ret = udp_queue_rcv_skb(sk, skb);
 		sock_put(sk);
 
 		/* a return value > 0 means to resubmit the input, but
@@ -1147,6 +1152,7 @@ int udp_rcv(struct sk_buff *skb)
 		return 0;
 	}
 
+drop_noncritical:
 	if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
 		goto drop;
 	nf_reset(skb);
net/core/dev.c +97 -0

133 lines not on this page · raw message

net/ipv4/af_inet.c +8 -0
Index: linux-2.6/net/ipv4/af_inet.c
===================================================================
@@ -132,6 +132,14 @@ void inet_sock_destruct(struct sock *sk)
 {
 	struct inet_sock *inet = inet_sk(sk);
 
+	if (sk_is_memalloc(sk)) {
+		struct net_device *dev = ip_dev_find(inet->rcv_saddr);
+		if (dev) {
+			dev_adjust_memalloc(dev, -1);
+			dev_put(dev);
+		}
+	}
+
 	__skb_queue_purge(&sk->sk_receive_queue);
 	__skb_queue_purge(&sk->sk_error_queue);
 
net/core/sock.c +25 -0
Index: linux-2.6/net/core/sock.c
===================================================================
@@ -111,6 +111,7 @@
 #include <linux/poll.h>
 #include <linux/tcp.h>
 #include <linux/init.h>
+#include <linux/inetdevice.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -195,6 +196,30 @@ __u32 sysctl_rmem_default = SK_RMEM_MAX;
 /* Maximal space eaten by iovec or ancilliary data plus some space */
 int sysctl_optmem_max = sizeof(unsigned long)*(2*UIO_MAXIOV + 512);
 
+/**
+ *	sk_set_memalloc - sets %SOCK_MEMALLOC
+ *	@sk: socket to set it on
+ *
+ *	Set %SOCK_MEMALLOC on a socket and increase the memalloc reserve
+ *	accordingly.
+ */
+int sk_set_memalloc(struct sock *sk)
+{
+	struct inet_sock *inet = inet_sk(sk);
+	struct net_device *dev = ip_dev_find(inet->rcv_saddr);
+	int err = 0;
+
+	if (!dev)
+		return -ENODEV;
+
+	if (!(err = dev_adjust_memalloc(dev, 1)))
+		sock_set_flag(sk, SOCK_MEMALLOC);
+
+	dev_put(dev);
+	return err;
+}
+EXPORT_SYMBOL_GPL(sk_set_memalloc);
+
 static int sock_set_timeout(long *timeo_p, char __user *optval, int optlen)
 {
 	struct timeval tv;
#1 lore raw
Quoted 1 line · Peter Zijlstra

Hi,

Hello, Peter.

Quoted 2 lines

So I try again, please tell me if I'm still on crack and should go detox. However if you do so, I kindly request some words on the how and why of it.

I think you should talk with doctor in that case, but not with kernel hackers :)

I have some comments about implementation, not overall design, since we have slightly diametral points of view there.

...

Quoted 62 lines
net/core/skbuff.c +42 -0
@@ -43,6 +43,7 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
+#include <linux/pagemap.h>
 #include <linux/interrupt.h>
 #include <linux/in.h>
 #include <linux/inet.h>
@@ -125,6 +126,8 @@ EXPORT_SYMBOL(skb_truesize_bug);
  *
  */
 
+#define ceiling_log2(x)	fls((x) - 1)
+
 /**
  *	__alloc_skb	-	allocate a network buffer
  *	@size: size to allocate
@@ -147,6 +150,59 @@ struct sk_buff *__alloc_skb(unsigned int
 	struct sk_buff *skb;
 	u8 *data;
 
+	size = SKB_DATA_ALIGN(size);
+
+	if (gfp_mask & __GFP_MEMALLOC) {
+		/*
+		 * Fallback allocation for memalloc reserves.
+		 *
+		 * the page is populated like so:
+		 *
+		 *   struct sk_buff
+		 *   [ struct sk_buff ]
+		 *   [ atomic_t ]
+		 *   unsigned int
+		 *   struct skb_shared_info
+		 *   char []
+		 *
+		 * We have to do higher order allocations for icky jumbo
+		 * frame drivers :-(. They really should be migrated to
+		 * scather/gather DMA and use skb fragments.
+		 */
+		unsigned int data_offset =
+			sizeof(struct sk_buff) + sizeof(unsigned int);
+		unsigned long length = size + data_offset +
+			sizeof(struct skb_shared_info);
+		unsigned int pages;
+		unsigned int order;
+		struct page *page;
+		void *kaddr;
+
+		/*
+		 * Force fclone alloc in order to fudge a lacking in skb_clone().
+		 */
+		fclone = 1;
+		if (fclone) {
+			data_offset += sizeof(struct sk_buff) + sizeof(atomic_t);
+			length += sizeof(struct sk_buff) + sizeof(atomic_t);
+		}
+		pages = (length + PAGE_SIZE - 1) >> PAGE_SHIFT;
+		order = ceiling_log2(pages);
+		skb = NULL;

net/core/skbuff.c struct sk_buff *__alloc_skb(unsigned int · line 201

Quoted 10 lines
net/core/skbuff.c +10 -0
+		if (!(page = alloc_pages(gfp_mask & ~__GFP_HIGHMEM, order)))
+			goto out;
+
+		kaddr = pfn_to_kaddr(page_to_pfn(page));
+		skb = (struct sk_buff *)kaddr;
+
+		*((unsigned int *)(kaddr + data_offset -
+					sizeof(unsigned int))) = order;
+		data = (u8 *)(kaddr + data_offset);
+

Tricky, but since you are using own allocator here, you could change it to be not so aggressive - i.e. do not round size to number of pages.

net/core/skbuff.c struct sk_buff *__alloc_skb(unsigned int · line 158

Quoted 11 lines
net/core/skbuff.c +3 -1
+		goto allocated;
+	}
+
 	cache = fclone ? skbuff_fclone_cache : skbuff_head_cache;
 
 	/* Get the HEAD */
@@ -155,12 +211,13 @@ struct sk_buff *__alloc_skb(unsigned int
 		goto out;
 
 	/* Get the DATA. Size must match skb_add_mtu(). */
-	size = SKB_DATA_ALIGN(size);

Bad sign.

Quoted 19 lines
net/core/skbuff.c +15 -0
 	data = ____kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
 	if (!data)
 		goto nodata;
 
+struct sk_buff *__netdev_alloc_skb(struct net_device *dev,
+		unsigned length, gfp_t gfp_mask)
+{
+	struct sk_buff *skb;
+
+	WARN_ON(gfp_mask & (__GFP_NOMEMALLOC | __GFP_MEMALLOC));
+	gfp_mask &= ~(__GFP_NOMEMALLOC | __GFP_MEMALLOC);
+
+	skb = ___netdev_alloc_skb(dev, length, gfp_mask | __GFP_NOMEMALLOC);
+	if (skb)
+		goto done;
+
+	if (atomic_read(&dev->rx_reserve_used) >=
+			dev->rx_reserve * dev->memalloc_socks)
+		goto out;

net/core/skbuff.c struct sk_buff *__netdev_alloc_skb(struc · line 372

Quoted 10 lines
net/core/skbuff.c +10 -0
+
+	/*
+	 * pre-inc guards against a race with netdev_wait_memalloc()
+	 */
+	atomic_inc(&dev->rx_reserve_used);
+	skb = ___netdev_alloc_skb(dev, length, gfp_mask | __GFP_MEMALLOC);
+	if (unlikely(!skb)) {
+		atomic_dec(&dev->rx_reserve_used);
+		goto out;
+	}

Since you have added atomic operation in that path, you can use device's reference counter instead and do not care that it can dissapear.

Quoted 82 lines
net/core/skbuff.c +39 -4
+done:
+	skb->dev = dev;
+out:
+	return skb;
+}
+
 static void skb_drop_list(struct sk_buff **listp)
 {
 	struct sk_buff *list = *listp;
@@ -313,10 +417,35 @@ static void skb_release_data(struct sk_b
 		if (skb_shinfo(skb)->frag_list)
 			skb_drop_fraglist(skb);
 
-		kfree(skb->head);
+		if (!skb->memalloc)
+			kfree(skb->head);
+		skb->head = NULL;
 	}
 }
 
+/**
+ *	free_skb_pages - frees a memalloced skbuff
+ *	@cache: fake %kmem_cache argument
+ *	@objp: %sk_buff pointer
+ *
+ *	Function is made to look like %kmem_cache_free so we can easily
+ *	substitue the free function in %kfree_skbmem.
+ */
+static void free_skb_pages(struct kmem_cache *cache, void *objp)
+{
+	struct sk_buff *skb = (struct sk_buff *)objp;
+	/*
+	 * The input_dev is the initial input device;
+	 * we have it pinned by virtue of rx_reserve_used not being zero.
+	 */
+	struct net_device *dev = skb->input_dev ?: skb->dev;
+	unsigned int order =
+		*(unsigned int *)(skb->head - sizeof(unsigned int));
+	if (!skb->head)
+		atomic_dec(&dev->rx_reserve_used);
+	free_pages((unsigned long)skb, order);
+}
+
 /*
  *	Free an skbuff by memory without cleaning the state.
  */
@@ -324,17 +453,21 @@ void kfree_skbmem(struct sk_buff *skb)
 {
 	struct sk_buff *other;
 	atomic_t *fclone_ref;
+	void (*free_skb)(struct kmem_cache *, void *);
 
 	skb_release_data(skb);
+
+	free_skb = skb->memalloc ? free_skb_pages : kmem_cache_free;
+
 	switch (skb->fclone) {
 	case SKB_FCLONE_UNAVAILABLE:
-		kmem_cache_free(skbuff_head_cache, skb);
+		free_skb(skbuff_head_cache, skb);
 		break;
 
 	case SKB_FCLONE_ORIG:
 		fclone_ref = (atomic_t *) (skb + 2);
 		if (atomic_dec_and_test(fclone_ref))
-			kmem_cache_free(skbuff_fclone_cache, skb);
+			free_skb(skbuff_fclone_cache, skb);
 		break;
 
 	case SKB_FCLONE_CLONE:
@@ -347,7 +480,7 @@ void kfree_skbmem(struct sk_buff *skb)
 		skb->fclone = SKB_FCLONE_UNAVAILABLE;
 
 		if (atomic_dec_and_test(fclone_ref))
-			kmem_cache_free(skbuff_fclone_cache, other);
+			free_skb(skbuff_fclone_cache, other);
 		break;
 	};
 }
@@ -434,6 +567,12 @@ struct sk_buff *skb_clone(struct sk_buff
 		n->fclone = SKB_FCLONE_CLONE;
 		atomic_inc(fclone_ref);

net/core/skbuff.c struct sk_buff *skb_clone(struct sk_buff · line 578

Quoted 10 lines
net/core/skbuff.c +6 -0
 	} else {
+		/*
+		 * should we special-case skb->memalloc cloning?
+		 * for now fudge it by forcing fast-clone alloc.
+		 */
+		BUG_ON(skb->memalloc);
+
 		n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
 		if (!n)
 			return NULL;

Ugh... cloning is a one of the shoulders of giant where Linux network stack is staying...

net/core/skbuff.c int pskb_expand_head(struct sk_buff *skb · line 832

Quoted 9 lines
net/core/skbuff.c +2 -0
@@ -686,6 +825,8 @@ int pskb_expand_head(struct sk_buff *skb
 	if (skb_shared(skb))
 		BUG();
 
+	BUG_ON(skb->memalloc);
+
 	size = SKB_DATA_ALIGN(size);
 
 	data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);

And that is a bug.
That operation can happen even with usual receiving processing.

Quoted 111 lines
net/core/skbuff.c +74 -0
+/**
+ *	dev_adjust_memalloc - adjust the global memalloc reserve for this device
+ *	@dev: device that has memalloc demands
+ *	@nr_socks: number of new %SOCK_MEMALLOC sockets
+ *
+ *	This function adjusts the memalloc reserve based on device
+ *	demand. For each %SOCK_MEMALLOC socket this device will reserve
+ *	2 * %MAX_PHYS_SEGMENTS pages for outbound traffic (assumption:
+ *	each %SOCK_MEMALLOC socket will have a %request_queue associated)
+ *	and @dev->rx_reserve mtu pages.
+ */
+int dev_adjust_memalloc(struct net_device *dev, int nr_socks)
+{
+	unsigned long flags;
+	unsigned long reserve;
+	int err;
+
+	spin_lock_irqsave(&dev->memalloc_lock, flags);
+
+	dev->memalloc_socks += nr_socks;
+	BUG_ON(dev->memalloc_socks < 0);
+
+	reserve = dev->memalloc_socks *
+		(2 * MAX_PHYS_SEGMENTS +		 /* outbound */
+		 dev->rx_reserve * mtu_pages(dev->mtu)); /* inbound */
+
+	err = adjust_memalloc_reserve(reserve - dev->memalloc_reserve);
+	if (err) {
+		printk(KERN_WARNING
+			"%s: Unable to change RX reserve to: %lu, error: %d\n",
+			dev->name, reserve, err);
+		goto unlock;
+	}
+	dev->memalloc_reserve = reserve;
+
+unlock:
+	spin_unlock_irqrestore(&dev->memalloc_lock, flags);
+	return err;
+}
+EXPORT_SYMBOL_GPL(dev_adjust_memalloc);
 
 /*
  *	Device change register/unregister. These are not inline or static
@@ -2464,6 +2527,9 @@ int dev_set_mtu(struct net_device *dev, 
 		err = dev->change_mtu(dev, new_mtu);
 	else
 		dev->mtu = new_mtu;
+
+	dev_adjust_memalloc(dev, 0);
+
 	if (!err && dev->flags & IFF_UP)
 		raw_notifier_call_chain(&netdev_chain,
 				NETDEV_CHANGEMTU, dev);
@@ -2900,6 +2966,7 @@ int register_netdevice(struct net_device
 #ifdef CONFIG_NET_CLS_ACT
 	spin_lock_init(&dev->ingress_lock);
 #endif
+	spin_lock_init(&dev->memalloc_lock);
 
 	ret = alloc_divert_blk(dev);
 	if (ret)
@@ -3106,6 +3173,28 @@ static void netdev_wait_allrefs(struct n
 	}
 }
 
+/* netdev_wait_memalloc - wait for all outstanding memalloc skbs
+ *
+ * This is called when unregistering network devices.
+ *
+ * Better make sure the skb -> dev mapping is correct, if we leak
+ * some this will stall forever.
+ */
+static void netdev_wait_memalloc(struct net_device *dev)
+{
+	unsigned long warning_time = jiffies;
+	while (atomic_read(&dev->rx_reserve_used) != 0) {
+		msleep(250);
+		if (time_after(jiffies, warning_time + 10 * HZ)) {
+			printk(KERN_EMERG "netdev_wait_memalloc: "
+			       "waiting for %s to become free. SKB "
+			       "count = %d\n",
+			       dev->name, atomic_read(&dev->rx_reserve_used));
+			warning_time = jiffies;
+		}
+	}
+}
+
 /* The sequence is:
  *
  *	rtnl_lock();
@@ -3165,6 +3254,14 @@ void netdev_run_todo(void)
 
 		netdev_wait_allrefs(dev);
 
+		netdev_wait_memalloc(dev);
+
+		/* Get rid of any SOCK_MEMALLOC reserves. */
+		if (dev->memalloc_reserve) {
+			BUG_ON(!dev->memalloc_socks);
+			dev_adjust_memalloc(dev, -dev->memalloc_socks);
+		}
+
 		/* paranoia */
 		BUG_ON(atomic_read(&dev->refcnt));
 		BUG_TRAP(!dev->ip_ptr);

Index: linux-2.6/net/ipv4/af_inet.c
===================================================================

net/ipv4/af_inet.c +0 -0
@@ -132,6 +132,14 @@ void inet_sock_destruct(struct sock *sk)
 {

net/ipv4/af_inet.c void inet_sock_destruct(struct sock *sk) · line 142

Quoted 10 lines
net/ipv4/af_inet.c +8 -0
 	struct inet_sock *inet = inet_sk(sk);
 
+	if (sk_is_memalloc(sk)) {
+		struct net_device *dev = ip_dev_find(inet->rcv_saddr);
+		if (dev) {
+			dev_adjust_memalloc(dev, -1);
+			dev_put(dev);
+		}
+	}
+

This looks very strange - you decrement reference counter both in socket destruction code and in netdevice destruction code.

Signature · 7 lines
	Evgeniy Polyakov

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#2 lore raw
Quoted 39 lines · Evgeniy Polyakov
Quoted 1 line · Peter Zijlstra

Hi,

Hello, Peter.

Quoted 2 lines

So I try again, please tell me if I'm still on crack and should go detox. However if you do so, I kindly request some words on the how and why of it.

I think you should talk with doctor in that case, but not with kernel hackers :)

I have some comments about implementation, not overall design, since we have slightly diametral points of view there.

....

Quoted 23 lines
net/core/skbuff.c +3 -0
@@ -43,6 +43,7 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
+#include <linux/pagemap.h>
 #include <linux/interrupt.h>
 #include <linux/in.h>
 #include <linux/inet.h>
@@ -125,6 +126,8 @@ EXPORT_SYMBOL(skb_truesize_bug);
  *
  */
 
+#define ceiling_log2(x)	fls((x) - 1)
+
 /**
  *	__alloc_skb	-	allocate a network buffer
  *	@size: size to allocate
@@ -147,6 +150,59 @@ struct sk_buff *__alloc_skb(unsigned int
 	struct sk_buff *skb;
 	u8 *data;
 
Quoted 1 line

net/core/skbuff.c struct sk_buff *__alloc_skb(unsigned int · line 153

Quoted 1 line
net/core/skbuff.c +1 -0
+	size = SKB_DATA_ALIGN(size);

I moved it here.

Quoted 5 lines

net/core/skbuff.c struct sk_buff *__alloc_skb(unsigned int · line 180

Quoted 5 lines
net/core/skbuff.c +5 -0
+
+	if (gfp_mask & __GFP_MEMALLOC) {
+		/*
+		 * Fallback allocation for memalloc reserves.
+

* This allocator is build on alloc_pages() so that freed
* skbuffs return to the memalloc reserve imediately. SLAB
* memory might not ever be returned.

This was missing,...

Quoted 38 lines
Quoted 38 lines
net/core/skbuff.c +38 -0
+		 * the page is populated like so:
+		 *
+		 *   struct sk_buff
+		 *   [ struct sk_buff ]
+		 *   [ atomic_t ]
+		 *   unsigned int
+		 *   struct skb_shared_info
+		 *   char []
+		 *
+		 * We have to do higher order allocations for icky jumbo
+		 * frame drivers :-(. They really should be migrated to
+		 * scather/gather DMA and use skb fragments.
+		 */
+		unsigned int data_offset =
+			sizeof(struct sk_buff) + sizeof(unsigned int);
+		unsigned long length = size + data_offset +
+			sizeof(struct skb_shared_info);
+		unsigned int pages;
+		unsigned int order;
+		struct page *page;
+		void *kaddr;
+
+		/*
+		 * Force fclone alloc in order to fudge a lacking in skb_clone().
+		 */
+		fclone = 1;
+		if (fclone) {
+			data_offset += sizeof(struct sk_buff) + sizeof(atomic_t);
+			length += sizeof(struct sk_buff) + sizeof(atomic_t);
+		}
+		pages = (length + PAGE_SIZE - 1) >> PAGE_SHIFT;
+		order = ceiling_log2(pages);
+		skb = NULL;
+		if (!(page = alloc_pages(gfp_mask & ~__GFP_HIGHMEM, order)))
+			goto out;
+
+		kaddr = pfn_to_kaddr(page_to_pfn(page));
+		skb = (struct sk_buff *)kaddr;
Quoted 8 lines

net/core/skbuff.c struct sk_buff *__alloc_skb(unsigned int · line 202

Quoted 5 lines
net/core/skbuff.c +5 -0
+
+		*((unsigned int *)(kaddr + data_offset -
+					sizeof(unsigned int))) = order;
+		data = (u8 *)(kaddr + data_offset);
+

Tricky, but since you are using own allocator here, you could change it to be not so aggressive - i.e. do not round size to number of pages.

I'm not sure I follow you, I'm explicitly using alloc_pages()/free_page(), if
I were to go smart here, I'd loose the whole reason for doing so.

Quoted 6 lines
Quoted 6 lines
net/core/skbuff.c +3 -0
+		goto allocated;
+	}
+
 	cache = fclone ? skbuff_fclone_cache : skbuff_head_cache;
 
Quoted 8 lines

net/core/skbuff.c struct sk_buff *__alloc_skb(unsigned int · line 158

Quoted 6 lines
net/core/skbuff.c +0 -1
 	/* Get the HEAD */
@@ -155,12 +211,13 @@ struct sk_buff *__alloc_skb(unsigned int
 		goto out;
 
 	/* Get the DATA. Size must match skb_add_mtu(). */
-	size = SKB_DATA_ALIGN(size);

Bad sign.

See above.

Quoted 24 lines
Quoted 24 lines
net/core/skbuff.c +20 -0
 	data = ____kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
 	if (!data)
 		goto nodata;
 
+struct sk_buff *__netdev_alloc_skb(struct net_device *dev,
+		unsigned length, gfp_t gfp_mask)
+{
+	struct sk_buff *skb;
+
+	WARN_ON(gfp_mask & (__GFP_NOMEMALLOC | __GFP_MEMALLOC));
+	gfp_mask &= ~(__GFP_NOMEMALLOC | __GFP_MEMALLOC);
+
+	skb = ___netdev_alloc_skb(dev, length, gfp_mask | __GFP_NOMEMALLOC);
+	if (skb)
+		goto done;
+
+	if (atomic_read(&dev->rx_reserve_used) >=
+			dev->rx_reserve * dev->memalloc_socks)
+		goto out;
+
+	/*
+	 * pre-inc guards against a race with netdev_wait_memalloc()
+	 */
+	atomic_inc(&dev->rx_reserve_used);
Quoted 8 lines

net/core/skbuff.c struct sk_buff *__netdev_alloc_skb(struc · line 372

Quoted 5 lines
net/core/skbuff.c +5 -0
+	skb = ___netdev_alloc_skb(dev, length, gfp_mask | __GFP_MEMALLOC);
+	if (unlikely(!skb)) {
+		atomic_dec(&dev->rx_reserve_used);
+		goto out;
+	}

Since you have added atomic operation in that path, you can use device's reference counter instead and do not care that it can dissapear.

Is that the sole reason taking a reference on the device is bad?

Quoted 8 lines
Quoted 8 lines
net/core/skbuff.c +4 -0
@@ -434,6 +567,12 @@ struct sk_buff *skb_clone(struct sk_buff
 		n->fclone = SKB_FCLONE_CLONE;
 		atomic_inc(fclone_ref);
 	} else {
+		/*
+		 * should we special-case skb->memalloc cloning?
+		 * for now fudge it by forcing fast-clone alloc.
+		 */
Quoted 8 lines

net/core/skbuff.c struct sk_buff *skb_clone(struct sk_buff · line 578

Quoted 5 lines
net/core/skbuff.c +2 -0
+		BUG_ON(skb->memalloc);
+
 		n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
 		if (!n)
 			return NULL;

Ugh... cloning is a one of the shoulders of giant where Linux network stack is staying...

Yes, I'm aware of that, I have a plan to fix this, however I haven't had time
to implement it. My immediate concern is the point wrt. the net_device mapping.

My idea was: instead of the order, store the size, and allocate clone skbuffs in the available room at the end of the page(s), allocating extra pages
if needed.

Quoted 4 lines
Quoted 4 lines
net/core/skbuff.c +0 -0
@@ -686,6 +825,8 @@ int pskb_expand_head(struct sk_buff *skb
 	if (skb_shared(skb))
 		BUG();
 
Quoted 8 lines

net/core/skbuff.c int pskb_expand_head(struct sk_buff *skb · line 832

Quoted 5 lines
net/core/skbuff.c +2 -0
+	BUG_ON(skb->memalloc);
+
 	size = SKB_DATA_ALIGN(size);
 
 	data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);

And that is a bug.
That operation can happen even with usual receiving processing.

Yes, more allocator work..

Quoted 8 lines
Quoted 8 lines

Index: linux-2.6/net/ipv4/af_inet.c
===================================================================

net/ipv4/af_inet.c +0 -0
@@ -132,6 +132,14 @@ void inet_sock_destruct(struct sock *sk)
 {
 	struct inet_sock *inet = inet_sk(sk);
 
Quoted 11 lines

net/ipv4/af_inet.c void inet_sock_destruct(struct sock *sk) · line 142

Quoted 8 lines
net/ipv4/af_inet.c +8 -0
+	if (sk_is_memalloc(sk)) {
+		struct net_device *dev = ip_dev_find(inet->rcv_saddr);
+		if (dev) {
+			dev_adjust_memalloc(dev, -1);
+			dev_put(dev);
+		}
+	}
+

This looks very strange - you decrement reference counter both in socket destruction code and in netdevice destruction code.

This is the right place; however I was not sure net_device destruction implied
destruction of all related sockets; in case that is not so, you will have to
clean up there, because this destructor will not find the device any longer.

Thanks for the feedback.

#3 lore raw
Quoted 40 lines · Peter Zijlstra
Quoted 39 lines · Evgeniy Polyakov
Quoted 1 line · Peter Zijlstra

Hi,

Hello, Peter.

Quoted 2 lines

So I try again, please tell me if I'm still on crack and should go detox. However if you do so, I kindly request some words on the how and why of it.

I think you should talk with doctor in that case, but not with kernel hackers :)

I have some comments about implementation, not overall design, since we have slightly diametral points of view there.

....

Quoted 23 lines
net/core/skbuff.c +3 -0
@@ -43,6 +43,7 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
+#include <linux/pagemap.h>
 #include <linux/interrupt.h>
 #include <linux/in.h>
 #include <linux/inet.h>
@@ -125,6 +126,8 @@ EXPORT_SYMBOL(skb_truesize_bug);
  *
  */
 
+#define ceiling_log2(x)	fls((x) - 1)
+
 /**
  *	__alloc_skb	-	allocate a network buffer
  *	@size: size to allocate
@@ -147,6 +150,59 @@ struct sk_buff *__alloc_skb(unsigned int
 	struct sk_buff *skb;
 	u8 *data;
 
Quoted 3 lines
Quoted 1 line

net/core/skbuff.c struct sk_buff *__alloc_skb(unsigned int · line 153

Quoted 1 line
net/core/skbuff.c +1 -0
+	size = SKB_DATA_ALIGN(size);

I moved it here.

Yep.

Quoted 59 lines
Quoted 5 lines

net/core/skbuff.c struct sk_buff *__alloc_skb(unsigned int · line 180

Quoted 5 lines
net/core/skbuff.c +5 -0
+
+	if (gfp_mask & __GFP_MEMALLOC) {
+		/*
+		 * Fallback allocation for memalloc reserves.
+

* This allocator is build on alloc_pages() so that freed
* skbuffs return to the memalloc reserve imediately. SLAB
* memory might not ever be returned.

This was missing,...

Quoted 38 lines
Quoted 38 lines
net/core/skbuff.c +38 -0
+		 * the page is populated like so:
+		 *
+		 *   struct sk_buff
+		 *   [ struct sk_buff ]
+		 *   [ atomic_t ]
+		 *   unsigned int
+		 *   struct skb_shared_info
+		 *   char []
+		 *
+		 * We have to do higher order allocations for icky jumbo
+		 * frame drivers :-(. They really should be migrated to
+		 * scather/gather DMA and use skb fragments.
+		 */
+		unsigned int data_offset =
+			sizeof(struct sk_buff) + sizeof(unsigned int);
+		unsigned long length = size + data_offset +
+			sizeof(struct skb_shared_info);
+		unsigned int pages;
+		unsigned int order;
+		struct page *page;
+		void *kaddr;
+
+		/*
+		 * Force fclone alloc in order to fudge a lacking in skb_clone().
+		 */
+		fclone = 1;
+		if (fclone) {
+			data_offset += sizeof(struct sk_buff) + sizeof(atomic_t);
+			length += sizeof(struct sk_buff) + sizeof(atomic_t);
+		}
+		pages = (length + PAGE_SIZE - 1) >> PAGE_SHIFT;
+		order = ceiling_log2(pages);
+		skb = NULL;
+		if (!(page = alloc_pages(gfp_mask & ~__GFP_HIGHMEM, order)))
+			goto out;
+
+		kaddr = pfn_to_kaddr(page_to_pfn(page));
+		skb = (struct sk_buff *)kaddr;
Quoted 8 lines

net/core/skbuff.c struct sk_buff *__alloc_skb(unsigned int · line 202

Quoted 5 lines
net/core/skbuff.c +5 -0
+
+		*((unsigned int *)(kaddr + data_offset -
+					sizeof(unsigned int))) = order;
+		data = (u8 *)(kaddr + data_offset);
+

Tricky, but since you are using own allocator here, you could change it to be not so aggressive - i.e. do not round size to number of pages.

Quoted 3 lines

I'm not sure I follow you, I'm explicitly using alloc_pages()/free_page(), if
I were to go smart here, I'd loose the whole reason for doing so.

You can use page to put there several skbs for example or at least add there a fclone (fast clone).

Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
net/core/skbuff.c +3 -0
+		goto allocated;
+	}
+
 	cache = fclone ? skbuff_fclone_cache : skbuff_head_cache;
 
 	/* Get the HEAD */
@@ -155,12 +211,13 @@ struct sk_buff *__alloc_skb(unsigned int
 		goto out;
 
Quoted 6 lines
Quoted 4 lines

net/core/skbuff.c struct sk_buff *__alloc_skb(unsigned int · line 158

Quoted 2 lines
net/core/skbuff.c +0 -1
 	/* Get the DATA. Size must match skb_add_mtu(). */
-	size = SKB_DATA_ALIGN(size);

Bad sign.

See above.

Yep, I've found.

Quoted 33 lines
Quoted 24 lines
Quoted 24 lines
net/core/skbuff.c +20 -0
 	data = ____kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
 	if (!data)
 		goto nodata;
 
+struct sk_buff *__netdev_alloc_skb(struct net_device *dev,
+		unsigned length, gfp_t gfp_mask)
+{
+	struct sk_buff *skb;
+
+	WARN_ON(gfp_mask & (__GFP_NOMEMALLOC | __GFP_MEMALLOC));
+	gfp_mask &= ~(__GFP_NOMEMALLOC | __GFP_MEMALLOC);
+
+	skb = ___netdev_alloc_skb(dev, length, gfp_mask | __GFP_NOMEMALLOC);
+	if (skb)
+		goto done;
+
+	if (atomic_read(&dev->rx_reserve_used) >=
+			dev->rx_reserve * dev->memalloc_socks)
+		goto out;
+
+	/*
+	 * pre-inc guards against a race with netdev_wait_memalloc()
+	 */
+	atomic_inc(&dev->rx_reserve_used);
Quoted 8 lines

net/core/skbuff.c struct sk_buff *__netdev_alloc_skb(struc · line 372

Quoted 5 lines
net/core/skbuff.c +5 -0
+	skb = ___netdev_alloc_skb(dev, length, gfp_mask | __GFP_MEMALLOC);
+	if (unlikely(!skb)) {
+		atomic_dec(&dev->rx_reserve_used);
+		goto out;
+	}

Since you have added atomic operation in that path, you can use device's reference counter instead and do not care that it can dissapear.

Quoted 1 line

Is that the sole reason taking a reference on the device is bad?

Taking a reference is bad due to performance reasons, since atomic increment is not that cheap. If you do it for one variable for the purpose of reference counting you can use device's refcnt istead, which will solve some races.

Quoted 22 lines
Quoted 8 lines
Quoted 8 lines
net/core/skbuff.c +4 -0
@@ -434,6 +567,12 @@ struct sk_buff *skb_clone(struct sk_buff
 		n->fclone = SKB_FCLONE_CLONE;
 		atomic_inc(fclone_ref);
 	} else {
+		/*
+		 * should we special-case skb->memalloc cloning?
+		 * for now fudge it by forcing fast-clone alloc.
+		 */
Quoted 8 lines

net/core/skbuff.c struct sk_buff *skb_clone(struct sk_buff · line 578

Quoted 5 lines
net/core/skbuff.c +2 -0
+		BUG_ON(skb->memalloc);
+
 		n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
 		if (!n)
 			return NULL;

Ugh... cloning is a one of the shoulders of giant where Linux network stack is staying...

Yes, I'm aware of that, I have a plan to fix this, however I haven't had time
to implement it. My immediate concern is the point wrt. the net_device mapping.

Quoted 4 lines

My idea was: instead of the order, store the size, and allocate clone skbuffs in the available room at the end of the page(s), allocating extra pages
if needed.

You can check if requested skb with fclone fits allocated pages, and if so use fclone magic, otherwise postpone clone allocation until it is required.

Quoted 35 lines
Quoted 4 lines
Quoted 4 lines
net/core/skbuff.c +0 -0
@@ -686,6 +825,8 @@ int pskb_expand_head(struct sk_buff *skb
 	if (skb_shared(skb))
 		BUG();
 
Quoted 8 lines

net/core/skbuff.c int pskb_expand_head(struct sk_buff *skb · line 832

Quoted 5 lines
net/core/skbuff.c +2 -0
+	BUG_ON(skb->memalloc);
+
 	size = SKB_DATA_ALIGN(size);
 
 	data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);

And that is a bug.
That operation can happen even with usual receiving processing.

Yes, more allocator work..

Quoted 8 lines
Quoted 8 lines

Index: linux-2.6/net/ipv4/af_inet.c
===================================================================

net/ipv4/af_inet.c +0 -0
@@ -132,6 +132,14 @@ void inet_sock_destruct(struct sock *sk)
 {
 	struct inet_sock *inet = inet_sk(sk);
 
Quoted 11 lines

net/ipv4/af_inet.c void inet_sock_destruct(struct sock *sk) · line 142

Quoted 8 lines
net/ipv4/af_inet.c +8 -0
+	if (sk_is_memalloc(sk)) {
+		struct net_device *dev = ip_dev_find(inet->rcv_saddr);
+		if (dev) {
+			dev_adjust_memalloc(dev, -1);
+			dev_put(dev);
+		}
+	}
+

This looks very strange - you decrement reference counter both in socket destruction code and in netdevice destruction code.

Quoted 6 lines

This is the right place; however I was not sure net_device destruction implied
destruction of all related sockets; in case that is not so, you will have to
clean up there, because this destructor will not find the device any longer.

Sockets can live without network devices at all, I expect it is enough to clean up in socket destructor, since packets can come from different devices into the same socket.

Signature · 7 lines
	Evgeniy Polyakov

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#4 lore raw
Quoted 9 lines
Quoted 6 lines
Quoted 2 lines

Tricky, but since you are using own allocator here, you could change it to be not so aggressive - i.e. do not round size to number of pages.

I'm not sure I follow you, I'm explicitly using alloc_pages()/free_page(), if
I were to go smart here, I'd loose the whole reason for doing so.

You can use page to put there several skbs for example or at least add there a fclone (fast clone).

fclone support is there.

Quoted 29 lines
Quoted 29 lines
Quoted 28 lines
Quoted 15 lines
net/core/skbuff.c +15 -0
+struct sk_buff *__netdev_alloc_skb(struct net_device *dev,
+		unsigned length, gfp_t gfp_mask)
+{
+	struct sk_buff *skb;
+
+	WARN_ON(gfp_mask & (__GFP_NOMEMALLOC | __GFP_MEMALLOC));
+	gfp_mask &= ~(__GFP_NOMEMALLOC | __GFP_MEMALLOC);
+
+	skb = ___netdev_alloc_skb(dev, length, gfp_mask | __GFP_NOMEMALLOC);
+	if (skb)
+		goto done;
+
+	if (atomic_read(&dev->rx_reserve_used) >=
+			dev->rx_reserve * dev->memalloc_socks)
+		goto out;

net/core/skbuff.c struct sk_buff *__netdev_alloc_skb(struc · line 372

Quoted 10 lines
net/core/skbuff.c +10 -0
+
+	/*
+	 * pre-inc guards against a race with netdev_wait_memalloc()
+	 */
+	atomic_inc(&dev->rx_reserve_used);
+	skb = ___netdev_alloc_skb(dev, length, gfp_mask | __GFP_MEMALLOC);
+	if (unlikely(!skb)) {
+		atomic_dec(&dev->rx_reserve_used);
+		goto out;
+	}

Since you have added atomic operation in that path, you can use device's reference counter instead and do not care that it can dissapear.

Quoted 6 lines
Quoted 1 line

Is that the sole reason taking a reference on the device is bad?

Taking a reference is bad due to performance reasons, since atomic increment is not that cheap. If you do it for one variable for the purpose of reference counting you can use device's refcnt istead, which will solve some races.

Yes, I understand you. However I'm not sure if performance is the only reason not to take a refcount on the device. Anyway, I think I have just been convinced to abandon the per device thing and go global.

Quoted 22 lines
Quoted 22 lines
Quoted 8 lines
Quoted 8 lines
net/core/skbuff.c +4 -0
@@ -434,6 +567,12 @@ struct sk_buff *skb_clone(struct sk_buff
 		n->fclone = SKB_FCLONE_CLONE;
 		atomic_inc(fclone_ref);
 	} else {
+		/*
+		 * should we special-case skb->memalloc cloning?
+		 * for now fudge it by forcing fast-clone alloc.
+		 */
Quoted 8 lines

net/core/skbuff.c struct sk_buff *skb_clone(struct sk_buff · line 578

Quoted 5 lines
net/core/skbuff.c +2 -0
+		BUG_ON(skb->memalloc);
+
 		n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
 		if (!n)
 			return NULL;

Ugh... cloning is a one of the shoulders of giant where Linux network stack is staying...

Yes, I'm aware of that, I have a plan to fix this, however I haven't had time
to implement it. My immediate concern is the point wrt. the net_device mapping.

Quoted 8 lines
Quoted 4 lines

My idea was: instead of the order, store the size, and allocate clone skbuffs in the available room at the end of the page(s), allocating extra pages
if needed.

You can check if requested skb with fclone fits allocated pages, and if so use fclone magic, otherwise postpone clone allocation until it is required.

Yes the fclone magic works, however that will only let you have one clone.
I'm just not confident no receive path will ever exceed that.

Quoted 3 lines

Sockets can live without network devices at all, I expect it is enough to clean up in socket destructor, since packets can come from different devices into the same socket.

You are right if the reserve wasn't device bound - which I will abandon because you are right that with multi-path routing, bridge device and other advanced goodies this scheme is broken in that there is no unambiguous
mapping from sockets to devices.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#5 lore raw
Quoted 4 lines · Peter Zijlstra

You are right if the reserve wasn't device bound - which I will abandon because you are right that with multi-path routing, bridge device and other advanced goodies this scheme is broken in that there is no unambiguous mapping from sockets to devices.

The natural thing seems to make reserves socket bound, but that has overhead too and the simplicity of a global reserve is very tempting.

What about adding a flag to sk_set_memalloc() which says if memalloc is on or off on the socket? (Or add sk_unset_memalloc). That way it's possible to switch it off again, which doesn't seem like that a bad idea, because then it can be turned on only when the socket can be used to reduce total memory usage. Also if it is turned off again when no more memory can be freed by using this socket, it will solve the starvation problem as a starved socket now has a new chance to do its thing.

Greetings,

Indan

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>