Commit 0d5d6045 authored by David S. Miller's avatar David S. Miller

Merge branch 'ionic-support-for-firmware-upgrade'

Shannon Nelson says:

====================
ionic support for firmware upgrade

The Pensando Distributed Services Card can get firmware upgrades from
the off-host centralized management suite, and can be upgraded without a
host reboot or driver reload.  This patchset sets up the support for fw
upgrade in the Linux driver.

When the upgrade begins, the DSC first brings the link down, then stops
the firmware.  The driver will notice this and quiesce itself by stopping
the queues and releasing DMA resources, then monitoring for firmware to
start back up.  When the upgrade is finished the firmware is restarted
and link is brought up, and the driver rebuilds the queues and restarts
traffic flow.

First we separate the Link state from the netdev state, then reorganize a
few things to prepare for partial tear-down of the queues.  Next we fix
up the state machine so that we take the Tx and Rx queues down and back
up when we get LINK_DOWN and LINK_UP events.  Lastly, we add handling of
the FW reset itself by tearing down the lif internals and rebuilding them
with the new FW setup.

v2: This changes the design from (ab)using the full .ndo_stop and
    .ndo_open routines to getting a better separation between the
    alloc and the init functions so that we can keep our resource
    allocations as long as possible.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents ea315c55 c672412f
