Commit 43e1b129 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull virtio updates from Michael Tsirkin:
 "vhost and virtio fixes and features:

   - Hardening work by Jason

   - vdpa driver for Alibaba ENI

   - Performance tweaks for virtio blk

   - virtio rng rework using an internal buffer

   - mac/mtu programming for mlx5 vdpa

   - Misc fixes, cleanups"

* tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost: (45 commits)
  vdpa/mlx5: Forward only packets with allowed MAC address
  vdpa/mlx5: Support configuration of MAC
  vdpa/mlx5: Fix clearing of VIRTIO_NET_F_MAC feature bit
  vdpa_sim_net: Enable user to set mac address and mtu
  vdpa: Enable user to set mac and mtu of vdpa device
  vdpa: Use kernel coding style for structure comments
  vdpa: Introduce query of device config layout
  vdpa: Introduce and use vdpa device get, set config helpers
  virtio-scsi: don't let virtio core to validate used buffer length
  virtio-blk: don't let virtio core to validate used length
  virtio-net: don't let virtio core to validate used length
  virtio_ring: validate used buffer length
  virtio_blk: correct types for status handling
  virtio_blk: allow 0 as num_request_queues
  i2c: virtio: Add support for zero-length requests
  virtio-blk: fixup coccinelle warnings
  virtio_ring: fix typos in vring_desc_extra
  virtio-pci: harden INTX interrupts
  virtio_pci: harden MSI-X interrupts
  virtio_config: introduce a new .enable_cbs method
  ...
parents d4ec3d55 540061ac
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -20083,6 +20083,13 @@ S: Maintained
F:	drivers/i2c/busses/i2c-virtio.c
F:	include/uapi/linux/virtio_i2c.h
VIRTIO PMEM DRIVER
M:	Pankaj Gupta <pankaj.gupta.linux@gmail.com>
L:	virtualization@lists.linux-foundation.org
S:	Maintained
F:	drivers/nvdimm/virtio_pmem.c
F:	drivers/nvdimm/nd_virtio.c
VIRTUAL BOX GUEST DEVICE DRIVER
M:	Hans de Goede <hdegoede@redhat.com>
M:	Arnd Bergmann <arnd@arndb.de>
+1 −0
Original line number Diff line number Diff line
@@ -371,6 +371,7 @@ config XEN_BLKDEV_BACKEND
config VIRTIO_BLK
	tristate "Virtio block driver"
	depends on VIRTIO
	select SG_POOL
	help
	  This is the virtual block driver for virtio.  It can be used with
          QEMU based VMMs (like KVM or Xen).  Say Y or M.
+119 −59
Original line number Diff line number Diff line
@@ -24,6 +24,19 @@
/* The maximum number of sg elements that fit into a virtqueue */
#define VIRTIO_BLK_MAX_SG_ELEMS 32768

#ifdef CONFIG_ARCH_NO_SG_CHAIN
#define VIRTIO_BLK_INLINE_SG_CNT	0
#else
#define VIRTIO_BLK_INLINE_SG_CNT	2
#endif

static unsigned int num_request_queues;
module_param(num_request_queues, uint, 0644);
MODULE_PARM_DESC(num_request_queues,
		 "Limit the number of request queues to use for blk device. "
		 "0 for no limit. "
		 "Values > nr_cpu_ids truncated to nr_cpu_ids.");

static int major;
static DEFINE_IDA(vd_index_ida);

@@ -77,6 +90,7 @@ struct virtio_blk {
struct virtblk_req {
	struct virtio_blk_outhdr out_hdr;
	u8 status;
	struct sg_table sg_table;
	struct scatterlist sg[];
};

@@ -162,12 +176,93 @@ static int virtblk_setup_discard_write_zeroes(struct request *req, bool unmap)
	return 0;
}

static inline void virtblk_request_done(struct request *req)
static void virtblk_unmap_data(struct request *req, struct virtblk_req *vbr)
{
	struct virtblk_req *vbr = blk_mq_rq_to_pdu(req);
	if (blk_rq_nr_phys_segments(req))
		sg_free_table_chained(&vbr->sg_table,
				      VIRTIO_BLK_INLINE_SG_CNT);
}

static int virtblk_map_data(struct blk_mq_hw_ctx *hctx, struct request *req,
		struct virtblk_req *vbr)
{
	int err;

	if (!blk_rq_nr_phys_segments(req))
		return 0;

	vbr->sg_table.sgl = vbr->sg;
	err = sg_alloc_table_chained(&vbr->sg_table,
				     blk_rq_nr_phys_segments(req),
				     vbr->sg_table.sgl,
				     VIRTIO_BLK_INLINE_SG_CNT);
	if (unlikely(err))
		return -ENOMEM;

