/****************************************************************************** * grant_table.c * * Granting foreign access to our memory reservation. * * Copyright (c) 2005-2006, Christopher Clark * Copyright (c) 2004-2005, K A Fraser * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation; or, when distributed * separately from the Linux kernel or incorporated into other * software packages, subject to the following license: * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this source file (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE.
*/
/* * Handling of free grants: * * Free grants are in a simple list anchored in gnttab_free_head. They are * linked by grant ref, the last element contains GNTTAB_LIST_END. The number * of free entries is stored in gnttab_free_count. * Additionally there is a bitmap of free entries anchored in * gnttab_free_bitmap. This is being used for simplifying allocation of * multiple consecutive grants, which is needed e.g. for support of virtio. * gnttab_last_free is used to add free entries of new frames at the end of * the free list. * gnttab_free_tail_ptr specifies the variable which references the start * of consecutive free grants ending with gnttab_last_free. This pointer is * updated in a rather defensive way, in order to avoid performance hits in * hot paths. * All those variables are protected by gnttab_list_lock.
*/ staticint gnttab_free_count; staticunsignedint gnttab_size; static grant_ref_t gnttab_free_head = GNTTAB_LIST_END; static grant_ref_t gnttab_last_free = GNTTAB_LIST_END; static grant_ref_t *gnttab_free_tail_ptr; staticunsignedlong *gnttab_free_bitmap; static DEFINE_SPINLOCK(gnttab_list_lock);
/*This is a structure of function pointers for grant table*/ struct gnttab_ops { /* * Version of the grant interface.
*/ unsignedint version; /* * Grant refs per grant frame.
*/ unsignedint grefs_per_grant_frame; /* * Mapping a list of frames for storing grant entries. Frames parameter * is used to store grant table address when grant table being setup, * nr_gframes is the number of frames to map grant table. Returning * GNTST_okay means success and negative value means failure.
*/ int (*map_frames)(xen_pfn_t *frames, unsignedint nr_gframes); /* * Release a list of frames which are mapped in map_frames for grant * entry status.
*/ void (*unmap_frames)(void); /* * Introducing a valid entry into the grant table, granting the frame of * this grant entry to domain for accessing. Ref * parameter is reference of this introduced grant entry, domid is id of * granted domain, frame is the page frame to be granted, and flags is * status of the grant entry to be updated.
*/ void (*update_entry)(grant_ref_t ref, domid_t domid, unsignedlong frame, unsigned flags); /* * Stop granting a grant entry to domain for accessing. Ref parameter is * reference of a grant entry whose grant access will be stopped. * If the grant entry is currently mapped for reading or writing, just * return failure(==0) directly and don't tear down the grant access. * Otherwise, stop grant access for this entry and return success(==1).
*/ int (*end_foreign_access_ref)(grant_ref_t ref); /* * Read the frame number related to a given grant reference.
*/ unsignedlong (*read_frame)(grant_ref_t ref);
};
struct unmap_refs_callback_data { struct completion completion; int result;
};
staticconststruct gnttab_ops *gnttab_interface;
/* This reflects status of grant entries, so act as a global value. */ static grant_status_t *grstatus;
staticinline grant_ref_t *__gnttab_entry(grant_ref_t entry)
{ return &gnttab_list[(entry) / RPP][(entry) % RPP];
} /* This can be used as an l-value */ #define gnttab_entry(entry) (*__gnttab_entry(entry))
/* Rebuilds the free grant list and tries to find count consecutive entries. */ staticint get_free_seq(unsignedint count)
{ int ret = -ENOSPC; unsignedint from, to;
grant_ref_t *last;
gnttab_free_tail_ptr = &gnttab_free_head;
last = &gnttab_free_head;
for (from = find_first_bit(gnttab_free_bitmap, gnttab_size);
from < gnttab_size;
from = find_next_bit(gnttab_free_bitmap, gnttab_size, to + 1)) {
to = find_next_zero_bit(gnttab_free_bitmap, gnttab_size,
from + 1); if (ret < 0 && to - from >= count) {
ret = from;
bitmap_clear(gnttab_free_bitmap, ret, count);
from += count;
gnttab_free_count -= count; if (from == to) continue;
}
/* * Recreate the free list in order to have it properly sorted. * This is needed to make sure that the free tail has the maximum * possible size.
*/ while (from < to) {
*last = from;
last = __gnttab_entry(from);
gnttab_last_free = from;
from++;
} if (to < gnttab_size)
gnttab_free_tail_ptr = __gnttab_entry(to - 1);
}
flags = *pflags; do { if (flags & (GTF_reading|GTF_writing)) return 0;
} while (!sync_try_cmpxchg(pflags, &flags, 0));
return 1;
}
staticint gnttab_end_foreign_access_ref_v2(grant_ref_t ref)
{
gnttab_shared.v2[ref].hdr.flags = 0;
mb(); /* Concurrent access by hypervisor. */ if (grstatus[ref] & (GTF_reading|GTF_writing)) { return 0;
} else { /* * The read of grstatus needs to have acquire semantics. * On x86, reads already have that, and we just need to * protect against compiler reorderings. * On other architectures we may need a full barrier.
*/ #ifdef CONFIG_X86
barrier(); #else
mb(); #endif
}
int gnttab_end_foreign_access_ref(grant_ref_t ref)
{ if (_gnttab_end_foreign_access_ref(ref)) return 1;
pr_warn("WARNING: g.e. %#x still in use!\n", ref); return 0;
}
EXPORT_SYMBOL_GPL(gnttab_end_foreign_access_ref);
if (entry == first) break;
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), %llu remaining\n",
entry->ref, page_to_pfn(entry->page),
(unsignedlonglong)ret);
put_page(entry->page);
freed++;
kfree(entry);
entry = NULL;
} else { if (!--entry->warn_delay)
pr_info("g.e. %#x still pending\n", entry->ref); if (!first)
first = entry;
}
spin_lock_irqsave(&gnttab_list_lock, flags); if (entry)
list_add_tail(&entry->list, &deferred_list);
} if (list_empty(&deferred_list))
WARN_ON(atomic64_read(&deferred_count)); elseif (!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);
}
spin_lock_irqsave(&gnttab_list_lock, flags); for (i = count; i > 0; i--)
put_free_entry_locked(head + i - 1);
check_free_callbacks();
spin_unlock_irqrestore(&gnttab_list_lock, flags);
}
EXPORT_SYMBOL_GPL(gnttab_free_grant_reference_seq);
int gnttab_alloc_grant_references(u16 count, grant_ref_t *head)
{ int h = get_free_entries(count);
/** * gnttab_alloc_pages - alloc pages suitable for grant mapping into * @nr_pages: number of pages to alloc * @pages: returns the pages
*/ int gnttab_alloc_pages(int nr_pages, struct page **pages)
{ int ret;
ret = xen_alloc_unpopulated_pages(nr_pages, pages); if (ret < 0) return ret;
ret = gnttab_pages_set_private(nr_pages, pages); if (ret < 0)
gnttab_free_pages(nr_pages, pages);
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;
for (i = 0; i < nr_pages; i++) { if (PagePrivate(pages[i])) { #if BITS_PER_LONG < 64
kfree((void *)page_private(pages[i])); #endif
ClearPagePrivate(pages[i]);
}
}
}
EXPORT_SYMBOL_GPL(gnttab_pages_clear_private);
/** * gnttab_free_pages - free pages allocated by gnttab_alloc_pages() * @nr_pages: number of pages to free * @pages: the pages
*/ void gnttab_free_pages(int nr_pages, struct page **pages)
{
gnttab_pages_clear_private(nr_pages, pages);
xen_free_unpopulated_pages(nr_pages, pages);
}
EXPORT_SYMBOL_GPL(gnttab_free_pages);
#ifdef CONFIG_XEN_GRANT_DMA_ALLOC /** * gnttab_dma_alloc_pages - alloc DMAable pages suitable for grant mapping into * @args: arguments to the function
*/ int gnttab_dma_alloc_pages(struct gnttab_dma_alloc_args *args)
{ unsignedlong pfn, start_pfn;
size_t size; int i, ret;
ret = xenmem_reservation_decrease(args->nr_pages, args->frames); if (ret != args->nr_pages) {
pr_debug("Failed to decrease reservation for DMA buffer\n");
ret = -EFAULT; goto fail;
}
ret = gnttab_pages_set_private(args->nr_pages, args->pages); if (ret < 0) goto fail;
/** * gnttab_dma_free_pages - free DMAable pages * @args: arguments to the function
*/ int gnttab_dma_free_pages(struct gnttab_dma_alloc_args *args)
{
size_t size; int i, ret;
for (i = 0; i < args->nr_pages; i++)
args->frames[i] = page_to_xen_pfn(args->pages[i]);
ret = xenmem_reservation_increase(args->nr_pages, args->frames); if (ret != args->nr_pages) {
pr_debug("Failed to increase reservation for DMA buffer\n");
ret = -EFAULT;
} else {
ret = 0;
}
case GNTST_no_device_space:
pr_warn_ratelimited("maptrack limit reached, can't map all guest pages\n"); break;
case GNTST_eagain: /* Retry eagain maps */
gnttab_retry_eagain_gop(GNTTABOP_map_grant_ref,
map_ops + i,
&map_ops[i].status, __func__); /* Test status in next loop iteration. */
i--; break;
/* No need for kzalloc as it is initialized in following hypercall * GNTTABOP_get_status_frames.
*/
sframes = kmalloc_array(nr_sframes, sizeof(uint64_t), GFP_ATOMIC); if (!sframes) return -ENOMEM;
if (xen_feature(XENFEAT_auto_translated_physmap)) { struct xen_add_to_physmap xatp; unsignedint i = end_idx;
rc = 0;
BUG_ON(xen_auto_xlat_grant_frames.count < nr_gframes); /* * Loop backwards, so that the first hypercall has the largest * index, ensuring that the table will grow only once.
*/ do {
xatp.domid = DOMID_SELF;
xatp.idx = i;
xatp.space = XENMAPSPACE_grant_table;
xatp.gpfn = xen_auto_xlat_grant_frames.pfn[i];
rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp); if (rc != 0) {
pr_warn("grant table add_to_physmap failed, err=%d\n",
rc); break;
}
} while (i-- > start_idx);
return rc;
}
/* No need for kzalloc as it is initialized in following hypercall * GNTTABOP_setup_table.
*/
frames = kmalloc_array(nr_gframes, sizeof(unsignedlong), GFP_ATOMIC); if (!frames) return -ENOMEM;
/* Determine the maximum number of frames required for the * grant reference free list on the current hypervisor.
*/
max_nr_glist_frames = max_nr_grefs / RPP;
ini_nomem: for (i--; i >= 0; i--)
free_page((unsignedlong)gnttab_list[i]);
kfree(gnttab_list);
bitmap_free(gnttab_free_bitmap); return ret;
}
EXPORT_SYMBOL_GPL(gnttab_init);
staticint __gnttab_init(void)
{ if (!xen_domain()) return -ENODEV;
/* Delay grant-table initialization in the PV on HVM case */ if (xen_hvm_domain() && !xen_pvh_domain()) return 0;
return gnttab_init();
} /* Starts after core_initcall so that xen_pvh_gnttab_setup can be called
* beforehand to initialize xen_auto_xlat_grant_frames. */
core_initcall_sync(__gnttab_init);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.