Commit 95242e44 authored by Jean-Philippe Brucker's avatar Jean-Philippe Brucker Committed by Will Deacon
Browse files

virtio: Implement notify_status



Modern virtio require proper status handling and reset. A "notify_status"
callback is already present in the virtio ops, but isn't implemented by
any device. Instead they currently use "set_guest_feature" to reset the
device and deal with endianess. This isn't sufficient for proper device
reset, so add the notify_status callback to all devices that need it.

To add useful hints like "start" and "stop", extend the status variable to
32-bits.
Signed-off-by: default avatarJean-Philippe Brucker <jean-philippe.brucker@arm.com>
[Julien T: Remove VIRTIO_CONFIG_S_NEEDS_RESET from config mask, as
	it is virtio v1+ macro and kvmtool only implements v0.9, this
	macro should not be referenced for now]
Signed-off-by: default avatarJulien Thierry <julien.thierry@arm.com>
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
parent 56c82a03
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/virtio_pci.h> #include <linux/virtio_pci.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/virtio_config.h>
#include <sys/uio.h> #include <sys/uio.h>
#include "kvm/barrier.h" #include "kvm/barrier.h"
...@@ -27,6 +28,20 @@ ...@@ -27,6 +28,20 @@
#define VIRTIO_ENDIAN_HOST VIRTIO_ENDIAN_BE #define VIRTIO_ENDIAN_HOST VIRTIO_ENDIAN_BE
#endif #endif
/* Reserved status bits */
#define VIRTIO_CONFIG_S_MASK \
(VIRTIO_CONFIG_S_ACKNOWLEDGE | \
VIRTIO_CONFIG_S_DRIVER | \
VIRTIO_CONFIG_S_DRIVER_OK | \
VIRTIO_CONFIG_S_FEATURES_OK | \
VIRTIO_CONFIG_S_FAILED)
/* Kvmtool status bits */
/* Start the device */
#define VIRTIO__STATUS_START (1 << 8)
/* Stop the device */
#define VIRTIO__STATUS_STOP (1 << 9)
struct virt_queue { struct virt_queue {
struct vring vring; struct vring vring;
u32 pfn; u32 pfn;
...@@ -162,6 +177,7 @@ struct virtio_device { ...@@ -162,6 +177,7 @@ struct virtio_device {
struct virtio_ops *ops; struct virtio_ops *ops;
u16 endian; u16 endian;
u32 features; u32 features;
u32 status;
}; };
struct virtio_ops { struct virtio_ops {
...@@ -178,7 +194,7 @@ struct virtio_ops { ...@@ -178,7 +194,7 @@ struct virtio_ops {
void (*notify_vq_eventfd)(struct kvm *kvm, void *dev, u32 vq, u32 efd); void (*notify_vq_eventfd)(struct kvm *kvm, void *dev, u32 vq, u32 efd);
int (*signal_vq)(struct kvm *kvm, struct virtio_device *vdev, u32 queueid); int (*signal_vq)(struct kvm *kvm, struct virtio_device *vdev, u32 queueid);
int (*signal_config)(struct kvm *kvm, struct virtio_device *vdev); int (*signal_config)(struct kvm *kvm, struct virtio_device *vdev);
void (*notify_status)(struct kvm *kvm, void *dev, u8 status); void (*notify_status)(struct kvm *kvm, void *dev, u32 status);
int (*init)(struct kvm *kvm, void *dev, struct virtio_device *vdev, int (*init)(struct kvm *kvm, void *dev, struct virtio_device *vdev,
int device_id, int subsys_id, int class); int device_id, int subsys_id, int class);
int (*exit)(struct kvm *kvm, struct virtio_device *vdev); int (*exit)(struct kvm *kvm, struct virtio_device *vdev);
...@@ -204,5 +220,7 @@ static inline void virtio_init_device_vq(struct virtio_device *vdev, ...@@ -204,5 +220,7 @@ static inline void virtio_init_device_vq(struct virtio_device *vdev,
void virtio_set_guest_features(struct kvm *kvm, struct virtio_device *vdev, void virtio_set_guest_features(struct kvm *kvm, struct virtio_device *vdev,
void *dev, u32 features); void *dev, u32 features);
void virtio_notify_status(struct kvm *kvm, struct virtio_device *vdev,
void *dev, u8 status);
#endif /* KVM__VIRTIO_H */ #endif /* KVM__VIRTIO_H */
...@@ -1382,6 +1382,10 @@ static void set_guest_features(struct kvm *kvm, void *dev, u32 features) ...@@ -1382,6 +1382,10 @@ static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
conf->tag_len = virtio_host_to_guest_u16(&p9dev->vdev, conf->tag_len); conf->tag_len = virtio_host_to_guest_u16(&p9dev->vdev, conf->tag_len);
} }
static void notify_status(struct kvm *kvm, void *dev, u32 status)
{
}
static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align, static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align,
u32 pfn) u32 pfn)
{ {
...@@ -1441,6 +1445,7 @@ struct virtio_ops p9_dev_virtio_ops = { ...@@ -1441,6 +1445,7 @@ struct virtio_ops p9_dev_virtio_ops = {
.get_host_features = get_host_features, .get_host_features = get_host_features,
.set_guest_features = set_guest_features, .set_guest_features = set_guest_features,
.init_vq = init_vq, .init_vq = init_vq,
.notify_status = notify_status,
.notify_vq = notify_vq, .notify_vq = notify_vq,
.get_pfn_vq = get_pfn_vq, .get_pfn_vq = get_pfn_vq,
.get_size_vq = get_size_vq, .get_size_vq = get_size_vq,
......
...@@ -193,6 +193,10 @@ static void set_guest_features(struct kvm *kvm, void *dev, u32 features) ...@@ -193,6 +193,10 @@ static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
bdev->features = features; bdev->features = features;
} }
static void notify_status(struct kvm *kvm, void *dev, u32 status)
{
}
static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align, static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align,
u32 pfn) u32 pfn)
{ {
...@@ -244,6 +248,7 @@ struct virtio_ops bln_dev_virtio_ops = { ...@@ -244,6 +248,7 @@ struct virtio_ops bln_dev_virtio_ops = {
.get_host_features = get_host_features, .get_host_features = get_host_features,
.set_guest_features = set_guest_features, .set_guest_features = set_guest_features,
.init_vq = init_vq, .init_vq = init_vq,
.notify_status = notify_status,
.notify_vq = notify_vq, .notify_vq = notify_vq,
.get_pfn_vq = get_pfn_vq, .get_pfn_vq = get_pfn_vq,
.get_size_vq = get_size_vq, .get_size_vq = get_size_vq,
......
...@@ -174,6 +174,10 @@ static void set_guest_features(struct kvm *kvm, void *dev, u32 features) ...@@ -174,6 +174,10 @@ static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
conf->opt_io_size = virtio_host_to_guest_u32(&bdev->vdev, conf->opt_io_size); conf->opt_io_size = virtio_host_to_guest_u32(&bdev->vdev, conf->opt_io_size);
} }
static void notify_status(struct kvm *kvm, void *dev, u32 status)
{
}
static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align, static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align,
u32 pfn) u32 pfn)
{ {
...@@ -249,6 +253,7 @@ static struct virtio_ops blk_dev_virtio_ops = { ...@@ -249,6 +253,7 @@ static struct virtio_ops blk_dev_virtio_ops = {
.get_host_features = get_host_features, .get_host_features = get_host_features,
.set_guest_features = set_guest_features, .set_guest_features = set_guest_features,
.init_vq = init_vq, .init_vq = init_vq,
.notify_status = notify_status,
.notify_vq = notify_vq, .notify_vq = notify_vq,
.get_pfn_vq = get_pfn_vq, .get_pfn_vq = get_pfn_vq,
.get_size_vq = get_size_vq, .get_size_vq = get_size_vq,
......
...@@ -140,6 +140,10 @@ static void set_guest_features(struct kvm *kvm, void *dev, u32 features) ...@@ -140,6 +140,10 @@ static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
conf->max_nr_ports = virtio_host_to_guest_u32(&cdev->vdev, conf->max_nr_ports); conf->max_nr_ports = virtio_host_to_guest_u32(&cdev->vdev, conf->max_nr_ports);
} }
static void notify_status(struct kvm *kvm, void *dev, u32 status)
{
}
static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align, static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align,
u32 pfn) u32 pfn)
{ {
...@@ -203,6 +207,7 @@ static struct virtio_ops con_dev_virtio_ops = { ...@@ -203,6 +207,7 @@ static struct virtio_ops con_dev_virtio_ops = {
.get_host_features = get_host_features, .get_host_features = get_host_features,
.set_guest_features = set_guest_features, .set_guest_features = set_guest_features,
.init_vq = init_vq, .init_vq = init_vq,
.notify_status = notify_status,
.notify_vq = notify_vq, .notify_vq = notify_vq,
.get_pfn_vq = get_pfn_vq, .get_pfn_vq = get_pfn_vq,
.get_size_vq = get_size_vq, .get_size_vq = get_size_vq,
......
...@@ -214,6 +214,29 @@ void virtio_set_guest_features(struct kvm *kvm, struct virtio_device *vdev, ...@@ -214,6 +214,29 @@ void virtio_set_guest_features(struct kvm *kvm, struct virtio_device *vdev,
vdev->ops->set_guest_features(kvm, dev, features); vdev->ops->set_guest_features(kvm, dev, features);
} }
void virtio_notify_status(struct kvm *kvm, struct virtio_device *vdev,
void *dev, u8 status)
{
u32 ext_status = status;
vdev->status &= ~VIRTIO_CONFIG_S_MASK;
vdev->status |= status;
/* Add a few hints to help devices */
if ((status & VIRTIO_CONFIG_S_DRIVER_OK) &&
!(vdev->status & VIRTIO__STATUS_START)) {
vdev->status |= VIRTIO__STATUS_START;
ext_status |= VIRTIO__STATUS_START;
} else if (!status && (vdev->status & VIRTIO__STATUS_START)) {
vdev->status &= ~VIRTIO__STATUS_START;
ext_status |= VIRTIO__STATUS_STOP;
}
if (vdev->ops->notify_status)
vdev->ops->notify_status(kvm, dev, ext_status);
}
int virtio_init(struct kvm *kvm, void *dev, struct virtio_device *vdev, int virtio_init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
struct virtio_ops *ops, enum virtio_trans trans, struct virtio_ops *ops, enum virtio_trans trans,
int device_id, int subsys_id, int class) int device_id, int subsys_id, int class)
......
...@@ -162,8 +162,7 @@ static void virtio_mmio_config_out(struct kvm_cpu *vcpu, ...@@ -162,8 +162,7 @@ static void virtio_mmio_config_out(struct kvm_cpu *vcpu,
vmmio->hdr.status = ioport__read32(data); vmmio->hdr.status = ioport__read32(data);
if (!vmmio->hdr.status) /* Sample endianness on reset */ if (!vmmio->hdr.status) /* Sample endianness on reset */
vdev->endian = kvm_cpu__get_endianness(vcpu); vdev->endian = kvm_cpu__get_endianness(vcpu);
if (vdev->ops->notify_status) virtio_notify_status(kvm, vdev, vmmio->dev, vmmio->hdr.status);
vdev->ops->notify_status(kvm, vmmio->dev, vmmio->hdr.status);
break; break;
case VIRTIO_MMIO_GUEST_FEATURES: case VIRTIO_MMIO_GUEST_FEATURES:
if (vmmio->hdr.guest_features_sel == 0) { if (vmmio->hdr.guest_features_sel == 0) {
......
...@@ -518,7 +518,10 @@ static void set_guest_features(struct kvm *kvm, void *dev, u32 features) ...@@ -518,7 +518,10 @@ static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
conf->status = virtio_host_to_guest_u16(&ndev->vdev, conf->status); conf->status = virtio_host_to_guest_u16(&ndev->vdev, conf->status);
conf->max_virtqueue_pairs = virtio_host_to_guest_u16(&ndev->vdev, conf->max_virtqueue_pairs = virtio_host_to_guest_u16(&ndev->vdev,
conf->max_virtqueue_pairs); conf->max_virtqueue_pairs);
}
static void virtio_net_start(struct net_dev *ndev)
{
if (ndev->mode == NET_MODE_TAP) { if (ndev->mode == NET_MODE_TAP) {
if (!virtio_net__tap_init(ndev)) if (!virtio_net__tap_init(ndev))
die_perror("TAP device initialized failed because"); die_perror("TAP device initialized failed because");
...@@ -534,6 +537,12 @@ static void set_guest_features(struct kvm *kvm, void *dev, u32 features) ...@@ -534,6 +537,12 @@ static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
} }
} }
static void notify_status(struct kvm *kvm, void *dev, u32 status)
{
if (status & VIRTIO__STATUS_START)
virtio_net_start(dev);
}
static bool is_ctrl_vq(struct net_dev *ndev, u32 vq) static bool is_ctrl_vq(struct net_dev *ndev, u32 vq)
{ {
return vq == (u32)(ndev->queue_pairs * 2); return vq == (u32)(ndev->queue_pairs * 2);
...@@ -683,6 +692,7 @@ static struct virtio_ops net_dev_virtio_ops = { ...@@ -683,6 +692,7 @@ static struct virtio_ops net_dev_virtio_ops = {
.notify_vq = notify_vq, .notify_vq = notify_vq,
.notify_vq_gsi = notify_vq_gsi, .notify_vq_gsi = notify_vq_gsi,
.notify_vq_eventfd = notify_vq_eventfd, .notify_vq_eventfd = notify_vq_eventfd,
.notify_status = notify_status,
}; };
static void virtio_net__vhost_init(struct kvm *kvm, struct net_dev *ndev) static void virtio_net__vhost_init(struct kvm *kvm, struct net_dev *ndev)
......
...@@ -285,8 +285,7 @@ static bool virtio_pci__io_out(struct ioport *ioport, struct kvm_cpu *vcpu, u16 ...@@ -285,8 +285,7 @@ static bool virtio_pci__io_out(struct ioport *ioport, struct kvm_cpu *vcpu, u16
vpci->status = ioport__read8(data); vpci->status = ioport__read8(data);
if (!vpci->status) /* Sample endianness on reset */ if (!vpci->status) /* Sample endianness on reset */
vdev->endian = kvm_cpu__get_endianness(vcpu); vdev->endian = kvm_cpu__get_endianness(vcpu);
if (vdev->ops->notify_status) virtio_notify_status(kvm, vdev, vpci->dev, vpci->status);
vdev->ops->notify_status(kvm, vpci->dev, vpci->status);
break; break;
default: default:
ret = virtio_pci__specific_io_out(kvm, vdev, port, data, size, offset); ret = virtio_pci__specific_io_out(kvm, vdev, port, data, size, offset);
......
...@@ -50,6 +50,10 @@ static void set_guest_features(struct kvm *kvm, void *dev, u32 features) ...@@ -50,6 +50,10 @@ static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
sdev->features = features; sdev->features = features;
} }
static void notify_status(struct kvm *kvm, void *dev, u32 status)
{
}
static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align, static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align,
u32 pfn) u32 pfn)
{ {
...@@ -171,6 +175,7 @@ static struct virtio_ops scsi_dev_virtio_ops = { ...@@ -171,6 +175,7 @@ static struct virtio_ops scsi_dev_virtio_ops = {
.get_pfn_vq = get_pfn_vq, .get_pfn_vq = get_pfn_vq,
.get_size_vq = get_size_vq, .get_size_vq = get_size_vq,
.set_size_vq = set_size_vq, .set_size_vq = set_size_vq,
.notify_status = notify_status,
.notify_vq = notify_vq, .notify_vq = notify_vq,
.notify_vq_gsi = notify_vq_gsi, .notify_vq_gsi = notify_vq_gsi,
.notify_vq_eventfd = notify_vq_eventfd, .notify_vq_eventfd = notify_vq_eventfd,
......
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