	return blk_rq_map_sg(hctx->queue, req, vbr->sg_table.sgl);
}

static void virtblk_cleanup_cmd(struct request *req)
{
	if (req->rq_flags & RQF_SPECIAL_PAYLOAD)
		kfree(bvec_virt(&req->special_vec));
}

static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,
				      struct request *req,
				      struct virtblk_req *vbr)
{
	bool unmap = false;
	u32 type;

	vbr->out_hdr.sector = 0;

	switch (req_op(req)) {
	case REQ_OP_READ:
		type = VIRTIO_BLK_T_IN;
		vbr->out_hdr.sector = cpu_to_virtio64(vdev,
						      blk_rq_pos(req));
		break;
	case REQ_OP_WRITE:
		type = VIRTIO_BLK_T_OUT;
		vbr->out_hdr.sector = cpu_to_virtio64(vdev,
						      blk_rq_pos(req));
		break;
	case REQ_OP_FLUSH:
		type = VIRTIO_BLK_T_FLUSH;
		break;
	case REQ_OP_DISCARD:
		type = VIRTIO_BLK_T_DISCARD;
		break;
	case REQ_OP_WRITE_ZEROES:
		type = VIRTIO_BLK_T_WRITE_ZEROES;
		unmap = !(req->cmd_flags & REQ_NOUNMAP);
		break;
	case REQ_OP_DRV_IN:
		type = VIRTIO_BLK_T_GET_ID;
		break;
	default:
		WARN_ON_ONCE(1);
		return BLK_STS_IOERR;
	}

	vbr->out_hdr.type = cpu_to_virtio32(vdev, type);
	vbr->out_hdr.ioprio = cpu_to_virtio32(vdev, req_get_ioprio(req));

	if (type == VIRTIO_BLK_T_DISCARD || type == VIRTIO_BLK_T_WRITE_ZEROES) {
		if (virtblk_setup_discard_write_zeroes(req, unmap))
			return BLK_STS_RESOURCE;
	}

	return 0;
}

static inline void virtblk_request_done(struct request *req)
{
	struct virtblk_req *vbr = blk_mq_rq_to_pdu(req);

	virtblk_unmap_data(req, vbr);
	virtblk_cleanup_cmd(req);
	blk_mq_end_request(req, virtblk_result(vbr));
}

