diff mbox

[v3] kvm: Use a bitmap for tracking used GSIs

Message ID 20090512220142.5663.72948.stgit@dl380g6-3.ned.telco.ned.telco (mailing list archive)
State New, archived
Headers show

Commit Message

Alex Williamson May 12, 2009, 10:07 p.m. UTC
We're currently using a counter to track the most recent GSI we've
handed out.  This quickly hits KVM_MAX_IRQ_ROUTES when using device
assignment with a driver that regularly toggles the MSI enable bit.
This can mean only a few minutes of usable run time.  Instead, track
used GSIs in a bitmap.

Signed-off-by: Alex Williamson <alex.williamson@hp.com>
---

 v2: Added mutex to protect gsi bitmap
 v3: Updated for comments from Michael Tsirkin
     No longer depends on "[PATCH] kvm: device-assignment: Catch GSI overflow"

 hw/device-assignment.c  |    4 ++
 kvm/libkvm/kvm-common.h |    4 ++
 kvm/libkvm/libkvm.c     |   83 +++++++++++++++++++++++++++++++++++++++++------
 kvm/libkvm/libkvm.h     |   10 ++++++
 4 files changed, 88 insertions(+), 13 deletions(-)


--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Yang, Sheng May 13, 2009, 3:30 a.m. UTC | #1
On Wednesday 13 May 2009 06:07:15 Alex Williamson wrote:
> We're currently using a counter to track the most recent GSI we've
> handed out.  This quickly hits KVM_MAX_IRQ_ROUTES when using device
> assignment with a driver that regularly toggles the MSI enable bit.
> This can mean only a few minutes of usable run time.  Instead, track
> used GSIs in a bitmap.
>
> Signed-off-by: Alex Williamson <alex.williamson@hp.com>
> ---
>
>  v2: Added mutex to protect gsi bitmap
>  v3: Updated for comments from Michael Tsirkin
>      No longer depends on "[PATCH] kvm: device-assignment: Catch GSI
> overflow"
>
>  hw/device-assignment.c  |    4 ++
>  kvm/libkvm/kvm-common.h |    4 ++
>  kvm/libkvm/libkvm.c     |   83
> +++++++++++++++++++++++++++++++++++++++++------ kvm/libkvm/libkvm.h     |  
> 10 ++++++
>  4 files changed, 88 insertions(+), 13 deletions(-)
>
> diff --git a/hw/device-assignment.c b/hw/device-assignment.c
> index a7365c8..a6cc9b9 100644
> --- a/hw/device-assignment.c
> +++ b/hw/device-assignment.c
> @@ -561,8 +561,10 @@ static void free_dev_irq_entries(AssignedDevice *dev)
>  {
>      int i;
>
> -    for (i = 0; i < dev->irq_entries_nr; i++)
> +    for (i = 0; i < dev->irq_entries_nr; i++) {
>          kvm_del_routing_entry(kvm_context, &dev->entry[i]);
> +        kvm_free_irq_route_gsi(kvm_context, dev->entry[i].gsi);
> +    }
>      free(dev->entry);
>      dev->entry = NULL;
>      dev->irq_entries_nr = 0;
> diff --git a/kvm/libkvm/kvm-common.h b/kvm/libkvm/kvm-common.h
> index 591fb53..4b3cb51 100644
> --- a/kvm/libkvm/kvm-common.h
> +++ b/kvm/libkvm/kvm-common.h
> @@ -66,8 +66,10 @@ struct kvm_context {
>  #ifdef KVM_CAP_IRQ_ROUTING
>  	struct kvm_irq_routing *irq_routes;
>  	int nr_allocated_irq_routes;
> +	void *used_gsi_bitmap;
> +	int max_gsi;
> +	pthread_mutex_t gsi_mutex;
>  #endif
> -	int max_used_gsi;
>  };
>
>  int kvm_alloc_kernel_memory(kvm_context_t kvm, unsigned long memory,
> diff --git a/kvm/libkvm/libkvm.c b/kvm/libkvm/libkvm.c
> index ba0a5d1..3d7ab75 100644
> --- a/kvm/libkvm/libkvm.c
> +++ b/kvm/libkvm/libkvm.c
> @@ -35,6 +35,7 @@
>  #include <errno.h>
>  #include <sys/ioctl.h>
>  #include <inttypes.h>
> +#include <pthread.h>
>  #include "libkvm.h"
>
>  #if defined(__x86_64__) || defined(__i386__)
> @@ -65,6 +66,8 @@
>  int kvm_abi = EXPECTED_KVM_API_VERSION;
>  int kvm_page_size;
>
> +static inline void set_bit(uint32_t *buf, unsigned int bit);
> +
>  struct slot_info {
>  	unsigned long phys_addr;
>  	unsigned long len;
> @@ -286,6 +289,9 @@ kvm_context_t kvm_init(struct kvm_callbacks *callbacks,
>  	int fd;
>  	kvm_context_t kvm;
>  	int r;
> +#ifdef KVM_CAP_IRQ_ROUTING
> +	int gsi_count, gsi_bytes, i;
> +#endif
>
>  	fd = open("/dev/kvm", O_RDWR);
>  	if (fd == -1) {
> @@ -322,6 +328,27 @@ kvm_context_t kvm_init(struct kvm_callbacks
> *callbacks, kvm->dirty_pages_log_all = 0;
>  	kvm->no_irqchip_creation = 0;
>  	kvm->no_pit_creation = 0;
> +#ifdef KVM_CAP_IRQ_ROUTING
> +	pthread_mutex_init(&kvm->gsi_mutex, NULL);
> +
> +	gsi_count = kvm_get_gsi_count(kvm);
> +	/* Round up so we can search ints using ffs */
> +	gsi_bytes = (gsi_count + 31) / 32;

CMIW, should it be gsi_bytes = (gsi_count + 7) / 8? This looks like bits-to-
int. 

> +	kvm->used_gsi_bitmap = malloc(gsi_bytes);
> +	if (!kvm->used_gsi_bitmap) {
> +		pthread_mutex_unlock(&kvm->gsi_mutex);
> +		goto out_close;
> +	}
> +	memset(kvm->used_gsi_bitmap, 0, gsi_bytes);
> +	kvm->max_gsi = gsi_bytes * 8;

So max_gsi = gsi_count / 4?
Alex Williamson May 13, 2009, 3:42 a.m. UTC | #2
On Wed, 2009-05-13 at 11:30 +0800, Yang, Sheng wrote:
> On Wednesday 13 May 2009 06:07:15 Alex Williamson wrote:
> > We're currently using a counter to track the most recent GSI we've
> > handed out.  This quickly hits KVM_MAX_IRQ_ROUTES when using device
> > assignment with a driver that regularly toggles the MSI enable bit.
> > This can mean only a few minutes of usable run time.  Instead, track
> > used GSIs in a bitmap.
> >
> > Signed-off-by: Alex Williamson <alex.williamson@hp.com>
> > ---
> >
> >  v2: Added mutex to protect gsi bitmap
> >  v3: Updated for comments from Michael Tsirkin
> >      No longer depends on "[PATCH] kvm: device-assignment: Catch GSI
> > overflow"
> >
> >  hw/device-assignment.c  |    4 ++
> >  kvm/libkvm/kvm-common.h |    4 ++
> >  kvm/libkvm/libkvm.c     |   83
> > +++++++++++++++++++++++++++++++++++++++++------ kvm/libkvm/libkvm.h     |  
> > 10 ++++++
> >  4 files changed, 88 insertions(+), 13 deletions(-)
> >
> > diff --git a/hw/device-assignment.c b/hw/device-assignment.c
> > index a7365c8..a6cc9b9 100644
> > --- a/hw/device-assignment.c
> > +++ b/hw/device-assignment.c
> > @@ -561,8 +561,10 @@ static void free_dev_irq_entries(AssignedDevice *dev)
> >  {
> >      int i;
> >
> > -    for (i = 0; i < dev->irq_entries_nr; i++)
> > +    for (i = 0; i < dev->irq_entries_nr; i++) {
> >          kvm_del_routing_entry(kvm_context, &dev->entry[i]);
> > +        kvm_free_irq_route_gsi(kvm_context, dev->entry[i].gsi);
> > +    }
> >      free(dev->entry);
> >      dev->entry = NULL;
> >      dev->irq_entries_nr = 0;
> > diff --git a/kvm/libkvm/kvm-common.h b/kvm/libkvm/kvm-common.h
> > index 591fb53..4b3cb51 100644
> > --- a/kvm/libkvm/kvm-common.h
> > +++ b/kvm/libkvm/kvm-common.h
> > @@ -66,8 +66,10 @@ struct kvm_context {
> >  #ifdef KVM_CAP_IRQ_ROUTING
> >  	struct kvm_irq_routing *irq_routes;
> >  	int nr_allocated_irq_routes;
> > +	void *used_gsi_bitmap;
> > +	int max_gsi;
> > +	pthread_mutex_t gsi_mutex;
> >  #endif
> > -	int max_used_gsi;
> >  };
> >
> >  int kvm_alloc_kernel_memory(kvm_context_t kvm, unsigned long memory,
> > diff --git a/kvm/libkvm/libkvm.c b/kvm/libkvm/libkvm.c
> > index ba0a5d1..3d7ab75 100644
> > --- a/kvm/libkvm/libkvm.c
> > +++ b/kvm/libkvm/libkvm.c
> > @@ -35,6 +35,7 @@
> >  #include <errno.h>
> >  #include <sys/ioctl.h>
> >  #include <inttypes.h>
> > +#include <pthread.h>
> >  #include "libkvm.h"
> >
> >  #if defined(__x86_64__) || defined(__i386__)
> > @@ -65,6 +66,8 @@
> >  int kvm_abi = EXPECTED_KVM_API_VERSION;
> >  int kvm_page_size;
> >
> > +static inline void set_bit(uint32_t *buf, unsigned int bit);
> > +
> >  struct slot_info {
> >  	unsigned long phys_addr;
> >  	unsigned long len;
> > @@ -286,6 +289,9 @@ kvm_context_t kvm_init(struct kvm_callbacks *callbacks,
> >  	int fd;
> >  	kvm_context_t kvm;
> >  	int r;
> > +#ifdef KVM_CAP_IRQ_ROUTING
> > +	int gsi_count, gsi_bytes, i;
> > +#endif
> >
> >  	fd = open("/dev/kvm", O_RDWR);
> >  	if (fd == -1) {
> > @@ -322,6 +328,27 @@ kvm_context_t kvm_init(struct kvm_callbacks
> > *callbacks, kvm->dirty_pages_log_all = 0;
> >  	kvm->no_irqchip_creation = 0;
> >  	kvm->no_pit_creation = 0;
> > +#ifdef KVM_CAP_IRQ_ROUTING
> > +	pthread_mutex_init(&kvm->gsi_mutex, NULL);
> > +
> > +	gsi_count = kvm_get_gsi_count(kvm);
> > +	/* Round up so we can search ints using ffs */
> > +	gsi_bytes = (gsi_count + 31) / 32;
> 
> CMIW, should it be gsi_bytes = (gsi_count + 7) / 8? This looks like bits-to-
> int. 

Oops, I missed a multiplier in there.  What you have would be correct
for rounding up to a byte, but we really want to round up to an int, so
I need to factor in a bytes/int, which gives me this:

gsi_bytes = ((gsi_count + 31) / 32) * 4;

Then the rest works out correctly.  Thanks,

Alex

> > +	kvm->used_gsi_bitmap = malloc(gsi_bytes);
> > +	if (!kvm->used_gsi_bitmap) {
> > +		pthread_mutex_unlock(&kvm->gsi_mutex);
> > +		goto out_close;
> > +	}
> > +	memset(kvm->used_gsi_bitmap, 0, gsi_bytes);
> > +	kvm->max_gsi = gsi_bytes * 8;
> 
> So max_gsi = gsi_count / 4?


--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Alex Williamson May 13, 2009, 4:10 a.m. UTC | #3
On Tue, 2009-05-12 at 21:42 -0600, Alex Williamson wrote:
> On Wed, 2009-05-13 at 11:30 +0800, Yang, Sheng wrote:
> > > +	kvm->used_gsi_bitmap = malloc(gsi_bytes);
> > > +	if (!kvm->used_gsi_bitmap) {
> > > +		pthread_mutex_unlock(&kvm->gsi_mutex);
> > > +		goto out_close;
> > > +	}
> > > +	memset(kvm->used_gsi_bitmap, 0, gsi_bytes);
> > > +	kvm->max_gsi = gsi_bytes * 8;
> > 
> > So max_gsi = gsi_count / 4?

kvm->max_gsi actually becomes the number of GSIs available in the
bitmap, which may be more than gsi_count if we rounded up.  We
preallocate GSIs between gsi_count and max_gsi to avoid using them.
This just lets us not need to special case testing whether a bit in the
last index is < gsi_count.  Am I overlooking anything here?  Thanks,

Alex


--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Yang, Sheng May 13, 2009, 4:15 a.m. UTC | #4
On Wednesday 13 May 2009 12:10:34 Alex Williamson wrote:
> On Tue, 2009-05-12 at 21:42 -0600, Alex Williamson wrote:
> > On Wed, 2009-05-13 at 11:30 +0800, Yang, Sheng wrote:
> > > > +	kvm->used_gsi_bitmap = malloc(gsi_bytes);
> > > > +	if (!kvm->used_gsi_bitmap) {
> > > > +		pthread_mutex_unlock(&kvm->gsi_mutex);
> > > > +		goto out_close;
> > > > +	}
> > > > +	memset(kvm->used_gsi_bitmap, 0, gsi_bytes);
> > > > +	kvm->max_gsi = gsi_bytes * 8;
> > >
> > > So max_gsi = gsi_count / 4?
>
> kvm->max_gsi actually becomes the number of GSIs available in the
> bitmap, which may be more than gsi_count if we rounded up.  We
> preallocate GSIs between gsi_count and max_gsi to avoid using them.
> This just lets us not need to special case testing whether a bit in the
> last index is < gsi_count.  Am I overlooking anything here?  Thanks,
>
Oh, I understand that, and I just follow the logic of last comment here(which 
"gsi_bytes = (gsi_count + 31) / 32" )... Sorry for confusion...
Michael S. Tsirkin May 13, 2009, 7:03 a.m. UTC | #5
On Tue, May 12, 2009 at 04:07:15PM -0600, Alex Williamson wrote:
> We're currently using a counter to track the most recent GSI we've
> handed out.  This quickly hits KVM_MAX_IRQ_ROUTES when using device
> assignment with a driver that regularly toggles the MSI enable bit.

BTW, dwhich driver does that? Any idea why?
Michael S. Tsirkin May 13, 2009, 7:04 a.m. UTC | #6
On Tue, May 12, 2009 at 04:07:15PM -0600, Alex Williamson wrote:
> We're currently using a counter to track the most recent GSI we've
> handed out.  This quickly hits KVM_MAX_IRQ_ROUTES when using device
> assignment with a driver that regularly toggles the MSI enable bit.
> This can mean only a few minutes of usable run time.  Instead, track
> used GSIs in a bitmap.
> 
> Signed-off-by: Alex Williamson <alex.williamson@hp.com>
> ---
> 
>  v2: Added mutex to protect gsi bitmap
>  v3: Updated for comments from Michael Tsirkin
>      No longer depends on "[PATCH] kvm: device-assignment: Catch GSI overflow"
> 
>  hw/device-assignment.c  |    4 ++
>  kvm/libkvm/kvm-common.h |    4 ++
>  kvm/libkvm/libkvm.c     |   83 +++++++++++++++++++++++++++++++++++++++++------
>  kvm/libkvm/libkvm.h     |   10 ++++++
>  4 files changed, 88 insertions(+), 13 deletions(-)
> 
> diff --git a/hw/device-assignment.c b/hw/device-assignment.c
> index a7365c8..a6cc9b9 100644
> --- a/hw/device-assignment.c
> +++ b/hw/device-assignment.c
> @@ -561,8 +561,10 @@ static void free_dev_irq_entries(AssignedDevice *dev)
>  {
>      int i;
>  
> -    for (i = 0; i < dev->irq_entries_nr; i++)
> +    for (i = 0; i < dev->irq_entries_nr; i++) {
>          kvm_del_routing_entry(kvm_context, &dev->entry[i]);
> +        kvm_free_irq_route_gsi(kvm_context, dev->entry[i].gsi);
> +    }
>      free(dev->entry);
>      dev->entry = NULL;
>      dev->irq_entries_nr = 0;
> diff --git a/kvm/libkvm/kvm-common.h b/kvm/libkvm/kvm-common.h
> index 591fb53..4b3cb51 100644
> --- a/kvm/libkvm/kvm-common.h
> +++ b/kvm/libkvm/kvm-common.h
> @@ -66,8 +66,10 @@ struct kvm_context {
>  #ifdef KVM_CAP_IRQ_ROUTING
>  	struct kvm_irq_routing *irq_routes;
>  	int nr_allocated_irq_routes;
> +	void *used_gsi_bitmap;
> +	int max_gsi;
> +	pthread_mutex_t gsi_mutex;
>  #endif
> -	int max_used_gsi;
>  };
>  
>  int kvm_alloc_kernel_memory(kvm_context_t kvm, unsigned long memory,
> diff --git a/kvm/libkvm/libkvm.c b/kvm/libkvm/libkvm.c
> index ba0a5d1..3d7ab75 100644
> --- a/kvm/libkvm/libkvm.c
> +++ b/kvm/libkvm/libkvm.c
> @@ -35,6 +35,7 @@
>  #include <errno.h>
>  #include <sys/ioctl.h>
>  #include <inttypes.h>
> +#include <pthread.h>
>  #include "libkvm.h"
>  
>  #if defined(__x86_64__) || defined(__i386__)
> @@ -65,6 +66,8 @@
>  int kvm_abi = EXPECTED_KVM_API_VERSION;
>  int kvm_page_size;
>  
> +static inline void set_bit(uint32_t *buf, unsigned int bit);
> +
>  struct slot_info {
>  	unsigned long phys_addr;
>  	unsigned long len;
> @@ -286,6 +289,9 @@ kvm_context_t kvm_init(struct kvm_callbacks *callbacks,
>  	int fd;
>  	kvm_context_t kvm;
>  	int r;
> +#ifdef KVM_CAP_IRQ_ROUTING

Let's kill all these ifdefs. Or at least, let's not add them.
Alex Williamson May 13, 2009, 12:15 p.m. UTC | #7
On Wed, 2009-05-13 at 10:03 +0300, Michael S. Tsirkin wrote:
> On Tue, May 12, 2009 at 04:07:15PM -0600, Alex Williamson wrote:
> > We're currently using a counter to track the most recent GSI we've
> > handed out.  This quickly hits KVM_MAX_IRQ_ROUTES when using device
> > assignment with a driver that regularly toggles the MSI enable bit.
> 
> BTW, dwhich driver does that? Any idea why?

I've seen it from both e1000e and qla2xxx.  I assumed it was some kind
of interrupt mitigation since the devices seem to work fine otherwise.

Alex

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Alex Williamson May 13, 2009, 12:19 p.m. UTC | #8
On Wed, 2009-05-13 at 10:04 +0300, Michael S. Tsirkin wrote:
> On Tue, May 12, 2009 at 04:07:15PM -0600, Alex Williamson wrote:
> > @@ -286,6 +289,9 @@ kvm_context_t kvm_init(struct kvm_callbacks *callbacks,
> >  	int fd;
> >  	kvm_context_t kvm;
> >  	int r;
> > +#ifdef KVM_CAP_IRQ_ROUTING
> 
> Let's kill all these ifdefs. Or at least, let's not add them.

AFAICT, they're still used both for builds against older kernels and
architectures that don't support it.  Hollis just added the one around
kvm_get_irq_route_gsi() 10 days ago to fix ppc build.  Has it since been
deprecated?  Thanks,

Alex


--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Michael S. Tsirkin May 13, 2009, 2:25 p.m. UTC | #9
On Wed, May 13, 2009 at 06:19:35AM -0600, Alex Williamson wrote:
> On Wed, 2009-05-13 at 10:04 +0300, Michael S. Tsirkin wrote:
> > On Tue, May 12, 2009 at 04:07:15PM -0600, Alex Williamson wrote:
> > > @@ -286,6 +289,9 @@ kvm_context_t kvm_init(struct kvm_callbacks *callbacks,
> > >  	int fd;
> > >  	kvm_context_t kvm;
> > >  	int r;
> > > +#ifdef KVM_CAP_IRQ_ROUTING
> > 
> > Let's kill all these ifdefs. Or at least, let's not add them.
> 
> AFAICT, they're still used both for builds against older kernels and
> architectures that don't support it.

architectures only, I think. you don't know what kernel you will
run on, at build time.

> Hollis just added the one around
> kvm_get_irq_route_gsi() 10 days ago to fix ppc build.  Has it since been
> deprecated?  Thanks,
> 
> Alex
> 

These should be local to libkvm I think.
I think kvm_get_gsi_count already returns an error on ppc,
so things should work without ifdefs.
Avi Kivity May 17, 2009, 8:49 p.m. UTC | #10
Alex Williamson wrote:
> On Wed, 2009-05-13 at 10:04 +0300, Michael S. Tsirkin wrote:
>   
>> On Tue, May 12, 2009 at 04:07:15PM -0600, Alex Williamson wrote:
>>     
>>> @@ -286,6 +289,9 @@ kvm_context_t kvm_init(struct kvm_callbacks *callbacks,
>>>  	int fd;
>>>  	kvm_context_t kvm;
>>>  	int r;
>>> +#ifdef KVM_CAP_IRQ_ROUTING
>>>       
>> Let's kill all these ifdefs. Or at least, let's not add them.
>>     
>
> AFAICT, they're still used both for builds against older kernels and
> architectures that don't support it.  Hollis just added the one around
> kvm_get_irq_route_gsi() 10 days ago to fix ppc build.  Has it since been
> deprecated?  Thanks,
>   

They are somewhat documentary.  They used to be needed since we 
supported building against any kernel header, but aren't strictly needed 
now.  Let's keep them until we have Documentation/kvm/extensions.txt.
diff mbox

Patch

diff --git a/hw/device-assignment.c b/hw/device-assignment.c
index a7365c8..a6cc9b9 100644
--- a/hw/device-assignment.c
+++ b/hw/device-assignment.c
@@ -561,8 +561,10 @@  static void free_dev_irq_entries(AssignedDevice *dev)
 {
     int i;
 
-    for (i = 0; i < dev->irq_entries_nr; i++)
+    for (i = 0; i < dev->irq_entries_nr; i++) {
         kvm_del_routing_entry(kvm_context, &dev->entry[i]);
+        kvm_free_irq_route_gsi(kvm_context, dev->entry[i].gsi);
+    }
     free(dev->entry);
     dev->entry = NULL;
     dev->irq_entries_nr = 0;
diff --git a/kvm/libkvm/kvm-common.h b/kvm/libkvm/kvm-common.h
index 591fb53..4b3cb51 100644
--- a/kvm/libkvm/kvm-common.h
+++ b/kvm/libkvm/kvm-common.h
@@ -66,8 +66,10 @@  struct kvm_context {
 #ifdef KVM_CAP_IRQ_ROUTING
 	struct kvm_irq_routing *irq_routes;
 	int nr_allocated_irq_routes;
+	void *used_gsi_bitmap;
+	int max_gsi;
+	pthread_mutex_t gsi_mutex;
 #endif
-	int max_used_gsi;
 };
 
 int kvm_alloc_kernel_memory(kvm_context_t kvm, unsigned long memory,
diff --git a/kvm/libkvm/libkvm.c b/kvm/libkvm/libkvm.c
index ba0a5d1..3d7ab75 100644
--- a/kvm/libkvm/libkvm.c
+++ b/kvm/libkvm/libkvm.c
@@ -35,6 +35,7 @@ 
 #include <errno.h>
 #include <sys/ioctl.h>
 #include <inttypes.h>
+#include <pthread.h>
 #include "libkvm.h"
 
 #if defined(__x86_64__) || defined(__i386__)
@@ -65,6 +66,8 @@ 
 int kvm_abi = EXPECTED_KVM_API_VERSION;
 int kvm_page_size;
 
+static inline void set_bit(uint32_t *buf, unsigned int bit);
+
 struct slot_info {
 	unsigned long phys_addr;
 	unsigned long len;
@@ -286,6 +289,9 @@  kvm_context_t kvm_init(struct kvm_callbacks *callbacks,
 	int fd;
 	kvm_context_t kvm;
 	int r;
+#ifdef KVM_CAP_IRQ_ROUTING
+	int gsi_count, gsi_bytes, i;
+#endif
 
 	fd = open("/dev/kvm", O_RDWR);
 	if (fd == -1) {
@@ -322,6 +328,27 @@  kvm_context_t kvm_init(struct kvm_callbacks *callbacks,
 	kvm->dirty_pages_log_all = 0;
 	kvm->no_irqchip_creation = 0;
 	kvm->no_pit_creation = 0;
+#ifdef KVM_CAP_IRQ_ROUTING
+	pthread_mutex_init(&kvm->gsi_mutex, NULL);
+
+	gsi_count = kvm_get_gsi_count(kvm);
+	/* Round up so we can search ints using ffs */
+	gsi_bytes = (gsi_count + 31) / 32;
+	kvm->used_gsi_bitmap = malloc(gsi_bytes);
+	if (!kvm->used_gsi_bitmap) {
+		pthread_mutex_unlock(&kvm->gsi_mutex);
+		goto out_close;
+	}
+	memset(kvm->used_gsi_bitmap, 0, gsi_bytes);
+	kvm->max_gsi = gsi_bytes * 8;
+
+	/* Mark all the IOAPIC pin GSIs and any over-allocated
+	 * GSIs as already in use. */
+	for (i = 0; i < KVM_IOAPIC_NUM_PINS; i++)
+		set_bit(kvm->used_gsi_bitmap, i);
+	for (i = gsi_count; i < kvm->max_gsi; i++)
+		set_bit(kvm->used_gsi_bitmap, i);
+#endif
 
 	return kvm;
  out_close:
@@ -1298,8 +1325,6 @@  int kvm_add_routing_entry(kvm_context_t kvm,
 	new->flags = entry->flags;
 	new->u = entry->u;
 
-	if (entry->gsi > kvm->max_used_gsi)
-		kvm->max_used_gsi = entry->gsi;
 	return 0;
 #else
 	return -ENOSYS;
@@ -1404,18 +1429,54 @@  int kvm_commit_irq_routes(kvm_context_t kvm)
 #endif
 }
 
+#ifdef KVM_CAP_IRQ_ROUTING
+static inline void set_bit(uint32_t *buf, unsigned int bit)
+{
+	buf[bit / 32] |= 1U << (bit % 32);
+}
+
+static inline void clear_bit(uint32_t *buf, unsigned int bit)
+{
+	buf[bit / 32] &= ~(1U << (bit % 32));
+}
+
+static int kvm_find_free_gsi(kvm_context_t kvm)
+{
+	int i, bit, gsi;
+	uint32_t *buf = kvm->used_gsi_bitmap;
+
+	for (i = 0; i < kvm->max_gsi / 32; i++) {
+		bit = ffs(~buf[i]);
+		if (!bit)
+			continue;
+
+		gsi = bit - 1 + i * 32;
+		set_bit(buf, gsi);
+		return gsi;
+	}
+
+	return -ENOSPC;
+}
+#endif
+
 int kvm_get_irq_route_gsi(kvm_context_t kvm)
 {
+	int gsi = -ENOSYS;
+
 #ifdef KVM_CAP_IRQ_ROUTING
-	if (kvm->max_used_gsi >= KVM_IOAPIC_NUM_PINS)  {
-	    if (kvm->max_used_gsi <= kvm_get_gsi_count(kvm))
-                return kvm->max_used_gsi + 1;
-            else
-                return -ENOSPC;
-        } else
-            return KVM_IOAPIC_NUM_PINS;
-#else
-	return -ENOSYS;
+	pthread_mutex_lock(&kvm->gsi_mutex);
+	gsi = kvm_find_free_gsi(kvm);
+	pthread_mutex_unlock(&kvm->gsi_mutex);
+#endif
+	return gsi;
+}
+ 
+void kvm_free_irq_route_gsi(kvm_context_t kvm, int gsi)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+	pthread_mutex_lock(&kvm->gsi_mutex);
+	clear_bit(kvm->used_gsi_bitmap, gsi);
+	pthread_mutex_unlock(&kvm->gsi_mutex);
 #endif
 }
 
diff --git a/kvm/libkvm/libkvm.h b/kvm/libkvm/libkvm.h
index 4821a1e..845bb8a 100644
--- a/kvm/libkvm/libkvm.h
+++ b/kvm/libkvm/libkvm.h
@@ -856,6 +856,16 @@  int kvm_commit_irq_routes(kvm_context_t kvm);
  */
 int kvm_get_irq_route_gsi(kvm_context_t kvm);
 
+/*!
+ * \brief Free used GSI number
+ *
+ * Free used GSI number acquired from kvm_get_irq_route_gsi()
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param gsi GSI number to free
+ */
+void kvm_free_irq_route_gsi(kvm_context_t kvm, int gsi);
+
 #ifdef KVM_CAP_DEVICE_MSIX
 int kvm_assign_set_msix_nr(kvm_context_t kvm,
 			   struct kvm_assigned_msix_nr *msix_nr);