Merge tag 'drm-next-2020-12-11' of git://anongit.freedesktop.org/drm/drm
[linux-2.6-microblaze.git] / drivers / xen / grant-table.c
index 523dcdf..3729bea 100644 (file)
@@ -813,6 +813,129 @@ int gnttab_alloc_pages(int nr_pages, struct page **pages)
 }
 EXPORT_SYMBOL_GPL(gnttab_alloc_pages);
 
+#ifdef CONFIG_XEN_UNPOPULATED_ALLOC
+static inline void cache_init(struct gnttab_page_cache *cache)
+{
+       cache->pages = NULL;
+}
+
+static inline bool cache_empty(struct gnttab_page_cache *cache)
+{
+       return !cache->pages;
+}
+
+static inline struct page *cache_deq(struct gnttab_page_cache *cache)
+{
+       struct page *page;
+
+       page = cache->pages;
+       cache->pages = page->zone_device_data;
+
+       return page;
+}
+
+static inline void cache_enq(struct gnttab_page_cache *cache, struct page *page)
+{
+       page->zone_device_data = cache->pages;
+       cache->pages = page;
+}
+#else
+static inline void cache_init(struct gnttab_page_cache *cache)
+{
+       INIT_LIST_HEAD(&cache->pages);
+}
+
+static inline bool cache_empty(struct gnttab_page_cache *cache)
+{
+       return list_empty(&cache->pages);
+}
+
+static inline struct page *cache_deq(struct gnttab_page_cache *cache)
+{
+       struct page *page;
+
+       page = list_first_entry(&cache->pages, struct page, lru);
+       list_del(&page->lru);
+
+       return page;
+}
+
+static inline void cache_enq(struct gnttab_page_cache *cache, struct page *page)
+{
+       list_add(&page->lru, &cache->pages);
+}
+#endif
+
+void gnttab_page_cache_init(struct gnttab_page_cache *cache)
+{
+       spin_lock_init(&cache->lock);
+       cache_init(cache);
+       cache->num_pages = 0;
+}
+EXPORT_SYMBOL_GPL(gnttab_page_cache_init);
+
+int gnttab_page_cache_get(struct gnttab_page_cache *cache, struct page **page)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&cache->lock, flags);
+
+       if (cache_empty(cache)) {
+               spin_unlock_irqrestore(&cache->lock, flags);
+               return gnttab_alloc_pages(1, page);
+       }
+
+       page[0] = cache_deq(cache);
+       cache->num_pages--;
+
+       spin_unlock_irqrestore(&cache->lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(gnttab_page_cache_get);
+
+void gnttab_page_cache_put(struct gnttab_page_cache *cache, struct page **page,
+                          unsigned int num)
+{
+       unsigned long flags;
+       unsigned int i;
+
+       spin_lock_irqsave(&cache->lock, flags);
+
+       for (i = 0; i < num; i++)
+               cache_enq(cache, page[i]);
+       cache->num_pages += num;
+
+       spin_unlock_irqrestore(&cache->lock, flags);
+}
+EXPORT_SYMBOL_GPL(gnttab_page_cache_put);
+
+void gnttab_page_cache_shrink(struct gnttab_page_cache *cache, unsigned int num)
+{
+       struct page *page[10];
+       unsigned int i = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&cache->lock, flags);
+
+       while (cache->num_pages > num) {
+               page[i] = cache_deq(cache);
+               cache->num_pages--;
+               if (++i == ARRAY_SIZE(page)) {
+                       spin_unlock_irqrestore(&cache->lock, flags);
+                       gnttab_free_pages(i, page);
+                       i = 0;
+                       spin_lock_irqsave(&cache->lock, flags);
+               }
+       }
+
+       spin_unlock_irqrestore(&cache->lock, flags);
+
+       if (i != 0)
+               gnttab_free_pages(i, page);
+}
+EXPORT_SYMBOL_GPL(gnttab_page_cache_shrink);
+
 void gnttab_pages_clear_private(int nr_pages, struct page **pages)
 {
        int i;