/** * struct thunderbolt_ip_frame_header - Header for each Thunderbolt frame * @frame_size: size of the data with the frame * @frame_index: running index on the frames * @frame_id: ID of the frame to match frames to specific packet * @frame_count: how many frames assembles a full packet * * Each data frame passed to the high-speed DMA ring has this header. If * the XDomain network directory announces that %TBNET_MATCH_FRAGS_ID is * supported then @frame_id is filled, otherwise it stays %0.
*/ struct thunderbolt_ip_frame_header {
__le32 frame_size;
__le16 frame_index;
__le16 frame_id;
__le32 frame_count;
};
/** * struct tbnet - ThunderboltIP network driver private data * @svc: XDomain service the driver is bound to * @xd: XDomain the service belongs to * @handler: ThunderboltIP configuration protocol handler * @dev: Networking device * @napi: NAPI structure for Rx polling * @stats: Network statistics * @skb: Network packet that is currently processed on Rx path * @command_id: ID used for next configuration protocol packet * @login_sent: ThunderboltIP login message successfully sent * @login_received: ThunderboltIP login message received from the remote * host * @local_transmit_path: HopID we are using to send out packets * @remote_transmit_path: HopID the other end is using to send packets to us * @connection_lock: Lock serializing access to @login_sent, * @login_received and @transmit_path. * @login_retries: Number of login retries currently done * @login_work: Worker to send ThunderboltIP login packets * @connected_work: Worker that finalizes the ThunderboltIP connection * setup and enables DMA paths for high speed data * transfers * @disconnect_work: Worker that handles tearing down the ThunderboltIP * connection * @rx_hdr: Copy of the currently processed Rx frame. Used when a * network packet consists of multiple Thunderbolt frames. * In host byte order. * @rx_ring: Software ring holding Rx frames * @frame_id: Frame ID use for next Tx packet * (if %TBNET_MATCH_FRAGS_ID is supported in both ends) * @tx_ring: Software ring holding Tx frames
*/ struct tbnet { conststruct tb_service *svc; struct tb_xdomain *xd; struct tb_protocol_handler handler; struct net_device *dev; struct napi_struct napi; struct tbnet_stats stats; struct sk_buff *skb;
atomic_t command_id; bool login_sent; bool login_received; int local_transmit_path; int remote_transmit_path; struct mutex connection_lock; int login_retries; struct delayed_work login_work; struct work_struct connected_work; struct work_struct disconnect_work; struct thunderbolt_ip_frame_header rx_hdr; struct tbnet_ring rx_ring;
atomic_t frame_id; struct tbnet_ring tx_ring;
};
for (i = 0; i < TBNET_RING_SIZE; i++) { struct device *dma_dev = tb_ring_dma_device(ring->ring); struct tbnet_frame *tf = &ring->frames[i]; enum dma_data_direction dir; unsignedint order;
size_t size;
if (!tf->page) continue;
if (ring->ring->is_tx) {
dir = DMA_TO_DEVICE;
order = 0;
size = TBNET_FRAME_SIZE;
} else {
dir = DMA_FROM_DEVICE;
order = TBNET_RX_PAGE_ORDER;
size = TBNET_RX_PAGE_SIZE;
}
ret = tb_xdomain_disable_paths(net->xd,
net->local_transmit_path,
net->tx_ring.ring->hop,
net->remote_transmit_path,
net->rx_ring.ring->hop); if (ret)
netdev_warn(net->dev, "failed to disable DMA paths\n");
/* Make sure the packet is for us */ if (size < sizeof(struct thunderbolt_ip_header)) return 0; if (!uuid_equal(&pkg->hdr.initiator_uuid, net->xd->remote_uuid)) return 0; if (!uuid_equal(&pkg->hdr.target_uuid, net->xd->local_uuid)) return 0;
/* If we reached the number of max retries or * previous logout, schedule another round of * login retries
*/ if (net->login_retries >= TBNET_LOGIN_RETRIES ||
!net->login_sent) {
net->login_retries = 0;
queue_delayed_work(system_long_wq,
&net->login_work, 0);
}
mutex_unlock(&net->connection_lock);
/* Allocate page (order > 0) so that it can hold maximum * ThunderboltIP frame (4kB) and the additional room for * SKB shared info required by build_skb().
*/
tf->page = dev_alloc_pages(TBNET_RX_PAGE_ORDER); if (!tf->page) {
ret = -ENOMEM; goto err_free;
}
dma_addr = dma_map_page(dma_dev, tf->page, 0,
TBNET_RX_PAGE_SIZE, DMA_FROM_DEVICE); if (dma_mapping_error(dma_dev, dma_addr)) {
ret = -ENOMEM; goto err_free;
}
ret = tb_xdomain_alloc_in_hopid(net->xd, net->remote_transmit_path); if (ret != net->remote_transmit_path) {
netdev_err(net->dev, "failed to allocate Rx HopID\n"); return;
}
/* Both logins successful so enable the rings, high-speed DMA * paths and start the network device queue. * * Note we enable the DMA paths last to make sure we have primed * the Rx ring before any incoming packets are allowed to * arrive.
*/
tb_ring_start(net->tx_ring.ring);
tb_ring_start(net->rx_ring.ring);
ret = tbnet_alloc_rx_buffers(net, TBNET_RING_SIZE); if (ret) goto err_stop_rings;
ret = tbnet_alloc_tx_buffers(net); if (ret) goto err_free_rx_buffers;
ret = tb_xdomain_enable_paths(net->xd, net->local_transmit_path,
net->tx_ring.ring->hop,
net->remote_transmit_path,
net->rx_ring.ring->hop); if (ret) {
netdev_err(net->dev, "failed to enable DMA paths\n"); goto err_free_tx_buffers;
}
/* Should be greater than just header i.e. contains data */
size = tbnet_frame_size(tf); if (size <= sizeof(*hdr)) {
net->stats.rx_length_errors++; returnfalse;
}
/* In case we're in the middle of packet, validate the frame * header based on first fragment of the packet.
*/ if (net->skb && net->rx_hdr.frame_count) { /* Check the frame count fits the count field */ if (frame_count != le32_to_cpu(net->rx_hdr.frame_count)) {
net->stats.rx_length_errors++; returnfalse;
}
/* Check the frame identifiers are incremented correctly, * and id is matching.
*/ if (frame_index != le16_to_cpu(net->rx_hdr.frame_index) + 1 ||
frame_id != le16_to_cpu(net->rx_hdr.frame_id)) {
net->stats.rx_missed_errors++; returnfalse;
}
if (net->skb->len + frame_size > TBNET_MAX_MTU) {
net->stats.rx_length_errors++; returnfalse;
}
returntrue;
}
/* Start of packet, validate the frame header */ if (frame_count == 0 || frame_count > TBNET_RING_SIZE / 4) {
net->stats.rx_length_errors++; returnfalse;
} if (frame_index != 0) {
net->stats.rx_missed_errors++; returnfalse;
}
/* Return some buffers to hardware, one at a time is too * slow so allocate MAX_SKB_FRAGS buffers at the same * time.
*/ if (cleaned_count >= MAX_SKB_FRAGS) {
tbnet_alloc_rx_buffers(net, cleaned_count);
cleaned_count = 0;
}
frame = tb_ring_poll(net->rx_ring.ring); if (!frame) break;
flags = RING_FLAG_FRAME; /* Only enable full E2E if the other end supports it too */ if (tbnet_e2e && net->svc->prtcstns & TBNET_E2E)
flags |= RING_FLAG_E2E;
ring = tb_ring_alloc_tx(xd->tb->nhi, -1, TBNET_RING_SIZE, flags); if (!ring) {
netdev_err(dev, "failed to allocate Tx ring\n"); return -ENOMEM;
}
net->tx_ring.ring = ring;
if (skb->ip_summed != CHECKSUM_PARTIAL) { /* No need to calculate checksum so we just update the * total frame count and sync the frames for DMA.
*/ for (i = 0; i < frame_count; i++) {
hdr = page_address(frames[i]->page);
hdr->frame_count = cpu_to_le32(frame_count);
trace_tbnet_tx_ip_frame(hdr->frame_size, hdr->frame_id,
hdr->frame_index, hdr->frame_count);
dma_sync_single_for_device(dma_dev,
frames[i]->frame.buffer_phy,
tbnet_frame_size(frames[i]), DMA_TO_DEVICE);
}
returntrue;
}
if (protocol == htons(ETH_P_8021Q)) { struct vlan_hdr *vhdr, vh;
vhdr = skb_header_pointer(skb, ETH_HLEN, sizeof(vh), &vh); if (!vhdr) returnfalse;
protocol = vhdr->h_vlan_encapsulated_proto;
}
/* Data points on the beginning of packet. * Check is the checksum absolute place in the packet. * ipcso will update IP checksum. * tucso will update TCP/UDP checksum.
*/ if (protocol == htons(ETH_P_IP)) {
__sum16 *ipcso = dest + ((void *)&(ip_hdr(skb)->check) - data);
/* First frame was headers, rest of the frames contain data. * Calculate checksum over each frame.
*/ for (i = 0; i < frame_count; i++) {
hdr = page_address(frames[i]->page);
dest = (void *)(hdr + 1) + offset;
len = le32_to_cpu(hdr->frame_size) - offset;
wsum = csum_partial(dest, len, wsum);
hdr->frame_count = cpu_to_le32(frame_count);
trace_tbnet_tx_ip_frame(hdr->frame_size, hdr->frame_id,
hdr->frame_index, hdr->frame_count);
offset = 0;
}
*tucso = csum_fold(wsum);
/* Checksum is finally calculated and we don't touch the memory * anymore, so DMA sync the frames now.
*/ for (i = 0; i < frame_count; i++) {
dma_sync_single_for_device(dma_dev, frames[i]->frame.buffer_phy,
tbnet_frame_size(frames[i]), DMA_TO_DEVICE);
}
do { if (len > size_left) { /* Copy data onto Tx buffer data with * full frame size then break and go to * next frame
*/
memcpy(dest, src, size_left);
len -= size_left;
dest += size_left;
src += size_left; break;
}
memcpy(dest, src, len);
size_left -= len;
dest += len;
if (unmap) {
kunmap_local(src);
unmap = false;
}
/* Ensure all fragments have been processed */ if (frag < skb_shinfo(skb)->nr_frags) { /* Map and then unmap quickly */
src = tbnet_kmap_frag(skb, frag++, &len);
unmap = true;
} elseif (unlikely(size_left > 0)) { goto err_drop;
}
} while (size_left > 0);
/* ThunderboltIP takes advantage of TSO packets but instead of * segmenting them we just split the packet into Thunderbolt * frames (maximum payload size of each frame is 4084 bytes) and * calculate checksum over the whole packet here. * * The receiving side does the opposite if the host OS supports * LRO, otherwise it needs to split the large packet into MTU * sized smaller packets. * * In order to receive large packets from the networking stack, * we need to announce support for most of the offloading * features here.
*/
dev->hw_features = NETIF_F_SG | NETIF_F_ALL_TSO | NETIF_F_GRO |
NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
dev->features = dev->hw_features | NETIF_F_HIGHDMA;
dev->hard_header_len += sizeof(struct thunderbolt_ip_frame_header);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.