p = ncsi_find_package(ndp, NCSI_PACKAGE_INDEX(id));
c = p ? ncsi_find_channel(p, NCSI_CHANNEL_INDEX(id)) : NULL;
if (np)
*np = p; if (nc)
*nc = c;
}
/* For two consecutive NCSI commands, the packet IDs shouldn't * be same. Otherwise, the bogus response might be replied. So * the available IDs are allocated in round-robin fashion.
*/ struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp, unsignedint req_flags)
{ struct ncsi_request *nr = NULL; int i, limit = ARRAY_SIZE(ndp->requests); unsignedlong flags;
/* Check if there is one available request until the ceiling */
spin_lock_irqsave(&ndp->lock, flags); for (i = ndp->request_id; i < limit; i++) { if (ndp->requests[i].used) continue;
nr = &ndp->requests[i];
nr->used = true;
nr->flags = req_flags;
ndp->request_id = i + 1; goto found;
}
/* Fail back to check from the starting cursor */ for (i = NCSI_REQ_START_IDX; i < ndp->request_id; i++) { if (ndp->requests[i].used) continue;
nr = &ndp->requests[i];
nr->used = true;
nr->flags = req_flags;
ndp->request_id = i + 1; goto found;
}
/* If the request already had associated response, * let the response handler to release it.
*/
spin_lock_irqsave(&ndp->lock, flags);
nr->enabled = false; if (nr->rsp || !nr->cmd) {
spin_unlock_irqrestore(&ndp->lock, flags); return;
}
spin_unlock_irqrestore(&ndp->lock, flags);
if (nr->flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) { if (nr->cmd) { /* Find the package */
cmd = (struct ncsi_cmd_pkt *)
skb_network_header(nr->cmd);
ncsi_find_package_and_channel(ndp,
cmd->cmd.common.channel,
&np, &nc);
ncsi_send_netlink_timeout(nr, np, nc);
}
}
/* Release the request */
ncsi_free_request(nr);
}
/* To retrieve the last link states of channels in current * package when current active channel needs fail over to * another one. It means we will possibly select another * channel as next active one. The link states of channels * are most important factor of the selection. So we need * accurate link states. Unfortunately, the link states on * inactive channels can't be updated with LSC AEN in time.
*/ if (ndp->flags & NCSI_DEV_RESHUFFLE)
nd->state = ncsi_dev_state_suspend_gls; else
nd->state = ncsi_dev_state_suspend_dcnt;
ret = ncsi_xmit_cmd(&nca); if (ret) goto error;
break; case ncsi_dev_state_suspend_gls:
ndp->pending_req_num = 1;
nca.type = NCSI_PKT_CMD_GLS;
nca.package = np->id;
nca.channel = ndp->channel_probe_id;
ret = ncsi_xmit_cmd(&nca); if (ret) goto error;
ndp->channel_probe_id++;
nd->state = ncsi_dev_state_suspend_deselect;
ret = ncsi_xmit_cmd(&nca); if (ret) goto error;
NCSI_FOR_EACH_CHANNEL(np, tmp) { /* If there is another channel active on this package * do not deselect the package.
*/ if (tmp != nc && tmp->state == NCSI_CHANNEL_ACTIVE) {
nd->state = ncsi_dev_state_suspend_done; break;
}
} break; case ncsi_dev_state_suspend_deselect:
ndp->pending_req_num = 1;
nca->type = NCSI_PKT_CMD_SVF;
nca->words[1] = vid; /* HW filter index starts at 1 */
nca->bytes[6] = index + 1;
nca->bytes[7] = 0x00; return 0;
}
/* Find an outstanding VLAN tag and construct a "Set VLAN Filter - Enable" * packet.
*/ staticint set_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc, struct ncsi_cmd_arg *nca)
{ struct ncsi_channel_vlan_filter *ncf; struct vlan_vid *vlan = NULL; unsignedlong flags; int i, index; void *bitmap;
u16 vid;
if (list_empty(&ndp->vlan_vids)) return -1;
ncf = &nc->vlan_filter;
bitmap = &ncf->bitmap;
spin_lock_irqsave(&nc->lock, flags);
rcu_read_lock();
list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
vid = vlan->vid; for (i = 0; i < ncf->n_vids; i++) if (ncf->vids[i] == vid) {
vid = 0; break;
} if (vid) break;
}
rcu_read_unlock();
if (!vid) { /* No VLAN ID is not set */
spin_unlock_irqrestore(&nc->lock, flags); return -1;
}
index = find_first_zero_bit(bitmap, ncf->n_vids); if (index < 0 || index >= ncf->n_vids) {
netdev_err(ndp->ndev.dev, "Channel %u already has all VLAN filters set\n",
nc->id);
spin_unlock_irqrestore(&nc->lock, flags); return -1;
}
/* This function should only be called once, return if flag set */ if (nca->ndp->gma_flag == 1) return -1;
/* Find gma handler for given manufacturer id */ for (i = 0; i < ARRAY_SIZE(ncsi_oem_gma_handlers); i++) { if (ncsi_oem_gma_handlers[i].mfr_id == mf_id) { if (ncsi_oem_gma_handlers[i].handler)
nch = &ncsi_oem_gma_handlers[i]; break;
}
}
if (!nch) {
netdev_err(nca->ndp->ndev.dev, "NCSI: No GMA handler available for MFR-ID (0x%x)\n",
mf_id); return -1;
}
/* Get Mac address from NCSI device */ return nch->handler(nca);
}
/* Determine if a given channel from the channel_queue should be used for Tx */ staticbool ncsi_channel_is_tx(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc)
{ struct ncsi_channel_mode *ncm; struct ncsi_channel *channel; struct ncsi_package *np;
/* Check if any other channel has Tx enabled; a channel may have already * been configured and removed from the channel queue.
*/
NCSI_FOR_EACH_PACKAGE(ndp, np) { if (!ndp->multi_package && np != nc->package) continue;
NCSI_FOR_EACH_CHANNEL(np, channel) {
ncm = &channel->modes[NCSI_MODE_TX_ENABLE]; if (ncm->enable) returnfalse;
}
}
/* This channel is the preferred channel and has link */
list_for_each_entry_rcu(channel, &ndp->channel_queue, link) {
np = channel->package; if (np->preferred_channel &&
ncsi_channel_has_link(np->preferred_channel)) { return np->preferred_channel == nc;
}
}
/* This channel has link */ if (ncsi_channel_has_link(nc)) returntrue;
list_for_each_entry_rcu(channel, &ndp->channel_queue, link) if (ncsi_channel_has_link(channel)) returnfalse;
/* No other channel has link; default to this one */ returntrue;
}
/* Change the active Tx channel in a multi-channel setup */ int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp, struct ncsi_package *package, struct ncsi_channel *disable, struct ncsi_channel *enable)
{ struct ncsi_cmd_arg nca; struct ncsi_channel *nc; struct ncsi_package *np; int ret = 0;
if (!package->multi_channel && !ndp->multi_package)
netdev_warn(ndp->ndev.dev, "NCSI: Trying to update Tx channel in single-channel mode\n");
nca.ndp = ndp;
nca.req_flags = 0;
/* Find current channel with Tx enabled */
NCSI_FOR_EACH_PACKAGE(ndp, np) { if (disable) break; if (!ndp->multi_package && np != package) continue;
break; case ncsi_dev_state_config_apply_mac:
rtnl_lock();
ret = dev_set_mac_address(dev, &ndp->pending_mac, NULL);
rtnl_unlock(); if (ret < 0)
netdev_warn(dev, "NCSI: 'Writing MAC address to device failed\n");
nd->state = ncsi_dev_state_config_clear_vids;
fallthrough; case ncsi_dev_state_config_clear_vids: case ncsi_dev_state_config_svf: case ncsi_dev_state_config_ev: case ncsi_dev_state_config_sma: case ncsi_dev_state_config_ebf: case ncsi_dev_state_config_dgmf: case ncsi_dev_state_config_ecnt: case ncsi_dev_state_config_ec: case ncsi_dev_state_config_ae: case ncsi_dev_state_config_gls:
ndp->pending_req_num = 1;
nca.package = np->id;
nca.channel = nc->id;
/* Clear any active filters on the channel before setting */ if (nd->state == ncsi_dev_state_config_clear_vids) {
ret = clear_one_vid(ndp, nc, &nca); if (ret) {
nd->state = ncsi_dev_state_config_svf;
schedule_work(&ndp->work); break;
} /* Repeat */
nd->state = ncsi_dev_state_config_clear_vids; /* Add known VLAN tags to the filter */
} elseif (nd->state == ncsi_dev_state_config_svf) {
ret = set_one_vid(ndp, nc, &nca); if (ret) {
nd->state = ncsi_dev_state_config_ev;
schedule_work(&ndp->work); break;
} /* Repeat */
nd->state = ncsi_dev_state_config_svf; /* Enable/Disable the VLAN filter */
} elseif (nd->state == ncsi_dev_state_config_ev) { if (list_empty(&ndp->vlan_vids)) {
nca.type = NCSI_PKT_CMD_DV;
} else {
nca.type = NCSI_PKT_CMD_EV;
nca.bytes[3] = NCSI_CAP_VLAN_NO;
}
nd->state = ncsi_dev_state_config_sma;
} elseif (nd->state == ncsi_dev_state_config_sma) { /* Use first entry in unicast filter table. Note that * the MAC filter table starts from entry 1 instead of * 0.
*/
nca.type = NCSI_PKT_CMD_SMA; for (index = 0; index < 6; index++)
nca.bytes[index] = dev->dev_addr[index];
nca.bytes[6] = 0x1;
nca.bytes[7] = 0x1;
nd->state = ncsi_dev_state_config_ebf;
} elseif (nd->state == ncsi_dev_state_config_ebf) {
nca.type = NCSI_PKT_CMD_EBF;
nca.dwords[0] = nc->caps[NCSI_CAP_BC].cap; /* if multicast global filtering is supported then * disable it so that all multicast packet will be * forwarded to management controller
*/ if (nc->caps[NCSI_CAP_GENERIC].cap &
NCSI_CAP_GENERIC_MC)
nd->state = ncsi_dev_state_config_dgmf; elseif (ncsi_channel_is_tx(ndp, nc))
nd->state = ncsi_dev_state_config_ecnt; else
nd->state = ncsi_dev_state_config_ec;
} elseif (nd->state == ncsi_dev_state_config_dgmf) {
nca.type = NCSI_PKT_CMD_DGMF; if (ncsi_channel_is_tx(ndp, nc))
nd->state = ncsi_dev_state_config_ecnt; else
nd->state = ncsi_dev_state_config_ec;
} elseif (nd->state == ncsi_dev_state_config_ecnt) { if (np->preferred_channel &&
nc != np->preferred_channel)
netdev_info(ndp->ndev.dev, "NCSI: Tx failed over to channel %u\n",
nc->id);
nca.type = NCSI_PKT_CMD_ECNT;
nd->state = ncsi_dev_state_config_ec;
} elseif (nd->state == ncsi_dev_state_config_ec) { /* Enable AEN if it's supported */
nca.type = NCSI_PKT_CMD_EC;
nd->state = ncsi_dev_state_config_ae; if (!(nc->caps[NCSI_CAP_AEN].cap & NCSI_CAP_AEN_MASK))
nd->state = ncsi_dev_state_config_gls;
} elseif (nd->state == ncsi_dev_state_config_ae) {
nca.type = NCSI_PKT_CMD_AE;
nca.bytes[0] = 0;
nca.dwords[1] = nc->caps[NCSI_CAP_AEN].cap;
nd->state = ncsi_dev_state_config_gls;
} elseif (nd->state == ncsi_dev_state_config_gls) {
nca.type = NCSI_PKT_CMD_GLS;
nd->state = ncsi_dev_state_config_done;
}
ret = ncsi_xmit_cmd(&nca); if (ret) {
netdev_err(ndp->ndev.dev, "NCSI: Failed to transmit CMD %x\n",
nca.type); goto error;
} break; case ncsi_dev_state_config_done:
netdev_dbg(ndp->ndev.dev, "NCSI: channel %u config done\n",
nc->id);
spin_lock_irqsave(&nc->lock, flags);
nc->state = NCSI_CHANNEL_ACTIVE;
if (ndp->flags & NCSI_DEV_RESET) { /* A reset event happened during config, start it now */
nc->reconfigure_needed = false;
spin_unlock_irqrestore(&nc->lock, flags);
ncsi_reset_dev(nd); break;
}
if (nc->reconfigure_needed) { /* This channel's configuration has been updated * part-way during the config state - start the * channel configuration over
*/
nc->reconfigure_needed = false;
nc->state = NCSI_CHANNEL_INACTIVE;
spin_unlock_irqrestore(&nc->lock, flags);
/* By default the search is done once an inactive channel with up * link is found, unless a preferred channel is set. * If multi_package or multi_channel are configured all channels in the * whitelist are added to the channel queue.
*/
found = NULL;
with_link = false;
NCSI_FOR_EACH_PACKAGE(ndp, np) { if (!(ndp->package_whitelist & (0x1 << np->id))) continue;
NCSI_FOR_EACH_CHANNEL(np, nc) { if (!(np->channel_whitelist & (0x1 << nc->id))) continue;
ncm = &nc->modes[NCSI_MODE_LINK]; if (ncm->data[2] & 0x1) {
found = nc;
with_link = true;
}
/* If multi_channel is enabled configure all valid * channels whether or not they currently have link * so they will have AENs enabled.
*/ if (with_link || np->multi_channel) {
spin_lock_irqsave(&ndp->lock, flags);
list_add_tail_rcu(&nc->link,
&ndp->channel_queue);
spin_unlock_irqrestore(&ndp->lock, flags);
/* The hardware arbitration is disabled if any one channel * doesn't support explicitly.
*/
NCSI_FOR_EACH_PACKAGE(ndp, np) {
NCSI_FOR_EACH_CHANNEL(np, nc) {
has_channel = true;
nca.ndp = ndp;
nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN; switch (nd->state) { case ncsi_dev_state_probe:
nd->state = ncsi_dev_state_probe_deselect;
fallthrough; case ncsi_dev_state_probe_deselect:
ndp->pending_req_num = 8;
/* Deselect all possible packages */
nca.type = NCSI_PKT_CMD_DP;
nca.channel = NCSI_RESERVED_CHANNEL; for (index = 0; index < 8; index++) {
nca.package = index;
ret = ncsi_xmit_cmd(&nca); if (ret) goto error;
}
nd->state = ncsi_dev_state_probe_package; break; case ncsi_dev_state_probe_package: if (ndp->package_probe_id >= 8) { /* Last package probed, finishing */
ndp->flags |= NCSI_DEV_PROBED; break;
}
ndp->pending_req_num = 1;
nca.type = NCSI_PKT_CMD_SP;
nca.bytes[0] = 1;
nca.package = ndp->package_probe_id;
nca.channel = NCSI_RESERVED_CHANNEL;
ret = ncsi_xmit_cmd(&nca); if (ret) goto error;
nd->state = ncsi_dev_state_probe_channel; break; case ncsi_dev_state_probe_channel:
ndp->active_package = ncsi_find_package(ndp,
ndp->package_probe_id); if (!ndp->active_package) { /* No response */
nd->state = ncsi_dev_state_probe_dp;
schedule_work(&ndp->work); break;
}
nd->state = ncsi_dev_state_probe_cis; if (IS_ENABLED(CONFIG_NCSI_OEM_CMD_GET_MAC) &&
ndp->mlx_multi_host)
nd->state = ncsi_dev_state_probe_mlx_gma;
schedule_work(&ndp->work); break; case ncsi_dev_state_probe_mlx_gma:
ndp->pending_req_num = 1;
nca.type = NCSI_PKT_CMD_OEM;
nca.package = ndp->active_package->id;
nca.channel = 0;
ret = ncsi_oem_gma_handler_mlx(&nca); if (ret) goto error;
nd->state = ncsi_dev_state_probe_mlx_smaf; break; case ncsi_dev_state_probe_mlx_smaf:
ndp->pending_req_num = 1;
nca.type = NCSI_PKT_CMD_OEM;
nca.package = ndp->active_package->id;
nca.channel = 0;
ret = ncsi_oem_smaf_mlx(&nca); if (ret) goto error;
nd->state = ncsi_dev_state_probe_cis; break; case ncsi_dev_state_probe_keep_phy:
ndp->pending_req_num = 1;
nca.type = NCSI_PKT_CMD_OEM;
nca.package = ndp->active_package->id;
nca.channel = 0;
ret = ncsi_oem_keep_phy_intel(&nca); if (ret) goto error;
nd->state = ncsi_dev_state_probe_gvi; break; case ncsi_dev_state_probe_cis: case ncsi_dev_state_probe_gvi: case ncsi_dev_state_probe_gc: case ncsi_dev_state_probe_gls:
np = ndp->active_package;
ndp->pending_req_num = 1;
/* Clear initial state Retrieve version, capability or link status */ if (nd->state == ncsi_dev_state_probe_cis)
nca.type = NCSI_PKT_CMD_CIS; elseif (nd->state == ncsi_dev_state_probe_gvi)
nca.type = NCSI_PKT_CMD_GVI; elseif (nd->state == ncsi_dev_state_probe_gc)
nca.type = NCSI_PKT_CMD_GC; else
nca.type = NCSI_PKT_CMD_GLS;
/* Channels may be busy, mark dirty instead of * kicking if; * a) not ACTIVE (configured) * b) in the channel_queue (to be configured) * c) it's ndev is in the config state
*/ if (nc->state != NCSI_CHANNEL_ACTIVE) { if ((ndp->ndev.state & 0xff00) ==
ncsi_dev_state_config ||
!list_empty(&nc->link)) {
netdev_dbg(nd->dev, "NCSI: channel %p marked dirty\n",
nc);
nc->reconfigure_needed = true;
}
spin_unlock_irqrestore(&nc->lock, flags); continue;
}
int ncsi_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
{ struct ncsi_dev_priv *ndp; unsignedint n_vids = 0; struct vlan_vid *vlan; struct ncsi_dev *nd; bool found = false;
if (vid == 0) return 0;
nd = ncsi_find_dev(dev); if (!nd) {
netdev_warn(dev, "NCSI: No net_device?\n"); return 0;
}
ndp = TO_NCSI_DEV_PRIV(nd);
/* Add the VLAN id to our internal list */
list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
n_vids++; if (vlan->vid == vid) {
netdev_dbg(dev, "NCSI: vid %u already registered\n",
vid); return 0;
}
} if (n_vids >= NCSI_MAX_VLAN_VIDS) {
netdev_warn(dev, "tried to add vlan id %u but NCSI max already registered (%u)\n",
vid, NCSI_MAX_VLAN_VIDS); return -ENOSPC;
}
vlan = kzalloc(sizeof(*vlan), GFP_KERNEL); if (!vlan) return -ENOMEM;
return found ? ncsi_process_next_channel(ndp) : 0;
}
EXPORT_SYMBOL_GPL(ncsi_vlan_rx_add_vid);
int ncsi_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
{ struct vlan_vid *vlan, *tmp; struct ncsi_dev_priv *ndp; struct ncsi_dev *nd; bool found = false;
if (vid == 0) return 0;
nd = ncsi_find_dev(dev); if (!nd) {
netdev_warn(dev, "NCSI: no net_device?\n"); return 0;
}
ndp = TO_NCSI_DEV_PRIV(nd);
/* Remove the VLAN id from our internal list */
list_for_each_entry_safe(vlan, tmp, &ndp->vlan_vids, list) if (vlan->vid == vid) {
netdev_dbg(dev, "NCSI: vid %u found, removing\n", vid);
list_del_rcu(&vlan->list);
found = true;
kfree(vlan);
}
if (!found) {
netdev_err(dev, "NCSI: vid %u wasn't registered!\n", vid); return -EINVAL;
}
found = ncsi_kick_channels(ndp) != 0;
return found ? ncsi_process_next_channel(ndp) : 0;
}
EXPORT_SYMBOL_GPL(ncsi_vlan_rx_kill_vid);
/* Stop the channel monitor on any active channels. Don't reset the * channel state so we know which were active when ncsi_start_dev() * is next called.
*/
NCSI_FOR_EACH_PACKAGE(ndp, np) {
NCSI_FOR_EACH_CHANNEL(np, nc) {
ncsi_stop_channel_monitor(nc);
if (!(ndp->flags & NCSI_DEV_RESET)) { /* Haven't been called yet, check states */ switch (nd->state & ncsi_dev_state_major) { case ncsi_dev_state_registered: case ncsi_dev_state_probe: /* Not even probed yet - do nothing */
spin_unlock_irqrestore(&ndp->lock, flags); return 0; case ncsi_dev_state_suspend: case ncsi_dev_state_config: /* Wait for the channel to finish its suspend/config * operation; once it finishes it will check for * NCSI_DEV_RESET and reset the state.
*/
ndp->flags |= NCSI_DEV_RESET;
spin_unlock_irqrestore(&ndp->lock, flags); return 0;
}
} else { switch (nd->state) { case ncsi_dev_state_suspend_done: case ncsi_dev_state_config_done: case ncsi_dev_state_functional: /* Ok */ break; default: /* Current reset operation happening */
spin_unlock_irqrestore(&ndp->lock, flags); return 0;
}
}
if (!list_empty(&ndp->channel_queue)) { /* Clear any channel queue we may have interrupted */
list_for_each_entry_safe(nc, tmp, &ndp->channel_queue, link)
list_del_init(&nc->link);
}
spin_unlock_irqrestore(&ndp->lock, flags);
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.