Skip to content

Commit

Permalink
Merge branch 'ionic-rework-fix-for-doorbell-miss'
Browse files Browse the repository at this point in the history
Shannon Nelson says:

====================
ionic: rework fix for doorbell miss

A latency test in a scaled out setting (many VMs with many queues)
has uncovered an issue with our missed doorbell fix from
commit b69585b ("ionic: missed doorbell workaround")

As a refresher, the Elba ASIC has an issue where once in a blue
moon it might miss/drop a queue doorbell notification from
the driver.  This can result in Tx timeouts and potential Rx
buffer misses.

The basic problem with the original solution is that
we're delaying things with a timer for every single queue,
periodically using mod_timer() to reset to reset the alarm, and
mod_timer() becomes a more and more expensive thing as there
are more and more VFs and queues each with their own timer.
A ping-pong latency test tends to exacerbate the effect such
that every napi is doing a mod_timer() in every cycle.

An alternative has been worked out to replace this using
periodic workqueue items outside the napi cycle to request a
napi_schedule driven by a single delayed-workqueue per device
rather than a timer for every queue.  Also, now that newer
firmware is actually reporting its ASIC type, we can restrict
this to the appropriate chip.

The testing scenario used 128 VFs in UP state, 16 queues per
VF, and latency tests were done using TCP_RR with adaptive
interrupt coalescing enabled, running on 1 VF.  We would see
99th percentile latencies of up to 900us range, with some max
fliers as much as 4ms.

With these fixes the 99th percentile latencies are typically well
under 50us with the occasional max under 500us.

v1:
https://lore.kernel.org/netdev/20240610230706.34883-1-shannon.nelson@amd.com/
====================

Link: https://lore.kernel.org/r/20240619003257.6138-1-shannon.nelson@amd.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Jakub Kicinski committed Jun 20, 2024
2 parents 6f80fcd + da0262c commit 7e8fcb8
Show file tree
Hide file tree
Showing 9 changed files with 264 additions and 76 deletions.
7 changes: 7 additions & 0 deletions drivers/net/ethernet/pensando/ionic/ionic.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ struct ionic_lif;
#define PCI_DEVICE_ID_PENSANDO_IONIC_ETH_PF 0x1002
#define PCI_DEVICE_ID_PENSANDO_IONIC_ETH_VF 0x1003

#define IONIC_ASIC_TYPE_ELBA 2

#define DEVCMD_TIMEOUT 5
#define IONIC_ADMINQ_TIME_SLICE msecs_to_jiffies(100)

Expand Down Expand Up @@ -47,13 +49,16 @@ struct ionic {
struct ionic_dev_bar bars[IONIC_BARS_MAX];
unsigned int num_bars;
struct ionic_identity ident;
struct workqueue_struct *wq;
struct ionic_lif *lif;
unsigned int nnqs_per_lif;
unsigned int neqs_per_lif;
unsigned int ntxqs_per_lif;
unsigned int nrxqs_per_lif;
unsigned int nintrs;
DECLARE_BITMAP(intrs, IONIC_INTR_CTRL_REGS_MAX);
cpumask_var_t *affinity_masks;
struct delayed_work doorbell_check_dwork;
struct work_struct nb_work;
struct notifier_block nb;
struct rw_semaphore vf_op_lock; /* lock for VF operations */
Expand Down Expand Up @@ -93,4 +98,6 @@ int ionic_port_identify(struct ionic *ionic);
int ionic_port_init(struct ionic *ionic);
int ionic_port_reset(struct ionic *ionic);

bool ionic_doorbell_wa(struct ionic *ionic);

#endif /* _IONIC_H_ */
3 changes: 3 additions & 0 deletions drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)

mod_timer(&ionic->watchdog_timer,
round_jiffies(jiffies + ionic->watchdog_period));
ionic_queue_doorbell_check(ionic, IONIC_NAPI_DEADLINE);

return 0;

Expand Down Expand Up @@ -411,6 +412,8 @@ static void ionic_remove(struct pci_dev *pdev)
if (test_and_clear_bit(IONIC_LIF_F_FW_RESET, ionic->lif->state))
set_bit(IONIC_LIF_F_FW_STOPPING, ionic->lif->state);

if (ionic->lif->doorbell_wa)
cancel_delayed_work_sync(&ionic->doorbell_check_dwork);
ionic_lif_unregister(ionic->lif);
ionic_devlink_unregister(ionic);
ionic_lif_deinit(ionic->lif);
Expand Down
129 changes: 121 additions & 8 deletions drivers/net/ethernet/pensando/ionic/ionic_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,99 @@ static void ionic_watchdog_cb(struct timer_list *t)

