Merge branch 'ionic-fw-recovery'
Shannon Nelson says: ==================== ionic: updates for stable FW recovery Recent FW work has tightened up timings in its error recovery handling and uncovered weaknesses in the driver's responses, so this is a set of updates primarily for better handling of the firmware's recovery mechanisms. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
8a0de61c40
|
@ -18,7 +18,7 @@ struct ionic_lif;
|
|||
#define PCI_DEVICE_ID_PENSANDO_IONIC_ETH_PF 0x1002
|
||||
#define PCI_DEVICE_ID_PENSANDO_IONIC_ETH_VF 0x1003
|
||||
|
||||
#define DEVCMD_TIMEOUT 10
|
||||
#define DEVCMD_TIMEOUT 5
|
||||
#define IONIC_ADMINQ_TIME_SLICE msecs_to_jiffies(100)
|
||||
|
||||
#define IONIC_PHC_UPDATE_NS 10000000000 /* 10s in nanoseconds */
|
||||
|
@ -78,6 +78,9 @@ void ionic_adminq_netdev_err_print(struct ionic_lif *lif, u8 opcode,
|
|||
u8 status, int err);
|
||||
|
||||
int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_wait);
|
||||
int ionic_dev_cmd_wait_nomsg(struct ionic *ionic, unsigned long max_wait);
|
||||
void ionic_dev_cmd_dev_err_print(struct ionic *ionic, u8 opcode, u8 status,
|
||||
int err);
|
||||
int ionic_set_dma_mask(struct ionic *ionic);
|
||||
int ionic_setup(struct ionic *ionic);
|
||||
|
||||
|
@ -89,4 +92,6 @@ int ionic_port_identify(struct ionic *ionic);
|
|||
int ionic_port_init(struct ionic *ionic);
|
||||
int ionic_port_reset(struct ionic *ionic);
|
||||
|
||||
const char *ionic_vf_attr_to_str(enum ionic_vf_attr attr);
|
||||
|
||||
#endif /* _IONIC_H_ */
|
||||
|
|
|
@ -109,8 +109,8 @@ void ionic_bus_unmap_dbpage(struct ionic *ionic, void __iomem *page)
|
|||
|
||||
static void ionic_vf_dealloc_locked(struct ionic *ionic)
|
||||
{
|
||||
struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_STATSADDR };
|
||||
struct ionic_vf *v;
|
||||
dma_addr_t dma = 0;
|
||||
int i;
|
||||
|
||||
if (!ionic->vfs)
|
||||
|
@ -120,9 +120,8 @@ static void ionic_vf_dealloc_locked(struct ionic *ionic)
|
|||
v = &ionic->vfs[i];
|
||||
|
||||
if (v->stats_pa) {
|
||||
(void)ionic_set_vf_config(ionic, i,
|
||||
IONIC_VF_ATTR_STATSADDR,
|
||||
(u8 *)&dma);
|
||||
vfc.stats_pa = 0;
|
||||
(void)ionic_set_vf_config(ionic, i, &vfc);
|
||||
dma_unmap_single(ionic->dev, v->stats_pa,
|
||||
sizeof(v->stats), DMA_FROM_DEVICE);
|
||||
v->stats_pa = 0;
|
||||
|
@ -143,6 +142,7 @@ static void ionic_vf_dealloc(struct ionic *ionic)
|
|||
|
||||
static int ionic_vf_alloc(struct ionic *ionic, int num_vfs)
|
||||
{
|
||||
struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_STATSADDR };
|
||||
struct ionic_vf *v;
|
||||
int err = 0;
|
||||
int i;
|
||||
|
@ -166,9 +166,10 @@ static int ionic_vf_alloc(struct ionic *ionic, int num_vfs)
|
|||
}
|
||||
|
||||
ionic->num_vfs++;
|
||||
|
||||
/* ignore failures from older FW, we just won't get stats */
|
||||
(void)ionic_set_vf_config(ionic, i, IONIC_VF_ATTR_STATSADDR,
|
||||
(u8 *)&v->stats_pa);
|
||||
vfc.stats_pa = cpu_to_le64(v->stats_pa);
|
||||
(void)ionic_set_vf_config(ionic, i, &vfc);
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -331,6 +332,9 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
goto err_out_deregister_lifs;
|
||||
}
|
||||
|
||||
mod_timer(&ionic->watchdog_timer,
|
||||
round_jiffies(jiffies + ionic->watchdog_period));
|
||||
|
||||
return 0;
|
||||
|
||||
err_out_deregister_lifs:
|
||||
|
@ -348,7 +352,6 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
err_out_reset:
|
||||
ionic_reset(ionic);
|
||||
err_out_teardown:
|
||||
del_timer_sync(&ionic->watchdog_timer);
|
||||
pci_clear_master(pdev);
|
||||
/* Don't fail the probe for these errors, keep
|
||||
* the hw interface around for inspection
|
||||
|
|
|
@ -33,7 +33,8 @@ static void ionic_watchdog_cb(struct timer_list *t)
|
|||
!test_bit(IONIC_LIF_F_FW_RESET, lif->state))
|
||||
ionic_link_status_check_request(lif, CAN_NOT_SLEEP);
|
||||
|
||||
if (test_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state)) {
|
||||
if (test_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state) &&
|
||||
!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) {
|
||||
work = kzalloc(sizeof(*work), GFP_ATOMIC);
|
||||
if (!work) {
|
||||
netdev_err(lif->netdev, "rxmode change dropped\n");
|
||||
|
@ -46,6 +47,24 @@ static void ionic_watchdog_cb(struct timer_list *t)
|
|||
}
|
||||
}
|
||||
|
||||
static void ionic_watchdog_init(struct ionic *ionic)
|
||||
{
|
||||
struct ionic_dev *idev = &ionic->idev;
|
||||
|
||||
timer_setup(&ionic->watchdog_timer, ionic_watchdog_cb, 0);
|
||||
ionic->watchdog_period = IONIC_WATCHDOG_SECS * HZ;
|
||||
|
||||
/* set times to ensure the first check will proceed */
|
||||
atomic_long_set(&idev->last_check_time, jiffies - 2 * HZ);
|
||||
idev->last_hb_time = jiffies - 2 * ionic->watchdog_period;
|
||||
/* init as ready, so no transition if the first check succeeds */
|
||||
idev->last_fw_hb = 0;
|
||||
idev->fw_hb_ready = true;
|
||||
idev->fw_status_ready = true;
|
||||
idev->fw_generation = IONIC_FW_STS_F_GENERATION &
|
||||
ioread8(&idev->dev_info_regs->fw_status);
|
||||
}
|
||||
|
||||
void ionic_init_devinfo(struct ionic *ionic)
|
||||
{
|
||||
struct ionic_dev *idev = &ionic->idev;
|
||||
|
@ -109,21 +128,7 @@ int ionic_dev_setup(struct ionic *ionic)
|
|||
return -EFAULT;
|
||||
}
|
||||
|
||||
timer_setup(&ionic->watchdog_timer, ionic_watchdog_cb, 0);
|
||||
ionic->watchdog_period = IONIC_WATCHDOG_SECS * HZ;
|
||||
|
||||
/* set times to ensure the first check will proceed */
|
||||
atomic_long_set(&idev->last_check_time, jiffies - 2 * HZ);
|
||||
idev->last_hb_time = jiffies - 2 * ionic->watchdog_period;
|
||||
/* init as ready, so no transition if the first check succeeds */
|
||||
idev->last_fw_hb = 0;
|
||||
idev->fw_hb_ready = true;
|
||||
idev->fw_status_ready = true;
|
||||
idev->fw_generation = IONIC_FW_STS_F_GENERATION &
|
||||
ioread8(&idev->dev_info_regs->fw_status);
|
||||
|
||||
mod_timer(&ionic->watchdog_timer,
|
||||
round_jiffies(jiffies + ionic->watchdog_period));
|
||||
ionic_watchdog_init(ionic);
|
||||
|
||||
idev->db_pages = bar->vaddr;
|
||||
idev->phy_db_pages = bar->bus_addr;
|
||||
|
@ -132,10 +137,21 @@ int ionic_dev_setup(struct ionic *ionic)
|
|||
}
|
||||
|
||||
/* Devcmd Interface */
|
||||
bool ionic_is_fw_running(struct ionic_dev *idev)
|
||||
{
|
||||
u8 fw_status = ioread8(&idev->dev_info_regs->fw_status);
|
||||
|
||||
/* firmware is useful only if the running bit is set and
|
||||
* fw_status != 0xff (bad PCI read)
|
||||
*/
|
||||
return (fw_status != 0xff) && (fw_status & IONIC_FW_STS_F_RUNNING);
|
||||
}
|
||||
|
||||
int ionic_heartbeat_check(struct ionic *ionic)
|
||||
{
|
||||
struct ionic_dev *idev = &ionic->idev;
|
||||
unsigned long check_time, last_check_time;
|
||||
struct ionic_dev *idev = &ionic->idev;
|
||||
struct ionic_lif *lif = ionic->lif;
|
||||
bool fw_status_ready = true;
|
||||
bool fw_hb_ready;
|
||||
u8 fw_generation;
|
||||
|
@ -155,13 +171,10 @@ int ionic_heartbeat_check(struct ionic *ionic)
|
|||
goto do_check_time;
|
||||
}
|
||||
|
||||
/* firmware is useful only if the running bit is set and
|
||||
* fw_status != 0xff (bad PCI read)
|
||||
* If fw_status is not ready don't bother with the generation.
|
||||
*/
|
||||
fw_status = ioread8(&idev->dev_info_regs->fw_status);
|
||||
|
||||
if (fw_status == 0xff || !(fw_status & IONIC_FW_STS_F_RUNNING)) {
|
||||
/* If fw_status is not ready don't bother with the generation */
|
||||
if (!ionic_is_fw_running(idev)) {
|
||||
fw_status_ready = false;
|
||||
} else {
|
||||
fw_generation = fw_status & IONIC_FW_STS_F_GENERATION;
|
||||
|
@ -176,31 +189,41 @@ int ionic_heartbeat_check(struct ionic *ionic)
|
|||
* the down, the next watchdog will see the fw is up
|
||||
* and the generation value stable, so will trigger
|
||||
* the fw-up activity.
|
||||
*
|
||||
* If we had already moved to FW_RESET from a RESET event,
|
||||
* it is possible that we never saw the fw_status go to 0,
|
||||
* so we fake the current idev->fw_status_ready here to
|
||||
* force the transition and get FW up again.
|
||||
*/
|
||||
fw_status_ready = false;
|
||||
if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
|
||||
idev->fw_status_ready = false; /* go to running */
|
||||
else
|
||||
fw_status_ready = false; /* go to down */
|
||||
}
|
||||
}
|
||||
|
||||
/* is this a transition? */
|
||||
if (fw_status_ready != idev->fw_status_ready) {
|
||||
struct ionic_lif *lif = ionic->lif;
|
||||
bool trigger = false;
|
||||
|
||||
idev->fw_status_ready = fw_status_ready;
|
||||
if (!fw_status_ready && lif &&
|
||||
!test_bit(IONIC_LIF_F_FW_RESET, lif->state) &&
|
||||
!test_and_set_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) {
|
||||
dev_info(ionic->dev, "FW stopped 0x%02x\n", fw_status);
|
||||
trigger = true;
|
||||
|
||||
if (!fw_status_ready) {
|
||||
dev_info(ionic->dev, "FW stopped %u\n", fw_status);
|
||||
if (lif && !test_bit(IONIC_LIF_F_FW_RESET, lif->state))
|
||||
trigger = true;
|
||||
} else {
|
||||
dev_info(ionic->dev, "FW running %u\n", fw_status);
|
||||
if (lif && test_bit(IONIC_LIF_F_FW_RESET, lif->state))
|
||||
trigger = true;
|
||||
} else if (fw_status_ready && lif &&
|
||||
test_bit(IONIC_LIF_F_FW_RESET, lif->state) &&
|
||||
!test_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) {
|
||||
dev_info(ionic->dev, "FW running 0x%02x\n", fw_status);
|
||||
trigger = true;
|
||||
}
|
||||
|
||||
if (trigger) {
|
||||
struct ionic_deferred_work *work;
|
||||
|
||||
idev->fw_status_ready = fw_status_ready;
|
||||
|
||||
work = kzalloc(sizeof(*work), GFP_ATOMIC);
|
||||
if (work) {
|
||||
work->type = IONIC_DW_TYPE_LIF_RESET;
|
||||
|
@ -210,12 +233,14 @@ int ionic_heartbeat_check(struct ionic *ionic)
|
|||
}
|
||||
}
|
||||
|
||||
if (!fw_status_ready)
|
||||
if (!idev->fw_status_ready)
|
||||
return -ENXIO;
|
||||
|
||||
/* wait at least one watchdog period since the last heartbeat */
|
||||
/* Because of some variability in the actual FW heartbeat, we
|
||||
* wait longer than the DEVCMD_TIMEOUT before checking again.
|
||||
*/
|
||||
last_check_time = idev->last_hb_time;
|
||||
if (time_before(check_time, last_check_time + ionic->watchdog_period))
|
||||
if (time_before(check_time, last_check_time + DEVCMD_TIMEOUT * 2 * HZ))
|
||||
return 0;
|
||||
|
||||
fw_hb = ioread32(&idev->dev_info_regs->fw_heartbeat);
|
||||
|
@ -392,60 +417,63 @@ void ionic_dev_cmd_port_pause(struct ionic_dev *idev, u8 pause_type)
|
|||
}
|
||||
|
||||
/* VF commands */
|
||||
int ionic_set_vf_config(struct ionic *ionic, int vf, u8 attr, u8 *data)
|
||||
int ionic_set_vf_config(struct ionic *ionic, int vf,
|
||||
struct ionic_vf_setattr_cmd *vfc)
|
||||
{
|
||||
union ionic_dev_cmd cmd = {
|
||||
.vf_setattr.opcode = IONIC_CMD_VF_SETATTR,
|
||||
.vf_setattr.attr = attr,
|
||||
.vf_setattr.attr = vfc->attr,
|
||||
.vf_setattr.vf_index = cpu_to_le16(vf),
|
||||
};
|
||||
int err;
|
||||
|
||||
memcpy(cmd.vf_setattr.pad, vfc->pad, sizeof(vfc->pad));
|
||||
|
||||
mutex_lock(&ionic->dev_cmd_lock);
|
||||
ionic_dev_cmd_go(&ionic->idev, &cmd);
|
||||
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
|
||||
mutex_unlock(&ionic->dev_cmd_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int ionic_dev_cmd_vf_getattr(struct ionic *ionic, int vf, u8 attr,
|
||||
struct ionic_vf_getattr_comp *comp)
|
||||
{
|
||||
union ionic_dev_cmd cmd = {
|
||||
.vf_getattr.opcode = IONIC_CMD_VF_GETATTR,
|
||||
.vf_getattr.attr = attr,
|
||||
.vf_getattr.vf_index = cpu_to_le16(vf),
|
||||
};
|
||||
int err;
|
||||
|
||||
if (vf >= ionic->num_vfs)
|
||||
return -EINVAL;
|
||||
|
||||
switch (attr) {
|
||||
case IONIC_VF_ATTR_SPOOFCHK:
|
||||
cmd.vf_setattr.spoofchk = *data;
|
||||
dev_dbg(ionic->dev, "%s: vf %d spoof %d\n",
|
||||
__func__, vf, *data);
|
||||
break;
|
||||
case IONIC_VF_ATTR_TRUST:
|
||||
cmd.vf_setattr.trust = *data;
|
||||
dev_dbg(ionic->dev, "%s: vf %d trust %d\n",
|
||||
__func__, vf, *data);
|
||||
break;
|
||||
case IONIC_VF_ATTR_LINKSTATE:
|
||||
cmd.vf_setattr.linkstate = *data;
|
||||
dev_dbg(ionic->dev, "%s: vf %d linkstate %d\n",
|
||||
__func__, vf, *data);
|
||||
break;
|
||||
case IONIC_VF_ATTR_MAC:
|
||||
ether_addr_copy(cmd.vf_setattr.macaddr, data);
|
||||
dev_dbg(ionic->dev, "%s: vf %d macaddr %pM\n",
|
||||
__func__, vf, data);
|
||||
break;
|
||||
case IONIC_VF_ATTR_VLAN:
|
||||
cmd.vf_setattr.vlanid = cpu_to_le16(*(u16 *)data);
|
||||
dev_dbg(ionic->dev, "%s: vf %d vlan %d\n",
|
||||
__func__, vf, *(u16 *)data);
|
||||
break;
|
||||
case IONIC_VF_ATTR_RATE:
|
||||
cmd.vf_setattr.maxrate = cpu_to_le32(*(u32 *)data);
|
||||
dev_dbg(ionic->dev, "%s: vf %d maxrate %d\n",
|
||||
__func__, vf, *(u32 *)data);
|
||||
break;
|
||||
case IONIC_VF_ATTR_STATSADDR:
|
||||
cmd.vf_setattr.stats_pa = cpu_to_le64(*(u64 *)data);
|
||||
dev_dbg(ionic->dev, "%s: vf %d stats_pa 0x%08llx\n",
|
||||
__func__, vf, *(u64 *)data);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&ionic->dev_cmd_lock);
|
||||
ionic_dev_cmd_go(&ionic->idev, &cmd);
|
||||
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
|
||||
err = ionic_dev_cmd_wait_nomsg(ionic, DEVCMD_TIMEOUT);
|
||||
memcpy_fromio(comp, &ionic->idev.dev_cmd_regs->comp.vf_getattr,
|
||||
sizeof(*comp));
|
||||
mutex_unlock(&ionic->dev_cmd_lock);
|
||||
|
||||
if (err && comp->status != IONIC_RC_ENOSUPP)
|
||||
ionic_dev_cmd_dev_err_print(ionic, cmd.vf_getattr.opcode,
|
||||
comp->status, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -318,7 +318,10 @@ void ionic_dev_cmd_port_autoneg(struct ionic_dev *idev, u8 an_enable);
|
|||
void ionic_dev_cmd_port_fec(struct ionic_dev *idev, u8 fec_type);
|
||||
void ionic_dev_cmd_port_pause(struct ionic_dev *idev, u8 pause_type);
|
||||
|
||||
int ionic_set_vf_config(struct ionic *ionic, int vf, u8 attr, u8 *data);
|
||||
int ionic_set_vf_config(struct ionic *ionic, int vf,
|
||||
struct ionic_vf_setattr_cmd *vfc);
|
||||
int ionic_dev_cmd_vf_getattr(struct ionic *ionic, int vf, u8 attr,
|
||||
struct ionic_vf_getattr_comp *comp);
|
||||
void ionic_dev_cmd_queue_identify(struct ionic_dev *idev,
|
||||
u16 lif_type, u8 qtype, u8 qver);
|
||||
void ionic_dev_cmd_lif_identify(struct ionic_dev *idev, u8 type, u8 ver);
|
||||
|
@ -353,5 +356,6 @@ void ionic_q_rewind(struct ionic_queue *q, struct ionic_desc_info *start);
|
|||
void ionic_q_service(struct ionic_queue *q, struct ionic_cq_info *cq_info,
|
||||
unsigned int stop_index);
|
||||
int ionic_heartbeat_check(struct ionic *ionic);
|
||||
bool ionic_is_fw_running(struct ionic_dev *idev);
|
||||
|
||||
#endif /* _IONIC_DEV_H_ */
|
||||
|
|
|
@ -1112,12 +1112,17 @@ static bool ionic_notifyq_service(struct ionic_cq *cq,
|
|||
ionic_link_status_check_request(lif, CAN_NOT_SLEEP);
|
||||
break;
|
||||
case IONIC_EVENT_RESET:
|
||||
work = kzalloc(sizeof(*work), GFP_ATOMIC);
|
||||
if (!work) {
|
||||
netdev_err(lif->netdev, "Reset event dropped\n");
|
||||
} else {
|
||||
work->type = IONIC_DW_TYPE_LIF_RESET;
|
||||
ionic_lif_deferred_enqueue(&lif->deferred, work);
|
||||
if (lif->ionic->idev.fw_status_ready &&
|
||||
!test_bit(IONIC_LIF_F_FW_RESET, lif->state) &&
|
||||
!test_and_set_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) {
|
||||
work = kzalloc(sizeof(*work), GFP_ATOMIC);
|
||||
if (!work) {
|
||||
netdev_err(lif->netdev, "Reset event dropped\n");
|
||||
clear_bit(IONIC_LIF_F_FW_STOPPING, lif->state);
|
||||
} else {
|
||||
work->type = IONIC_DW_TYPE_LIF_RESET;
|
||||
ionic_lif_deferred_enqueue(&lif->deferred, work);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -1782,7 +1787,7 @@ static void ionic_lif_quiesce(struct ionic_lif *lif)
|
|||
|
||||
err = ionic_adminq_post_wait(lif, &ctx);
|
||||
if (err)
|
||||
netdev_err(lif->netdev, "lif quiesce failed %d\n", err);
|
||||
netdev_dbg(lif->netdev, "lif quiesce failed %d\n", err);
|
||||
}
|
||||
|
||||
static void ionic_txrx_disable(struct ionic_lif *lif)
|
||||
|
@ -2152,6 +2157,76 @@ static int ionic_eth_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd
|
|||
}
|
||||
}
|
||||
|
||||
static int ionic_update_cached_vf_config(struct ionic *ionic, int vf)
|
||||
{
|
||||
struct ionic_vf_getattr_comp comp = { 0 };
|
||||
int err;
|
||||
u8 attr;
|
||||
|
||||
attr = IONIC_VF_ATTR_VLAN;
|
||||
err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp);
|
||||
if (err && comp.status != IONIC_RC_ENOSUPP)
|
||||
goto err_out;
|
||||
if (!err)
|
||||
ionic->vfs[vf].vlanid = comp.vlanid;
|
||||
|
||||
attr = IONIC_VF_ATTR_SPOOFCHK;
|
||||
err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp);
|
||||
if (err && comp.status != IONIC_RC_ENOSUPP)
|
||||
goto err_out;
|
||||
if (!err)
|
||||
ionic->vfs[vf].spoofchk = comp.spoofchk;
|
||||
|
||||
attr = IONIC_VF_ATTR_LINKSTATE;
|
||||
err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp);
|
||||
if (err && comp.status != IONIC_RC_ENOSUPP)
|
||||
goto err_out;
|
||||
if (!err) {
|
||||
switch (comp.linkstate) {
|
||||
case IONIC_VF_LINK_STATUS_UP:
|
||||
ionic->vfs[vf].linkstate = IFLA_VF_LINK_STATE_ENABLE;
|
||||
break;
|
||||
case IONIC_VF_LINK_STATUS_DOWN:
|
||||
ionic->vfs[vf].linkstate = IFLA_VF_LINK_STATE_DISABLE;
|
||||
break;
|
||||
case IONIC_VF_LINK_STATUS_AUTO:
|
||||
ionic->vfs[vf].linkstate = IFLA_VF_LINK_STATE_AUTO;
|
||||
break;
|
||||
default:
|
||||
dev_warn(ionic->dev, "Unexpected link state %u\n", comp.linkstate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
attr = IONIC_VF_ATTR_RATE;
|
||||
err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp);
|
||||
if (err && comp.status != IONIC_RC_ENOSUPP)
|
||||
goto err_out;
|
||||
if (!err)
|
||||
ionic->vfs[vf].maxrate = comp.maxrate;
|
||||
|
||||
attr = IONIC_VF_ATTR_TRUST;
|
||||
err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp);
|
||||
if (err && comp.status != IONIC_RC_ENOSUPP)
|
||||
goto err_out;
|
||||
if (!err)
|
||||
ionic->vfs[vf].trusted = comp.trust;
|
||||
|
||||
attr = IONIC_VF_ATTR_MAC;
|
||||
err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp);
|
||||
if (err && comp.status != IONIC_RC_ENOSUPP)
|
||||
goto err_out;
|
||||
if (!err)
|
||||
ether_addr_copy(ionic->vfs[vf].macaddr, comp.macaddr);
|
||||
|
||||
err_out:
|
||||
if (err)
|
||||
dev_err(ionic->dev, "Failed to get %s for VF %d\n",
|
||||
ionic_vf_attr_to_str(attr), vf);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ionic_get_vf_config(struct net_device *netdev,
|
||||
int vf, struct ifla_vf_info *ivf)
|
||||
{
|
||||
|
@ -2167,14 +2242,18 @@ static int ionic_get_vf_config(struct net_device *netdev,
|
|||
if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
ivf->vf = vf;
|
||||
ivf->vlan = le16_to_cpu(ionic->vfs[vf].vlanid);
|
||||
ivf->qos = 0;
|
||||
ivf->spoofchk = ionic->vfs[vf].spoofchk;
|
||||
ivf->linkstate = ionic->vfs[vf].linkstate;
|
||||
ivf->max_tx_rate = le32_to_cpu(ionic->vfs[vf].maxrate);
|
||||
ivf->trusted = ionic->vfs[vf].trusted;
|
||||
ether_addr_copy(ivf->mac, ionic->vfs[vf].macaddr);
|
||||
ivf->vf = vf;
|
||||
ivf->qos = 0;
|
||||
|
||||
ret = ionic_update_cached_vf_config(ionic, vf);
|
||||
if (!ret) {
|
||||
ivf->vlan = le16_to_cpu(ionic->vfs[vf].vlanid);
|
||||
ivf->spoofchk = ionic->vfs[vf].spoofchk;
|
||||
ivf->linkstate = ionic->vfs[vf].linkstate;
|
||||
ivf->max_tx_rate = le32_to_cpu(ionic->vfs[vf].maxrate);
|
||||
ivf->trusted = ionic->vfs[vf].trusted;
|
||||
ether_addr_copy(ivf->mac, ionic->vfs[vf].macaddr);
|
||||
}
|
||||
}
|
||||
|
||||
up_read(&ionic->vf_op_lock);
|
||||
|
@ -2220,6 +2299,7 @@ static int ionic_get_vf_stats(struct net_device *netdev, int vf,
|
|||
|
||||
static int ionic_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
|
||||
{
|
||||
struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_MAC };
|
||||
struct ionic_lif *lif = netdev_priv(netdev);
|
||||
struct ionic *ionic = lif->ionic;
|
||||
int ret;
|
||||
|
@ -2235,7 +2315,11 @@ static int ionic_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
|
|||
if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
ret = ionic_set_vf_config(ionic, vf, IONIC_VF_ATTR_MAC, mac);
|
||||
ether_addr_copy(vfc.macaddr, mac);
|
||||
dev_dbg(ionic->dev, "%s: vf %d macaddr %pM\n",
|
||||
__func__, vf, vfc.macaddr);
|
||||
|
||||
ret = ionic_set_vf_config(ionic, vf, &vfc);
|
||||
if (!ret)
|
||||
ether_addr_copy(ionic->vfs[vf].macaddr, mac);
|
||||
}
|
||||
|
@ -2247,6 +2331,7 @@ static int ionic_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
|
|||
static int ionic_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan,
|
||||
u8 qos, __be16 proto)
|
||||
{
|
||||
struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_VLAN };
|
||||
struct ionic_lif *lif = netdev_priv(netdev);
|
||||
struct ionic *ionic = lif->ionic;
|
||||
int ret;
|
||||
|
@ -2269,8 +2354,11 @@ static int ionic_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan,
|
|||
if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
ret = ionic_set_vf_config(ionic, vf,
|
||||
IONIC_VF_ATTR_VLAN, (u8 *)&vlan);
|
||||
vfc.vlanid = cpu_to_le16(vlan);
|
||||
dev_dbg(ionic->dev, "%s: vf %d vlan %d\n",
|
||||
__func__, vf, le16_to_cpu(vfc.vlanid));
|
||||
|
||||
ret = ionic_set_vf_config(ionic, vf, &vfc);
|
||||
if (!ret)
|
||||
ionic->vfs[vf].vlanid = cpu_to_le16(vlan);
|
||||
}
|
||||
|
@ -2282,6 +2370,7 @@ static int ionic_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan,
|
|||
static int ionic_set_vf_rate(struct net_device *netdev, int vf,
|
||||
int tx_min, int tx_max)
|
||||
{
|
||||
struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_RATE };
|
||||
struct ionic_lif *lif = netdev_priv(netdev);
|
||||
struct ionic *ionic = lif->ionic;
|
||||
int ret;
|
||||
|
@ -2298,8 +2387,11 @@ static int ionic_set_vf_rate(struct net_device *netdev, int vf,
|
|||
if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
ret = ionic_set_vf_config(ionic, vf,
|
||||
IONIC_VF_ATTR_RATE, (u8 *)&tx_max);
|
||||
vfc.maxrate = cpu_to_le32(tx_max);
|
||||
dev_dbg(ionic->dev, "%s: vf %d maxrate %d\n",
|
||||
__func__, vf, le32_to_cpu(vfc.maxrate));
|
||||
|
||||
ret = ionic_set_vf_config(ionic, vf, &vfc);
|
||||
if (!ret)
|
||||
lif->ionic->vfs[vf].maxrate = cpu_to_le32(tx_max);
|
||||
}
|
||||
|
@ -2310,9 +2402,9 @@ static int ionic_set_vf_rate(struct net_device *netdev, int vf,
|
|||
|
||||
static int ionic_set_vf_spoofchk(struct net_device *netdev, int vf, bool set)
|
||||
{
|
||||
struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_SPOOFCHK };
|
||||
struct ionic_lif *lif = netdev_priv(netdev);
|
||||
struct ionic *ionic = lif->ionic;
|
||||
u8 data = set; /* convert to u8 for config */
|
||||
int ret;
|
||||
|
||||
if (!netif_device_present(netdev))
|
||||
|
@ -2323,10 +2415,13 @@ static int ionic_set_vf_spoofchk(struct net_device *netdev, int vf, bool set)
|
|||
if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
ret = ionic_set_vf_config(ionic, vf,
|
||||
IONIC_VF_ATTR_SPOOFCHK, &data);
|
||||
vfc.spoofchk = set;
|
||||
dev_dbg(ionic->dev, "%s: vf %d spoof %d\n",
|
||||
__func__, vf, vfc.spoofchk);
|
||||
|
||||
ret = ionic_set_vf_config(ionic, vf, &vfc);
|
||||
if (!ret)
|
||||
ionic->vfs[vf].spoofchk = data;
|
||||
ionic->vfs[vf].spoofchk = set;
|
||||
}
|
||||
|
||||
up_write(&ionic->vf_op_lock);
|
||||
|
@ -2335,9 +2430,9 @@ static int ionic_set_vf_spoofchk(struct net_device *netdev, int vf, bool set)
|
|||
|
||||
static int ionic_set_vf_trust(struct net_device *netdev, int vf, bool set)
|
||||
{
|
||||
struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_TRUST };
|
||||
struct ionic_lif *lif = netdev_priv(netdev);
|
||||
struct ionic *ionic = lif->ionic;
|
||||
u8 data = set; /* convert to u8 for config */
|
||||
int ret;
|
||||
|
||||
if (!netif_device_present(netdev))
|
||||
|
@ -2348,10 +2443,13 @@ static int ionic_set_vf_trust(struct net_device *netdev, int vf, bool set)
|
|||
if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
ret = ionic_set_vf_config(ionic, vf,
|
||||
IONIC_VF_ATTR_TRUST, &data);
|
||||
vfc.trust = set;
|
||||
dev_dbg(ionic->dev, "%s: vf %d trust %d\n",
|
||||
__func__, vf, vfc.trust);
|
||||
|
||||
ret = ionic_set_vf_config(ionic, vf, &vfc);
|
||||
if (!ret)
|
||||
ionic->vfs[vf].trusted = data;
|
||||
ionic->vfs[vf].trusted = set;
|
||||
}
|
||||
|
||||
up_write(&ionic->vf_op_lock);
|
||||
|
@ -2360,20 +2458,21 @@ static int ionic_set_vf_trust(struct net_device *netdev, int vf, bool set)
|
|||
|
||||
static int ionic_set_vf_link_state(struct net_device *netdev, int vf, int set)
|
||||
{
|
||||
struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_LINKSTATE };
|
||||
struct ionic_lif *lif = netdev_priv(netdev);
|
||||
struct ionic *ionic = lif->ionic;
|
||||
u8 data;
|
||||
u8 vfls;
|
||||
int ret;
|
||||
|
||||
switch (set) {
|
||||
case IFLA_VF_LINK_STATE_ENABLE:
|
||||
data = IONIC_VF_LINK_STATUS_UP;
|
||||
vfls = IONIC_VF_LINK_STATUS_UP;
|
||||
break;
|
||||
case IFLA_VF_LINK_STATE_DISABLE:
|
||||
data = IONIC_VF_LINK_STATUS_DOWN;
|
||||
vfls = IONIC_VF_LINK_STATUS_DOWN;
|
||||
break;
|
||||
case IFLA_VF_LINK_STATE_AUTO:
|
||||
data = IONIC_VF_LINK_STATUS_AUTO;
|
||||
vfls = IONIC_VF_LINK_STATUS_AUTO;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -2387,8 +2486,11 @@ static int ionic_set_vf_link_state(struct net_device *netdev, int vf, int set)
|
|||
if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
ret = ionic_set_vf_config(ionic, vf,
|
||||
IONIC_VF_ATTR_LINKSTATE, &data);
|
||||
vfc.linkstate = vfls;
|
||||
dev_dbg(ionic->dev, "%s: vf %d linkstate %d\n",
|
||||
__func__, vf, vfc.linkstate);
|
||||
|
||||
ret = ionic_set_vf_config(ionic, vf, &vfc);
|
||||
if (!ret)
|
||||
ionic->vfs[vf].linkstate = set;
|
||||
}
|
||||
|
@ -2835,6 +2937,7 @@ static void ionic_lif_handle_fw_down(struct ionic_lif *lif)
|
|||
|
||||
mutex_unlock(&lif->queue_lock);
|
||||
|
||||
clear_bit(IONIC_LIF_F_FW_STOPPING, lif->state);
|
||||
dev_info(ionic->dev, "FW Down: LIFs stopped\n");
|
||||
}
|
||||
|
||||
|
@ -2934,8 +3037,6 @@ void ionic_lif_free(struct ionic_lif *lif)
|
|||
/* unmap doorbell page */
|
||||
ionic_bus_unmap_dbpage(lif->ionic, lif->kern_dbpage);
|
||||
lif->kern_dbpage = NULL;
|
||||
kfree(lif->dbid_inuse);
|
||||
lif->dbid_inuse = NULL;
|
||||
|
||||
mutex_destroy(&lif->config_lock);
|
||||
mutex_destroy(&lif->queue_lock);
|
||||
|
@ -3135,22 +3236,12 @@ int ionic_lif_init(struct ionic_lif *lif)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
lif->dbid_inuse = bitmap_zalloc(lif->dbid_count, GFP_KERNEL);
|
||||
if (!lif->dbid_inuse) {
|
||||
dev_err(dev, "Failed alloc doorbell id bitmap, aborting\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* first doorbell id reserved for kernel (dbid aka pid == zero) */
|
||||
set_bit(0, lif->dbid_inuse);
|
||||
lif->kern_pid = 0;
|
||||
|
||||
dbpage_num = ionic_db_page_num(lif, lif->kern_pid);
|
||||
lif->kern_dbpage = ionic_bus_map_dbpage(lif->ionic, dbpage_num);
|
||||
if (!lif->kern_dbpage) {
|
||||
dev_err(dev, "Cannot map dbpage, aborting\n");
|
||||
err = -ENOMEM;
|
||||
goto err_out_free_dbid;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = ionic_lif_adminq_init(lif);
|
||||
|
@ -3186,15 +3277,13 @@ int ionic_lif_init(struct ionic_lif *lif)
|
|||
return 0;
|
||||
|
||||
err_out_notifyq_deinit:
|
||||
napi_disable(&lif->adminqcq->napi);
|
||||
ionic_lif_qcq_deinit(lif, lif->notifyqcq);
|
||||
err_out_adminq_deinit:
|
||||
ionic_lif_qcq_deinit(lif, lif->adminqcq);
|
||||
ionic_lif_reset(lif);
|
||||
ionic_bus_unmap_dbpage(lif->ionic, lif->kern_dbpage);
|
||||
lif->kern_dbpage = NULL;
|
||||
err_out_free_dbid:
|
||||
kfree(lif->dbid_inuse);
|
||||
lif->dbid_inuse = NULL;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -135,6 +135,7 @@ enum ionic_lif_state_flags {
|
|||
IONIC_LIF_F_LINK_CHECK_REQUESTED,
|
||||
IONIC_LIF_F_FILTER_SYNC_NEEDED,
|
||||
IONIC_LIF_F_FW_RESET,
|
||||
IONIC_LIF_F_FW_STOPPING,
|
||||
IONIC_LIF_F_SPLIT_INTR,
|
||||
IONIC_LIF_F_BROKEN,
|
||||
IONIC_LIF_F_TX_DIM_INTR,
|
||||
|
@ -213,7 +214,6 @@ struct ionic_lif {
|
|||
u32 rx_coalesce_hw; /* what the hw is using */
|
||||
u32 tx_coalesce_usecs; /* what the user asked for */
|
||||
u32 tx_coalesce_hw; /* what the hw is using */
|
||||
unsigned long *dbid_inuse;
|
||||
unsigned int dbid_count;
|
||||
|
||||
struct ionic_phc *phc;
|
||||
|
|
|
@ -188,6 +188,28 @@ static const char *ionic_opcode_to_str(enum ionic_cmd_opcode opcode)
|
|||
}
|
||||
}
|
||||
|
||||
const char *ionic_vf_attr_to_str(enum ionic_vf_attr attr)
|
||||
{
|
||||
switch (attr) {
|
||||
case IONIC_VF_ATTR_SPOOFCHK:
|
||||
return "IONIC_VF_ATTR_SPOOFCHK";
|
||||
case IONIC_VF_ATTR_TRUST:
|
||||
return "IONIC_VF_ATTR_TRUST";
|
||||
case IONIC_VF_ATTR_LINKSTATE:
|
||||
return "IONIC_VF_ATTR_LINKSTATE";
|
||||
case IONIC_VF_ATTR_MAC:
|
||||
return "IONIC_VF_ATTR_MAC";
|
||||
case IONIC_VF_ATTR_VLAN:
|
||||
return "IONIC_VF_ATTR_VLAN";
|
||||
case IONIC_VF_ATTR_RATE:
|
||||
return "IONIC_VF_ATTR_RATE";
|
||||
case IONIC_VF_ATTR_STATSADDR:
|
||||
return "IONIC_VF_ATTR_STATSADDR";
|
||||
default:
|
||||
return "IONIC_VF_ATTR_UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static void ionic_adminq_flush(struct ionic_lif *lif)
|
||||
{
|
||||
struct ionic_desc_info *desc_info;
|
||||
|
@ -215,9 +237,13 @@ static void ionic_adminq_flush(struct ionic_lif *lif)
|
|||
void ionic_adminq_netdev_err_print(struct ionic_lif *lif, u8 opcode,
|
||||
u8 status, int err)
|
||||
{
|
||||
const char *stat_str;
|
||||
|
||||
stat_str = (err == -ETIMEDOUT) ? "TIMEOUT" :
|
||||
ionic_error_to_str(status);
|
||||
|
||||
netdev_err(lif->netdev, "%s (%d) failed: %s (%d)\n",
|
||||
ionic_opcode_to_str(opcode), opcode,
|
||||
ionic_error_to_str(status), err);
|
||||
ionic_opcode_to_str(opcode), opcode, stat_str, err);
|
||||
}
|
||||
|
||||
static int ionic_adminq_check_err(struct ionic_lif *lif,
|
||||
|
@ -318,6 +344,7 @@ int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx,
|
|||
if (do_msg && !test_bit(IONIC_LIF_F_FW_RESET, lif->state))
|
||||
netdev_err(netdev, "Posting of %s (%d) failed: %d\n",
|
||||
name, ctx->cmd.cmd.opcode, err);
|
||||
ctx->comp.comp.status = IONIC_RC_ERROR;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -331,11 +358,15 @@ int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx,
|
|||
if (remaining)
|
||||
break;
|
||||
|
||||
/* interrupt the wait if FW stopped */
|
||||
if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) {
|
||||
/* force a check of FW status and break out if FW reset */
|
||||
(void)ionic_heartbeat_check(lif->ionic);
|
||||
if ((test_bit(IONIC_LIF_F_FW_RESET, lif->state) &&
|
||||
!lif->ionic->idev.fw_status_ready) ||
|
||||
test_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) {
|
||||
if (do_msg)
|
||||
netdev_err(netdev, "%s (%d) interrupted, FW in reset\n",
|
||||
name, ctx->cmd.cmd.opcode);
|
||||
netdev_warn(netdev, "%s (%d) interrupted, FW in reset\n",
|
||||
name, ctx->cmd.cmd.opcode);
|
||||
ctx->comp.comp.status = IONIC_RC_ERROR;
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
|
@ -370,21 +401,34 @@ int ionic_adminq_post_wait_nomsg(struct ionic_lif *lif, struct ionic_admin_ctx *
|
|||
|
||||
static void ionic_dev_cmd_clean(struct ionic *ionic)
|
||||
{
|
||||
union __iomem ionic_dev_cmd_regs *regs = ionic->idev.dev_cmd_regs;
|
||||
struct ionic_dev *idev = &ionic->idev;
|
||||
|
||||
iowrite32(0, ®s->doorbell);
|
||||
memset_io(®s->cmd, 0, sizeof(regs->cmd));
|
||||
iowrite32(0, &idev->dev_cmd_regs->doorbell);
|
||||
memset_io(&idev->dev_cmd_regs->cmd, 0, sizeof(idev->dev_cmd_regs->cmd));
|
||||
}
|
||||
|
||||
int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds)
|
||||
void ionic_dev_cmd_dev_err_print(struct ionic *ionic, u8 opcode, u8 status,
|
||||
int err)
|
||||
{
|
||||
const char *stat_str;
|
||||
|
||||
stat_str = (err == -ETIMEDOUT) ? "TIMEOUT" :
|
||||
ionic_error_to_str(status);
|
||||
|
||||
dev_err(ionic->dev, "DEV_CMD %s (%d) error, %s (%d) failed\n",
|
||||
ionic_opcode_to_str(opcode), opcode, stat_str, err);
|
||||
}
|
||||
|
||||
static int __ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds,
|
||||
const bool do_msg)
|
||||
{
|
||||
struct ionic_dev *idev = &ionic->idev;
|
||||
unsigned long start_time;
|
||||
unsigned long max_wait;
|
||||
unsigned long duration;
|
||||
int done = 0;
|
||||
bool fw_up;
|
||||
int opcode;
|
||||
int hb = 0;
|
||||
int done;
|
||||
int err;
|
||||
|
||||
/* Wait for dev cmd to complete, retrying if we get EAGAIN,
|
||||
|
@ -394,31 +438,24 @@ int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds)
|
|||
try_again:
|
||||
opcode = readb(&idev->dev_cmd_regs->cmd.cmd.opcode);
|
||||
start_time = jiffies;
|
||||
do {
|
||||
for (fw_up = ionic_is_fw_running(idev);
|
||||
!done && fw_up && time_before(jiffies, max_wait);
|
||||
fw_up = ionic_is_fw_running(idev)) {
|
||||
done = ionic_dev_cmd_done(idev);
|
||||
if (done)
|
||||
break;
|
||||
usleep_range(100, 200);
|
||||
|
||||
/* Don't check the heartbeat on FW_CONTROL commands as they are
|
||||
* notorious for interrupting the firmware's heartbeat update.
|
||||
*/
|
||||
if (opcode != IONIC_CMD_FW_CONTROL)
|
||||
hb = ionic_heartbeat_check(ionic);
|
||||
} while (!done && !hb && time_before(jiffies, max_wait));
|
||||
}
|
||||
duration = jiffies - start_time;
|
||||
|
||||
dev_dbg(ionic->dev, "DEVCMD %s (%d) done=%d took %ld secs (%ld jiffies)\n",
|
||||
ionic_opcode_to_str(opcode), opcode,
|
||||
done, duration / HZ, duration);
|
||||
|
||||
if (!done && hb) {
|
||||
/* It is possible (but unlikely) that FW was busy and missed a
|
||||
* heartbeat check but is still alive and will process this
|
||||
* request, so don't clean the dev_cmd in this case.
|
||||
*/
|
||||
dev_dbg(ionic->dev, "DEVCMD %s (%d) failed - FW halted\n",
|
||||
ionic_opcode_to_str(opcode), opcode);
|
||||
if (!done && !fw_up) {
|
||||
ionic_dev_cmd_clean(ionic);
|
||||
dev_warn(ionic->dev, "DEVCMD %s (%d) interrupted - FW is down\n",
|
||||
ionic_opcode_to_str(opcode), opcode);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
|
@ -444,9 +481,9 @@ int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds)
|
|||
}
|
||||
|
||||
if (!(opcode == IONIC_CMD_FW_CONTROL && err == IONIC_RC_EAGAIN))
|
||||
dev_err(ionic->dev, "DEV_CMD %s (%d) error, %s (%d) failed\n",
|
||||
ionic_opcode_to_str(opcode), opcode,
|
||||
ionic_error_to_str(err), err);
|
||||
if (do_msg)
|
||||
ionic_dev_cmd_dev_err_print(ionic, opcode, err,
|
||||
ionic_error_to_errno(err));
|
||||
|
||||
return ionic_error_to_errno(err);
|
||||
}
|
||||
|
@ -454,6 +491,16 @@ int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds)
|
||||
{
|
||||
return __ionic_dev_cmd_wait(ionic, max_seconds, true);
|
||||
}
|
||||
|
||||
int ionic_dev_cmd_wait_nomsg(struct ionic *ionic, unsigned long max_seconds)
|
||||
{
|
||||
return __ionic_dev_cmd_wait(ionic, max_seconds, false);
|
||||
}
|
||||
|
||||
int ionic_setup(struct ionic *ionic)
|
||||
{
|
||||
int err;
|
||||
|
@ -540,6 +587,9 @@ int ionic_reset(struct ionic *ionic)
|
|||
struct ionic_dev *idev = &ionic->idev;
|
||||
int err;
|
||||
|
||||
if (!ionic_is_fw_running(idev))
|
||||
return 0;
|
||||
|
||||
mutex_lock(&ionic->dev_cmd_lock);
|
||||
ionic_dev_cmd_reset(idev);
|
||||
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
|
||||
|
@ -612,15 +662,17 @@ int ionic_port_init(struct ionic *ionic)
|
|||
int ionic_port_reset(struct ionic *ionic)
|
||||
{
|
||||
struct ionic_dev *idev = &ionic->idev;
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
if (!idev->port_info)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&ionic->dev_cmd_lock);
|
||||
ionic_dev_cmd_port_reset(idev);
|
||||
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
|
||||
mutex_unlock(&ionic->dev_cmd_lock);
|
||||
if (ionic_is_fw_running(idev)) {
|
||||
mutex_lock(&ionic->dev_cmd_lock);
|
||||
ionic_dev_cmd_port_reset(idev);
|
||||
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
|
||||
mutex_unlock(&ionic->dev_cmd_lock);
|
||||
}
|
||||
|
||||
dma_free_coherent(ionic->dev, idev->port_info_sz,
|
||||
idev->port_info, idev->port_info_pa);
|
||||
|
@ -628,9 +680,6 @@ int ionic_port_reset(struct ionic *ionic)
|
|||
idev->port_info = NULL;
|
||||
idev->port_info_pa = 0;
|
||||
|
||||
if (err)
|
||||
dev_err(ionic->dev, "Failed to reset port\n");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -376,10 +376,24 @@ static int ionic_lif_filter_add(struct ionic_lif *lif,
|
|||
|
||||
spin_unlock_bh(&lif->rx_filters.lock);
|
||||
|
||||
if (err == -ENOSPC) {
|
||||
if (le16_to_cpu(ctx.cmd.rx_filter_add.match) == IONIC_RX_FILTER_MATCH_VLAN)
|
||||
lif->max_vlans = lif->nvlans;
|
||||
/* store the max_vlans limit that we found */
|
||||
if (err == -ENOSPC &&
|
||||
le16_to_cpu(ctx.cmd.rx_filter_add.match) == IONIC_RX_FILTER_MATCH_VLAN)
|
||||
lif->max_vlans = lif->nvlans;
|
||||
|
||||
/* Prevent unnecessary error messages on recoverable
|
||||
* errors as the filter will get retried on the next
|
||||
* sync attempt.
|
||||
*/
|
||||
switch (err) {
|
||||
case -ENOSPC:
|
||||
case -ENXIO:
|
||||
case -ETIMEDOUT:
|
||||
case -EAGAIN:
|
||||
case -EBUSY:
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ionic_adminq_netdev_err_print(lif, ctx.cmd.cmd.opcode,
|
||||
|
@ -494,9 +508,22 @@ static int ionic_lif_filter_del(struct ionic_lif *lif,
|
|||
spin_unlock_bh(&lif->rx_filters.lock);
|
||||
|
||||
if (state != IONIC_FILTER_STATE_NEW) {
|
||||
err = ionic_adminq_post_wait(lif, &ctx);
|
||||
if (err && err != -EEXIST)
|
||||
err = ionic_adminq_post_wait_nomsg(lif, &ctx);
|
||||
|
||||
switch (err) {
|
||||
/* ignore these errors */
|
||||
case -EEXIST:
|
||||
case -ENXIO:
|
||||
case -ETIMEDOUT:
|
||||
case -EAGAIN:
|
||||
case -EBUSY:
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
ionic_adminq_netdev_err_print(lif, ctx.cmd.cmd.opcode,
|
||||
ctx.comp.comp.status, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -669,27 +669,37 @@ static int ionic_tx_map_skb(struct ionic_queue *q, struct sk_buff *skb,
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
static void ionic_tx_desc_unmap_bufs(struct ionic_queue *q,
|
||||
struct ionic_desc_info *desc_info)
|
||||
{
|
||||
struct ionic_buf_info *buf_info = desc_info->bufs;
|
||||
struct device *dev = q->dev;
|
||||
unsigned int i;
|
||||
|
||||
if (!desc_info->nbufs)
|
||||
return;
|
||||
|
||||
dma_unmap_single(dev, (dma_addr_t)buf_info->dma_addr,
|
||||
buf_info->len, DMA_TO_DEVICE);
|
||||
buf_info++;
|
||||
for (i = 1; i < desc_info->nbufs; i++, buf_info++)
|
||||
dma_unmap_page(dev, (dma_addr_t)buf_info->dma_addr,
|
||||
buf_info->len, DMA_TO_DEVICE);
|
||||
|
||||
desc_info->nbufs = 0;
|
||||
}
|
||||
|
||||
static void ionic_tx_clean(struct ionic_queue *q,
|
||||
struct ionic_desc_info *desc_info,
|
||||
struct ionic_cq_info *cq_info,
|
||||
void *cb_arg)
|
||||
{
|
||||
struct ionic_buf_info *buf_info = desc_info->bufs;
|
||||
struct ionic_tx_stats *stats = q_to_tx_stats(q);
|
||||
struct ionic_qcq *qcq = q_to_qcq(q);
|
||||
struct sk_buff *skb = cb_arg;
|
||||
struct device *dev = q->dev;
|
||||
unsigned int i;
|
||||
u16 qi;
|
||||
|
||||
if (desc_info->nbufs) {
|
||||
dma_unmap_single(dev, (dma_addr_t)buf_info->dma_addr,
|
||||
buf_info->len, DMA_TO_DEVICE);
|
||||
buf_info++;
|
||||
for (i = 1; i < desc_info->nbufs; i++, buf_info++)
|
||||
dma_unmap_page(dev, (dma_addr_t)buf_info->dma_addr,
|
||||
buf_info->len, DMA_TO_DEVICE);
|
||||
}
|
||||
ionic_tx_desc_unmap_bufs(q, desc_info);
|
||||
|
||||
if (!skb)
|
||||
return;
|
||||
|
@ -931,8 +941,11 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb)
|
|||
err = ionic_tx_tcp_inner_pseudo_csum(skb);
|
||||
else
|
||||
err = ionic_tx_tcp_pseudo_csum(skb);
|
||||
if (err)
|
||||
if (err) {
|
||||
/* clean up mapping from ionic_tx_map_skb */
|
||||
ionic_tx_desc_unmap_bufs(q, desc_info);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (encap)
|
||||
hdrlen = skb_inner_transport_header(skb) - skb->data +
|
||||
|
@ -1003,8 +1016,8 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb,
|
||||
struct ionic_desc_info *desc_info)
|
||||
static void ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb,
|
||||
struct ionic_desc_info *desc_info)
|
||||
{
|
||||
struct ionic_txq_desc *desc = desc_info->txq_desc;
|
||||
struct ionic_buf_info *buf_info = desc_info->bufs;
|
||||
|
@ -1038,12 +1051,10 @@ static int ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb,
|
|||
stats->crc32_csum++;
|
||||
else
|
||||
stats->csum++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ionic_tx_calc_no_csum(struct ionic_queue *q, struct sk_buff *skb,
|
||||
struct ionic_desc_info *desc_info)
|
||||
static void ionic_tx_calc_no_csum(struct ionic_queue *q, struct sk_buff *skb,
|
||||
struct ionic_desc_info *desc_info)
|
||||
{
|
||||
struct ionic_txq_desc *desc = desc_info->txq_desc;
|
||||
struct ionic_buf_info *buf_info = desc_info->bufs;
|
||||
|
@ -1074,12 +1085,10 @@ static int ionic_tx_calc_no_csum(struct ionic_queue *q, struct sk_buff *skb,
|
|||
desc->csum_offset = 0;
|
||||
|
||||
stats->csum_none++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ionic_tx_skb_frags(struct ionic_queue *q, struct sk_buff *skb,
|
||||
struct ionic_desc_info *desc_info)
|
||||
static void ionic_tx_skb_frags(struct ionic_queue *q, struct sk_buff *skb,
|
||||
struct ionic_desc_info *desc_info)
|
||||
{
|
||||
struct ionic_txq_sg_desc *sg_desc = desc_info->txq_sg_desc;
|
||||
struct ionic_buf_info *buf_info = &desc_info->bufs[1];
|
||||
|
@ -1093,31 +1102,24 @@ static int ionic_tx_skb_frags(struct ionic_queue *q, struct sk_buff *skb,
|
|||
}
|
||||
|
||||
stats->frags += skb_shinfo(skb)->nr_frags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ionic_tx(struct ionic_queue *q, struct sk_buff *skb)
|
||||
{
|
||||
struct ionic_desc_info *desc_info = &q->info[q->head_idx];
|
||||
struct ionic_tx_stats *stats = q_to_tx_stats(q);
|
||||
int err;
|
||||
|
||||
if (unlikely(ionic_tx_map_skb(q, skb, desc_info)))
|
||||
return -EIO;
|
||||
|
||||
/* set up the initial descriptor */
|
||||
if (skb->ip_summed == CHECKSUM_PARTIAL)
|
||||
err = ionic_tx_calc_csum(q, skb, desc_info);
|
||||
ionic_tx_calc_csum(q, skb, desc_info);
|
||||
else
|
||||
err = ionic_tx_calc_no_csum(q, skb, desc_info);
|
||||
if (err)
|
||||
return err;
|
||||
ionic_tx_calc_no_csum(q, skb, desc_info);
|
||||
|
||||
/* add frags */
|
||||
err = ionic_tx_skb_frags(q, skb, desc_info);
|
||||
if (err)
|
||||
return err;
|
||||
ionic_tx_skb_frags(q, skb, desc_info);
|
||||
|
||||
skb_tx_timestamp(skb);
|
||||
stats->pkts++;
|
||||
|
|
Loading…
Reference in New Issue