Commit 8a0de61c authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents ca73b68a 36197d82
......@@ -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) {
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;
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;
} 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_ */
......@@ -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, &regs->doorbell);
memset_io(&regs->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++;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment