Files
vpp/src/plugins/dev_iavf/adminq.c
Damjan Marion 67f03ba71d iavf: interrupt mode support
Type: improvement
Change-Id: Ie5fcaa706ab0995e0021cf1ee74b95c5a3b30283
Signed-off-by: Damjan Marion <damarion@cisco.com>
2023-11-09 19:56:35 +00:00

486 lines
13 KiB
C

/* SPDX-License-Identifier: Apache-2.0
* Copyright (c) 2023 Cisco Systems, Inc.
*/
#include <ctype.h>
#include <vnet/vnet.h>
#include <vnet/dev/dev.h>
#include <vnet/dev/pci.h>
#include <vnet/dev/counters.h>
#include <dev_iavf/iavf.h>
#include <dev_iavf/iavf_regs.h>
#include <dev_iavf/virtchnl.h>
#include <vnet/ethernet/ethernet.h>
#define IIAVF_AQ_LARGE_BUF 512
#define IIAVF_AQ_ATQ_LEN 4
#define IIAVF_AQ_ARQ_LEN 16
VLIB_REGISTER_LOG_CLASS (iavf_log, static) = {
.class_name = "iavf",
.subclass_name = "adminq",
};
struct iavf_adminq_dma_mem
{
iavf_aq_desc_t atq[IIAVF_AQ_ATQ_LEN];
iavf_aq_desc_t arq[IIAVF_AQ_ARQ_LEN];
struct
{
u8 data[IIAVF_AQ_BUF_SIZE];
} atq_bufs[IIAVF_AQ_ATQ_LEN];
struct
{
u8 data[IIAVF_AQ_BUF_SIZE];
} arq_bufs[IIAVF_AQ_ARQ_LEN];
};
static const iavf_dyn_ctl dyn_ctl0_disable = {
.itr_indx = 3,
};
static const iavf_dyn_ctl dyn_ctl0_enable = {
.intena = 1,
.clearpba = 1,
.itr_indx = 3,
};
static const iavf_vfint_icr0_ena1 icr0_ena1_aq_enable = {
.adminq = 1,
};
static inline void
iavf_irq_0_disable (iavf_device_t *ad)
{
iavf_reg_write (ad, IAVF_VFINT_ICR0_ENA1, 0);
iavf_reg_write (ad, IAVF_VFINT_DYN_CTL0, dyn_ctl0_disable.as_u32);
iavf_reg_flush (ad);
}
static inline void
iavf_irq_0_enable (iavf_device_t *ad)
{
iavf_reg_write (ad, IAVF_VFINT_ICR0_ENA1, icr0_ena1_aq_enable.as_u32);
iavf_reg_write (ad, IAVF_VFINT_DYN_CTL0, dyn_ctl0_enable.as_u32);
iavf_reg_flush (ad);
}
static_always_inline int
iavf_aq_desc_is_done (iavf_aq_desc_t *d)
{
iavf_aq_desc_flags_t flags;
flags.as_u16 = __atomic_load_n (&d->flags.as_u16, __ATOMIC_ACQUIRE);
return flags.dd;
}
static u8 *
format_iavf_aq_desc_flags (u8 *s, va_list *args)
{
iavf_aq_desc_flags_t f = va_arg (*args, iavf_aq_desc_flags_t);
int i = 0;
#define _(n, v) \
if (f.v) \
{ \
char str[] = #v, *sp = str; \
if (i++) \
{ \
vec_add1 (s, ','); \
vec_add1 (s, ' '); \
} \
while (sp[0]) \
vec_add1 (s, (u8) toupper (sp++[0])); \
}
foreach_iavf_aq_desc_flag
#undef _
return s;
}
static u8 *
format_iavf_aq_desc_retval (u8 *s, va_list *args)
{
iavf_aq_desc_retval_t rv = va_arg (*args, u32);
char *retvals[] = {
#define _(a, b) [a] = #b,
foreach_iavf_aq_desc_retval
#undef _
};
if (rv >= ARRAY_LEN (retvals) || retvals[rv] == 0)
return format (s, "UNKNOWN(%d)", rv);
return format (s, "%s", retvals[rv]);
}
static u8 *
format_iavf_aq_desc (u8 *s, va_list *args)
{
iavf_aq_desc_t *d = va_arg (*args, iavf_aq_desc_t *);
u32 indent = format_get_indent (s);
s = format (s, "opcode 0x%04x datalen %u retval %U (%u) flags %U", d->opcode,
d->datalen, format_iavf_aq_desc_retval, d->retval, d->retval,
format_iavf_aq_desc_flags, d->flags);
if (d->opcode == IIAVF_AQ_DESC_OP_SEND_TO_PF ||
d->opcode == IIAVF_AQ_DESC_OP_MESSAGE_FROM_PF)
{
s =
format (s, "\n%Uv_opcode %U (%u) v_retval %U (%d) buf_dma_addr 0x%lx",
format_white_space, indent, format_virtchnl_op_name,
d->v_opcode, d->v_opcode, format_virtchnl_status, d->v_retval,
d->v_retval, (uword) d->param2 << 32 | d->param3);
}
else
{
s = format (
s, "\n%Ucookie_hi 0x%x cookie_lo 0x%x params %08x %08x %08x %08x",
format_white_space, indent, d->cookie_hi, d->cookie_lo, d->param0,
d->param1, d->param2, d->param3);
}
return s;
}
vnet_dev_rv_t
iavf_aq_alloc (vlib_main_t *vm, vnet_dev_t *dev)
{
iavf_device_t *ad = vnet_dev_get_data (dev);
return vnet_dev_dma_mem_alloc (vm, dev, sizeof (iavf_adminq_dma_mem_t), 0,
(void **) &ad->aq_mem);
}
void
iavf_aq_free (vlib_main_t *vm, vnet_dev_t *dev)
{
iavf_device_t *ad = vnet_dev_get_data (dev);
vnet_dev_dma_mem_free (vm, dev, ad->aq_mem);
}
static void
iavf_aq_arq_slot_init (vlib_main_t *vm, vnet_dev_t *dev, u16 slot)
{
iavf_device_t *ad = vnet_dev_get_data (dev);
u64 pa = vnet_dev_get_dma_addr (vm, dev, ad->aq_mem->arq_bufs + slot);
ad->aq_mem->arq[slot] = (iavf_aq_desc_t){
.flags.buf = 1,
.flags.lb = IIAVF_AQ_BUF_SIZE > IIAVF_AQ_LARGE_BUF,
.datalen = sizeof (ad->aq_mem->arq_bufs[0].data),
.addr_hi = (u32) (pa >> 32),
.addr_lo = (u32) pa,
};
}
static void
iavf_aq_poll (vlib_main_t *vm, vnet_dev_t *dev)
{
iavf_device_t *ad = vnet_dev_get_data (dev);
iavf_aq_desc_t *d;
u8 *b;
while (iavf_aq_arq_next_acq (vm, dev, &d, &b, 0))
{
log_debug (dev, "poll[%u] flags %x %U op %u v_op %u", ad->arq_next_slot,
d->flags.as_u16, format_iavf_aq_desc_flags, d->flags,
d->opcode, d->v_opcode);
if ((d->datalen != sizeof (virtchnl_pf_event_t)) ||
((d->flags.buf) == 0))
{
log_err (dev, "event message error");
}
vec_add1 (ad->events, *(virtchnl_pf_event_t *) b);
iavf_aq_arq_next_rel (vm, dev);
}
if (vec_len (ad->events))
{
virtchnl_pf_event_t *e;
char *virtchnl_event_names[] = {
#define _(v, n) [v] = #n,
foreach_virtchnl_event_code
#undef _
};
vec_foreach (e, ad->events)
{
log_debug (dev, "event %s (%u) sev %d",
virtchnl_event_names[e->event], e->event, e->severity);
if (e->event == VIRTCHNL_EVENT_LINK_CHANGE)
{
vnet_dev_port_state_changes_t changes = {};
vnet_dev_port_t *port = vnet_dev_get_port_by_id (dev, 0);
if (port)
{
iavf_port_t *ap = vnet_dev_get_port_data (port);
int link_up;
u32 speed = 0;
if (ap->vf_cap_flags & VIRTCHNL_VF_CAP_ADV_LINK_SPEED)
{
link_up = e->event_data.link_event_adv.link_status;
speed = e->event_data.link_event_adv.link_speed;
}
else
{
const u32 speed_table[8] = { 100, 1000, 10000, 40000,
20000, 25000, 2500, 5000 };
link_up = e->event_data.link_event.link_status;
speed = e->event_data.link_event.link_speed;
if (count_set_bits (speed) == 1 && speed &&
pow2_mask (8))
speed = speed_table[get_lowest_set_bit_index (speed)];
else
{
if (link_up)
log_warn (dev,
"unsupported link speed value "
"received (0x%x)",
speed);
speed = 0;
}
}
log_debug (dev, "LINK_CHANGE speed %u state %u", speed,
link_up);
if (port->link_up != link_up)
{
changes.change.link_state = 1;
changes.link_state = link_up;
log_debug (dev, "link state changed to %s",
link_up ? "up" : "down");
}
if (port->speed != speed * 1000)
{
changes.change.link_speed = 1;
changes.link_speed = speed * 1000;
log_debug (dev, "link speed changed to %u Mbps", speed);
}
if (changes.change.any)
vnet_dev_port_state_change (vm, port, changes);
}
}
}
vec_reset_length (ad->events);
}
}
static void
iavf_adminq_msix_handler (vlib_main_t *vm, vnet_dev_t *dev, u16 line)
{
iavf_device_t *ad = vnet_dev_get_data (dev);
iavf_reg_write (ad, IAVF_VFINT_DYN_CTL0, dyn_ctl0_enable.as_u32);
log_debug (dev, "MSI-X interrupt %u received", line);
vnet_dev_process_call_op_no_wait (vm, dev, iavf_aq_poll);
}
static void
iavf_adminq_intx_handler (vlib_main_t *vm, vnet_dev_t *dev)
{
iavf_adminq_msix_handler (vm, dev, 0);
}
void
iavf_aq_init (vlib_main_t *vm, vnet_dev_t *dev)
{
iavf_device_t *ad = vnet_dev_get_data (dev);
uword pa;
u32 len;
/* disable both tx and rx adminq queue */
iavf_reg_write (ad, IAVF_ATQLEN, 0);
iavf_reg_write (ad, IAVF_ARQLEN, 0);
len = IIAVF_AQ_ATQ_LEN;
pa = vnet_dev_get_dma_addr (vm, dev, &ad->aq_mem->atq);
iavf_reg_write (ad, IAVF_ATQT, 0); /* Tail */
iavf_reg_write (ad, IAVF_ATQH, 0); /* Head */
iavf_reg_write (ad, IAVF_ATQBAL, (u32) pa); /* Base Address Low */
iavf_reg_write (ad, IAVF_ATQBAH, (u32) (pa >> 32)); /* Base Address High */
iavf_reg_write (ad, IAVF_ATQLEN, len | (1ULL << 31)); /* len & ena */
len = IIAVF_AQ_ARQ_LEN;
pa = vnet_dev_get_dma_addr (vm, dev, ad->aq_mem->arq);
iavf_reg_write (ad, IAVF_ARQT, 0); /* Tail */
iavf_reg_write (ad, IAVF_ARQH, 0); /* Head */
iavf_reg_write (ad, IAVF_ARQBAL, (u32) pa); /* Base Address Low */
iavf_reg_write (ad, IAVF_ARQBAH, (u32) (pa >> 32)); /* Base Address High */
iavf_reg_write (ad, IAVF_ARQLEN, len | (1ULL << 31)); /* len & ena */
for (int i = 0; i < len; i++)
iavf_aq_arq_slot_init (vm, dev, i);
iavf_reg_write (ad, IAVF_ARQT, len - 1); /* Tail */
ad->atq_next_slot = 0;
ad->arq_next_slot = 0;
ad->adminq_active = 1;
}
void
iavf_aq_poll_on (vlib_main_t *vm, vnet_dev_t *dev)
{
iavf_device_t *ad = vnet_dev_get_data (dev);
vnet_dev_poll_dev_add (vm, dev, IIAVF_AQ_POLL_INTERVAL, iavf_aq_poll);
if (vnet_dev_get_pci_n_msix_interrupts (dev) > 0)
{
vnet_dev_pci_msix_add_handler (vm, dev, iavf_adminq_msix_handler, 0, 1);
vnet_dev_pci_msix_enable (vm, dev, 0, 1);
}
else
vnet_dev_pci_intx_add_handler (vm, dev, iavf_adminq_intx_handler);
iavf_irq_0_enable (ad);
}
void
iavf_aq_poll_off (vlib_main_t *vm, vnet_dev_t *dev)
{
iavf_device_t *ad = vnet_dev_get_data (dev);
iavf_irq_0_disable (ad);
vnet_dev_poll_dev_remove (vm, dev, iavf_aq_poll);
if (vnet_dev_get_pci_n_msix_interrupts (dev) > 0)
{
vnet_dev_pci_msix_disable (vm, dev, 0, 1);
vnet_dev_pci_msix_remove_handler (vm, dev, 0, 1);
}
else
vnet_dev_pci_intx_remove_handler (vm, dev);
}
vnet_dev_rv_t
iavf_aq_atq_enq (vlib_main_t *vm, vnet_dev_t *dev, iavf_aq_desc_t *desc,
const u8 *data, u16 len, f64 timeout)
{
iavf_device_t *ad = vnet_dev_get_data (dev);
iavf_aq_desc_t *d = ad->aq_mem->atq + ad->atq_next_slot;
u8 *buf = ad->aq_mem->atq_bufs[ad->atq_next_slot].data;
ASSERT (len <= IIAVF_AQ_BUF_SIZE);
*d = *desc;
if (len)
{
u64 pa = vnet_dev_get_dma_addr (vm, dev, buf);
d->datalen = len;
d->addr_hi = (u32) (pa >> 32);
d->addr_lo = (u32) pa;
d->flags.buf = 1;
d->flags.rd = 1;
d->flags.lb = len > IIAVF_AQ_LARGE_BUF;
clib_memcpy_fast (buf, data, len);
}
log_debug (dev, "slot %u\n %U", ad->atq_next_slot, format_iavf_aq_desc, d);
ad->atq_next_slot = (ad->atq_next_slot + 1) % IIAVF_AQ_ATQ_LEN;
iavf_reg_write (ad, IAVF_ATQT, ad->atq_next_slot);
iavf_reg_flush (ad);
if (timeout > 0)
{
f64 suspend_time = timeout / 62;
f64 t0 = vlib_time_now (vm);
iavf_aq_desc_flags_t flags;
while (1)
{
flags.as_u16 = __atomic_load_n (&d->flags.as_u16, __ATOMIC_ACQUIRE);
if (flags.err)
{
log_err (dev, "adminq enqueue error [opcode 0x%x, retval %d]",
d->opcode, d->retval);
return VNET_DEV_ERR_BUG;
}
if (flags.dd && flags.cmp)
return VNET_DEV_OK;
if (vlib_time_now (vm) - t0 > timeout)
{
log_err (dev, "adminq enqueue timeout [opcode 0x%x]", d->opcode);
return VNET_DEV_ERR_TIMEOUT;
}
vlib_process_suspend (vm, suspend_time);
suspend_time *= 2;
}
}
return VNET_DEV_OK;
}
void
iavf_aq_deinit (vlib_main_t *vm, vnet_dev_t *dev)
{
iavf_device_t *ad = vnet_dev_get_data (dev);
if (ad->adminq_active)
{
iavf_aq_desc_t d = {
.opcode = IIAVF_AQ_DESC_OP_QUEUE_SHUTDOWN,
.driver_unloading = 1,
.flags = { .si = 1 },
};
log_debug (dev, "adminq queue shutdown");
iavf_aq_atq_enq (vm, dev, &d, 0, 0, 0);
ad->adminq_active = 0;
}
}
int
iavf_aq_arq_next_acq (vlib_main_t *vm, vnet_dev_t *dev, iavf_aq_desc_t **dp,
u8 **bp, f64 timeout)
{
iavf_device_t *ad = vnet_dev_get_data (dev);
iavf_aq_desc_t *d = ad->aq_mem->arq + ad->arq_next_slot;
if (timeout)
{
f64 suspend_time = timeout / 62;
f64 t0 = vlib_time_now (vm);
while (!iavf_aq_desc_is_done (d))
{
if (vlib_time_now (vm) - t0 > timeout)
return 0;
vlib_process_suspend (vm, suspend_time);
suspend_time *= 2;
}
}
else if (!iavf_aq_desc_is_done (d))
return 0;
log_debug (dev, "arq desc acquired in slot %u\n %U", ad->arq_next_slot,
format_iavf_aq_desc, d);
*dp = d;
*bp = ad->aq_mem->arq_bufs[ad->arq_next_slot].data;
return 1;
}
void
iavf_aq_arq_next_rel (vlib_main_t *vm, vnet_dev_t *dev)
{
iavf_device_t *ad = vnet_dev_get_data (dev);
ASSERT (iavf_aq_desc_is_done (ad->aq_mem->arq + ad->arq_next_slot));
iavf_aq_arq_slot_init (vm, dev, ad->arq_next_slot);
iavf_reg_write (ad, IAVF_ARQT, ad->arq_next_slot);
iavf_reg_flush (ad);
ad->arq_next_slot = (ad->arq_next_slot + 1) % IIAVF_AQ_ARQ_LEN;
}