Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c45a6816 authored by Rusty Russell's avatar Rusty Russell
Browse files

virtio: explicit advertisement of driver features



A recent proposed feature addition to the virtio block driver revealed
some flaws in the API: in particular, we assume that feature
negotiation is complete once a driver's probe function returns.

There is nothing in the API to require this, however, and even I
didn't notice when it was violated.

So instead, we require the driver to specify what features it supports
in a table, we can then move the feature negotiation into the virtio
core.  The intersection of device and driver features are presented in
a new 'features' bitmap in the struct virtio_device.

Note that this highlights the difference between Linux unsigned-long
bitmaps where each unsigned long is in native endian, and a
straight-forward little-endian array of bytes.

Drivers can still remove feature bits in their probe routine if they
really have to.

API changes:
- dev->config->feature() no longer gets and acks a feature.
- drivers should advertise their features in the 'feature_table' field
- use virtio_has_feature() for extra sanity when checking feature bits

Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 72e61eb4
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -242,7 +242,7 @@ static int virtblk_probe(struct virtio_device *vdev)
	index++;

	/* If barriers are supported, tell block layer that queue is ordered */
	if (vdev->config->feature(vdev, VIRTIO_BLK_F_BARRIER))
	if (virtio_has_feature(vdev, VIRTIO_BLK_F_BARRIER))
		blk_queue_ordered(vblk->disk->queue, QUEUE_ORDERED_TAG, NULL);

	/* Host must always specify the capacity. */
@@ -308,7 +308,13 @@ static struct virtio_device_id id_table[] = {
	{ 0 },
};

static unsigned int features[] = {
	VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX,
};

static struct virtio_driver virtio_blk = {
	.feature_table = features,
	.feature_table_size = ARRAY_SIZE(features),
	.driver.name =	KBUILD_MODNAME,
	.driver.owner =	THIS_MODULE,
	.id_table =	id_table,
+28 −20
Original line number Diff line number Diff line
@@ -85,27 +85,34 @@ static unsigned desc_size(const struct lguest_device_desc *desc)
		+ desc->config_len;
}

/* This tests (and acknowleges) a feature bit. */
static bool lg_feature(struct virtio_device *vdev, unsigned fbit)
/* This gets the device's feature bits. */
static u32 lg_get_features(struct virtio_device *vdev)
{
	unsigned int i;
	u32 features = 0;
	struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
	u8 *in_features = lg_features(desc);

	/* We do this the slow but generic way. */
	for (i = 0; i < min(desc->feature_len * 8, 32); i++)
		if (in_features[i / 8] & (1 << (i % 8)))
			features |= (1 << i);

	return features;
}

static void lg_set_features(struct virtio_device *vdev, u32 features)
{
	unsigned int i;
	struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
	u8 *features;

	/* Obviously if they ask for a feature off the end of our feature
	 * bitmap, it's not set. */
	if (fbit / 8 > desc->feature_len)
		return false;

	/* The feature bitmap comes after the virtqueues. */
	features = lg_features(desc);
	if (!(features[fbit / 8] & (1 << (fbit % 8))))
		return false;

	/* We set the matching bit in the other half of the bitmap to tell the
	 * Host we want to use this feature.  We don't use this yet, but we
	 * could in future. */
	features[desc->feature_len + fbit / 8] |= (1 << (fbit % 8));
	return true;
	/* Second half of bitmap is features we accept. */
	u8 *out_features = lg_features(desc) + desc->feature_len;

	memset(out_features, 0, desc->feature_len);
	for (i = 0; i < min(desc->feature_len * 8, 32); i++) {
		if (features & (1 << i))
			out_features[i / 8] |= (1 << (i % 8));
	}
}

/* Once they've found a field, getting a copy of it is easy. */
@@ -286,7 +293,8 @@ static void lg_del_vq(struct virtqueue *vq)

/* The ops structure which hooks everything together. */
static struct virtio_config_ops lguest_config_ops = {
	.feature = lg_feature,
	.get_features = lg_get_features,
	.set_features = lg_set_features,
	.get = lg_get,
	.set = lg_set,
	.get_status = lg_get_status,
+15 −7
Original line number Diff line number Diff line
@@ -378,26 +378,26 @@ static int virtnet_probe(struct virtio_device *vdev)
	SET_NETDEV_DEV(dev, &vdev->dev);

