diff -r 14b148d759ae -r cf3c717101a8 linux-2.6-xen-sparse/drivers/xen/xenidc/Makefile --- a/linux-2.6-xen-sparse/drivers/xen/xenidc/Makefile Wed Nov 23 15:08:28 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/Makefile Wed Nov 23 15:13:22 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 14b148d759ae -r cf3c717101a8 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_buffer_resource_provider.c --- /dev/null Wed Nov 23 15:08:28 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_buffer_resource_provider.c Wed Nov 23 15:13:22 2005 @@ -0,0 +1,346 @@ +/*****************************************************************************/ +/* 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 "xenidc_trace.h" + +struct xenidc_buffer_resource_provider { + spinlock_t lock; + struct xenidc_buffer_resource_list resources; + struct 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 +alloc_or_free_pages(struct xenidc_buffer_resource_provider *provider, int free) +{ + int return_value = 0; + int i; + trace_info("%p", provider); + if (free) + goto exit_path; + INIT_LIST_HEAD(&provider->page_list); + for (i = 0; i < provider->resources.pages; i++) { + void *page = (void *)__get_free_page(GFP_KERNEL); + if (page == NULL) { + return_value = -ENOMEM; + goto exit_path; + } + xenidc_buffer_resource_provider_free_page(provider, page); + } + return 0; + + exit_path: + while (!list_empty(&provider->page_list)) { + free_page( + (unsigned long)xenidc_buffer_resource_provider_allocate_page( + provider)); + } + return return_value; +} + +static int +alloc_or_free_grant_refs(struct xenidc_buffer_resource_provider *provider, +int free) +{ + int return_value = 0; + if (provider->resources.grant_references == 0) + return 0; + if (free) + goto exit_path; + return_value = gnttab_alloc_grant_references( + provider->resources.grant_references, + &provider->grant_ref_pool); + if (return_value != 0) { + trace_info("failed to allocate grant reference pool"); + goto exit_no_grant_references; + } + provider->free_resources.grant_references = + provider->resources.grant_references; + return 0; + + exit_path: + gnttab_free_grant_references(provider->grant_ref_pool); + + exit_no_grant_references: + return return_value; +} + +static int +alloc_or_free_page_ranges(struct xenidc_buffer_resource_provider *provider, +int free) +{ + int return_value = 0; + int i; + if (provider->resources.empty_page_ranges == 0) + return 0; + if (free) + goto exit_path; + provider->page = balloon_alloc_empty_page_range( + provider->resources.empty_page_ranges * + provider->resources.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 = kmalloc( + sizeof(struct list_head) * + provider->resources.empty_page_ranges, + GFP_KERNEL); + if (provider->empty_page_range_link == NULL) { + return_value = -ENOMEM; + goto exit_no_empty_page_range_link; + } + for (i = 0; i < provider->resources.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->resources.empty_page_ranges; + provider->free_resources.empty_page_range_page_count = + provider->resources.empty_page_range_page_count; + return 0; + exit_path: + kfree(provider->empty_page_range_link); + exit_no_empty_page_range_link: + balloon_dealloc_empty_page_range(provider->page, + provider->resources.empty_page_ranges * + provider->resources.empty_page_range_page_count); + exit_no_empty_page_range: + return return_value; +} + +static int +xenidc_buffer_resource_provider_init_or_exit( +struct xenidc_buffer_resource_provider *provider, int exit) +{ + int return_value = 0; + trace_info("%p", provider); + if (exit) + goto exit_path; + spin_lock_init(&provider->lock); + provider->free_resources = xenidc_buffer_resource_list_null(); + if( ( return_value = alloc_or_free_pages(provider, 0) ) != 0 ) + goto exit_no_pages; + if( ( return_value = alloc_or_free_grant_refs(provider, 0) ) != 0 ) + goto exit_no_grant_refs; + if( ( return_value = alloc_or_free_page_ranges(provider, 0) ) != 0 ) + goto exit_no_page_ranges; + return 0; + exit_path: + alloc_or_free_page_ranges(provider, 1); + exit_no_page_ranges: + alloc_or_free_grant_refs(provider, 1); + exit_no_grant_refs: + alloc_or_free_pages(provider, 1); + exit_no_pages: + return return_value; +} + +struct xenidc_buffer_resource_provider * +xenidc_allocate_buffer_resource_provider( +struct xenidc_buffer_resource_list resources) +{ + struct xenidc_buffer_resource_provider *provider; + trace(); + provider = kmalloc(sizeof(struct xenidc_buffer_resource_provider), + GFP_KERNEL); + if (provider != NULL) { + provider->resources = resources; + if (xenidc_buffer_resource_provider_init_or_exit(provider, 0) + != 0) { + kfree(provider); + provider = NULL; + } + } + return provider; +} + +void xenidc_free_buffer_resource_provider( +struct xenidc_buffer_resource_provider *provider) +{ + trace(); + (void)xenidc_buffer_resource_provider_init_or_exit(provider, 1); + vfree(provider); +} + +struct xenidc_buffer_resource_list +xenidc_buffer_resource_provider_query_free_resources( +struct xenidc_buffer_resource_provider *provider) +{ + struct xenidc_buffer_resource_list list; + unsigned long flags; + trace(); + 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( +struct xenidc_buffer_resource_provider *provider) +{ + struct list_head *link; + unsigned long flags; + trace(); + spin_lock_irqsave(&provider->lock, flags); + provider->free_resources.pages--; + link = provider->page_list.next; + trace_info("%p", link); + list_del(link); + spin_unlock_irqrestore(&provider->lock, flags); + return link; +} + +void xenidc_buffer_resource_provider_free_page( +struct xenidc_buffer_resource_provider *provider, void *page) +{ + struct list_head *link = page; + unsigned long flags; + trace_info("%p", page); + INIT_LIST_HEAD(link); + 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( +struct xenidc_buffer_resource_provider *provider) +{ + grant_ref_t reference; + unsigned long flags; + trace(); + 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( +struct xenidc_buffer_resource_provider *provider, grant_ref_t reference) +{ + unsigned long flags; + trace(); + 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( +struct xenidc_buffer_resource_provider *provider, unsigned long page_count) +{ + unsigned long page_range; + unsigned long flags; + struct list_head *link; + trace(); + spin_lock_irqsave(&provider->lock, flags); + 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->resources. + 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( +struct xenidc_buffer_resource_provider *provider, unsigned long page_range) +{ + int i; + unsigned long flags; + trace(); + i = ((page_range - provider->mmap_vstart) + / + (PAGE_SIZE + * provider->resources.empty_page_range_page_count) + ); + 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(struct 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 14b148d759ae -r cf3c717101a8 linux-2.6-xen-sparse/include/asm-xen/xenidc_buffer_resource_provider.h --- /dev/null Wed Nov 23 15:08:28 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc_buffer_resource_provider.h Wed Nov 23 15:13:22 2005 @@ -0,0 +1,144 @@ +/*****************************************************************************/ +/* 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. */ + +struct xenidc_buffer_resource_list { + 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 struct xenidc_buffer_resource_list +xenidc_buffer_resource_list_null(void) +{ + struct 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(struct xenidc_buffer_resource_list *a, +struct 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(struct xenidc_buffer_resource_list *a, +struct 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. */ + +extern struct xenidc_buffer_resource_provider * +xenidc_allocate_buffer_resource_provider( +struct xenidc_buffer_resource_list resource_allocation); + +extern void +xenidc_free_buffer_resource_provider( +struct xenidc_buffer_resource_provider *provider); + +/* Find out what resources are currently free for allocation. */ + +extern struct xenidc_buffer_resource_list +xenidc_buffer_resource_provider_query_free_resources( +struct xenidc_buffer_resource_provider *provider); + +/* Resource-type specific allocate/free functions. */ + +extern void * +xenidc_buffer_resource_provider_allocate_page( +struct xenidc_buffer_resource_provider *provider); + +extern void +xenidc_buffer_resource_provider_free_page( +struct xenidc_buffer_resource_provider *provider, void *page); + +extern grant_ref_t +xenidc_buffer_resource_provider_allocate_grant_reference( +struct xenidc_buffer_resource_provider *provider); + +extern void +xenidc_buffer_resource_provider_free_grant_reference( +struct xenidc_buffer_resource_provider *provider, grant_ref_t reference); + +extern unsigned long +xenidc_buffer_resource_provider_allocate_empty_page_range( +struct xenidc_buffer_resource_provider *provider, unsigned long page_count); + +extern void +xenidc_buffer_resource_provider_free_empty_page_range( +struct 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(struct xenidc_buffer_resource_list list); + +#endif