diff options
Diffstat (limited to 'net/ipv4/ip_output.c')
| -rw-r--r-- | net/ipv4/ip_output.c | 83 |
1 files changed, 78 insertions, 5 deletions
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 87e350069abb..17758234a3e3 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c | |||
| @@ -275,7 +275,8 @@ int ip_output(struct sk_buff *skb) | |||
| 275 | { | 275 | { |
| 276 | IP_INC_STATS(IPSTATS_MIB_OUTREQUESTS); | 276 | IP_INC_STATS(IPSTATS_MIB_OUTREQUESTS); |
| 277 | 277 | ||
| 278 | if (skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->tso_size) | 278 | if (skb->len > dst_mtu(skb->dst) && |
| 279 | !(skb_shinfo(skb)->ufo_size || skb_shinfo(skb)->tso_size)) | ||
| 279 | return ip_fragment(skb, ip_finish_output); | 280 | return ip_fragment(skb, ip_finish_output); |
| 280 | else | 281 | else |
| 281 | return ip_finish_output(skb); | 282 | return ip_finish_output(skb); |
| @@ -688,6 +689,60 @@ csum_page(struct page *page, int offset, int copy) | |||
| 688 | return csum; | 689 | return csum; |
| 689 | } | 690 | } |
| 690 | 691 | ||
| 692 | inline int ip_ufo_append_data(struct sock *sk, | ||
| 693 | int getfrag(void *from, char *to, int offset, int len, | ||
| 694 | int odd, struct sk_buff *skb), | ||
| 695 | void *from, int length, int hh_len, int fragheaderlen, | ||
| 696 | int transhdrlen, int mtu,unsigned int flags) | ||
| 697 | { | ||
| 698 | struct sk_buff *skb; | ||
| 699 | int err; | ||
| 700 | |||
| 701 | /* There is support for UDP fragmentation offload by network | ||
| 702 | * device, so create one single skb packet containing complete | ||
| 703 | * udp datagram | ||
| 704 | */ | ||
| 705 | if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) { | ||
| 706 | skb = sock_alloc_send_skb(sk, | ||
| 707 | hh_len + fragheaderlen + transhdrlen + 20, | ||
| 708 | (flags & MSG_DONTWAIT), &err); | ||
| 709 | |||
| 710 | if (skb == NULL) | ||
| 711 | return err; | ||
| 712 | |||
| 713 | /* reserve space for Hardware header */ | ||
| 714 | skb_reserve(skb, hh_len); | ||
| 715 | |||
| 716 | /* create space for UDP/IP header */ | ||
| 717 | skb_put(skb,fragheaderlen + transhdrlen); | ||
| 718 | |||
| 719 | /* initialize network header pointer */ | ||
| 720 | skb->nh.raw = skb->data; | ||
| 721 | |||
| 722 | /* initialize protocol header pointer */ | ||
| 723 | skb->h.raw = skb->data + fragheaderlen; | ||
| 724 | |||
| 725 | skb->ip_summed = CHECKSUM_HW; | ||
| 726 | skb->csum = 0; | ||
| 727 | sk->sk_sndmsg_off = 0; | ||
| 728 | } | ||
| 729 | |||
| 730 | err = skb_append_datato_frags(sk,skb, getfrag, from, | ||
| 731 | (length - transhdrlen)); | ||
| 732 | if (!err) { | ||
| 733 | /* specify the length of each IP datagram fragment*/ | ||
| 734 | skb_shinfo(skb)->ufo_size = (mtu - fragheaderlen); | ||
| 735 | __skb_queue_tail(&sk->sk_write_queue, skb); | ||
| 736 | |||
| 737 | return 0; | ||
| 738 | } | ||
| 739 | /* There is not enough support do UFO , | ||
| 740 | * so follow normal path | ||
| 741 | */ | ||
| 742 | kfree_skb(skb); | ||
| 743 | return err; | ||
| 744 | } | ||
| 745 | |||
| 691 | /* | 746 | /* |
| 692 | * ip_append_data() and ip_append_page() can make one large IP datagram | 747 | * ip_append_data() and ip_append_page() can make one large IP datagram |
| 693 | * from many pieces of data. Each pieces will be holded on the socket | 748 | * from many pieces of data. Each pieces will be holded on the socket |
| @@ -777,6 +832,15 @@ int ip_append_data(struct sock *sk, | |||
| 777 | csummode = CHECKSUM_HW; | 832 | csummode = CHECKSUM_HW; |
| 778 | 833 | ||
| 779 | inet->cork.length += length; | 834 | inet->cork.length += length; |
| 835 | if (((length > mtu) && (sk->sk_protocol == IPPROTO_UDP)) && | ||
| 836 | (rt->u.dst.dev->features & NETIF_F_UFO)) { | ||
| 837 | |||
| 838 | if(ip_ufo_append_data(sk, getfrag, from, length, hh_len, | ||
| 839 | fragheaderlen, transhdrlen, mtu, flags)) | ||
| 840 | goto error; | ||
| 841 | |||
| 842 | return 0; | ||
| 843 | } | ||
| 780 | 844 | ||
| 781 | /* So, what's going on in the loop below? | 845 | /* So, what's going on in the loop below? |
| 782 | * | 846 | * |
| @@ -1008,14 +1072,23 @@ ssize_t ip_append_page(struct sock *sk, struct page *page, | |||
| 1008 | return -EINVAL; | 1072 | return -EINVAL; |
| 1009 | 1073 | ||
| 1010 | inet->cork.length += size; | 1074 | inet->cork.length += size; |
| 1075 | if ((sk->sk_protocol == IPPROTO_UDP) && | ||
| 1076 | (rt->u.dst.dev->features & NETIF_F_UFO)) | ||
| 1077 | skb_shinfo(skb)->ufo_size = (mtu - fragheaderlen); | ||
| 1078 | |||
| 1011 | 1079 | ||
| 1012 | while (size > 0) { | 1080 | while (size > 0) { |
| 1013 | int i; | 1081 | int i; |
| 1014 | 1082 | ||
| 1015 | /* Check if the remaining data fits into current packet. */ | 1083 | if (skb_shinfo(skb)->ufo_size) |
| 1016 | len = mtu - skb->len; | 1084 | len = size; |
| 1017 | if (len < size) | 1085 | else { |
| 1018 | len = maxfraglen - skb->len; | 1086 | |
| 1087 | /* Check if the remaining data fits into current packet. */ | ||
| 1088 | len = mtu - skb->len; | ||
| 1089 | if (len < size) | ||
| 1090 | len = maxfraglen - skb->len; | ||
| 1091 | } | ||
| 1019 | if (len <= 0) { | 1092 | if (len <= 0) { |
| 1020 | struct sk_buff *skb_prev; | 1093 | struct sk_buff *skb_prev; |
| 1021 | char *data; | 1094 | char *data; |
