static void gnttab_handle_deferred(struct timer_list *);
 static DEFINE_TIMER(deferred_timer, gnttab_handle_deferred);
 
+static atomic64_t deferred_count;
+static atomic64_t leaked_count;
+static unsigned int free_per_iteration = 10;
+module_param(free_per_iteration, uint, 0600);
+
 static void gnttab_handle_deferred(struct timer_list *unused)
 {
-       unsigned int nr = 10;
+       unsigned int nr = READ_ONCE(free_per_iteration);
+       const bool ignore_limit = nr == 0;
        struct deferred_entry *first = NULL;
        unsigned long flags;
+       size_t freed = 0;
 
        spin_lock_irqsave(&gnttab_list_lock, flags);
-       while (nr--) {
+       while ((ignore_limit || nr--) && !list_empty(&deferred_list)) {
                struct deferred_entry *entry
                        = list_first_entry(&deferred_list,
                                           struct deferred_entry, list);
                list_del(&entry->list);
                spin_unlock_irqrestore(&gnttab_list_lock, flags);
                if (_gnttab_end_foreign_access_ref(entry->ref)) {
+                       uint64_t ret = atomic64_dec_return(&deferred_count);
+
                        put_free_entry(entry->ref);
-                       pr_debug("freeing g.e. %#x (pfn %#lx)\n",
-                                entry->ref, page_to_pfn(entry->page));
+                       pr_debug("freeing g.e. %#x (pfn %#lx), %llu remaining\n",
+                                entry->ref, page_to_pfn(entry->page),
+                                (unsigned long long)ret);
                        put_page(entry->page);
+                       freed++;
                        kfree(entry);
                        entry = NULL;
                } else {
                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)) {
+       if (list_empty(&deferred_list))
+               WARN_ON(atomic64_read(&deferred_count));
+       else if (!timer_pending(&deferred_timer)) {
                deferred_timer.expires = jiffies + HZ;
                add_timer(&deferred_timer);
        }
        spin_unlock_irqrestore(&gnttab_list_lock, flags);
+       pr_debug("Freed %zu references", freed);
 }
 
 static void gnttab_add_deferred(grant_ref_t ref, struct page *page)
 {
        struct deferred_entry *entry;
        gfp_t gfp = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL;
-       const char *what = KERN_WARNING "leaking";
+       uint64_t leaked, deferred;
 
        entry = kmalloc(sizeof(*entry), gfp);
        if (!page) {
                        add_timer(&deferred_timer);
                }
                spin_unlock_irqrestore(&gnttab_list_lock, flags);
-               what = KERN_DEBUG "deferring";
+               deferred = atomic64_inc_return(&deferred_count);
+               leaked = atomic64_read(&leaked_count);
+               pr_debug("deferring g.e. %#x (pfn %#lx) (total deferred %llu, total leaked %llu)\n",
+                        ref, page ? page_to_pfn(page) : -1, deferred, leaked);
+       } else {
+               deferred = atomic64_read(&deferred_count);
+               leaked = atomic64_inc_return(&leaked_count);
+               pr_warn("leaking g.e. %#x (pfn %#lx) (total deferred %llu, total leaked %llu)\n",
+                       ref, page ? page_to_pfn(page) : -1, deferred, leaked);
        }
-       printk("%s g.e. %#x (pfn %#lx)\n",
-              what, ref, page ? page_to_pfn(page) : -1);
 }
 
 int gnttab_try_end_foreign_access(grant_ref_t ref)