/* CREATE_TRACE_POINTS only needs to be defined once. Other dpaa files *usingtraceeventsonlyneedto#include<trace/events/sched.h>
*/ #define CREATE_TRACE_POINTS #include"dpaa_eth_trace.h"
#define DPAA_CS_THRESHOLD_10G 0x10000000 /* The size in bytes of the egress Congestion State notification threshold on *10Gports,range0x1000..0x10000000
*/
/* Largest value that the FQD's OAL field can hold */ #define FSL_QMAN_MAX_OAL 127
/* Default alignment for start of data in an Rx FD */ #ifdef CONFIG_DPAA_ERRATUM_A050385 /* aligning data start to 64 avoids DMA transaction splits, unless the buffer *iscrossinga4kpageboundary
*/ #define DPAA_FD_DATA_ALIGNMENT (fman_has_errata_a050385() ? 64 : 16) /* aligning to 256 avoids DMA transaction splits caused by 4k page boundary *crossings;also,allSGfragmentsexceptthelastmusthaveasizemultiple *of256toavoidDMAtransactionsplits
*/ #define DPAA_A050385_ALIGN 256 #define DPAA_FD_RX_DATA_ALIGNMENT (fman_has_errata_a050385() ? \
DPAA_A050385_ALIGN : 16) #else #define DPAA_FD_DATA_ALIGNMENT 16 #define DPAA_FD_RX_DATA_ALIGNMENT DPAA_FD_DATA_ALIGNMENT #endif
/* The DPAA requires 256 bytes reserved and mapped for the SGT */ #define DPAA_SGT_SIZE 256
/* Values for the L3R field of the FM Parse Results
*/ /* L3 Type field: First IP Present IPv4 */ #define FM_L3_PARSE_RESULT_IPV4 0x8000 /* L3 Type field: First IP Present IPv6 */ #define FM_L3_PARSE_RESULT_IPV6 0x4000 /* Values for the L4R field of the FM Parse Results */ /* L4 Type field: UDP */ #define FM_L4_PARSE_RESULT_UDP 0x40 /* L4 Type field: TCP */ #define FM_L4_PARSE_RESULT_TCP 0x20
/* FD status field indicating whether the FM Parser has attempted to validate *theL4csumoftheframe. *Notethathavingthisbitsetdoesn'tnecessarilyimplythatthechecksum *isvalid.Onewouldhavetochecktheparseresultstofindthatout.
*/ #define FM_FD_STAT_L4CV 0x00000004
#define DPAA_SGT_MAX_ENTRIES 16/* maximum number of entries in SG Table */ #define DPAA_BUFF_RELEASE_MAX 8/* maximum number of buffers released at once */
/* Although we access another CPU's private data here *wedoitatinitializationsoitissafe
*/
for_each_possible_cpu(i) {
percpu_priv = per_cpu_ptr(priv->percpu_priv, i);
percpu_priv->net_dev = net_dev;
}
if (is_valid_ether_addr(mac_addr)) {
memcpy(net_dev->perm_addr, mac_addr, net_dev->addr_len);
eth_hw_addr_set(net_dev, mac_addr);
} else {
eth_hw_addr_random(net_dev);
err = mac_dev->change_addr(mac_dev->fman_mac,
(const enet_addr_t *)net_dev->dev_addr); if (err) {
dev_err(dev, "Failed to set random MAC address\n"); return -EINVAL;
}
dev_info(dev, "Using random MAC address: %pM\n",
net_dev->dev_addr);
}
/* The rest of the config is filled in by the mac device already */
mac_dev->phylink_config.dev = &net_dev->dev;
mac_dev->phylink_config.type = PHYLINK_NETDEV;
mac_dev->update_speed = dpaa_eth_cgr_set_speed;
mac_dev->phylink = phylink_create(&mac_dev->phylink_config,
dev_fwnode(mac_dev->dev),
mac_dev->phy_if,
mac_dev->phylink_ops); if (IS_ERR(mac_dev->phylink)) {
err = PTR_ERR(mac_dev->phylink);
dev_err_probe(dev, err, "Could not create phylink\n"); return err;
}
/* start without the RUNNING flag, phylib controls it later */
netif_carrier_off(net_dev);
/* checks if this bpool is already allocated */ staticbool dpaa_bpid2pool_use(int bpid)
{ if (dpaa_bpid2pool(bpid)) {
refcount_inc(&dpaa_bp_array[bpid]->refs); returntrue;
}
returnfalse;
}
/* called only once per bpid by dpaa_bp_alloc_pool() */ staticvoid dpaa_bpid2pool_map(int bpid, struct dpaa_bp *dpaa_bp)
{
dpaa_bp_array[bpid] = dpaa_bp;
refcount_set(&dpaa_bp->refs, 1);
}
staticint dpaa_bp_alloc_pool(struct dpaa_bp *dpaa_bp)
{ int err;
if (dpaa_bp->size == 0 || dpaa_bp->config_count == 0) {
pr_err("%s: Buffer pool is not properly initialized! Missing size or initial number of buffers\n",
__func__); return -EINVAL;
}
/* If the pool is already specified, we only create one per bpid */ if (dpaa_bp->bpid != FSL_DPAA_BPID_INV &&
dpaa_bpid2pool_use(dpaa_bp->bpid)) return0;
if (dpaa_bp->bpid == FSL_DPAA_BPID_INV) {
dpaa_bp->pool = bman_new_pool(); if (!dpaa_bp->pool) {
pr_err("%s: bman_new_pool() failed\n",
__func__); return -ENODEV;
}
if (dpaa_bp->seed_cb) {
err = dpaa_bp->seed_cb(dpaa_bp); if (err) goto pool_seed_failed;
}
dpaa_bpid2pool_map(dpaa_bp->bpid, dpaa_bp);
return0;
pool_seed_failed:
pr_err("%s: pool seeding failed\n", __func__);
bman_free_pool(dpaa_bp->pool);
return err;
}
/* remove and free all the buffers from the given buffer pool */ staticvoid dpaa_bp_drain(struct dpaa_bp *bp)
{
u8 num = 8; int ret;
do { struct bm_buffer bmb[8]; int i;
ret = bman_acquire(bp->pool, bmb, num); if (ret < 0) { if (num == 8) { /* we have less than 8 buffers left; *drainthemonebyone
*/
num = 1;
ret = 1; continue;
} else { /* Pool is fully drained */ break;
}
}
if (bp->free_buf_cb) for (i = 0; i < num; i++)
bp->free_buf_cb(bp, &bmb[i]);
} while (ret > 0);
}
/* the mapping between bpid and dpaa_bp is done very late in the *allocationprocedure;ifsomethingfailedbeforethemapping,thebp *wasnotconfigured,thereforewedon'tneedthebelowinstructions
*/ if (!bp) return;
/* the PCD FQIDs range needs to be aligned for correct operation */ if (qman_alloc_fqid_range(&fq_base, 2 * DPAA_ETH_PCD_RXQ_NUM)) goto fq_alloc_failed;
/* Set different thresholds based on the configured MAC speed. *ThismayturnsuboptimaliftheMACisreconfiguredatanother *speed,soMACsmustcalldpaa_eth_cgr_set_speedintheirlink_up *callback.
*/ if (priv->mac_dev->phylink_config.mac_capabilities & MAC_10000FD)
cs_th = DPAA_CS_THRESHOLD_10G; else
cs_th = DPAA_CS_THRESHOLD_1G;
qm_cgr_cs_thres_set64(&initcgr.cgr.cs_thres, cs_th, 1);
if (dpaa_fq->init) {
memset(&initfq, 0, sizeof(initfq));
initfq.we_mask = cpu_to_be16(QM_INITFQ_WE_FQCTRL); /* Note: we may get to keep an empty FQ in cache */
initfq.fqd.fq_ctrl = cpu_to_be16(QM_FQCTRL_PREFERINCACHE);
/* Try to reduce the number of portal interrupts for *TxConfirmationFQs.
*/ if (dpaa_fq->fq_type == FQ_TYPE_TX_CONFIRM)
initfq.fqd.fq_ctrl |= cpu_to_be16(QM_FQCTRL_AVOIDBLOCK);
staticint dpaa_bman_release(conststruct dpaa_bp *dpaa_bp, struct bm_buffer *bmb, int cnt)
{ int err;
err = bman_release(dpaa_bp->pool, bmb, cnt); /* Should never occur, address anyway to avoid leaking the buffers */ if (WARN_ON(err) && dpaa_bp->free_buf_cb) while (cnt-- > 0)
dpaa_bp->free_buf_cb(dpaa_bp, &bmb[cnt]);
return cnt;
}
staticvoid dpaa_release_sgt_members(struct qm_sg_entry *sgt)
{ struct bm_buffer bmb[DPAA_BUFF_RELEASE_MAX]; struct dpaa_bp *dpaa_bp; int i = 0, j;
memset(bmb, 0, sizeof(bmb));
do {
dpaa_bp = dpaa_bpid2pool(sgt[i].bpid); if (!dpaa_bp) return;
/* Note: L3 csum seems to be already computed in sw, but we can't choose *L4alonefromtheFMconfigurationanyway.
*/
/* Fill in some fields of the Parse Results array, so the FMan *canfindthemasiftheycamefromtheFManParser.
*/
parse_result = (struct fman_prs_result *)parse_results;
/* If we're dealing with VLAN, get the real Ethernet type */ if (ethertype == ETH_P_8021Q)
ethertype = ntohs(skb_vlan_eth_hdr(skb)->h_vlan_encapsulated_proto);
/* Fill in the relevant L3 parse result fields *andreadtheL4protocoltype
*/ switch (ethertype) { case ETH_P_IP:
parse_result->l3r = cpu_to_be16(FM_L3_PARSE_RESULT_IPV4);
iph = ip_hdr(skb);
WARN_ON(!iph);
l4_proto = iph->protocol; break; case ETH_P_IPV6:
parse_result->l3r = cpu_to_be16(FM_L3_PARSE_RESULT_IPV6);
ipv6h = ipv6_hdr(skb);
WARN_ON(!ipv6h);
l4_proto = ipv6h->nexthdr; break; default: /* We shouldn't even be here */ if (net_ratelimit())
netif_alert(priv, tx_err, priv->net_dev, "Can't compute HW csum for L3 proto 0x%x\n",
ntohs(skb->protocol));
retval = -EIO; goto return_error;
}
/* Fill in the relevant L4 parse result fields */ switch (l4_proto) { case IPPROTO_UDP:
parse_result->l4r = FM_L4_PARSE_RESULT_UDP; break; case IPPROTO_TCP:
parse_result->l4r = FM_L4_PARSE_RESULT_TCP; break; default: if (net_ratelimit())
netif_alert(priv, tx_err, priv->net_dev, "Can't compute HW csum for L4 proto 0x%x\n",
l4_proto);
retval = -EIO; goto return_error;
}
/* At index 0 is IPOffset_1 as defined in the Parse Results */
parse_result->ip_off[0] = (u8)skb_network_offset(skb);
parse_result->l4_off = (u8)skb_transport_offset(skb);
/* Enable L3 (and L4, if TCP or UDP) HW checksum. */
fd->cmd |= cpu_to_be32(FM_FD_CMD_RPD | FM_FD_CMD_DTC);
/* On P1023 and similar platforms fd->cmd interpretation could *bedisabledbysettingCONTEXT_AbitICMD;currentlythisbit *isnotsetsowedonotneedtocheck;inthefuture,if/when *usingcontext_aweneedtocheckthisbit
*/
for (i = 0; i < 8; i++) {
p = dev_alloc_pages(0); if (unlikely(!p)) {
netdev_err(net_dev, "dev_alloc_pages() failed\n"); goto release_previous_buffs;
}
/* The sgt buffer has been allocated with netdev_alloc_frag(), *it'sfromlowmem.
*/
sgt = vaddr + qm_fd_get_offset(fd);
/* sgt[0] is from lowmem, was dma_map_single()-ed */
dma_unmap_single(priv->tx_dma_dev, qm_sg_addr(&sgt[0]),
qm_sg_entry_get_len(&sgt[0]), dma_dir);
/* remaining pages were mapped with skb_frag_dma_map() */ for (i = 1; (i < DPAA_SGT_MAX_ENTRIES) &&
!qm_sg_entry_is_final(&sgt[i - 1]); i++) {
WARN_ON(qm_sg_entry_is_ext(&sgt[i]));
/* No skb backpointer is set when running XDP. An xdp_frame *backpointerissavedinstead.
*/ if (!skb) {
xdp_return_frame(swbp->xdpf); return NULL;
}
/* DMA unmapping is required before accessing the HW provided info */ if (ts && priv->tx_tstamp &&
skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {
memset(&shhwtstamps, 0, sizeof(shhwtstamps));
if (qm_fd_get_format(fd) == qm_fd_sg) /* Free the page that we allocated on Tx for the SGT */
free_pages((unsignedlong)vaddr, 0);
return skb;
}
static u8 rx_csum_offload(conststruct dpaa_priv *priv, conststruct qm_fd *fd)
{ /* The parser has run and performed L4 checksum validation. *Weknowtherewerenoparsererrors(andimplicitlyno *L4csumerror),otherwisewewouldn'tbehere.
*/ if ((priv->net_dev->features & NETIF_F_RXCSUM) &&
(be32_to_cpu(fd->status) & FM_FD_STAT_L4CV)) return CHECKSUM_UNNECESSARY;
/* We're here because either the parser didn't run or the L4 checksum *wasnotverified.ThismayincludethecaseofaUDPframewith *checksumzerooranL4protootherthanTCP/UDP
*/ return CHECKSUM_NONE;
}
#define PTR_IS_ALIGNED(x, a) (IS_ALIGNED((unsignedlong)(x), (a)))
/* Build a linear skb around the received buffer. *Weareguaranteedthereisenoughroomattheendofthedatabufferto *accommodatethesharedinfoareaoftheskb.
*/ staticstruct sk_buff *contig_fd_to_skb(conststruct dpaa_priv *priv, conststruct qm_fd *fd)
{
ssize_t fd_off = qm_fd_get_offset(fd);
dma_addr_t addr = qm_fd_addr(fd); struct dpaa_bp *dpaa_bp; struct sk_buff *skb; void *vaddr;
/* Build an skb with the data of the first S/G entry in the linear portion and *therestoftheframeasskbfragments. * *ThepagefragmentholdingtheS/GTableisrecycledhere.
*/ staticstruct sk_buff *sg_fd_to_skb(conststruct dpaa_priv *priv, conststruct qm_fd *fd)
{
ssize_t fd_off = qm_fd_get_offset(fd);
dma_addr_t addr = qm_fd_addr(fd); conststruct qm_sg_entry *sgt; struct page *page, *head_page; struct dpaa_bp *dpaa_bp; void *vaddr, *sg_vaddr; struct sk_buff *skb;
dma_addr_t sg_addr; int page_offset; unsignedint sz; int *count_ptr; int i, j;
/* Iterate through the SGT entries and add data buffers to the skb */
sgt = vaddr + fd_off;
skb = NULL; for (i = 0; i < DPAA_SGT_MAX_ENTRIES; i++) { /* Extension bit is not supported */
WARN_ON(qm_sg_entry_is_ext(&sgt[i]));
/* We may use multiple Rx pools */
dpaa_bp = dpaa_bpid2pool(sgt[i].bpid); if (!dpaa_bp) goto free_buffers;
if (!skb) {
sz = dpaa_bp->size +
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
skb = build_skb(sg_vaddr, sz); if (WARN_ON(!skb)) goto free_buffers;
skb->ip_summed = rx_csum_offload(priv, fd);
/* Make sure forwarded skbs will have enough space *onTx,ifextraheadersareadded.
*/
WARN_ON(fd_off != priv->rx_headroom); /* The offset to data start within the buffer holding *theSGTshouldalwaysbeequaltotheoffsettodata *startwithinthefirstbufferholdingtheframe.
*/
WARN_ON_ONCE(fd_off != qm_sg_entry_get_off(&sgt[i]));
skb_reserve(skb, fd_off);
skb_put(skb, qm_sg_entry_get_len(&sgt[i]));
} else { /* Not the first S/G entry; all data from buffer will *beaddedinanskbfragment;fragmentindexisoffset *byonesincefirstS/Gentrywasincorporatedinthe *linearpartoftheskb. * *Caution:'page'maybeatailpage.
*/
page = virt_to_page(sg_vaddr);
head_page = virt_to_head_page(sg_vaddr);
/* Non-initial SGT entries should not have a buffer *offset.
*/
WARN_ON_ONCE(qm_sg_entry_get_off(&sgt[i]));
/* skb_add_rx_frag() does no checking on the page; if *wepassitatailpage,we'llendupwith *badpageaccountingandeventuallywithsegfaults.
*/
skb_add_rx_frag(skb, i - 1, head_page, page_offset,
qm_sg_entry_get_len(&sgt[i]),
dpaa_bp->size);
}
/* Update the pool count for the current {cpu x bpool} */
count_ptr = this_cpu_ptr(dpaa_bp->percpu_count);
(*count_ptr)--;
if (qm_sg_entry_is_final(&sgt[i])) break;
}
WARN_ONCE(i == DPAA_SGT_MAX_ENTRIES, "No final bit on SGT\n");
/* free the SG table buffer */
free_pages((unsignedlong)vaddr, 0);
return skb;
free_buffers: /* free all the SG entries */ for (j = 0; j < DPAA_SGT_MAX_ENTRIES ; j++) {
sg_addr = qm_sg_addr(&sgt[j]);
sg_vaddr = phys_to_virt(sg_addr); /* all pages 0..i were unmaped */ if (j > i)
dma_unmap_page(priv->rx_dma_dev, qm_sg_addr(&sgt[j]),
DPAA_BP_RAW_SIZE, DMA_FROM_DEVICE);
free_pages((unsignedlong)sg_vaddr, 0); /* counters 0..i-1 were decremented */ if (j >= i) {
dpaa_bp = dpaa_bpid2pool(sgt[j].bpid); if (dpaa_bp) {
count_ptr = this_cpu_ptr(dpaa_bp->percpu_count);
(*count_ptr)--;
}
}
if (qm_sg_entry_is_final(&sgt[j])) break;
} /* free the SGT fragment */
free_pages((unsignedlong)vaddr, 0);
/* We are guaranteed to have at least tx_headroom bytes *available,sojustusethatforoffset.
*/
fd->bpid = FSL_DPAA_BPID_INV;
buff_start = skb->data - priv->tx_headroom;
dma_dir = DMA_TO_DEVICE;
/* Fill in the rest of the FD fields */
qm_fd_set_contig(fd, priv->tx_headroom, skb->len);
fd->cmd |= cpu_to_be32(FM_FD_CMD_FCO);
/* Map the entire buffer size that may be seen by FMan, but no more */
addr = dma_map_single(priv->tx_dma_dev, buff_start,
priv->tx_headroom + skb->len, dma_dir); if (unlikely(dma_mapping_error(priv->tx_dma_dev, addr))) { if (net_ratelimit())
netif_err(priv, tx_err, net_dev, "dma_map_single() failed\n"); return -EINVAL;
}
qm_fd_addr_set64(fd, addr);
/* get a page to store the SGTable */
p = dev_alloc_pages(0); if (unlikely(!p)) {
netdev_err(net_dev, "dev_alloc_pages() failed\n"); return -ENOMEM;
}
buff_start = page_address(p);
/* check linear buffer alignment */ if (!PTR_IS_ALIGNED(skb->data, DPAA_A050385_ALIGN)) goto workaround;
/* linear buffers just need to have an aligned start */ if (!skb_is_nonlinear(skb)) return0;
/* linear data size for nonlinear skbs needs to be aligned */ if (!IS_ALIGNED(skb_headlen(skb), DPAA_A050385_ALIGN)) goto workaround;
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
/* all fragments need to have aligned start addresses */ if (!IS_ALIGNED(skb_frag_off(frag), DPAA_A050385_ALIGN)) goto workaround;
/* all but last fragment need to have aligned sizes */ if (!IS_ALIGNED(skb_frag_size(frag), DPAA_A050385_ALIGN) &&
(i < skb_shinfo(skb)->nr_frags - 1)) goto workaround;
}
return0;
workaround: /* copy all the skb content into a new linear buffer */
new_skb = netdev_alloc_skb(net_dev, skb->len + DPAA_A050385_ALIGN - 1 +
priv->tx_headroom); if (!new_skb) return -ENOMEM;
/* NET_SKB_PAD bytes already reserved, adding up to tx_headroom */
skb_reserve(new_skb, priv->tx_headroom - NET_SKB_PAD);
/* Workaround for DPAA_A050385 requires data start to be aligned */
start = PTR_ALIGN(new_skb->data, DPAA_A050385_ALIGN); if (start - new_skb->data)
skb_reserve(new_skb, start - new_skb->data);
/* Copy relevant timestamp info from the old skb to the new */ if (priv->tx_tstamp) {
skb_shinfo(new_skb)->tx_flags = skb_shinfo(skb)->tx_flags;
skb_shinfo(new_skb)->hwtstamps = skb_shinfo(skb)->hwtstamps;
skb_shinfo(new_skb)->tskey = skb_shinfo(skb)->tskey; if (skb->sk)
skb_set_owner_w(new_skb, skb->sk);
}
/* We move the headroom when we align it so we have to reset the *networkandtransportheaderoffsetsrelativetothenewdata *pointer.Thechecksumoffloadreliesontheseoffsets.
*/
skb_set_network_header(new_skb, skb_network_offset(skb));
skb_set_transport_header(new_skb, skb_transport_offset(skb));
/* Check the data alignment and make sure the headroom is large *enoughtostorethexdpfbackpointer.Useanalignedheadroom *value. * *Duetoalignmentconstraints,wegiveXDPaccesstothefull256 *byteframeheadroom.IftheXDPprogramusesallofit,copythe *datatoanewbufferandmakeroomforstoringthebackpointer.
*/ if (PTR_IS_ALIGNED(xdpf->data, DPAA_FD_DATA_ALIGNMENT) &&
xdpf->headroom >= priv->tx_headroom) {
xdpf->headroom = priv->tx_headroom; return0;
}
/* Try to move the data inside the buffer just enough to align it and *storethexdpfbackpointer.Iftheavailableheadroomisn'tlarge *enough,resorttoallocatinganewbufferandcopyingthedata.
*/
aligned_data = PTR_ALIGN_DOWN(xdpf->data, DPAA_FD_DATA_ALIGNMENT);
data_shift = xdpf->data - aligned_data;
/* The XDP frame's headroom needs to be large enough to accommodate *shiftingthedataaswellasstoringthexdpfbackpointer.
*/ if (xdpf->headroom >= data_shift + priv->tx_headroom) {
memmove(aligned_data, xdpf->data, xdpf->len);
xdpf->data = aligned_data;
xdpf->headroom = priv->tx_headroom; return0;
}
/* The new xdp_frame is stored in the new buffer. Reserve enough space *intheheadroomforstoringitalongwiththedriver'sprivate *info.TheheadroomneedstobealignedtoDPAA_FD_DATA_ALIGNMENTto *guaranteethedata'salignmentinthebuffer.
*/
headroom = ALIGN(sizeof(*new_xdpf) + priv->tx_headroom,
DPAA_FD_DATA_ALIGNMENT);
/* Assure the extended headroom and data don't overflow the buffer, *whilemaintainingthemandatorytailroom.
*/ if (headroom + xdpf->len > DPAA_BP_RAW_SIZE -
SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) return -ENOMEM;
p = dev_alloc_pages(0); if (unlikely(!p)) return -ENOMEM;
/* Copy the data to the new buffer at a properly aligned offset */
new_buff = page_address(p);
memcpy(new_buff + headroom, xdpf->data, xdpf->len);
/* Create an XDP frame around the new buffer in a similar fashion *toxdp_convert_buff_to_frame.
*/
new_xdpf = new_buff;
new_xdpf->data = new_buff + headroom;
new_xdpf->len = xdpf->len;
new_xdpf->headroom = priv->tx_headroom;
new_xdpf->frame_sz = DPAA_BP_RAW_SIZE;
new_xdpf->mem_type = MEM_TYPE_PAGE_ORDER0;
/* Release the initial buffer */
xdp_return_frame_rx_napi(xdpf);
/* Packet data is always read as 32-bit words, so zero out any part of *theskbwhichmightbesentifwehavetopadthepacket
*/ if (__skb_put_padto(skb, ETH_ZLEN, false)) goto enomem;
nonlinear = skb_is_nonlinear(skb); if (!nonlinear) { /* We're going to store the skb backpointer at the beginning *ofthedatabuffer,soweneedaprivatelyownedskb * *We'vemadesureskbisnotsharedindev->priv_flags, *weneedtoverifytheskbheadisnotcloned
*/ if (skb_cow_head(skb, priv->tx_headroom)) goto enomem;
WARN_ON(skb_is_nonlinear(skb));
}
/* MAX_SKB_FRAGS is equal or larger than our dpaa_SGT_MAX_ENTRIES; *makesurewedon'tfeedFManwithmorefragmentsthanitsupports.
*/ if (unlikely(nonlinear &&
(skb_shinfo(skb)->nr_frags >= DPAA_SGT_MAX_ENTRIES))) { /* If the egress skb contains more fragments than we support *wehavenochoicebuttolinearizeitourselves.
*/ if (__skb_linearize(skb)) goto enomem;
nonlinear = skb_is_nonlinear(skb);
}
#ifdef CONFIG_DPAA_ERRATUM_A050385 if (unlikely(fman_has_errata_a050385())) { if (dpaa_a050385_wa_skb(net_dev, &skb)) goto enomem;
nonlinear = skb_is_nonlinear(skb);
} #endif
if (nonlinear) { /* Just create a S/G fd based on the skb */
err = skb_to_sg_fd(priv, skb, &fd);
percpu_priv->tx_frag_skbuffs++;
} else { /* Create a contig FD from this skb */
err = skb_to_contig_fd(priv, skb, &fd, &offset);
} if (unlikely(err < 0)) goto skb_to_fd_failed;
if (be32_to_cpu(fd->status) & FM_FD_ERR_DMA)
percpu_priv->rx_errors.dme++; if (be32_to_cpu(fd->status) & FM_FD_ERR_PHYSICAL)
percpu_priv->rx_errors.fpe++; if (be32_to_cpu(fd->status) & FM_FD_ERR_SIZE)
percpu_priv->rx_errors.fse++; if (be32_to_cpu(fd->status) & FM_FD_ERR_PRS_HDR_ERR)
percpu_priv->rx_errors.phe++;
/* Leave empty the skb backpointer at the start of the buffer. *SavetheXDPframeforeasycleanuponconfirmation.
*/
swbp = (struct dpaa_eth_swbp *)buff_start;
swbp->skb = NULL;
swbp->xdpf = xdpf;
/* We reserve a fixed headroom of 256 bytes under the erratum and we *offeritalltoXDPprogramstouse.Ifnoroomisleftforthe *xdpfbackpointeronTX,wewillneedtocopythedata. *Disablemetadatasupportsincedatarealignmentsmightberequired *andtheinformationcanbelost.
*/ #ifdef CONFIG_DPAA_ERRATUM_A050385 if (unlikely(fman_has_errata_a050385())) {
xdp_set_data_meta_invalid(&xdp);
xdp.data_hard_start = vaddr;
xdp.frame_sz = DPAA_BP_RAW_SIZE;
} #endif
xdp_act = bpf_prog_run_xdp(xdp_prog, &xdp);
/* Update the length and the offset of the FD */
qm_fd_set_contig(fd, xdp.data - vaddr, xdp.data_end - xdp.data);
switch (xdp_act) { case XDP_PASS: #ifdef CONFIG_DPAA_ERRATUM_A050385
*xdp_meta_len = xdp_data_meta_unsupported(&xdp) ? 0 :
xdp.data - xdp.data_meta; #else
*xdp_meta_len = xdp.data - xdp.data_meta; #endif break; case XDP_TX: /* We can access the full headroom when sending the frame *backout
*/
xdp.data_hard_start = vaddr;
xdp.frame_sz = DPAA_BP_RAW_SIZE;
xdpf = xdp_convert_buff_to_frame(&xdp); if (unlikely(!xdpf)) {
free_pages((unsignedlong)vaddr, 0); break;
}
if (dpaa_xdp_xmit_frame(priv->net_dev, xdpf))
xdp_return_frame_rx_napi(xdpf);
break; case XDP_REDIRECT: /* Allow redirect to use the full headroom */
xdp.data_hard_start = vaddr;
xdp.frame_sz = DPAA_BP_RAW_SIZE;
err = xdp_do_redirect(priv->net_dev, &xdp, xdp_prog); if (err) {
trace_xdp_exception(priv->net_dev, xdp_prog, xdp_act);
free_pages((unsignedlong)vaddr, 0);
} break; default:
bpf_warn_invalid_xdp_action(priv->net_dev, xdp_prog, xdp_act);
fallthrough; case XDP_ABORTED:
trace_xdp_exception(priv->net_dev, xdp_prog, xdp_act);
fallthrough; case XDP_DROP: /* Free the buffer */
free_pages((unsignedlong)vaddr, 0); break;
}
if (unlikely(dpaa_eth_napi_schedule(percpu_priv, portal, sched_napi))) return qman_cb_dqrr_stop;
/* Make sure we didn't run out of buffers */ if (unlikely(dpaa_eth_refill_bpools(priv))) { /* Unable to refill the buffer pool due to insufficient *systemmemory.Justreleasetheframebackintothepool, *otherwisewe'llsoonendupwithanemptybufferpool.
*/
dpaa_fd_release(net_dev, &dq->fd); return qman_cb_dqrr_consume;
}
if (unlikely(fd_status & FM_FD_STAT_RX_ERRORS) != 0) { if (net_ratelimit())
netif_warn(priv, hw, net_dev, "FD status = 0x%08x\n",
fd_status & FM_FD_STAT_RX_ERRORS);
/* prefetch the first 64 bytes of the frame or the SGT start */
vaddr = phys_to_virt(addr);
prefetch(vaddr + qm_fd_get_offset(fd));
/* The only FD types that we may receive are contig and S/G */
WARN_ON((fd_format != qm_fd_contig) && (fd_format != qm_fd_sg));
/* Account for either the contig buffer or the SGT buffer (depending on *whichcasewewerein)havingbeenremovedfromthepool.
*/
count_ptr = this_cpu_ptr(dpaa_bp->percpu_count);
(*count_ptr)--;
/* Extract the timestamp stored in the headroom before running XDP */ if (priv->rx_tstamp) { if (!fman_port_get_tstamp(priv->mac_dev->port[RX], vaddr, &ns))
ts_valid = true; else
WARN_ONCE(1, "fman_port_get_tstamp failed!\n");
}
/* Extract the hash stored in the headroom before running XDP */ if (net_dev->features & NETIF_F_RXHASH && priv->keygen_in_use &&
!fman_port_get_hash_result_offset(priv->mac_dev->port[RX],
&hash_offset)) {
hash = be32_to_cpu(*(__be32 *)(vaddr + hash_offset));
hash_valid = true;
}
if (likely(fd_format == qm_fd_contig)) {
xdp_act = dpaa_run_xdp(priv, (struct qm_fd *)fd, vaddr,
dpaa_fq, &xdp_meta_len);
np->xdp_act |= xdp_act; if (xdp_act != XDP_PASS) {
percpu_stats->rx_packets++;
percpu_stats->rx_bytes += qm_fd_get_length(fd); return qman_cb_dqrr_consume;
}
skb = contig_fd_to_skb(priv, fd);
} else { /* XDP doesn't support S/G frames. Return the fragments to the *bufferpoolandreleasetheSGT.
*/ if (READ_ONCE(priv->xdp_prog)) {
WARN_ONCE(1, "S/G frames not supported under XDP\n");
sgt = vaddr + qm_fd_get_offset(fd);
dpaa_release_sgt_members(sgt);
free_pages((unsignedlong)vaddr, 0); return qman_cb_dqrr_consume;
}
skb = sg_fd_to_skb(priv, fd);
} if (!skb) return qman_cb_dqrr_consume;
if (xdp_meta_len)
skb_metadata_set(skb, xdp_meta_len);
/* Set the previously extracted timestamp */ if (ts_valid) {
shhwtstamps = skb_hwtstamps(skb);
memset(shhwtstamps, 0, sizeof(*shhwtstamps));
shhwtstamps->hwtstamp = ns_to_ktime(ns);
}
skb->protocol = eth_type_trans(skb, net_dev);
/* Set the previously extracted hash */ if (hash_valid) { enum pkt_hash_types type;
/* if L4 exists, it was used in the hash generation */
type = be32_to_cpu(fd->status) & FM_FD_STAT_L4CV ?
PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3;
skb_set_hash(skb, hash, type);
}
skb_len = skb->len;
if (unlikely(netif_receive_skb(skb) == NET_RX_DROP)) {
percpu_stats->rx_dropped++; return qman_cb_dqrr_consume;
}
staticbool xdp_validate_mtu(struct dpaa_priv *priv, int mtu)
{ int max_contig_data = priv->dpaa_bp->size - priv->rx_headroom;
/* We do not support S/G fragments when XDP is enabled. *LimittheMTUinrelationtothebuffersize.
*/ if (mtu + VLAN_ETH_HLEN + ETH_FCS_LEN > max_contig_data) {
dev_warn(priv->net_dev->dev.parent, "The maximum MTU for XDP is %d\n",
max_contig_data - VLAN_ETH_HLEN - ETH_FCS_LEN); returnfalse;
}
/* S/G fragments are not supported in XDP-mode */ if (bpf->prog && !xdp_validate_mtu(priv, net_dev->mtu)) {
NL_SET_ERR_MSG_MOD(bpf->extack, "MTU too large for XDP"); return -EINVAL;
}
up = netif_running(net_dev);
if (up)
dpaa_eth_stop(net_dev);
old_prog = xchg(&priv->xdp_prog, bpf->prog); if (old_prog)
bpf_prog_put(old_prog);
if (up) {
err = dpaa_open(net_dev); if (err) {
NL_SET_ERR_MSG_MOD(bpf->extack, "dpaa_open() failed"); return err;
}
}
/* This CGR will be associated with the SWP affined to the current CPU. *However,we'llplaceallouringressFQsinit.
*/
err = qman_create_cgr(&priv->ingress_cgr, QMAN_CGR_FLAG_USE_INIT,
&initcgr); if (err < 0) { if (netif_msg_drv(priv))
pr_err("Error %d creating ingress CGR with ID %d\n",
err, priv->ingress_cgr.cgrid);
qman_release_cgrid(priv->ingress_cgr.cgrid); goto out_error;
} if (netif_msg_drv(priv))
pr_debug("Created ingress CGR %d for netdev with hwaddr %pM\n",
priv->ingress_cgr.cgrid, priv->mac_dev->addr);
err = bman_is_probed(); if (!err) return -EPROBE_DEFER; if (err < 0) {
dev_err(dev, "failing probe due to bman probe error\n"); return -ENODEV;
}
err = qman_is_probed(); if (!err) return -EPROBE_DEFER; if (err < 0) {
dev_err(dev, "failing probe due to qman probe error\n"); return -ENODEV;
}
err = bman_portals_probed(); if (!err) return -EPROBE_DEFER; if (err < 0) {
dev_err(dev, "failing probe due to bman portals probe error\n"); return -ENODEV;
}
err = qman_portals_probed(); if (!err) return -EPROBE_DEFER; if (err < 0) {
dev_err(dev, "failing probe due to qman portals probe error\n"); return -ENODEV;
}
/* Allocate this early, so we can store relevant information in *theprivatearea
*/
net_dev = alloc_etherdev_mq(sizeof(*priv), dpaa_max_num_txqs()); if (!net_dev) {
dev_err(dev, "alloc_etherdev_mq() failed\n"); return -ENOMEM;
}
/* Do this here, so we can be verbose early */
SET_NETDEV_DEV(net_dev, dev->parent);
dev_set_drvdata(dev, net_dev);
/* Devices used for DMA mapping */
priv->rx_dma_dev = fman_port_get_device(mac_dev->port[RX]);
priv->tx_dma_dev = fman_port_get_device(mac_dev->port[TX]);
err = dma_coerce_mask_and_coherent(priv->rx_dma_dev, DMA_BIT_MASK(40)); if (!err)
err = dma_coerce_mask_and_coherent(priv->tx_dma_dev,
DMA_BIT_MASK(40)); if (err) {
netdev_err(net_dev, "dma_coerce_mask_and_coherent() failed\n"); goto free_netdev;
}
/* If fsl_fm_max_frm is set to a higher value than the all-common 1500, *wechooseconservativelyandlettheuserexplicitlysetahigher *MTUviaifconfig.Otherwise,theusermayendupwithdifferentMTUs *inthesameLAN. *Ifontheotherhandfsl_fm_max_frmhasbeenchosenbelow1500, *startwiththemaximumallowed.
*/
net_dev->mtu = min(dpaa_get_max_mtu(), ETH_DATA_LEN);
netdev_dbg(net_dev, "Setting initial MTU on net device: %d\n",
net_dev->mtu);
/* bp init */
dpaa_bp = dpaa_bp_alloc(dev); if (IS_ERR(dpaa_bp)) {
err = PTR_ERR(dpaa_bp); goto free_dpaa_bps;
} /* the raw size of the buffers used for reception */
dpaa_bp->raw_size = DPAA_BP_RAW_SIZE; /* avoid runtime computations by keeping the usable size here */
dpaa_bp->size = dpaa_bp_size(dpaa_bp->raw_size);
dpaa_bp->priv = priv;
/* Walk the CPUs with affine portals *andaddthispoolchanneltoeach'sdequeuemask.
*/
dpaa_eth_add_channel(priv->channel, &pdev->dev);
err = dpaa_fq_setup(priv, &dpaa_fq_cbs, priv->mac_dev->port[TX]); if (err) goto free_dpaa_bps;
/* Create a congestion group for this netdev, with *dynamically-allocatedCGRID. *MustbeexecutedafterprobingtheMAC,butbefore *assigningtheegressFQstotheCGRs.
*/
err = dpaa_eth_cgr_init(priv); if (err < 0) {
dev_err(dev, "Error initializing CGR\n"); goto free_dpaa_bps;
}
/* Add the FQs to the interface, and make them active */
list_for_each_entry_safe(dpaa_fq, tmp, &priv->dpaa_fq_list, list) {
err = dpaa_fq_init(dpaa_fq, false); if (err < 0) goto free_dpaa_fqs;
}
/* All real interfaces need their ports initialized */
err = dpaa_eth_init_ports(mac_dev, dpaa_bp, &port_fqs,
&priv->buf_layout[0], dev); if (err) goto free_dpaa_fqs;
/* Rx traffic distribution based on keygen hashing defaults to on */
priv->keygen_in_use = true;
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.84Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-03)
¤
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.