Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 14 Mar 2013 14:13:43 -0600
From:      Chris Torek <torek@torek.net>
To:        Peter Grehan <grehan@freebsd.org>
Cc:        freebsd-virtualization@freebsd.org
Subject:   Re: trivial improvement for usr.sbin/bhyve/pci_virtio_block.c
Message-ID:  <201303142013.r2EKDhAE081454@elf.torek.net>
In-Reply-To: Your message of "Thu, 28 Feb 2013 18:15:54 -0700." <5130014A.3030607@freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
>> I was looking through the bhyve code and noticed an obvious
>> easy (if trivial) code improvement.
>>
>> Tested "standalone" rather than inside bhyve (with both gcc and
>> clang, on FreeBSD 9.0).
>
>  Thanks; I'll apply it.
>
>> Not sure where/how diffs should go, so I figured I would send this
>> here as a test. :-)
>
>  This is as good a place as any :)
>
>later,
>
>Peter.

I looked at the svn commit and there's a glitch...

I only sent you one file (pci_virtio_block.c) of the two (not
having realized the code was duplicated in pci_virtio_net.c).  You
applied the change to both files but missed something:

	Index: pci_virtio_block.c
	===================================================================
	--- pci_virtio_block.c	(revision 247865)
	+++ pci_virtio_block.c	(revision 247871)
	@@ -164,14 +164,19 @@
	 static int
	 hq_num_avail(struct vring_hqueue *hq)
	 {
	-	int ndesc;
	+	uint16_t ndesc;

This change (to use uint16_t) is important.
 
	-	if (*hq->hq_avail_idx >= hq->hq_cur_aidx)
	-		ndesc = *hq->hq_avail_idx - hq->hq_cur_aidx;
	-	else
	-		ndesc = UINT16_MAX - hq->hq_cur_aidx + *hq->hq_avail_idx + 1;
	+	/*
	+	 * We're just computing (a-b) in GF(216).

(minor: this should read "2 caret 16" or maybe "2**16", presumably
some mail software ate it?)

	+	 *
	+	 * The only glitch here is that in standard C,
	+	 * uint16_t promotes to (signed) int when int has
	+	 * more than 16 bits (pretty much always now), so
	+	 * we have to force it back to unsigned.
	+	 */
	+	ndesc = (unsigned)*hq->hq_avail_idx - (unsigned)hq->hq_cur_aidx;

The trick here is that if, e.g., avail is 3 and cur is 29,
we get 3 - 29 = 0xffffffe6, which should be considered "the
same as" 65510.  The original calculation did (via the else
half):

    ndesc = UINT16_MAX - hq->hq_cur_aidx + *hq->hq_avail_idx + 1
          = 65535 - 29 + 3 + 1

which is 65510.  The new one computes (in 32-bit unsigned)
0xffffffe6 but then assigns the result to a 16-bit unsigned
temporary (i.e., ndesc), chopping off the unwanted high bits
and resulting in 0xffe6 = 65510.

	-	assert(ndesc >= 0 && ndesc <= hq->hq_size);
	+	assert(ndesc <= hq->hq_size);
	 
		return (ndesc);
	 }

	Index: pci_virtio_net.c
	===================================================================
	--- pci_virtio_net.c	(revision 247865)
	+++ pci_virtio_net.c	(revision 247871)
	@@ -172,12 +172,17 @@
	 {
		int ndesc;

Uh oh, here we (still) have a regular plain "int"...

	-	if (*hq->hq_avail_idx >= hq->hq_cur_aidx)
	-		ndesc = *hq->hq_avail_idx - hq->hq_cur_aidx;
	-	else
	-		ndesc = UINT16_MAX - hq->hq_cur_aidx + *hq->hq_avail_idx + 1;
	+	/*
	+	 * We're just computing (a-b) in GF(216).
	+	 *
	+	 * The only glitch here is that in standard C,
	+	 * uint16_t promotes to (signed) int when int has
	+	 * more than 16 bits (pretty much always now), so
	+	 * we have to force it back to unsigned.
	+	 */
	+	ndesc = (unsigned)*hq->hq_avail_idx - (unsigned)hq->hq_cur_aidx;

This time we'll do something like "3 - 29" in unsigned and get
0xffffffe6 as before, but then stick that in a (32 bit) int and
believe it means -26.

	-	assert(ndesc >= 0 && ndesc <= hq->hq_size);
	+	assert(ndesc <= hq->hq_size);

Without the >= 0 part of the assert, we'll miss the underflow
since ndesc is signed, and hq_size is uint16_t and will widen
to (plain signed) int here.

(Of course it's more typical to have, e.g., not 3 and 29 but
0xfffc = 65532 and 24, where the subtraction will produce
-65508 when we wanted 28.  I'd have to look closely at the
rest of the code to see what happens after that.)

		return (ndesc);
	 }

The comment might be misleading -- the trick is to compute the
result in two steps, first in GF(2**{anything >= 16}), then
*reduce* it (via assigment to uint16_t) to GF(2**16).  (I used
"**" this time instead of ^, just to be different :-) )

Chris



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201303142013.r2EKDhAE081454>