Skip to content

Commit 3ece782

Browse files
wdebruijdavem330
authored andcommitted
sock: skb_copy_ubufs support for compound pages
Refine skb_copy_ubufs to support compound pages. With upcoming TCP zerocopy sendmsg, such fragments may appear. The existing code replaces each page one for one. Splitting each compound page into an independent number of regular pages can result in exceeding limit MAX_SKB_FRAGS if data is not exactly page aligned. Instead, fill all destination pages but the last to PAGE_SIZE. Split the existing alloc + copy loop into separate stages: 1. compute bytelength and minimum number of pages to store this. 2. allocate 3. copy, filling each page except the last to PAGE_SIZE bytes 4. update skb frag array Signed-off-by: Willem de Bruijn <willemb@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 98ba0bd commit 3ece782

File tree

2 files changed

+45
-17
lines changed

2 files changed

+45
-17
lines changed

include/linux/skbuff.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,13 +1796,18 @@ static inline unsigned int skb_headlen(const struct sk_buff *skb)
17961796
return skb->len - skb->data_len;
17971797
}
17981798

1799-
static inline unsigned int skb_pagelen(const struct sk_buff *skb)
1799+
static inline unsigned int __skb_pagelen(const struct sk_buff *skb)
18001800
{
18011801
unsigned int i, len = 0;
18021802

18031803
for (i = skb_shinfo(skb)->nr_frags - 1; (int)i >= 0; i--)
18041804
len += skb_frag_size(&skb_shinfo(skb)->frags[i]);
1805-
return len + skb_headlen(skb);
1805+
return len;
1806+
}
1807+
1808+
static inline unsigned int skb_pagelen(const struct sk_buff *skb)
1809+
{
1810+
return skb_headlen(skb) + __skb_pagelen(skb);
18061811
}
18071812

18081813
/**

net/core/skbuff.c

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -932,17 +932,20 @@ EXPORT_SYMBOL_GPL(skb_morph);
932932
*/
933933
int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask)
934934
{
935-
int i;
935+
struct ubuf_info *uarg = skb_shinfo(skb)->destructor_arg;
936936
int num_frags = skb_shinfo(skb)->nr_frags;
937937
struct page *page, *head = NULL;
938-
struct ubuf_info *uarg = skb_shinfo(skb)->destructor_arg;
938+
int i, new_frags;
939+
u32 d_off;
939940

940-
for (i = 0; i < num_frags; i++) {
941-
skb_frag_t *f = &skb_shinfo(skb)->frags[i];
942-
u32 p_off, p_len, copied;
943-
struct page *p;
944-
u8 *vaddr;
941+
if (!num_frags)
942+
return 0;
943+
944+
if (skb_shared(skb) || skb_unclone(skb, gfp_mask))
945+
return -EINVAL;
945946

947+
new_frags = (__skb_pagelen(skb) + PAGE_SIZE - 1) >> PAGE_SHIFT;
948+
for (i = 0; i < new_frags; i++) {
946949
page = alloc_page(gfp_mask);
947950
if (!page) {
948951
while (head) {
@@ -952,17 +955,36 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask)
952955
}
953956
return -ENOMEM;
954957
}
958+
set_page_private(page, (unsigned long)head);
959+
head = page;
960+
}
961+
962+
page = head;
963+
d_off = 0;
964+
for (i = 0; i < num_frags; i++) {
965+
skb_frag_t *f = &skb_shinfo(skb)->frags[i];
966+
u32 p_off, p_len, copied;
967+
struct page *p;
968+
u8 *vaddr;
955969

956970
skb_frag_foreach_page(f, f->page_offset, skb_frag_size(f),
957971
p, p_off, p_len, copied) {
972+
u32 copy, done = 0;
958973
vaddr = kmap_atomic(p);
959-
memcpy(page_address(page) + copied, vaddr + p_off,
960-
p_len);
974+
975+
while (done < p_len) {
976+
if (d_off == PAGE_SIZE) {
977+
d_off = 0;
978+
page = (struct page *)page_private(page);
979+
}
980+
copy = min_t(u32, PAGE_SIZE - d_off, p_len - done);
981+
memcpy(page_address(page) + d_off,
982+
vaddr + p_off + done, copy);
983+
done += copy;
984+
d_off += copy;
985+
}
961986
kunmap_atomic(vaddr);
962987
}
963-
964-
set_page_private(page, (unsigned long)head);
965-
head = page;
966988
}
967989

968990
/* skb frags release userspace buffers */
@@ -972,11 +994,12 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask)
972994
uarg->callback(uarg, false);
973995

974996
/* skb frags point to kernel buffers */
975-
for (i = num_frags - 1; i >= 0; i--) {
976-
__skb_fill_page_desc(skb, i, head, 0,
977-
skb_shinfo(skb)->frags[i].size);
997+
for (i = 0; i < new_frags - 1; i++) {
998+
__skb_fill_page_desc(skb, i, head, 0, PAGE_SIZE);
978999
head = (struct page *)page_private(head);
9791000
}
1001+
__skb_fill_page_desc(skb, new_frags - 1, head, 0, d_off);
1002+
skb_shinfo(skb)->nr_frags = new_frags;
9801003

9811004
skb_shinfo(skb)->tx_flags &= ~SKBTX_DEV_ZEROCOPY;
9821005
return 0;

0 commit comments

Comments
 (0)