Skip to content

Commit

Permalink
xen/gnttab: add deferred freeing logic
Browse files Browse the repository at this point in the history
Rather than just leaking pages that can't be freed at the point where
access permission for the backend domain gets revoked, put them on a
list and run a timer to (infrequently) retry freeing them. (This can
particularly happen when unloading a frontend driver when devices are
still present, and the backend still has them in non-closed state or
hasn't finished closing them yet.)

Signed-off-by: Jan Beulich <[email protected]>
Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
  • Loading branch information
jbeulich authored and konradwilk committed Apr 17, 2012
1 parent 9fe2a70 commit 569ca5b
Showing 1 changed file with 96 additions and 10 deletions.
106 changes: 96 additions & 10 deletions drivers/xen/grant-table.c
Original file line number Diff line number Diff line change
Expand Up @@ -426,10 +426,8 @@ static int gnttab_end_foreign_access_ref_v1(grant_ref_t ref, int readonly)
nflags = *pflags;
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(pflags, flags, 0)) != flags);

return 1;
Expand Down Expand Up @@ -458,25 +456,113 @@ static int gnttab_end_foreign_access_ref_v2(grant_ref_t ref, int readonly)
return 1;
}

int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
static inline int _gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
{
return gnttab_interface->end_foreign_access_ref(ref, readonly);
}

int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
{
if (_gnttab_end_foreign_access_ref(ref, readonly))
return 1;
pr_warn("WARNING: g.e. %#x still in use!\n", ref);
return 0;
}
EXPORT_SYMBOL_GPL(gnttab_end_foreign_access_ref);

struct deferred_entry {
struct list_head list;
grant_ref_t ref;
bool ro;
uint16_t warn_delay;
struct page *page;
};
static LIST_HEAD(deferred_list);
static void gnttab_handle_deferred(unsigned long);
static DEFINE_TIMER(deferred_timer, gnttab_handle_deferred, 0, 0);

static void gnttab_handle_deferred(unsigned long unused)
{
unsigned int nr = 10;
struct deferred_entry *first = NULL;
unsigned long flags;

spin_lock_irqsave(&gnttab_list_lock, flags);
while (nr--) {
struct deferred_entry *entry
= list_first_entry(&deferred_list,
struct deferred_entry, list);

if (entry == first)
break;
list_del(&entry->list);
spin_unlock_irqrestore(&gnttab_list_lock, flags);
if (_gnttab_end_foreign_access_ref(entry->ref, entry->ro)) {
put_free_entry(entry->ref);
if (entry->page) {
pr_debug("freeing g.e. %#x (pfn %#lx)\n",
entry->ref, page_to_pfn(entry->page));
__free_page(entry->page);
} else
pr_info("freeing g.e. %#x\n", entry->ref);
kfree(entry);
entry = NULL;
} else {
if (!--entry->warn_delay)
pr_info("g.e. %#x still pending\n",
entry->ref);
if (!first)
first = entry;
}
spin_lock_irqsave(&gnttab_list_lock, flags);
if (entry)
list_add_tail(&entry->list, &deferred_list);
else if (list_empty(&deferred_list))
break;
}
if (!list_empty(&deferred_list) && !timer_pending(&deferred_timer)) {
deferred_timer.expires = jiffies + HZ;
add_timer(&deferred_timer);
}
spin_unlock_irqrestore(&gnttab_list_lock, flags);
}

static void gnttab_add_deferred(grant_ref_t ref, bool readonly,
struct page *page)
{
struct deferred_entry *entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
const char *what = KERN_WARNING "leaking";

if (entry) {
unsigned long flags;

entry->ref = ref;
entry->ro = readonly;
entry->page = page;
entry->warn_delay = 60;
spin_lock_irqsave(&gnttab_list_lock, flags);
list_add_tail(&entry->list, &deferred_list);
if (!timer_pending(&deferred_timer)) {
deferred_timer.expires = jiffies + HZ;
add_timer(&deferred_timer);
}
spin_unlock_irqrestore(&gnttab_list_lock, flags);
what = KERN_DEBUG "deferring";
}
printk("%s g.e. %#x (pfn %#lx)\n",
what, ref, page ? page_to_pfn(page) : -1);
}

void gnttab_end_foreign_access(grant_ref_t ref, int readonly,
unsigned long page)
{
if (gnttab_end_foreign_access_ref(ref, readonly)) {
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");
}
} else
gnttab_add_deferred(ref, readonly,
page ? virt_to_page(page) : NULL);
}
EXPORT_SYMBOL_GPL(gnttab_end_foreign_access);

Expand Down

0 comments on commit 569ca5b

Please sign in to comment.