VM deadlock prevention core -v3
Subsystems (10)
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_deviceside of things, the extra 'structnet_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
@@ -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
@@ -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
@@ -409,6 +409,12 @@ struct net_devicestruct 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_nextern 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
@@ -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
@@ -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 sockreturn 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
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 intstruct 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
-0data =____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_bif (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 *skbif (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
-0struct 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>
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 intstruct 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
-0data =____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 *skbif (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.
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 intstruct 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
-0data =____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. thenet_devicemapping.
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 *skbif (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_devicedestruction 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>
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. thenet_devicemapping.
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>
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>