mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-22 14:41:27 +00:00
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: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
f35d9d8aae
commit
a586d4f601
11 changed files with 278 additions and 279 deletions
|
@ -34,6 +34,8 @@
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stddef.h>
|
||||||
#include "linux/lguest_launcher.h"
|
#include "linux/lguest_launcher.h"
|
||||||
#include "linux/virtio_config.h"
|
#include "linux/virtio_config.h"
|
||||||
#include "linux/virtio_net.h"
|
#include "linux/virtio_net.h"
|
||||||
|
@ -99,13 +101,11 @@ struct device_list
|
||||||
/* The descriptor page for the devices. */
|
/* The descriptor page for the devices. */
|
||||||
u8 *descpage;
|
u8 *descpage;
|
||||||
|
|
||||||
/* The tail of the last descriptor. */
|
|
||||||
unsigned int desc_used;
|
|
||||||
|
|
||||||
/* A single linked list of devices. */
|
/* A single linked list of devices. */
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
/* ... And an end pointer so we can easily append new devices */
|
/* And a pointer to the last device for easy append and also for
|
||||||
struct device **lastdev;
|
* configuration appending. */
|
||||||
|
struct device *lastdev;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* The list of Guest devices, based on command line arguments. */
|
/* The list of Guest devices, based on command line arguments. */
|
||||||
|
@ -191,7 +191,7 @@ static void *_convert(struct iovec *iov, size_t size, size_t align,
|
||||||
#define cpu_to_le64(v64) (v64)
|
#define cpu_to_le64(v64) (v64)
|
||||||
#define le16_to_cpu(v16) (v16)
|
#define le16_to_cpu(v16) (v16)
|
||||||
#define le32_to_cpu(v32) (v32)
|
#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
|
/*L:100 The Launcher code itself takes us out into userspace, that scary place
|
||||||
* where pointers run wild and free! Unfortunately, like most userspace
|
* where pointers run wild and free! Unfortunately, like most userspace
|
||||||
|
@ -986,54 +986,44 @@ static void handle_input(int fd)
|
||||||
*
|
*
|
||||||
* All devices need a descriptor so the Guest knows it exists, and a "struct
|
* 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
|
* device" so the Launcher can keep track of it. We have common helper
|
||||||
* routines to allocate them.
|
* routines to allocate and manage them. */
|
||||||
*
|
|
||||||
* This routine allocates a new "struct lguest_device_desc" from descriptor
|
/* The layout of the device page is a "struct lguest_device_desc" followed by a
|
||||||
* table just above the Guest's normal memory. It returns a pointer to that
|
* number of virtqueue descriptors, then two sets of feature bits, then an
|
||||||
* descriptor. */
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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)
|
static struct lguest_device_desc *new_dev_desc(u16 type)
|
||||||
{
|
{
|
||||||
struct lguest_device_desc *d;
|
struct lguest_device_desc d = { .type = type };
|
||||||
|
void *p;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
/* We only have one page for all the descriptors. */
|
/* We only have one page for all the descriptors. */
|
||||||
if (devices.desc_used + sizeof(*d) > getpagesize())
|
if (p + sizeof(d) > (void *)devices.descpage + getpagesize())
|
||||||
errx(1, "Too many devices");
|
errx(1, "Too many devices");
|
||||||
|
|
||||||
/* We don't need to set config_len or status: page is 0 already. */
|
/* p might not be aligned, so we memcpy in. */
|
||||||
d = (void *)devices.descpage + devices.desc_used;
|
return memcpy(p, &d, sizeof(d));
|
||||||
d->type = type;
|
|
||||||
devices.desc_used += sizeof(*d);
|
|
||||||
|
|
||||||
return d;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Each device descriptor is followed by some configuration information.
|
/* Each device descriptor is followed by the description of its virtqueues. We
|
||||||
* Each configuration field looks like: u8 type, u8 len, [... len bytes...].
|
* specify how many descriptors the virtqueue is to have. */
|
||||||
*
|
|
||||||
* 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 is the last descriptor, right? */
|
|
||||||
assert(devices.descpage + devices.desc_used
|
|
||||||
== (u8 *)(dev->desc + 1) + dev->desc->config_len);
|
|
||||||
|
|
||||||
/* We only have one page of device descriptions. */
|
|
||||||
if (devices.desc_used + 2 + len > getpagesize())
|
|
||||||
errx(1, "Too many devices");
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
/* Update the device descriptor length: two byte head then data. */
|
|
||||||
dev->desc->config_len += 2 + len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This routine adds a virtqueue to a device. We specify how many descriptors
|
|
||||||
* the virtqueue is to have. */
|
|
||||||
static void add_virtqueue(struct device *dev, unsigned int num_descs,
|
static void add_virtqueue(struct device *dev, unsigned int num_descs,
|
||||||
void (*handle_output)(int fd, struct virtqueue *me))
|
void (*handle_output)(int fd, struct virtqueue *me))
|
||||||
{
|
{
|
||||||
|
@ -1059,9 +1049,15 @@ static void add_virtqueue(struct device *dev, unsigned int num_descs,
|
||||||
/* Initialize the vring. */
|
/* Initialize the vring. */
|
||||||
vring_init(&vq->vring, num_descs, p, getpagesize());
|
vring_init(&vq->vring, num_descs, p, getpagesize());
|
||||||
|
|
||||||
/* Add the configuration information to this device's descriptor. */
|
/* Append virtqueue to this device's descriptor. We use
|
||||||
add_desc_field(dev, VIRTIO_CONFIG_F_VIRTQUEUE,
|
* device_config() to get the end of the device's current virtqueues;
|
||||||
sizeof(vq->config), &vq->config);
|
* 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
|
/* Add to tail of list, so dev->vq is first vq, dev->vq->next is
|
||||||
* second. */
|
* second. */
|
||||||
|
@ -1077,6 +1073,37 @@ static void add_virtqueue(struct device *dev, unsigned int num_descs,
|
||||||
vq->vring.used->flags = VRING_USED_F_NO_NOTIFY;
|
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
|
/* This routine does all the creation and setup of a new device, including
|
||||||
* calling new_dev_desc() to allocate the descriptor and device memory. */
|
* calling new_dev_desc() to allocate the descriptor and device memory. */
|
||||||
static struct device *new_device(const char *name, u16 type, int fd,
|
static struct device *new_device(const char *name, u16 type, int fd,
|
||||||
|
@ -1084,14 +1111,6 @@ static struct device *new_device(const char *name, u16 type, int fd,
|
||||||
{
|
{
|
||||||
struct device *dev = malloc(sizeof(*dev));
|
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. */
|
/* Now we populate the fields one at a time. */
|
||||||
dev->fd = fd;
|
dev->fd = fd;
|
||||||
/* If we have an input handler for this file descriptor, then we add it
|
/* If we have an input handler for this file descriptor, then we add it
|
||||||
|
@ -1102,6 +1121,17 @@ static struct device *new_device(const char *name, u16 type, int fd,
|
||||||
dev->handle_input = handle_input;
|
dev->handle_input = handle_input;
|
||||||
dev->name = name;
|
dev->name = name;
|
||||||
dev->vq = NULL;
|
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;
|
return dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1226,7 +1256,7 @@ static void setup_tun_net(const char *arg)
|
||||||
int netfd, ipfd;
|
int netfd, ipfd;
|
||||||
u32 ip;
|
u32 ip;
|
||||||
const char *br_name = NULL;
|
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
|
/* 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
|
* tap device is like a tun device, only somehow different. To tell
|
||||||
|
@ -1265,12 +1295,13 @@ static void setup_tun_net(const char *arg)
|
||||||
ip = str2ip(arg);
|
ip = str2ip(arg);
|
||||||
|
|
||||||
/* Set up the tun device, and get the mac address for the interface. */
|
/* 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. */
|
/* 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);
|
close(ipfd);
|
||||||
|
|
||||||
verbose("device %u: tun net %u.%u.%u.%u\n",
|
verbose("device %u: tun net %u.%u.%u.%u\n",
|
||||||
|
@ -1458,8 +1489,7 @@ static void setup_block_file(const char *filename)
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct vblk_info *vblk;
|
struct vblk_info *vblk;
|
||||||
void *stack;
|
void *stack;
|
||||||
u64 cap;
|
struct virtio_blk_config conf;
|
||||||
unsigned int val;
|
|
||||||
|
|
||||||
/* This is the pipe the I/O thread will use to tell us I/O is done. */
|
/* This is the pipe the I/O thread will use to tell us I/O is done. */
|
||||||
pipe(p);
|
pipe(p);
|
||||||
|
@ -1477,14 +1507,18 @@ static void setup_block_file(const char *filename)
|
||||||
vblk->fd = open_or_die(filename, O_RDWR|O_LARGEFILE);
|
vblk->fd = open_or_die(filename, O_RDWR|O_LARGEFILE);
|
||||||
vblk->len = lseek64(vblk->fd, 0, SEEK_END);
|
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. */
|
/* Tell Guest how many sectors this device has. */
|
||||||
cap = cpu_to_le64(vblk->len / 512);
|
conf.capacity = cpu_to_le64(vblk->len / 512);
|
||||||
add_desc_field(dev, VIRTIO_CONFIG_BLK_F_CAPACITY, sizeof(cap), &cap);
|
|
||||||
|
|
||||||
/* Tell Guest not to put in too many descriptors at once: two are used
|
/* Tell Guest not to put in too many descriptors at once: two are used
|
||||||
* for the in and out elements. */
|
* for the in and out elements. */
|
||||||
val = cpu_to_le32(VIRTQUEUE_NUM - 2);
|
add_feature(dev, VIRTIO_BLK_F_SEG_MAX);
|
||||||
add_desc_field(dev, VIRTIO_CONFIG_BLK_F_SEG_MAX, sizeof(val), &val);
|
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. */
|
/* The I/O thread writes to this end of the pipe when done. */
|
||||||
vblk->done_fd = p[1];
|
vblk->done_fd = p[1];
|
||||||
|
@ -1505,7 +1539,7 @@ static void setup_block_file(const char *filename)
|
||||||
close(vblk->workpipe[0]);
|
close(vblk->workpipe[0]);
|
||||||
|
|
||||||
verbose("device %u: virtblock %llu sectors\n",
|
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. :*/
|
/* That's the end of device setup. :*/
|
||||||
|
|
||||||
|
@ -1610,12 +1644,12 @@ int main(int argc, char *argv[])
|
||||||
/* First we initialize the device list. Since console and network
|
/* First we initialize the device list. Since console and network
|
||||||
* device receive input from a file descriptor, we keep an fdset
|
* device receive input from a file descriptor, we keep an fdset
|
||||||
* (infds) and the maximum fd number (max_infd) with the head of the
|
* (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
|
* list. We also keep a pointer to the last device. Finally, we keep
|
||||||
* to the list. Finally, we keep the next interrupt number to hand out
|
* the next interrupt number to hand out (1: remember that 0 is used by
|
||||||
* (1: remember that 0 is used by the timer). */
|
* the timer). */
|
||||||
FD_ZERO(&devices.infds);
|
FD_ZERO(&devices.infds);
|
||||||
devices.max_infd = -1;
|
devices.max_infd = -1;
|
||||||
devices.lastdev = &devices.dev;
|
devices.lastdev = NULL;
|
||||||
devices.next_irq = 1;
|
devices.next_irq = 1;
|
||||||
|
|
||||||
cpu_id = 0;
|
cpu_id = 0;
|
||||||
|
|
|
@ -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)
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
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)
|
||||||
/* 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 {
|
||||||
/*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)
|
||||||
|
{
|
||||||
|
return lg_features(desc) + desc->feature_len * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The total size of the config page used by this device (incl. desc) */
|
||||||
|
static unsigned desc_size(const struct lguest_device_desc *desc)
|
||||||
|
{
|
||||||
|
return sizeof(*desc)
|
||||||
|
+ desc->num_vq * sizeof(struct lguest_vqconfig)
|
||||||
|
+ desc->feature_len * 2
|
||||||
|
+ desc->config_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This tests (and acknowleges) a feature bit. */
|
||||||
|
static bool lg_feature(struct virtio_device *vdev, unsigned fbit)
|
||||||
{
|
{
|
||||||
struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
|
struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
|
||||||
int i;
|
u8 *features;
|
||||||
|
|
||||||
for (i = 0; i < desc->config_len; i += FHDR_LEN + desc->config[i+1]) {
|
/* Obviously if they ask for a feature off the end of our feature
|
||||||
if (desc->config[i] == type) {
|
* bitmap, it's not set. */
|
||||||
/* Mark it used, so Host can know we looked at it, and
|
if (fbit / 8 > desc->feature_len)
|
||||||
* also so we won't find the same one twice. */
|
return false;
|
||||||
desc->config[i] |= 0x80;
|
|
||||||
/* Remember, the second byte is the length. */
|
|
||||||
*len = desc->config[i+1];
|
|
||||||
/* We return a pointer to the field header. */
|
|
||||||
return desc->config + i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Not found: return NULL for failure. */
|
/* The feature bitmap comes after the virtqueues. */
|
||||||
return NULL;
|
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)
|
||||||
*
|
*
|
||||||
* 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)
|
||||||
|
|
||||||
/* 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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
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 @@
|
||||||
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 {
|
||||||
};
|
};
|
||||||
|
|
||||||
/*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 @@
|
||||||
#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). */
|
struct virtio_blk_config
|
||||||
#define VIRTIO_CONFIG_BLK_F_CAPACITY 0x41
|
{
|
||||||
/* The maximum segment size. */
|
/* The capacity (in 512-byte sectors). */
|
||||||
#define VIRTIO_CONFIG_BLK_F_SIZE_MAX 0x42
|
__le64 capacity;
|
||||||
/* The maximum number of segments. */
|
/* The maximum segment size (if VIRTIO_BLK_F_SIZE_MAX) */
|
||||||
#define VIRTIO_CONFIG_BLK_F_SEG_MAX 0x43
|
__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 @@
|
||||||
* 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 @@
|
||||||
/* 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;
|
||||||
* @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.
|
* @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 return value is -ENOENT if the feature doesn't exist. Otherwise
|
||||||
* be found again. This version does endian conversion. */
|
* the value is endian-corrected and returned in v. */
|
||||||
#define virtio_config_val(vdev, type, v) ({ \
|
#define virtio_config_val(vdev, fbit, offset, v) ({ \
|
||||||
int _err = __virtio_config_val((vdev),(type),(v),sizeof(*(v))); \
|
int _err; \
|
||||||
\
|
if ((vdev)->config->feature((vdev), (fbit))) { \
|
||||||
BUILD_BUG_ON(sizeof(*(v)) != 1 && sizeof(*(v)) != 2 \
|
__virtio_config_val((vdev), (offset), (v)); \
|
||||||
&& sizeof(*(v)) != 4 && sizeof(*(v)) != 8); \
|
_err = 0; \
|
||||||
if (!_err) { \
|
} else \
|
||||||
switch (sizeof(*(v))) { \
|
_err = -ENOENT; \
|
||||||
case 2: le16_to_cpus((__u16 *) v); break; \
|
|
||||||
case 4: le32_to_cpus((__u32 *) v); break; \
|
|
||||||
case 8: le64_to_cpus((__u64 *) v); break; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
_err; \
|
_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.
|
* __virtio_config_val - get a single virtio config without feature check.
|
||||||
* @dev: the virtio device
|
* @vdev: the virtio device
|
||||||
* @token: the token as returned from vdev->config->find().
|
* @offset: the type to search for.
|
||||||
* @len: the length of the field.
|
* @val: a pointer to the value to fill in.
|
||||||
* @bitnum: the bit to test.
|
|
||||||
*
|
*
|
||||||
* If handed a NULL token, it returns false, otherwise returns bit status.
|
* The value is endian-corrected and returned in v. */
|
||||||
* If it's one, it sets the mirroring acknowledgement bit. */
|
#define __virtio_config_val(vdev, offset, v) do { \
|
||||||
int virtio_use_bit(struct virtio_device *vdev,
|
BUILD_BUG_ON(sizeof(*(v)) != 1 && sizeof(*(v)) != 2 \
|
||||||
void *token, unsigned int len, unsigned int bitnum);
|
&& sizeof(*(v)) != 4 && sizeof(*(v)) != 8); \
|
||||||
|
(vdev)->config->get((vdev), (offset), (v), sizeof(*(v))); \
|
||||||
|
switch (sizeof(*(v))) { \
|
||||||
|
case 2: le16_to_cpus((__u16 *) v); break; \
|
||||||
|
case 4: le32_to_cpus((__u32 *) v); break; \
|
||||||
|
case 8: le64_to_cpus((__u64 *) v); break; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
#endif /* __KERNEL__ */
|
#endif /* __KERNEL__ */
|
||||||
#endif /* _LINUX_VIRTIO_CONFIG_H */
|
#endif /* _LINUX_VIRTIO_CONFIG_H */
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
/* 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;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue