xen/grant-table: add gnttab_try_end_foreign_access()
[linux-2.6-microblaze.git] / drivers / xen / grant-table.c
index 523dcdf..1b82e7a 100644 (file)
@@ -435,11 +435,21 @@ static void gnttab_add_deferred(grant_ref_t ref, bool readonly,
               what, ref, page ? page_to_pfn(page) : -1);
 }
 
+int gnttab_try_end_foreign_access(grant_ref_t ref)
+{
+       int ret = _gnttab_end_foreign_access_ref(ref, 0);
+
+       if (ret)
+               put_free_entry(ref);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(gnttab_try_end_foreign_access);
+
 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 (gnttab_try_end_foreign_access(ref)) {
                if (page != 0)
                        put_page(virt_to_page(page));
        } else
@@ -813,6 +823,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;