diff -r 1e3977e029fd -r 19c77906a845 xen/include/asm-x86/mm.h --- a/xen/include/asm-x86/mm.h Mon May 8 18:21:41 2006 +++ b/xen/include/asm-x86/mm.h Wed May 10 14:30:39 2006 @@ -160,6 +160,7 @@ extern unsigned long max_page; extern unsigned long total_pages; void init_frametable(void); +void io_page_init(void); int alloc_page_type(struct page_info *page, unsigned long type); void free_page_type(struct page_info *page, unsigned long type); diff -r 1e3977e029fd -r 19c77906a845 xen/arch/x86/iomm.c --- /dev/null Mon May 8 18:21:41 2006 +++ b/xen/arch/x86/iomm.c Wed May 10 14:30:39 2006 @@ -0,0 +1,282 @@ +/****************************************************************************** + * iomm.c + * + * Reference counting for I/O Memory Pages - pages of I/O memory are not + * accounted for by the usual means. The frame table covers all normal RAM and + * the bits of I/O memory that fall within RAM blocks (like <1 MB), but + * everything else is not tracked by the frame table. This file handles + * reference counting for the rest of memory by lazily creating structures + * to track resource usage when needed. + */ +#include +#include +#include +#include +#include + +/* struct page_users_entry exists in case a page of I/O memory is shared with + * multiple domains. Hopefully, no one will need this code, but just in case... + */ +struct page_users_entry { + struct page_users_entry *next; + unsigned int count; + struct domain *domain; +}; + +#define COUNT_BITS (20) +#define IOPF_count_mask ((1<mfn = mfn; + io_page->count_flags = 0; + io_page->u.domain = NULL; + io_page_nodes++; + } + + return io_page; +} + +static inline void io_page_destroy(struct io_page *io_page) +{ + struct page_users_entry *cur, *next; + + rb_erase(&io_page->node, &io_page_tree); + + if (io_page->count_flags & IOPF_users_list) + { + for (cur = io_page->u.page_users_list; cur; cur = next) + { + next = cur->next; + xfree(cur); + } + io_page->u.page_users_list = NULL; + } + + io_page_nodes--; + + xfree(io_page); +} + +static struct io_page *find_io_page(unsigned long mfn, int create) +{ + struct rb_node **n = &io_page_tree.rb_node; + struct rb_node *parent = NULL; + struct io_page *io_page; + + while (*n) + { + parent = *n; + io_page = rb_entry(parent, struct io_page, node); + + if (mfn < io_page->mfn) + n = &(*n)->rb_left; + else if (mfn > io_page->mfn) + n = &(*n)->rb_right; + else + return io_page; + } + + if (create) + { + io_page = io_page_create(mfn); + if (io_page) + { + rb_link_node(&io_page->node, parent, n); + rb_insert_color(&io_page->node, &io_page_tree); + return io_page; + } + } + + return NULL; +} + +int get_io_page(unsigned long mfn, struct domain *d, void *data) +{ + struct page_users_entry *e1, *e2; + struct io_page *io_page; + int rc = 0; + + io_page = find_io_page(mfn, 1); + if (!io_page) + return -ENOMEM; + + if ((io_page->count_flags & IOPF_count_mask) == IOPF_count_mask) + { + rc = -EBUSY; + goto out; + } + else if ((io_page->count_flags & IOPF_count_mask) == 0) + { + io_page->count_flags &= ~IOPF_users_list; + io_page->count_flags++; + io_page->u.domain = d; + } + else if (io_page->count_flags & IOPF_users_list) + { + for (e1 = io_page->u.page_users_list; e1; e1 = e1->next) + { + if (e1->domain == d) + { + e1->count++; + break; + } + } + + /* didn't find an entry for this domain */ + if (!e1) + { + e1 = xmalloc(struct page_users_entry); + if (!e1) + { + rc = -ENOMEM; + goto out; + } + e1->next = io_page->u.page_users_list; + e1->count = 1; + io_page->u.page_users_list = e1; + } + + io_page->count_flags++; + } + else + { + if (io_page->u.domain == d) + io_page->count_flags++; + else + { + e1 = xmalloc(struct page_users_entry); + if (!e1) + { + rc = -ENOMEM; + goto out; + } + e2 = xmalloc(struct page_users_entry); + if (!e2) + { + xfree(e1); + rc = -ENOMEM; + goto out; + } + + e1->next = e2; + e2->next = NULL; + + e1->domain = io_page->u.domain; + e2->domain = d; + + e1->count = io_page->count_flags & IOPF_count_mask; + e2->count = 1; + io_page->count_flags++; + + io_page->u.page_users_list = e1; + io_page->count_flags |= IOPF_users_list; + } + } + + out: + if (rc) + io_page_destroy(io_page); + return rc; +} + +void put_io_page(unsigned long mfn, struct domain *d, void *data) +{ + struct io_page *io_page; + struct page_users_entry *e, *prev; + + io_page = find_io_page(mfn, 0); + if (!io_page) + return; + + ASSERT((io_page->count_flags & IOPF_count_mask) > 0); + + if (io_page->count_flags & IOPF_users_list) + { + for (prev = NULL, e = io_page->u.page_users_list; e; + prev = e, e = e->next) + { + if (e->domain == d) + { + if (e->count == 1) + { + if (prev) + prev->next = e->next; + else + io_page->u.page_users_list = e->next; + + xfree(e); + /* If there's only one entry left, we + could remove the list entirely */ + } + else + e->count--; + + io_page->count_flags--; + break; + } + } + } + else if (io_page->u.domain == d) + io_page->count_flags--; + + if ((io_page->count_flags & IOPF_count_mask) == 0) + io_page_destroy(io_page); +} + +u32 get_io_page_count(unsigned long mfn, struct domain *d) +{ + struct io_page *io_page; + struct page_users_entry *e; + + io_page = find_io_page(mfn, 0); + if (!io_page) + return 0; + + if (io_page->count_flags & IOPF_users_list) + { + for (e = io_page->u.page_users_list; e; e = e->next) + { + if (e->domain == d) + return (u32)(e->count); + } + } + else if (io_page->u.domain == d) + return (u32)(io_page->count_flags & IOPF_count_mask); + + return 0; +} + +static void dump_io_page_stats(unsigned char key) +{ + printk("I/O Memory Page Tracking Statistics\n"); + printk("io_page nodes %ld\n", io_page_nodes); + printk("size of io_page node %d\n", sizeof(struct io_page)); +} + +void io_page_init(void) +{ + register_keyhandler('p', dump_io_page_stats, + "dump stats about I/O page usage"); +}