diff options
-rw-r--r-- | net/ipv4/ip_output.c | 15 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 19 |
2 files changed, 25 insertions, 9 deletions
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index da4abbee10f7..f2338e40c37d 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c | |||
@@ -878,11 +878,13 @@ static int __ip_append_data(struct sock *sk, | |||
878 | struct rtable *rt = (struct rtable *)cork->dst; | 878 | struct rtable *rt = (struct rtable *)cork->dst; |
879 | unsigned int wmem_alloc_delta = 0; | 879 | unsigned int wmem_alloc_delta = 0; |
880 | u32 tskey = 0; | 880 | u32 tskey = 0; |
881 | bool paged; | ||
881 | 882 | ||
882 | skb = skb_peek_tail(queue); | 883 | skb = skb_peek_tail(queue); |
883 | 884 | ||
884 | exthdrlen = !skb ? rt->dst.header_len : 0; | 885 | exthdrlen = !skb ? rt->dst.header_len : 0; |
885 | mtu = cork->gso_size ? IP_MAX_MTU : cork->fragsize; | 886 | mtu = cork->gso_size ? IP_MAX_MTU : cork->fragsize; |
887 | paged = !!cork->gso_size; | ||
886 | 888 | ||
887 | if (cork->tx_flags & SKBTX_ANY_SW_TSTAMP && | 889 | if (cork->tx_flags & SKBTX_ANY_SW_TSTAMP && |
888 | sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID) | 890 | sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID) |
@@ -934,6 +936,7 @@ static int __ip_append_data(struct sock *sk, | |||
934 | unsigned int fraglen; | 936 | unsigned int fraglen; |
935 | unsigned int fraggap; | 937 | unsigned int fraggap; |
936 | unsigned int alloclen; | 938 | unsigned int alloclen; |
939 | unsigned int pagedlen = 0; | ||
937 | struct sk_buff *skb_prev; | 940 | struct sk_buff *skb_prev; |
938 | alloc_new_skb: | 941 | alloc_new_skb: |
939 | skb_prev = skb; | 942 | skb_prev = skb; |
@@ -954,8 +957,12 @@ alloc_new_skb: | |||
954 | if ((flags & MSG_MORE) && | 957 | if ((flags & MSG_MORE) && |
955 | !(rt->dst.dev->features&NETIF_F_SG)) | 958 | !(rt->dst.dev->features&NETIF_F_SG)) |
956 | alloclen = mtu; | 959 | alloclen = mtu; |
957 | else | 960 | else if (!paged) |
958 | alloclen = fraglen; | 961 | alloclen = fraglen; |
962 | else { | ||
963 | alloclen = min_t(int, fraglen, MAX_HEADER); | ||
964 | pagedlen = fraglen - alloclen; | ||
965 | } | ||
959 | 966 | ||
960 | alloclen += exthdrlen; | 967 | alloclen += exthdrlen; |
961 | 968 | ||
@@ -999,7 +1006,7 @@ alloc_new_skb: | |||
999 | /* | 1006 | /* |
1000 | * Find where to start putting bytes. | 1007 | * Find where to start putting bytes. |
1001 | */ | 1008 | */ |
1002 | data = skb_put(skb, fraglen + exthdrlen); | 1009 | data = skb_put(skb, fraglen + exthdrlen - pagedlen); |
1003 | skb_set_network_header(skb, exthdrlen); | 1010 | skb_set_network_header(skb, exthdrlen); |
1004 | skb->transport_header = (skb->network_header + | 1011 | skb->transport_header = (skb->network_header + |
1005 | fragheaderlen); | 1012 | fragheaderlen); |
@@ -1015,7 +1022,7 @@ alloc_new_skb: | |||
1015 | pskb_trim_unique(skb_prev, maxfraglen); | 1022 | pskb_trim_unique(skb_prev, maxfraglen); |
1016 | } | 1023 | } |
1017 | 1024 | ||
1018 | copy = datalen - transhdrlen - fraggap; | 1025 | copy = datalen - transhdrlen - fraggap - pagedlen; |
1019 | if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) { | 1026 | if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) { |
1020 | err = -EFAULT; | 1027 | err = -EFAULT; |
1021 | kfree_skb(skb); | 1028 | kfree_skb(skb); |
@@ -1023,7 +1030,7 @@ alloc_new_skb: | |||
1023 | } | 1030 | } |
1024 | 1031 | ||
1025 | offset += copy; | 1032 | offset += copy; |
1026 | length -= datalen - fraggap; | 1033 | length -= copy + transhdrlen; |
1027 | transhdrlen = 0; | 1034 | transhdrlen = 0; |
1028 | exthdrlen = 0; | 1035 | exthdrlen = 0; |
1029 | csummode = CHECKSUM_NONE; | 1036 | csummode = CHECKSUM_NONE; |
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index a1c4a78132d2..dfd8af41824e 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
@@ -1276,6 +1276,7 @@ static int __ip6_append_data(struct sock *sk, | |||
1276 | int csummode = CHECKSUM_NONE; | 1276 | int csummode = CHECKSUM_NONE; |
1277 | unsigned int maxnonfragsize, headersize; | 1277 | unsigned int maxnonfragsize, headersize; |
1278 | unsigned int wmem_alloc_delta = 0; | 1278 | unsigned int wmem_alloc_delta = 0; |
1279 | bool paged; | ||
1279 | 1280 | ||
1280 | skb = skb_peek_tail(queue); | 1281 | skb = skb_peek_tail(queue); |
1281 | if (!skb) { | 1282 | if (!skb) { |
@@ -1283,6 +1284,7 @@ static int __ip6_append_data(struct sock *sk, | |||
1283 | dst_exthdrlen = rt->dst.header_len - rt->rt6i_nfheader_len; | 1284 | dst_exthdrlen = rt->dst.header_len - rt->rt6i_nfheader_len; |
1284 | } | 1285 | } |
1285 | 1286 | ||
1287 | paged = !!cork->gso_size; | ||
1286 | mtu = cork->gso_size ? IP6_MAX_MTU : cork->fragsize; | 1288 | mtu = cork->gso_size ? IP6_MAX_MTU : cork->fragsize; |
1287 | orig_mtu = mtu; | 1289 | orig_mtu = mtu; |
1288 | 1290 | ||
@@ -1374,6 +1376,7 @@ emsgsize: | |||
1374 | unsigned int fraglen; | 1376 | unsigned int fraglen; |
1375 | unsigned int fraggap; | 1377 | unsigned int fraggap; |
1376 | unsigned int alloclen; | 1378 | unsigned int alloclen; |
1379 | unsigned int pagedlen = 0; | ||
1377 | alloc_new_skb: | 1380 | alloc_new_skb: |
1378 | /* There's no room in the current skb */ | 1381 | /* There's no room in the current skb */ |
1379 | if (skb) | 1382 | if (skb) |
@@ -1396,11 +1399,17 @@ alloc_new_skb: | |||
1396 | 1399 | ||
1397 | if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen) | 1400 | if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen) |
1398 | datalen = maxfraglen - fragheaderlen - rt->dst.trailer_len; | 1401 | datalen = maxfraglen - fragheaderlen - rt->dst.trailer_len; |
1402 | fraglen = datalen + fragheaderlen; | ||
1403 | |||
1399 | if ((flags & MSG_MORE) && | 1404 | if ((flags & MSG_MORE) && |
1400 | !(rt->dst.dev->features&NETIF_F_SG)) | 1405 | !(rt->dst.dev->features&NETIF_F_SG)) |
1401 | alloclen = mtu; | 1406 | alloclen = mtu; |
1402 | else | 1407 | else if (!paged) |
1403 | alloclen = datalen + fragheaderlen; | 1408 | alloclen = fraglen; |
1409 | else { | ||
1410 | alloclen = min_t(int, fraglen, MAX_HEADER); | ||
1411 | pagedlen = fraglen - alloclen; | ||
1412 | } | ||
1404 | 1413 | ||
1405 | alloclen += dst_exthdrlen; | 1414 | alloclen += dst_exthdrlen; |
1406 | 1415 | ||
@@ -1422,7 +1431,7 @@ alloc_new_skb: | |||
1422 | */ | 1431 | */ |
1423 | alloclen += sizeof(struct frag_hdr); | 1432 | alloclen += sizeof(struct frag_hdr); |
1424 | 1433 | ||
1425 | copy = datalen - transhdrlen - fraggap; | 1434 | copy = datalen - transhdrlen - fraggap - pagedlen; |
1426 | if (copy < 0) { | 1435 | if (copy < 0) { |
1427 | err = -EINVAL; | 1436 | err = -EINVAL; |
1428 | goto error; | 1437 | goto error; |
@@ -1461,7 +1470,7 @@ alloc_new_skb: | |||
1461 | /* | 1470 | /* |
1462 | * Find where to start putting bytes | 1471 | * Find where to start putting bytes |
1463 | */ | 1472 | */ |
1464 | data = skb_put(skb, fraglen); | 1473 | data = skb_put(skb, fraglen - pagedlen); |
1465 | skb_set_network_header(skb, exthdrlen); | 1474 | skb_set_network_header(skb, exthdrlen); |
1466 | data += fragheaderlen; | 1475 | data += fragheaderlen; |
1467 | skb->transport_header = (skb->network_header + | 1476 | skb->transport_header = (skb->network_header + |
@@ -1484,7 +1493,7 @@ alloc_new_skb: | |||
1484 | } | 1493 | } |
1485 | 1494 | ||
1486 | offset += copy; | 1495 | offset += copy; |
1487 | length -= datalen - fraggap; | 1496 | length -= copy + transhdrlen; |
1488 | transhdrlen = 0; | 1497 | transhdrlen = 0; |
1489 | exthdrlen = 0; | 1498 | exthdrlen = 0; |
1490 | dst_exthdrlen = 0; | 1499 | dst_exthdrlen = 0; |