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

Commit 02cdf082 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* 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.
parents 8bec4a5d a007a751
Loading
Loading
Loading
Loading
+41 −21
Original line number Diff line number Diff line
@@ -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;
};
@@ -925,14 +928,14 @@ 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;

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

		/* Clear any features they've acked. */
		memset(get_feature_bits(dev) + dev->desc->feature_len, 0,
@@ -944,6 +947,22 @@ static void reset_device(struct device *dev)
			       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);
	}
}

/* This is the generic routine we call when the Guest uses LHCALL_NOTIFY. */
@@ -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;
		}

@@ -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
@@ -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;
@@ -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
@@ -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 */
@@ -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 */

@@ -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;
		}
	}

+32 −12
Original line number Diff line number Diff line
@@ -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)
@@ -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;
@@ -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;
@@ -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)
{
	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;
}

@@ -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) {
@@ -289,7 +304,6 @@ out:
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));
@@ -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);
@@ -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,
+41 −27
Original line number Diff line number Diff line
@@ -85,27 +85,34 @@ static unsigned desc_size(const struct lguest_device_desc *desc)
		+ desc->config_len;
}

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

/*
@@ -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,
+1 −3
Original line number Diff line number Diff line
@@ -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. */
@@ -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)
+68 −28
Original line number Diff line number Diff line
@@ -41,6 +41,9 @@ struct virtnet_info
	struct net_device *dev;
	struct napi_struct napi;

	/* The skb we couldn't send because buffers were full. */
	struct sk_buff *last_xmit_skb;

	/* Number of input buffers, and max we've ever had. */
	unsigned int num, max;

@@ -142,10 +145,10 @@ drop:
static void try_fill_recv(struct virtnet_info *vi)
{
	struct sk_buff *skb;
	struct scatterlist sg[1+MAX_SKB_FRAGS];
	struct scatterlist sg[2+MAX_SKB_FRAGS];
	int num, err;

	sg_init_table(sg, 1+MAX_SKB_FRAGS);
	sg_init_table(sg, 2+MAX_SKB_FRAGS);
	for (;;) {
		skb = netdev_alloc_skb(vi->dev, MAX_PACKET_LEN);
		if (unlikely(!skb))
@@ -221,23 +224,22 @@ static void free_old_xmit_skbs(struct virtnet_info *vi)
	while ((skb = vi->svq->vq_ops->get_buf(vi->svq, &len)) != NULL) {
		pr_debug("Sent skb %p\n", skb);
		__skb_unlink(skb, &vi->send);
		vi->dev->stats.tx_bytes += len;
		vi->dev->stats.tx_bytes += skb->len;
		vi->dev->stats.tx_packets++;
		kfree_skb(skb);
	}
}

static int start_xmit(struct sk_buff *skb, struct net_device *dev)
static int xmit_skb(struct virtnet_info *vi, struct sk_buff *skb)
{
	struct virtnet_info *vi = netdev_priv(dev);
	int num, err;
	struct scatterlist sg[1+MAX_SKB_FRAGS];
	int num;
	struct scatterlist sg[2+MAX_SKB_FRAGS];
	struct virtio_net_hdr *hdr;
	const unsigned char *dest = ((struct ethhdr *)skb->data)->h_dest;

	sg_init_table(sg, 1+MAX_SKB_FRAGS);
	sg_init_table(sg, 2+MAX_SKB_FRAGS);

	pr_debug("%s: xmit %p " MAC_FMT "\n", dev->name, skb,
	pr_debug("%s: xmit %p " MAC_FMT "\n", vi->dev->name, skb,
		 dest[0], dest[1], dest[2],
		 dest[3], dest[4], dest[5]);

@@ -272,13 +274,40 @@ static int start_xmit(struct sk_buff *skb, struct net_device *dev)

	vnet_hdr_to_sg(sg, skb);
	num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1;
	__skb_queue_head(&vi->send, skb);

	return vi->svq->vq_ops->add_buf(vi->svq, sg, num, 0, skb);
}

static int start_xmit(struct sk_buff *skb, struct net_device *dev)
{
	struct virtnet_info *vi = netdev_priv(dev);

again:
	/* Free up any pending old buffers before queueing new ones. */
	free_old_xmit_skbs(vi);
	err = vi->svq->vq_ops->add_buf(vi->svq, sg, num, 0, skb);
	if (err) {

	/* If we has a buffer left over from last time, send it now. */
	if (vi->last_xmit_skb) {
		if (xmit_skb(vi, vi->last_xmit_skb) != 0) {
			/* Drop this skb: we only queue one. */
			vi->dev->stats.tx_dropped++;
			kfree_skb(skb);
			goto stop_queue;
		}
		vi->last_xmit_skb = NULL;
	}

	/* Put new one in send queue and do transmit */
	__skb_queue_head(&vi->send, skb);
	if (xmit_skb(vi, skb) != 0) {
		vi->last_xmit_skb = skb;
		goto stop_queue;
	}
done:
	vi->svq->vq_ops->kick(vi->svq);
	return NETDEV_TX_OK;

stop_queue:
	pr_debug("%s: virtio not prepared to send\n", dev->name);
	netif_stop_queue(dev);

@@ -289,13 +318,7 @@ again:
		netif_start_queue(dev);
		goto again;
	}
		__skb_unlink(skb, &vi->send);

		return NETDEV_TX_BUSY;
	}
	vi->svq->vq_ops->kick(vi->svq);

	return 0;
	goto done;
}

#ifdef CONFIG_NET_POLL_CONTROLLER
@@ -355,17 +378,26 @@ static int virtnet_probe(struct virtio_device *vdev)
	SET_NETDEV_DEV(dev, &vdev->dev);

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

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

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

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