diff options
| -rw-r--r-- | drivers/block/virtio_blk.c | 83 | ||||
| -rw-r--r-- | drivers/char/hw_random/virtio-rng.c | 4 | ||||
| -rw-r--r-- | drivers/char/virtio_console.c | 25 | ||||
| -rw-r--r-- | drivers/lguest/lguest_device.c | 3 | ||||
| -rw-r--r-- | drivers/lguest/x86/core.c | 6 | ||||
| -rw-r--r-- | drivers/net/caif/caif_virtio.c | 23 | ||||
| -rw-r--r-- | drivers/net/virtio_net.c | 44 | ||||
| -rw-r--r-- | drivers/remoteproc/remoteproc_virtio.c | 3 | ||||
| -rw-r--r-- | drivers/s390/kvm/kvm_virtio.c | 8 | ||||
| -rw-r--r-- | drivers/s390/kvm/virtio_ccw.c | 5 | ||||
| -rw-r--r-- | drivers/scsi/virtio_scsi.c | 19 | ||||
| -rw-r--r-- | drivers/virtio/virtio_balloon.c | 14 | ||||
| -rw-r--r-- | drivers/virtio/virtio_mmio.c | 5 | ||||
| -rw-r--r-- | drivers/virtio/virtio_pci.c | 3 | ||||
| -rw-r--r-- | drivers/virtio/virtio_ring.c | 34 | ||||
| -rw-r--r-- | include/linux/virtio.h | 6 | ||||
| -rw-r--r-- | include/linux/virtio_config.h | 161 | ||||
| -rw-r--r-- | include/linux/virtio_ring.h | 2 | ||||
| -rw-r--r-- | net/9p/trans_virtio.c | 9 | ||||
| -rw-r--r-- | tools/virtio/virtio_test.c | 6 | ||||
| -rw-r--r-- | tools/virtio/vringh_test.c | 13 | 
21 files changed, 310 insertions, 166 deletions
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 5cdf88b7ad9e..f3be496ac8fa 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -292,6 +292,8 @@ static void virtblk_done(struct virtqueue *vq)  				req_done = true;  			}  		} +		if (unlikely(virtqueue_is_broken(vq))) +			break;  	} while (!virtqueue_enable_cb(vq));  	/* In case queue is stopped waiting for more buffers. */  	if (req_done) @@ -456,18 +458,15 @@ static int virtblk_ioctl(struct block_device *bdev, fmode_t mode,  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; +	if (virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_GEOMETRY)) { +		virtio_cread(vblk->vdev, struct virtio_blk_config, +			     geometry.cylinders, &geo->cylinders); +		virtio_cread(vblk->vdev, struct virtio_blk_config, +			     geometry.heads, &geo->heads); +		virtio_cread(vblk->vdev, struct virtio_blk_config, +			     geometry.sectors, &geo->sectors);  	} else {  		/* some standard values, similar to sd */  		geo->heads = 1 << 6; @@ -529,8 +528,7 @@ static void virtblk_config_changed_work(struct work_struct *work)  		goto done;  	/* Host must always specify the capacity. */ -	vdev->config->get(vdev, offsetof(struct virtio_blk_config, capacity), -			  &capacity, sizeof(capacity)); +	virtio_cread(vdev, struct virtio_blk_config, capacity, &capacity);  	/* If capacity is too big, truncate with warning. */  	if ((sector_t)capacity != capacity) { @@ -608,9 +606,9 @@ static int virtblk_get_cache_mode(struct virtio_device *vdev)  	u8 writeback;  	int err; -	err = virtio_config_val(vdev, VIRTIO_BLK_F_CONFIG_WCE, -				offsetof(struct virtio_blk_config, wce), -				&writeback); +	err = virtio_cread_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE, +				   struct virtio_blk_config, wce, +				   &writeback);  	if (err)  		writeback = virtio_has_feature(vdev, VIRTIO_BLK_F_WCE); @@ -642,7 +640,6 @@ virtblk_cache_type_store(struct device *dev, struct device_attribute *attr,  	struct virtio_blk *vblk = disk->private_data;  	struct virtio_device *vdev = vblk->vdev;  	int i; -	u8 writeback;  	BUG_ON(!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_CONFIG_WCE));  	for (i = ARRAY_SIZE(virtblk_cache_types); --i >= 0; ) @@ -652,11 +649,7 @@ virtblk_cache_type_store(struct device *dev, struct device_attribute *attr,  	if (i < 0)  		return -EINVAL; -	writeback = i; -	vdev->config->set(vdev, -			  offsetof(struct virtio_blk_config, wce), -			  &writeback, sizeof(writeback)); - +	virtio_cwrite8(vdev, offsetof(struct virtio_blk_config, wce), i);  	virtblk_update_cache_mode(vdev);  	return count;  } @@ -699,9 +692,9 @@ static int virtblk_probe(struct virtio_device *vdev)  	index = err;  	/* We need to know how many segments before we allocate. */ -	err = virtio_config_val(vdev, VIRTIO_BLK_F_SEG_MAX, -				offsetof(struct virtio_blk_config, seg_max), -				&sg_elems); +	err = virtio_cread_feature(vdev, VIRTIO_BLK_F_SEG_MAX, +				   struct virtio_blk_config, seg_max, +				   &sg_elems);  	/* We need at least one SG element, whatever they say. */  	if (err || !sg_elems) @@ -772,8 +765,7 @@ static int virtblk_probe(struct virtio_device *vdev)  		set_disk_ro(vblk->disk, 1);  	/* Host must always specify the capacity. */ -	vdev->config->get(vdev, offsetof(struct virtio_blk_config, capacity), -			  &cap, sizeof(cap)); +	virtio_cread(vdev, struct virtio_blk_config, capacity, &cap);  	/* If capacity is too big, truncate with warning. */  	if ((sector_t)cap != cap) { @@ -794,46 +786,45 @@ static int virtblk_probe(struct virtio_device *vdev)  	/* 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); +	err = virtio_cread_feature(vdev, VIRTIO_BLK_F_SIZE_MAX, +				   struct virtio_blk_config, size_max, &v);  	if (!err)  		blk_queue_max_segment_size(q, v);  	else  		blk_queue_max_segment_size(q, -1U);  	/* Host can optionally specify the block size of the device */ -	err = virtio_config_val(vdev, VIRTIO_BLK_F_BLK_SIZE, -				offsetof(struct virtio_blk_config, blk_size), -				&blk_size); +	err = virtio_cread_feature(vdev, VIRTIO_BLK_F_BLK_SIZE, +				   struct virtio_blk_config, blk_size, +				   &blk_size);  	if (!err)  		blk_queue_logical_block_size(q, blk_size);  	else  		blk_size = queue_logical_block_size(q);  	/* Use topology information if available */ -	err = virtio_config_val(vdev, VIRTIO_BLK_F_TOPOLOGY, -			offsetof(struct virtio_blk_config, physical_block_exp), -			&physical_block_exp); +	err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY, +				   struct virtio_blk_config, physical_block_exp, +				   &physical_block_exp);  	if (!err && physical_block_exp)  		blk_queue_physical_block_size(q,  				blk_size * (1 << physical_block_exp)); -	err = virtio_config_val(vdev, VIRTIO_BLK_F_TOPOLOGY, -			offsetof(struct virtio_blk_config, alignment_offset), -			&alignment_offset); +	err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY, +				   struct virtio_blk_config, alignment_offset, +				   &alignment_offset);  	if (!err && alignment_offset)  		blk_queue_alignment_offset(q, blk_size * alignment_offset); -	err = virtio_config_val(vdev, VIRTIO_BLK_F_TOPOLOGY, -			offsetof(struct virtio_blk_config, min_io_size), -			&min_io_size); +	err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY, +				   struct virtio_blk_config, min_io_size, +				   &min_io_size);  	if (!err && min_io_size)  		blk_queue_io_min(q, blk_size * min_io_size); -	err = virtio_config_val(vdev, VIRTIO_BLK_F_TOPOLOGY, -			offsetof(struct virtio_blk_config, opt_io_size), -			&opt_io_size); +	err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY, +				   struct virtio_blk_config, opt_io_size, +				   &opt_io_size);  	if (!err && opt_io_size)  		blk_queue_io_opt(q, blk_size * opt_io_size); @@ -899,7 +890,7 @@ static void virtblk_remove(struct virtio_device *vdev)  		ida_simple_remove(&vd_index_ida, index);  } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  static int virtblk_freeze(struct virtio_device *vdev)  {  	struct virtio_blk *vblk = vdev->priv; @@ -959,7 +950,7 @@ static struct virtio_driver virtio_blk = {  	.probe			= virtblk_probe,  	.remove			= virtblk_remove,  	.config_changed		= virtblk_config_changed, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  	.freeze			= virtblk_freeze,  	.restore		= virtblk_restore,  #endif diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index ef46a9cfd832..c12398d1517c 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -133,7 +133,7 @@ static void virtrng_remove(struct virtio_device *vdev)  	remove_common(vdev);  } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  static int virtrng_freeze(struct virtio_device *vdev)  {  	remove_common(vdev); @@ -157,7 +157,7 @@ static struct virtio_driver virtio_rng_driver = {  	.id_table =	id_table,  	.probe =	virtrng_probe,  	.remove =	virtrng_remove, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  	.freeze =	virtrng_freeze,  	.restore =	virtrng_restore,  #endif diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index b79cf3e1b793..feea87cc6b8f 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -577,7 +577,8 @@ static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id,  	spin_lock(&portdev->c_ovq_lock);  	if (virtqueue_add_outbuf(vq, sg, 1, &cpkt, GFP_ATOMIC) == 0) {  		virtqueue_kick(vq); -		while (!virtqueue_get_buf(vq, &len)) +		while (!virtqueue_get_buf(vq, &len) +			&& !virtqueue_is_broken(vq))  			cpu_relax();  	}  	spin_unlock(&portdev->c_ovq_lock); @@ -650,7 +651,8 @@ static ssize_t __send_to_port(struct port *port, struct scatterlist *sg,  	 * we need to kmalloc a GFP_ATOMIC buffer each time the  	 * console driver writes something out.  	 */ -	while (!virtqueue_get_buf(out_vq, &len)) +	while (!virtqueue_get_buf(out_vq, &len) +		&& !virtqueue_is_broken(out_vq))  		cpu_relax();  done:  	spin_unlock_irqrestore(&port->outvq_lock, flags); @@ -1837,12 +1839,8 @@ static void config_intr(struct virtio_device *vdev)  		struct port *port;  		u16 rows, cols; -		vdev->config->get(vdev, -				  offsetof(struct virtio_console_config, cols), -				  &cols, sizeof(u16)); -		vdev->config->get(vdev, -				  offsetof(struct virtio_console_config, rows), -				  &rows, sizeof(u16)); +		virtio_cread(vdev, struct virtio_console_config, cols, &cols); +		virtio_cread(vdev, struct virtio_console_config, rows, &rows);  		port = find_port_by_id(portdev, 0);  		set_console_size(port, rows, cols); @@ -2014,10 +2012,9 @@ static int virtcons_probe(struct virtio_device *vdev)  	/* Don't test MULTIPORT at all if we're rproc: not a valid feature! */  	if (!is_rproc_serial(vdev) && -	    virtio_config_val(vdev, VIRTIO_CONSOLE_F_MULTIPORT, -				  offsetof(struct virtio_console_config, -					   max_nr_ports), -				  &portdev->config.max_nr_ports) == 0) { +	    virtio_cread_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT, +				 struct virtio_console_config, max_nr_ports, +				 &portdev->config.max_nr_ports) == 0) {  		multiport = true;  	} @@ -2142,7 +2139,7 @@ static struct virtio_device_id rproc_serial_id_table[] = {  static unsigned int rproc_serial_features[] = {  }; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  static int virtcons_freeze(struct virtio_device *vdev)  {  	struct ports_device *portdev; @@ -2220,7 +2217,7 @@ static struct virtio_driver virtio_console = {  	.probe =	virtcons_probe,  	.remove =	virtcons_remove,  	.config_changed = config_intr, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  	.freeze =	virtcons_freeze,  	.restore =	virtcons_restore,  #endif diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c index b3256ff0d426..d0a1d8a45c81 100644 --- a/drivers/lguest/lguest_device.c +++ b/drivers/lguest/lguest_device.c @@ -229,7 +229,7 @@ struct lguest_vq_info {   * make a hypercall.  We hand the physical address of the virtqueue so the Host   * knows which virtqueue we're talking about.   */ -static void lg_notify(struct virtqueue *vq) +static bool lg_notify(struct virtqueue *vq)  {  	/*  	 * We store our virtqueue information in the "priv" pointer of the @@ -238,6 +238,7 @@ static void lg_notify(struct virtqueue *vq)  	struct lguest_vq_info *lvq = vq->priv;  	hcall(LHCALL_NOTIFY, lvq->config.pfn << PAGE_SHIFT, 0, 0, 0); +	return true;  }  /* An extern declaration inside a C file is bad form.  Don't do it. */ diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index 516923926335..922a1acbf652 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c @@ -157,7 +157,7 @@ static void run_guest_once(struct lg_cpu *cpu, struct lguest_pages *pages)  	 * stack, then the address of this call.  This stack layout happens to  	 * exactly match the stack layout created by an interrupt...  	 */ -	asm volatile("pushf; lcall *lguest_entry" +	asm volatile("pushf; lcall *%4"  		     /*  		      * This is how we tell GCC that %eax ("a") and %ebx ("b")  		      * are changed by this routine.  The "=" means output. @@ -169,7 +169,9 @@ static void run_guest_once(struct lg_cpu *cpu, struct lguest_pages *pages)  		      * physical address of the Guest's top-level page  		      * directory.  		      */ -		     : "0"(pages), "1"(__pa(cpu->lg->pgdirs[cpu->cpu_pgd].pgdir)) +		     : "0"(pages),  +		       "1"(__pa(cpu->lg->pgdirs[cpu->cpu_pgd].pgdir)), +		       "m"(lguest_entry)  		     /*  		      * We tell gcc that all these registers could change,  		      * which means we don't have to save and restore them in diff --git a/drivers/net/caif/caif_virtio.c b/drivers/net/caif/caif_virtio.c index b9ed1288ce2d..985608634f8c 100644 --- a/drivers/net/caif/caif_virtio.c +++ b/drivers/net/caif/caif_virtio.c @@ -686,18 +686,19 @@ static int cfv_probe(struct virtio_device *vdev)  		goto err;  	/* Get the CAIF configuration from virtio config space, if available */ -#define GET_VIRTIO_CONFIG_OPS(_v, _var, _f) \ -	((_v)->config->get(_v, offsetof(struct virtio_caif_transf_config, _f), \ -			   &_var, \ -			   FIELD_SIZEOF(struct virtio_caif_transf_config, _f))) -  	if (vdev->config->get) { -		GET_VIRTIO_CONFIG_OPS(vdev, cfv->tx_hr, headroom); -		GET_VIRTIO_CONFIG_OPS(vdev, cfv->rx_hr, headroom); -		GET_VIRTIO_CONFIG_OPS(vdev, cfv->tx_tr, tailroom); -		GET_VIRTIO_CONFIG_OPS(vdev, cfv->rx_tr, tailroom); -		GET_VIRTIO_CONFIG_OPS(vdev, cfv->mtu, mtu); -		GET_VIRTIO_CONFIG_OPS(vdev, cfv->mru, mtu); +		virtio_cread(vdev, struct virtio_caif_transf_config, headroom, +			     &cfv->tx_hr); +		virtio_cread(vdev, struct virtio_caif_transf_config, headroom, +			     &cfv->rx_hr); +		virtio_cread(vdev, struct virtio_caif_transf_config, tailroom, +			     &cfv->tx_tr); +		virtio_cread(vdev, struct virtio_caif_transf_config, tailroom, +			     &cfv->rx_tr); +		virtio_cread(vdev, struct virtio_caif_transf_config, mtu, +			     &cfv->mtu); +		virtio_cread(vdev, struct virtio_caif_transf_config, mtu, +			     &cfv->mru);  	} else {  		cfv->tx_hr = CFV_DEF_HEADROOM;  		cfv->rx_hr = CFV_DEF_HEADROOM; diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index bf7c734259ad..cdc7c90a6a9e 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -591,7 +591,8 @@ static bool try_fill_recv(struct receive_queue *rq, gfp_t gfp)  	} while (rq->vq->num_free);  	if (unlikely(rq->num > rq->max))  		rq->max = rq->num; -	virtqueue_kick(rq->vq); +	if (unlikely(!virtqueue_kick(rq->vq))) +		return false;  	return !oom;  } @@ -797,7 +798,7 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)  	err = xmit_skb(sq, skb);  	/* This should not happen! */ -	if (unlikely(err)) { +	if (unlikely(err) || unlikely(!virtqueue_kick(sq->vq))) {  		dev->stats.tx_fifo_errors++;  		if (net_ratelimit())  			dev_warn(&dev->dev, @@ -806,7 +807,6 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)  		kfree_skb(skb);  		return NETDEV_TX_OK;  	} -	virtqueue_kick(sq->vq);  	/* Don't wait up for transmitted skbs to be freed. */  	skb_orphan(skb); @@ -865,12 +865,14 @@ static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd,  	BUG_ON(virtqueue_add_sgs(vi->cvq, sgs, out_num, in_num, vi, GFP_ATOMIC)  	       < 0); -	virtqueue_kick(vi->cvq); +	if (unlikely(!virtqueue_kick(vi->cvq))) +		return status == VIRTIO_NET_OK;  	/* Spin for a response, the kick causes an ioport write, trapping  	 * into the hypervisor, so the request should be handled immediately.  	 */ -	while (!virtqueue_get_buf(vi->cvq, &tmp)) +	while (!virtqueue_get_buf(vi->cvq, &tmp) && +	       !virtqueue_is_broken(vi->cvq))  		cpu_relax();  	return status == VIRTIO_NET_OK; @@ -898,8 +900,13 @@ static int virtnet_set_mac_address(struct net_device *dev, void *p)  			return -EINVAL;  		}  	} else if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC)) { -		vdev->config->set(vdev, offsetof(struct virtio_net_config, mac), -				  addr->sa_data, dev->addr_len); +		unsigned int i; + +		/* Naturally, this has an atomicity problem. */ +		for (i = 0; i < dev->addr_len; i++) +			virtio_cwrite8(vdev, +				       offsetof(struct virtio_net_config, mac) + +				       i, addr->sa_data[i]);  	}  	eth_commit_mac_addr_change(dev, p); @@ -1281,9 +1288,8 @@ static void virtnet_config_changed_work(struct work_struct *work)  	if (!vi->config_enable)  		goto done; -	if (virtio_config_val(vi->vdev, VIRTIO_NET_F_STATUS, -			      offsetof(struct virtio_net_config, status), -			      &v) < 0) +	if (virtio_cread_feature(vi->vdev, VIRTIO_NET_F_STATUS, +				 struct virtio_net_config, status, &v) < 0)  		goto done;  	if (v & VIRTIO_NET_S_ANNOUNCE) { @@ -1507,9 +1513,9 @@ static int virtnet_probe(struct virtio_device *vdev)  	u16 max_queue_pairs;  	/* Find if host supports multiqueue virtio_net device */ -	err = virtio_config_val(vdev, VIRTIO_NET_F_MQ, -				offsetof(struct virtio_net_config, -				max_virtqueue_pairs), &max_queue_pairs); +	err = virtio_cread_feature(vdev, VIRTIO_NET_F_MQ, +				   struct virtio_net_config, +				   max_virtqueue_pairs, &max_queue_pairs);  	/* We need at least 2 queue's */  	if (err || max_queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN || @@ -1561,9 +1567,11 @@ static int virtnet_probe(struct virtio_device *vdev)  	dev->vlan_features = dev->features;  	/* Configuration may specify what MAC to use.  Otherwise random. */ -	if (virtio_config_val_len(vdev, VIRTIO_NET_F_MAC, -				  offsetof(struct virtio_net_config, mac), -				  dev->dev_addr, dev->addr_len) < 0) +	if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC)) +		virtio_cread_bytes(vdev, +				   offsetof(struct virtio_net_config, mac), +				   dev->dev_addr, dev->addr_len); +	else  		eth_hw_addr_random(dev);  	/* Set up our device-specific information */ @@ -1704,7 +1712,7 @@ static void virtnet_remove(struct virtio_device *vdev)  	free_netdev(vi->dev);  } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  static int virtnet_freeze(struct virtio_device *vdev)  {  	struct virtnet_info *vi = vdev->priv; @@ -1795,7 +1803,7 @@ static struct virtio_driver virtio_net_driver = {  	.probe =	virtnet_probe,  	.remove =	virtnet_remove,  	.config_changed = virtnet_config_changed, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  	.freeze =	virtnet_freeze,  	.restore =	virtnet_restore,  #endif diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index b09c75c21b60..a34b50690b4e 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -30,7 +30,7 @@  #include "remoteproc_internal.h"  /* kick the remote processor, and let it know which virtqueue to poke at */ -static void rproc_virtio_notify(struct virtqueue *vq) +static bool rproc_virtio_notify(struct virtqueue *vq)  {  	struct rproc_vring *rvring = vq->priv;  	struct rproc *rproc = rvring->rvdev->rproc; @@ -39,6 +39,7 @@ static void rproc_virtio_notify(struct virtqueue *vq)  	dev_dbg(&rproc->dev, "kicking vq index: %d\n", notifyid);  	rproc->ops->kick(rproc, notifyid); +	return true;  }  /** diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index af2166fa5159..1abd0db29915 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -166,11 +166,15 @@ static void kvm_reset(struct virtio_device *vdev)   * make a hypercall.  We hand the address  of the virtqueue so the Host   * knows which virtqueue we're talking about.   */ -static void kvm_notify(struct virtqueue *vq) +static bool kvm_notify(struct virtqueue *vq)  { +	long rc;  	struct kvm_vqconfig *config = vq->priv; -	kvm_hypercall1(KVM_S390_VIRTIO_NOTIFY, config->address); +	rc = kvm_hypercall1(KVM_S390_VIRTIO_NOTIFY, config->address); +	if (rc < 0) +		return false; +	return true;  }  /* diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c index 779dc5136291..d6297176ab85 100644 --- a/drivers/s390/kvm/virtio_ccw.c +++ b/drivers/s390/kvm/virtio_ccw.c @@ -162,7 +162,7 @@ static inline long do_kvm_notify(struct subchannel_id schid,  	return __rc;  } -static void virtio_ccw_kvm_notify(struct virtqueue *vq) +static bool virtio_ccw_kvm_notify(struct virtqueue *vq)  {  	struct virtio_ccw_vq_info *info = vq->priv;  	struct virtio_ccw_device *vcdev; @@ -171,6 +171,9 @@ static void virtio_ccw_kvm_notify(struct virtqueue *vq)  	vcdev = to_vc_device(info->vq->vdev);  	ccw_device_get_schid(vcdev->cdev, &schid);  	info->cookie = do_kvm_notify(schid, vq->index, info->cookie); +	if (info->cookie < 0) +		return false; +	return true;  }  static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev, diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index 74b88efde6ad..c3173dced870 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -224,6 +224,9 @@ static void virtscsi_vq_done(struct virtio_scsi *vscsi,  		virtqueue_disable_cb(vq);  		while ((buf = virtqueue_get_buf(vq, &len)) != NULL)  			fn(vscsi, buf); + +		if (unlikely(virtqueue_is_broken(vq))) +			break;  	} while (!virtqueue_enable_cb(vq));  	spin_unlock_irqrestore(&virtscsi_vq->vq_lock, flags);  } @@ -710,19 +713,15 @@ static struct scsi_host_template virtscsi_host_template_multi = {  #define virtscsi_config_get(vdev, fld) \  	({ \  		typeof(((struct virtio_scsi_config *)0)->fld) __val; \ -		vdev->config->get(vdev, \ -				  offsetof(struct virtio_scsi_config, fld), \ -				  &__val, sizeof(__val)); \ +		virtio_cread(vdev, struct virtio_scsi_config, fld, &__val); \  		__val; \  	})  #define virtscsi_config_set(vdev, fld, val) \ -	(void)({ \ +	do { \  		typeof(((struct virtio_scsi_config *)0)->fld) __val = (val); \ -		vdev->config->set(vdev, \ -				  offsetof(struct virtio_scsi_config, fld), \ -				  &__val, sizeof(__val)); \ -	}) +		virtio_cwrite(vdev, struct virtio_scsi_config, fld, &__val); \ +	} while(0)  static void __virtscsi_set_affinity(struct virtio_scsi *vscsi, bool affinity)  { @@ -954,7 +953,7 @@ static void virtscsi_remove(struct virtio_device *vdev)  	scsi_host_put(shost);  } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  static int virtscsi_freeze(struct virtio_device *vdev)  {  	virtscsi_remove_vqs(vdev); @@ -988,7 +987,7 @@ static struct virtio_driver virtio_scsi_driver = {  	.id_table = id_table,  	.probe = virtscsi_probe,  	.scan = virtscsi_scan, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  	.freeze = virtscsi_freeze,  	.restore = virtscsi_restore,  #endif diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 1f572c00a1be..c444654fc33f 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -275,9 +275,8 @@ static inline s64 towards_target(struct virtio_balloon *vb)  	__le32 v;  	s64 target; -	vb->vdev->config->get(vb->vdev, -			      offsetof(struct virtio_balloon_config, num_pages), -			      &v, sizeof(v)); +	virtio_cread(vb->vdev, struct virtio_balloon_config, num_pages, &v); +  	target = le32_to_cpu(v);  	return target - vb->num_pages;  } @@ -286,9 +285,8 @@ static void update_balloon_size(struct virtio_balloon *vb)  {  	__le32 actual = cpu_to_le32(vb->num_pages); -	vb->vdev->config->set(vb->vdev, -			      offsetof(struct virtio_balloon_config, actual), -			      &actual, sizeof(actual)); +	virtio_cwrite(vb->vdev, struct virtio_balloon_config, num_pages, +		      &actual);  }  static int balloon(void *_vballoon) @@ -513,7 +511,7 @@ static void virtballoon_remove(struct virtio_device *vdev)  	kfree(vb);  } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  static int virtballoon_freeze(struct virtio_device *vdev)  {  	struct virtio_balloon *vb = vdev->priv; @@ -556,7 +554,7 @@ static struct virtio_driver virtio_balloon_driver = {  	.probe =	virtballoon_probe,  	.remove =	virtballoon_remove,  	.config_changed = virtballoon_changed, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  	.freeze	=	virtballoon_freeze,  	.restore =	virtballoon_restore,  #endif diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index 1ba0d6831015..c600ccfd6922 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -219,13 +219,14 @@ static void vm_reset(struct virtio_device *vdev)  /* Transport interface */  /* the notify function used when creating a virt queue */ -static void vm_notify(struct virtqueue *vq) +static bool vm_notify(struct virtqueue *vq)  {  	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vq->vdev);  	/* We write the queue's selector into the notification register to  	 * signal the other end */  	writel(vq->index, vm_dev->base + VIRTIO_MMIO_QUEUE_NOTIFY); +	return true;  }  /* Notify all virtqueues on an interrupt. */ @@ -470,7 +471,7 @@ static int virtio_mmio_probe(struct platform_device *pdev)  	/* Check magic value */  	magic = readl(vm_dev->base + VIRTIO_MMIO_MAGIC_VALUE); -	if (memcmp(&magic, "virt", 4) != 0) { +	if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) {  		dev_warn(&pdev->dev, "Wrong magic value 0x%08lx!\n", magic);  		return -ENODEV;  	} diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index 98917fc872a4..a37c69941d30 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -197,13 +197,14 @@ static void vp_reset(struct virtio_device *vdev)  }  /* the notify function used when creating a virt queue */ -static void vp_notify(struct virtqueue *vq) +static bool vp_notify(struct virtqueue *vq)  {  	struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev);  	/* we write the queue's selector into the notification register to  	 * signal the other end */  	iowrite16(vq->index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_NOTIFY); +	return true;  }  /* Handle a configuration change: Tell driver if it wants to know. */ diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 6b4a4db4404d..28b5338fff71 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -81,7 +81,7 @@ struct vring_virtqueue  	u16 last_used_idx;  	/* How to notify other side. FIXME: commonalize hcalls! */ -	void (*notify)(struct virtqueue *vq); +	bool (*notify)(struct virtqueue *vq);  #ifdef DEBUG  	/* They're supposed to lock for us. */ @@ -173,6 +173,8 @@ static inline int vring_add_indirect(struct vring_virtqueue *vq,  	head = vq->free_head;  	vq->vring.desc[head].flags = VRING_DESC_F_INDIRECT;  	vq->vring.desc[head].addr = virt_to_phys(desc); +	/* kmemleak gives a false positive, as it's hidden by virt_to_phys */ +	kmemleak_ignore(desc);  	vq->vring.desc[head].len = i * sizeof(struct vring_desc);  	/* Update free pointer */ @@ -428,13 +430,22 @@ EXPORT_SYMBOL_GPL(virtqueue_kick_prepare);   * @vq: the struct virtqueue   *   * This does not need to be serialized. + * + * Returns false if host notify failed or queue is broken, otherwise true.   */ -void virtqueue_notify(struct virtqueue *_vq) +bool virtqueue_notify(struct virtqueue *_vq)  {  	struct vring_virtqueue *vq = to_vvq(_vq); +	if (unlikely(vq->broken)) +		return false; +  	/* Prod other side to tell it about changes. */ -	vq->notify(_vq); +	if (!vq->notify(_vq)) { +		vq->broken = true; +		return false; +	} +	return true;  }  EXPORT_SYMBOL_GPL(virtqueue_notify); @@ -447,11 +458,14 @@ EXPORT_SYMBOL_GPL(virtqueue_notify);   *   * Caller must ensure we don't call this with other virtqueue   * operations at the same time (except where noted). + * + * Returns false if kick failed, otherwise true.   */ -void virtqueue_kick(struct virtqueue *vq) +bool virtqueue_kick(struct virtqueue *vq)  {  	if (virtqueue_kick_prepare(vq)) -		virtqueue_notify(vq); +		return virtqueue_notify(vq); +	return true;  }  EXPORT_SYMBOL_GPL(virtqueue_kick); @@ -742,7 +756,7 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,  				      struct virtio_device *vdev,  				      bool weak_barriers,  				      void *pages, -				      void (*notify)(struct virtqueue *), +				      bool (*notify)(struct virtqueue *),  				      void (*callback)(struct virtqueue *),  				      const char *name)  { @@ -837,4 +851,12 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *_vq)  }  EXPORT_SYMBOL_GPL(virtqueue_get_vring_size); +bool virtqueue_is_broken(struct virtqueue *_vq) +{ +	struct vring_virtqueue *vq = to_vvq(_vq); + +	return vq->broken; +} +EXPORT_SYMBOL_GPL(virtqueue_is_broken); +  MODULE_LICENSE("GPL"); diff --git a/include/linux/virtio.h b/include/linux/virtio.h index 36d36cc89329..e4abb84199be 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -51,11 +51,11 @@ int virtqueue_add_sgs(struct virtqueue *vq,  		      void *data,  		      gfp_t gfp); -void virtqueue_kick(struct virtqueue *vq); +bool virtqueue_kick(struct virtqueue *vq);  bool virtqueue_kick_prepare(struct virtqueue *vq); -void virtqueue_notify(struct virtqueue *vq); +bool virtqueue_notify(struct virtqueue *vq);  void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len); @@ -73,6 +73,8 @@ void *virtqueue_detach_unused_buf(struct virtqueue *vq);  unsigned int virtqueue_get_vring_size(struct virtqueue *vq); +bool virtqueue_is_broken(struct virtqueue *vq); +  /**   * virtio_device - representation of a device using virtio   * @index: unique position on the virtio bus diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index 29b9104232b4..e8f8f71e843c 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h @@ -96,33 +96,6 @@ static inline bool virtio_has_feature(const struct virtio_device *vdev,  	return test_bit(fbit, vdev->features);  } -/** - * virtio_config_val - look for a feature and get a virtio config entry. - * @vdev: the virtio device - * @fbit: the feature bit - * @offset: the type to search for. - * @v: a pointer to the value to fill in. - * - * The return value is -ENOENT if the feature doesn't exist.  Otherwise - * the config value is copied into whatever is pointed to by v. */ -#define virtio_config_val(vdev, fbit, offset, v) \ -	virtio_config_buf((vdev), (fbit), (offset), (v), sizeof(*v)) - -#define virtio_config_val_len(vdev, fbit, offset, v, len) \ -	virtio_config_buf((vdev), (fbit), (offset), (v), (len)) - -static inline int virtio_config_buf(struct virtio_device *vdev, -				    unsigned int fbit, -				    unsigned int offset, -				    void *buf, unsigned len) -{ -	if (!virtio_has_feature(vdev, fbit)) -		return -ENOENT; - -	vdev->config->get(vdev, offset, buf, len); -	return 0; -} -  static inline  struct virtqueue *virtio_find_single_vq(struct virtio_device *vdev,  					vq_callback_t *c, const char *n) @@ -162,5 +135,139 @@ int virtqueue_set_affinity(struct virtqueue *vq, int cpu)  	return 0;  } +/* Config space accessors. */ +#define virtio_cread(vdev, structname, member, ptr)			\ +	do {								\ +		/* Must match the member's type, and be integer */	\ +		if (!typecheck(typeof((((structname*)0)->member)), *(ptr))) \ +			(*ptr) = 1;					\ +									\ +		switch (sizeof(*ptr)) {					\ +		case 1:							\ +			*(ptr) = virtio_cread8(vdev,			\ +					       offsetof(structname, member)); \ +			break;						\ +		case 2:							\ +			*(ptr) = virtio_cread16(vdev,			\ +						offsetof(structname, member)); \ +			break;						\ +		case 4:							\ +			*(ptr) = virtio_cread32(vdev,			\ +						offsetof(structname, member)); \ +			break;						\ +		case 8:							\ +			*(ptr) = virtio_cread64(vdev,			\ +						offsetof(structname, member)); \ +			break;						\ +		default:						\ +			BUG();						\ +		}							\ +	} while(0) + +/* Config space accessors. */ +#define virtio_cwrite(vdev, structname, member, ptr)			\ +	do {								\ +		/* Must match the member's type, and be integer */	\ +		if (!typecheck(typeof((((structname*)0)->member)), *(ptr))) \ +			BUG_ON((*ptr) == 1);				\ +									\ +		switch (sizeof(*ptr)) {					\ +		case 1:							\ +			virtio_cwrite8(vdev,				\ +				       offsetof(structname, member),	\ +				       *(ptr));				\ +			break;						\ +		case 2:							\ +			virtio_cwrite16(vdev,				\ +					offsetof(structname, member),	\ +					*(ptr));			\ +			break;						\ +		case 4:							\ +			virtio_cwrite32(vdev,				\ +					offsetof(structname, member),	\ +					*(ptr));			\ +			break;						\ +		case 8:							\ +			virtio_cwrite64(vdev,				\ +					offsetof(structname, member),	\ +					*(ptr));			\ +			break;						\ +		default:						\ +			BUG();						\ +		}							\ +	} while(0) + +static inline u8 virtio_cread8(struct virtio_device *vdev, unsigned int offset) +{ +	u8 ret; +	vdev->config->get(vdev, offset, &ret, sizeof(ret)); +	return ret; +} + +static inline void virtio_cread_bytes(struct virtio_device *vdev, +				      unsigned int offset, +				      void *buf, size_t len) +{ +	vdev->config->get(vdev, offset, buf, len); +} + +static inline void virtio_cwrite8(struct virtio_device *vdev, +				  unsigned int offset, u8 val) +{ +	vdev->config->set(vdev, offset, &val, sizeof(val)); +} + +static inline u16 virtio_cread16(struct virtio_device *vdev, +				 unsigned int offset) +{ +	u16 ret; +	vdev->config->get(vdev, offset, &ret, sizeof(ret)); +	return ret; +} + +static inline void virtio_cwrite16(struct virtio_device *vdev, +				   unsigned int offset, u16 val) +{ +	vdev->config->set(vdev, offset, &val, sizeof(val)); +} + +static inline u32 virtio_cread32(struct virtio_device *vdev, +				 unsigned int offset) +{ +	u32 ret; +	vdev->config->get(vdev, offset, &ret, sizeof(ret)); +	return ret; +} + +static inline void virtio_cwrite32(struct virtio_device *vdev, +				   unsigned int offset, u32 val) +{ +	vdev->config->set(vdev, offset, &val, sizeof(val)); +} + +static inline u64 virtio_cread64(struct virtio_device *vdev, +				 unsigned int offset) +{ +	u64 ret; +	vdev->config->get(vdev, offset, &ret, sizeof(ret)); +	return ret; +} + +static inline void virtio_cwrite64(struct virtio_device *vdev, +				   unsigned int offset, u64 val) +{ +	vdev->config->set(vdev, offset, &val, sizeof(val)); +} + +/* Conditional config space accessors. */ +#define virtio_cread_feature(vdev, fbit, structname, member, ptr)	\ +	({								\ +		int _r = 0;						\ +		if (!virtio_has_feature(vdev, fbit))			\ +			_r = -ENOENT;					\ +		else							\ +			virtio_cread((vdev), structname, member, ptr);	\ +		_r;							\ +	})  #endif /* _LINUX_VIRTIO_CONFIG_H */ diff --git a/include/linux/virtio_ring.h b/include/linux/virtio_ring.h index b300787af8e0..67e06fe18c03 100644 --- a/include/linux/virtio_ring.h +++ b/include/linux/virtio_ring.h @@ -71,7 +71,7 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,  				      struct virtio_device *vdev,  				      bool weak_barriers,  				      void *pages, -				      void (*notify)(struct virtqueue *vq), +				      bool (*notify)(struct virtqueue *vq),  				      void (*callback)(struct virtqueue *vq),  				      const char *name);  void vring_del_virtqueue(struct virtqueue *vq); diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index 990afab2be1b..9c5a1aa34d12 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c @@ -544,9 +544,7 @@ static int p9_virtio_probe(struct virtio_device *vdev)  	chan->inuse = false;  	if (virtio_has_feature(vdev, VIRTIO_9P_MOUNT_TAG)) { -		vdev->config->get(vdev, -				offsetof(struct virtio_9p_config, tag_len), -				&tag_len, sizeof(tag_len)); +		virtio_cread(vdev, struct virtio_9p_config, tag_len, &tag_len);  	} else {  		err = -EINVAL;  		goto out_free_vq; @@ -556,8 +554,9 @@ static int p9_virtio_probe(struct virtio_device *vdev)  		err = -ENOMEM;  		goto out_free_vq;  	} -	vdev->config->get(vdev, offsetof(struct virtio_9p_config, tag), -			tag, tag_len); + +	virtio_cread_bytes(vdev, offsetof(struct virtio_9p_config, tag), +			   tag, tag_len);  	chan->tag = tag;  	chan->tag_len = tag_len;  	err = sysfs_create_file(&(vdev->dev.kobj), &dev_attr_mount_tag.attr); diff --git a/tools/virtio/virtio_test.c b/tools/virtio/virtio_test.c index da7a19558281..bdb71a26ae35 100644 --- a/tools/virtio/virtio_test.c +++ b/tools/virtio/virtio_test.c @@ -41,13 +41,14 @@ struct vdev_info {  	struct vhost_memory *mem;  }; -void vq_notify(struct virtqueue *vq) +bool vq_notify(struct virtqueue *vq)  {  	struct vq_info *info = vq->priv;  	unsigned long long v = 1;  	int r;  	r = write(info->kick, &v, sizeof v);  	assert(r == sizeof v); +	return true;  }  void vq_callback(struct virtqueue *vq) @@ -171,7 +172,8 @@ static void run_test(struct vdev_info *dev, struct vq_info *vq,  							 GFP_ATOMIC);  				if (likely(r == 0)) {  					++started; -					virtqueue_kick(vq->vq); +					if (unlikely(!virtqueue_kick(vq->vq)) +						r = -1;  				}  			} else  				r = -1; diff --git a/tools/virtio/vringh_test.c b/tools/virtio/vringh_test.c index d053ea40c001..14a4f4cab5b9 100644 --- a/tools/virtio/vringh_test.c +++ b/tools/virtio/vringh_test.c @@ -22,7 +22,7 @@ static u64 user_addr_offset;  #define RINGSIZE 256  #define ALIGN 4096 -static void never_notify_host(struct virtqueue *vq) +static bool never_notify_host(struct virtqueue *vq)  {  	abort();  } @@ -65,17 +65,22 @@ struct guest_virtio_device {  	unsigned long notifies;  }; -static void parallel_notify_host(struct virtqueue *vq) +static bool parallel_notify_host(struct virtqueue *vq)  { +	int rc;  	struct guest_virtio_device *gvdev;  	gvdev = container_of(vq->vdev, struct guest_virtio_device, vdev); -	write(gvdev->to_host_fd, "", 1); +	rc = write(gvdev->to_host_fd, "", 1); +	if (rc < 0) +		return false;  	gvdev->notifies++; +	return true;  } -static void no_notify_host(struct virtqueue *vq) +static bool no_notify_host(struct virtqueue *vq)  { +	return true;  }  #define NUM_XFERS (10000000)  | 
