aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen
diff options
context:
space:
mode:
authorJan Beulich <JBeulich@suse.com>2012-04-05 11:10:07 -0400
committerKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>2012-04-17 13:29:13 -0400
commit569ca5b3f94cd0b3295ec5943aa457cf4a4f6a3a (patch)
tree5771fbace4a88ef1feb29f5dd4096f8ba4c9009b /drivers/xen
parent9fe2a7015393dc0203ac39242ae9c89038994f3c (diff)
xen/gnttab: add deferred freeing logic
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 <jbeulich@suse.com> Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Diffstat (limited to 'drivers/xen')
-rw-r--r--drivers/xen/grant-table.c106
1 files changed, 96 insertions, 10 deletions
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index b4d4eac761db..9f514bb561af 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -426,10 +426,8 @@ static int gnttab_end_foreign_access_ref_v1(grant_ref_t ref, int readonly)
426 nflags = *pflags; 426 nflags = *pflags;
427 do { 427 do {
428 flags = nflags; 428 flags = nflags;
429 if (flags & (GTF_reading|GTF_writing)) { 429 if (flags & (GTF_reading|GTF_writing))
430 printk(KERN_ALERT "WARNING: g.e. still in use!\n");
431 return 0; 430 return 0;
432 }
433 } while ((nflags = sync_cmpxchg(pflags, flags, 0)) != flags); 431 } while ((nflags = sync_cmpxchg(pflags, flags, 0)) != flags);
434 432
435 return 1; 433 return 1;
@@ -458,12 +456,103 @@ static int gnttab_end_foreign_access_ref_v2(grant_ref_t ref, int readonly)
458 return 1; 456 return 1;
459} 457}
460 458
461int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly) 459static inline int _gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
462{ 460{
463 return gnttab_interface->end_foreign_access_ref(ref, readonly); 461 return gnttab_interface->end_foreign_access_ref(ref, readonly);
464} 462}
463
464int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
465{
466 if (_gnttab_end_foreign_access_ref(ref, readonly))
467 return 1;
468 pr_warn("WARNING: g.e. %#x still in use!\n", ref);
469 return 0;
470}
465EXPORT_SYMBOL_GPL(gnttab_end_foreign_access_ref); 471EXPORT_SYMBOL_GPL(gnttab_end_foreign_access_ref);
466 472
473struct deferred_entry {
474 struct list_head list;
475 grant_ref_t ref;
476 bool ro;
477 uint16_t warn_delay;
478 struct page *page;
479};
480static LIST_HEAD(deferred_list);
481static void gnttab_handle_deferred(unsigned long);
482static DEFINE_TIMER(deferred_timer, gnttab_handle_deferred, 0, 0);
483
484static void gnttab_handle_deferred(unsigned long unused)
485{
486 unsigned int nr = 10;
487 struct deferred_entry *first = NULL;
488 unsigned long flags;
489
490 spin_lock_irqsave(&gnttab_list_lock, flags);
491 while (nr--) {
492 struct deferred_entry *entry
493 = list_first_entry(&deferred_list,
494 struct deferred_entry, list);
495
496 if (entry == first)
497 break;
498 list_del(&entry->list);
499 spin_unlock_irqrestore(&gnttab_list_lock, flags);
500 if (_gnttab_end_foreign_access_ref(entry->ref, entry->ro)) {
501 put_free_entry(entry->ref);
502 if (entry->page) {
503 pr_debug("freeing g.e. %#x (pfn %#lx)\n",
504 entry->ref, page_to_pfn(entry->page));
505 __free_page(entry->page);
506 } else
507 pr_info("freeing g.e. %#x\n", entry->ref);
508 kfree(entry);
509 entry = NULL;
510 } else {
511 if (!--entry->warn_delay)
512 pr_info("g.e. %#x still pending\n",
513 entry->ref);
514 if (!first)
515 first = entry;
516 }
517 spin_lock_irqsave(&gnttab_list_lock, flags);
518 if (entry)
519 list_add_tail(&entry->list, &deferred_list);
520 else if (list_empty(&deferred_list))
521 break;
522 }
523 if (!list_empty(&deferred_list) && !timer_pending(&deferred_timer)) {
524 deferred_timer.expires = jiffies + HZ;
525 add_timer(&deferred_timer);
526 }
527 spin_unlock_irqrestore(&gnttab_list_lock, flags);
528}
529
530static void gnttab_add_deferred(grant_ref_t ref, bool readonly,
531 struct page *page)
532{
533 struct deferred_entry *entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
534 const char *what = KERN_WARNING "leaking";
535
536 if (entry) {
537 unsigned long flags;
538
539 entry->ref = ref;
540 entry->ro = readonly;
541 entry->page = page;
542 entry->warn_delay = 60;
543 spin_lock_irqsave(&gnttab_list_lock, flags);
544 list_add_tail(&entry->list, &deferred_list);
545 if (!timer_pending(&deferred_timer)) {
546 deferred_timer.expires = jiffies + HZ;
547 add_timer(&deferred_timer);
548 }
549 spin_unlock_irqrestore(&gnttab_list_lock, flags);
550 what = KERN_DEBUG "deferring";
551 }
552 printk("%s g.e. %#x (pfn %#lx)\n",
553 what, ref, page ? page_to_pfn(page) : -1);
554}
555
467void gnttab_end_foreign_access(grant_ref_t ref, int readonly, 556void gnttab_end_foreign_access(grant_ref_t ref, int readonly,
468 unsigned long page) 557 unsigned long page)
469{ 558{
@@ -471,12 +560,9 @@ void gnttab_end_foreign_access(grant_ref_t ref, int readonly,
471 put_free_entry(ref); 560 put_free_entry(ref);
472 if (page != 0) 561 if (page != 0)
473 free_page(page); 562 free_page(page);
474 } else { 563 } else
475 /* XXX This needs to be fixed so that the ref and page are 564 gnttab_add_deferred(ref, readonly,
476 placed on a list to be freed up later. */ 565 page ? virt_to_page(page) : NULL);
477 printk(KERN_WARNING
478 "WARNING: leaking g.e. and page still in use!\n");
479 }
480} 566}
481EXPORT_SYMBOL_GPL(gnttab_end_foreign_access); 567EXPORT_SYMBOL_GPL(gnttab_end_foreign_access);
482 568