/* 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 *Thisisgrantedbythedma_alloc_coherent. * *HWhaslimitationthatallvringsaddressesmustsharethesame *upper16msbbitspartof48bitsaddress.Toworkaroundthat, *ifweareusingmorethan32bitaddressesswitchto32bit *allocationbeforeallocatingvringmemory. * *There'snocheckforthereturnvalueofdma_set_mask_and_coherent, *sinceweassumeifwewereabletosetthemaskduring *initializationinthissystemitwillnotfailifwesetitagain
*/ 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 *ForTxandRx,ownershipbitisatthesamelocation,thus *wecanuseany
*/ 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. *Incaseofcid>=8,thevaluewouldbecidmodulo8andweneedto *findrealcidbylocatingthetransmitter(ta)insidestaarray
*/ 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. *multistaisusedonlyinP2P_GOorAPmode.Inothermodesreturn *cidfromtherxdescriptor
*/ 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), *tofindtherealcid,comparetransmitteraddresswiththestored *stationsmacaddressinthedriverstaarray
*/ 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) *Drivershouldrecognizeitbyframetype,thatisfound *inRxdescriptor.Iftypeisnotdata,itis802.11frameasis
*/
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 *withoutoverheadforprintingeveryRxframe
*/
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 *andincaseoferrordropthepacket *higherstacklayerswillhandleretransmission(ifrequired)
*/ 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 *Forexample,HWdon'tunderstandMicrosoftIPstackthat *mis-calculatesTCPchecksum-ifitshouldbe0x0, *itwrites0xffffinviolationofRFC1624
*/ else
stats->rx_csum_err++;
}
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 *newAP
*/
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 *fortheAPweconnectedto. *find1-stvringeligibleforthisskbanduseit.
*/ 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(realbroadcast): *usededicatedbroadcastvring *2.Old(pseudo-DMS): *Find1-stvringandreturnit; *duplicateskbandsendittootheractivevrings; *inallcasesoverridedestaddresstounicastpeer'saddress *Useoldstrategywhennewisnotsupportedyet: *-forPBSS
*/ 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 *(seeNL80211_CMD_SET_MULTICAST_TO_UNICASTformoreinfo)
*/ 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 *@skbisusedtoobtaintheprotocolandheaderslength. *@tso_desc_typeisadescriptortypeforTSO:0-aheader,1-firstdata, *2-middle,3-lastdescriptor.
*/
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 *@skbisusedtoobtaintheprotocolandheaderslength. *Returnstheprotocol:0-notTCP,1-TCPv4,2-TCPv6. *Note,ifd==NULL,thefunctiononlyreturnstheprotocolresult. * *Itisverysimilartopreviouswil_tx_desc_offload_setup_tso.This *is"ifunrolling"tooptimizethecriticalpath.
*/
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 *isafullpayload,that'showmin_desc_requiredhasbeen *calculated.Inrealwemightneedmoreorlessdescriptors, *thisistheinitialcheckonly.
*/ 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 *asrequiredbytheoffloadingdoc
*/
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. *Itisalsoillegalforbothtobesetsimultaneously
*/ return -EINVAL;
}
if (skb->ip_summed != CHECKSUM_PARTIAL) return -EINVAL;
/* tcp header length and skb network header length are fixed for all *packet'sdescriptors-readthenoncehere
*/
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. *inthiscasedpointerisinvalid
*/ 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 *topreventskbreleasebeforeaccounting *incaseofimmediate"txdone"
*/
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. *Thiswillpreventaraceconditionwherethecompletionthread *willseetheDUbitsetfrompreviousrunandwillhandlethe *skbbeforeitwascompleted.
*/
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 - *ifitsucceededfor1-stdescriptor, *itwillsucceedheretoo
*/
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 *topreventskbreleasebeforeaccounting *incaseofimmediate"txdone"
*/
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. *Thiswillpreventaraceconditionwherethecompletionthread *willseetheDUbitsetfrompreviousrunandwillhandlethe *skbbeforeitwascompleted.
*/
wmb();
/* make sure all writes to descriptors (shared memory) are done before *committingthemtoHW
*/
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);
return0;
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;
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 *allstationsVRINGs
*/
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 *theskbinallstationsVRINGs
*/
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) { case0: /* 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)
sta->stats.tx_latency_max_us = skb_time_us;
}
/* Clean up transmitted skb's from the Tx VRING * *Returnnumberofdescriptorscleared * *SafetocallfromIRQ
*/ int wil_tx_complete(struct wil6210_vif *vif, int ringid)
{ struct wil6210_priv *wil = vif_to_wil(vif); struct net_device *ndev = vif_to_ndev(vif); struct device *dev = wil_to_dev(wil); struct wil_ring *vring = &wil->ring_tx[ringid]; struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ringid]; int done = 0; int cid = wil->ring2cid_tid[ringid][0]; struct wil_net_stats *stats = NULL; volatilestruct vring_tx_desc *_d; int used_before_complete; int used_new;
if (unlikely(!vring->va)) {
wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid); return0;
}
if (cid < wil->max_assoc_sta)
stats = &wil->sta[cid].stats;
while (!wil_ring_is_empty(vring)) { int new_swtail; struct wil_ctx *ctx = &vring->ctx[vring->swtail]; /* For the fragmented skb, HW will set DU bit only for the *lastfragment.lookforit. *InTSOthefirstDUwillincludehdrdesc
*/ int lf = (vring->swtail + ctx->nr_frags) % vring->size; /* TODO: check we are not past head */
_d = &vring->va[lf].tx.legacy; if (unlikely(!(_d->dma.status & TX_DMA_STATUS_DU))) break;
if (skb->protocol == cpu_to_be16(ETH_P_PAE))
wil_tx_complete_handle_eapol(vif, skb);
wil_consume_skb(skb, d->dma.error == 0);
}
memset(ctx, 0, sizeof(*ctx)); /* Make sure the ctx is zeroed before updating the tail *topreventacasewherewil_tx_ringwillsee *thisdescriptorasusedandhandleitbeforectxzero *iscompleted.
*/
wmb(); /* There is no need to touch HW descriptor: *-ststusbitTX_DMA_STATUS_DUissetbydesign, *sohardwarewillnottrytoprocessthisdesc., *-restofdescriptorwillbeinitializedonTx.
*/
vring->swtail = wil_ring_next_tail(vring);
done++;
}
}
staticvoid wil_get_reorder_params(struct wil6210_priv *wil, struct sk_buff *skb, int *tid, int *cid, int *mid, u16 *seq, int *mcast, int *retry)
{ struct vring_rx_desc *d = wil_skb_rxdesc(skb);
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.