summaryrefslogtreecommitdiff
path: root/usr/src/uts
diff options
context:
space:
mode:
authorDan McDonald <danmcd@mnx.io>2022-07-14 20:42:12 -0400
committerDan McDonald <danmcd@mnx.io>2022-07-14 20:42:12 -0400
commit964bebb5480b816d8a7d2dd58484eb826c732d52 (patch)
tree485159e8a28b01338bc963895ee0c2af4616d14b /usr/src/uts
parent6638d136db644920fd3090c414ece292ca77b108 (diff)
parent62d03eda78495dc4dc79a65a6b1899e11dc34b12 (diff)
downloadillumos-joyent-OS-8394.tar.gz
Merge branch 'master' into OS-8394OS-8394
Diffstat (limited to 'usr/src/uts')
-rw-r--r--usr/src/uts/common/io/scsi/impl/scsi_subr.c5
-rw-r--r--usr/src/uts/i86pc/io/pci/pci_common.c27
-rw-r--r--usr/src/uts/intel/io/viona/viona_impl.h23
-rw-r--r--usr/src/uts/intel/io/viona/viona_main.c123
-rw-r--r--usr/src/uts/intel/io/viona/viona_ring.c194
-rw-r--r--usr/src/uts/intel/io/viona/viona_rx.c6
-rw-r--r--usr/src/uts/intel/io/viona/viona_tx.c89
-rw-r--r--usr/src/uts/intel/io/vmm/vmm_sol_dev.c18
-rw-r--r--usr/src/uts/intel/sys/viona_io.h30
9 files changed, 430 insertions, 85 deletions
diff --git a/usr/src/uts/common/io/scsi/impl/scsi_subr.c b/usr/src/uts/common/io/scsi/impl/scsi_subr.c
index 1fd97aeeb3..6542098fe2 100644
--- a/usr/src/uts/common/io/scsi/impl/scsi_subr.c
+++ b/usr/src/uts/common/io/scsi/impl/scsi_subr.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2022 Garrett D'Amore
*/
#include <sys/scsi/scsi.h>
@@ -390,8 +391,6 @@ free_pktiopb(struct scsi_pkt *pkt, caddr_t datap, int datalen)
* Common naming functions
*/
-static char scsi_tmpname[64];
-
char *
scsi_dname(int dtyp)
{
@@ -479,7 +478,7 @@ scsi_cname(uchar_t cmd, register char **cmdvec)
}
cmdvec++;
}
- return (sprintf(scsi_tmpname, "<undecoded cmd 0x%x>", cmd));
+ return ("<undecoded cmd>");
}
char *
diff --git a/usr/src/uts/i86pc/io/pci/pci_common.c b/usr/src/uts/i86pc/io/pci/pci_common.c
index feb5fbfd25..fe1918d121 100644
--- a/usr/src/uts/i86pc/io/pci/pci_common.c
+++ b/usr/src/uts/i86pc/io/pci/pci_common.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2022 Oxide Computer Company
*/
/*
@@ -1018,6 +1019,31 @@ pci_common_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp)
for (i = 0; i < number; i++) {
if ((assigned_addr[i].pci_phys_hi & PCI_CONF_ADDR_MASK) ==
phys_hi) {
+ /*
+ * When the system does not manage to allocate PCI
+ * resources for a device, then the value that is stored
+ * in assigned addresses ends up being the hardware
+ * default reset value of '0'. On currently supported
+ * platforms, physical address zero is associated with
+ * memory; however, on other platforms this may be the
+ * exception vector table (ARM), etc. and so we opt to
+ * generally keep the idea in PCI that the reset value
+ * will not be used for actual MMIO allocations. If such
+ * a platform comes around where it is worth using that
+ * bit of MMIO for PCI then we should make this check
+ * platform-specific.
+ *
+ * Note, the +1 in the print statement is because a
+ * given regs[0] describes B/D/F information for the
+ * device.
+ */
+ if (assigned_addr[i].pci_phys_mid == 0 &&
+ assigned_addr[i].pci_phys_low == 0) {
+ dev_err(dip, CE_WARN, "regs[%u] does not have "
+ "a valid MMIO address", i + 1);
+ goto err;
+ }
+
pci_rp->pci_phys_mid = assigned_addr[i].pci_phys_mid;
pci_rp->pci_phys_low = assigned_addr[i].pci_phys_low;
ddi_prop_free(assigned_addr);
@@ -1025,6 +1051,7 @@ pci_common_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp)
}
}
+err:
ddi_prop_free(assigned_addr);
return (DDI_FAILURE);
}
diff --git a/usr/src/uts/intel/io/viona/viona_impl.h b/usr/src/uts/intel/io/viona/viona_impl.h
index 760474e78b..c99bd2c51e 100644
--- a/usr/src/uts/intel/io/viona/viona_impl.h
+++ b/usr/src/uts/intel/io/viona/viona_impl.h
@@ -35,7 +35,7 @@
*
* Copyright 2015 Pluribus Networks Inc.
* Copyright 2019 Joyent, Inc.
- * Copyright 2021 Oxide Computer Company
+ * Copyright 2022 Oxide Computer Company
*/
#ifndef _VIONA_IMPL_H
@@ -76,7 +76,8 @@ enum viona_ring_state {
enum viona_ring_state_flags {
VRSF_REQ_START = 0x1, /* start running from INIT state */
VRSF_REQ_STOP = 0x2, /* stop running, clean up, goto RESET state */
- VRSF_RENEW = 0x4, /* ring renewing lease */
+ VRSF_REQ_PAUSE = 0x4, /* stop running, goto INIT state */
+ VRSF_RENEW = 0x8, /* ring renewing lease */
};
typedef struct viona_vring {
@@ -232,11 +233,6 @@ struct virtio_net_hdr {
};
#pragma pack()
-#define VRING_NEED_BAIL(ring, proc) \
- (((ring)->vr_state_flags & VRSF_REQ_STOP) != 0 || \
- ((proc)->p_flag & SEXITING) != 0)
-
-
#define VNETHOOK_INTERESTED_IN(neti) \
(neti)->vni_nethook.vnh_event_in.he_interested
#define VNETHOOK_INTERESTED_OUT(neti) \
@@ -288,12 +284,23 @@ struct virtio_net_hdr {
#define VIRTIO_F_RING_INDIRECT_DESC (1 << 28)
#define VIRTIO_F_RING_EVENT_IDX (1 << 29)
+struct viona_ring_params {
+ uint64_t vrp_pa;
+ uint16_t vrp_size;
+ uint16_t vrp_avail_idx;
+ uint16_t vrp_used_idx;
+};
void viona_ring_alloc(viona_link_t *, viona_vring_t *);
void viona_ring_free(viona_vring_t *);
+int viona_ring_get_state(viona_link_t *, uint16_t, struct viona_ring_params *);
+int viona_ring_set_state(viona_link_t *, uint16_t,
+ const struct viona_ring_params *);
int viona_ring_reset(viona_vring_t *, boolean_t);
-int viona_ring_init(viona_link_t *, uint16_t, uint16_t, uint64_t);
+int viona_ring_init(viona_link_t *, uint16_t, const struct viona_ring_params *);
boolean_t viona_ring_lease_renew(viona_vring_t *);
+bool vring_need_bail(const viona_vring_t *);
+int viona_ring_pause(viona_vring_t *);
int vq_popchain(viona_vring_t *, struct iovec *, uint_t, uint16_t *,
vmm_page_t **);
diff --git a/usr/src/uts/intel/io/viona/viona_main.c b/usr/src/uts/intel/io/viona/viona_main.c
index a34196ba1a..c6a750d532 100644
--- a/usr/src/uts/intel/io/viona/viona_main.c
+++ b/usr/src/uts/intel/io/viona/viona_main.c
@@ -35,7 +35,7 @@
*
* Copyright 2015 Pluribus Networks Inc.
* Copyright 2019 Joyent, Inc.
- * Copyright 2021 Oxide Computer Company
+ * Copyright 2022 Oxide Computer Company
*/
/*
@@ -111,6 +111,14 @@
* | VRS_RESET and exit.
* | ^
* | |
+ * |<-------------------------------------------<+
+ * | | |
+ * | | ^
+ * | * If ring is requested to pause (but not stop)from the
+ * | VRS_RUN state, it will return to the VRS_INIT state.
+ * |
+ * | ^
+ * | |
* | ^
* V
* +-----------+ The worker thread associated with the ring has started
@@ -286,8 +294,11 @@ static int viona_ioc_delete(viona_soft_state_t *, boolean_t);
static int viona_ioc_set_notify_ioport(viona_link_t *, uint16_t);
static int viona_ioc_ring_init(viona_link_t *, void *, int);
+static int viona_ioc_ring_set_state(viona_link_t *, void *, int);
+static int viona_ioc_ring_get_state(viona_link_t *, void *, int);
static int viona_ioc_ring_reset(viona_link_t *, uint_t);
static int viona_ioc_ring_kick(viona_link_t *, uint_t);
+static int viona_ioc_ring_pause(viona_link_t *, uint_t);
static int viona_ioc_ring_set_msi(viona_link_t *, void *, int);
static int viona_ioc_ring_intr_clear(viona_link_t *, uint_t);
static int viona_ioc_intr_poll(viona_link_t *, void *, int, int *);
@@ -530,6 +541,9 @@ viona_ioctl(dev_t dev, int cmd, intptr_t data, int md, cred_t *cr, int *rv)
return (viona_ioc_create(ss, dptr, md, cr));
case VNA_IOC_DELETE:
return (viona_ioc_delete(ss, B_FALSE));
+ case VNA_IOC_VERSION:
+ *rv = VIONA_CURRENT_INTERFACE_VERSION;
+ return (0);
default:
break;
}
@@ -578,6 +592,16 @@ viona_ioctl(dev_t dev, int cmd, intptr_t data, int md, cred_t *cr, int *rv)
case VNA_IOC_RING_INTR_CLR:
err = viona_ioc_ring_intr_clear(link, (uint_t)data);
break;
+ case VNA_IOC_RING_SET_STATE:
+ err = viona_ioc_ring_set_state(link, dptr, md);
+ break;
+ case VNA_IOC_RING_GET_STATE:
+ err = viona_ioc_ring_get_state(link, dptr, md);
+ break;
+ case VNA_IOC_RING_PAUSE:
+ err = viona_ioc_ring_pause(link, (uint_t)data);
+ break;
+
case VNA_IOC_INTR_POLL:
err = viona_ioc_intr_poll(link, dptr, md, rv);
break;
@@ -853,13 +877,65 @@ viona_ioc_ring_init(viona_link_t *link, void *udata, int md)
if (ddi_copyin(udata, &kri, sizeof (kri), md) != 0) {
return (EFAULT);
}
+ const struct viona_ring_params params = {
+ .vrp_pa = kri.ri_qaddr,
+ .vrp_size = kri.ri_qsize,
+ .vrp_avail_idx = 0,
+ .vrp_used_idx = 0,
+ };
+
+ err = viona_ring_init(link, kri.ri_index, &params);
+
+ return (err);
+}
+
+static int
+viona_ioc_ring_set_state(viona_link_t *link, void *udata, int md)
+{
+ vioc_ring_state_t krs;
+ int err;
+
+ if (ddi_copyin(udata, &krs, sizeof (krs), md) != 0) {
+ return (EFAULT);
+ }
+ const struct viona_ring_params params = {
+ .vrp_pa = krs.vrs_qaddr,
+ .vrp_size = krs.vrs_qsize,
+ .vrp_avail_idx = krs.vrs_avail_idx,
+ .vrp_used_idx = krs.vrs_used_idx,
+ };
- err = viona_ring_init(link, kri.ri_index, kri.ri_qsize, kri.ri_qaddr);
+ err = viona_ring_init(link, krs.vrs_index, &params);
return (err);
}
static int
+viona_ioc_ring_get_state(viona_link_t *link, void *udata, int md)
+{
+ vioc_ring_state_t krs;
+
+ if (ddi_copyin(udata, &krs, sizeof (krs), md) != 0) {
+ return (EFAULT);
+ }
+
+ struct viona_ring_params params;
+ int err = viona_ring_get_state(link, krs.vrs_index, &params);
+ if (err != 0) {
+ return (err);
+ }
+ krs.vrs_qsize = params.vrp_size;
+ krs.vrs_qaddr = params.vrp_pa;
+ krs.vrs_avail_idx = params.vrp_avail_idx;
+ krs.vrs_used_idx = params.vrp_used_idx;
+
+ if (ddi_copyout(&krs, udata, sizeof (krs), md) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+}
+
+static int
viona_ioc_ring_reset(viona_link_t *link, uint_t idx)
{
viona_vring_t *ring;
@@ -909,6 +985,17 @@ viona_ioc_ring_kick(viona_link_t *link, uint_t idx)
}
static int
+viona_ioc_ring_pause(viona_link_t *link, uint_t idx)
+{
+ if (idx >= VIONA_VQ_MAX) {
+ return (EINVAL);
+ }
+
+ viona_vring_t *ring = &link->l_vrings[idx];
+ return (viona_ring_pause(ring));
+}
+
+static int
viona_ioc_ring_set_msi(viona_link_t *link, void *data, int md)
{
vioc_ring_msi_t vrm;
@@ -935,21 +1022,33 @@ viona_notify_iop(void *arg, bool in, uint16_t port, uint8_t bytes,
uint32_t *val)
{
viona_link_t *link = (viona_link_t *)arg;
- uint16_t vq = *val;
- if (in) {
- /*
- * Do not service read (in/ins) requests on this ioport.
- * Instead, indicate that the handler is not found, causing a
- * fallback to userspace processing.
- */
+ /*
+ * If the request is a read (in/ins), or direct at a port other than
+ * what we expect to be registered on, ignore it.
+ */
+ if (in || port != link->l_notify_ioport) {
return (ESRCH);
}
- if (port != link->l_notify_ioport) {
- return (EINVAL);
+ /* Let userspace handle notifications for rings other than RX/TX. */
+ const uint16_t vq = *val;
+ if (vq >= VIONA_VQ_MAX) {
+ return (ESRCH);
}
- return (viona_ioc_ring_kick(link, vq));
+
+ viona_vring_t *ring = &link->l_vrings[vq];
+ int res = 0;
+
+ mutex_enter(&ring->vr_lock);
+ if (ring->vr_state == VRS_RUN) {
+ cv_broadcast(&ring->vr_cv);
+ } else {
+ res = ESRCH;
+ }
+ mutex_exit(&ring->vr_lock);
+
+ return (res);
}
static int
diff --git a/usr/src/uts/intel/io/viona/viona_ring.c b/usr/src/uts/intel/io/viona/viona_ring.c
index 2d847dda09..9339f805d4 100644
--- a/usr/src/uts/intel/io/viona/viona_ring.c
+++ b/usr/src/uts/intel/io/viona/viona_ring.c
@@ -35,7 +35,7 @@
*
* Copyright 2015 Pluribus Networks Inc.
* Copyright 2019 Joyent, Inc.
- * Copyright 2021 Oxide Computer Company
+ * Copyright 2022 Oxide Computer Company
*/
@@ -281,15 +281,19 @@ viona_ring_free(viona_vring_t *ring)
}
int
-viona_ring_init(viona_link_t *link, uint16_t idx, uint16_t qsz, uint64_t pa)
+viona_ring_init(viona_link_t *link, uint16_t idx,
+ const struct viona_ring_params *params)
{
viona_vring_t *ring;
kthread_t *t;
int err = 0;
+ const uint16_t qsz = params->vrp_size;
+ const uint64_t pa = params->vrp_pa;
if (idx >= VIONA_VQ_MAX) {
return (EINVAL);
}
+
if (qsz == 0 || qsz > VRING_MAX_LEN || (1 << (ffs(qsz) - 1)) != qsz) {
return (EINVAL);
}
@@ -320,8 +324,8 @@ viona_ring_init(viona_link_t *link, uint16_t idx, uint16_t qsz, uint64_t pa)
}
/* Initialize queue indexes */
- ring->vr_cur_aidx = 0;
- ring->vr_cur_uidx = 0;
+ ring->vr_cur_aidx = params->vrp_avail_idx;
+ ring->vr_cur_uidx = params->vrp_used_idx;
if (idx == VIONA_VQ_TX) {
viona_tx_ring_alloc(ring, qsz);
@@ -351,11 +355,48 @@ fail:
ring->vr_size = 0;
ring->vr_mask = 0;
ring->vr_pa = 0;
+ ring->vr_cur_aidx = 0;
+ ring->vr_cur_uidx = 0;
mutex_exit(&ring->vr_lock);
return (err);
}
int
+viona_ring_get_state(viona_link_t *link, uint16_t idx,
+ struct viona_ring_params *params)
+{
+ viona_vring_t *ring;
+
+ if (idx >= VIONA_VQ_MAX) {
+ return (EINVAL);
+ }
+
+ ring = &link->l_vrings[idx];
+ mutex_enter(&ring->vr_lock);
+
+ params->vrp_size = ring->vr_size;
+ params->vrp_pa = ring->vr_pa;
+
+ if (ring->vr_state == VRS_RUN) {
+ /* On a running ring, we must heed the avail/used locks */
+ mutex_enter(&ring->vr_a_mutex);
+ params->vrp_avail_idx = ring->vr_cur_aidx;
+ mutex_exit(&ring->vr_a_mutex);
+ mutex_enter(&ring->vr_u_mutex);
+ params->vrp_used_idx = ring->vr_cur_uidx;
+ mutex_exit(&ring->vr_u_mutex);
+ } else {
+ /* Otherwise vr_lock is adequate protection */
+ params->vrp_avail_idx = ring->vr_cur_aidx;
+ params->vrp_used_idx = ring->vr_cur_uidx;
+ }
+
+ mutex_exit(&ring->vr_lock);
+
+ return (0);
+}
+
+int
viona_ring_reset(viona_vring_t *ring, boolean_t heed_signals)
{
mutex_enter(&ring->vr_lock);
@@ -485,40 +526,152 @@ viona_intr_ring(viona_vring_t *ring, boolean_t skip_flags_check)
}
}
+static inline bool
+vring_stop_req(const viona_vring_t *ring)
+{
+ return ((ring->vr_state_flags & VRSF_REQ_STOP) != 0);
+}
+
+static inline bool
+vring_pause_req(const viona_vring_t *ring)
+{
+ return ((ring->vr_state_flags & VRSF_REQ_PAUSE) != 0);
+}
+
+static inline bool
+vring_start_req(const viona_vring_t *ring)
+{
+ return ((ring->vr_state_flags & VRSF_REQ_START) != 0);
+}
+
+/*
+ * Check if vring worker thread should bail out. This will heed indications
+ * that the containing process is exiting, as well as requests to stop or pause
+ * the ring. The `stop_only` parameter controls if pause requests are ignored
+ * (true) or checked (false).
+ *
+ * Caller should hold vr_lock.
+ */
+static bool
+vring_need_bail_ext(const viona_vring_t *ring, bool stop_only)
+{
+ ASSERT(MUTEX_HELD(&ring->vr_lock));
+
+ if (vring_stop_req(ring) ||
+ (!stop_only && vring_pause_req(ring))) {
+ return (true);
+ }
+
+ kthread_t *t = ring->vr_worker_thread;
+ if (t != NULL) {
+ proc_t *p = ttoproc(t);
+
+ ASSERT(p != NULL);
+ if ((p->p_flag & SEXITING) != 0) {
+ return (true);
+ }
+ }
+ return (false);
+}
+
+bool
+vring_need_bail(const viona_vring_t *ring)
+{
+ return (vring_need_bail_ext(ring, false));
+}
+
+int
+viona_ring_pause(viona_vring_t *ring)
+{
+ mutex_enter(&ring->vr_lock);
+ switch (ring->vr_state) {
+ case VRS_RESET:
+ case VRS_SETUP:
+ case VRS_INIT:
+ /*
+ * For rings which have not yet started (even those in the
+ * VRS_SETUP and VRS_INIT phases, where there a running worker
+ * thread (waiting to be released to do its intended task), it
+ * is adequate to simply clear any start request, to keep them
+ * from proceeding into the actual work processing function.
+ */
+ ring->vr_state_flags &= ~VRSF_REQ_START;
+ mutex_exit(&ring->vr_lock);
+ return (0);
+
+ case VRS_STOP:
+ if ((ring->vr_state_flags & VRSF_REQ_STOP) != 0) {
+ /* A ring on its way to RESET cannot be paused. */
+ mutex_exit(&ring->vr_lock);
+ return (EBUSY);
+ }
+ /* FALLTHROUGH */
+ case VRS_RUN:
+ ring->vr_state_flags |= VRSF_REQ_PAUSE;
+ cv_broadcast(&ring->vr_cv);
+ break;
+
+ default:
+ panic("invalid ring state %d", ring->vr_state);
+ break;
+ }
+
+ for (;;) {
+ int res = cv_wait_sig(&ring->vr_cv, &ring->vr_lock);
+
+ if (ring->vr_state == VRS_INIT ||
+ (ring->vr_state_flags & VRSF_REQ_PAUSE) == 0) {
+ /* Ring made it to (or through) paused state */
+ mutex_exit(&ring->vr_lock);
+ return (0);
+ }
+ if (res == 0) {
+ /* interrupted by signal */
+ mutex_exit(&ring->vr_lock);
+ return (EINTR);
+ }
+ }
+ /* NOTREACHED */
+}
+
static void
viona_worker(void *arg)
{
viona_vring_t *ring = (viona_vring_t *)arg;
viona_link_t *link = ring->vr_link;
- proc_t *p = ttoproc(curthread);
mutex_enter(&ring->vr_lock);
VERIFY3U(ring->vr_state, ==, VRS_SETUP);
/* Bail immediately if ring shutdown or process exit was requested */
- if (VRING_NEED_BAIL(ring, p)) {
- goto cleanup;
+ if (vring_need_bail_ext(ring, true)) {
+ goto ring_reset;
}
/* Report worker thread as alive and notify creator */
+ring_init:
ring->vr_state = VRS_INIT;
cv_broadcast(&ring->vr_cv);
- while (ring->vr_state_flags == 0) {
+ while (!vring_start_req(ring)) {
/*
* Keeping lease renewals timely while waiting for the ring to
* be started is important for avoiding deadlocks.
*/
if (vmm_drv_lease_expired(ring->vr_lease)) {
if (!viona_ring_lease_renew(ring)) {
- goto cleanup;
+ goto ring_reset;
}
}
(void) cv_wait_sig(&ring->vr_cv, &ring->vr_lock);
- if (VRING_NEED_BAIL(ring, p)) {
- goto cleanup;
+ if (vring_pause_req(ring)) {
+ /* We are already paused in the INIT state. */
+ ring->vr_state_flags &= ~VRSF_REQ_PAUSE;
+ }
+ if (vring_need_bail_ext(ring, true)) {
+ goto ring_reset;
}
}
@@ -529,7 +682,7 @@ viona_worker(void *arg)
/* Ensure ring lease is valid first */
if (vmm_drv_lease_expired(ring->vr_lease)) {
if (!viona_ring_lease_renew(ring)) {
- goto cleanup;
+ goto ring_reset;
}
}
@@ -543,15 +696,18 @@ viona_worker(void *arg)
}
VERIFY3U(ring->vr_state, ==, VRS_STOP);
+ VERIFY3U(ring->vr_xfer_outstanding, ==, 0);
-cleanup:
- if (ring->vr_txdesb != NULL) {
- /*
- * Transmit activity must be entirely concluded before the
- * associated descriptors can be cleaned up.
- */
- VERIFY(ring->vr_xfer_outstanding == 0);
+ /* Respond to a pause request if the ring is not required to stop */
+ if (vring_pause_req(ring)) {
+ ring->vr_state_flags &= ~VRSF_REQ_PAUSE;
+
+ if (!vring_need_bail_ext(ring, true)) {
+ goto ring_init;
+ }
}
+
+ring_reset:
viona_ring_misc_free(ring);
viona_ring_lease_drop(ring);
diff --git a/usr/src/uts/intel/io/viona/viona_rx.c b/usr/src/uts/intel/io/viona/viona_rx.c
index 2fbf6be972..e6f6f421d5 100644
--- a/usr/src/uts/intel/io/viona/viona_rx.c
+++ b/usr/src/uts/intel/io/viona/viona_rx.c
@@ -35,7 +35,7 @@
*
* Copyright 2015 Pluribus Networks Inc.
* Copyright 2019 Joyent, Inc.
- * Copyright 2021 Oxide Computer Company
+ * Copyright 2022 Oxide Computer Company
*/
#include <sys/types.h>
@@ -86,8 +86,6 @@ viona_rx_fini(void)
void
viona_worker_rx(viona_vring_t *ring, viona_link_t *link)
{
- proc_t *p = ttoproc(curthread);
-
(void) thread_vsetname(curthread, "viona_rx_%p", ring);
ASSERT(MUTEX_HELD(&ring->vr_lock));
@@ -122,7 +120,7 @@ viona_worker_rx(viona_vring_t *ring, viona_link_t *link)
* place to inject frames into the guest.
*/
(void) cv_wait_sig(&ring->vr_cv, &ring->vr_lock);
- } while (!VRING_NEED_BAIL(ring, p));
+ } while (!vring_need_bail(ring));
ring->vr_state = VRS_STOP;
diff --git a/usr/src/uts/intel/io/viona/viona_tx.c b/usr/src/uts/intel/io/viona/viona_tx.c
index 277ee521a1..306c6f308e 100644
--- a/usr/src/uts/intel/io/viona/viona_tx.c
+++ b/usr/src/uts/intel/io/viona/viona_tx.c
@@ -35,7 +35,7 @@
*
* Copyright 2015 Pluribus Networks Inc.
* Copyright 2019 Joyent, Inc.
- * Copyright 2021 Oxide Computer Company
+ * Copyright 2022 Oxide Computer Company
*/
@@ -179,11 +179,11 @@ viona_tx_done(viona_vring_t *ring, uint32_t len, uint16_t cookie)
viona_intr_ring(ring, B_FALSE);
}
+#define TX_BURST_THRESH 32
+
void
viona_worker_tx(viona_vring_t *ring, viona_link_t *link)
{
- proc_t *p = ttoproc(curthread);
-
(void) thread_vsetname(curthread, "viona_tx_%p", ring);
ASSERT(MUTEX_HELD(&ring->vr_lock));
@@ -192,23 +192,30 @@ viona_worker_tx(viona_vring_t *ring, viona_link_t *link)
mutex_exit(&ring->vr_lock);
for (;;) {
- boolean_t bail = B_FALSE;
- boolean_t renew = B_FALSE;
- uint_t ntx = 0;
+ uint_t ntx = 0, burst = 0;
viona_ring_disable_notify(ring);
- while (viona_ring_num_avail(ring)) {
+ while (viona_ring_num_avail(ring) != 0) {
viona_tx(link, ring);
+ ntx++;
+ burst++;
/*
* It is advantageous for throughput to keep this
* transmission loop tight, but periodic breaks to
* check for other events are of value too.
*/
- if (ntx++ >= ring->vr_size)
- break;
+ if (burst >= TX_BURST_THRESH) {
+ mutex_enter(&ring->vr_lock);
+ const bool need_bail = vring_need_bail(ring);
+ mutex_exit(&ring->vr_lock);
+
+ if (need_bail) {
+ break;
+ }
+ burst = 0;
+ }
}
- viona_ring_enable_notify(ring);
VIONA_PROBE2(tx, viona_link_t *, link, uint_t, ntx);
@@ -219,14 +226,11 @@ viona_worker_tx(viona_vring_t *ring, viona_link_t *link)
* The barrier ensures that visibility of the no-notify
* store does not cross the viona_ring_num_avail() check below.
*/
+ viona_ring_enable_notify(ring);
membar_enter();
- bail = VRING_NEED_BAIL(ring, p);
- renew = vmm_drv_lease_expired(ring->vr_lease);
- if (!bail && !renew && viona_ring_num_avail(ring)) {
- continue;
- }
- if ((link->l_features & VIRTIO_F_RING_NOTIFY_ON_EMPTY) != 0) {
+ if (viona_ring_num_avail(ring) == 0 &&
+ (link->l_features & VIRTIO_F_RING_NOTIFY_ON_EMPTY) != 0) {
/*
* The NOTIFY_ON_EMPTY interrupt should not pay heed to
* the presence of AVAIL_NO_INTERRUPT.
@@ -235,36 +239,43 @@ viona_worker_tx(viona_vring_t *ring, viona_link_t *link)
}
mutex_enter(&ring->vr_lock);
+ for (;;) {
+ if (vring_need_bail(ring)) {
+ ring->vr_state = VRS_STOP;
+ viona_tx_wait_outstanding(ring);
+ return;
+ }
- while (!bail && !renew && !viona_ring_num_avail(ring)) {
- (void) cv_wait_sig(&ring->vr_cv, &ring->vr_lock);
- bail = VRING_NEED_BAIL(ring, p);
- renew = vmm_drv_lease_expired(ring->vr_lease);
- }
-
- if (bail) {
- break;
- } else if (renew) {
- ring->vr_state_flags |= VRSF_RENEW;
- /*
- * When renewing the lease for the ring, no TX
- * frames may be outstanding, as they contain
- * references to guest memory.
- */
- viona_tx_wait_outstanding(ring);
+ if (vmm_drv_lease_expired(ring->vr_lease)) {
+ ring->vr_state_flags |= VRSF_RENEW;
+ /*
+ * When renewing the lease for the ring, no TX
+ * frames may be outstanding, as they contain
+ * references to guest memory.
+ */
+ viona_tx_wait_outstanding(ring);
+
+ const boolean_t renewed =
+ viona_ring_lease_renew(ring);
+ ring->vr_state_flags &= ~VRSF_RENEW;
+
+ if (!renewed) {
+ /* stop ring on failed renewal */
+ ring->vr_state = VRS_STOP;
+ return;
+ }
+ }
- if (!viona_ring_lease_renew(ring)) {
+ if (viona_ring_num_avail(ring) != 0) {
break;
}
- ring->vr_state_flags &= ~VRSF_RENEW;
+
+ /* Wait for further activity on the ring */
+ (void) cv_wait_sig(&ring->vr_cv, &ring->vr_lock);
}
mutex_exit(&ring->vr_lock);
}
-
- ASSERT(MUTEX_HELD(&ring->vr_lock));
-
- ring->vr_state = VRS_STOP;
- viona_tx_wait_outstanding(ring);
+ /* UNREACHABLE */
}
static void
diff --git a/usr/src/uts/intel/io/vmm/vmm_sol_dev.c b/usr/src/uts/intel/io/vmm/vmm_sol_dev.c
index ee07779b21..da83735e43 100644
--- a/usr/src/uts/intel/io/vmm/vmm_sol_dev.c
+++ b/usr/src/uts/intel/io/vmm/vmm_sol_dev.c
@@ -2239,6 +2239,24 @@ vmm_drv_purge(vmm_softc_t *sc)
hold = list_next(&sc->vmm_holds, hold)) {
hold->vmh_release_req = B_TRUE;
}
+
+ /*
+ * Require that all leases on the instance be broken, now that
+ * all associated holds have been marked as needing release.
+ *
+ * Dropping vmm_mtx is not strictly necessary, but if any of the
+ * lessees are slow to respond, it would be nice to leave it
+ * available for other parties.
+ */
+ mutex_exit(&vmm_mtx);
+ vmm_lease_block(sc);
+ vmm_lease_unblock(sc);
+ mutex_enter(&vmm_mtx);
+
+ /*
+ * With all of the leases broken, we can proceed in an orderly
+ * fashion to waiting for any lingering holds to be dropped.
+ */
while ((sc->vmm_flags & VMM_HELD) != 0) {
if (cv_wait_sig(&sc->vmm_cv, &vmm_mtx) <= 0) {
return (EINTR);
diff --git a/usr/src/uts/intel/sys/viona_io.h b/usr/src/uts/intel/sys/viona_io.h
index 46cc72eb06..761445986c 100644
--- a/usr/src/uts/intel/sys/viona_io.h
+++ b/usr/src/uts/intel/sys/viona_io.h
@@ -12,6 +12,7 @@
/*
* Copyright 2013 Pluribus Networks Inc.
* Copyright 2018 Joyent, Inc.
+ * Copyright 2022 Oxide Computer Company
*/
#ifndef _VIONA_IO_H_
@@ -20,18 +21,39 @@
#define VNA_IOC (('V' << 16)|('C' << 8))
#define VNA_IOC_CREATE (VNA_IOC | 0x01)
#define VNA_IOC_DELETE (VNA_IOC | 0x02)
+#define VNA_IOC_VERSION (VNA_IOC | 0x03)
#define VNA_IOC_RING_INIT (VNA_IOC | 0x10)
#define VNA_IOC_RING_RESET (VNA_IOC | 0x11)
#define VNA_IOC_RING_KICK (VNA_IOC | 0x12)
#define VNA_IOC_RING_SET_MSI (VNA_IOC | 0x13)
#define VNA_IOC_RING_INTR_CLR (VNA_IOC | 0x14)
+#define VNA_IOC_RING_SET_STATE (VNA_IOC | 0x15)
+#define VNA_IOC_RING_GET_STATE (VNA_IOC | 0x16)
+#define VNA_IOC_RING_PAUSE (VNA_IOC | 0x17)
#define VNA_IOC_INTR_POLL (VNA_IOC | 0x20)
#define VNA_IOC_SET_FEATURES (VNA_IOC | 0x21)
#define VNA_IOC_GET_FEATURES (VNA_IOC | 0x22)
#define VNA_IOC_SET_NOTIFY_IOP (VNA_IOC | 0x23)
+
+/*
+ * Viona Interface Version
+ *
+ * Like bhyve, viona exposes Private interfaces which are nonetheless consumed
+ * by out-of-gate consumers. While those consumers assume all risk of breakage
+ * incurred by subsequent changes, it would be nice to equip them to potentially
+ * detect (and handle) those modifications.
+ *
+ * There are no established criteria for the magnitude of change which requires
+ * this version to be incremented, and maintenance of it is considered a
+ * best-effort activity. Nothing is to be inferred about the magnitude of a
+ * change when the version is modified. It follows no rules like semver.
+ *
+ */
+#define VIONA_CURRENT_INTERFACE_VERSION 1
+
typedef struct vioc_create {
datalink_id_t c_linkid;
int c_vmfd;
@@ -43,6 +65,14 @@ typedef struct vioc_ring_init {
uint64_t ri_qaddr;
} vioc_ring_init_t;
+typedef struct vioc_ring_state {
+ uint16_t vrs_index;
+ uint16_t vrs_avail_idx;
+ uint16_t vrs_used_idx;
+ uint16_t vrs_qsize;
+ uint64_t vrs_qaddr;
+} vioc_ring_state_t;
+
typedef struct vioc_ring_msi {
uint16_t rm_index;
uint64_t rm_addr;