I do not have a way of reproducing this decent enough to recommend: I'll keep digging.
When usercopy triggers, .coals shows values of 2/3 for 128/192 bytes respectively.
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index a098d95..7cd744c 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -40,6 +40,7 @@
#include <linux/in6.h>
#include <linux/if_packet.h>
#include <net/flow.h>
+#include <linux/slub_def.h>
/* The interface for checksum offload between the stack and networking drivers
* is as follows...
@@ -316,7 +317,8 @@ struct skb_frag_struct {
} page;
#if (BITS_PER_LONG > 32) || (PAGE_SIZE >= 65536)
__u32 page_offset;
- __u32 size;
+ __u16 size;
+ __u16 coals;
#else
__u16 page_offset;
__u16 size;
@@ -1850,9 +1852,11 @@ static inline void __skb_fill_page_desc(struct sk_buff *skb, int i,
*/
frag->page.p = page;
frag->page_offset = off;
+ frag->coals = 0;
skb_frag_size_set(frag, size);
page = compound_head(page);
+ WARN_ON(PageSlab(page) && (page->slab_cache->size < size)); // does NOT trigger
if (page_is_pfmemalloc(page))
skb->pfmemalloc = true;
}
@@ -2849,10 +2853,14 @@ static inline bool skb_can_coalesce(struct sk_buff *skb, int i,
const struct page *page, int off)
{
if (i) {
- const struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i - 1];
+ struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i - 1];
- return page == skb_frag_page(frag) &&
+ bool ret = page == skb_frag_page(frag) &&
off == frag->page_offset + skb_frag_size(frag);
+ if (unlikely(ret))
+ if (PageSlab(compound_head((struct page *)page)))
+ frag->coals++;
+ return ret;
}
return false;
}