Skip to content

Commit

Permalink
Merge git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux-2.6-f…
Browse files Browse the repository at this point in the history
…or-linus

* git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux-2.6-for-linus:
  lguest: make Launcher see device status updates
  lguest: remove bogus NULL cpu check
  lguest: avoid using NR_CPUS as a bounds check.
  virtio: add virtio disk geometry feature
  virtio: explicit advertisement of driver features
  virtio: change config to guest endian.
  virtio: finer-grained features for virtio_net
  virtio: wean net driver off NETDEV_TX_BUSY
  virtio-blk: fix remove oops
  virtio: fix scatterlist sizing in net driver.
  virtio: de-structify virtio_block status byte
  virtio: export more headers to userspace
  virtio: fix sparse return void-valued expression warnings
  virtio: fix tx_ stats in virtio_net
  virtio: ignore corrupted virtqueues rather than spinning.
  • Loading branch information
Linus Torvalds committed May 2, 2008
2 parents 8bec4a5 + a007a75 commit 02cdf08
Show file tree
Hide file tree
Showing 14 changed files with 324 additions and 159 deletions.
62 changes: 41 additions & 21 deletions Documentation/lguest/lguest.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ struct device
/* Any queues attached to this device */
struct virtqueue *vq;

/* Handle status being finalized (ie. feature bits stable). */
void (*ready)(struct device *me);

/* Device-specific data. */
void *priv;
};
Expand Down Expand Up @@ -925,24 +928,40 @@ static void enable_fd(int fd, struct virtqueue *vq)
write(waker_fd, &vq->dev->fd, sizeof(vq->dev->fd));
}

/* When the Guest asks us to reset a device, it's is fairly easy. */
static void reset_device(struct device *dev)
/* When the Guest tells us they updated the status field, we handle it. */
static void update_device_status(struct device *dev)
{
struct virtqueue *vq;

verbose("Resetting device %s\n", dev->name);
/* Clear the status. */
dev->desc->status = 0;
/* This is a reset. */
if (dev->desc->status == 0) {
verbose("Resetting device %s\n", dev->name);

/* Clear any features they've acked. */
memset(get_feature_bits(dev) + dev->desc->feature_len, 0,
dev->desc->feature_len);
/* Clear any features they've acked. */
memset(get_feature_bits(dev) + dev->desc->feature_len, 0,
dev->desc->feature_len);

/* Zero out the virtqueues. */
for (vq = dev->vq; vq; vq = vq->next) {
memset(vq->vring.desc, 0,
vring_size(vq->config.num, getpagesize()));
vq->last_avail_idx = 0;
/* Zero out the virtqueues. */
for (vq = dev->vq; vq; vq = vq->next) {
memset(vq->vring.desc, 0,
vring_size(vq->config.num, getpagesize()));
vq->last_avail_idx = 0;
}
} else if (dev->desc->status & VIRTIO_CONFIG_S_FAILED) {
warnx("Device %s configuration FAILED", dev->name);
} else if (dev->desc->status & VIRTIO_CONFIG_S_DRIVER_OK) {
unsigned int i;

verbose("Device %s OK: offered", dev->name);
for (i = 0; i < dev->desc->feature_len; i++)
verbose(" %08x", get_feature_bits(dev)[i]);
verbose(", accepted");
for (i = 0; i < dev->desc->feature_len; i++)
verbose(" %08x", get_feature_bits(dev)
[dev->desc->feature_len+i]);

if (dev->ready)
dev->ready(dev);
}
}

Expand All @@ -954,9 +973,9 @@ static void handle_output(int fd, unsigned long addr)

/* Check each device and virtqueue. */
for (i = devices.dev; i; i = i->next) {
/* Notifications to device descriptors reset the device. */
/* Notifications to device descriptors update device status. */
if (from_guest_phys(addr) == i->desc) {
reset_device(i);
update_device_status(i);
return;
}

Expand Down Expand Up @@ -1170,6 +1189,7 @@ static struct device *new_device(const char *name, u16 type, int fd,
dev->handle_input = handle_input;
dev->name = name;
dev->vq = NULL;
dev->ready = 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
Expand Down Expand Up @@ -1398,7 +1418,7 @@ static bool service_io(struct device *dev)
struct vblk_info *vblk = dev->priv;
unsigned int head, out_num, in_num, wlen;
int ret;
struct virtio_blk_inhdr *in;
u8 *in;
struct virtio_blk_outhdr *out;
struct iovec iov[dev->vq->vring.num];
off64_t off;
Expand All @@ -1416,7 +1436,7 @@ static bool service_io(struct device *dev)
head, out_num, in_num);

out = convert(&iov[0], struct virtio_blk_outhdr);
in = convert(&iov[out_num+in_num-1], struct virtio_blk_inhdr);
in = convert(&iov[out_num+in_num-1], u8);
off = out->sector * 512;

/* The block device implements "barriers", where the Guest indicates
Expand All @@ -1430,7 +1450,7 @@ static bool service_io(struct device *dev)
* It'd be nice if we supported eject, for example, but we don't. */
if (out->type & VIRTIO_BLK_T_SCSI_CMD) {
fprintf(stderr, "Scsi commands unsupported\n");
in->status = VIRTIO_BLK_S_UNSUPP;
*in = VIRTIO_BLK_S_UNSUPP;
wlen = sizeof(*in);
} else if (out->type & VIRTIO_BLK_T_OUT) {
/* Write */
Expand All @@ -1453,7 +1473,7 @@ static bool service_io(struct device *dev)
errx(1, "Write past end %llu+%u", off, ret);
}
wlen = sizeof(*in);
in->status = (ret >= 0 ? VIRTIO_BLK_S_OK : VIRTIO_BLK_S_IOERR);
*in = (ret >= 0 ? VIRTIO_BLK_S_OK : VIRTIO_BLK_S_IOERR);
} else {
/* Read */

Expand All @@ -1466,10 +1486,10 @@ static bool service_io(struct device *dev)
verbose("READ from sector %llu: %i\n", out->sector, ret);
if (ret >= 0) {
wlen = sizeof(*in) + ret;
in->status = VIRTIO_BLK_S_OK;
*in = VIRTIO_BLK_S_OK;
} else {
wlen = sizeof(*in);
in->status = VIRTIO_BLK_S_IOERR;
*in = VIRTIO_BLK_S_IOERR;
}
}

Expand Down
44 changes: 32 additions & 12 deletions drivers/block/virtio_blk.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct virtblk_req
struct list_head list;
struct request *req;
struct virtio_blk_outhdr out_hdr;
struct virtio_blk_inhdr in_hdr;
u8 status;
};

