Skip to content

Commit

Permalink
ionic: remove lifs on fw reset
Browse files Browse the repository at this point in the history
When the FW RESET event comes to the driver from the firmware,
or the fw_status goes to 0 (stopped) or to 0xff (no PCI
connection), then shut down the driver activity.  This event
signals a FW upgrade where we need to quiesce all operations and
wait for the FW to restart.  The FW will continue the update
process once it sees all the LIFs are reset.  When the update
process is done it will set the fw_status back to RUNNING.
Meanwhile, the heartbeat check continues and when the fw_status
is seen as set to running we can restart the driver operations.

Signed-off-by: Shannon Nelson <snelson@pensando.io>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Shannon Nelson authored and David S. Miller committed Mar 30, 2020
1 parent 49d3b49 commit c672412
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 25 deletions.
40 changes: 38 additions & 2 deletions drivers/net/ethernet/pensando/ionic/ionic_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ int ionic_dev_setup(struct ionic *ionic)
return -EFAULT;
}

idev->last_fw_status = 0xff;
timer_setup(&ionic->watchdog_timer, ionic_watchdog_cb, 0);
ionic->watchdog_period = IONIC_WATCHDOG_SECS * HZ;
mod_timer(&ionic->watchdog_timer,
Expand Down Expand Up @@ -119,8 +120,43 @@ int ionic_heartbeat_check(struct ionic *ionic)
* fw_status != 0xff (bad PCI read)
*/
fw_status = ioread8(&idev->dev_info_regs->fw_status);
if (fw_status == 0xff ||
!(fw_status & IONIC_FW_STS_F_RUNNING))
if (fw_status != 0xff)
fw_status &= IONIC_FW_STS_F_RUNNING; /* use only the run bit */

/* is this a transition? */
if (fw_status != idev->last_fw_status &&
idev->last_fw_status != 0xff) {
struct ionic_lif *lif = ionic->master_lif;
bool trigger = false;

if (!fw_status || fw_status == 0xff) {
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 (trigger) {
struct ionic_deferred_work *work;

work = kzalloc(sizeof(*work), GFP_ATOMIC);
if (!work) {
dev_err(ionic->dev, "%s OOM\n", __func__);
} else {
work->type = IONIC_DW_TYPE_LIF_RESET;
if (fw_status & IONIC_FW_STS_F_RUNNING &&
fw_status != 0xff)
work->fw_status = 1;
ionic_lif_deferred_enqueue(&lif->deferred, work);
}
}
}
idev->last_fw_status = fw_status;

if (!fw_status || fw_status == 0xff)
return -ENXIO;

/* early FW has no heartbeat, else FW will return non-zero */
Expand Down
1 change: 1 addition & 0 deletions drivers/net/ethernet/pensando/ionic/ionic_dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ struct ionic_dev {

unsigned long last_hb_time;
u32 last_hb;
u8 last_fw_status;

u64 __iomem *db_pages;
dma_addr_t phy_db_pages;
Expand Down
130 changes: 110 additions & 20 deletions drivers/net/ethernet/pensando/ionic/ionic_lif.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ static void ionic_lif_rx_mode(struct ionic_lif *lif, unsigned int rx_mode);
static int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr);
static int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr);
static void ionic_link_status_check(struct ionic_lif *lif);
static void ionic_lif_handle_fw_down(struct ionic_lif *lif);
static void ionic_lif_handle_fw_up(struct ionic_lif *lif);
static void ionic_lif_set_netdev_info(struct ionic_lif *lif);

static int ionic_start_queues(struct ionic_lif *lif);
static void ionic_stop_queues(struct ionic_lif *lif);
Expand Down Expand Up @@ -53,6 +56,12 @@ static void ionic_lif_deferred_work(struct work_struct *work)
case IONIC_DW_TYPE_LINK_STATUS:
ionic_link_status_check(lif);
break;
case IONIC_DW_TYPE_LIF_RESET:
if (w->fw_status)
ionic_lif_handle_fw_up(lif);
else
ionic_lif_handle_fw_down(lif);
break;
default:
break;
}
Expand All @@ -61,8 +70,8 @@ static void ionic_lif_deferred_work(struct work_struct *work)
}
}

static void ionic_lif_deferred_enqueue(struct ionic_deferred *def,
struct ionic_deferred_work *work)
void ionic_lif_deferred_enqueue(struct ionic_deferred *def,
struct ionic_deferred_work *work)
{
spin_lock_bh(&def->lock);
list_add_tail(&work->list, &def->list);
Expand Down Expand Up @@ -682,6 +691,7 @@ static bool ionic_notifyq_service(struct ionic_cq *cq,
struct ionic_cq_info *cq_info)
{
union ionic_notifyq_comp *comp = cq_info->cq_desc;
struct ionic_deferred_work *work;
struct net_device *netdev;
struct ionic_queue *q;
struct ionic_lif *lif;
Expand All @@ -707,11 +717,13 @@ static bool ionic_notifyq_service(struct ionic_cq *cq,
ionic_link_status_check_request(lif);
break;
case IONIC_EVENT_RESET:
netdev_info(netdev, "Notifyq IONIC_EVENT_RESET eid=%lld\n",
eid);
netdev_info(netdev, " reset_code=%d state=%d\n",
comp->reset.reset_code,
comp->reset.state);
work = kzalloc(sizeof(*work), GFP_ATOMIC);
if (!work) {
netdev_err(lif->netdev, "%s OOM\n", __func__);
} else {
work->type = IONIC_DW_TYPE_LIF_RESET;
ionic_lif_deferred_enqueue(&lif->deferred, work);
}
break;
default:
netdev_warn(netdev, "Notifyq unknown event ecode=%d eid=%lld\n",
Expand Down Expand Up @@ -1224,7 +1236,8 @@ static int ionic_init_nic_features(struct ionic_lif *lif)
netdev->hw_features |= netdev->hw_enc_features;
netdev->features |= netdev->hw_features;

netdev->priv_flags |= IFF_UNICAST_FLT;
netdev->priv_flags |= IFF_UNICAST_FLT |
IFF_LIVE_ADDR_CHANGE;

return 0;
}
Expand Down Expand Up @@ -1669,6 +1682,9 @@ int ionic_stop(struct net_device *netdev)
{
struct ionic_lif *lif = netdev_priv(netdev);

if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
return 0;

ionic_stop_queues(lif);
ionic_txrx_deinit(lif);
ionic_txrx_free(lif);
Expand Down Expand Up @@ -2064,6 +2080,80 @@ static void ionic_lif_reset(struct ionic_lif *lif)
mutex_unlock(&lif->ionic->dev_cmd_lock);
}

static void ionic_lif_handle_fw_down(struct ionic_lif *lif)
{
struct ionic *ionic = lif->ionic;

if (test_and_set_bit(IONIC_LIF_F_FW_RESET, lif->state))
return;

dev_info(ionic->dev, "FW Down: Stopping LIFs\n");

netif_device_detach(lif->netdev);

if (test_bit(IONIC_LIF_F_UP, lif->state)) {
dev_info(ionic->dev, "Surprise FW stop, stopping queues\n");
ionic_stop_queues(lif);
}

if (netif_running(lif->netdev)) {
ionic_txrx_deinit(lif);
ionic_txrx_free(lif);
}
ionic_lifs_deinit(ionic);
ionic_qcqs_free(lif);

dev_info(ionic->dev, "FW Down: LIFs stopped\n");
}

static void ionic_lif_handle_fw_up(struct ionic_lif *lif)
{
struct ionic *ionic = lif->ionic;
int err;

if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state))
return;

dev_info(ionic->dev, "FW Up: restarting LIFs\n");

err = ionic_qcqs_alloc(lif);
if (err)
goto err_out;

err = ionic_lifs_init(ionic);
if (err)
goto err_qcqs_free;

if (lif->registered)
ionic_lif_set_netdev_info(lif);

if (netif_running(lif->netdev)) {
err = ionic_txrx_alloc(lif);
if (err)
goto err_lifs_deinit;

err = ionic_txrx_init(lif);
if (err)
goto err_txrx_free;
}

clear_bit(IONIC_LIF_F_FW_RESET, lif->state);
ionic_link_status_check_request(lif);
netif_device_attach(lif->netdev);
dev_info(ionic->dev, "FW Up: LIFs restarted\n");

return;

err_txrx_free:
ionic_txrx_free(lif);
err_lifs_deinit:
ionic_lifs_deinit(ionic);
err_qcqs_free:
ionic_qcqs_free(lif);
err_out:
dev_err(ionic->dev, "FW Up: LIFs restart failed - err %d\n", err);
}

static void ionic_lif_free(struct ionic_lif *lif)
{
struct device *dev = lif->ionic->dev;
Expand All @@ -2076,7 +2166,8 @@ static void ionic_lif_free(struct ionic_lif *lif)

/* free queues */
ionic_qcqs_free(lif);
ionic_lif_reset(lif);
if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state))
ionic_lif_reset(lif);

