Commit a586d4f6 authored by Rusty Russell's avatar Rusty Russell

virtio: simplify config mechanism.

Previously we used a type/len pair within the config space, but this
seems overkill.  We now simply define a structure which represents the
layout in the config space: the config space can now only be extended
at the end.

The main driver-visible changes:
1) We indicate what fields are present with an explicit feature bit.
2) Virtqueues are explicitly numbered, and not in the config space.
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent f35d9d8a
This diff is collapsed.
...@@ -162,8 +162,6 @@ static int virtblk_probe(struct virtio_device *vdev) ...@@ -162,8 +162,6 @@ static int virtblk_probe(struct virtio_device *vdev)
{ {
struct virtio_blk *vblk; struct virtio_blk *vblk;
int err, major; int err, major;
void *token;
unsigned int len;
u64 cap; u64 cap;
u32 v; u32 v;
...@@ -178,7 +176,7 @@ static int virtblk_probe(struct virtio_device *vdev) ...@@ -178,7 +176,7 @@ static int virtblk_probe(struct virtio_device *vdev)
vblk->vdev = vdev; vblk->vdev = vdev;
/* We expect one virtqueue, for output. */ /* We expect one virtqueue, for output. */
vblk->vq = vdev->config->find_vq(vdev, blk_done); vblk->vq = vdev->config->find_vq(vdev, 0, blk_done);
if (IS_ERR(vblk->vq)) { if (IS_ERR(vblk->vq)) {
err = PTR_ERR(vblk->vq); err = PTR_ERR(vblk->vq);
goto out_free_vblk; goto out_free_vblk;
...@@ -216,15 +214,12 @@ static int virtblk_probe(struct virtio_device *vdev) ...@@ -216,15 +214,12 @@ static int virtblk_probe(struct virtio_device *vdev)
vblk->disk->fops = &virtblk_fops; vblk->disk->fops = &virtblk_fops;
/* If barriers are supported, tell block layer that queue is ordered */ /* If barriers are supported, tell block layer that queue is ordered */
token = vdev->config->find(vdev, VIRTIO_CONFIG_BLK_F, &len); if (vdev->config->feature(vdev, VIRTIO_BLK_F_BARRIER))
if (virtio_use_bit(vdev, token, len, VIRTIO_BLK_F_BARRIER))
blk_queue_ordered(vblk->disk->queue, QUEUE_ORDERED_TAG, NULL); blk_queue_ordered(vblk->disk->queue, QUEUE_ORDERED_TAG, NULL);
err = virtio_config_val(vdev, VIRTIO_CONFIG_BLK_F_CAPACITY, &cap); /* Host must always specify the capacity. */
if (err) { __virtio_config_val(vdev, offsetof(struct virtio_blk_config, capacity),
dev_err(&vdev->dev, "Bad/missing capacity in config\n"); &cap);
goto out_cleanup_queue;
}
/* If capacity is too big, truncate with warning. */ /* If capacity is too big, truncate with warning. */
if ((sector_t)cap != cap) { if ((sector_t)cap != cap) {
...@@ -234,27 +229,23 @@ static int virtblk_probe(struct virtio_device *vdev) ...@@ -234,27 +229,23 @@ static int virtblk_probe(struct virtio_device *vdev)
} }
set_capacity(vblk->disk, cap); set_capacity(vblk->disk, cap);
err = virtio_config_val(vdev, VIRTIO_CONFIG_BLK_F_SIZE_MAX, &v); /* Host can optionally specify maximum segment size and number of
* segments. */
err = virtio_config_val(vdev, VIRTIO_BLK_F_SIZE_MAX,
offsetof(struct virtio_blk_config, size_max),
&v);
if (!err) if (!err)
blk_queue_max_segment_size(vblk->disk->queue, v); blk_queue_max_segment_size(vblk->disk->queue, v);
else if (err != -ENOENT) {
dev_err(&vdev->dev, "Bad SIZE_MAX in config\n");
goto out_cleanup_queue;
}
err = virtio_config_val(vdev, VIRTIO_CONFIG_BLK_F_SEG_MAX, &v); err = virtio_config_val(vdev, VIRTIO_BLK_F_SEG_MAX,
offsetof(struct virtio_blk_config, seg_max),
&v);
if (!err) if (!err)
blk_queue_max_hw_segments(vblk->disk->queue, v); blk_queue_max_hw_segments(vblk->disk->queue, v);
else if (err != -ENOENT) {
dev_err(&vdev->dev, "Bad SEG_MAX in config\n");
goto out_cleanup_queue;
}
add_disk(vblk->disk); add_disk(vblk->disk);
return 0; return 0;
out_cleanup_queue:
blk_cleanup_queue(vblk->disk->queue);
out_put_disk: out_put_disk:
put_disk(vblk->disk); put_disk(vblk->disk);
out_unregister_blkdev: out_unregister_blkdev:
......
...@@ -158,13 +158,13 @@ static int __devinit virtcons_probe(struct virtio_device *dev) ...@@ -158,13 +158,13 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
/* Find the input queue. */ /* Find the input queue. */
/* FIXME: This is why we want to wean off hvc: we do nothing /* FIXME: This is why we want to wean off hvc: we do nothing
* when input comes in. */ * when input comes in. */
in_vq = vdev->config->find_vq(vdev, NULL); in_vq = vdev->config->find_vq(vdev, 0, NULL);
if (IS_ERR(in_vq)) { if (IS_ERR(in_vq)) {
err = PTR_ERR(in_vq); err = PTR_ERR(in_vq);
goto free; goto free;
} }
out_vq = vdev->config->find_vq(vdev, NULL); out_vq = vdev->config->find_vq(vdev, 1, NULL);
if (IS_ERR(out_vq)) { if (IS_ERR(out_vq)) {
err = PTR_ERR(out_vq); err = PTR_ERR(out_vq);
goto free_in_vq; goto free_in_vq;
......
...@@ -52,57 +52,82 @@ struct lguest_device { ...@@ -52,57 +52,82 @@ struct lguest_device {
/*D:130 /*D:130
* Device configurations * Device configurations
* *
* The configuration information for a device consists of a series of fields. * The configuration information for a device consists of one or more
* We don't really care what they are: the Launcher set them up, and the driver * virtqueues, a feature bitmaks, and some configuration bytes. The
* will look at them during setup. * configuration bytes don't really matter to us: the Launcher set them up, and
* the driver will look at them during setup.
* *
* For us these fields come immediately after that device's descriptor in the * A convenient routine to return the device's virtqueue config array:
* lguest_devices page. * immediately after the descriptor. */
* static struct lguest_vqconfig *lg_vq(const struct lguest_device_desc *desc)
* Each field starts with a "type" byte, a "length" byte, then that number of {
* bytes of configuration information. The device descriptor tells us the return (void *)(desc + 1);
* total configuration length so we know when we've reached the last field. */ }
/* type + length bytes */ /* The features come immediately after the virtqueues. */
#define FHDR_LEN 2 static u8 *lg_features(const struct lguest_device_desc *desc)
{
return (void *)(lg_vq(desc) + desc->num_vq);
}
/* This finds the first field of a given type for a device's configuration. */ /* The config space comes after the two feature bitmasks. */
static void *lg_find(struct virtio_device *vdev, u8 type, unsigned int *len) static u8 *lg_config(const struct lguest_device_desc *desc)
{ {
struct lguest_device_desc *desc = to_lgdev(vdev)->desc; return lg_features(desc) + desc->feature_len * 2;
int i; }
for (i = 0; i < desc->config_len; i += FHDR_LEN + desc->config[i+1]) { /* The total size of the config page used by this device (incl. desc) */
if (desc->config[i] == type) { static unsigned desc_size(const struct lguest_device_desc *desc)
/* Mark it used, so Host can know we looked at it, and {
* also so we won't find the same one twice. */ return sizeof(*desc)
desc->config[i] |= 0x80; + desc->num_vq * sizeof(struct lguest_vqconfig)
/* Remember, the second byte is the length. */ + desc->feature_len * 2
*len = desc->config[i+1]; + desc->config_len;
/* We return a pointer to the field header. */ }
return desc->config + i;
}
}
/* Not found: return NULL for failure. */ /* This tests (and acknowleges) a feature bit. */
return NULL; static bool lg_feature(struct virtio_device *vdev, unsigned fbit)
{
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;
} }
/* Once they've found a field, getting a copy of it is easy. */ /* Once they've found a field, getting a copy of it is easy. */
static void lg_get(struct virtio_device *vdev, void *token, static void lg_get(struct virtio_device *vdev, unsigned int offset,
void *buf, unsigned len) void *buf, unsigned len)
{ {
/* Check they didn't ask for more than the length of the field! */ struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
BUG_ON(len > ((u8 *)token)[1]);
memcpy(buf, token + FHDR_LEN, len); /* Check they didn't ask for more than the length of the config! */
BUG_ON(offset + len > desc->config_len);
memcpy(buf, lg_config(desc) + offset, len);
} }
/* Setting the contents is also trivial. */ /* Setting the contents is also trivial. */
static void lg_set(struct virtio_device *vdev, void *token, static void lg_set(struct virtio_device *vdev, unsigned int offset,
const void *buf, unsigned len) const void *buf, unsigned len)
{ {
BUG_ON(len > ((u8 *)token)[1]); struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
memcpy(token + FHDR_LEN, buf, len);
/* Check they didn't ask for more than the length of the config! */
BUG_ON(offset + len > desc->config_len);
memcpy(lg_config(desc) + offset, buf, len);
} }
/* The operations to get and set the status word just access the status field /* The operations to get and set the status word just access the status field
...@@ -165,39 +190,29 @@ static void lg_notify(struct virtqueue *vq) ...@@ -165,39 +190,29 @@ static void lg_notify(struct virtqueue *vq)
* *
* So we provide devices with a "find virtqueue and set it up" function. */ * So we provide devices with a "find virtqueue and set it up" function. */
static struct virtqueue *lg_find_vq(struct virtio_device *vdev, static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
unsigned index,
bool (*callback)(struct virtqueue *vq)) bool (*callback)(struct virtqueue *vq))
{ {
struct lguest_device *ldev = to_lgdev(vdev);
struct lguest_vq_info *lvq; struct lguest_vq_info *lvq;
struct virtqueue *vq; struct virtqueue *vq;
unsigned int len;
void *token;
int err; int err;
/* Look for a field of the correct type to mark a virtqueue. Note that /* We must have this many virtqueues. */
* if this succeeds, then the type will be changed so it won't be found if (index >= ldev->desc->num_vq)
* again, and future lg_find_vq() calls will find the next
* virtqueue (if any). */
token = vdev->config->find(vdev, VIRTIO_CONFIG_F_VIRTQUEUE, &len);
if (!token)
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
lvq = kmalloc(sizeof(*lvq), GFP_KERNEL); lvq = kmalloc(sizeof(*lvq), GFP_KERNEL);
if (!lvq) if (!lvq)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
/* Note: we could use a configuration space inside here, just like we /* Make a copy of the "struct lguest_vqconfig" entry, which sits after
* do for the device. This would allow expansion in future, because * the descriptor. We need a copy because the config space might not
* our configuration system is designed to be expansible. But this is * be aligned correctly. */
* way easier. */ memcpy(&lvq->config, lg_vq(ldev->desc)+index, sizeof(lvq->config));
if (len != sizeof(lvq->config)) {
dev_err(&vdev->dev, "Unexpected virtio config len %u\n", len);
err = -EIO;
goto free_lvq;
}
/* Make a copy of the "struct lguest_vqconfig" field. We need a copy
* because the config space might not be aligned correctly. */
vdev->config->get(vdev, token, &lvq->config, sizeof(lvq->config));
printk("Mapping virtqueue %i addr %lx\n", index,
(unsigned long)lvq->config.pfn << PAGE_SHIFT);
/* Figure out how many pages the ring will take, and map that memory */ /* Figure out how many pages the ring will take, and map that memory */
lvq->pages = lguest_map((unsigned long)lvq->config.pfn << PAGE_SHIFT, lvq->pages = lguest_map((unsigned long)lvq->config.pfn << PAGE_SHIFT,
DIV_ROUND_UP(vring_size(lvq->config.num, DIV_ROUND_UP(vring_size(lvq->config.num,
...@@ -259,7 +274,7 @@ static void lg_del_vq(struct virtqueue *vq) ...@@ -259,7 +274,7 @@ static void lg_del_vq(struct virtqueue *vq)
/* The ops structure which hooks everything together. */ /* The ops structure which hooks everything together. */
static struct virtio_config_ops lguest_config_ops = { static struct virtio_config_ops lguest_config_ops = {
.find = lg_find, .feature = lg_feature,
.get = lg_get, .get = lg_get,
.set = lg_set, .set = lg_set,
.get_status = lg_get_status, .get_status = lg_get_status,
...@@ -329,13 +344,14 @@ static void scan_devices(void) ...@@ -329,13 +344,14 @@ static void scan_devices(void)
struct lguest_device_desc *d; struct lguest_device_desc *d;
/* We start at the page beginning, and skip over each entry. */ /* We start at the page beginning, and skip over each entry. */
for (i = 0; i < PAGE_SIZE; i += sizeof(*d) + d->config_len) { for (i = 0; i < PAGE_SIZE; i += desc_size(d)) {
d = lguest_devices + i; d = lguest_devices + i;
/* Once we hit a zero, stop. */ /* Once we hit a zero, stop. */
if (d->type == 0) if (d->type == 0)
break; break;
printk("Device at %i has size %u\n", i, desc_size(d));
add_lguest_device(d); add_lguest_device(d);
} }
} }
......
...@@ -311,10 +311,8 @@ static int virtnet_close(struct net_device *dev) ...@@ -311,10 +311,8 @@ static int virtnet_close(struct net_device *dev)
static int virtnet_probe(struct virtio_device *vdev) static int virtnet_probe(struct virtio_device *vdev)
{ {
int err; int err;
unsigned int len;
struct net_device *dev; struct net_device *dev;
struct virtnet_info *vi; struct virtnet_info *vi;
void *token;
/* Allocate ourselves a network device with room for our info */ /* Allocate ourselves a network device with room for our info */
dev = alloc_etherdev(sizeof(struct virtnet_info)); dev = alloc_etherdev(sizeof(struct virtnet_info));
...@@ -330,25 +328,24 @@ static int virtnet_probe(struct virtio_device *vdev) ...@@ -330,25 +328,24 @@ static int virtnet_probe(struct virtio_device *vdev)
SET_NETDEV_DEV(dev, &vdev->dev); SET_NETDEV_DEV(dev, &vdev->dev);
/* Do we support "hardware" checksums? */ /* Do we support "hardware" checksums? */
token = vdev->config->find(vdev, VIRTIO_CONFIG_NET_F, &len); if (vdev->config->feature(vdev, VIRTIO_NET_F_NO_CSUM)) {
if (virtio_use_bit(vdev, token, len, VIRTIO_NET_F_NO_CSUM)) {
/* This opens up the world of extra features. */ /* This opens up the world of extra features. */
dev->features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST; dev->features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST;
if (virtio_use_bit(vdev, token, len, VIRTIO_NET_F_TSO4)) if (vdev->config->feature(vdev, VIRTIO_NET_F_TSO4))
dev->features |= NETIF_F_TSO; dev->features |= NETIF_F_TSO;
if (virtio_use_bit(vdev, token, len, VIRTIO_NET_F_UFO)) if (vdev->config->feature(vdev, VIRTIO_NET_F_UFO))
dev->features |= NETIF_F_UFO; dev->features |= NETIF_F_UFO;
if (virtio_use_bit(vdev, token, len, VIRTIO_NET_F_TSO4_ECN)) if (vdev->config->feature(vdev, VIRTIO_NET_F_TSO4_ECN))
dev->features |= NETIF_F_TSO_ECN; dev->features |= NETIF_F_TSO_ECN;
if (virtio_use_bit(vdev, token, len, VIRTIO_NET_F_TSO6)) if (vdev->config->feature(vdev, VIRTIO_NET_F_TSO6))
dev->features |= NETIF_F_TSO6; dev->features |= NETIF_F_TSO6;
} }
/* Configuration may specify what MAC to use. Otherwise random. */ /* Configuration may specify what MAC to use. Otherwise random. */
token = vdev->config->find(vdev, VIRTIO_CONFIG_NET_MAC_F, &len); if (vdev->config->feature(vdev, VIRTIO_NET_F_MAC)) {
if (token) { vdev->config->get(vdev,
dev->addr_len = len; offsetof(struct virtio_net_config, mac),
vdev->config->get(vdev, token, dev->dev_addr, len); dev->dev_addr, dev->addr_len);
} else } else
random_ether_addr(dev->dev_addr); random_ether_addr(dev->dev_addr);
...@@ -359,13 +356,13 @@ static int virtnet_probe(struct virtio_device *vdev) ...@@ -359,13 +356,13 @@ static int virtnet_probe(struct virtio_device *vdev)
vi->vdev = vdev; vi->vdev = vdev;
/* We expect two virtqueues, receive then send. */ /* We expect two virtqueues, receive then send. */
vi->rvq = vdev->config->find_vq(vdev, skb_recv_done); vi->rvq = vdev->config->find_vq(vdev, 0, skb_recv_done);
if (IS_ERR(vi->rvq)) { if (IS_ERR(vi->rvq)) {
err = PTR_ERR(vi->rvq); err = PTR_ERR(vi->rvq);
goto free; goto free;
} }
vi->svq = vdev->config->find_vq(vdev, skb_xmit_done); vi->svq = vdev->config->find_vq(vdev, 1, skb_xmit_done);
if (IS_ERR(vi->svq)) { if (IS_ERR(vi->svq)) {
err = PTR_ERR(vi->svq); err = PTR_ERR(vi->svq);
goto free_recv; goto free_recv;
......
...@@ -148,51 +148,6 @@ void unregister_virtio_device(struct virtio_device *dev) ...@@ -148,51 +148,6 @@ void unregister_virtio_device(struct virtio_device *dev)
} }
EXPORT_SYMBOL_GPL(unregister_virtio_device); EXPORT_SYMBOL_GPL(unregister_virtio_device);
int __virtio_config_val(struct virtio_device *vdev,
u8 type, void *val, size_t size)
{
void *token;
unsigned int len;
token = vdev->config->find(vdev, type, &len);
if (!token)
return -ENOENT;
if (len != size)
return -EIO;
vdev->config->get(vdev, token, val, size);
return 0;
}
EXPORT_SYMBOL_GPL(__virtio_config_val);
int virtio_use_bit(struct virtio_device *vdev,
void *token, unsigned int len, unsigned int bitnum)
{
unsigned long bits[16];
/* This makes it convenient to pass-through find() results. */
if (!token)
return 0;
/* bit not in range of this bitfield? */
if (bitnum * 8 >= len / 2)
return 0;
/* Giant feature bitfields are silly. */
BUG_ON(len > sizeof(bits));
vdev->config->get(vdev, token, bits, len);
if (!test_bit(bitnum, bits))
return 0;
/* Set acknowledge bit, and write it back. */
set_bit(bitnum + len * 8 / 2, bits);
vdev->config->set(vdev, token, bits, len);
return 1;
}
EXPORT_SYMBOL_GPL(virtio_use_bit);
static int virtio_init(void) static int virtio_init(void)
{ {
if (bus_register(&virtio_bus) != 0) if (bus_register(&virtio_bus) != 0)
......
...@@ -23,7 +23,12 @@ ...@@ -23,7 +23,12 @@
struct lguest_device_desc { struct lguest_device_desc {
/* The device type: console, network, disk etc. Type 0 terminates. */ /* The device type: console, network, disk etc. Type 0 terminates. */
__u8 type; __u8 type;
/* The number of bytes of the config array. */ /* The number of virtqueues (first in config array) */
__u8 num_vq;
/* The number of bytes of feature bits. Multiply by 2: one for host
* features and one for guest acknowledgements. */
__u8 feature_len;
/* The number of bytes of the config array after virtqueues. */
__u8 config_len; __u8 config_len;
/* A status byte, written by the Guest. */ /* A status byte, written by the Guest. */
__u8 status; __u8 status;
...@@ -31,7 +36,7 @@ struct lguest_device_desc { ...@@ -31,7 +36,7 @@ struct lguest_device_desc {
}; };
/*D:135 This is how we expect the device configuration field for a virtqueue /*D:135 This is how we expect the device configuration field for a virtqueue
* (type VIRTIO_CONFIG_F_VIRTQUEUE) to be laid out: */ * to be laid out in config space. */
struct lguest_vqconfig { struct lguest_vqconfig {
/* The number of entries in the virtio_ring */ /* The number of entries in the virtio_ring */
__u16 num; __u16 num;
......
...@@ -6,15 +6,19 @@ ...@@ -6,15 +6,19 @@
#define VIRTIO_ID_BLOCK 2 #define VIRTIO_ID_BLOCK 2
/* Feature bits */ /* Feature bits */
#define VIRTIO_CONFIG_BLK_F 0x40 #define VIRTIO_BLK_F_BARRIER 0 /* Does host support barriers? */
#define VIRTIO_BLK_F_BARRIER 1 /* Does host support barriers? */ #define VIRTIO_BLK_F_SIZE_MAX 1 /* Indicates maximum segment size */
#define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */
/* The capacity (in 512-byte sectors). */
#define VIRTIO_CONFIG_BLK_F_CAPACITY 0x41 struct virtio_blk_config
/* The maximum segment size. */ {
#define VIRTIO_CONFIG_BLK_F_SIZE_MAX 0x42 /* The capacity (in 512-byte sectors). */
/* The maximum number of segments. */ __le64 capacity;
#define VIRTIO_CONFIG_BLK_F_SEG_MAX 0x43 /* The maximum segment size (if VIRTIO_BLK_F_SIZE_MAX) */
__le32 size_max;
/* The maximum number of segments (if VIRTIO_BLK_F_SEG_MAX) */
__le32 seg_max;
} __attribute__((packed));
/* These two define direction. */ /* These two define direction. */
#define VIRTIO_BLK_T_IN 0 #define VIRTIO_BLK_T_IN 0
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* store and access that space differently. */ * store and access that space differently. */
#include <linux/types.h> #include <linux/types.h>
/* Status byte for guest to report progress, and synchronize config. */ /* Status byte for guest to report progress, and synchronize features. */
/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ /* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */
#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 #define VIRTIO_CONFIG_S_ACKNOWLEDGE 1
/* We have found a driver for the device. */ /* We have found a driver for the device. */
...@@ -15,34 +15,27 @@ ...@@ -15,34 +15,27 @@
/* We've given up on this device. */ /* We've given up on this device. */
#define VIRTIO_CONFIG_S_FAILED 0x80 #define VIRTIO_CONFIG_S_FAILED 0x80
/* Feature byte (actually 7 bits availabe): */
/* Requirements/features of the virtio implementation. */
#define VIRTIO_CONFIG_F_VIRTIO 1
/* Requirements/features of the virtqueue (may have more than one). */
#define VIRTIO_CONFIG_F_VIRTQUEUE 2
#ifdef __KERNEL__ #ifdef __KERNEL__
struct virtio_device; struct virtio_device;
/** /**
* virtio_config_ops - operations for configuring a virtio device * virtio_config_ops - operations for configuring a virtio device
* @find: search for the next configuration field of the given type. * @feature: search for a feature in this config
* vdev: the virtio_device * vdev: the virtio_device
* type: the feature type * bit: the feature bit
* len: the (returned) length of the field if found. * Returns true if the feature is supported. Acknowledges the feature
* Returns a token if found, or NULL. Never returnes the same field twice * so the host can see it.
* (ie. it's used up). * @get: read the value of a configuration field
* @get: read the value of a configuration field after find().
* vdev: the virtio_device * vdev: the virtio_device
* token: the token returned from find(). * offset: the offset of the configuration field
* buf: the buffer to write the field value into. * buf: the buffer to write the field value into.
* len: the length of the buffer (given by find()). * len: the length of the buffer
* Note that contents are conventionally little-endian. * Note that contents are conventionally little-endian.
* @set: write the value of a configuration field after find(). * @set: write the value of a configuration field
* vdev: the virtio_device * vdev: the virtio_device
* token: the token returned from find(). * offset: the offset of the configuration field
* buf: the buffer to read the field value from. * buf: the buffer to read the field value from.
* len: the length of the buffer (given by find()). * len: the length of the buffer
* Note that contents are conventionally little-endian. * Note that contents are conventionally little-endian.
* @get_status: read the status byte * @get_status: read the status byte
* vdev: the virtio_device * vdev: the virtio_device
...@@ -50,62 +43,63 @@ struct virtio_device; ...@@ -50,62 +43,63 @@ struct virtio_device;
* @set_status: write the status byte * @set_status: write the status byte
* vdev: the virtio_device * vdev: the virtio_device
* status: the new status byte * status: the new status byte
* @find_vq: find the first VIRTIO_CONFIG_F_VIRTQUEUE and create a virtqueue. * @find_vq: find a virtqueue and instantiate it.
* vdev: the virtio_device * vdev: the virtio_device
* index: the 0-based virtqueue number in case there's more than one.
* callback: the virqtueue callback * callback: the virqtueue callback
* Returns the new virtqueue or ERR_PTR(). * Returns the new virtqueue or ERR_PTR() (eg. -ENOENT).
* @del_vq: free a virtqueue found by find_vq(). * @del_vq: free a virtqueue found by find_vq().
*/ */
struct virtio_config_ops struct virtio_config_ops
{ {
void *(*find)(struct virtio_device *vdev, u8 type, unsigned *len); bool (*feature)(struct virtio_device *vdev, unsigned bit);
void (*get)(struct virtio_device *vdev, void *token, void (*get)(struct virtio_device *vdev, unsigned offset,
void *buf, unsigned len); void *buf, unsigned len);
void (*set)(struct virtio_device *vdev, void *token, void (*set)(struct virtio_device *vdev, unsigned offset,
const void *buf, unsigned len); const void *buf, unsigned len);
u8 (*get_status)(struct virtio_device *vdev); u8 (*get_status)(struct virtio_device *vdev);
void (*set_status)(struct virtio_device *vdev, u8 status); void (*set_status)(struct virtio_device *vdev, u8 status);
struct virtqueue *(*find_vq)(struct virtio_device *vdev, struct virtqueue *(*find_vq)(struct virtio_device *vdev,
unsigned index,
bool (*callback)(struct virtqueue *)); bool (*callback)(struct virtqueue *));
void (*del_vq)(struct virtqueue *vq); void (*del_vq)(struct virtqueue *vq);
}; };
/** /**
* virtio_config_val - get a single virtio config and mark it used. * virtio_config_val - look for a feature and get a single virtio config.
* @config: the virtio config space * @vdev: the virtio device
* @type: the type to search for. * @fbit: the feature bit
* @offset: the type to search for.
* @val: a pointer to the value to fill in.
*
* The return value is -ENOENT if the feature doesn't exist. Otherwise
* the value is endian-corrected and returned in v. */
#define virtio_config_val(vdev, fbit, offset, v) ({ \
int _err; \
if ((vdev)->config->feature((vdev), (fbit))) { \
__virtio_config_val((vdev), (offset), (v)); \
_err = 0; \
} else \
_err = -ENOENT; \
_err; \
})
/**
* __virtio_config_val - get a single virtio config without feature check.
* @vdev: the virtio device
* @offset: the type to search for.
* @val: a pointer to the value to fill in. * @val: a pointer to the value to fill in.
* *
* Once used, the config type is marked with VIRTIO_CONFIG_F_USED so it can't * The value is endian-corrected and returned in v. */
* be found again. This version does endian conversion. */ #define __virtio_config_val(vdev, offset, v) do { \
#define virtio_config_val(vdev, type, v) ({ \
int _err = __virtio_config_val((vdev),(type),(v),sizeof(*(v))); \
\
BUILD_BUG_ON(sizeof(*(v)) != 1 && sizeof(*(v)) != 2 \ BUILD_BUG_ON(sizeof(*(v)) != 1 && sizeof(*(v)) != 2 \
&& sizeof(*(v)) != 4 && sizeof(*(v)) != 8); \ && sizeof(*(v)) != 4 && sizeof(*(v)) != 8); \
if (!_err) { \ (vdev)->config->get((vdev), (offset), (v), sizeof(*(v))); \
switch (sizeof(*(v))) { \ switch (sizeof(*(v))) { \
case 2: le16_to_cpus((__u16 *) v); break; \ case 2: le16_to_cpus((__u16 *) v); break; \
case 4: le32_to_cpus((__u32 *) v); break; \ case 4: le32_to_cpus((__u32 *) v); break; \
case 8: le64_to_cpus((__u64 *) v); break; \ case 8: le64_to_cpus((__u64 *) v); break; \
} \ } \
} \ } while(0)
_err; \
})
int __virtio_config_val(struct virtio_device *dev,
u8 type, void *val, size_t size);
/**
* virtio_use_bit - helper to use a feature bit in a bitfield value.
* @dev: the virtio device
* @token: the token as returned from vdev->config->find().
* @len: the length of the field.
* @bitnum: the bit to test.
*
* If handed a NULL token, it returns false, otherwise returns bit status.
* If it's one, it sets the mirroring acknowledgement bit. */
int virtio_use_bit(struct virtio_device *vdev,
void *token, unsigned int len, unsigned int bitnum);
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _LINUX_VIRTIO_CONFIG_H */ #endif /* _LINUX_VIRTIO_CONFIG_H */
...@@ -5,16 +5,19 @@ ...@@ -5,16 +5,19 @@
/* The ID for virtio_net */ /* The ID for virtio_net */
#define VIRTIO_ID_NET 1 #define VIRTIO_ID_NET 1
/* The bitmap of config for virtio net */ /* The feature bitmap for virtio net */
#define VIRTIO_CONFIG_NET_F 0x40
#define VIRTIO_NET_F_NO_CSUM 0 #define VIRTIO_NET_F_NO_CSUM 0
#define VIRTIO_NET_F_TSO4 1 #define VIRTIO_NET_F_TSO4 1
#define VIRTIO_NET_F_UFO 2 #define VIRTIO_NET_F_UFO 2
#define VIRTIO_NET_F_TSO4_ECN 3 #define VIRTIO_NET_F_TSO4_ECN 3
#define VIRTIO_NET_F_TSO6 4 #define VIRTIO_NET_F_TSO6 4
#define VIRTIO_NET_F_MAC 5
/* The config defining mac address. */ struct virtio_net_config
#define VIRTIO_CONFIG_NET_MAC_F 0x41 {
/* The config defining mac address (if VIRTIO_NET_F_MAC) */
__u8 mac[6];
} __attribute__((packed));
/* This is the first element of the scatter-gather list. If you don't /* This is the first element of the scatter-gather list. If you don't
* specify GSO or CSUM features, you can simply ignore the header. */ * specify GSO or CSUM features, you can simply ignore the header. */
......
...@@ -236,13 +236,13 @@ static int p9_virtio_probe(struct virtio_device *dev) ...@@ -236,13 +236,13 @@ static int p9_virtio_probe(struct virtio_device *dev)
/* Find the input queue. */ /* Find the input queue. */
dev->priv = chan; dev->priv = chan;
chan->in_vq = dev->config->find_vq(dev, p9_virtio_intr); chan->in_vq = dev->config->find_vq(dev, 0, p9_virtio_intr);
if (IS_ERR(chan->in_vq)) { if (IS_ERR(chan->in_vq)) {
err = PTR_ERR(chan->in_vq); err = PTR_ERR(chan->in_vq);
goto free; goto free;
} }
chan->out_vq = dev->config->find_vq(dev, NULL); chan->out_vq = dev->config->find_vq(dev, 1, NULL);
if (IS_ERR(chan->out_vq)) { if (IS_ERR(chan->out_vq)) {
err = PTR_ERR(chan->out_vq); err = PTR_ERR(chan->out_vq);
goto free_in_vq; goto free_in_vq;
......
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