From: Rusty Russell <rusty@rustcorp.com.au>
To: Paolo Bonzini <pbonzini@redhat.com>,
"Michael S. Tsirkin" <mst@redhat.com>
Cc: virtualization@lists.linux-foundation.org
Subject: Re: [PATCH] virtio-spec: add field for scsi command size
Date: Thu, 20 Jun 2013 12:10:35 +0930 [thread overview]
Message-ID: <8761x9mrmk.fsf@rustcorp.com.au> (raw)
In-Reply-To: <51C16BC2.2060302@redhat.com>
Paolo Bonzini <pbonzini@redhat.com> writes:
> Il 19/06/2013 10:24, Michael S. Tsirkin ha scritto:
>>> > 2) We introduce VIRTIO_NET_F_ANY_LAYOUT and VIRTIO_BLK_F_ANY_LAYOUT
>>> > specifically for net and block (note the new names).
>
> So why not a transport feature? Is it just because the SCSI commands
> for virtio-blk also require a config space field? Sorry if I missed
> this upthread.
Mainly because I'm not sure that *all* devices are now safe. Are they?
I had a stress test half-written for this, pasted below.
Otherwise I'd be happy to do both: use feature 25 for
VIRTIO_F_ANY_LAYOUT and another feature bit for the virtio-blk layout
change.
Cheers,
Rusty.
virtio: CONFIG_VIRTIO_DEVICE_TORTURE
Virtio devices are not supposed to depend on the framing of the scatter-gather
lists, but various implementations did. Safeguard this in future by adding
an option to deliberately create perverse descriptors.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index 8d5bddb..99c0187 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -5,6 +5,22 @@ config VIRTIO
bus, such as CONFIG_VIRTIO_PCI, CONFIG_VIRTIO_MMIO, CONFIG_LGUEST,
CONFIG_RPMSG or CONFIG_S390_GUEST.
+config VIRTIO_TORTURE
+ bool "Virtio torture debugging"
+ depends on VIRTIO && DEBUG_KERNEL
+ help
+
+ This makes the virtio_ring implementation stress-test
+ devices. In particularly, creatively change the format of
+ requests to make sure that devices are properly implemented,
+ as well as implement various checks to ensure drivers are
+ correct. This will make your virtual machine slow *and*
+ unreliable! Say N.
+
+ Put virtio_ring.device_torture to your boot commandline to
+ torture devices (otherwise only simply sanity checks are
+ done).
+
menu "Virtio drivers"
config VIRTIO_PCI
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index e82821a..6e5271c 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -45,35 +45,6 @@
#define virtio_wmb(vq) wmb()
#endif
-#ifdef DEBUG
-/* For development, we want to crash whenever the ring is screwed. */
-#define BAD_RING(_vq, fmt, args...) \
- do { \
- dev_err(&(_vq)->vq.vdev->dev, \
- "%s:"fmt, (_vq)->vq.name, ##args); \
- BUG(); \
- } while (0)
-/* Caller is supposed to guarantee no reentry. */
-#define START_USE(_vq) \
- do { \
- if ((_vq)->in_use) \
- panic("%s:in_use = %i\n", \
- (_vq)->vq.name, (_vq)->in_use); \
- (_vq)->in_use = __LINE__; \
- } while (0)
-#define END_USE(_vq) \
- do { BUG_ON(!(_vq)->in_use); (_vq)->in_use = 0; } while(0)
-#else
-#define BAD_RING(_vq, fmt, args...) \
- do { \
- dev_err(&_vq->vq.vdev->dev, \
- "%s:"fmt, (_vq)->vq.name, ##args); \
- (_vq)->broken = true; \
- } while (0)
-#define START_USE(vq)
-#define END_USE(vq)
-#endif
-
struct indirect_cache {
unsigned int max;
struct vring_desc *cache;
@@ -109,7 +80,7 @@ struct vring_virtqueue
/* How to notify other side. FIXME: commonalize hcalls! */
void (*notify)(struct virtqueue *vq);
-#ifdef DEBUG
+#ifdef CONFIG_VIRTIO_TORTURE
/* They're supposed to lock for us. */
unsigned int in_use;
@@ -134,6 +105,200 @@ static inline struct indirect_cache *indirect_cache(struct vring_virtqueue *vq)
return (struct indirect_cache *)&vq->data[vq->vring.num];
}
+#ifdef CONFIG_VIRTIO_TORTURE
+/* For development, we want to crash whenever the ring is screwed. */
+#define BAD_RING(_vq, fmt, args...) \
+ do { \
+ dev_err(&(_vq)->vq.vdev->dev, \
+ "%s:"fmt, (_vq)->vq.name, ##args); \
+ BUG(); \
+ } while (0)
+/* Caller is supposed to guarantee no reentry. */
+#define START_USE(_vq) \
+ do { \
+ if ((_vq)->in_use) \
+ panic("%s:in_use = %i\n", \
+ (_vq)->vq.name, (_vq)->in_use); \
+ (_vq)->in_use = __LINE__; \
+ } while (0)
+#define END_USE(_vq) \
+ do { BUG_ON(!(_vq)->in_use); (_vq)->in_use = 0; } while(0)
+
+static bool device_torture;
+module_param(device_torture, bool, 0644);
+
+struct torture {
+ unsigned int orig_out, orig_in;
+ void *orig_data;
+ struct scatterlist sg[4];
+ struct scatterlist orig_sg[];
+};
+
+static unsigned tot_len(struct scatterlist sg[], unsigned num)
+{
+ unsigned len, i;
+
+ for (len = 0, i = 0; i < num; i++)
+ len += sg[i].length;
+
+ return len;
+}
+
+static void copy_sg_data(const struct scatterlist *dst, unsigned dnum,
+ const struct scatterlist *src, unsigned snum)
+{
+ unsigned len;
+ struct scatterlist s, d;
+
+ s = *src;
+ d = *dst;
+
+ while (snum && dnum) {
+ len = min(s.length, d.length);
+ memcpy(sg_virt(&d), sg_virt(&s), len);
+ d.offset += len;
+ d.length -= len;
+ s.offset += len;
+ s.length -= len;
+ if (!s.length) {
+ BUG_ON(snum == 0);
+ src++;
+ snum--;
+ s = *src;
+ }
+ if (!d.length) {
+ BUG_ON(dnum == 0);
+ dst++;
+ dnum--;
+ d = *dst;
+ }
+ }
+}
+
+static bool torture_replace(struct scatterlist **sg,
+ unsigned int *out,
+ unsigned int *in,
+ void **data,
+ gfp_t gfp)
+{
+ static size_t seed;
+ struct torture *t;
+ unsigned long outlen, inlen, ourseed, len1;
+ void *buf;
+
+ if (!device_torture)
+ return true;
+
+ outlen = tot_len(*sg, *out);
+ inlen = tot_len(*sg + *out, *in);
+
+ /* This will break horribly on large block requests. */
+ t = kmalloc(sizeof(*t) + (*out + *in) * sizeof(t->orig_sg[1])
+ + outlen + 1 + inlen + 1, gfp);
+ if (!t)
+ return false;
+
+ sg_init_table(t->sg, 4);
+ buf = &t->orig_sg[*out + *in];
+
+ memcpy(t->orig_sg, *sg, sizeof(**sg) * (*out + *in));
+ t->orig_out = *out;
+ t->orig_in = *in;
+ t->orig_data = *data;
+ *data = t;
+
+ ourseed = ACCESS_ONCE(seed);
+ seed++;
+
+ *sg = t->sg;
+ if (outlen) {
+ /* Split outbuf into two parts, one byte apart. */
+ *out = 2;
+ len1 = ourseed % (outlen + 1);
+ sg_set_buf(&t->sg[0], buf, len1);
+ buf += len1 + 1;
+ sg_set_buf(&t->sg[1], buf, outlen - len1);
+ buf += outlen - len1;
+ copy_sg_data(t->sg, *out, t->orig_sg, t->orig_out);
+ }
+
+ if (inlen) {
+ /* Split inbuf into two parts, one byte apart. */
+ *in = 2;
+ len1 = ourseed % (inlen + 1);
+ sg_set_buf(&t->sg[*out], buf, len1);
+ buf += len1 + 1;
+ sg_set_buf(&t->sg[*out + 1], buf, inlen - len1);
+ buf += inlen - len1;
+ }
+ return true;
+}
+
+static void *torture_done(struct torture *t)
+{
+ void *data;
+
+ if (!device_torture)
+ return t;
+
+ if (t->orig_in)
+ copy_sg_data(t->orig_sg + t->orig_out, t->orig_in,
+ t->sg + (t->orig_out ? 2 : 0), 2);
+
+ data = t->orig_data;
+ kfree(t);
+ return data;
+}
+
+static unsigned long tot_inlen(struct virtqueue *vq, unsigned int i)
+{
+ struct vring_desc *desc;
+ unsigned long len = 0;
+
+ if (vq->vring.desc[i].flags & VRING_DESC_F_INDIRECT) {
+ unsigned int num = vq->vring.desc[i].len / sizeof(*desc);
+ desc = phys_to_virt(vq->vring.desc[i].addr);
+
+ for (i = 0; i < num; i++) {
+ if (desc[i].flags & VRING_DESC_F_WRITE)
+ len += desc[i].flags.len;
+ }
+ } else {
+ desc = vq->vring.desc;
+ while (desc[i].flags & VRING_DESC_F_NEXT) {
+ if (desc[i].flags & VRING_DESC_F_WRITE)
+ len += desc[i].flags.len;
+ i = desc[i].next;
+ }
+ }
+
+ return len;
+}
+#else
+static bool torture_replace(struct scatterlist **sg,
+ unsigned int *out,
+ unsigned int *in,
+ void **data,
+ gfp_t gfp)
+{
+ return true;
+}
+
+static void *torture_done(void *data)
+{
+ return data;
+}
+
+#define BAD_RING(_vq, fmt, args...) \
+ do { \
+ dev_err(&_vq->vq.vdev->dev, \
+ "%s:"fmt, (_vq)->vq.name, ##args); \
+ (_vq)->broken = true; \
+ } while (0)
+#define START_USE(vq)
+#define END_USE(vq)
+#endif /* CONFIG_VIRTIO_TORTURE */
+
/* Set up an indirect table of descriptors and add it to the queue. */
static int vring_add_indirect(struct vring_virtqueue *vq,
struct scatterlist sg[],
@@ -228,7 +393,10 @@ int virtqueue_add_buf(struct virtqueue *_vq,
BUG_ON(data == NULL);
-#ifdef DEBUG
+ if (!torture_replace(&sg, &out, &in, &data, gfp))
+ return -ENOMEM;
+
+#ifdef CONFIG_VIRTIO_TORTURE
{
ktime_t now = ktime_get();
@@ -261,6 +429,7 @@ int virtqueue_add_buf(struct virtqueue *_vq,
if (out)
vq->notify(&vq->vq);
END_USE(vq);
+ torture_done(data);
return -ENOSPC;
}
@@ -341,7 +510,7 @@ bool virtqueue_kick_prepare(struct virtqueue *_vq)
new = vq->vring.avail->idx;
vq->num_added = 0;
-#ifdef DEBUG
+#ifdef CONFIG_VIRTIO_TORTURE
if (vq->last_add_time_valid) {
WARN_ON(ktime_to_ms(ktime_sub(ktime_get(),
vq->last_add_time)) > 100);
@@ -474,6 +643,14 @@ void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len)
return NULL;
}
+#ifdef CONFIG_VIRTIO_TORTURE
+ if (unlikely(tot_inlen(vq, i) < *len)) {
+ BAD_RING(vq, "id %u: %u > %u used!\n",
+ i, *len, tot_inlen(vq, i));
+ return NULL;
+ }
+#endif
+
/* detach_buf clears data, so grab it now. */
ret = vq->data[i];
detach_buf(vq, i);
@@ -486,12 +663,14 @@ void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len)
virtio_mb(vq);
}
-#ifdef DEBUG
+#ifdef CONFIG_VIRTIO_TORTURE
vq->last_add_time_valid = false;
+ BUG_ON(*len >
+
#endif
END_USE(vq);
- return ret;
+ return torture_done(ret);
}
EXPORT_SYMBOL_GPL(virtqueue_get_buf);
@@ -683,7 +862,7 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,
vq->last_used_idx = 0;
vq->num_added = 0;
list_add_tail(&vq->vq.list, &vdev->vqs);
-#ifdef DEBUG
+#ifdef CONFIG_VIRTIO_TORTURE
vq->in_use = false;
vq->last_add_time_valid = false;
#endif
next prev parent reply other threads:[~2013-06-20 2:40 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-03-14 11:10 [PATCH] virtio-spec: add field for scsi command size Michael S. Tsirkin
2013-03-14 15:15 ` Paolo Bonzini
2013-03-14 17:39 ` Michael S. Tsirkin
2013-03-17 9:26 ` Michael S. Tsirkin
2013-06-13 4:42 ` Rusty Russell
2013-06-13 7:33 ` Michael S. Tsirkin
2013-06-13 8:02 ` Michael S. Tsirkin
2013-06-13 8:10 ` Michael S. Tsirkin
2013-06-17 6:37 ` Michael S. Tsirkin
2013-06-19 4:46 ` Rusty Russell
2013-06-19 8:24 ` Michael S. Tsirkin
2013-06-19 8:28 ` Paolo Bonzini
2013-06-19 9:21 ` Michael S. Tsirkin
2013-06-20 2:40 ` Rusty Russell [this message]
2013-06-20 9:26 ` Paolo Bonzini
2013-06-30 23:47 ` Rusty Russell
2013-07-01 11:57 ` Paolo Bonzini
2013-07-02 6:04 ` Rusty Russell
2013-07-04 7:49 ` Michael S. Tsirkin
2013-07-07 11:31 ` Michael S. Tsirkin
2013-07-08 1:21 ` Rusty Russell
2013-07-08 5:44 ` Michael S. Tsirkin
2013-07-09 1:19 ` Rusty Russell
2013-07-04 7:39 ` Michael S. Tsirkin
2013-07-04 9:05 ` Paolo Bonzini
2013-07-08 4:28 ` Rusty Russell
2013-07-04 7:38 ` Michael S. Tsirkin
2013-06-20 2:45 ` Rusty Russell
2013-03-18 21:47 ` Michael S. Tsirkin
2013-03-19 1:21 ` Rusty Russell
2013-03-19 7:58 ` Michael S. Tsirkin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=8761x9mrmk.fsf@rustcorp.com.au \
--to=rusty@rustcorp.com.au \
--cc=mst@redhat.com \
--cc=pbonzini@redhat.com \
--cc=virtualization@lists.linux-foundation.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.