static void blk_done(struct virtqueue *vq)
Expand All @@ -48,7 +48,7 @@ static void blk_done(struct virtqueue *vq)
spin_lock_irqsave(&vblk->lock, flags);
while ((vbr = vblk->vq->vq_ops->get_buf(vblk->vq, &len)) != NULL) {
int uptodate;
switch (vbr->in_hdr.status) {
switch (vbr->status) {
case VIRTIO_BLK_S_OK:
uptodate = 1;
break;
Expand Down Expand Up @@ -101,7 +101,7 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
sg_init_table(vblk->sg, VIRTIO_MAX_SG);
sg_set_buf(&vblk->sg[0], &vbr->out_hdr, sizeof(vbr->out_hdr));
num = blk_rq_map_sg(q, vbr->req, vblk->sg+1);
sg_set_buf(&vblk->sg[num+1], &vbr->in_hdr, sizeof(vbr->in_hdr));
sg_set_buf(&vblk->sg[num+1], &vbr->status, sizeof(vbr->status));

if (rq_data_dir(vbr->req) == WRITE) {
vbr->out_hdr.type |= VIRTIO_BLK_T_OUT;
Expand Down Expand Up @@ -157,10 +157,25 @@ static int virtblk_ioctl(struct inode *inode, struct file *filp,
/* We provide getgeo only to please some old bootloader/partitioning tools */
static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo)
{
/* some standard values, similar to sd */
geo->heads = 1 << 6;
geo->sectors = 1 << 5;
geo->cylinders = get_capacity(bd->bd_disk) >> 11;
struct virtio_blk *vblk = bd->bd_disk->private_data;
struct virtio_blk_geometry vgeo;
int err;

/* see if the host passed in geometry config */
err = virtio_config_val(vblk->vdev, VIRTIO_BLK_F_GEOMETRY,
offsetof(struct virtio_blk_config, geometry),
&vgeo);

if (!err) {
geo->heads = vgeo.heads;
geo->sectors = vgeo.sectors;
geo->cylinders = vgeo.cylinders;
} else {
/* some standard values, similar to sd */
geo->heads = 1 << 6;
geo->sectors = 1 << 5;
geo->cylinders = get_capacity(bd->bd_disk) >> 11;
}
return 0;
}

Expand Down Expand Up @@ -242,12 +257,12 @@ static int virtblk_probe(struct virtio_device *vdev)
index++;

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

/* Host must always specify the capacity. */
__virtio_config_val(vdev, offsetof(struct virtio_blk_config, capacity),
&cap);
vdev->config->get(vdev, offsetof(struct virtio_blk_config, capacity),
&cap, sizeof(cap));

/* If capacity is too big, truncate with warning. */
if ((sector_t)cap != cap) {
Expand Down Expand Up @@ -289,7 +304,6 @@ static int virtblk_probe(struct virtio_device *vdev)
static void virtblk_remove(struct virtio_device *vdev)
{
struct virtio_blk *vblk = vdev->priv;
int major = vblk->disk->major;

/* Nothing should be pending. */
BUG_ON(!list_empty(&vblk->reqs));
Expand All @@ -299,7 +313,6 @@ static void virtblk_remove(struct virtio_device *vdev)

blk_cleanup_queue(vblk->disk->queue);
put_disk(vblk->disk);
unregister_blkdev(major, "virtblk");
mempool_destroy(vblk->pool);
vdev->config->del_vq(vblk->vq);
kfree(vblk);
Expand All @@ -310,7 +323,14 @@ static struct virtio_device_id id_table[] = {
{ 0 },
};

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

static struct virtio_driver virtio_blk = {
.feature_table = features,
.feature_table_size = ARRAY_SIZE(features),
.driver.name = KBUILD_MODNAME,
.driver.owner = THIS_MODULE,
.id_table = id_table,
Expand Down
68 changes: 41 additions & 27 deletions drivers/lguest/lguest_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,27 +85,34 @@ static unsigned desc_size(const struct lguest_device_desc *desc)
+ desc->config_len;
}

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

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

return features;
}

static void lg_set_features(struct virtio_device *vdev, u32 features)
{
unsigned int i;
struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
/* Second half of bitmap is features we accept. */
u8 *out_features = lg_features(desc) + desc->feature_len;

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

/* Once they've found a field, getting a copy of it is easy. */
Expand Down Expand Up @@ -137,20 +144,26 @@ static u8 lg_get_status(struct virtio_device *vdev)
return to_lgdev(vdev)->desc->status;
}

/* To notify on status updates, we (ab)use the NOTIFY hypercall, with the
* descriptor address of the device. A zero status means "reset". */
static void set_status(struct virtio_device *vdev, u8 status)
{
unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices;

/* We set the status. */
to_lgdev(vdev)->desc->status = status;
hcall(LHCALL_NOTIFY, (max_pfn<<PAGE_SHIFT) + offset, 0, 0);
}

static void lg_set_status(struct virtio_device *vdev, u8 status)
{
BUG_ON(!status);
to_lgdev(vdev)->desc->status = status;
set_status(vdev, status);
}

/* To reset the device, we (ab)use the NOTIFY hypercall, with the descriptor
* address of the device. The Host will zero the status and all the
* features. */
static void lg_reset(struct virtio_device *vdev)
{
unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices;

hcall(LHCALL_NOTIFY, (max_pfn<<PAGE_SHIFT) + offset, 0, 0);
set_status(vdev, 0);
}

/*
Expand Down Expand Up @@ -286,7 +299,8 @@ static void lg_del_vq(struct virtqueue *vq)

/* The ops structure which hooks everything together. */
static struct virtio_config_ops lguest_config_ops = {
.feature = lg_feature,
.get_features = lg_get_features,
.set_features = lg_set_features,
.get = lg_get,
.set = lg_set,
.get_status = lg_get_status,
Expand Down
4 changes: 1 addition & 3 deletions drivers/lguest/lguest_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ static ssize_t read(struct file *file, char __user *user, size_t size,loff_t*o)
static int lg_cpu_start(struct lg_cpu *cpu, unsigned id, unsigned long start_ip)
{
/* We have a limited number the number of CPUs in the lguest struct. */
if (id >= NR_CPUS)
if (id >= ARRAY_SIZE(cpu->lg->cpus))
return -EINVAL;

/* Set up this CPU's id, and pointer back to the lguest struct. */
Expand Down Expand Up @@ -251,8 +251,6 @@ static ssize_t write(struct file *file, const char __user *in,
if (!lg || (cpu_id >= lg->nr_cpus))
return -EINVAL;
cpu = &lg->cpus[cpu_id];
if (!cpu)
return -EINVAL;

/* Once the Guest is dead, you can only read() why it died. */
if (lg->dead)
Expand Down
Loading

0 comments on commit 02cdf08

Please sign in to comment.