...@@ -14,11 +14,15 @@ ...@@ -14,11 +14,15 @@
static void ionic_watchdog_cb(struct timer_list *t) static void ionic_watchdog_cb(struct timer_list *t)
{ {
struct ionic *ionic = from_timer(ionic, t, watchdog_timer); struct ionic *ionic = from_timer(ionic, t, watchdog_timer);
int hb;
mod_timer(&ionic->watchdog_timer, mod_timer(&ionic->watchdog_timer,
round_jiffies(jiffies + ionic->watchdog_period)); round_jiffies(jiffies + ionic->watchdog_period));
ionic_heartbeat_check(ionic); hb = ionic_heartbeat_check(ionic);
if (hb >= 0 && ionic->master_lif)
ionic_link_status_check_request(ionic->master_lif);
} }
void ionic_init_devinfo(struct ionic *ionic) void ionic_init_devinfo(struct ionic *ionic)
...@@ -82,6 +86,7 @@ int ionic_dev_setup(struct ionic *ionic) ...@@ -82,6 +86,7 @@ int ionic_dev_setup(struct ionic *ionic)
return -EFAULT; return -EFAULT;
} }
idev->last_fw_status = 0xff;
timer_setup(&ionic->watchdog_timer, ionic_watchdog_cb, 0); timer_setup(&ionic->watchdog_timer, ionic_watchdog_cb, 0);
ionic->watchdog_period = IONIC_WATCHDOG_SECS * HZ; ionic->watchdog_period = IONIC_WATCHDOG_SECS * HZ;
mod_timer(&ionic->watchdog_timer, mod_timer(&ionic->watchdog_timer,
...@@ -115,8 +120,43 @@ int ionic_heartbeat_check(struct ionic *ionic) ...@@ -115,8 +120,43 @@ int ionic_heartbeat_check(struct ionic *ionic)
* fw_status != 0xff (bad PCI read) * fw_status != 0xff (bad PCI read)
*/ */
fw_status = ioread8(&idev->dev_info_regs->fw_status); fw_status = ioread8(&idev->dev_info_regs->fw_status);
if (fw_status == 0xff || if (fw_status != 0xff)
!(fw_status & IONIC_FW_STS_F_RUNNING)) 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; return -ENXIO;
/* early FW has no heartbeat, else FW will return non-zero */ /* early FW has no heartbeat, else FW will return non-zero */
......
...@@ -132,6 +132,7 @@ struct ionic_dev { ...@@ -132,6 +132,7 @@ struct ionic_dev {
unsigned long last_hb_time; unsigned long last_hb_time;
u32 last_hb; u32 last_hb;
u8 last_fw_status;
u64 __iomem *db_pages; u64 __iomem *db_pages;
dma_addr_t phy_db_pages; dma_addr_t phy_db_pages;
......
...@@ -98,6 +98,7 @@ struct ionic_deferred_work { ...@@ -98,6 +98,7 @@ struct ionic_deferred_work {
union { union {
unsigned int rx_mode; unsigned int rx_mode;
u8 addr[ETH_ALEN]; u8 addr[ETH_ALEN];
u8 fw_status;
}; };
}; };
...@@ -126,6 +127,7 @@ enum ionic_lif_state_flags { ...@@ -126,6 +127,7 @@ enum ionic_lif_state_flags {
IONIC_LIF_F_UP, IONIC_LIF_F_UP,
IONIC_LIF_F_LINK_CHECK_REQUESTED, IONIC_LIF_F_LINK_CHECK_REQUESTED,
IONIC_LIF_F_QUEUE_RESET, IONIC_LIF_F_QUEUE_RESET,
IONIC_LIF_F_FW_RESET,
/* leave this as last */ /* leave this as last */
IONIC_LIF_F_STATE_SIZE IONIC_LIF_F_STATE_SIZE
...@@ -224,6 +226,9 @@ static inline u32 ionic_coal_hw_to_usec(struct ionic *ionic, u32 units) ...@@ -224,6 +226,9 @@ static inline u32 ionic_coal_hw_to_usec(struct ionic *ionic, u32 units)
return (units * div) / mult; return (units * div) / mult;
} }
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); int ionic_lifs_alloc(struct ionic *ionic);
void ionic_lifs_free(struct ionic *ionic); void ionic_lifs_free(struct ionic *ionic);
void ionic_lifs_deinit(struct ionic *ionic); void ionic_lifs_deinit(struct ionic *ionic);
......
...@@ -286,9 +286,11 @@ int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx) ...@@ -286,9 +286,11 @@ int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)
err = ionic_adminq_post(lif, ctx); err = ionic_adminq_post(lif, ctx);
if (err) { if (err) {
name = ionic_opcode_to_str(ctx->cmd.cmd.opcode); if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) {
netdev_err(netdev, "Posting of %s (%d) failed: %d\n", name = ionic_opcode_to_str(ctx->cmd.cmd.opcode);
name, ctx->cmd.cmd.opcode, err); netdev_err(netdev, "Posting of %s (%d) failed: %d\n",
name, ctx->cmd.cmd.opcode, err);
}
return err; return err;
} }
......
...@@ -593,6 +593,22 @@ void ionic_tx_flush(struct ionic_cq *cq) ...@@ -593,6 +593,22 @@ void ionic_tx_flush(struct ionic_cq *cq)
work_done, 0); work_done, 0);
} }
void ionic_tx_empty(struct ionic_queue *q)
{
struct ionic_desc_info *desc_info;
int done = 0;
/* walk the not completed tx entries, if any */
while (q->head != q->tail) {
desc_info = q->tail;
q->tail = desc_info->next;
ionic_tx_clean(q, desc_info, NULL, desc_info->cb_arg);
desc_info->cb = NULL;
desc_info->cb_arg = NULL;
done++;
}
}
static int ionic_tx_tcp_inner_pseudo_csum(struct sk_buff *skb) static int ionic_tx_tcp_inner_pseudo_csum(struct sk_buff *skb)
{ {
int err; int err;
......
...@@ -9,6 +9,7 @@ void ionic_tx_flush(struct ionic_cq *cq); ...@@ -9,6 +9,7 @@ void ionic_tx_flush(struct ionic_cq *cq);
void ionic_rx_fill(struct ionic_queue *q); void ionic_rx_fill(struct ionic_queue *q);
void ionic_rx_empty(struct ionic_queue *q); void ionic_rx_empty(struct ionic_queue *q);
void ionic_tx_empty(struct ionic_queue *q);
int ionic_rx_napi(struct napi_struct *napi, int budget); int ionic_rx_napi(struct napi_struct *napi, int budget);
netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev); netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev);
......
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