/* If we leave a bridge (bridge_dev is NULL), find an unused * FDB and use that.
*/ if (!bridge_dev) {
fdb = dpaa2_switch_fdb_get_unused(port_priv->ethsw_data);
/* If there is no unused FDB, we must be the last port that * leaves the last bridge, all the others are standalone. We * can just keep the FDB that we already have.
*/
if (!fdb) {
port_priv->fdb->bridge_dev = NULL; return 0;
}
/* The below call to netdev_for_each_lower_dev() demands the RTNL lock * being held. Assert on it so that it's easier to catch new code * paths that reach this point without the RTNL lock.
*/
ASSERT_RTNL();
/* If part of a bridge, use the FDB of the first dpaa2 switch interface * to be present in that bridge
*/
netdev_for_each_lower_dev(bridge_dev, other_dev, iter) { if (!dpaa2_switch_port_dev_check(other_dev)) continue;
/* The current port is about to change its FDB to the one used by the * first port that joined the bridge.
*/ if (other_port_priv) { /* The previous FDB is about to become unused, since the * interface is no longer standalone.
*/
port_priv->fdb->in_use = false;
port_priv->fdb->bridge_dev = NULL;
/* Get a reference to the new FDB */
port_priv->fdb = other_port_priv->fdb;
}
/* Keep track of the new upper bridge device */
port_priv->fdb->bridge_dev = bridge_dev;
return 0;
}
staticvoid dpaa2_switch_fdb_get_flood_cfg(struct ethsw_core *ethsw, u16 fdb_id, enum dpsw_flood_type type, struct dpsw_egress_flood_cfg *cfg)
{ int i = 0, j;
memset(cfg, 0, sizeof(*cfg));
/* Add all the DPAA2 switch ports found in the same bridging domain to * the egress flooding domain
*/ for (j = 0; j < ethsw->sw_attr.num_ifs; j++) { if (!ethsw->ports[j]) continue; if (ethsw->ports[j]->fdb->fdb_id != fdb_id) continue;
/* Interface needs to be down to change PVID */
up = dpaa2_switch_port_is_up(port_priv); if (up) {
err = dpsw_if_disable(ethsw->mc_io, 0,
ethsw->dpsw_handle,
port_priv->idx); if (err) {
netdev_err(netdev, "dpsw_if_disable err %d\n", err); return err;
}
}
/* Delete previous PVID info and mark the new one */
port_priv->vlans[port_priv->pvid] &= ~ETHSW_VLAN_PVID;
port_priv->vlans[pvid] |= ETHSW_VLAN_PVID;
port_priv->pvid = pvid;
set_tci_error: if (up) {
ret = dpsw_if_enable(ethsw->mc_io, 0,
ethsw->dpsw_handle,
port_priv->idx); if (ret) {
netdev_err(netdev, "dpsw_if_enable err %d\n", ret); return ret;
}
}
/* When we manage the MAC/PHY using phylink there is no need * to manually update the netif_carrier. * We can avoid locking because we are called from the "link changed" * IRQ handler, which is the same as the "endpoint changed" IRQ handler * (the writer to port_priv->mac), so we cannot race with it.
*/ if (dpaa2_mac_is_type_phy(port_priv->mac)) return 0;
/* Interrupts are received even though no one issued an 'ifconfig up' * on the switch interface. Ignore these link state update interrupts
*/ if (!netif_running(netdev)) return 0;
WARN_ONCE(state.up > 1, "Garbage read into link_state");
if (state.up != port_priv->link_state) { if (state.up) {
netif_carrier_on(netdev);
netif_tx_start_all_queues(netdev);
} else {
netif_carrier_off(netdev);
netif_tx_stop_all_queues(netdev);
}
port_priv->link_state = state.up;
}
return 0;
}
/* Manage all NAPI instances for the control interface. * * We only have one RX queue and one Tx Conf queue for all * switch ports. Therefore, we only need to enable the NAPI instance once, the * first time one of the switch ports runs .dev_open().
*/
staticvoid dpaa2_switch_enable_ctrl_if_napi(struct ethsw_core *ethsw)
{ int i;
/* Access to the ethsw->napi_users relies on the RTNL lock */
ASSERT_RTNL();
/* a new interface is using the NAPI instance */
ethsw->napi_users++;
/* if there is already a user of the instance, return */ if (ethsw->napi_users > 1) return;
for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++)
napi_enable(ðsw->fq[i].napi);
}
staticvoid dpaa2_switch_disable_ctrl_if_napi(struct ethsw_core *ethsw)
{ int i;
/* Access to the ethsw->napi_users relies on the RTNL lock */
ASSERT_RTNL();
/* If we are not the last interface using the NAPI, return */
ethsw->napi_users--; if (ethsw->napi_users) return;
for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++)
napi_disable(ðsw->fq[i].napi);
}
if (!dpaa2_switch_port_is_type_phy(port_priv)) { /* Explicitly set carrier off, otherwise * netif_carrier_ok() will return true and cause 'ip link show' * to report the LOWER_UP flag, even though the link * notification wasn't even received.
*/
netif_carrier_off(netdev);
}
if (!(ethsw->features & ETHSW_FEATURE_MAC_ADDR)) return 0;
/* Get firmware address, if any */
err = dpsw_if_get_port_mac_addr(ethsw->mc_io, 0, ethsw->dpsw_handle,
port_priv->idx, mac_addr); if (err) {
dev_err(dev, "dpsw_if_get_port_mac_addr() failed\n"); return err;
}
/* First check if firmware has any address configured by bootloader */ if (!is_zero_ether_addr(mac_addr)) {
eth_hw_addr_set(net_dev, mac_addr);
} else { /* No MAC address configured, fill in net_dev->dev_addr * with a random one
*/
eth_hw_addr_random(net_dev);
dev_dbg_once(dev, "device(s) have all-zero hwaddr, replaced with random\n");
/* Override NET_ADDR_RANDOM set by eth_hw_addr_random(); for all * practical purposes, this will be our "permanent" mac address, * at least until the next reboot. This move will also permit * register_netdevice() to properly fill up net_dev->perm_addr.
*/
net_dev->addr_assign_type = NET_ADDR_PERM;
}
/* Clear FAS to have consistent values for TX confirmation. It is * located in the first 8 bytes of the buffer's hardware annotation * area
*/
hwa = buff_start + DPAA2_SWITCH_SWA_SIZE;
memset(hwa, 0, 8);
/* Store a backpointer to the skb at the beginning of the buffer * (in the private data area) such that we can release it * on Tx confirm
*/
skbh = (struct sk_buff **)buff_start;
*skbh = skb;
/* We'll be holding a back-reference to the skb until Tx confirmation */
skb = skb_unshare(skb, GFP_ATOMIC); if (unlikely(!skb)) { /* skb_unshare() has already freed the skb */
net_err_ratelimited("%s: Error copying the socket buffer\n", net_dev->name); goto err_exit;
}
/* At this stage, we do not support non-linear skbs so just try to * linearize the skb and if that's not working, just drop the packet.
*/
err = skb_linearize(skb); if (err) {
net_err_ratelimited("%s: skb_linearize error (%d)!\n", net_dev->name, err); goto err_free_skb;
}
/* Offload all the mirror entries found in the block on this new port * joining it.
*/
err = dpaa2_switch_block_offload_mirror(block, port_priv); if (err) return err;
/* If the port is already bound to this ACL table then do nothing. This * can happen when this port is the first one to join a tc block
*/ if (port_priv->filter_block == block) return 0;
err = dpaa2_switch_port_acl_tbl_unbind(port_priv, old_block); if (err) return err;
/* Mark the previous ACL table as being unused if this was the last * port that was using it.
*/ if (old_block->ports == 0)
old_block->in_use = false;
/* Unoffload all the mirror entries found in the block from the * port leaving it.
*/
err = dpaa2_switch_block_unoffload_mirror(block, port_priv); if (err) return err;
/* We are the last port that leaves a block (an ACL table). * We'll continue to use this table.
*/ if (block->ports == BIT(port_priv->idx)) return 0;
err = dpaa2_switch_port_acl_tbl_unbind(port_priv, block); if (err) return err;
if (!block_cb) { /* If the filter block is not already known, then this port * must be the first to join it. In this case, we can just * continue to use our private table
*/
filter_block = port_priv->filter_block;
block_cb = flow_block_cb_alloc(dpaa2_switch_port_setup_tc_block_cb_ig,
ethsw, filter_block, NULL); if (IS_ERR(block_cb)) return PTR_ERR(block_cb);
err = dpaa2_mac_open(mac); if (err) goto err_free_mac;
if (dpaa2_mac_is_type_phy(mac)) {
err = dpaa2_mac_connect(mac); if (err) {
netdev_err(port_priv->netdev, "Error connecting to the MAC endpoint %pe\n",
ERR_PTR(err)); goto err_close_mac;
}
}
if (status & DPSW_IRQ_EVENT_LINK_CHANGED)
dpaa2_switch_port_link_state_update(port_priv->netdev);
if (status & DPSW_IRQ_EVENT_ENDPOINT_CHANGED) {
dpaa2_switch_port_set_mac_addr(port_priv); /* We can avoid locking because the "endpoint changed" IRQ * handler is the only one who changes priv->mac at runtime, * so we are not racing with anyone.
*/
had_mac = !!port_priv->mac; if (had_mac)
dpaa2_switch_port_disconnect_mac(port_priv); else
dpaa2_switch_port_connect_mac(port_priv);
}
err = dpsw_clear_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle,
DPSW_IRQ_INDEX_IF, status); if (err)
dev_err(dev, "Can't clear irq status (err %d)\n", err);
err = dpaa2_switch_port_set_stp_state(port_priv, state); if (err) return err;
switch (state) { case BR_STATE_DISABLED: case BR_STATE_BLOCKING: case BR_STATE_LISTENING:
err = dpaa2_switch_port_set_learning(port_priv, false); break; case BR_STATE_LEARNING: case BR_STATE_FORWARDING:
err = dpaa2_switch_port_set_learning(port_priv,
port_priv->learn_ena); break;
}
/* Make sure that the VLAN is not already configured * on the switch port
*/ if (port_priv->vlans[vlan->vid] & ETHSW_VLAN_MEMBER) {
netdev_err(netdev, "VLAN %d already configured\n", vlan->vid); return -EEXIST;
}
/* Check if there is space for a new VLAN */
err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle,
ðsw->sw_attr); if (err) {
netdev_err(netdev, "dpsw_get_attributes err %d\n", err); return err;
} if (attr->max_vlans - attr->num_vlans < 1) return -ENOSPC;
/* Check if there is space for a new VLAN */
err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle,
ðsw->sw_attr); if (err) {
netdev_err(netdev, "dpsw_get_attributes err %d\n", err); return err;
} if (attr->max_vlans - attr->num_vlans < 1) return -ENOSPC;
if (!port_priv->ethsw_data->vlans[vlan->vid]) { /* this is a new VLAN */
err = dpaa2_switch_add_vlan(port_priv, vlan->vid); if (err) return err;
if (port_priv->vlans[vid] & ETHSW_VLAN_PVID) { /* If we are deleting the PVID of a port, use VLAN 4095 instead * as we are sure that neither the bridge nor the 8021q module * will use it
*/
err = dpaa2_switch_port_set_pvid(port_priv, 4095); if (err) return err;
}
/* Delete VLAN from switch if it is no longer configured on * any port
*/ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) { if (ethsw->ports[i] &&
ethsw->ports[i]->vlans[vid] & ETHSW_VLAN_MEMBER) return 0; /* Found a port member in VID */
}
ethsw->vlans[vid] &= ~ETHSW_VLAN_GLOBAL;
err = dpaa2_switch_dellink(ethsw, vid); if (err) return err;
}
/* Delete the previously manually installed VLAN 1 */
err = dpaa2_switch_port_del_vlan(port_priv, 1); if (err) return err;
dpaa2_switch_port_set_fdb(port_priv, upper_dev);
/* Inherit the initial bridge port learning state */
learn_ena = br_port_flag_is_set(netdev, BR_LEARNING);
err = dpaa2_switch_port_set_learning(port_priv, learn_ena);
port_priv->learn_ena = learn_ena;
/* Setup the egress flood policy (broadcast, unknown unicast) */
err = dpaa2_switch_fdb_set_egress_flood(ethsw, port_priv->fdb->fdb_id); if (err) goto err_egress_flood;
/* Recreate the egress flood domain of the FDB that we just left. */
err = dpaa2_switch_fdb_set_egress_flood(ethsw, old_fdb->fdb_id); if (err) goto err_egress_flood;
/* First of all, fast age any learn FDB addresses on this switch port */
dpaa2_switch_port_fast_age(port_priv);
/* Clear all RX VLANs installed through vlan_vid_add() either as VLAN * upper devices or otherwise from the FDB table that we are about to * leave
*/
err = vlan_for_each(netdev, dpaa2_switch_port_clear_rxvlan, netdev); if (err)
netdev_err(netdev, "Unable to clear RX VLANs from old FDB table, err (%d)\n", err);
dpaa2_switch_port_set_fdb(port_priv, NULL);
/* Restore all RX VLANs into the new FDB table that we just joined */
err = vlan_for_each(netdev, dpaa2_switch_port_restore_rxvlan, netdev); if (err)
netdev_err(netdev, "Unable to restore RX VLANs to the new FDB, err (%d)\n", err);
/* Reset the flooding state to denote that this port can send any * packet in standalone mode. With this, we are also ensuring that any * later bridge join will have the flooding flag on.
*/
port_priv->bcast_flood = true;
port_priv->ucast_flood = true;
/* Setup the egress flood policy (broadcast, unknown unicast). * When the port is not under a bridge, only the CTRL interface is part * of the flooding domain besides the actual port
*/
err = dpaa2_switch_fdb_set_egress_flood(ethsw, port_priv->fdb->fdb_id); if (err) return err;
/* Recreate the egress flood domain of the FDB that we just left */
err = dpaa2_switch_fdb_set_egress_flood(ethsw, old_fdb->fdb_id); if (err) return err;
/* No HW learning when not under a bridge */
err = dpaa2_switch_port_set_learning(port_priv, false); if (err) return err;
port_priv->learn_ena = false;
/* Add the VLAN 1 as PVID when not under a bridge. We need this since * the dpaa2 switch interfaces are not capable to be VLAN unaware
*/ return dpaa2_switch_port_add_vlan(port_priv, DEFAULT_VLAN_ID,
BRIDGE_VLAN_INFO_UNTAGGED | BRIDGE_VLAN_INFO_PVID);
}
/* RCU read lock not necessary because we have write-side protection * (rtnl_mutex), however a non-rcu iterator does not exist.
*/
netdev_for_each_upper_dev_rcu(netdev, upper_dev, iter) if (is_vlan_dev(upper_dev)) return -EOPNOTSUPP;
if (!br_vlan_enabled(upper_dev)) {
NL_SET_ERR_MSG_MOD(extack, "Cannot join a VLAN-unaware bridge"); return -EOPNOTSUPP;
}
err = dpaa2_switch_prevent_bridging_with_8021q_upper(netdev); if (err) {
NL_SET_ERR_MSG_MOD(extack, "Cannot join a bridge while VLAN uppers are present"); return 0;
}
netdev_for_each_lower_dev(upper_dev, other_dev, iter) { if (!dpaa2_switch_port_dev_check(other_dev)) continue;
other_port_priv = netdev_priv(other_dev); if (other_port_priv->ethsw_data != port_priv->ethsw_data) {
NL_SET_ERR_MSG_MOD(extack, "Interface from a different DPSW is in the bridge already"); return -EINVAL;
}
}
/* get switch ingress interface ID */
if_id = upper_32_bits(dpaa2_fd_get_flc(fd)) & 0x0000FFFF;
if (if_id >= ethsw->sw_attr.num_ifs) {
dev_err(ethsw->dev, "Frame received from unknown interface!\n"); goto err_free_fd;
}
port_priv = ethsw->ports[if_id];
netdev = port_priv->netdev;
/* build the SKB based on the FD received */ if (dpaa2_fd_get_format(fd) != dpaa2_fd_single) { if (net_ratelimit()) {
netdev_err(netdev, "Received invalid frame format\n"); goto err_free_fd;
}
}
skb = dpaa2_switch_build_linear_skb(ethsw, fd); if (unlikely(!skb)) goto err_free_fd;
skb_reset_mac_header(skb);
/* Remove the VLAN header if the packet that we just received has a vid * equal to the port PVIDs. Since the dpaa2-switch can operate only in * VLAN-aware mode and no alterations are made on the packet when it's * redirected/mirrored to the control interface, we are sure that there * will always be a VLAN header present.
*/
hdr = vlan_eth_hdr(skb);
vid = ntohs(hdr->h_vlan_TCI) & VLAN_VID_MASK; if (vid == port_priv->pvid) {
err = __skb_vlan_pop(skb, &vlan_tci); if (err) {
dev_info(ethsw->dev, "__skb_vlan_pop() returned %d", err); goto err_free_fd;
}
}
/* Free buffers acquired from the buffer pool or which were meant to * be released in the pool
*/ staticvoid dpaa2_switch_free_bufs(struct ethsw_core *ethsw, u64 *buf_array, int count)
{ struct device *dev = ethsw->dev; void *vaddr; int i;
for (i = 0; i < count; i++) {
vaddr = dpaa2_iova_to_virt(ethsw->iommu_domain, buf_array[i]);
dma_unmap_page(dev, buf_array[i], DPAA2_SWITCH_RX_BUF_SIZE,
DMA_FROM_DEVICE);
free_pages((unsignedlong)vaddr, 0);
}
}
/* Perform a single release command to add buffers * to the specified buffer pool
*/ staticint dpaa2_switch_add_bufs(struct ethsw_core *ethsw, u16 bpid)
{ struct device *dev = ethsw->dev;
u64 buf_array[BUFS_PER_CMD]; struct page *page; int retries = 0;
dma_addr_t addr; int err; int i;
for (i = 0; i < BUFS_PER_CMD; i++) { /* Allocate one page for each Rx buffer. WRIOP sees * the entire page except for a tailroom reserved for * skb shared info
*/
page = dev_alloc_pages(0); if (!page) {
dev_err(dev, "buffer allocation failed\n"); goto err_alloc;
}
release_bufs: /* In case the portal is busy, retry until successful or * max retries hit.
*/ while ((err = dpaa2_io_service_release(NULL, bpid,
buf_array, i)) == -EBUSY) { if (retries++ >= DPAA2_SWITCH_SWP_BUSY_RETRIES) break;
cpu_relax();
}
/* If release command failed, clean up and bail out. */ if (err) {
dpaa2_switch_free_bufs(ethsw, buf_array, i); return 0;
}
return i;
err_map:
__free_pages(page, 0);
err_alloc: /* If we managed to allocate at least some buffers, * release them to hardware
*/ if (i) goto release_bufs;
return 0;
}
staticint dpaa2_switch_refill_bp(struct ethsw_core *ethsw)
{ int *count = ðsw->buf_count; int new_count; int err = 0;
if (unlikely(*count < DPAA2_ETHSW_REFILL_THRESH)) { do {
new_count = dpaa2_switch_add_bufs(ethsw, ethsw->bpid); if (unlikely(!new_count)) { /* Out of memory; abort for now, we'll * try later on
*/ break;
}
*count += new_count;
} while (*count < DPAA2_ETHSW_NUM_BUFS);
if (unlikely(*count < DPAA2_ETHSW_NUM_BUFS))
err = -ENOMEM;
}
return err;
}
staticint dpaa2_switch_seed_bp(struct ethsw_core *ethsw)
{ int *count, ret, i;
for (i = 0; i < DPAA2_ETHSW_NUM_BUFS; i += BUFS_PER_CMD) {
ret = dpaa2_switch_add_bufs(ethsw, ethsw->bpid);
count = ðsw->buf_count;
*count += ret;
if (unlikely(ret < BUFS_PER_CMD)) return -ENOMEM;
}
return 0;
}
staticvoid dpaa2_switch_drain_bp(struct ethsw_core *ethsw)
{
u64 buf_array[BUFS_PER_CMD]; int ret;
do {
ret = dpaa2_io_service_acquire(NULL, ethsw->bpid,
buf_array, BUFS_PER_CMD); if (ret < 0) {
dev_err(ethsw->dev, "dpaa2_io_service_acquire() = %d\n", ret); return;
}
dpaa2_switch_free_bufs(ethsw, buf_array, ret);
staticint dpaa2_switch_alloc_rings(struct ethsw_core *ethsw)
{ int i;
for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++) {
ethsw->fq[i].store =
dpaa2_io_store_create(DPAA2_SWITCH_STORE_SIZE,
ethsw->dev); if (!ethsw->fq[i].store) {
dev_err(ethsw->dev, "dpaa2_io_store_create failed\n"); while (--i >= 0)
dpaa2_io_store_destroy(ethsw->fq[i].store); return -ENOMEM;
}
}
return 0;
}
staticvoid dpaa2_switch_destroy_rings(struct ethsw_core *ethsw)
{ int i;
for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++)
dpaa2_io_store_destroy(ethsw->fq[i].store);
}
staticint dpaa2_switch_pull_fq(struct dpaa2_switch_fq *fq)
{ int err, retries = 0;
/* Try to pull from the FQ while the portal is busy and we didn't hit * the maximum number fo retries
*/ do {
err = dpaa2_io_service_pull_fq(NULL, fq->fqid, fq->store);
cpu_relax();
} while (err == -EBUSY && retries++ < DPAA2_SWITCH_SWP_BUSY_RETRIES);
if (unlikely(err))
dev_err(fq->ethsw->dev, "dpaa2_io_service_pull err %d", err);
return err;
}
/* Consume all frames pull-dequeued into the store */ staticint dpaa2_switch_store_consume(struct dpaa2_switch_fq *fq)
{ struct ethsw_core *ethsw = fq->ethsw; int cleaned = 0, is_last; struct dpaa2_dq *dq; int retries = 0;
do { /* Get the next available FD from the store */
dq = dpaa2_io_store_next(fq->store, &is_last); if (unlikely(!dq)) { if (retries++ >= DPAA2_SWITCH_SWP_BUSY_RETRIES) {
dev_err_once(ethsw->dev, "No valid dequeue response\n"); return -ETIMEDOUT;
} continue;
}
if (fq->type == DPSW_QUEUE_RX)
dpaa2_switch_rx(fq, dpaa2_dq_fd(dq)); else
dpaa2_switch_tx_conf(fq, dpaa2_dq_fd(dq));
cleaned++;
} while (!is_last);
return cleaned;
}
/* NAPI poll routine */ staticint dpaa2_switch_poll(struct napi_struct *napi, int budget)
{ int err, cleaned = 0, store_cleaned, work_done; struct dpaa2_switch_fq *fq; int retries = 0;
/* We didn't consume the entire budget, so finish napi and re-enable * data availability notifications
*/
napi_complete_done(napi, cleaned); do {
err = dpaa2_io_service_rearm(NULL, &fq->nctx);
cpu_relax();
} while (err == -EBUSY && retries++ < DPAA2_SWITCH_SWP_BUSY_RETRIES);
staticint dpaa2_switch_setup_dpio(struct ethsw_core *ethsw)
{ struct dpsw_ctrl_if_queue_cfg queue_cfg; struct dpaa2_io_notification_ctx *nctx; int err, i, j;
for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++) {
nctx = ðsw->fq[i].nctx;
/* Register a new software context for the FQID. * By using NULL as the first parameter, we specify that we do * not care on which cpu are interrupts received for this queue
*/
nctx->is_cdan = 0;
nctx->id = ethsw->fq[i].fqid;
nctx->desired_cpu = DPAA2_IO_ANY_CPU;
nctx->cb = dpaa2_switch_fqdan_cb;
err = dpaa2_io_service_register(NULL, nctx, ethsw->dev); if (err) {
err = -EPROBE_DEFER; goto err_register;
}
/* Add an ACL to redirect frames with specific destination MAC address to * control interface
*/ staticint dpaa2_switch_port_trap_mac_addr(struct ethsw_port_priv *port_priv, constchar *mac)
{ struct dpaa2_switch_acl_entry acl_entry = {0};
/* Match on the destination MAC address */
ether_addr_copy(acl_entry.key.match.l2_dest_mac, mac);
eth_broadcast_addr(acl_entry.key.mask.l2_dest_mac);
/* Trap to CPU */
acl_entry.cfg.precedence = 0;
acl_entry.cfg.result.action = DPSW_ACL_ACTION_REDIRECT_TO_CTRL_IF;
/* Get the Tx queue for this specific port */
err = dpsw_if_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle,
port_priv->idx, &dpsw_if_attr); if (err) {
netdev_err(netdev, "dpsw_if_get_attributes err %d\n", err); return err;
}
port_priv->tx_qdid = dpsw_if_attr.qdid;
/* Create a FDB table for this particular switch port */
fdb_cfg.num_fdb_entries = ethsw->sw_attr.max_fdb_entries / ethsw->sw_attr.num_ifs;
err = dpsw_fdb_add(ethsw->mc_io, 0, ethsw->dpsw_handle,
&fdb_id, &fdb_cfg); if (err) {
netdev_err(netdev, "dpsw_fdb_add err %d\n", err); return err;
}
/* Find an unused dpaa2_switch_fdb structure and use it */
fdb = dpaa2_switch_fdb_get_unused(ethsw);
fdb->fdb_id = fdb_id;
fdb->in_use = true;
fdb->bridge_dev = NULL;
port_priv->fdb = fdb;
/* We need to add VLAN 1 as the PVID on this port until it is under a * bridge since the DPAA2 switch is not able to handle the traffic in a * VLAN unaware fashion
*/
err = dpaa2_switch_port_vlans_add(netdev, &vlan); if (err) return err;
/* Setup the egress flooding domains (broadcast, unknown unicast */
err = dpaa2_switch_fdb_set_egress_flood(ethsw, port_priv->fdb->fdb_id); if (err) return err;
/* Create an ACL table to be used by this switch port */
acl_cfg.max_entries = DPAA2_ETHSW_PORT_MAX_ACL_ENTRIES;
err = dpsw_acl_add(ethsw->mc_io, 0, ethsw->dpsw_handle,
&acl_tbl_id, &acl_cfg); if (err) {
netdev_err(netdev, "dpsw_acl_add err %d\n", err); return err;
}
/* Set MTU limits */
port_netdev->min_mtu = ETH_MIN_MTU;
port_netdev->max_mtu = ETHSW_MAX_FRAME_LENGTH;
/* Populate the private port structure so that later calls to * dpaa2_switch_port_init() can use it.
*/
ethsw->ports[port_idx] = port_priv;
/* The DPAA2 switch's ingress path depends on the VLAN table, * thus we are not able to disable VLAN filtering.
*/
port_netdev->features = NETIF_F_HW_VLAN_CTAG_FILTER |
NETIF_F_HW_VLAN_STAG_FILTER |
NETIF_F_HW_TC;
port_netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
err = dpaa2_switch_port_init(port_priv, port_idx); if (err) goto err_port_probe;
err = dpaa2_switch_port_set_mac_addr(port_priv); if (err) goto err_port_probe;
for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
err = dpaa2_switch_probe_port(ethsw, i); if (err) goto err_free_netdev;
}
/* Add a NAPI instance for each of the Rx queues. The first port's * net_device will be associated with the instances since we do not have * different queues for each switch ports.
*/ for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++)
netif_napi_add(ethsw->ports[0]->netdev, ðsw->fq[i].napi,
dpaa2_switch_poll);
/* By convention, if the mirror port is equal to the number of switch * interfaces, then mirroring of any kind is disabled.
*/
ethsw->mirror_port = ethsw->sw_attr.num_ifs;
/* Register the netdev only when the entire setup is done and the * switch port interfaces are ready to receive traffic
*/ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
err = register_netdev(ethsw->ports[i]->netdev); if (err < 0) {
dev_err(dev, "register_netdev error %d\n", err); goto err_unregister_ports;
}
}
return 0;
err_unregister_ports: for (i--; i >= 0; i--)
unregister_netdev(ethsw->ports[i]->netdev);
dpaa2_switch_teardown_irqs(sw_dev);
err_stop:
dpsw_disable(ethsw->mc_io, 0, ethsw->dpsw_handle);
err_free_netdev: for (i--; i >= 0; i--)
dpaa2_switch_remove_port(ethsw, i);
kfree(ethsw->filter_blocks);
err_free_fdbs:
kfree(ethsw->fdbs);
err_free_ports:
kfree(ethsw->ports);
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.