diff options
author | Jan Beulich <JBeulich@suse.com> | 2012-04-05 11:10:07 -0400 |
---|---|---|
committer | Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> | 2012-04-17 13:29:13 -0400 |
commit | 569ca5b3f94cd0b3295ec5943aa457cf4a4f6a3a (patch) | |
tree | 5771fbace4a88ef1feb29f5dd4096f8ba4c9009b | |
parent | 9fe2a7015393dc0203ac39242ae9c89038994f3c (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>
-rw-r--r-- | drivers/xen/grant-table.c | 106 |
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 | ||
461 | int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly) | 459 | static 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 | |||
464 | int 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 | } | ||
465 | EXPORT_SYMBOL_GPL(gnttab_end_foreign_access_ref); | 471 | EXPORT_SYMBOL_GPL(gnttab_end_foreign_access_ref); |
466 | 472 | ||
473 | struct 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 | }; | ||
480 | static LIST_HEAD(deferred_list); | ||
481 | static void gnttab_handle_deferred(unsigned long); | ||
482 | static DEFINE_TIMER(deferred_timer, gnttab_handle_deferred, 0, 0); | ||
483 | |||
484 | static 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 | |||
530 | static 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 | |||
467 | void gnttab_end_foreign_access(grant_ref_t ref, int readonly, | 556 | void 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 | } |
481 | EXPORT_SYMBOL_GPL(gnttab_end_foreign_access); | 567 | EXPORT_SYMBOL_GPL(gnttab_end_foreign_access); |
482 | 568 | ||