// SPDX-License-Identifier: ISC /* * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
*/
/* wil_ring_wmark_low - low watermark for available descriptor space */ staticinlineint wil_ring_wmark_low(struct wil_ring *ring)
{ return ring->size / 8;
}
/* wil_ring_wmark_high - high watermark for available descriptor space */ staticinlineint wil_ring_wmark_high(struct wil_ring *ring)
{ return ring->size / 4;
}
/* returns true if num avail descriptors is lower than wmark_low */ staticinlineint wil_ring_avail_low(struct wil_ring *ring)
{ return wil_ring_avail_tx(ring) < wil_ring_wmark_low(ring);
}
/* returns true if num avail descriptors is higher than wmark_high */ staticinlineint wil_ring_avail_high(struct wil_ring *ring)
{ return wil_ring_avail_tx(ring) > wil_ring_wmark_high(ring);
}
/* returns true when all tx vrings are empty */ bool wil_is_tx_idle(struct wil6210_priv *wil)
{ int i; unsignedlong data_comp_to; int min_ring_id = wil_get_min_tx_ring_id(wil);
for (i = min_ring_id; i < WIL6210_MAX_TX_RINGS; i++) { struct wil_ring *vring = &wil->ring_tx[i]; int vring_index = vring - wil->ring_tx; struct wil_ring_tx_data *txdata =
&wil->ring_tx_data[vring_index];
spin_lock(&txdata->lock);
if (!vring->va || !txdata->enabled) {
spin_unlock(&txdata->lock); continue;
}
data_comp_to = jiffies + msecs_to_jiffies(
WIL_DATA_COMPLETION_TO_MS); if (test_bit(wil_status_napi_en, wil->status)) { while (!wil_ring_is_empty(vring)) { if (time_after(jiffies, data_comp_to)) {
wil_dbg_pm(wil, "TO waiting for idle tx\n");
spin_unlock(&txdata->lock); returnfalse;
}
wil_dbg_ratelimited(wil, "tx vring is not empty -> NAPI\n");
spin_unlock(&txdata->lock);
napi_synchronize(&wil->napi_tx);
msleep(20);
spin_lock(&txdata->lock); if (!vring->va || !txdata->enabled) break;
}
}
/* vring->va should be aligned on its size rounded up to power of 2 * This is granted by the dma_alloc_coherent. * * HW has limitation that all vrings addresses must share the same * upper 16 msb bits part of 48 bits address. To workaround that, * if we are using more than 32 bit addresses switch to 32 bit * allocation before allocating vring memory. * * There's no check for the return value of dma_set_mask_and_coherent, * since we assume if we were able to set the mask during * initialization in this system it will not fail if we set it again
*/ if (wil->dma_addr_size > 32)
dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (wil->dma_addr_size > 32)
dma_set_mask_and_coherent(dev,
DMA_BIT_MASK(wil->dma_addr_size));
/* initially, all descriptors are SW owned * For Tx and Rx, ownership bit is at the same location, thus * we can use any
*/ for (i = 0; i < vring->size; i++) { volatilestruct vring_tx_desc *_d =
&vring->va[i].tx.legacy;
_d = (struct vring_rx_desc *)&ring->va[ring->swhead].rx.legacy; if (_d->dma.status & RX_DMA_STATUS_DU) returnfalse;
returntrue;
}
staticint wil_rx_get_cid_by_skb(struct wil6210_priv *wil, struct sk_buff *skb)
{ struct vring_rx_desc *d = wil_skb_rxdesc(skb); int mid = wil_rxdesc_mid(d); struct wil6210_vif *vif = wil->vifs[mid]; /* cid from DMA descriptor is limited to 3 bits. * In case of cid>=8, the value would be cid modulo 8 and we need to * find real cid by locating the transmitter (ta) inside sta array
*/ int cid = wil_rxdesc_cid(d); unsignedint snaplen = wil_rx_snaplen(); struct ieee80211_hdr_3addr *hdr; int i; unsignedchar *ta;
u8 ftype;
/* in monitor mode there are no connections */ if (vif->wdev.iftype == NL80211_IFTYPE_MONITOR) return cid;
ftype = wil_rxdesc_ftype(d) << 2; if (likely(ftype == IEEE80211_FTYPE_DATA)) { if (unlikely(skb->len < ETH_HLEN + snaplen)) {
wil_err_ratelimited(wil, "Short data frame, len = %d\n",
skb->len); return -ENOENT;
}
ta = wil_skb_get_sa(skb);
} else { if (unlikely(skb->len < sizeof(struct ieee80211_hdr_3addr))) {
wil_err_ratelimited(wil, "Short frame, len = %d\n",
skb->len); return -ENOENT;
}
hdr = (void *)skb->data;
ta = hdr->addr2;
}
if (wil->max_assoc_sta <= WIL6210_RX_DESC_MAX_CID) return cid;
/* assuming no concurrency between AP interfaces and STA interfaces. * multista is used only in P2P_GO or AP mode. In other modes return * cid from the rx descriptor
*/ if (vif->wdev.iftype != NL80211_IFTYPE_P2P_GO &&
vif->wdev.iftype != NL80211_IFTYPE_AP) return cid;
/* For Rx packets cid from rx descriptor is limited to 3 bits (0..7), * to find the real cid, compare transmitter address with the stored * stations mac address in the driver sta array
*/ for (i = cid; i < wil->max_assoc_sta; i += WIL6210_RX_DESC_MAX_CID) { if (wil->sta[i].status != wil_sta_unused &&
ether_addr_equal(wil->sta[i].addr, ta)) {
cid = i; break;
}
} if (i >= wil->max_assoc_sta) {
wil_err_ratelimited(wil, "Could not find cid for frame with transmit addr = %pM, iftype = %d, frametype = %d, len = %d\n",
ta, vif->wdev.iftype, ftype, skb->len);
cid = -ENOENT;
}
again: if (unlikely(wil_ring_is_empty(vring))) return NULL;
i = (int)vring->swhead;
_d = &vring->va[i].rx.legacy; if (unlikely(!(_d->dma.status & RX_DMA_STATUS_DU))) { /* it is not error, we just reached end of Rx done area */ return NULL;
}
skb = vring->ctx[i].skb;
vring->ctx[i].skb = NULL;
wil_ring_advance_head(vring, 1); if (!skb) {
wil_err(wil, "No Rx skb at [%d]\n", i); goto again;
}
d = wil_skb_rxdesc(skb);
*d = *_d;
pa = wil_desc_addr(&d->dma.addr);
stats->last_mcs_rx = wil_rxdesc_mcs(d); if (stats->last_mcs_rx < ARRAY_SIZE(stats->rx_per_mcs))
stats->rx_per_mcs[stats->last_mcs_rx]++;
/* use radiotap header only if required */ if (ndev->type == ARPHRD_IEEE80211_RADIOTAP)
wil_rx_add_radiotap_header(wil, skb);
/* no extra checks if in sniffer mode */ if (ndev->type != ARPHRD_ETHER) return skb; /* Non-data frames may be delivered through Rx DMA channel (ex: BAR) * Driver should recognize it by frame type, that is found * in Rx descriptor. If type is not data, it is 802.11 frame as is
*/
ftype = wil_rxdesc_ftype(d) << 2; if (unlikely(ftype != IEEE80211_FTYPE_DATA)) {
u8 fc1 = wil_rxdesc_fc1(d); int tid = wil_rxdesc_tid(d);
u16 seq = wil_rxdesc_seq(d);
wil_dbg_txrx(wil, "Non-data frame FC[7:0] 0x%02x MID %d CID %d TID %d Seq 0x%03x\n",
fc1, mid, cid, tid, seq);
stats->rx_non_data_frame++; if (wil_is_back_req(fc1)) {
wil_dbg_txrx(wil, "BAR: MID %d CID %d TID %d Seq 0x%03x\n",
mid, cid, tid, seq);
wil_rx_bar(wil, vif, cid, tid, seq);
} else { /* print again all info. One can enable only this * without overhead for printing every Rx frame
*/
wil_dbg_txrx(wil, "Unhandled non-data frame FC[7:0] 0x%02x MID %d CID %d TID %d Seq 0x%03x\n",
fc1, mid, cid, tid, seq);
wil_hex_dump_txrx("RxD ", DUMP_PREFIX_NONE, 32, 4,
(constvoid *)d, sizeof(*d), false);
wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
skb->data, skb_headlen(skb), false);
}
kfree_skb(skb); goto again;
}
/* L4 IDENT is on when HW calculated checksum, check status * and in case of error drop the packet * higher stack layers will handle retransmission (if required)
*/ if (likely(d->dma.status & RX_DMA_STATUS_L4I)) { /* L4 protocol identified, csum calculated */ if (likely((d->dma.error & RX_DMA_ERROR_L4_ERR) == 0))
skb->ip_summed = CHECKSUM_UNNECESSARY; /* If HW reports bad checksum, let IP stack re-check it * For example, HW don't understand Microsoft IP stack that * mis-calculates TCP checksum - if it should be 0x0, * it writes 0xffff in violation of RFC 1624
*/ else
stats->rx_csum_err++;
}
if (snaplen) { /* Packet layout * +-------+-------+---------+------------+------+ * | SA(6) | DA(6) | SNAP(6) | ETHTYPE(2) | DATA | * +-------+-------+---------+------------+------+ * Need to remove SNAP, shifting SA and DA forward
*/
memmove(skb->data + snaplen, skb->data, 2 * ETH_ALEN);
skb_pull(skb, snaplen);
}
return skb;
}
/* allocate and fill up to @count buffers in rx ring * buffers posted at @swtail * Note: we have a single RX queue for servicing all VIFs, but we * allocate skbs with headroom according to main interface only. This * means it will not work with monitor interface together with other VIFs. * Currently we only support monitor interface on its own without other VIFs, * and we will need to fix this code once we add support.
*/ staticint wil_rx_refill(struct wil6210_priv *wil, int count)
{ struct net_device *ndev = wil->main_ndev; struct wil_ring *v = &wil->ring_rx;
u32 next_tail; int rc = 0; int headroom = ndev->type == ARPHRD_IEEE80211_RADIOTAP ?
WIL6210_RTAP_SIZE : 0;
/* make sure all writes to descriptors (shared memory) are done before * committing them to HW
*/
wmb();
wil_w(wil, v->hwtail, v->swtail);
return rc;
}
/** * reverse_memcmp - Compare two areas of memory, in reverse order * @cs: One area of memory * @ct: Another area of memory * @count: The size of the area. * * Cut'n'paste from original memcmp (see lib/string.c) * with minimal modifications
*/ int reverse_memcmp(constvoid *cs, constvoid *ct, size_t count)
{ constunsignedchar *su1, *su2; int res = 0;
/* * Check if skb is ptk eapol key message * * returns a pointer to the start of the eapol key structure, NULL * if frame is not PTK eapol key
*/ staticstruct wil_eapol_key *wil_is_ptk_eapol_key(struct wil6210_priv *wil, struct sk_buff *skb)
{
u8 *buf; conststruct wil_1x_hdr *hdr; struct wil_eapol_key *key;
u16 key_info; int len = skb->len;
if (!skb_mac_header_was_set(skb)) {
wil_err(wil, "mac header was not set\n"); return NULL;
}
if (wdev->iftype != NL80211_IFTYPE_STATION ||
!test_bit(WMI_FW_CAPABILITY_SPLIT_REKEY, wil->fw_capabilities)) return;
/* check if skb is a EAP message 3/4 */ if (!wil_skb_is_eap_3(wil, skb)) return;
if (vif->ptk_rekey_state == WIL_REKEY_IDLE)
vif->ptk_rekey_state = WIL_REKEY_M3_RECEIVED;
}
/* * Pass Rx packet to the netif. Update statistics. * Called in softirq context (NAPI poll).
*/ void wil_netif_rx(struct sk_buff *skb, struct net_device *ndev, int cid, struct wil_net_stats *stats, bool gro)
{ struct wil6210_vif *vif = ndev_to_vif(ndev); struct wil6210_priv *wil = ndev_to_wil(ndev); struct wireless_dev *wdev = vif_to_wdev(vif); unsignedint len = skb->len;
u8 *sa, *da = wil_skb_get_da(skb); /* here looking for DA, not A1, thus Rxdesc's 'mcast' indication * is not suitable, need to look at data
*/ int mcast = is_multicast_ether_addr(da); struct sk_buff *xmit_skb = NULL;
if (wdev->iftype == NL80211_IFTYPE_STATION) {
sa = wil_skb_get_sa(skb); if (mcast && ether_addr_equal(sa, ndev->dev_addr)) { /* mcast packet looped back to us */
dev_kfree_skb(skb);
ndev->stats.rx_dropped++;
stats->rx_dropped++;
wil_dbg_txrx(wil, "Rx drop %d bytes\n", len); return;
}
} elseif (wdev->iftype == NL80211_IFTYPE_AP && !vif->ap_isolate) { if (mcast) { /* send multicast frames both to higher layers in * local net stack and back to the wireless medium
*/
xmit_skb = skb_copy(skb, GFP_ATOMIC);
} else { int xmit_cid = wil_find_cid(wil, vif->mid, da);
if (xmit_cid >= 0) { /* The destination station is associated to * this AP (in this VLAN), so send the frame * directly to it and do not pass it to local * net stack.
*/
xmit_skb = skb;
skb = NULL;
}
}
} if (xmit_skb) { /* Send to wireless media and increase priority by 256 to * keep the received priority instead of reclassifying * the frame (see cfg80211_classify8021d).
*/
xmit_skb->dev = ndev;
xmit_skb->priority += 256;
xmit_skb->protocol = htons(ETH_P_802_3);
skb_reset_network_header(xmit_skb);
skb_reset_mac_header(xmit_skb);
wil_dbg_txrx(wil, "Rx -> Tx %d bytes\n", len);
dev_queue_xmit(xmit_skb);
}
if (skb) { /* deliver to local stack */
skb->protocol = eth_type_trans(skb, ndev);
skb->dev = ndev;
if (skb->protocol == cpu_to_be16(ETH_P_PAE))
wil_rx_handle_eapol(vif, skb);
if (gro)
napi_gro_receive(&wil->napi_rx, skb); else
netif_rx(skb);
}
ndev->stats.rx_packets++;
stats->rx_packets++;
ndev->stats.rx_bytes += len;
stats->rx_bytes += len; if (mcast)
ndev->stats.multicast++;
}
if (security && (wil->txrx_ops.rx_crypto_check(wil, skb) != 0)) {
wil_dbg_txrx(wil, "Rx drop %d bytes\n", skb->len);
dev_kfree_skb(skb);
ndev->stats.rx_dropped++;
stats->rx_replay++;
stats->rx_dropped++; return;
}
/* check errors reported by HW and update statistics */ if (unlikely(wil->txrx_ops.rx_error_check(wil, skb, stats))) {
dev_kfree_skb(skb); return;
}
wil_netif_rx(skb, ndev, cid, stats, true);
}
/* Proceed all completed skb's from Rx VRING * * Safe to call from NAPI poll, i.e. softirq with interrupts enabled
*/ void wil_rx_handle(struct wil6210_priv *wil, int *quota)
{ struct net_device *ndev = wil->main_ndev; struct wireless_dev *wdev = ndev->ieee80211_ptr; struct wil_ring *v = &wil->ring_rx; struct sk_buff *skb;
if (unlikely(!v->va)) {
wil_err(wil, "Rx IRQ while Rx not yet initialized\n"); return;
}
wil_dbg_txrx(wil, "rx_handle\n"); while ((*quota > 0) && (NULL != (skb = wil_vring_reap_rx(wil, v)))) {
(*quota)--;
/* monitor is currently supported on main interface only */ if (wdev->iftype == NL80211_IFTYPE_MONITOR) {
skb->dev = ndev;
skb_reset_mac_header(skb);
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->pkt_type = PACKET_OTHERHOST;
skb->protocol = htons(ETH_P_802_2);
wil_netif_rx_any(skb, ndev);
} else {
wil_rx_reorder(wil, skb);
}
}
wil_rx_refill(wil, v->size);
}
staticvoid wil_rx_buf_len_init(struct wil6210_priv *wil)
{
wil->rx_buf_len = rx_large_buf ?
WIL_MAX_ETH_MTU : TXRX_BUF_LEN_DEFAULT - WIL_MAX_MPDU_OVERHEAD; if (mtu_max > wil->rx_buf_len) { /* do not allow RX buffers to be smaller than mtu_max, for * backward compatibility (mtu_max parameter was also used * to support receiving large packets)
*/
wil_info(wil, "Override RX buffer to mtu_max(%d)\n", mtu_max);
wil->rx_buf_len = mtu_max;
}
}
wil_dbg_misc(wil, "vring_modify: ring %d cid %d tid %d\n", ring_id,
cid, tid);
lockdep_assert_held(&wil->mutex);
if (!vring->va) {
wil_err(wil, "Tx ring [%d] not allocated\n", ring_id); return -EINVAL;
}
if (wil->ring2cid_tid[ring_id][0] != cid ||
wil->ring2cid_tid[ring_id][1] != tid) {
wil_err(wil, "ring info does not match cid=%u tid=%u\n",
wil->ring2cid_tid[ring_id][0],
wil->ring2cid_tid[ring_id][1]);
}
if (reply.cmd.status != WMI_FW_STATUS_SUCCESS) {
wil_err(wil, "Tx modify failed, status 0x%02x\n",
reply.cmd.status);
rc = -EINVAL; goto fail;
}
/* set BA aggregation window size to 0 to force a new BA with the * new AP
*/
txdata->agg_wsize = 0; if (txdata->dot1x_open && agg_wsize >= 0)
wil_addba_tx_request(wil, ring_id, agg_wsize);
/* In the STA mode, it is expected to have only 1 VRING * for the AP we connected to. * find 1-st vring eligible for this skb and use it.
*/ for (i = min_ring_id; i < WIL6210_MAX_TX_RINGS; i++) {
ring = &wil->ring_tx[i];
txdata = &wil->ring_tx_data[i]; if (!ring->va || !txdata->enabled || txdata->mid != vif->mid) continue;
if (!wil->ring_tx_data[i].dot1x_open &&
skb->protocol != cpu_to_be16(ETH_P_PAE)) continue;
wil_dbg_txrx(wil, "Tx -> ring %d\n", i);
return ring;
}
wil_dbg_txrx(wil, "Tx while no rings active?\n");
return NULL;
}
/* Use one of 2 strategies: * * 1. New (real broadcast): * use dedicated broadcast vring * 2. Old (pseudo-DMS): * Find 1-st vring and return it; * duplicate skb and send it to other active vrings; * in all cases override dest address to unicast peer's address * Use old strategy when new is not supported yet: * - for PBSS
*/ staticstruct wil_ring *wil_find_tx_bcast_1(struct wil6210_priv *wil, struct wil6210_vif *vif, struct sk_buff *skb)
{ struct wil_ring *v; struct wil_ring_tx_data *txdata; int i = vif->bcast_ring;
if (i < 0) return NULL;
v = &wil->ring_tx[i];
txdata = &wil->ring_tx_data[i]; if (!v->va || !txdata->enabled) return NULL; if (!wil->ring_tx_data[i].dot1x_open &&
skb->protocol != cpu_to_be16(ETH_P_PAE)) return NULL;
return v;
}
/* apply multicast to unicast only for ARP and IP packets * (see NL80211_CMD_SET_MULTICAST_TO_UNICAST for more info)
*/ staticbool wil_check_multicast_to_unicast(struct wil6210_priv *wil, struct sk_buff *skb)
{ conststruct ethhdr *eth = (void *)skb->data; conststruct vlan_ethhdr *ethvlan = (void *)skb->data;
__be16 ethertype;
if (!wil->multicast_to_unicast) returnfalse;
/* multicast to unicast conversion only for some payload */
ethertype = eth->h_proto; if (ethertype == htons(ETH_P_8021Q) && skb->len >= VLAN_ETH_HLEN)
ethertype = ethvlan->h_vlan_encapsulated_proto; switch (ethertype) { case htons(ETH_P_ARP): case htons(ETH_P_IP): case htons(ETH_P_IPV6): break; default: returnfalse;
}
returntrue;
}
staticvoid wil_set_da_for_vring(struct wil6210_priv *wil, struct sk_buff *skb, int vring_index)
{
u8 *da = wil_skb_get_da(skb); int cid = wil->ring2cid_tid[vring_index][0];
/* find 1-st vring eligible for data */ for (i = min_ring_id; i < WIL6210_MAX_TX_RINGS; i++) {
v = &wil->ring_tx[i];
txdata = &wil->ring_tx_data[i]; if (!v->va || !txdata->enabled || txdata->mid != vif->mid) continue;
cid = wil->ring2cid_tid[i][0]; if (cid >= wil->max_assoc_sta) /* skip BCAST */ continue; if (!wil->ring_tx_data[i].dot1x_open &&
skb->protocol != cpu_to_be16(ETH_P_PAE)) continue;
/* don't Tx back to source when re-routing Rx->Tx at the AP */ if (0 == memcmp(wil->sta[cid].addr, src, ETH_ALEN)) continue;
goto found;
}
wil_dbg_txrx(wil, "Tx while no vrings active?\n");
return NULL;
found:
wil_dbg_txrx(wil, "BCAST -> ring %d\n", i);
wil_set_da_for_vring(wil, skb, i);
/* find other active vrings and duplicate skb for each */ for (i++; i < WIL6210_MAX_TX_RINGS; i++) {
v2 = &wil->ring_tx[i];
txdata2 = &wil->ring_tx_data[i]; if (!v2->va || txdata2->mid != vif->mid) continue;
cid = wil->ring2cid_tid[i][0]; if (cid >= wil->max_assoc_sta) /* skip BCAST */ continue; if (!wil->ring_tx_data[i].dot1x_open &&
skb->protocol != cpu_to_be16(ETH_P_PAE)) continue;
if (0 == memcmp(wil->sta[cid].addr, src, ETH_ALEN)) continue;
/* Sets the descriptor @d up for csum and/or TSO offloading. The corresponding * @skb is used to obtain the protocol and headers length. * @tso_desc_type is a descriptor type for TSO: 0 - a header, 1 - first data, * 2 - middle, 3 - last descriptor.
*/
staticvoid wil_tx_desc_offload_setup_tso(struct vring_tx_desc *d, struct sk_buff *skb, int tso_desc_type, bool is_ipv4, int tcp_hdr_len, int skb_net_hdr_len)
{
d->dma.b11 = ETH_HLEN; /* MAC header length */
d->dma.b11 |= is_ipv4 << DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS;
/* Sets the descriptor @d up for csum. The corresponding * @skb is used to obtain the protocol and headers length. * Returns the protocol: 0 - not TCP, 1 - TCPv4, 2 - TCPv6. * Note, if d==NULL, the function only returns the protocol result. * * It is very similar to previous wil_tx_desc_offload_setup_tso. This * is "if unrolling" to optimize the critical path.
*/
staticint wil_tx_desc_offload_setup(struct vring_tx_desc *d, struct sk_buff *skb){ int protocol;
int descs_used = 0; /* total number of used descriptors */ int sg_desc_cnt = 0; /* number of descriptors for current mss*/
u32 swhead = vring->swhead; int used, avail = wil_ring_avail_tx(vring); int nr_frags = skb_shinfo(skb)->nr_frags; int min_desc_required = nr_frags + 1; int mss = skb_shinfo(skb)->gso_size; /* payload size w/o headers */ int f, len, hdrlen, headlen; int vring_index = vring - wil->ring_tx; struct wil_ring_tx_data *txdata = &wil->ring_tx_data[vring_index];
uint i = swhead;
dma_addr_t pa; const skb_frag_t *frag = NULL; int rem_data = mss; int lenmss; int hdr_compensation_need = true; int desc_tso_type = wil_tso_type_first; bool is_ipv4; int tcp_hdr_len; int skb_net_hdr_len; int gso_type; int rc = -EINVAL;
wil_dbg_txrx(wil, "tx_vring_tso: %d bytes to vring %d\n", skb->len,
vring_index);
if (unlikely(!txdata->enabled)) return -EINVAL;
/* A typical page 4K is 3-4 payloads, we assume each fragment * is a full payload, that's how min_desc_required has been * calculated. In real we might need more or less descriptors, * this is the initial check only.
*/ if (unlikely(avail < min_desc_required)) {
wil_err_ratelimited(wil, "TSO: Tx ring[%2d] full. No space for %d fragments\n",
vring_index, min_desc_required); return -ENOMEM;
}
/* Header Length = MAC header len + IP header len + TCP header len*/
hdrlen = skb_tcp_all_headers(skb);
gso_type = skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV6 | SKB_GSO_TCPV4); switch (gso_type) { case SKB_GSO_TCPV4: /* TCP v4, zero out the IP length and IPv4 checksum fields * as required by the offloading doc
*/
ip_hdr(skb)->tot_len = 0;
ip_hdr(skb)->check = 0;
is_ipv4 = true; break; case SKB_GSO_TCPV6: /* TCP v6, zero out the payload length */
ipv6_hdr(skb)->payload_len = 0;
is_ipv4 = false; break; default: /* other than TCPv4 or TCPv6 types are not supported for TSO. * It is also illegal for both to be set simultaneously
*/ return -EINVAL;
}
if (skb->ip_summed != CHECKSUM_PARTIAL) return -EINVAL;
/* tcp header length and skb network header length are fixed for all * packet's descriptors - read then once here
*/
tcp_hdr_len = tcp_hdrlen(skb);
skb_net_hdr_len = skb_network_header_len(skb);
_hdr_desc = &vring->va[i].tx.legacy;
pa = dma_map_single(dev, skb->data, hdrlen, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(dev, pa))) {
wil_err(wil, "TSO: Skb head DMA map error\n"); goto err_exit;
}
/* first descriptor may also be the last. * in this case d pointer is invalid
*/ if (_first_desc == _desc)
d = first_desc;
/* Last data descriptor */
wil_set_tx_desc_last_tso(d);
*_desc = *d;
/* Fill the total number of descriptors in first desc (hdr)*/
wil_tx_desc_set_nr_frags(hdr_desc, descs_used);
*_hdr_desc = *hdr_desc;
/* hold reference to skb * to prevent skb release before accounting * in case of immediate "tx done"
*/
vring->ctx[i].skb = skb_get(skb);
/* performance monitoring */
used = wil_ring_used_tx(vring); if (wil_val_in_range(wil->ring_idle_trsh,
used, used + descs_used)) {
txdata->idle += get_cycles() - txdata->last_idle;
wil_dbg_txrx(wil, "Ring[%2d] not idle %d -> %d\n",
vring_index, used, used + descs_used);
}
/* Make sure to advance the head only after descriptor update is done. * This will prevent a race condition where the completion thread * will see the DU bit set from previous run and will handle the * skb before it was completed.
*/
wmb();
/* middle segments */ for (; f < nr_frags; f++) { const skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; int len = skb_frag_size(frag);
*_d = *d;
wil_dbg_txrx(wil, "Tx[%2d] desc[%4d]\n", ring_index, i);
wil_hex_dump_txrx("TxD ", DUMP_PREFIX_NONE, 32, 4,
(constvoid *)d, sizeof(*d), false);
i = (swhead + f + 1) % ring->size;
_d = &ring->va[i].tx.legacy;
pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag),
DMA_TO_DEVICE); if (unlikely(dma_mapping_error(dev, pa))) {
wil_err(wil, "Tx[%2d] failed to map fragment\n",
ring_index); goto dma_error;
}
ring->ctx[i].mapped_as = wil_mapped_as_page;
wil->txrx_ops.tx_desc_map((union wil_tx_desc *)d,
pa, len, ring_index); /* no need to check return code - * if it succeeded for 1-st descriptor, * it will succeed here too
*/
wil_tx_desc_offload_setup(d, skb);
} /* for the last seg only */
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS);
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_MARK_WB_POS);
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS);
*_d = *d;
wil_dbg_txrx(wil, "Tx[%2d] desc[%4d]\n", ring_index, i);
wil_hex_dump_txrx("TxD ", DUMP_PREFIX_NONE, 32, 4,
(constvoid *)d, sizeof(*d), false);
/* hold reference to skb * to prevent skb release before accounting * in case of immediate "tx done"
*/
ring->ctx[i].skb = skb_get(skb);
/* performance monitoring */
used = wil_ring_used_tx(ring); if (wil_val_in_range(wil->ring_idle_trsh,
used, used + nr_frags + 1)) {
txdata->idle += get_cycles() - txdata->last_idle;
wil_dbg_txrx(wil, "Ring[%2d] not idle %d -> %d\n",
ring_index, used, used + nr_frags + 1);
}
/* Make sure to advance the head only after descriptor update is done. * This will prevent a race condition where the completion thread * will see the DU bit set from previous run and will handle the * skb before it was completed.
*/
wmb();
/* make sure all writes to descriptors (shared memory) are done before * committing them to HW
*/
wmb();
if (wil->tx_latency)
*(ktime_t *)&skb->cb = ktime_get(); else
memset(skb->cb, 0, sizeof(ktime_t));
wil_w(wil, ring->hwtail, ring->swhead);
return 0;
dma_error: /* unmap what we have mapped */
nr_frags = f + 1; /* frags mapped + one for skb head */ for (f = 0; f < nr_frags; f++) { struct wil_ctx *ctx;
/* Check status of tx vrings and stop/wake net queues if needed * It will start/stop net queues of a specific VIF net_device. * * This function does one of two checks: * In case check_stop is true, will check if net queues need to be stopped. If * the conditions for stopping are met, netif_tx_stop_all_queues() is called. * In case check_stop is false, will check if net queues need to be waked. If * the conditions for waking are met, netif_tx_wake_all_queues() is called. * vring is the vring which is currently being modified by either adding * descriptors (tx) into it or removing descriptors (tx complete) from it. Can * be null when irrelevant (e.g. connect/disconnect events). * * The implementation is to stop net queues if modified vring has low * descriptor availability. Wake if all vrings are not in low descriptor * availability and modified vring has high descriptor availability.
*/ staticinlinevoid __wil_update_net_queues(struct wil6210_priv *wil, struct wil6210_vif *vif, struct wil_ring *ring, bool check_stop)
{ int i; int min_ring_id = wil_get_min_tx_ring_id(wil);
wil_dbg_txrx(wil, "start_xmit\n"); if (unlikely(!test_bit(wil_status_fwready, wil->status))) { if (!pr_once_fw) {
wil_err(wil, "FW not ready\n");
pr_once_fw = true;
} goto drop;
} if (unlikely(!test_bit(wil_vif_fwconnected, vif->status))) {
wil_dbg_ratelimited(wil, "VIF not connected, packet dropped\n"); goto drop;
} if (unlikely(vif->wdev.iftype == NL80211_IFTYPE_MONITOR)) {
wil_err(wil, "Xmit in monitor mode not supported\n"); goto drop;
}
pr_once_fw = false;
/* find vring */ if (vif->wdev.iftype == NL80211_IFTYPE_STATION && !vif->pbss) { /* in STA mode (ESS), all to same VRING (to AP) */
ring = wil_find_tx_ring_sta(wil, vif, skb);
} elseif (bcast) { if (vif->pbss || wil_check_multicast_to_unicast(wil, skb)) /* in pbss, no bcast VRING - duplicate skb in * all stations VRINGs
*/
ring = wil_find_tx_bcast_2(wil, vif, skb); elseif (vif->wdev.iftype == NL80211_IFTYPE_AP) /* AP has a dedicated bcast VRING */
ring = wil_find_tx_bcast_1(wil, vif, skb); else /* unexpected combination, fallback to duplicating * the skb in all stations VRINGs
*/
ring = wil_find_tx_bcast_2(wil, vif, skb);
} else { /* unicast, find specific VRING by dest. address */
ring = wil_find_tx_ucast(wil, vif, skb);
} if (unlikely(!ring)) {
wil_dbg_txrx(wil, "No Tx RING found for %pM\n", da); goto drop;
} /* set up vring entry */
rc = wil_tx_ring(wil, vif, ring, skb);
switch (rc) { case 0: /* shall we stop net queues? */
wil_update_net_queues_bh(wil, vif, ring, true); /* statistics will be updated on the tx_complete */
dev_kfree_skb_any(skb); return NETDEV_TX_OK; case -ENOMEM: if (drop_if_ring_full) goto drop; return NETDEV_TX_BUSY; default: break; /* goto drop; */
}
drop:
ndev->stats.tx_dropped++;
dev_kfree_skb_any(skb);
return NET_XMIT_DROP;
}
void wil_tx_latency_calc(struct wil6210_priv *wil, struct sk_buff *skb, struct wil_sta_info *sta)
{ int skb_time_us; int bin;
if (!wil->tx_latency) return;
if (ktime_to_ms(*(ktime_t *)&skb->cb) == 0) return;
skb_time_us = ktime_us_delta(ktime_get(), *(ktime_t *)&skb->cb);
bin = skb_time_us / wil->tx_latency_res;
bin = min_t(int, bin, WIL_NUM_LATENCY_BINS - 1);
wil_dbg_txrx(wil, "skb time %dus => bin %d\n", skb_time_us, bin);
sta->tx_latency_bins[bin]++;
sta->stats.tx_latency_total_us += skb_time_us; if (skb_time_us < sta->stats.tx_latency_min_us)
sta->stats.tx_latency_min_us = skb_time_us; if (skb_time_us > sta->stats.tx_latency_max_us)
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet)
¤
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.