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; ...@@ -18,7 +18,7 @@ struct ionic_lif;
#define PCI_DEVICE_ID_PENSANDO_IONIC_ETH_PF 0x1002 #define PCI_DEVICE_ID_PENSANDO_IONIC_ETH_PF 0x1002
#define PCI_DEVICE_ID_PENSANDO_IONIC_ETH_VF 0x1003 #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_ADMINQ_TIME_SLICE msecs_to_jiffies(100)
#define IONIC_PHC_UPDATE_NS 10000000000 /* 10s in nanoseconds */ #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, ...@@ -78,6 +78,9 @@ void ionic_adminq_netdev_err_print(struct ionic_lif *lif, u8 opcode,
u8 status, int err); u8 status, int err);
int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_wait); 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_set_dma_mask(struct ionic *ionic);
int ionic_setup(struct ionic *ionic); int ionic_setup(struct ionic *ionic);
...@@ -89,4 +92,6 @@ int ionic_port_identify(struct ionic *ionic); ...@@ -89,4 +92,6 @@ int ionic_port_identify(struct ionic *ionic);
int ionic_port_init(struct ionic *ionic); int ionic_port_init(struct ionic *ionic);
int ionic_port_reset(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_ */ #endif /* _IONIC_H_ */
...@@ -109,8 +109,8 @@ void ionic_bus_unmap_dbpage(struct ionic *ionic, void __iomem *page) ...@@ -109,8 +109,8 @@ void ionic_bus_unmap_dbpage(struct ionic *ionic, void __iomem *page)
static void ionic_vf_dealloc_locked(struct ionic *ionic) static void ionic_vf_dealloc_locked(struct ionic *ionic)
{ {
struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_STATSADDR };
struct ionic_vf *v; struct ionic_vf *v;
dma_addr_t dma = 0;
int i; int i;
if (!ionic->vfs) if (!ionic->vfs)
...@@ -120,9 +120,8 @@ static void ionic_vf_dealloc_locked(struct ionic *ionic) ...@@ -120,9 +120,8 @@ static void ionic_vf_dealloc_locked(struct ionic *ionic)
v = &ionic->vfs[i]; v = &ionic->vfs[i];
if (v->stats_pa) { if (v->stats_pa) {
(void)ionic_set_vf_config(ionic, i, vfc.stats_pa = 0;
IONIC_VF_ATTR_STATSADDR, (void)ionic_set_vf_config(ionic, i, &vfc);
(u8 *)&dma);
dma_unmap_single(ionic->dev, v->stats_pa, dma_unmap_single(ionic->dev, v->stats_pa,
sizeof(v->stats), DMA_FROM_DEVICE); sizeof(v->stats), DMA_FROM_DEVICE);
v->stats_pa = 0; v->stats_pa = 0;
...@@ -143,6 +142,7 @@ static void ionic_vf_dealloc(struct ionic *ionic) ...@@ -143,6 +142,7 @@ static void ionic_vf_dealloc(struct ionic *ionic)
static int ionic_vf_alloc(struct ionic *ionic, int num_vfs) 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; struct ionic_vf *v;
int err = 0; int err = 0;
int i; int i;
...@@ -166,9 +166,10 @@ static int ionic_vf_alloc(struct ionic *ionic, int num_vfs) ...@@ -166,9 +166,10 @@ static int ionic_vf_alloc(struct ionic *ionic, int num_vfs)
} }
ionic->num_vfs++; ionic->num_vfs++;
/* ignore failures from older FW, we just won't get stats */ /* ignore failures from older FW, we just won't get stats */
(void)ionic_set_vf_config(ionic, i, IONIC_VF_ATTR_STATSADDR, vfc.stats_pa = cpu_to_le64(v->stats_pa);
(u8 *)&v->stats_pa); (void)ionic_set_vf_config(ionic, i, &vfc);
} }
out: out:
...@@ -331,6 +332,9 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -331,6 +332,9 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_out_deregister_lifs; goto err_out_deregister_lifs;
} }
mod_timer(&ionic->watchdog_timer,
round_jiffies(jiffies + ionic->watchdog_period));
return 0; return 0;
err_out_deregister_lifs: err_out_deregister_lifs:
...@@ -348,7 +352,6 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -348,7 +352,6 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
err_out_reset: err_out_reset:
ionic_reset(ionic); ionic_reset(ionic);
err_out_teardown: err_out_teardown:
del_timer_sync(&ionic->watchdog_timer);
pci_clear_master(pdev); pci_clear_master(pdev);
/* Don't fail the probe for these errors, keep /* Don't fail the probe for these errors, keep
* the hw interface around for inspection * the hw interface around for inspection
......
...@@ -33,7 +33,8 @@ static void ionic_watchdog_cb(struct timer_list *t) ...@@ -33,7 +33,8 @@ static void ionic_watchdog_cb(struct timer_list *t)
!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) !test_bit(IONIC_LIF_F_FW_RESET, lif->state))
ionic_link_status_check_request(lif, CAN_NOT_SLEEP); 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); work = kzalloc(sizeof(*work), GFP_ATOMIC);
if (!work) { if (!work) {
netdev_err(lif->netdev, "rxmode change dropped\n"); netdev_err(lif->netdev, "rxmode change dropped\n");
...@@ -46,6 +47,24 @@ static void ionic_watchdog_cb(struct timer_list *t) ...@@ -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) void ionic_init_devinfo(struct ionic *ionic)
{ {
struct ionic_dev *idev = &ionic->idev; struct ionic_dev *idev = &ionic->idev;
...@@ -109,21 +128,7 @@ int ionic_dev_setup(struct ionic *ionic) ...@@ -109,21 +128,7 @@ int ionic_dev_setup(struct ionic *ionic)
return -EFAULT; return -EFAULT;
} }
timer_setup(&ionic->watchdog_timer, ionic_watchdog_cb, 0); ionic_watchdog_init(ionic);
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));
idev->db_pages = bar->vaddr; idev->db_pages = bar->vaddr;
idev->phy_db_pages = bar->bus_addr; idev->phy_db_pages = bar->bus_addr;
...@@ -132,10 +137,21 @@ int ionic_dev_setup(struct ionic *ionic) ...@@ -132,10 +137,21 @@ int ionic_dev_setup(struct ionic *ionic)
} }
/* Devcmd Interface */ /* 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) int ionic_heartbeat_check(struct ionic *ionic)
{ {
struct ionic_dev *idev = &ionic->idev;
unsigned long check_time, last_check_time; 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_status_ready = true;
bool fw_hb_ready; bool fw_hb_ready;
u8 fw_generation; u8 fw_generation;
...@@ -155,13 +171,10 @@ int ionic_heartbeat_check(struct ionic *ionic) ...@@ -155,13 +171,10 @@ int ionic_heartbeat_check(struct ionic *ionic)
goto do_check_time; 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); 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; fw_status_ready = false;
} else { } else {
fw_generation = fw_status & IONIC_FW_STS_F_GENERATION; fw_generation = fw_status & IONIC_FW_STS_F_GENERATION;
...@@ -176,31 +189,41 @@ int ionic_heartbeat_check(struct ionic *ionic) ...@@ -176,31 +189,41 @@ int ionic_heartbeat_check(struct ionic *ionic)
* the down, the next watchdog will see the fw is up * the down, the next watchdog will see the fw is up
* and the generation value stable, so will trigger * and the generation value stable, so will trigger
* the fw-up activity. * 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? */ /* is this a transition? */
if (fw_status_ready != idev->fw_status_ready) { if (fw_status_ready != idev->fw_status_ready) {
struct ionic_lif *lif = ionic->lif;
bool trigger = false; bool trigger = false;
idev->fw_status_ready = fw_status_ready; if (!fw_status_ready && lif &&
!test_bit(IONIC_LIF_F_FW_RESET, lif->state) &&
if (!fw_status_ready) { !test_and_set_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) {
dev_info(ionic->dev, "FW stopped %u\n", fw_status); dev_info(ionic->dev, "FW stopped 0x%02x\n", fw_status);
if (lif && !test_bit(IONIC_LIF_F_FW_RESET, lif->state)) trigger = true;
trigger = true;
} else { } else if (fw_status_ready && lif &&
dev_info(ionic->dev, "FW running %u\n", fw_status); test_bit(IONIC_LIF_F_FW_RESET, lif->state) &&
if (lif && test_bit(IONIC_LIF_F_FW_RESET, lif->state)) !test_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) {
trigger = true; dev_info(ionic->dev, "FW running 0x%02x\n", fw_status);
trigger = true;
} }
if (trigger) { if (trigger) {
struct ionic_deferred_work *work; struct ionic_deferred_work *work;
idev->fw_status_ready = fw_status_ready;
work = kzalloc(sizeof(*work), GFP_ATOMIC); work = kzalloc(sizeof(*work), GFP_ATOMIC);
if (work) { if (work) {
work->type = IONIC_DW_TYPE_LIF_RESET; work->type = IONIC_DW_TYPE_LIF_RESET;
...@@ -210,12 +233,14 @@ int ionic_heartbeat_check(struct ionic *ionic) ...@@ -210,12 +233,14 @@ int ionic_heartbeat_check(struct ionic *ionic)
} }
} }
if (!fw_status_ready) if (!idev->fw_status_ready)
return -ENXIO; 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; 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; return 0;
fw_hb = ioread32(&idev->dev_info_regs->fw_heartbeat); 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) ...@@ -392,60 +417,63 @@ void ionic_dev_cmd_port_pause(struct ionic_dev *idev, u8 pause_type)
} }
/* VF commands */ /* 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 = { union ionic_dev_cmd cmd = {
.vf_setattr.opcode = IONIC_CMD_VF_SETATTR, .vf_setattr.opcode = IONIC_CMD_VF_SETATTR,
.vf_setattr.attr = attr, .vf_setattr.attr = vfc->attr,
.vf_setattr.vf_index = cpu_to_le16(vf), .vf_setattr.vf_index = cpu_to_le16(vf),
}; };
int err; 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) { switch (attr) {
case IONIC_VF_ATTR_SPOOFCHK: 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: 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: 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: 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: 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: 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; break;
case IONIC_VF_ATTR_STATSADDR: 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: default:
return -EINVAL; return -EINVAL;
} }
mutex_lock(&ionic->dev_cmd_lock); mutex_lock(&ionic->dev_cmd_lock);
ionic_dev_cmd_go(&ionic->idev, &cmd); 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); 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; return err;
} }
......
...@@ -318,7 +318,10 @@ void ionic_dev_cmd_port_autoneg(struct ionic_dev *idev, u8 an_enable); ...@@ -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_fec(struct ionic_dev *idev, u8 fec_type);
void ionic_dev_cmd_port_pause(struct ionic_dev *idev, u8 pause_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, void ionic_dev_cmd_queue_identify(struct ionic_dev *idev,
u16 lif_type, u8 qtype, u8 qver); u16 lif_type, u8 qtype, u8 qver);
void ionic_dev_cmd_lif_identify(struct ionic_dev *idev, u8 type, u8 ver); 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); ...@@ -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, void ionic_q_service(struct ionic_queue *q, struct ionic_cq_info *cq_info,
unsigned int stop_index); unsigned int stop_index);
int ionic_heartbeat_check(struct ionic *ionic); int ionic_heartbeat_check(struct ionic *ionic);
bool ionic_is_fw_running(struct ionic_dev *idev);
#endif /* _IONIC_DEV_H_ */ #endif /* _IONIC_DEV_H_ */
...@@ -135,6 +135,7 @@ enum ionic_lif_state_flags { ...@@ -135,6 +135,7 @@ enum ionic_lif_state_flags {
IONIC_LIF_F_LINK_CHECK_REQUESTED, IONIC_LIF_F_LINK_CHECK_REQUESTED,
IONIC_LIF_F_FILTER_SYNC_NEEDED, IONIC_LIF_F_FILTER_SYNC_NEEDED,
IONIC_LIF_F_FW_RESET, IONIC_LIF_F_FW_RESET,
IONIC_LIF_F_FW_STOPPING,
IONIC_LIF_F_SPLIT_INTR, IONIC_LIF_F_SPLIT_INTR,
IONIC_LIF_F_BROKEN, IONIC_LIF_F_BROKEN,
IONIC_LIF_F_TX_DIM_INTR, IONIC_LIF_F_TX_DIM_INTR,
...@@ -213,7 +214,6 @@ struct ionic_lif { ...@@ -213,7 +214,6 @@ struct ionic_lif {
u32 rx_coalesce_hw; /* what the hw is using */ u32 rx_coalesce_hw; /* what the hw is using */
u32 tx_coalesce_usecs; /* what the user asked for */ u32 tx_coalesce_usecs; /* what the user asked for */
u32 tx_coalesce_hw; /* what the hw is using */ u32 tx_coalesce_hw; /* what the hw is using */
unsigned long *dbid_inuse;
unsigned int dbid_count; unsigned int dbid_count;
struct ionic_phc *phc; struct ionic_phc *phc;
......
...@@ -188,6 +188,28 @@ static const char *ionic_opcode_to_str(enum ionic_cmd_opcode opcode) ...@@ -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) static void ionic_adminq_flush(struct ionic_lif *lif)
{ {
struct ionic_desc_info *desc_info; struct ionic_desc_info *desc_info;
...@@ -215,9 +237,13 @@ static void ionic_adminq_flush(struct ionic_lif *lif) ...@@ -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, void ionic_adminq_netdev_err_print(struct ionic_lif *lif, u8 opcode,
u8 status, int err) 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", netdev_err(lif->netdev, "%s (%d) failed: %s (%d)\n",
ionic_opcode_to_str(opcode), opcode, ionic_opcode_to_str(opcode), opcode, stat_str, err);
ionic_error_to_str(status), err);
} }
static int ionic_adminq_check_err(struct ionic_lif *lif, 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, ...@@ -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)) if (do_msg && !test_bit(IONIC_LIF_F_FW_RESET, lif->state))
netdev_err(netdev, "Posting of %s (%d) failed: %d\n", netdev_err(netdev, "Posting of %s (%d) failed: %d\n",
name, ctx->cmd.cmd.opcode, err); name, ctx->cmd.cmd.opcode, err);
ctx->comp.comp.status = IONIC_RC_ERROR;
return err; return err;
} }
...@@ -331,11 +358,15 @@ int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, ...@@ -331,11 +358,15 @@ int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx,
if (remaining) if (remaining)
break; break;
/* interrupt the wait if FW stopped */ /* force a check of FW status and break out if FW reset */
if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) { (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) if (do_msg)
netdev_err(netdev, "%s (%d) interrupted, FW in reset\n", netdev_warn(netdev, "%s (%d) interrupted, FW in reset\n",
name, ctx->cmd.cmd.opcode); name, ctx->cmd.cmd.opcode);
ctx->comp.comp.status = IONIC_RC_ERROR;
return -ENXIO; return -ENXIO;
} }
...@@ -370,21 +401,34 @@ int ionic_adminq_post_wait_nomsg(struct ionic_lif *lif, struct ionic_admin_ctx * ...@@ -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) 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); iowrite32(0, &idev->dev_cmd_regs->doorbell);
memset_io(&regs->cmd, 0, sizeof(regs->cmd)); 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; struct ionic_dev *idev = &ionic->idev;
unsigned long start_time; unsigned long start_time;
unsigned long max_wait; unsigned long max_wait;
unsigned long duration; unsigned long duration;
int done = 0;
bool fw_up;
int opcode; int opcode;
int hb = 0;
int done;
int err; int err;
/* Wait for dev cmd to complete, retrying if we get EAGAIN, /* 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) ...@@ -394,31 +438,24 @@ int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds)
try_again: try_again:
opcode = readb(&idev->dev_cmd_regs->cmd.cmd.opcode); opcode = readb(&idev->dev_cmd_regs->cmd.cmd.opcode);
start_time = jiffies; 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); done = ionic_dev_cmd_done(idev);
if (done) if (done)
break; break;
usleep_range(100, 200); 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; duration = jiffies - start_time;
dev_dbg(ionic->dev, "DEVCMD %s (%d) done=%d took %ld secs (%ld jiffies)\n", dev_dbg(ionic->dev, "DEVCMD %s (%d) done=%d took %ld secs (%ld jiffies)\n",
ionic_opcode_to_str(opcode), opcode, ionic_opcode_to_str(opcode), opcode,
done, duration / HZ, duration); done, duration / HZ, duration);
if (!done && hb) { if (!done && !fw_up) {
/* It is possible (but unlikely) that FW was busy and missed a ionic_dev_cmd_clean(ionic);
* heartbeat check but is still alive and will process this dev_warn(ionic->dev, "DEVCMD %s (%d) interrupted - FW is down\n",
* request, so don't clean the dev_cmd in this case. ionic_opcode_to_str(opcode), opcode);
*/
dev_dbg(ionic->dev, "DEVCMD %s (%d) failed - FW halted\n",
ionic_opcode_to_str(opcode), opcode);
return -ENXIO; return -ENXIO;
} }
...@@ -444,9 +481,9 @@ int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds) ...@@ -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)) if (!(opcode == IONIC_CMD_FW_CONTROL && err == IONIC_RC_EAGAIN))
dev_err(ionic->dev, "DEV_CMD %s (%d) error, %s (%d) failed\n", if (do_msg)
ionic_opcode_to_str(opcode), opcode, ionic_dev_cmd_dev_err_print(ionic, opcode, err,
ionic_error_to_str(err), err); ionic_error_to_errno(err));
return 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) ...@@ -454,6 +491,16 @@ int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds)
return 0; 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 ionic_setup(struct ionic *ionic)
{ {
int err; int err;
...@@ -540,6 +587,9 @@ int ionic_reset(struct ionic *ionic) ...@@ -540,6 +587,9 @@ int ionic_reset(struct ionic *ionic)
struct ionic_dev *idev = &ionic->idev; struct ionic_dev *idev = &ionic->idev;
int err; int err;
if (!ionic_is_fw_running(idev))
return 0;
mutex_lock(&ionic->dev_cmd_lock); mutex_lock(&ionic->dev_cmd_lock);
ionic_dev_cmd_reset(idev); ionic_dev_cmd_reset(idev);
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
...@@ -612,15 +662,17 @@ int ionic_port_init(struct ionic *ionic) ...@@ -612,15 +662,17 @@ int ionic_port_init(struct ionic *ionic)
int ionic_port_reset(struct ionic *ionic) int ionic_port_reset(struct ionic *ionic)
{ {
struct ionic_dev *idev = &ionic->idev; struct ionic_dev *idev = &ionic->idev;
int err; int err = 0;
if (!idev->port_info) if (!idev->port_info)
return 0; return 0;
mutex_lock(&ionic->dev_cmd_lock); if (ionic_is_fw_running(idev)) {
ionic_dev_cmd_port_reset(idev); mutex_lock(&ionic->dev_cmd_lock);
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); ionic_dev_cmd_port_reset(idev);
mutex_unlock(&ionic->dev_cmd_lock); err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
mutex_unlock(&ionic->dev_cmd_lock);
}
dma_free_coherent(ionic->dev, idev->port_info_sz, dma_free_coherent(ionic->dev, idev->port_info_sz,
idev->port_info, idev->port_info_pa); idev->port_info, idev->port_info_pa);
...@@ -628,9 +680,6 @@ int ionic_port_reset(struct ionic *ionic) ...@@ -628,9 +680,6 @@ int ionic_port_reset(struct ionic *ionic)
idev->port_info = NULL; idev->port_info = NULL;
idev->port_info_pa = 0; idev->port_info_pa = 0;
if (err)
dev_err(ionic->dev, "Failed to reset port\n");
return err; return err;
} }
......
...@@ -376,10 +376,24 @@ static int ionic_lif_filter_add(struct ionic_lif *lif, ...@@ -376,10 +376,24 @@ static int ionic_lif_filter_add(struct ionic_lif *lif,
spin_unlock_bh(&lif->rx_filters.lock); spin_unlock_bh(&lif->rx_filters.lock);
if (err == -ENOSPC) { /* store the max_vlans limit that we found */
if (le16_to_cpu(ctx.cmd.rx_filter_add.match) == IONIC_RX_FILTER_MATCH_VLAN) if (err == -ENOSPC &&
lif->max_vlans = lif->nvlans; 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; return 0;
default:
break;
} }
ionic_adminq_netdev_err_print(lif, ctx.cmd.cmd.opcode, ionic_adminq_netdev_err_print(lif, ctx.cmd.cmd.opcode,
...@@ -494,9 +508,22 @@ static int ionic_lif_filter_del(struct ionic_lif *lif, ...@@ -494,9 +508,22 @@ static int ionic_lif_filter_del(struct ionic_lif *lif,
spin_unlock_bh(&lif->rx_filters.lock); spin_unlock_bh(&lif->rx_filters.lock);
if (state != IONIC_FILTER_STATE_NEW) { if (state != IONIC_FILTER_STATE_NEW) {
err = ionic_adminq_post_wait(lif, &ctx); err = ionic_adminq_post_wait_nomsg(lif, &ctx);
if (err && err != -EEXIST)
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 err;
}
} }
return 0; return 0;
......
...@@ -669,27 +669,37 @@ static int ionic_tx_map_skb(struct ionic_queue *q, struct sk_buff *skb, ...@@ -669,27 +669,37 @@ static int ionic_tx_map_skb(struct ionic_queue *q, struct sk_buff *skb,
return -EIO; 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, static void ionic_tx_clean(struct ionic_queue *q,
struct ionic_desc_info *desc_info, struct ionic_desc_info *desc_info,
struct ionic_cq_info *cq_info, struct ionic_cq_info *cq_info,
void *cb_arg) void *cb_arg)
{ {
struct ionic_buf_info *buf_info = desc_info->bufs;
struct ionic_tx_stats *stats = q_to_tx_stats(q); struct ionic_tx_stats *stats = q_to_tx_stats(q);
struct ionic_qcq *qcq = q_to_qcq(q); struct ionic_qcq *qcq = q_to_qcq(q);
struct sk_buff *skb = cb_arg; struct sk_buff *skb = cb_arg;
struct device *dev = q->dev;
unsigned int i;
u16 qi; u16 qi;
if (desc_info->nbufs) { ionic_tx_desc_unmap_bufs(q, desc_info);
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);
}
if (!skb) if (!skb)
return; return;
...@@ -931,8 +941,11 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) ...@@ -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); err = ionic_tx_tcp_inner_pseudo_csum(skb);
else else
err = ionic_tx_tcp_pseudo_csum(skb); 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; return err;
}
if (encap) if (encap)
hdrlen = skb_inner_transport_header(skb) - skb->data + 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) ...@@ -1003,8 +1016,8 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb)
return 0; return 0;
} }
static int ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb, static void ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb,
struct ionic_desc_info *desc_info) struct ionic_desc_info *desc_info)
{ {
struct ionic_txq_desc *desc = desc_info->txq_desc; struct ionic_txq_desc *desc = desc_info->txq_desc;
struct ionic_buf_info *buf_info = desc_info->bufs; 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, ...@@ -1038,12 +1051,10 @@ static int ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb,
stats->crc32_csum++; stats->crc32_csum++;
else else
stats->csum++; stats->csum++;
return 0;
} }
static int ionic_tx_calc_no_csum(struct ionic_queue *q, struct sk_buff *skb, static void ionic_tx_calc_no_csum(struct ionic_queue *q, struct sk_buff *skb,
struct ionic_desc_info *desc_info) struct ionic_desc_info *desc_info)
{ {
struct ionic_txq_desc *desc = desc_info->txq_desc; struct ionic_txq_desc *desc = desc_info->txq_desc;
struct ionic_buf_info *buf_info = desc_info->bufs; 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, ...@@ -1074,12 +1085,10 @@ static int ionic_tx_calc_no_csum(struct ionic_queue *q, struct sk_buff *skb,
desc->csum_offset = 0; desc->csum_offset = 0;
stats->csum_none++; stats->csum_none++;
return 0;
} }
static int ionic_tx_skb_frags(struct ionic_queue *q, struct sk_buff *skb, static void ionic_tx_skb_frags(struct ionic_queue *q, struct sk_buff *skb,
struct ionic_desc_info *desc_info) struct ionic_desc_info *desc_info)
{ {
struct ionic_txq_sg_desc *sg_desc = desc_info->txq_sg_desc; struct ionic_txq_sg_desc *sg_desc = desc_info->txq_sg_desc;
struct ionic_buf_info *buf_info = &desc_info->bufs[1]; 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, ...@@ -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; stats->frags += skb_shinfo(skb)->nr_frags;
return 0;
} }
static int ionic_tx(struct ionic_queue *q, struct sk_buff *skb) 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_desc_info *desc_info = &q->info[q->head_idx];
struct ionic_tx_stats *stats = q_to_tx_stats(q); struct ionic_tx_stats *stats = q_to_tx_stats(q);
int err;
if (unlikely(ionic_tx_map_skb(q, skb, desc_info))) if (unlikely(ionic_tx_map_skb(q, skb, desc_info)))
return -EIO; return -EIO;
/* set up the initial descriptor */ /* set up the initial descriptor */
if (skb->ip_summed == CHECKSUM_PARTIAL) if (skb->ip_summed == CHECKSUM_PARTIAL)
err = ionic_tx_calc_csum(q, skb, desc_info); ionic_tx_calc_csum(q, skb, desc_info);
else else
err = ionic_tx_calc_no_csum(q, skb, desc_info); ionic_tx_calc_no_csum(q, skb, desc_info);
if (err)
return err;
/* add frags */ /* add frags */
err = ionic_tx_skb_frags(q, skb, desc_info); ionic_tx_skb_frags(q, skb, desc_info);
if (err)
return err;
skb_tx_timestamp(skb); skb_tx_timestamp(skb);
stats->pkts++; 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