summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/uts/common/io/vioblk/vioblk.c118
-rw-r--r--usr/src/uts/common/io/virtio/virtio.h16
-rw-r--r--usr/src/uts/common/io/virtio/virtio_impl.h6
-rw-r--r--usr/src/uts/common/io/virtio/virtio_main.c135
4 files changed, 217 insertions, 58 deletions
diff --git a/usr/src/uts/common/io/vioblk/vioblk.c b/usr/src/uts/common/io/vioblk/vioblk.c
index 0cccb84bce..f6649bdd12 100644
--- a/usr/src/uts/common/io/vioblk/vioblk.c
+++ b/usr/src/uts/common/io/vioblk/vioblk.c
@@ -25,6 +25,7 @@
* Copyright 2020 Joyent Inc.
* Copyright 2019 Western Digital Corporation.
* Copyright 2020 Oxide Computer Company
+ * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
*/
/*
@@ -94,9 +95,10 @@
#include "vioblk.h"
static void vioblk_get_id(vioblk_t *);
-uint_t vioblk_int_handler(caddr_t, caddr_t);
+static uint_t vioblk_int_handler(caddr_t, caddr_t);
static uint_t vioblk_poll(vioblk_t *);
static int vioblk_quiesce(dev_info_t *);
+static int vioblk_read_capacity(vioblk_t *);
static int vioblk_attach(dev_info_t *, ddi_attach_cmd_t);
static int vioblk_detach(dev_info_t *, ddi_detach_cmd_t);
@@ -737,8 +739,8 @@ vioblk_poll(vioblk_t *vib)
return (count);
}
-uint_t
-vioblk_int_handler(caddr_t arg0, caddr_t arg1)
+static uint_t
+vioblk_int_handler(caddr_t arg0, caddr_t arg1 __unused)
{
vioblk_t *vib = (vioblk_t *)arg0;
uint_t count;
@@ -755,6 +757,33 @@ vioblk_int_handler(caddr_t arg0, caddr_t arg1)
return (DDI_INTR_CLAIMED);
}
+static uint_t
+vioblk_cfgchange(caddr_t arg0, caddr_t arg1 __unused)
+{
+ vioblk_t *vib = (vioblk_t *)arg0;
+
+ dev_err(vib->vib_dip, CE_NOTE, "!Configuration changed");
+
+ mutex_enter(&vib->vib_mutex);
+
+ /*
+ * The configuration space of the device has changed in some way.
+ * At present, we only re-read the device capacity and trigger
+ * blkdev to check the device state.
+ */
+
+ if (vioblk_read_capacity(vib) == DDI_FAILURE) {
+ mutex_exit(&vib->vib_mutex);
+ return (DDI_INTR_CLAIMED);
+ }
+
+ mutex_exit(&vib->vib_mutex);
+
+ bd_state_change(vib->vib_bd_h);
+
+ return (DDI_INTR_CLAIMED);
+}
+
static void
vioblk_free_reqs(vioblk_t *vib)
{
@@ -823,6 +852,50 @@ fail:
}
static int
+vioblk_read_capacity(vioblk_t *vib)
+{
+ virtio_t *vio = vib->vib_virtio;
+
+ /* The capacity is always available */
+ if ((vib->vib_nblks = virtio_dev_get64(vio,
+ VIRTIO_BLK_CONFIG_CAPACITY)) == UINT64_MAX) {
+ dev_err(vib->vib_dip, CE_WARN, "invalid capacity");
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Determine the optimal logical block size recommended by the device.
+ * This size is advisory; the protocol always deals in 512 byte blocks.
+ */
+ vib->vib_blk_size = DEV_BSIZE;
+ if (virtio_feature_present(vio, VIRTIO_BLK_F_BLK_SIZE)) {
+ uint32_t v = virtio_dev_get32(vio, VIRTIO_BLK_CONFIG_BLK_SIZE);
+
+ if (v != 0 && v != PCI_EINVAL32)
+ vib->vib_blk_size = v;
+ }
+
+ /*
+ * Device capacity is always in 512-byte units, convert to
+ * native blocks.
+ */
+ vib->vib_nblks = (vib->vib_nblks * DEV_BSIZE) / vib->vib_blk_size;
+
+ /*
+ * The device may also provide an advisory physical block size.
+ */
+ vib->vib_pblk_size = vib->vib_blk_size;
+ if (virtio_feature_present(vio, VIRTIO_BLK_F_TOPOLOGY)) {
+ uint8_t v = virtio_dev_get8(vio, VIRTIO_BLK_CONFIG_TOPO_PBEXP);
+
+ if (v != PCI_EINVAL8)
+ vib->vib_pblk_size <<= v;
+ }
+
+ return (DDI_SUCCESS);
+}
+
+static int
vioblk_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int instance = ddi_get_instance(dip);
@@ -906,6 +979,8 @@ vioblk_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
goto fail;
}
+ virtio_register_cfgchange_handler(vio, vioblk_cfgchange, vib);
+
if (virtio_init_complete(vio, 0) != DDI_SUCCESS) {
dev_err(dip, CE_WARN, "failed to complete Virtio init");
goto fail;
@@ -944,42 +1019,9 @@ vioblk_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
kstat_install(vib->vib_kstat);
vib->vib_readonly = virtio_feature_present(vio, VIRTIO_BLK_F_RO);
- if ((vib->vib_nblks = virtio_dev_get64(vio,
- VIRTIO_BLK_CONFIG_CAPACITY)) == UINT64_MAX) {
- dev_err(dip, CE_WARN, "invalid capacity");
- goto fail;
- }
-
- /*
- * Determine the optimal logical block size recommended by the device.
- * This size is advisory; the protocol always deals in 512 byte blocks.
- */
- vib->vib_blk_size = DEV_BSIZE;
- if (virtio_feature_present(vio, VIRTIO_BLK_F_BLK_SIZE)) {
- uint32_t v = virtio_dev_get32(vio, VIRTIO_BLK_CONFIG_BLK_SIZE);
-
- if (v != 0 && v != PCI_EINVAL32) {
- vib->vib_blk_size = v;
- }
- }
-
- /*
- * Device capacity is always in 512-byte units, convert to
- * native blocks.
- */
- vib->vib_nblks = (vib->vib_nblks * DEV_BSIZE) / vib->vib_blk_size;
- /*
- * The device may also provide an advisory physical block size.
- */
- vib->vib_pblk_size = vib->vib_blk_size;
- if (virtio_feature_present(vio, VIRTIO_BLK_F_TOPOLOGY)) {
- uint8_t v = virtio_dev_get8(vio, VIRTIO_BLK_CONFIG_TOPO_PBEXP);
-
- if (v != PCI_EINVAL8) {
- vib->vib_pblk_size <<= v;
- }
- }
+ if (vioblk_read_capacity(vib) == DDI_FAILURE)
+ goto fail;
/*
* The maximum size for a cookie in a request.
diff --git a/usr/src/uts/common/io/virtio/virtio.h b/usr/src/uts/common/io/virtio/virtio.h
index 420f9ccfed..48e15b28f2 100644
--- a/usr/src/uts/common/io/virtio/virtio.h
+++ b/usr/src/uts/common/io/virtio/virtio.h
@@ -11,6 +11,7 @@
/*
* Copyright 2019 Joyent, Inc.
+ * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
*/
#ifndef _VIRTIO_H
@@ -81,6 +82,18 @@
* is usually either negotiated with the device, or determined structurally
* based on the shape of the buffers required for device operation.
*
+ * FRAMEWORK INITIALISATION: CONFIGURATION SPACE CHANGE HANDLER
+ *
+ * During the initialisation phase, the client driver may register a handler
+ * function for receiving device configuration space change events. Once
+ * initialisation has been completed, this cannot be changed without destroying
+ * the framework object and beginning again from scratch.
+ *
+ * When a configuration space change interrupt is received, the provided
+ * handler will be called with two arguments: first, the provided user data
+ * argument; and second, a pointer to the "virtio_t" object for this instance.
+ * The handler is called in an interrupt context.
+ *
* FRAMEWORK INITIALISATION: FINISHING
*
* Once queue configuration has been completed, the client driver calls
@@ -281,6 +294,9 @@ int virtio_init_complete(virtio_t *, int);
int virtio_quiesce(virtio_t *);
void virtio_shutdown(virtio_t *);
+void virtio_register_cfgchange_handler(virtio_t *, ddi_intr_handler_t *,
+ void *);
+
void *virtio_intr_pri(virtio_t *);
void virtio_device_reset(virtio_t *);
diff --git a/usr/src/uts/common/io/virtio/virtio_impl.h b/usr/src/uts/common/io/virtio/virtio_impl.h
index af786583f4..ef5f43397a 100644
--- a/usr/src/uts/common/io/virtio/virtio_impl.h
+++ b/usr/src/uts/common/io/virtio/virtio_impl.h
@@ -11,6 +11,7 @@
/*
* Copyright 2019 Joyent, Inc.
+ * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
*/
#ifndef _VIRTIO_IMPL_H
@@ -98,6 +99,11 @@ struct virtio {
int vio_interrupt_type;
int vio_interrupt_cap;
uint_t vio_interrupt_priority;
+
+ ddi_intr_handler_t *vio_cfgchange_handler;
+ void *vio_cfgchange_handlerarg;
+ boolean_t vio_cfgchange_handler_added;
+ uint_t vio_cfgchange_handler_index;
};
struct virtio_queue {
diff --git a/usr/src/uts/common/io/virtio/virtio_main.c b/usr/src/uts/common/io/virtio/virtio_main.c
index 04b22709e8..28dce6dc92 100644
--- a/usr/src/uts/common/io/virtio/virtio_main.c
+++ b/usr/src/uts/common/io/virtio/virtio_main.c
@@ -11,6 +11,7 @@
/*
* Copyright 2019 Joyent, Inc.
+ * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
*/
/*
@@ -322,6 +323,26 @@ virtio_init(dev_info_t *dip, uint64_t driver_features, boolean_t allow_indirect)
}
/*
+ * Some virtio devices can change their device configuration state at any
+ * time. This function may be called by the driver during the initialisation
+ * phase - before calling virtio_init_complete() - in order to register a
+ * handler function which will be called when the device configuration space
+ * is updated.
+ */
+void
+virtio_register_cfgchange_handler(virtio_t *vio, ddi_intr_handler_t *func,
+ void *funcarg)
+{
+ VERIFY(!(vio->vio_initlevel & VIRTIO_INITLEVEL_INT_ADDED));
+ VERIFY(!vio->vio_cfgchange_handler_added);
+
+ mutex_enter(&vio->vio_mutex);
+ vio->vio_cfgchange_handler = func;
+ vio->vio_cfgchange_handlerarg = funcarg;
+ mutex_exit(&vio->vio_mutex);
+}
+
+/*
* This function must be called by the driver once it has completed early setup
* calls. The value of "allowed_interrupt_types" is a mask of interrupt types
* (DDI_INTR_TYPE_MSIX, etc) that we'll try to use when installing handlers, or
@@ -333,7 +354,8 @@ virtio_init_complete(virtio_t *vio, int allowed_interrupt_types)
VERIFY(!(vio->vio_initlevel & VIRTIO_INITLEVEL_PROVIDER));
vio->vio_initlevel |= VIRTIO_INITLEVEL_PROVIDER;
- if (!list_is_empty(&vio->vio_queues)) {
+ if (!list_is_empty(&vio->vio_queues) ||
+ vio->vio_cfgchange_handler != NULL) {
/*
* Set up interrupts for the queues that have been registered.
*/
@@ -1343,32 +1365,45 @@ virtio_shared_isr(caddr_t arg0, caddr_t arg1)
* this field resets it to zero.
*/
isr = virtio_get8(vio, VIRTIO_LEGACY_ISR_STATUS);
- if ((isr & VIRTIO_ISR_CHECK_QUEUES) == 0) {
- goto done;
- }
- for (virtio_queue_t *viq = list_head(&vio->vio_queues); viq != NULL;
- viq = list_next(&vio->vio_queues, viq)) {
- if (viq->viq_func != NULL) {
- mutex_exit(&vio->vio_mutex);
- if (viq->viq_func(viq->viq_funcarg, arg0) ==
- DDI_INTR_CLAIMED) {
- r = DDI_INTR_CLAIMED;
- }
- mutex_enter(&vio->vio_mutex);
+ if ((isr & VIRTIO_ISR_CHECK_QUEUES) != 0) {
+ r = DDI_INTR_CLAIMED;
- if (vio->vio_initlevel & VIRTIO_INITLEVEL_SHUTDOWN) {
- /*
- * The device was shut down while in a queue
- * handler routine.
- */
- goto done;
+ for (virtio_queue_t *viq = list_head(&vio->vio_queues);
+ viq != NULL; viq = list_next(&vio->vio_queues, viq)) {
+ if (viq->viq_func != NULL) {
+ mutex_exit(&vio->vio_mutex);
+ (void) viq->viq_func(viq->viq_funcarg, arg0);
+ mutex_enter(&vio->vio_mutex);
+
+ if (vio->vio_initlevel &
+ VIRTIO_INITLEVEL_SHUTDOWN) {
+ /*
+ * The device was shut down while in a
+ * queue handler routine.
+ */
+ break;
+ }
}
}
}
-done:
mutex_exit(&vio->vio_mutex);
+
+ /*
+ * vio_cfgchange_{handler,handlerarg} cannot change while interrupts
+ * are configured so it is safe to access them outside of the lock.
+ */
+
+ if ((isr & VIRTIO_ISR_CHECK_CONFIG) != 0) {
+ r = DDI_INTR_CLAIMED;
+ if (vio->vio_cfgchange_handler != NULL) {
+ (void) vio->vio_cfgchange_handler(
+ (caddr_t)vio->vio_cfgchange_handlerarg,
+ (caddr_t)vio);
+ }
+ }
+
return (r);
}
@@ -1392,6 +1427,13 @@ virtio_interrupts_setup(virtio_t *vio, int allow_types)
}
}
+ /*
+ * If there is a configuration change handler, one extra interrupt
+ * is needed for that.
+ */
+ if (vio->vio_cfgchange_handler != NULL)
+ count++;
+
if (ddi_intr_get_supported_types(dip, &types) != DDI_SUCCESS) {
dev_err(dip, CE_WARN, "could not get supported interrupts");
mutex_exit(&vio->vio_mutex);
@@ -1493,6 +1535,22 @@ add_handlers:
VERIFY3S(vio->vio_ninterrupts, ==, count);
uint_t n = 0;
+
+ /* Bind the configuration vector interrupt */
+ if (vio->vio_cfgchange_handler != NULL) {
+ if (ddi_intr_add_handler(vio->vio_interrupts[n],
+ vio->vio_cfgchange_handler,
+ (caddr_t)vio->vio_cfgchange_handlerarg,
+ (caddr_t)vio) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN,
+ "adding configuration change interrupt failed");
+ goto fail;
+ }
+ vio->vio_cfgchange_handler_added = B_TRUE;
+ vio->vio_cfgchange_handler_index = n;
+ n++;
+ }
+
for (virtio_queue_t *viq = list_head(&vio->vio_queues); viq != NULL;
viq = list_next(&vio->vio_queues, viq)) {
if (viq->viq_func == NULL) {
@@ -1546,6 +1604,21 @@ virtio_interrupts_teardown(virtio_t *vio)
}
}
} else {
+ /*
+ * Remove the configuration vector interrupt handler.
+ */
+ if (vio->vio_cfgchange_handler_added) {
+ int r;
+
+ if ((r = ddi_intr_remove_handler(
+ vio->vio_interrupts[0])) != DDI_SUCCESS) {
+ dev_err(vio->vio_dip, CE_WARN,
+ "removing configuration change interrupt "
+ "handler failed (%d)", r);
+ }
+ vio->vio_cfgchange_handler_added = B_FALSE;
+ }
+
for (virtio_queue_t *viq = list_head(&vio->vio_queues);
viq != NULL; viq = list_next(&vio->vio_queues, viq)) {
int r;
@@ -1606,6 +1679,11 @@ virtio_interrupts_unwind(virtio_t *vio)
virtio_put16(vio, VIRTIO_LEGACY_MSIX_QUEUE,
VIRTIO_LEGACY_MSI_NO_VECTOR);
}
+
+ if (vio->vio_cfgchange_handler_added) {
+ virtio_put16(vio, VIRTIO_LEGACY_MSIX_CONFIG,
+ VIRTIO_LEGACY_MSI_NO_VECTOR);
+ }
}
if (vio->vio_interrupt_cap & DDI_INTR_FLAG_BLOCK) {
@@ -1703,6 +1781,23 @@ virtio_interrupts_enable(virtio_t *vio)
return (DDI_FAILURE);
}
}
+
+ if (vio->vio_cfgchange_handler_added) {
+ virtio_put16(vio, VIRTIO_LEGACY_MSIX_CONFIG,
+ vio->vio_cfgchange_handler_index);
+
+ /* Verify the value was accepted. */
+ if (virtio_get16(vio, VIRTIO_LEGACY_MSIX_CONFIG) !=
+ vio->vio_cfgchange_handler_index) {
+ dev_err(vio->vio_dip, CE_WARN,
+ "failed to configure MSI-X vector for "
+ "configuration");
+
+ virtio_interrupts_unwind(vio);
+ mutex_exit(&vio->vio_mutex);
+ return (DDI_FAILURE);
+ }
+ }
}
vio->vio_initlevel |= VIRTIO_INITLEVEL_INT_ENABLED;