#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 *whentheVirtualInterfaceattachedtothePortisdown.So *ifit'sdown,let'sgrabanychanges.
*/ if (!netif_running(dev))
(void)t4_update_port_info(pi);
/* If the firmware rejects the Link Configuration request, back out *thechangesandreporttheerror.
*/
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 *alwayssupportIEEE802.3"automatic"selectionofLinkFECtypeif *anyFECissupported.
*/
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 *ethtoolvalues.
*/
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.Ifitfails,reverttheattemptedchange.
*/
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;
}
/* Return the current global Adapter SGE Doorbell Queue Timer Tick for all *EthernetTXQueues.
*/ 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)) return0;
return adap->sge.dbqtimer_tick;
}
/* Return the SGE Doorbell Queue Timer Value for the Ethernet TX Queues *associatedwithaNetworkDevice.
*/ 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)) return0;
/* 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.Thisisthefundamental"Tick"thatsetsthescaleofvalueswhich *canbeused.IndividualEthernetTXQueuesindexintoarelativelysmall *arrayofTickMultipliers.ChangingthebaseTickwillthuschangeallof *theresultingTimerValuesassociatedwiththosemultipliersforall *EthernetTXQueues.
*/ 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)) return0;
/* return early if it's the same Timer Tick we're already using */ if (s->dbqtimer_tick == usecs) return0;
/* 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 *associatedwithaNetworkDevice.Thereisarelativelysmallarrayof *possibleTimerValuessoweneedtopicktheclosestvalueavailable.
*/ 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)) return0;
/* 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. *WeusethesameTimerIndexforalloftheTXQueuesforan *interfacesoit'sonlynecessarytocheckthefirstone.
*/
txq = &s->ethtxq[pi->first_qset]; if (txq->dbqtimerix == min_timerix) return0;
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;
} return0;
}
/* Set the global Adapter SGE Doorbell Queue Timer Tick for all Ethernet TX *QueuesandtheTimerValuefortheEthernetTXQueuesassociatedwitha *NetworkDevice.SincechangingtheglobalTickchangesallofthe *availableTimerValues,weneedtodothisfirstbeforeselectingthe *resultingclosestTimerValue.Moreover,sincetheTickisglobal, *changingitaffectstheTimerValuesforallNetworkDevicesonthe *adapter.So,beforechangingtheTick,wegraballofthecurrentTimer *ValuesforotherNetworkDevicesonthisAdapterandthenattempttoselect *newTimerValueswhichareclosetotheoldvalues...
*/ 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 *thenewoneforthisNetworkInterface.
*/
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;
}
/* The next two routines implement eeprom read/write from physical addresses.
*/
static int eeprom_rd_phys(struct adapter *adap, unsigned int phys_addr, u32 *v)
{
int vaddr = t4_eeprom_ptov(phys_addr, adap->pf, EEPROMPFSIZE);
static int cxgb4_ethtool_flash_phy(struct net_device *netdev,
const u8 *data, u32 size)
{
struct adapter *adap = netdev2adap(netdev);
int 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;
}
return 0;
}
static int cxgb4_ethtool_flash_fw(struct net_device *netdev,
const u8 *data, u32 size)
{
struct adapter *adap = netdev2adap(netdev);
unsigned int mbox = PCIE_FW_MASTER_M + 1;
int 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");
return ret;
}
static int cxgb4_ethtool_flash_region(struct net_device *netdev,
const u8 *data, u32 size, u32 region)
{
struct adapter *adap = netdev2adap(netdev);
int ret;
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;
}
static int cxgb4_ethtool_get_flash_region(const u8 *data, u32 *size)
{
if (!cxgb4_validate_fw_image(data, size))
return CXGB4_ETHTOOL_FLASH_FW;
if (!cxgb4_validate_boot_image(data, size))
return CXGB4_ETHTOOL_FLASH_BOOT;
if (!cxgb4_validate_phy_image(data, size))
return CXGB4_ETHTOOL_FLASH_PHY;
if (!cxgb4_validate_bootcfg_image(data, size))
return CXGB4_ETHTOOL_FLASH_BOOTCFG;
return -EOPNOTSUPP;
}
static int set_flash(struct net_device *netdev, struct ethtool_flash *ef)
{
struct adapter *adap = netdev2adap(netdev);
const struct firmware *fw;
unsigned int master;
u8 master_vld = 0;
const u8 *fw_data;
size_t fw_size;
u32 size = 0;
u32 pcie_fw;
int region;
int ret;
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;
static int get_rss_table(struct net_device *dev,
struct ethtool_rxfh_param *rxfh)
{
const struct port_info *pi = netdev_priv(dev);
unsigned int n = pi->rss_size;
rxfh->hfunc = ETH_RSS_HASH_TOP;
if (!rxfh->indir)
return 0;
while (n--)
rxfh->indir[n] = pi->rss[n];
return 0;
}
static int set_rss_table(struct net_device *dev,
struct ethtool_rxfh_param *rxfh,
struct netlink_ext_ack *extack)
{
unsigned int i;
struct port_info *pi = netdev_priv(dev);
/* 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);
goto exit;
}
fs.hitcnts = 1;
ret = cxgb4_flow_rule_replace(netdev, flow->rule, cmd->fs.location,
NULL, &fs, &tid);
if (ret)
goto free;
static int 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;
}
return ret;
}
static int set_dump(struct net_device *dev, struct ethtool_dump *eth_dump)
{
struct adapter *adapter = netdev2adap(dev);
u32 len = 0;
len = sizeof(struct cudbg_hdr) +
sizeof(struct cudbg_entity_hdr) * CUDBG_MAX_ENTITY;
len += cxgb4_get_dump_length(adapter, eth_dump->flag);
static bool cxgb4_fw_mod_type_info_available(unsigned int 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;
}
static int 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);
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.68Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-12)
¤
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.