[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 03/17] Fix a long-standing memory leak in the grant tables implementation.



According to the interface comments, gnttab_end_foreign_access() is
supposed to free the page once the grant is no longer in use, from a
polling timer, but that was never implemented.  Implement it.

This shouldn't make any real difference, because the existing drivers
all arrange that with well-behaved backends references are never ended
while they're still in use, but it tidies things up a bit.

Signed-off-by: Steven Smith <steven.smith@xxxxxxxxxx>
---
 drivers/xen/grant-table.c |  103 +++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 94 insertions(+), 9 deletions(-)

diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index cd82d22..52183aa 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -51,11 +51,17 @@
 #define GNTTAB_LIST_END 0xffffffff
 #define GREFS_PER_GRANT_FRAME (PAGE_SIZE / sizeof(struct grant_entry))
 
+static void pending_free_timer(unsigned long ignore);
+
 static grant_ref_t **gnttab_list;
 static unsigned int nr_grant_frames;
 static unsigned int boot_max_nr_grant_frames;
 static int gnttab_free_count;
 static grant_ref_t gnttab_free_head;
+static grant_ref_t gnttab_pending_free_gref_head = GNTTAB_LIST_END;
+static LIST_HEAD(gnttab_pending_free_pages);
+static DEFINE_TIMER(gnttab_delayed_free_timer, pending_free_timer, 0, 0);
+static DEFINE_SPINLOCK(gnttab_pending_free_lock);
 static DEFINE_SPINLOCK(gnttab_list_lock);
 
 static struct grant_entry *shared;
@@ -191,35 +197,114 @@ int gnttab_query_foreign_access(grant_ref_t ref)
 }
 EXPORT_SYMBOL_GPL(gnttab_query_foreign_access);
 
-int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
+static int _gnttab_end_foreign_access_ref(grant_ref_t ref)
 {
        u16 flags, nflags;
 
        nflags = shared[ref].flags;
        do {
                flags = nflags;
-               if (flags & (GTF_reading|GTF_writing)) {
-                       printk(KERN_ALERT "WARNING: g.e. still in use!\n");
+               if (flags & (GTF_reading|GTF_writing))
                        return 0;
-               }
        } while ((nflags = sync_cmpxchg(&shared[ref].flags, flags, 0)) != 
flags);
 
        return 1;
 }
+
+int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
+{
+       int r;
+
+       r = _gnttab_end_foreign_access_ref(ref);
+       if (!r)
+               printk(KERN_ALERT "WARNING: g.e. still in use!\n");
+       return r;
+}
 EXPORT_SYMBOL_GPL(gnttab_end_foreign_access_ref);
 
+static void pending_free_timer(unsigned long ignore)
+{
+       grant_ref_t gref, next_gref;
+       grant_ref_t prev; /* The last gref which we failed to release,
+                            or GNTTAB_LIST_END if there is no such
+                            gref. */
+       int need_mod_timer;
+       struct page *page, *next_page;
+
+       spin_lock(&gnttab_pending_free_lock);
+       prev = GNTTAB_LIST_END;
+       for (gref = gnttab_pending_free_gref_head;
+            gref != GNTTAB_LIST_END;
+            gref = next_gref) {
+               next_gref = gnttab_entry(gref);
+               if (_gnttab_end_foreign_access_ref(gref)) {
+                       put_free_entry(gref);
+                       if (prev != GNTTAB_LIST_END)
+                               gnttab_entry(prev) = next_gref;
+                       else
+                               gnttab_pending_free_gref_head = next_gref;
+               } else {
+                       prev = gref;
+               }
+       }
+       list_for_each_entry_safe(page, next_page,
+                                &gnttab_pending_free_pages, lru) {
+               gref = page->index;
+               if (_gnttab_end_foreign_access_ref(gref)) {
+                       list_del(&page->lru);
+                       put_free_entry(gref);
+                       /* The page hasn't been used in this domain
+                          for more than a second, so it's probably
+                          cold. */
+                       if (put_page_testzero(page)) {
+#ifdef MODULE
+                               __free_page(page);
+#else
+                               free_cold_page(page);
+#endif
+                       }
+               }
+       }
+
+       need_mod_timer =
+               (gnttab_pending_free_gref_head != GNTTAB_LIST_END) ||
+               !list_empty(&gnttab_pending_free_pages);
+       spin_unlock(&gnttab_pending_free_lock);
+
+       if (need_mod_timer)
+               mod_timer(&gnttab_delayed_free_timer, jiffies + HZ);
+}
+
 void gnttab_end_foreign_access(grant_ref_t ref, int readonly,
                               unsigned long page)
 {
-       if (gnttab_end_foreign_access_ref(ref, readonly)) {
+       int need_mod_timer;
+       struct page *page_struct;
+
+       if (_gnttab_end_foreign_access_ref(ref)) {
                put_free_entry(ref);
                if (page != 0)
                        free_page(page);
        } else {
-               /* XXX This needs to be fixed so that the ref and page are
-                  placed on a list to be freed up later. */
-               printk(KERN_WARNING
-                      "WARNING: leaking g.e. and page still in use!\n");
+               spin_lock_bh(&gnttab_pending_free_lock);
+               if (page == 0) {
+                       if (gnttab_pending_free_gref_head == GNTTAB_LIST_END)
+                               need_mod_timer = 1;
+                       else
+                               need_mod_timer = 0;
+                       gnttab_entry(ref) = gnttab_pending_free_gref_head;
+                       gnttab_pending_free_gref_head = ref;
+               } else {
+                       need_mod_timer =
+                               list_empty(&gnttab_pending_free_pages);
+                       page_struct = virt_to_page((void *)page);
+                       page_struct->index = ref;
+                       list_add_tail(&page_struct->lru,
+                                     &gnttab_pending_free_pages);
+               }
+               spin_unlock_bh(&gnttab_pending_free_lock);
+               if (need_mod_timer)
+                       mod_timer(&gnttab_delayed_free_timer, jiffies + HZ);
        }
 }
 EXPORT_SYMBOL_GPL(gnttab_end_foreign_access);
-- 
1.6.3.1


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.