@@ -223,59 +318,26 @@ static blk_status_t virtio_queue_rq(struct blk_mq_hw_ctx *hctx,
	unsigned long flags;
	unsigned int num;
	int qid = hctx->queue_num;
	int err;
	bool notify = false;
	bool unmap = false;
	u32 type;
	blk_status_t status;
	int err;

	BUG_ON(req->nr_phys_segments + 2 > vblk->sg_elems);

	switch (req_op(req)) {
	case REQ_OP_READ:
	case REQ_OP_WRITE:
		type = 0;
		break;
	case REQ_OP_FLUSH:
		type = VIRTIO_BLK_T_FLUSH;
		break;
	case REQ_OP_DISCARD:
		type = VIRTIO_BLK_T_DISCARD;
		break;
	case REQ_OP_WRITE_ZEROES:
		type = VIRTIO_BLK_T_WRITE_ZEROES;
		unmap = !(req->cmd_flags & REQ_NOUNMAP);
		break;
	case REQ_OP_DRV_IN:
		type = VIRTIO_BLK_T_GET_ID;
		break;
	default:
		WARN_ON_ONCE(1);
		return BLK_STS_IOERR;
	}

	vbr->out_hdr.type = cpu_to_virtio32(vblk->vdev, type);
	vbr->out_hdr.sector = type ?
		0 : cpu_to_virtio64(vblk->vdev, blk_rq_pos(req));
	vbr->out_hdr.ioprio = cpu_to_virtio32(vblk->vdev, req_get_ioprio(req));
	status = virtblk_setup_cmd(vblk->vdev, req, vbr);
	if (unlikely(status))
		return status;

	blk_mq_start_request(req);

	if (type == VIRTIO_BLK_T_DISCARD || type == VIRTIO_BLK_T_WRITE_ZEROES) {
		err = virtblk_setup_discard_write_zeroes(req, unmap);
		if (err)
	num = virtblk_map_data(hctx, req, vbr);
	if (unlikely(num < 0)) {
		virtblk_cleanup_cmd(req);
		return BLK_STS_RESOURCE;
	}

	num = blk_rq_map_sg(hctx->queue, req, vbr->sg);
	if (num) {
		if (rq_data_dir(req) == WRITE)
			vbr->out_hdr.type |= cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_OUT);
		else
			vbr->out_hdr.type |= cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_IN);
	}

	spin_lock_irqsave(&vblk->vqs[qid].lock, flags);
	err = virtblk_add_req(vblk->vqs[qid].vq, vbr, vbr->sg, num);
	err = virtblk_add_req(vblk->vqs[qid].vq, vbr, vbr->sg_table.sgl, num);
	if (err) {
		virtqueue_kick(vblk->vqs[qid].vq);
		/* Don't stop the queue if -ENOMEM: we may have failed to
@@ -284,6 +346,8 @@ static blk_status_t virtio_queue_rq(struct blk_mq_hw_ctx *hctx,
		if (err == -ENOSPC)
			blk_mq_stop_hw_queue(hctx);
		spin_unlock_irqrestore(&vblk->vqs[qid].lock, flags);
		virtblk_unmap_data(req, vbr);
		virtblk_cleanup_cmd(req);
		switch (err) {
		case -ENOSPC:
			return BLK_STS_DEV_RESOURCE;
@@ -497,8 +561,14 @@ static int init_vq(struct virtio_blk *vblk)
				   &num_vqs);
	if (err)
		num_vqs = 1;
	if (!err && !num_vqs) {
		dev_err(&vdev->dev, "MQ advertised but zero queues reported\n");
		return -EINVAL;
	}

	num_vqs = min_t(unsigned int, nr_cpu_ids, num_vqs);
	num_vqs = min_t(unsigned int,
			min_not_zero(num_request_queues, nr_cpu_ids),
			num_vqs);

	vblk->vqs = kmalloc_array(num_vqs, sizeof(*vblk->vqs), GFP_KERNEL);
	if (!vblk->vqs)
@@ -624,7 +694,7 @@ cache_type_show(struct device *dev, struct device_attribute *attr, char *buf)
	u8 writeback = virtblk_get_cache_mode(vblk->vdev);

	BUG_ON(writeback >= ARRAY_SIZE(virtblk_cache_types));
	return snprintf(buf, 40, "%s\n", virtblk_cache_types[writeback]);
	return sysfs_emit(buf, "%s\n", virtblk_cache_types[writeback]);
}

static DEVICE_ATTR_RW(cache_type);
@@ -660,16 +730,6 @@ static const struct attribute_group *virtblk_attr_groups[] = {
	NULL,
};

static int virtblk_init_request(struct blk_mq_tag_set *set, struct request *rq,
		unsigned int hctx_idx, unsigned int numa_node)
{
	struct virtio_blk *vblk = set->driver_data;
	struct virtblk_req *vbr = blk_mq_rq_to_pdu(rq);

	sg_init_table(vbr->sg, vblk->sg_elems);
	return 0;
}

static int virtblk_map_queues(struct blk_mq_tag_set *set)
{
	struct virtio_blk *vblk = set->driver_data;
@@ -682,7 +742,6 @@ static const struct blk_mq_ops virtio_mq_ops = {
	.queue_rq	= virtio_queue_rq,
	.commit_rqs	= virtio_commit_rqs,
	.complete	= virtblk_request_done,
	.init_request	= virtblk_init_request,
	.map_queues	= virtblk_map_queues,
};

@@ -762,7 +821,7 @@ static int virtblk_probe(struct virtio_device *vdev)
	vblk->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
	vblk->tag_set.cmd_size =
		sizeof(struct virtblk_req) +
		sizeof(struct scatterlist) * sg_elems;
		sizeof(struct scatterlist) * VIRTIO_BLK_INLINE_SG_CNT;
	vblk->tag_set.driver_data = vblk;
	vblk->tag_set.nr_hw_queues = vblk->num_vqs;

@@ -990,6 +1049,7 @@ static struct virtio_driver virtio_blk = {
	.feature_table_size		= ARRAY_SIZE(features),
	.feature_table_legacy		= features_legacy,
	.feature_table_size_legacy	= ARRAY_SIZE(features_legacy),
	.suppress_used_validation	= true,
	.driver.name			= KBUILD_MODNAME,
	.driver.owner			= THIS_MODULE,
	.id_table			= id_table,
+65 −21
Original line number Diff line number Diff line
@@ -18,13 +18,20 @@ static DEFINE_IDA(rng_index_ida);
struct virtrng_info {
	struct hwrng hwrng;
	struct virtqueue *vq;
	struct completion have_data;
	char name[25];
	unsigned int data_avail;
	int index;
	bool busy;
	bool hwrng_register_done;
	bool hwrng_removed;
	/* data transfer */
	struct completion have_data;
	unsigned int data_avail;
	unsigned int data_idx;
	/* minimal size returned by rng_buffer_size() */
#if SMP_CACHE_BYTES < 32
	u8 data[32];
#else
	u8 data[SMP_CACHE_BYTES];
#endif
};