	/* Do we support "hardware" checksums? */
	if (csum && vdev->config->feature(vdev, VIRTIO_NET_F_CSUM)) {
	if (csum && virtio_has_feature(vdev, VIRTIO_NET_F_CSUM)) {
		/* This opens up the world of extra features. */
		dev->features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST;
		if (gso && vdev->config->feature(vdev, VIRTIO_NET_F_GSO)) {
		if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_GSO)) {
			dev->features |= NETIF_F_TSO | NETIF_F_UFO
				| NETIF_F_TSO_ECN | NETIF_F_TSO6;
		}
		/* Individual feature bits: what can host handle? */
		if (gso && vdev->config->feature(vdev, VIRTIO_NET_F_HOST_TSO4))
		if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_TSO4))
			dev->features |= NETIF_F_TSO;
		if (gso && vdev->config->feature(vdev, VIRTIO_NET_F_HOST_TSO6))
		if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_TSO6))
			dev->features |= NETIF_F_TSO6;
		if (gso && vdev->config->feature(vdev, VIRTIO_NET_F_HOST_ECN))
		if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_ECN))
			dev->features |= NETIF_F_TSO_ECN;
		if (gso && vdev->config->feature(vdev, VIRTIO_NET_F_HOST_UFO))
		if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_UFO))
			dev->features |= NETIF_F_UFO;
	}

	/* Configuration may specify what MAC to use.  Otherwise random. */
	if (vdev->config->feature(vdev, VIRTIO_NET_F_MAC)) {
	if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC)) {
		vdev->config->get(vdev,
				  offsetof(struct virtio_net_config, mac),
				  dev->dev_addr, dev->addr_len);
@@ -486,7 +486,15 @@ static struct virtio_device_id id_table[] = {
	{ 0 },
};

static unsigned int features[] = {
	VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GSO, VIRTIO_NET_F_MAC,
	VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6,
	VIRTIO_NET_F_HOST_ECN,
};

static struct virtio_driver virtio_net = {
	.feature_table = features,
	.feature_table_size = ARRAY_SIZE(features),
	.driver.name =	KBUILD_MODNAME,
	.driver.owner =	THIS_MODULE,
	.id_table =	id_table,
+36 −2
Original line number Diff line number Diff line
@@ -80,19 +80,51 @@ static void add_status(struct virtio_device *dev, unsigned status)
	dev->config->set_status(dev, dev->config->get_status(dev) | status);
}

void virtio_check_driver_offered_feature(const struct virtio_device *vdev,
					 unsigned int fbit)
{
	unsigned int i;
	struct virtio_driver *drv = container_of(vdev->dev.driver,
						 struct virtio_driver, driver);

	for (i = 0; i < drv->feature_table_size; i++)
		if (drv->feature_table[i] == fbit)
			return;
	BUG();
}
EXPORT_SYMBOL_GPL(virtio_check_driver_offered_feature);

static int virtio_dev_probe(struct device *_d)
{
	int err;
	int err, i;
	struct virtio_device *dev = container_of(_d,struct virtio_device,dev);
	struct virtio_driver *drv = container_of(dev->dev.driver,
						 struct virtio_driver, driver);
	u32 device_features;

	/* We have a driver! */
	add_status(dev, VIRTIO_CONFIG_S_DRIVER);

	/* Figure out what features the device supports. */
	device_features = dev->config->get_features(dev);

	/* Features supported by both device and driver into dev->features. */
	memset(dev->features, 0, sizeof(dev->features));
	for (i = 0; i < drv->feature_table_size; i++) {
		unsigned int f = drv->feature_table[i];
		BUG_ON(f >= 32);
		if (device_features & (1 << f))
			set_bit(f, dev->features);
	}

	err = drv->probe(dev);
	if (err)
		add_status(dev, VIRTIO_CONFIG_S_FAILED);
	else
	else {
		add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
		/* They should never have set feature bits beyond 32 */
		dev->config->set_features(dev, dev->features[0]);
	}
	return err;
}

@@ -114,6 +146,8 @@ static int virtio_dev_remove(struct device *_d)

int register_virtio_driver(struct virtio_driver *driver)
{
	/* Catch this early. */
	BUG_ON(driver->feature_table_size && !driver->feature_table);
	driver->driver.bus = &virtio_bus;
	driver->driver.probe = virtio_dev_probe;
	driver->driver.remove = virtio_dev_remove;
+5 −1
Original line number Diff line number Diff line
@@ -227,7 +227,7 @@ static int virtballoon_probe(struct virtio_device *vdev)
	}

	vb->tell_host_first
		= vdev->config->feature(vdev, VIRTIO_BALLOON_F_MUST_TELL_HOST);
		= virtio_has_feature(vdev, VIRTIO_BALLOON_F_MUST_TELL_HOST);

	return 0;

@@ -259,7 +259,11 @@ static void virtballoon_remove(struct virtio_device *vdev)
	kfree(vb);
}

static unsigned int features[] = { VIRTIO_BALLOON_F_MUST_TELL_HOST };

static struct virtio_driver virtio_balloon = {
	.feature_table = features,
	.feature_table_size = ARRAY_SIZE(features),
	.driver.name =	KBUILD_MODNAME,
	.driver.owner =	THIS_MODULE,
	.id_table =	id_table,
Loading