0001
0002 #include <linux/export.h>
0003 #include <linux/if_vlan.h>
0004 #include <net/ip.h>
0005 #include <net/tso.h>
0006 #include <asm/unaligned.h>
0007
0008
0009 int tso_count_descs(const struct sk_buff *skb)
0010 {
0011
0012 return skb_shinfo(skb)->gso_segs * 2 + skb_shinfo(skb)->nr_frags;
0013 }
0014 EXPORT_SYMBOL(tso_count_descs);
0015
0016 void tso_build_hdr(const struct sk_buff *skb, char *hdr, struct tso_t *tso,
0017 int size, bool is_last)
0018 {
0019 int hdr_len = skb_transport_offset(skb) + tso->tlen;
0020 int mac_hdr_len = skb_network_offset(skb);
0021
0022 memcpy(hdr, skb->data, hdr_len);
0023 if (!tso->ipv6) {
0024 struct iphdr *iph = (void *)(hdr + mac_hdr_len);
0025
0026 iph->id = htons(tso->ip_id);
0027 iph->tot_len = htons(size + hdr_len - mac_hdr_len);
0028 tso->ip_id++;
0029 } else {
0030 struct ipv6hdr *iph = (void *)(hdr + mac_hdr_len);
0031
0032 iph->payload_len = htons(size + tso->tlen);
0033 }
0034 hdr += skb_transport_offset(skb);
0035 if (tso->tlen != sizeof(struct udphdr)) {
0036 struct tcphdr *tcph = (struct tcphdr *)hdr;
0037
0038 put_unaligned_be32(tso->tcp_seq, &tcph->seq);
0039
0040 if (!is_last) {
0041
0042 tcph->psh = 0;
0043 tcph->fin = 0;
0044 tcph->rst = 0;
0045 }
0046 } else {
0047 struct udphdr *uh = (struct udphdr *)hdr;
0048
0049 uh->len = htons(sizeof(*uh) + size);
0050 }
0051 }
0052 EXPORT_SYMBOL(tso_build_hdr);
0053
0054 void tso_build_data(const struct sk_buff *skb, struct tso_t *tso, int size)
0055 {
0056 tso->tcp_seq += size;
0057 tso->size -= size;
0058 tso->data += size;
0059
0060 if ((tso->size == 0) &&
0061 (tso->next_frag_idx < skb_shinfo(skb)->nr_frags)) {
0062 skb_frag_t *frag = &skb_shinfo(skb)->frags[tso->next_frag_idx];
0063
0064
0065 tso->size = skb_frag_size(frag);
0066 tso->data = skb_frag_address(frag);
0067 tso->next_frag_idx++;
0068 }
0069 }
0070 EXPORT_SYMBOL(tso_build_data);
0071
0072 int tso_start(struct sk_buff *skb, struct tso_t *tso)
0073 {
0074 int tlen = skb_is_gso_tcp(skb) ? tcp_hdrlen(skb) : sizeof(struct udphdr);
0075 int hdr_len = skb_transport_offset(skb) + tlen;
0076
0077 tso->tlen = tlen;
0078 tso->ip_id = ntohs(ip_hdr(skb)->id);
0079 tso->tcp_seq = (tlen != sizeof(struct udphdr)) ? ntohl(tcp_hdr(skb)->seq) : 0;
0080 tso->next_frag_idx = 0;
0081 tso->ipv6 = vlan_get_protocol(skb) == htons(ETH_P_IPV6);
0082
0083
0084 tso->size = skb_headlen(skb) - hdr_len;
0085 tso->data = skb->data + hdr_len;
0086 if ((tso->size == 0) &&
0087 (tso->next_frag_idx < skb_shinfo(skb)->nr_frags)) {
0088 skb_frag_t *frag = &skb_shinfo(skb)->frags[tso->next_frag_idx];
0089
0090
0091 tso->size = skb_frag_size(frag);
0092 tso->data = skb_frag_address(frag);
0093 tso->next_frag_idx++;
0094 }
0095 return hdr_len;
0096 }
0097 EXPORT_SYMBOL(tso_start);