work->type = IONIC_DW_TYPE_RX_MODE;
netdev_dbg(lif->netdev, "deferred: rx_mode\n");
ionic_lif_deferred_enqueue(&lif->deferred, work);
ionic_lif_deferred_enqueue(lif, work);
}
}

static void ionic_watchdog_init(struct ionic *ionic)
static void ionic_napi_schedule_do_softirq(struct napi_struct *napi)
{
local_bh_disable();
napi_schedule(napi);
local_bh_enable();
}

void ionic_doorbell_napi_work(struct work_struct *work)
{
struct ionic_qcq *qcq = container_of(work, struct ionic_qcq,
doorbell_napi_work);
unsigned long now, then, dif;

now = READ_ONCE(jiffies);
then = qcq->q.dbell_jiffies;
dif = now - then;

if (dif > qcq->q.dbell_deadline)
ionic_napi_schedule_do_softirq(&qcq->napi);
}

static int ionic_get_preferred_cpu(struct ionic *ionic,
struct ionic_intr_info *intr)
{
int cpu;

cpu = cpumask_first_and(*intr->affinity_mask, cpu_online_mask);
if (cpu >= nr_cpu_ids)
cpu = cpumask_local_spread(0, dev_to_node(ionic->dev));

return cpu;
}

static void ionic_queue_dbell_napi_work(struct ionic *ionic,
struct ionic_qcq *qcq)
{
int cpu;

if (!(qcq->flags & IONIC_QCQ_F_INTR))
return;

cpu = ionic_get_preferred_cpu(ionic, &qcq->intr);
queue_work_on(cpu, ionic->wq, &qcq->doorbell_napi_work);
}

static void ionic_doorbell_check_dwork(struct work_struct *work)
{
struct ionic *ionic = container_of(work, struct ionic,
doorbell_check_dwork.work);
struct ionic_lif *lif = ionic->lif;

mutex_lock(&lif->queue_lock);

if (test_bit(IONIC_LIF_F_FW_STOPPING, lif->state) ||
test_bit(IONIC_LIF_F_FW_RESET, lif->state)) {
mutex_unlock(&lif->queue_lock);
return;
}

ionic_napi_schedule_do_softirq(&lif->adminqcq->napi);

if (test_bit(IONIC_LIF_F_UP, lif->state)) {
int i;

for (i = 0; i < lif->nxqs; i++) {
ionic_queue_dbell_napi_work(ionic, lif->txqcqs[i]);
ionic_queue_dbell_napi_work(ionic, lif->rxqcqs[i]);
}

if (lif->hwstamp_txq &&
lif->hwstamp_txq->flags & IONIC_QCQ_F_INTR)
ionic_napi_schedule_do_softirq(&lif->hwstamp_txq->napi);
if (lif->hwstamp_rxq &&
lif->hwstamp_rxq->flags & IONIC_QCQ_F_INTR)
ionic_napi_schedule_do_softirq(&lif->hwstamp_rxq->napi);
}
mutex_unlock(&lif->queue_lock);

ionic_queue_doorbell_check(ionic, IONIC_NAPI_DEADLINE);
}

bool ionic_doorbell_wa(struct ionic *ionic)
{
u8 asic_type = ionic->idev.dev_info.asic_type;

return !asic_type || asic_type == IONIC_ASIC_TYPE_ELBA;
}

static int ionic_watchdog_init(struct ionic *ionic)
{
struct ionic_dev *idev = &ionic->idev;

Expand All @@ -63,6 +151,31 @@ static void ionic_watchdog_init(struct ionic *ionic)
idev->fw_status_ready = true;
idev->fw_generation = IONIC_FW_STS_F_GENERATION &
ioread8(&idev->dev_info_regs->fw_status);

ionic->wq = alloc_workqueue("%s-wq", WQ_UNBOUND, 0,
dev_name(ionic->dev));
if (!ionic->wq) {
dev_err(ionic->dev, "alloc_workqueue failed");
return -ENOMEM;
}

if (ionic_doorbell_wa(ionic))
INIT_DELAYED_WORK(&ionic->doorbell_check_dwork,
ionic_doorbell_check_dwork);

return 0;
}

void ionic_queue_doorbell_check(struct ionic *ionic, int delay)
{
int cpu;

if (!ionic->lif->doorbell_wa)
return;

cpu = ionic_get_preferred_cpu(ionic, &ionic->lif->adminqcq->intr);
queue_delayed_work_on(cpu, ionic->wq, &ionic->doorbell_check_dwork,
delay);
}

