Merge tag 'virtio-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 15 Nov 2013 04:28:47 +0000 (13:28 +0900)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 15 Nov 2013 04:28:47 +0000 (13:28 +0900)
Pull virtio updates from Rusty Russell:
 "Nothing really exciting: some groundwork for changing virtio endian,
  and some robustness fixes for broken virtio devices, plus minor
  tweaks"

* tag 'virtio-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux:
  virtio_scsi: verify if queue is broken after virtqueue_get_buf()
  x86, asmlinkage, lguest: Pass in globals into assembler statement
  virtio: mmio: fix signature checking for BE guests
  virtio_ring: adapt to notify() returning bool
  virtio_net: verify if queue is broken after virtqueue_get_buf()
  virtio_console: verify if queue is broken after virtqueue_get_buf()
  virtio_blk: verify if queue is broken after virtqueue_get_buf()
  virtio_ring: add new function virtqueue_is_broken()
  virtio_test: verify if virtqueue_kick() succeeded
  virtio_net: verify if virtqueue_kick() succeeded
  virtio_ring: let virtqueue_{kick()/notify()} return a bool
  virtio_ring: change host notification API
  virtio_config: remove virtio_config_val
  virtio: use size-based config accessors.
  virtio_config: introduce size-based accessors.
  virtio_ring: plug kmemleak false positive.
  virtio: pm: use CONFIG_PM_SLEEP instead of CONFIG_PM

21 files changed:
drivers/block/virtio_blk.c
drivers/char/hw_random/virtio-rng.c
drivers/char/virtio_console.c
drivers/lguest/lguest_device.c
drivers/lguest/x86/core.c
drivers/net/caif/caif_virtio.c
drivers/net/virtio_net.c
drivers/remoteproc/remoteproc_virtio.c
drivers/s390/kvm/kvm_virtio.c
drivers/s390/kvm/virtio_ccw.c
drivers/scsi/virtio_scsi.c
drivers/virtio/virtio_balloon.c
drivers/virtio/virtio_mmio.c
drivers/virtio/virtio_pci.c
drivers/virtio/virtio_ring.c
include/linux/virtio.h
include/linux/virtio_config.h
include/linux/virtio_ring.h
net/9p/trans_virtio.c
tools/virtio/virtio_test.c
tools/virtio/vringh_test.c

index 5cdf88b..f3be496 100644 (file)
@@ -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
index ef46a9c..c12398d 100644 (file)
@@ -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
index b79cf3e..feea87c 100644 (file)
@@ -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
index b3256ff..d0a1d8a 100644 (file)
@@ -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. */
index 5169239..922a1ac 100644 (file)
@@ -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
index b9ed128..9856086 100644 (file)
@@ -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;
index bf7c734..cdc7c90 100644 (file)
@@ -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
index b09c75c..a34b506 100644 (file)
@@ -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;
 }
 
 /**
index af2166f..1abd0db 100644 (file)
@@ -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;
 }
 
 /*
index 779dc51..d629717 100644 (file)
@@ -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,
index 74b88ef..c3173dc 100644 (file)
@@ -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
index 1f572c0..c444654 100644 (file)
@@ -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
index 1ba0d68..c600ccf 100644 (file)
@@ -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;
        }
index 98917fc..a37c699 100644 (file)
@@ -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. */
index 6b4a4db..28b5338 100644 (file)
@@ -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");
index 36d36cc..e4abb84 100644 (file)
@@ -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
index 29b9104..e8f8f71 100644 (file)
@@ -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 */
index b300787..67e06fe 100644 (file)
@@ -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);
index 990afab..9c5a1aa 100644 (file)
@@ -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);
index da7a195..bdb71a2 100644 (file)
@@ -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;
index d053ea4..14a4f4c 100644 (file)
@@ -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)