/* free lif info */
dma_free_coherent(dev, lif->info_sz, lif->info, lif->info_pa);
Expand Down Expand Up @@ -2109,17 +2200,19 @@ void ionic_lifs_free(struct ionic *ionic)

static void ionic_lif_deinit(struct ionic_lif *lif)
{
if (!test_bit(IONIC_LIF_F_INITED, lif->state))
if (!test_and_clear_bit(IONIC_LIF_F_INITED, lif->state))
return;

clear_bit(IONIC_LIF_F_INITED, lif->state);
if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) {
cancel_work_sync(&lif->deferred.work);
cancel_work_sync(&lif->tx_timeout_work);
}

ionic_rx_filters_deinit(lif);
if (lif->netdev->features & NETIF_F_RXHASH)
ionic_lif_rss_deinit(lif);

napi_disable(&lif->adminqcq->napi);
netif_napi_del(&lif->adminqcq->napi);
ionic_lif_qcq_deinit(lif, lif->notifyqcq);
ionic_lif_qcq_deinit(lif, lif->adminqcq);

Expand Down Expand Up @@ -2213,6 +2306,7 @@ static int ionic_lif_notifyq_init(struct ionic_lif *lif)
if (err)
return err;

lif->last_eid = 0;
q->hw_type = ctx.comp.q_init.hw_type;
q->hw_index = le32_to_cpu(ctx.comp.q_init.hw_index);
q->dbval = IONIC_DBELL_QID(q->hw_index);
Expand Down Expand Up @@ -2253,8 +2347,8 @@ static int ionic_station_set(struct ionic_lif *lif)
addr.sa_family = AF_INET;
err = eth_prepare_mac_addr_change(netdev, &addr);
if (err) {
netdev_warn(lif->netdev, "ignoring bad MAC addr from NIC %pM\n",
addr.sa_data);
netdev_warn(lif->netdev, "ignoring bad MAC addr from NIC %pM - err %d\n",
addr.sa_data, err);
return 0;
}

Expand Down Expand Up @@ -2464,12 +2558,8 @@ void ionic_lifs_unregister(struct ionic *ionic)
* current model, so don't bother searching the
* ionic->lif for candidates to unregister
*/
if (!ionic->master_lif)
return;

cancel_work_sync(&ionic->master_lif->deferred.work);
cancel_work_sync(&ionic->master_lif->tx_timeout_work);
if (ionic->master_lif->netdev->reg_state == NETREG_REGISTERED)
if (ionic->master_lif &&
ionic->master_lif->netdev->reg_state == NETREG_REGISTERED)
unregister_netdev(ionic->master_lif->netdev);
}

Expand Down
4 changes: 4 additions & 0 deletions drivers/net/ethernet/pensando/ionic/ionic_lif.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ struct ionic_deferred_work {
union {
unsigned int rx_mode;
u8 addr[ETH_ALEN];
u8 fw_status;
};
};

Expand Down Expand Up @@ -126,6 +127,7 @@ enum ionic_lif_state_flags {
IONIC_LIF_F_UP,
IONIC_LIF_F_LINK_CHECK_REQUESTED,
IONIC_LIF_F_QUEUE_RESET,
IONIC_LIF_F_FW_RESET,

/* leave this as last */
IONIC_LIF_F_STATE_SIZE
Expand Down Expand Up @@ -225,6 +227,8 @@ static inline u32 ionic_coal_hw_to_usec(struct ionic *ionic, u32 units)
}

void ionic_link_status_check_request(struct ionic_lif *lif);
void ionic_lif_deferred_enqueue(struct ionic_deferred *def,
struct ionic_deferred_work *work);
int ionic_lifs_alloc(struct ionic *ionic);
void ionic_lifs_free(struct ionic *ionic);
void ionic_lifs_deinit(struct ionic *ionic);
Expand Down
8 changes: 5 additions & 3 deletions drivers/net/ethernet/pensando/ionic/ionic_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,9 +286,11 @@ int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)

err = ionic_adminq_post(lif, ctx);
if (err) {
name = ionic_opcode_to_str(ctx->cmd.cmd.opcode);
netdev_err(netdev, "Posting of %s (%d) failed: %d\n",
name, ctx->cmd.cmd.opcode, err);
if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) {
name = ionic_opcode_to_str(ctx->cmd.cmd.opcode);
netdev_err(netdev, "Posting of %s (%d) failed: %d\n",
name, ctx->cmd.cmd.opcode, err);
}
return err;
}

Expand Down

0 comments on commit c672412

Please sign in to comment.