Skip to content

Commit

Permalink
virtio: simplify config mechanism.
Browse files Browse the repository at this point in the history
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: Rusty Russell <[email protected]>
  • Loading branch information
rustyrussell committed Feb 4, 2008
1 parent f35d9d8 commit a586d4f
Show file tree
Hide file tree
Showing 11 changed files with 280 additions and 281 deletions.
176 changes: 105 additions & 71 deletions Documentation/lguest/lguest.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
#include <zlib.h>
#include <assert.h>
#include <sched.h>
#include <limits.h>
#include <stddef.h>
#include "linux/lguest_launcher.h"
#include "linux/virtio_config.h"
#include "linux/virtio_net.h"
Expand Down Expand Up @@ -99,13 +101,11 @@ struct device_list
/* The descriptor page for the devices. */
u8 *descpage;

/* The tail of the last descriptor. */
unsigned int desc_used;

/* A single linked list of devices. */
struct device *dev;
/* ... And an end pointer so we can easily append new devices */
struct device **lastdev;
/* And a pointer to the last device for easy append and also for
* configuration appending. */
struct device *lastdev;
};

/* The list of Guest devices, based on command line arguments. */
Expand Down Expand Up @@ -191,7 +191,7 @@ static void *_convert(struct iovec *iov, size_t size, size_t align,
#define cpu_to_le64(v64) (v64)
#define le16_to_cpu(v16) (v16)
#define le32_to_cpu(v32) (v32)
#define le64_to_cpu(v32) (v64)
#define le64_to_cpu(v64) (v64)

/*L:100 The Launcher code itself takes us out into userspace, that scary place
* where pointers run wild and free! Unfortunately, like most userspace
Expand Down Expand Up @@ -986,54 +986,44 @@ static void handle_input(int fd)
*
* All devices need a descriptor so the Guest knows it exists, and a "struct
* device" so the Launcher can keep track of it. We have common helper
* routines to allocate them.
*
* This routine allocates a new "struct lguest_device_desc" from descriptor
* table just above the Guest's normal memory. It returns a pointer to that
* descriptor. */
static struct lguest_device_desc *new_dev_desc(u16 type)
{
struct lguest_device_desc *d;
* routines to allocate and manage them. */

/* We only have one page for all the descriptors. */
if (devices.desc_used + sizeof(*d) > getpagesize())
errx(1, "Too many devices");

/* We don't need to set config_len or status: page is 0 already. */
d = (void *)devices.descpage + devices.desc_used;
d->type = type;
devices.desc_used += sizeof(*d);

return d;
/* The layout of the device page is a "struct lguest_device_desc" followed by a
* number of virtqueue descriptors, then two sets of feature bits, then an
* array of configuration bytes. This routine returns the configuration
* pointer. */
static u8 *device_config(const struct device *dev)
{
return (void *)(dev->desc + 1)
+ dev->desc->num_vq * sizeof(struct lguest_vqconfig)
+ dev->desc->feature_len * 2;
}

/* Each device descriptor is followed by some configuration information.
* Each configuration field looks like: u8 type, u8 len, [... len bytes...].
*
* This routine adds a new field to an existing device's descriptor. It only
* works for the last device, but that's OK because that's how we use it. */
static void add_desc_field(struct device *dev, u8 type, u8 len, const void *c)
/* This routine allocates a new "struct lguest_device_desc" from descriptor
* table page just above the Guest's normal memory. It returns a pointer to
* that descriptor. */
static struct lguest_device_desc *new_dev_desc(u16 type)
{
/* This is the last descriptor, right? */
assert(devices.descpage + devices.desc_used
== (u8 *)(dev->desc + 1) + dev->desc->config_len);
struct lguest_device_desc d = { .type = type };
void *p;

/* We only have one page of device descriptions. */
if (devices.desc_used + 2 + len > getpagesize())
errx(1, "Too many devices");
/* Figure out where the next device config is, based on the last one. */
if (devices.lastdev)
p = device_config(devices.lastdev)
+ devices.lastdev->desc->config_len;
else
p = devices.descpage;

/* Copy in the new config header: type then length. */
devices.descpage[devices.desc_used++] = type;
devices.descpage[devices.desc_used++] = len;
memcpy(devices.descpage + devices.desc_used, c, len);
devices.desc_used += len;
/* We only have one page for all the descriptors. */
if (p + sizeof(d) > (void *)devices.descpage + getpagesize())
errx(1, "Too many devices");

/* Update the device descriptor length: two byte head then data. */
dev->desc->config_len += 2 + len;
/* p might not be aligned, so we memcpy in. */
return memcpy(p, &d, sizeof(d));
}

/* This routine adds a virtqueue to a device. We specify how many descriptors
* the virtqueue is to have. */
/* Each device descriptor is followed by the description of its virtqueues. We
* specify how many descriptors the virtqueue is to have. */
static void add_virtqueue(struct device *dev, unsigned int num_descs,
void (*handle_output)(int fd, struct virtqueue *me))
{
Expand All @@ -1059,9 +1049,15 @@ static void add_virtqueue(struct device *dev, unsigned int num_descs,
/* Initialize the vring. */
vring_init(&vq->vring, num_descs, p, getpagesize());

/* Add the configuration information to this device's descriptor. */
add_desc_field(dev, VIRTIO_CONFIG_F_VIRTQUEUE,
sizeof(vq->config), &vq->config);
/* Append virtqueue to this device's descriptor. We use
* device_config() to get the end of the device's current virtqueues;
* we check that we haven't added any config or feature information
* yet, otherwise we'd be overwriting them. */
assert(dev->desc->config_len == 0 && dev->desc->feature_len == 0);
memcpy(device_config(dev), &vq->config, sizeof(vq->config));
dev->desc->num_vq++;

verbose("Virtqueue page %#lx\n", to_guest_phys(p));

/* Add to tail of list, so dev->vq is first vq, dev->vq->next is
* second. */
Expand All @@ -1077,21 +1073,44 @@ static void add_virtqueue(struct device *dev, unsigned int num_descs,
vq->vring.used->flags = VRING_USED_F_NO_NOTIFY;
}

/* The virtqueue descriptors are followed by feature bytes. */
static void add_feature(struct device *dev, unsigned bit)
{
u8 *features;

/* We can't extend the feature bits once we've added config bytes */
if (dev->desc->feature_len <= bit / CHAR_BIT) {
assert(dev->desc->config_len == 0);
dev->desc->feature_len = (bit / CHAR_BIT) + 1;
}

features = (u8 *)(dev->desc + 1)
+ dev->desc->num_vq * sizeof(struct lguest_vqconfig);

features[bit / CHAR_BIT] |= (1 << (bit % CHAR_BIT));
}

/* This routine sets the configuration fields for an existing device's
* descriptor. It only works for the last device, but that's OK because that's
* how we use it. */
static void set_config(struct device *dev, unsigned len, const void *conf)
{
/* Check we haven't overflowed our single page. */
if (device_config(dev) + len > devices.descpage + getpagesize())
errx(1, "Too many devices");

/* Copy in the config information, and store the length. */
memcpy(device_config(dev), conf, len);
dev->desc->config_len = len;
}

/* This routine does all the creation and setup of a new device, including
* calling new_dev_desc() to allocate the descriptor and device memory. */
static struct device *new_device(const char *name, u16 type, int fd,
bool (*handle_input)(int, struct device *))
{
struct device *dev = malloc(sizeof(*dev));

/* Append to device list. Prepending to a single-linked list is
* easier, but the user expects the devices to be arranged on the bus
* in command-line order. The first network device on the command line
* is eth0, the first block device /dev/vda, etc. */
*devices.lastdev = dev;
dev->next = NULL;
devices.lastdev = &dev->next;

/* Now we populate the fields one at a time. */
dev->fd = fd;
/* If we have an input handler for this file descriptor, then we add it
Expand All @@ -1102,6 +1121,17 @@ static struct device *new_device(const char *name, u16 type, int fd,
dev->handle_input = handle_input;
dev->name = name;
dev->vq = NULL;

/* Append to device list. Prepending to a single-linked list is
* easier, but the user expects the devices to be arranged on the bus
* in command-line order. The first network device on the command line
* is eth0, the first block device /dev/vda, etc. */
if (devices.lastdev)
devices.lastdev->next = dev;
else
devices.dev = dev;
devices.lastdev = dev;

return dev;
}

Expand Down Expand Up @@ -1226,7 +1256,7 @@ static void setup_tun_net(const char *arg)
int netfd, ipfd;
u32 ip;
const char *br_name = NULL;
u8 hwaddr[6];
struct virtio_net_config conf;

/* We open the /dev/net/tun device and tell it we want a tap device. A
* tap device is like a tun device, only somehow different. To tell
Expand Down Expand Up @@ -1265,12 +1295,13 @@ static void setup_tun_net(const char *arg)
ip = str2ip(arg);

/* Set up the tun device, and get the mac address for the interface. */
configure_device(ipfd, ifr.ifr_name, ip, hwaddr);
configure_device(ipfd, ifr.ifr_name, ip, conf.mac);

/* Tell Guest what MAC address to use. */
add_desc_field(dev, VIRTIO_CONFIG_NET_MAC_F, sizeof(hwaddr), hwaddr);
add_feature(dev, VIRTIO_NET_F_MAC);
set_config(dev, sizeof(conf), &conf);

/* We don't seed the socket any more; setup is done. */
/* We don't need the socket any more; setup is done. */
close(ipfd);

verbose("device %u: tun net %u.%u.%u.%u\n",
Expand Down Expand Up @@ -1458,8 +1489,7 @@ static void setup_block_file(const char *filename)
struct device *dev;
struct vblk_info *vblk;
void *stack;
u64 cap;
unsigned int val;
struct virtio_blk_config conf;

/* This is the pipe the I/O thread will use to tell us I/O is done. */
pipe(p);
Expand All @@ -1477,14 +1507,18 @@ static void setup_block_file(const char *filename)
vblk->fd = open_or_die(filename, O_RDWR|O_LARGEFILE);
vblk->len = lseek64(vblk->fd, 0, SEEK_END);

/* We support barriers. */
add_feature(dev, VIRTIO_BLK_F_BARRIER);

/* Tell Guest how many sectors this device has. */
cap = cpu_to_le64(vblk->len / 512);
add_desc_field(dev, VIRTIO_CONFIG_BLK_F_CAPACITY, sizeof(cap), &cap);
conf.capacity = cpu_to_le64(vblk->len / 512);

/* Tell Guest not to put in too many descriptors at once: two are used
* for the in and out elements. */
val = cpu_to_le32(VIRTQUEUE_NUM - 2);
add_desc_field(dev, VIRTIO_CONFIG_BLK_F_SEG_MAX, sizeof(val), &val);
add_feature(dev, VIRTIO_BLK_F_SEG_MAX);
conf.seg_max = cpu_to_le32(VIRTQUEUE_NUM - 2);

set_config(dev, sizeof(conf), &conf);

/* The I/O thread writes to this end of the pipe when done. */
vblk->done_fd = p[1];
Expand All @@ -1505,7 +1539,7 @@ static void setup_block_file(const char *filename)
close(vblk->workpipe[0]);

verbose("device %u: virtblock %llu sectors\n",
devices.device_num, cap);
devices.device_num, le64_to_cpu(conf.capacity));
}
/* That's the end of device setup. :*/

Expand Down Expand Up @@ -1610,12 +1644,12 @@ int main(int argc, char *argv[])
/* First we initialize the device list. Since console and network
* device receive input from a file descriptor, we keep an fdset
* (infds) and the maximum fd number (max_infd) with the head of the
* list. We also keep a pointer to the last device, for easy appending
* to the list. Finally, we keep the next interrupt number to hand out
* (1: remember that 0 is used by the timer). */
* list. We also keep a pointer to the last device. Finally, we keep
* the next interrupt number to hand out (1: remember that 0 is used by
* the timer). */
FD_ZERO(&devices.infds);
devices.max_infd = -1;
devices.lastdev = &devices.dev;
devices.lastdev = NULL;
devices.next_irq = 1;

cpu_id = 0;
Expand Down
35 changes: 13 additions & 22 deletions drivers/block/virtio_blk.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,6 @@ static int virtblk_probe(struct virtio_device *vdev)
{
struct virtio_blk *vblk;
int err, major;
void *token;
unsigned int len;
u64 cap;
u32 v;

Expand All @@ -178,7 +176,7 @@ static int virtblk_probe(struct virtio_device *vdev)
vblk->vdev = vdev;

/* 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)) {
err = PTR_ERR(vblk->vq);
goto out_free_vblk;
Expand Down Expand Up @@ -216,15 +214,12 @@ static int virtblk_probe(struct virtio_device *vdev)
vblk->disk->fops = &virtblk_fops;

/* If barriers are supported, tell block layer that queue is ordered */
token = vdev->config->find(vdev, VIRTIO_CONFIG_BLK_F, &len);
if (virtio_use_bit(vdev, token, len, VIRTIO_BLK_F_BARRIER))
if (vdev->config->feature(vdev, VIRTIO_BLK_F_BARRIER))
blk_queue_ordered(vblk->disk->queue, QUEUE_ORDERED_TAG, NULL);

err = virtio_config_val(vdev, VIRTIO_CONFIG_BLK_F_CAPACITY, &cap);
if (err) {
dev_err(&vdev->dev, "Bad/missing capacity in config\n");
goto out_cleanup_queue;
}
/* Host must always specify the capacity. */
__virtio_config_val(vdev, offsetof(struct virtio_blk_config, capacity),
&cap);

/* If capacity is too big, truncate with warning. */
if ((sector_t)cap != cap) {
Expand All @@ -234,27 +229,23 @@ static int virtblk_probe(struct virtio_device *vdev)
}
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)
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)
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);
return 0;

out_cleanup_queue:
blk_cleanup_queue(vblk->disk->queue);
out_put_disk:
put_disk(vblk->disk);
out_unregister_blkdev:
Expand Down
4 changes: 2 additions & 2 deletions drivers/char/virtio_console.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,13 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
/* Find the input queue. */
/* FIXME: This is why we want to wean off hvc: we do nothing
* 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)) {
err = PTR_ERR(in_vq);
goto free;
}

out_vq = vdev->config->find_vq(vdev, NULL);
out_vq = vdev->config->find_vq(vdev, 1, NULL);
if (IS_ERR(out_vq)) {
err = PTR_ERR(out_vq);
goto free_in_vq;
Expand Down
Loading

0 comments on commit a586d4f

Please sign in to comment.