static void random_recv_done(struct virtqueue *vq)
@@ -35,54 +42,88 @@ static void random_recv_done(struct virtqueue *vq)
	if (!virtqueue_get_buf(vi->vq, &vi->data_avail))
		return;

	vi->data_idx = 0;

	complete(&vi->have_data);
}

/* The host will fill any buffer we give it with sweet, sweet randomness. */
static void register_buffer(struct virtrng_info *vi, u8 *buf, size_t size)
static void request_entropy(struct virtrng_info *vi)
{
	struct scatterlist sg;

	sg_init_one(&sg, buf, size);
	reinit_completion(&vi->have_data);
	vi->data_avail = 0;
	vi->data_idx = 0;

	sg_init_one(&sg, vi->data, sizeof(vi->data));

	/* There should always be room for one buffer. */
	virtqueue_add_inbuf(vi->vq, &sg, 1, buf, GFP_KERNEL);
	virtqueue_add_inbuf(vi->vq, &sg, 1, vi->data, GFP_KERNEL);

	virtqueue_kick(vi->vq);
}

static unsigned int copy_data(struct virtrng_info *vi, void *buf,
			      unsigned int size)
{
	size = min_t(unsigned int, size, vi->data_avail);
	memcpy(buf, vi->data + vi->data_idx, size);
	vi->data_idx += size;
	vi->data_avail -= size;
	if (vi->data_avail == 0)
		request_entropy(vi);
	return size;
}

static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
{
	int ret;
	struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
	unsigned int chunk;
	size_t read;

	if (vi->hwrng_removed)
		return -ENODEV;

	if (!vi->busy) {
		vi->busy = true;
		reinit_completion(&vi->have_data);
		register_buffer(vi, buf, size);
	read = 0;

	/* copy available data */
	if (vi->data_avail) {
		chunk = copy_data(vi, buf, size);
		size -= chunk;
		read += chunk;
	}

	if (!wait)
		return 0;
		return read;

	/* We have already copied available entropy,
	 * so either size is 0 or data_avail is 0
	 */
	while (size != 0) {
		/* data_avail is 0 but a request is pending */
		ret = wait_for_completion_killable(&vi->have_data);
		if (ret < 0)
			return ret;
		/* if vi->data_avail is 0, we have been interrupted
		 * by a cleanup, but buffer stays in the queue
		 */
		if (vi->data_avail == 0)
			return read;

	vi->busy = false;
		chunk = copy_data(vi, buf + read, size);
		size -= chunk;
		read += chunk;
	}

	return vi->data_avail;
	return read;
}

static void virtio_cleanup(struct hwrng *rng)
{
	struct virtrng_info *vi = (struct virtrng_info *)rng->priv;

	if (vi->busy)
		wait_for_completion(&vi->have_data);
	complete(&vi->have_data);
}

static int probe_common(struct virtio_device *vdev)
@@ -118,6 +159,9 @@ static int probe_common(struct virtio_device *vdev)
		goto err_find;
	}

	/* we always have a pending entropy request */
	request_entropy(vi);

	return 0;

err_find:
@@ -133,9 +177,9 @@ static void remove_common(struct virtio_device *vdev)

	vi->hwrng_removed = true;
	vi->data_avail = 0;
	vi->data_idx = 0;
	complete(&vi->have_data);
	vdev->config->reset(vdev);
	vi->busy = false;
	if (vi->hwrng_register_done)
		hwrng_unregister(&vi->hwrng);
	vdev->config->del_vqs(vdev);
+9 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#include "../tty/hvc/hvc_console.h"

#define is_rproc_enabled IS_ENABLED(CONFIG_REMOTEPROC)
#define VIRTCONS_MAX_PORTS 0x8000

/*
 * This is a global struct for storing common data for all the devices
@@ -2036,6 +2037,14 @@ static int virtcons_probe(struct virtio_device *vdev)
	    virtio_cread_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT,
				 struct virtio_console_config, max_nr_ports,
				 &portdev->max_nr_ports) == 0) {
		if (portdev->max_nr_ports == 0 ||
		    portdev->max_nr_ports > VIRTCONS_MAX_PORTS) {
			dev_err(&vdev->dev,
				"Invalidate max_nr_ports %d",
				portdev->max_nr_ports);
			err = -EINVAL;
			goto free;
		}
		multiport = true;
	}

Loading