void ionic_init_devinfo(struct ionic *ionic)
Expand Down Expand Up @@ -94,6 +207,7 @@ int ionic_dev_setup(struct ionic *ionic)
struct device *dev = ionic->dev;
int size;
u32 sig;
int err;

/* BAR0: dev_cmd and interrupts */
if (num_bars < 1) {
Expand Down Expand Up @@ -129,7 +243,9 @@ int ionic_dev_setup(struct ionic *ionic)
return -EFAULT;
}

ionic_watchdog_init(ionic);
err = ionic_watchdog_init(ionic);
if (err)
return err;

idev->db_pages = bar->vaddr;
idev->phy_db_pages = bar->bus_addr;
Expand Down Expand Up @@ -161,6 +277,7 @@ void ionic_dev_teardown(struct ionic *ionic)
idev->phy_cmb_pages = 0;
idev->cmb_npages = 0;

destroy_workqueue(ionic->wq);
mutex_destroy(&idev->cmb_inuse_lock);
}

Expand Down Expand Up @@ -273,7 +390,7 @@ int ionic_heartbeat_check(struct ionic *ionic)
if (work) {
work->type = IONIC_DW_TYPE_LIF_RESET;
work->fw_status = fw_status_ready;
ionic_lif_deferred_enqueue(&lif->deferred, work);
ionic_lif_deferred_enqueue(lif, work);
}
}
}
Expand Down Expand Up @@ -703,10 +820,6 @@ void ionic_q_post(struct ionic_queue *q, bool ring_doorbell)
q->dbval | q->head_idx);

q->dbell_jiffies = jiffies;

if (q_to_qcq(q)->napi_qcq)
mod_timer(&q_to_qcq(q)->napi_qcq->napi_deadline,
jiffies + IONIC_NAPI_DEADLINE);
}
}

Expand Down
8 changes: 5 additions & 3 deletions drivers/net/ethernet/pensando/ionic/ionic_dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
#define IONIC_DEV_INFO_REG_COUNT 32
#define IONIC_DEV_CMD_REG_COUNT 32

#define IONIC_NAPI_DEADLINE (HZ / 200) /* 5ms */
#define IONIC_NAPI_DEADLINE (HZ) /* 1 sec */
#define IONIC_ADMIN_DOORBELL_DEADLINE (HZ / 2) /* 500ms */
#define IONIC_TX_DOORBELL_DEADLINE (HZ / 100) /* 10ms */
#define IONIC_RX_MIN_DOORBELL_DEADLINE (HZ / 100) /* 10ms */
Expand Down Expand Up @@ -280,9 +280,9 @@ struct ionic_intr_info {
u64 rearm_count;
unsigned int index;
unsigned int vector;
unsigned int cpu;
u32 dim_coal_hw;
cpumask_t affinity_mask;
cpumask_var_t *affinity_mask;
struct irq_affinity_notify aff_notify;
};

struct ionic_cq {
Expand Down Expand Up @@ -386,6 +386,8 @@ bool ionic_q_is_posted(struct ionic_queue *q, unsigned int pos);

int ionic_heartbeat_check(struct ionic *ionic);
bool ionic_is_fw_running(struct ionic_dev *idev);
void ionic_doorbell_napi_work(struct work_struct *work);
void ionic_queue_doorbell_check(struct ionic *ionic, int delay);

bool ionic_adminq_poke_doorbell(struct ionic_queue *q);
bool ionic_txq_poke_doorbell(struct ionic_queue *q);
Expand Down
11 changes: 10 additions & 1 deletion drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include "ionic_ethtool.h"
#include "ionic_stats.h"

#define IONIC_MAX_RX_COPYBREAK min(U16_MAX, IONIC_MAX_BUF_LEN)

static void ionic_get_stats_strings(struct ionic_lif *lif, u8 *buf)
{
u32 i;
Expand Down Expand Up @@ -872,10 +874,17 @@ static int ionic_set_tunable(struct net_device *dev,
const void *data)
{
struct ionic_lif *lif = netdev_priv(dev);
u32 rx_copybreak;

switch (tuna->id) {
case ETHTOOL_RX_COPYBREAK:
lif->rx_copybreak = *(u32 *)data;
rx_copybreak = *(u32 *)data;
if (rx_copybreak > IONIC_MAX_RX_COPYBREAK) {
netdev_err(dev, "Max supported rx_copybreak size: %u\n",
IONIC_MAX_RX_COPYBREAK);
return -EINVAL;
}
lif->rx_copybreak = (u16)rx_copybreak;
break;
default:
return -EOPNOTSUPP;
Expand Down
Loading

0 comments on commit 7e8fcb8

Please sign in to comment.