/* port stats maintained per queue of the port. They should be in the same * order as in stats_strings above.
*/ struct queue_port_stats {
u64 tso;
u64 uso;
u64 tx_csum;
u64 rx_csum;
u64 vlan_ex;
u64 vlan_ins;
u64 gro_pkts;
u64 gro_merged; #if IS_ENABLED(CONFIG_CHELSIO_TLS_DEVICE)
u64 tx_tls_encrypted_packets;
u64 tx_tls_encrypted_bytes;
u64 tx_tls_ctx;
u64 tx_tls_ooo;
u64 tx_tls_skip_no_sync_data;
u64 tx_tls_drop_no_sync_data;
u64 tx_tls_drop_bypass_req; #endif
};
/** * speed_to_fw_caps - translate Port Speed to Firmware Port Capabilities * @speed: speed in Kb/s * * Translates a specific Port Speed into a Firmware Port Capabilities * value.
*/ staticunsignedint speed_to_fw_caps(int speed)
{ if (speed == 100) return FW_PORT_CAP32_SPEED_100M; if (speed == 1000) return FW_PORT_CAP32_SPEED_1G; if (speed == 10000) return FW_PORT_CAP32_SPEED_10G; if (speed == 25000) return FW_PORT_CAP32_SPEED_25G; if (speed == 40000) return FW_PORT_CAP32_SPEED_40G; if (speed == 50000) return FW_PORT_CAP32_SPEED_50G; if (speed == 100000) return FW_PORT_CAP32_SPEED_100G; if (speed == 200000) return FW_PORT_CAP32_SPEED_200G; if (speed == 400000) return FW_PORT_CAP32_SPEED_400G; return 0;
}
/** * fw_caps_to_lmm - translate Firmware to ethtool Link Mode Mask * @port_type: Firmware Port Type * @fw_caps: Firmware Port Capabilities * @link_mode_mask: ethtool Link Mode Mask * * Translate a Firmware Port Capabilities specification to an ethtool * Link Mode Mask.
*/ staticvoid fw_caps_to_lmm(enum fw_port_type port_type,
fw_port_cap32_t fw_caps, unsignedlong *link_mode_mask)
{ #define SET_LMM(__lmm_name) \ do { \
__set_bit(ETHTOOL_LINK_MODE_ ## __lmm_name ## _BIT, \
link_mode_mask); \
} while (0)
#define FW_CAPS_TO_LMM(__fw_name, __lmm_name) \ do { \ if (fw_caps & FW_PORT_CAP32_ ## __fw_name) \
SET_LMM(__lmm_name); \
} while (0)
switch (port_type) { case FW_PORT_TYPE_BT_SGMII: case FW_PORT_TYPE_BT_XFI: case FW_PORT_TYPE_BT_XAUI:
SET_LMM(TP);
FW_CAPS_TO_LMM(SPEED_100M, 100baseT_Full);
FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseT_Full); break;
case FW_PORT_TYPE_KX4: case FW_PORT_TYPE_KX:
SET_LMM(Backplane);
FW_CAPS_TO_LMM(SPEED_1G, 1000baseKX_Full);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseKX4_Full); break;
case FW_PORT_TYPE_KR:
SET_LMM(Backplane);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full); break;
case FW_PORT_TYPE_BP_AP:
SET_LMM(Backplane);
FW_CAPS_TO_LMM(SPEED_1G, 1000baseKX_Full);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseR_FEC);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full); break;
case FW_PORT_TYPE_FIBER_XFI: case FW_PORT_TYPE_FIBER_XAUI: case FW_PORT_TYPE_SFP: case FW_PORT_TYPE_QSFP_10G: case FW_PORT_TYPE_QSA:
SET_LMM(FIBRE);
FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseT_Full); break;
case FW_PORT_TYPE_BP40_BA: case FW_PORT_TYPE_QSFP:
SET_LMM(FIBRE);
FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseT_Full);
FW_CAPS_TO_LMM(SPEED_40G, 40000baseSR4_Full); break;
case FW_PORT_TYPE_CR_QSFP: case FW_PORT_TYPE_SFP28:
SET_LMM(FIBRE);
FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseT_Full);
FW_CAPS_TO_LMM(SPEED_25G, 25000baseCR_Full); break;
case FW_PORT_TYPE_KR_SFP28:
SET_LMM(Backplane);
FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full);
FW_CAPS_TO_LMM(SPEED_25G, 25000baseKR_Full); break;
case FW_PORT_TYPE_KR_XLAUI:
SET_LMM(Backplane);
FW_CAPS_TO_LMM(SPEED_1G, 1000baseKX_Full);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full);
FW_CAPS_TO_LMM(SPEED_40G, 40000baseKR4_Full); break;
case FW_PORT_TYPE_CR2_QSFP:
SET_LMM(FIBRE);
FW_CAPS_TO_LMM(SPEED_50G, 50000baseSR2_Full); break;
case FW_PORT_TYPE_KR4_100G: case FW_PORT_TYPE_CR4_QSFP:
SET_LMM(FIBRE);
FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full);
FW_CAPS_TO_LMM(SPEED_40G, 40000baseSR4_Full);
FW_CAPS_TO_LMM(SPEED_25G, 25000baseCR_Full);
FW_CAPS_TO_LMM(SPEED_50G, 50000baseCR2_Full);
FW_CAPS_TO_LMM(SPEED_100G, 100000baseCR4_Full); break;
/* For the nonce, the Firmware doesn't send up Port State changes * when the Virtual Interface attached to the Port is down. So * if it's down, let's grab any changes.
*/ if (!netif_running(dev))
(void)t4_update_port_info(pi);
/* If the firmware rejects the Link Configuration request, back out * the changes and report the error.
*/
ret = t4_link_l1cfg(pi->adapter, pi->adapter->mbox, pi->tx_chan, lc); if (ret)
*lc = old_lc;
return ret;
}
/* Translate the Firmware FEC value into the ethtool value. */ staticinlineunsignedint fwcap_to_eth_fec(unsignedint fw_fec)
{ unsignedint eth_fec = 0;
if (fw_fec & FW_PORT_CAP32_FEC_RS)
eth_fec |= ETHTOOL_FEC_RS; if (fw_fec & FW_PORT_CAP32_FEC_BASER_RS)
eth_fec |= ETHTOOL_FEC_BASER;
/* if nothing is set, then FEC is off */ if (!eth_fec)
eth_fec = ETHTOOL_FEC_OFF;
return eth_fec;
}
/* Translate Common Code FEC value into ethtool value. */ staticinlineunsignedint cc_to_eth_fec(unsignedint cc_fec)
{ unsignedint eth_fec = 0;
if (cc_fec & FEC_AUTO)
eth_fec |= ETHTOOL_FEC_AUTO; if (cc_fec & FEC_RS)
eth_fec |= ETHTOOL_FEC_RS; if (cc_fec & FEC_BASER_RS)
eth_fec |= ETHTOOL_FEC_BASER;
/* if nothing is set, then FEC is off */ if (!eth_fec)
eth_fec = ETHTOOL_FEC_OFF;
return eth_fec;
}
/* Translate ethtool FEC value into Common Code value. */ staticinlineunsignedint eth_to_cc_fec(unsignedint eth_fec)
{ unsignedint cc_fec = 0;
if (eth_fec & ETHTOOL_FEC_OFF) return cc_fec;
if (eth_fec & ETHTOOL_FEC_AUTO)
cc_fec |= FEC_AUTO; if (eth_fec & ETHTOOL_FEC_RS)
cc_fec |= FEC_RS; if (eth_fec & ETHTOOL_FEC_BASER)
cc_fec |= FEC_BASER_RS;
/* Translate the Firmware FEC Support into the ethtool value. We * always support IEEE 802.3 "automatic" selection of Link FEC type if * any FEC is supported.
*/
fec->fec = fwcap_to_eth_fec(lc->pcaps); if (fec->fec != ETHTOOL_FEC_OFF)
fec->fec |= ETHTOOL_FEC_AUTO;
/* Translate the current internal FEC parameters into the * ethtool values.
*/
fec->active_fec = cc_to_eth_fec(lc->fec);
/* Save old Link Configuration in case the L1 Configure below * fails.
*/
old_lc = *lc;
/* Try to perform the L1 Configure and return the result of that * effort. If it fails, revert the attempted change.
*/
lc->requested_fec = eth_to_cc_fec(fec->fec);
ret = t4_link_l1cfg(pi->adapter, pi->adapter->mbox,
pi->tx_chan, lc); if (ret)
*lc = old_lc; return ret;
}
if (adapter->flags & CXGB4_FULL_INIT_DONE) return -EBUSY;
for (i = 0; i < pi->nqsets; ++i) {
s->ethtxq[pi->first_qset + i].q.size = e->tx_pending;
s->ethrxq[pi->first_qset + i].fl.size = e->rx_pending + 8;
s->ethrxq[pi->first_qset + i].rspq.size = e->rx_mini_pending;
} return 0;
}
/** * set_rx_intr_params - set a net devices's RX interrupt holdoff paramete! * @dev: the network device * @us: the hold-off time in us, or 0 to disable timer * @cnt: the hold-off packet count, or 0 to disable counter * * Set the RX interrupt hold-off parameters for a network device.
*/ staticint set_rx_intr_params(struct net_device *dev, unsignedint us, unsignedint cnt)
{ int i, err; struct port_info *pi = netdev_priv(dev); struct adapter *adap = pi->adapter; struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset];
for (i = 0; i < pi->nqsets; i++, q++) {
err = cxgb4_set_rspq_intr_params(&q->rspq, us, cnt); if (err) return err;
} return 0;
}
/* Return the current global Adapter SGE Doorbell Queue Timer Tick for all * Ethernet TX Queues.
*/ staticint get_dbqtimer_tick(struct net_device *dev)
{ struct port_info *pi = netdev_priv(dev); struct adapter *adap = pi->adapter;
if (!(adap->flags & CXGB4_SGE_DBQ_TIMER)) return 0;
return adap->sge.dbqtimer_tick;
}
/* Return the SGE Doorbell Queue Timer Value for the Ethernet TX Queues * associated with a Network Device.
*/ staticint get_dbqtimer(struct net_device *dev)
{ struct port_info *pi = netdev_priv(dev); struct adapter *adap = pi->adapter; struct sge_eth_txq *txq;
txq = &adap->sge.ethtxq[pi->first_qset];
if (!(adap->flags & CXGB4_SGE_DBQ_TIMER)) return 0;
/* all of the TX Queues use the same Timer Index */ return adap->sge.dbqtimer_val[txq->dbqtimerix];
}
/* Set the global Adapter SGE Doorbell Queue Timer Tick for all Ethernet TX * Queues. This is the fundamental "Tick" that sets the scale of values which * can be used. Individual Ethernet TX Queues index into a relatively small * array of Tick Multipliers. Changing the base Tick will thus change all of * the resulting Timer Values associated with those multipliers for all * Ethernet TX Queues.
*/ staticint set_dbqtimer_tick(struct net_device *dev, int usecs)
{ struct port_info *pi = netdev_priv(dev); struct adapter *adap = pi->adapter; struct sge *s = &adap->sge;
u32 param, val; int ret;
if (!(adap->flags & CXGB4_SGE_DBQ_TIMER)) return 0;
/* return early if it's the same Timer Tick we're already using */ if (s->dbqtimer_tick == usecs) return 0;
/* attempt to set the new Timer Tick value */
param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) |
FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_DBQ_TIMERTICK));
val = usecs;
ret = t4_set_params(adap, adap->mbox, adap->pf, 0, 1, ¶m, &val); if (ret) return ret;
s->dbqtimer_tick = usecs;
/* if successful, reread resulting dependent Timer values */
ret = t4_read_sge_dbqtimers(adap, ARRAY_SIZE(s->dbqtimer_val),
s->dbqtimer_val); return ret;
}
/* Set the SGE Doorbell Queue Timer Value for the Ethernet TX Queues * associated with a Network Device. There is a relatively small array of * possible Timer Values so we need to pick the closest value available.
*/ staticint set_dbqtimer(struct net_device *dev, int usecs)
{ int qix, timerix, min_timerix, delta, min_delta; struct port_info *pi = netdev_priv(dev); struct adapter *adap = pi->adapter; struct sge *s = &adap->sge; struct sge_eth_txq *txq;
u32 param, val; int ret;
if (!(adap->flags & CXGB4_SGE_DBQ_TIMER)) return 0;
/* Find the SGE Doorbell Timer Value that's closest to the requested * value.
*/
min_delta = INT_MAX;
min_timerix = 0; for (timerix = 0; timerix < ARRAY_SIZE(s->dbqtimer_val); timerix++) {
delta = s->dbqtimer_val[timerix] - usecs; if (delta < 0)
delta = -delta; if (delta < min_delta) {
min_delta = delta;
min_timerix = timerix;
}
}
/* Return early if it's the same Timer Index we're already using. * We use the same Timer Index for all of the TX Queues for an * interface so it's only necessary to check the first one.
*/
txq = &s->ethtxq[pi->first_qset]; if (txq->dbqtimerix == min_timerix) return 0;
for (qix = 0; qix < pi->nqsets; qix++, txq++) { if (adap->flags & CXGB4_FULL_INIT_DONE) {
param =
(FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ) |
FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DMAQ_EQ_TIMERIX) |
FW_PARAMS_PARAM_YZ_V(txq->q.cntxt_id));
val = min_timerix;
ret = t4_set_params(adap, adap->mbox, adap->pf, 0,
1, ¶m, &val); if (ret) return ret;
}
txq->dbqtimerix = min_timerix;
} return 0;
}
/* Set the global Adapter SGE Doorbell Queue Timer Tick for all Ethernet TX * Queues and the Timer Value for the Ethernet TX Queues associated with a * Network Device. Since changing the global Tick changes all of the * available Timer Values, we need to do this first before selecting the * resulting closest Timer Value. Moreover, since the Tick is global, * changing it affects the Timer Values for all Network Devices on the * adapter. So, before changing the Tick, we grab all of the current Timer * Values for other Network Devices on this Adapter and then attempt to select * new Timer Values which are close to the old values ...
*/ staticint set_dbqtimer_tickval(struct net_device *dev, int tick_usecs, int timer_usecs)
{ struct port_info *pi = netdev_priv(dev); struct adapter *adap = pi->adapter; int timer[MAX_NPORTS]; unsignedint port; int ret;
/* Grab the other adapter Network Interface current timers and fill in * the new one for this Network Interface.
*/
for_each_port(adap, port) if (port == pi->port_id)
timer[port] = timer_usecs; else
timer[port] = get_dbqtimer(adap->port[port]);
/* Change the global Tick first ... */
ret = set_dbqtimer_tick(dev, tick_usecs); if (ret) return ret;
/* ... and then set all of the Network Interface Timer Values ... */
for_each_port(adap, port) {
ret = set_dbqtimer(adap->port[port], timer[port]); if (ret) return ret;
}
ret = cxgb4_validate_phy_image(data, NULL); if (ret) {
dev_err(adap->pdev_dev, "PHY signature mismatch\n"); return ret;
}
/* We have to RESET the chip/firmware because we need the * chip in uninitialized state for loading new PHY image. * Otherwise, the running firmware will only store the PHY * image in local RAM which will be lost after next reset.
*/
ret = t4_fw_reset(adap, adap->mbox, PIORSTMODE_F | PIORST_F); if (ret < 0) {
dev_err(adap->pdev_dev, "Set FW to RESET for flashing PHY FW failed. ret: %d\n",
ret); return ret;
}
ret = t4_load_phy_fw(adap, MEMWIN_NIC, NULL, data, size); if (ret < 0) {
dev_err(adap->pdev_dev, "Failed to load PHY FW. ret: %d\n",
ret); return ret;
}
/* If the adapter has been fully initialized then we'll go ahead and * try to get the firmware's cooperation in upgrading to the new * firmware image otherwise we'll try to do the entire job from the * host ... and we always "force" the operation in this path.
*/ if (adap->flags & CXGB4_FULL_INIT_DONE)
mbox = adap->mbox;
ret = t4_fw_upgrade(adap, mbox, data, size, 1); if (ret)
dev_err(adap->pdev_dev, "Failed to flash firmware\n");
switch (region) { case CXGB4_ETHTOOL_FLASH_FW:
ret = cxgb4_ethtool_flash_fw(netdev, data, size); break; case CXGB4_ETHTOOL_FLASH_PHY:
ret = cxgb4_ethtool_flash_phy(netdev, data, size); break; case CXGB4_ETHTOOL_FLASH_BOOT:
ret = cxgb4_ethtool_flash_boot(netdev, data, size); break; case CXGB4_ETHTOOL_FLASH_BOOTCFG:
ret = cxgb4_ethtool_flash_bootcfg(netdev, data, size); break; default:
ret = -EOPNOTSUPP; break;
}
pcie_fw = t4_read_reg(adap, PCIE_FW_A);
master = PCIE_FW_MASTER_G(pcie_fw); if (pcie_fw & PCIE_FW_MASTER_VLD_F)
master_vld = 1; /* if csiostor is the master return */ if (master_vld && (master != adap->pf)) {
dev_warn(adap->pdev_dev, "cxgb4 driver needs to be loaded as MASTER to support FW flash\n"); return -EOPNOTSUPP;
}
ef->data[sizeof(ef->data) - 1] = '\0';
ret = request_firmware(&fw, ef->data, adap->pdev_dev); if (ret < 0) return ret;
fw_data = fw->data;
fw_size = fw->size; if (ef->region == ETHTOOL_FLASH_ALL_REGIONS) { while (fw_size > 0) {
size = 0;
region = cxgb4_ethtool_get_flash_region(fw_data, &size); if (region < 0 || !size) {
ret = region; goto out_free_fw;
}
ret = cxgb4_ethtool_flash_region(netdev, fw_data, size,
region); if (ret) goto out_free_fw;
/* We require at least one supported parameter to be changed and no * change in any of the unsupported parameters
*/ if (rxfh->key ||
(rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE &&
rxfh->hfunc != ETH_RSS_HASH_TOP)) return -EOPNOTSUPP; if (!rxfh->indir) return 0;
/* Interface must be brought up atleast once */ if (pi->adapter->flags & CXGB4_FULL_INIT_DONE) { for (i = 0; i < pi->rss_size; i++)
pi->rss[i] = rxfh->indir[i];
if (!(adapter->flags & CXGB4_FULL_INIT_DONE)) return -EAGAIN; /* can still change nfilters */
if (!adapter->ethtool_filters) return -EOPNOTSUPP;
if (cmd->fs.location >= adapter->ethtool_filters->nentries) {
dev_err(adapter->pdev_dev, "Location must be < %u",
adapter->ethtool_filters->nentries); return -ERANGE;
}
if (!(adapter->flags & CXGB4_FULL_INIT_DONE)) return -EAGAIN; /* can still change nfilters */
if (!adapter->ethtool_filters) return -EOPNOTSUPP;
if (cmd->fs.location >= adapter->ethtool_filters->nentries) {
dev_err(adapter->pdev_dev, "Location must be < %u",
adapter->ethtool_filters->nentries); return -ERANGE;
}
if (test_bit(cmd->fs.location,
adapter->ethtool_filters->port[pi->port_id].bmap)) return -EEXIST;
memset(&fs, 0, sizeof(fs));
input.fs = &cmd->fs;
flow = ethtool_rx_flow_rule_create(&input); if (IS_ERR(flow)) {
ret = PTR_ERR(flow); gotoexit;
}
fs.hitcnts = 1;
ret = cxgb4_flow_rule_replace(netdev, flow->rule, cmd->fs.location,
NULL, &fs, &tid); if (ret) goto free;
staticint set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
{ int ret = -EOPNOTSUPP;
switch (cmd->cmd) { case ETHTOOL_SRXCLSRLINS:
ret = cxgb4_ntuple_set_filter(dev, cmd); break; case ETHTOOL_SRXCLSRLDEL:
ret = cxgb4_ntuple_del_filter(dev, cmd); break; default: break;
}
staticbool cxgb4_fw_mod_type_info_available(unsignedint fw_mod_type)
{ /* Read port module EEPROM as long as it is plugged-in and * safe to read.
*/ return (fw_mod_type != FW_PORT_MOD_TYPE_NONE &&
fw_mod_type != FW_PORT_MOD_TYPE_ERROR);
}
if (!cxgb4_fw_mod_type_info_available(pi->mod_type)) return -EINVAL;
switch (pi->port_type) { case FW_PORT_TYPE_SFP: case FW_PORT_TYPE_QSA: case FW_PORT_TYPE_SFP28:
ret = t4_i2c_rd(adapter, adapter->mbox, pi->tx_chan,
I2C_DEV_ADDR_A0, SFF_8472_COMP_ADDR,
SFF_8472_COMP_LEN, &sff8472_comp); if (ret) return ret;
ret = t4_i2c_rd(adapter, adapter->mbox, pi->tx_chan,
I2C_DEV_ADDR_A0, SFP_DIAG_TYPE_ADDR,
SFP_DIAG_TYPE_LEN, &sff_diag_type); if (ret) return ret;
case FW_PORT_TYPE_QSFP: case FW_PORT_TYPE_QSFP_10G: case FW_PORT_TYPE_CR_QSFP: case FW_PORT_TYPE_CR2_QSFP: case FW_PORT_TYPE_CR4_QSFP:
ret = t4_i2c_rd(adapter, adapter->mbox, pi->tx_chan,
I2C_DEV_ADDR_A0, SFF_REV_ADDR,
SFF_REV_LEN, &sff_rev); /* For QSFP type ports, revision value >= 3 * means the SFP is 8636 compliant.
*/ if (ret) return ret; if (sff_rev >= 0x3) {
modinfo->type = ETH_MODULE_SFF_8636;
modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
} else {
modinfo->type = ETH_MODULE_SFF_8436;
modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
} break;
default: return -EINVAL;
}
return 0;
}
staticint cxgb4_get_module_eeprom(struct net_device *dev, struct ethtool_eeprom *eprom, u8 *data)
{ int ret = 0, offset = eprom->offset, len = eprom->len; struct port_info *pi = netdev_priv(dev); struct adapter *adapter = pi->adapter;
memset(data, 0, eprom->len); if (offset + len <= I2C_PAGE_SIZE) return t4_i2c_rd(adapter, adapter->mbox, pi->tx_chan,
I2C_DEV_ADDR_A0, offset, len, data);
/* offset + len spans 0xa0 and 0xa1 pages */ if (offset <= I2C_PAGE_SIZE) { /* read 0xa0 page */
len = I2C_PAGE_SIZE - offset;
ret = t4_i2c_rd(adapter, adapter->mbox, pi->tx_chan,
I2C_DEV_ADDR_A0, offset, len, data); if (ret) return ret;
offset = I2C_PAGE_SIZE; /* Remaining bytes to be read from second page = * Total length - bytes read from first page
*/
len = eprom->len - len;
} /* Read additional optical diagnostics from page 0xa2 if supported */ return t4_i2c_rd(adapter, adapter->mbox, pi->tx_chan, I2C_DEV_ADDR_A2,
offset, len, &data[eprom->len - len]);
}
if (eth_filter_info) { for (i = 0; i < adap->params.nports; i++) {
kvfree(eth_filter_info[i].loc_array);
bitmap_free(eth_filter_info[i].bmap);
}
kfree(eth_filter_info);
}
for (i = 0; i < adap->params.nports; i++) {
eth_filter->port[i].loc_array = kvzalloc(nentries, GFP_KERNEL); if (!eth_filter->port[i].loc_array) {
ret = -ENOMEM; goto free_eth_finfo;
}
eth_filter->port[i].bmap = bitmap_zalloc(nentries, GFP_KERNEL); if (!eth_filter->port[i].bmap) {
ret = -ENOMEM;
kvfree(eth_filter->port[i].loc_array); goto free_eth_finfo;
}
}
adap->ethtool_filters = eth_filter; return 0;
free_eth_finfo: while (i-- > 0) {
bitmap_free(eth_filter->port[i].bmap);
kvfree(eth_filter->port[i].loc_array);
}
kfree(eth_filter_info);
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.