[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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |