diff -r 7adcceaaf851 -r 1e2c0ff9c46a linux-2.6-xen-sparse/drivers/xen/xenidc/Makefile --- a/linux-2.6-xen-sparse/drivers/xen/xenidc/Makefile Sun Nov 20 14:53:27 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/Makefile Sun Nov 20 14:53:55 2005 @@ -3,3 +3,4 @@ xenidc-objs = xenidc-objs += xenidc_callback.o xenidc-objs += xenidc_work.o +xenidc-objs += xenidc_buffer_resource_provider.o diff -r 7adcceaaf851 -r 1e2c0ff9c46a linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_buffer_resource_provider.c --- /dev/null Sun Nov 20 14:53:27 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_buffer_resource_provider.c Sun Nov 20 14:53:55 2005 @@ -0,0 +1,443 @@ +/*****************************************************************************/ +/* Xen inter-domain communication buffer resource provider. */ +/* */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ +/*****************************************************************************/ +/* Based on */ +/* */ +/* arch/xen/drivers/blkback/blkback.c */ +/* */ +/* original copyright notice follows... */ +/*****************************************************************************/ +/****************************************************************************** + * arch/xen/drivers/blkif/backend/main.c + * + * Back-end of the driver for virtual block devices. This portion of the + * driver exports a 'unified' block-device interface that can be accessed + * by any operating system that implements a compatible front end. A + * reference front-end implementation can be found in: + * arch/xen/drivers/blkif/frontend + * + * Copyright (c) 2003-2004, Keir Fraser & Steve Hand + * Copyright (c) 2005, Christopher Clark + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xenidc_trace.h" + +struct xenidc_buffer_resource_provider_struct { + spinlock_t lock; + xenidc_buffer_resource_list resource_allocation; + xenidc_buffer_resource_list free_resources; + struct list_head page_list; + grant_ref_t grant_ref_pool; + struct page *page; + unsigned long mmap_vstart; + struct list_head empty_page_range_list; + struct list_head *empty_page_range_link; +}; + +static int xenidc_buffer_resource_provider_init_or_exit + (xenidc_buffer_resource_provider * provider, int exit) { + trace1("%p", provider); + + { + int return_value = 0; + + if (exit) { + goto EXIT; + } + + spin_lock_init(&provider->lock); + + provider->free_resources = xenidc_buffer_resource_list_null(); + + INIT_LIST_HEAD(&provider->page_list); + + { + int i; + + for (i = 0; i < provider->resource_allocation.pages; + i++) { + void *page = + (void *)__get_free_page(GFP_KERNEL); + + if (page == NULL) { + return_value = -ENOMEM; + + goto EXIT_NO_PAGE; + } + + xenidc_buffer_resource_provider_free_page + (provider, page); + } + } + + if (provider->resource_allocation.grant_references != 0) { + return_value = gnttab_alloc_grant_references + (provider->resource_allocation.grant_references, + &provider->grant_ref_pool); + + if (return_value != 0) { + trace0 + ("failed to allocate grant reference pool"); + + goto EXIT_NO_GRANT_REF; + } + + provider->free_resources.grant_references = + provider->resource_allocation.grant_references; + } + + if (provider->resource_allocation.empty_page_ranges != 0) { + provider->page = balloon_alloc_empty_page_range + (provider->resource_allocation.empty_page_ranges + * + provider->resource_allocation. + empty_page_range_page_count); + + if (provider->page == NULL) { + return_value = -ENOMEM; + + goto EXIT_NO_EMPTY_PAGE_RANGE; + } + + provider->mmap_vstart = + (unsigned long) + pfn_to_kaddr(page_to_pfn(provider->page)); + + INIT_LIST_HEAD(&provider->empty_page_range_list); + + provider->empty_page_range_link = vmalloc + (sizeof(struct list_head) + * provider->resource_allocation.empty_page_ranges); + + if (provider->empty_page_range_link == NULL) { + return_value = -ENOMEM; + + goto EXIT_NO_EMPTY_PAGE_RANGE_LINK; + } + + { + int i; + + for (i = 0; + i < + provider->resource_allocation. + empty_page_ranges; i++) { + struct list_head *link = + &provider->empty_page_range_link[i]; + + INIT_LIST_HEAD(link); + + list_add(link, + &provider-> + empty_page_range_list); + } + } + + provider->free_resources.empty_page_ranges = + provider->resource_allocation.empty_page_ranges; + + provider->free_resources.empty_page_range_page_count = + provider->resource_allocation. + empty_page_range_page_count; + } + + return 0; + + EXIT: + + if (provider->resource_allocation.empty_page_ranges != 0) { + vfree(provider->empty_page_range_link); + + EXIT_NO_EMPTY_PAGE_RANGE_LINK: + + balloon_dealloc_empty_page_range + (provider->page, + provider->resource_allocation.empty_page_ranges + * + provider->resource_allocation. + empty_page_range_page_count); + } + + EXIT_NO_EMPTY_PAGE_RANGE: + + if (provider->resource_allocation.grant_references != 0) { + gnttab_free_grant_references(provider->grant_ref_pool); + } + + EXIT_NO_GRANT_REF: + + EXIT_NO_PAGE: + + while (!list_empty(&provider->page_list)) { + free_page + ((unsigned long) + xenidc_buffer_resource_provider_allocate_page + (provider) + ); + } + + return return_value; + } +} + +xenidc_buffer_resource_provider *xenidc_allocate_buffer_resource_provider + (xenidc_buffer_resource_list resource_allocation) { + trace(); + + { + xenidc_buffer_resource_provider *provider = + (xenidc_buffer_resource_provider *) + vmalloc(sizeof(xenidc_buffer_resource_provider)); + + if (provider != NULL) { + provider->resource_allocation = resource_allocation; + + if (xenidc_buffer_resource_provider_init_or_exit + (provider, 0) + != 0) { + vfree(provider); + + provider = NULL; + } + } + + return provider; + } +} + +void xenidc_free_buffer_resource_provider + (xenidc_buffer_resource_provider * provider) { + trace(); + + (void)xenidc_buffer_resource_provider_init_or_exit(provider, 1); + + vfree(provider); +} + +xenidc_buffer_resource_list + xenidc_buffer_resource_provider_query_free_resources + (xenidc_buffer_resource_provider * provider) { + trace(); + + { + xenidc_buffer_resource_list list; + + unsigned long flags; + + spin_lock_irqsave(&provider->lock, flags); + + list = provider->free_resources; + + spin_unlock_irqrestore(&provider->lock, flags); + + return list; + } +} + +void *xenidc_buffer_resource_provider_allocate_page + (xenidc_buffer_resource_provider * provider) { + trace(); + + { + struct list_head *link; + + unsigned long flags; + + spin_lock_irqsave(&provider->lock, flags); + + provider->free_resources.pages--; + + link = provider->page_list.next; + + trace1("%p", link); + + list_del(link); + + spin_unlock_irqrestore(&provider->lock, flags); + + return link; + } +} + +void xenidc_buffer_resource_provider_free_page + (xenidc_buffer_resource_provider * provider, void *page) { + trace1("%p", page); + + { + struct list_head *link = page; + + INIT_LIST_HEAD(link); + + { + unsigned long flags; + + spin_lock_irqsave(&provider->lock, flags); + + provider->free_resources.pages++; + + list_add(link, &provider->page_list); + + spin_unlock_irqrestore(&provider->lock, flags); + } + } +} + +grant_ref_t xenidc_buffer_resource_provider_allocate_grant_reference + (xenidc_buffer_resource_provider * provider) { + trace(); + + { + grant_ref_t reference; + + { + unsigned long flags; + + spin_lock_irqsave(&provider->lock, flags); + + provider->free_resources.grant_references--; + + reference = + gnttab_claim_grant_reference(&provider-> + grant_ref_pool); + + spin_unlock_irqrestore(&provider->lock, flags); + } + + return reference; + } +} + +void xenidc_buffer_resource_provider_free_grant_reference + (xenidc_buffer_resource_provider * provider, grant_ref_t reference) { + trace(); + + { + unsigned long flags; + + spin_lock_irqsave(&provider->lock, flags); + + gnttab_release_grant_reference(&provider->grant_ref_pool, + reference); + + provider->free_resources.grant_references++; + + spin_unlock_irqrestore(&provider->lock, flags); + } +} + +unsigned long xenidc_buffer_resource_provider_allocate_empty_page_range + (xenidc_buffer_resource_provider * provider, unsigned long page_count) { + trace(); + + { + unsigned long page_range; + + { + unsigned long flags; + + spin_lock_irqsave(&provider->lock, flags); + + { + struct list_head *link = + provider->empty_page_range_list.next; + + list_del_init(link); + + provider->free_resources.empty_page_ranges--; + + page_range = + (provider->mmap_vstart + + + (PAGE_SIZE + * + provider->resource_allocation. + empty_page_range_page_count + * + (((unsigned long)link + - + (unsigned long)provider-> + empty_page_range_link) + / sizeof(struct list_head) + ) + ) + ); + } + + spin_unlock_irqrestore(&provider->lock, flags); + } + + return page_range; + } +} + +void xenidc_buffer_resource_provider_free_empty_page_range + (xenidc_buffer_resource_provider * provider, unsigned long page_range) { + trace(); + + { + int i = ((page_range - provider->mmap_vstart) + / + (PAGE_SIZE + * + provider->resource_allocation. + empty_page_range_page_count) + ); + + unsigned long flags; + + spin_lock_irqsave(&provider->lock, flags); + + list_add + (&provider->empty_page_range_link[i], + &provider->empty_page_range_list); + + provider->free_resources.empty_page_ranges++; + + spin_unlock_irqrestore(&provider->lock, flags); + } +} + +void xenidc_buffer_resource_list_trace(xenidc_buffer_resource_list list) +{ + trace(); + + printk + (KERN_INFO + "xenidc %s: " + "pages:%d; " + "grant_references:%d; " + "empty_page_ranges:%d; " + "empty_page_range_page_count:%d\n", + __PRETTY_FUNCTION__, + list.pages, + list.grant_references, + list.empty_page_ranges, list.empty_page_range_page_count); +} diff -r 7adcceaaf851 -r 1e2c0ff9c46a linux-2.6-xen-sparse/include/asm-xen/xenidc_buffer_resource_provider.h --- /dev/null Sun Nov 20 14:53:27 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc_buffer_resource_provider.h Sun Nov 20 14:53:55 2005 @@ -0,0 +1,138 @@ +/*****************************************************************************/ +/* Xen inter-domain communication buffer resource provider. */ +/* */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ +/* */ +/* The main point of this buffer resource provider code is to make it */ +/* possible to write some other generic code to manipulate various types of */ +/* buffers and keep the other code independent of any buffer-type-specific */ +/* resource management. */ +/* */ +/* The initial implementation of this code provides sets of resources to */ +/* clients in isolated per-client pools but this code provides a centralised */ +/* point of buffer resource management so it will be easy to implement a */ +/* more efficient version which maintains isolation for a base set of */ +/* resources to avoid deadlock and then shares additional resources between */ +/* clients. */ + +#ifndef __ASM_XEN_XENIDC_BUFFER_RESOURCE_PROVIDER_H__ +#define __ASM_XEN_XENIDC_BUFFER_RESOURCE_PROVIDER_H__ + +#include +#include + +/* A xenidc_buffer_resource_list describes a set of resources, sometimes a */ +/* set of resources required for an operation and sometimes a set of */ +/* resources available to perform operations. */ + +typedef struct xenidc_buffer_resource_list_struct xenidc_buffer_resource_list; + +struct xenidc_buffer_resource_list_struct { + u32 pages; + u32 grant_references; + u32 empty_page_ranges; + u32 empty_page_range_page_count; +}; + +/* xenidc_buffer_resource_list_null returns a list specifying no resources. */ + +static inline xenidc_buffer_resource_list xenidc_buffer_resource_list_null(void) +{ + xenidc_buffer_resource_list list; + + memset(&list, 0, sizeof(list)); + + return list; +} + +/* xenidc_buffer_resource_list_subset_of returns true iff the resource */ +/* requirements specified by list 'a' are met by the resources available in */ +/* list 'b'. */ + +static inline int xenidc_buffer_resource_list_subset_of + (xenidc_buffer_resource_list * a, xenidc_buffer_resource_list * b) { + return ((a->pages <= b->pages) + && (a->grant_references <= b->grant_references) + && (a->empty_page_ranges <= b->empty_page_ranges) + && + (a->empty_page_range_page_count <= + b->empty_page_range_page_count) + ); +} + +/* xenidc_buffer_resource_list_plus_equals adds the resource requirements */ +/* specified by list 'b' to the resource requirements specified by list 'a' */ +/* such that the resulting list 'a' specifies a set of resources sufficient */ +/* to meet the sum of the requirements of the original 'a' list and 'b' lists.*/ + +static inline void xenidc_buffer_resource_list_plus_equals + (xenidc_buffer_resource_list * a, xenidc_buffer_resource_list * b) { + a->pages += b->pages; + a->grant_references += b->grant_references; + a->empty_page_ranges += b->empty_page_ranges; + + if (b->empty_page_range_page_count > a->empty_page_range_page_count) { + a->empty_page_range_page_count = b->empty_page_range_page_count; + } +} + +/* Clients allocate a xenidc_buffer_resource_provider to provide themselves */ +/* with a pool of resources. */ + +typedef struct xenidc_buffer_resource_provider_struct + xenidc_buffer_resource_provider; + +extern xenidc_buffer_resource_provider *xenidc_allocate_buffer_resource_provider + (xenidc_buffer_resource_list resource_allocation); + +extern void xenidc_free_buffer_resource_provider + (xenidc_buffer_resource_provider * provider); + +/* Find out what resources are currently free for allocation. */ + +extern xenidc_buffer_resource_list + xenidc_buffer_resource_provider_query_free_resources + (xenidc_buffer_resource_provider * provider); + +/* Resource-type specific allocate/free functions. */ + +extern void *xenidc_buffer_resource_provider_allocate_page + (xenidc_buffer_resource_provider * provider); + +extern void xenidc_buffer_resource_provider_free_page + (xenidc_buffer_resource_provider * provider, void *page); + +extern grant_ref_t xenidc_buffer_resource_provider_allocate_grant_reference + (xenidc_buffer_resource_provider * provider); + +extern void xenidc_buffer_resource_provider_free_grant_reference + (xenidc_buffer_resource_provider * provider, grant_ref_t reference); + +extern unsigned long xenidc_buffer_resource_provider_allocate_empty_page_range + (xenidc_buffer_resource_provider * provider, unsigned long page_count); + +extern void xenidc_buffer_resource_provider_free_empty_page_range + (xenidc_buffer_resource_provider * provider, unsigned long page_range); + +/* xenidc_buffer_resource_list_trace is used for debugging and printks a dump */ +/* of the resource list. */ + +extern void xenidc_buffer_resource_list_trace(xenidc_buffer_resource_list list); + +#endif