qeth_eddp.c
来自「linux 内核源代码」· C语言 代码 · 共 635 行 · 第 1/2 页
C
635 行
QETH_DBF_TEXT(trace, 5, "eddpcsdt"); page = ctx->pages[ctx->offset >> PAGE_SHIFT]; page_offset = ctx->offset % PAGE_SIZE; element = &ctx->elements[ctx->num_elements]; while (data_len){ page_remainder = PAGE_SIZE - page_offset; if (page_remainder < data_len){ qeth_eddp_copy_data_tcp(page + page_offset, eddp, page_remainder, &hcsum); element->length += page_remainder; if (first_lap) element->flags = SBAL_FLAGS_FIRST_FRAG; else element->flags = SBAL_FLAGS_MIDDLE_FRAG; ctx->num_elements++; element++; data_len -= page_remainder; ctx->offset += page_remainder; page = ctx->pages[ctx->offset >> PAGE_SHIFT]; page_offset = 0; element->addr = page + page_offset; } else { qeth_eddp_copy_data_tcp(page + page_offset, eddp, data_len, &hcsum); element->length += data_len; if (!first_lap) element->flags = SBAL_FLAGS_LAST_FRAG; ctx->num_elements++; ctx->offset += data_len; data_len = 0; } first_lap = 0; } ((struct tcphdr *)eddp->th_in_ctx)->check = csum_fold(hcsum);}static __wsumqeth_eddp_check_tcp4_hdr(struct qeth_eddp_data *eddp, int data_len){ __wsum phcsum; /* pseudo header checksum */ QETH_DBF_TEXT(trace, 5, "eddpckt4"); eddp->th.tcp.h.check = 0; /* compute pseudo header checksum */ phcsum = csum_tcpudp_nofold(eddp->nh.ip4.h.saddr, eddp->nh.ip4.h.daddr, eddp->thl + data_len, IPPROTO_TCP, 0); /* compute checksum of tcp header */ return csum_partial((u8 *)&eddp->th, eddp->thl, phcsum);}static __wsumqeth_eddp_check_tcp6_hdr(struct qeth_eddp_data *eddp, int data_len){ __be32 proto; __wsum phcsum; /* pseudo header checksum */ QETH_DBF_TEXT(trace, 5, "eddpckt6"); eddp->th.tcp.h.check = 0; /* compute pseudo header checksum */ phcsum = csum_partial((u8 *)&eddp->nh.ip6.h.saddr, sizeof(struct in6_addr), 0); phcsum = csum_partial((u8 *)&eddp->nh.ip6.h.daddr, sizeof(struct in6_addr), phcsum); proto = htonl(IPPROTO_TCP); phcsum = csum_partial((u8 *)&proto, sizeof(u32), phcsum); return phcsum;}static struct qeth_eddp_data *qeth_eddp_create_eddp_data(struct qeth_hdr *qh, u8 *nh, u8 nhl, u8 *th, u8 thl){ struct qeth_eddp_data *eddp; QETH_DBF_TEXT(trace, 5, "eddpcrda"); eddp = kzalloc(sizeof(struct qeth_eddp_data), GFP_ATOMIC); if (eddp){ eddp->nhl = nhl; eddp->thl = thl; memcpy(&eddp->qh, qh, sizeof(struct qeth_hdr)); memcpy(&eddp->nh, nh, nhl); memcpy(&eddp->th, th, thl); eddp->frag = -1; /* initially we're in skb->data */ } return eddp;}static void__qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx, struct qeth_eddp_data *eddp){ struct tcphdr *tcph; int data_len; __wsum hcsum; QETH_DBF_TEXT(trace, 5, "eddpftcp"); eddp->skb_offset = sizeof(struct qeth_hdr) + eddp->nhl + eddp->thl; if (eddp->qh.hdr.l2.id == QETH_HEADER_TYPE_LAYER2) { eddp->skb_offset += sizeof(struct ethhdr);#ifdef CONFIG_QETH_VLAN if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q)) eddp->skb_offset += VLAN_HLEN;#endif /* CONFIG_QETH_VLAN */ } tcph = tcp_hdr(eddp->skb); while (eddp->skb_offset < eddp->skb->len) { data_len = min((int)skb_shinfo(eddp->skb)->gso_size, (int)(eddp->skb->len - eddp->skb_offset)); /* prepare qdio hdr */ if (eddp->qh.hdr.l2.id == QETH_HEADER_TYPE_LAYER2){ eddp->qh.hdr.l2.pkt_length = data_len + ETH_HLEN + eddp->nhl + eddp->thl;#ifdef CONFIG_QETH_VLAN if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q)) eddp->qh.hdr.l2.pkt_length += VLAN_HLEN;#endif /* CONFIG_QETH_VLAN */ } else eddp->qh.hdr.l3.length = data_len + eddp->nhl + eddp->thl; /* prepare ip hdr */ if (eddp->skb->protocol == htons(ETH_P_IP)){ eddp->nh.ip4.h.tot_len = htons(data_len + eddp->nhl + eddp->thl); eddp->nh.ip4.h.check = 0; eddp->nh.ip4.h.check = ip_fast_csum((u8 *)&eddp->nh.ip4.h, eddp->nh.ip4.h.ihl); } else eddp->nh.ip6.h.payload_len = htons(data_len + eddp->thl); /* prepare tcp hdr */ if (data_len == (eddp->skb->len - eddp->skb_offset)){ /* last segment -> set FIN and PSH flags */ eddp->th.tcp.h.fin = tcph->fin; eddp->th.tcp.h.psh = tcph->psh; } if (eddp->skb->protocol == htons(ETH_P_IP)) hcsum = qeth_eddp_check_tcp4_hdr(eddp, data_len); else hcsum = qeth_eddp_check_tcp6_hdr(eddp, data_len); /* fill the next segment into the context */ qeth_eddp_create_segment_hdrs(ctx, eddp, data_len); qeth_eddp_create_segment_data_tcp(ctx, eddp, data_len, hcsum); if (eddp->skb_offset >= eddp->skb->len) break; /* prepare headers for next round */ if (eddp->skb->protocol == htons(ETH_P_IP)) eddp->nh.ip4.h.id = htons(ntohs(eddp->nh.ip4.h.id) + 1); eddp->th.tcp.h.seq = htonl(ntohl(eddp->th.tcp.h.seq) + data_len); }}static intqeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx, struct sk_buff *skb, struct qeth_hdr *qhdr){ struct qeth_eddp_data *eddp = NULL; QETH_DBF_TEXT(trace, 5, "eddpficx"); /* create our segmentation headers and copy original headers */ if (skb->protocol == htons(ETH_P_IP)) eddp = qeth_eddp_create_eddp_data(qhdr, skb_network_header(skb), ip_hdrlen(skb), skb_transport_header(skb), tcp_hdrlen(skb)); else eddp = qeth_eddp_create_eddp_data(qhdr, skb_network_header(skb), sizeof(struct ipv6hdr), skb_transport_header(skb), tcp_hdrlen(skb)); if (eddp == NULL) { QETH_DBF_TEXT(trace, 2, "eddpfcnm"); return -ENOMEM; } if (qhdr->hdr.l2.id == QETH_HEADER_TYPE_LAYER2) { skb_set_mac_header(skb, sizeof(struct qeth_hdr)); memcpy(&eddp->mac, eth_hdr(skb), ETH_HLEN);#ifdef CONFIG_QETH_VLAN if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q)) { eddp->vlan[0] = skb->protocol; eddp->vlan[1] = htons(vlan_tx_tag_get(skb)); }#endif /* CONFIG_QETH_VLAN */ } /* the next flags will only be set on the last segment */ eddp->th.tcp.h.fin = 0; eddp->th.tcp.h.psh = 0; eddp->skb = skb; /* begin segmentation and fill context */ __qeth_eddp_fill_context_tcp(ctx, eddp); kfree(eddp); return 0;}static voidqeth_eddp_calc_num_pages(struct qeth_eddp_context *ctx, struct sk_buff *skb, int hdr_len){ int skbs_per_page; QETH_DBF_TEXT(trace, 5, "eddpcanp"); /* can we put multiple skbs in one page? */ skbs_per_page = PAGE_SIZE / (skb_shinfo(skb)->gso_size + hdr_len); if (skbs_per_page > 1){ ctx->num_pages = (skb_shinfo(skb)->gso_segs + 1) / skbs_per_page + 1; ctx->elements_per_skb = 1; } else { /* no -> how many elements per skb? */ ctx->elements_per_skb = (skb_shinfo(skb)->gso_size + hdr_len + PAGE_SIZE) >> PAGE_SHIFT; ctx->num_pages = ctx->elements_per_skb * (skb_shinfo(skb)->gso_segs + 1); } ctx->num_elements = ctx->elements_per_skb * (skb_shinfo(skb)->gso_segs + 1);}static struct qeth_eddp_context *qeth_eddp_create_context_generic(struct qeth_card *card, struct sk_buff *skb, int hdr_len){ struct qeth_eddp_context *ctx = NULL; u8 *addr; int i; QETH_DBF_TEXT(trace, 5, "creddpcg"); /* create the context and allocate pages */ ctx = kzalloc(sizeof(struct qeth_eddp_context), GFP_ATOMIC); if (ctx == NULL){ QETH_DBF_TEXT(trace, 2, "ceddpcn1"); return NULL; } ctx->type = QETH_LARGE_SEND_EDDP; qeth_eddp_calc_num_pages(ctx, skb, hdr_len); if (ctx->elements_per_skb > QETH_MAX_BUFFER_ELEMENTS(card)){ QETH_DBF_TEXT(trace, 2, "ceddpcis"); kfree(ctx); return NULL; } ctx->pages = kcalloc(ctx->num_pages, sizeof(u8 *), GFP_ATOMIC); if (ctx->pages == NULL){ QETH_DBF_TEXT(trace, 2, "ceddpcn2"); kfree(ctx); return NULL; } for (i = 0; i < ctx->num_pages; ++i){ addr = (u8 *)__get_free_page(GFP_ATOMIC); if (addr == NULL){ QETH_DBF_TEXT(trace, 2, "ceddpcn3"); ctx->num_pages = i; qeth_eddp_free_context(ctx); return NULL; } memset(addr, 0, PAGE_SIZE); ctx->pages[i] = addr; } ctx->elements = kcalloc(ctx->num_elements, sizeof(struct qeth_eddp_element), GFP_ATOMIC); if (ctx->elements == NULL){ QETH_DBF_TEXT(trace, 2, "ceddpcn4"); qeth_eddp_free_context(ctx); return NULL; } /* reset num_elements; will be incremented again in fill_buffer to * reflect number of actually used elements */ ctx->num_elements = 0; return ctx;}static struct qeth_eddp_context *qeth_eddp_create_context_tcp(struct qeth_card *card, struct sk_buff *skb, struct qeth_hdr *qhdr){ struct qeth_eddp_context *ctx = NULL; QETH_DBF_TEXT(trace, 5, "creddpct"); if (skb->protocol == htons(ETH_P_IP)) ctx = qeth_eddp_create_context_generic(card, skb, (sizeof(struct qeth_hdr) + ip_hdrlen(skb) + tcp_hdrlen(skb))); else if (skb->protocol == htons(ETH_P_IPV6)) ctx = qeth_eddp_create_context_generic(card, skb, sizeof(struct qeth_hdr) + sizeof(struct ipv6hdr) + tcp_hdrlen(skb)); else QETH_DBF_TEXT(trace, 2, "cetcpinv"); if (ctx == NULL) { QETH_DBF_TEXT(trace, 2, "creddpnl"); return NULL; } if (qeth_eddp_fill_context_tcp(ctx, skb, qhdr)){ QETH_DBF_TEXT(trace, 2, "ceddptfe"); qeth_eddp_free_context(ctx); return NULL; } atomic_set(&ctx->refcnt, 1); return ctx;}struct qeth_eddp_context *qeth_eddp_create_context(struct qeth_card *card, struct sk_buff *skb, struct qeth_hdr *qhdr, unsigned char sk_protocol){ QETH_DBF_TEXT(trace, 5, "creddpc"); switch (sk_protocol) { case IPPROTO_TCP: return qeth_eddp_create_context_tcp(card, skb, qhdr); default: QETH_DBF_TEXT(trace, 2, "eddpinvp"); } return NULL;}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?