All of lore.kernel.org
 help / color / mirror / Atom feed
From: Paolo Bonzini <pbonzini@redhat.com>
To: Rusty Russell <rusty@rustcorp.com.au>
Cc: virtualization@lists.linux-foundation.org,
	"Michael S. Tsirkin" <mst@redhat.com>
Subject: Re: [PATCH] virtio-spec: add field for scsi command size
Date: Thu, 20 Jun 2013 11:26:21 +0200	[thread overview]
Message-ID: <51C2CABD.70000@redhat.com> (raw)
In-Reply-To: <8761x9mrmk.fsf@rustcorp.com.au>

Il 20/06/2013 04:40, Rusty Russell ha scritto:
> 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?

virtio-scsi's implementation in QEMU is not safe (been delaying that for
too long, sorry), but the spec is safe.

Paolo

> 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
> 

  reply	other threads:[~2013-06-20  9:26 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
2013-06-20  9:26                     ` Paolo Bonzini [this message]
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=51C2CABD.70000@redhat.com \
    --to=pbonzini@redhat.com \
    --cc=mst@redhat.com \
    --cc=rusty@rustcorp.com.au \
    --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.