2008-04-02 21:54:01 +04:00
/*
* 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
*
* Copyright ( C ) IBM Corp . 2006
*
* Authors : Hollis Blanchard < hollisb @ us . ibm . com >
*/
# include <linux/gfp.h>
# include <linux/mm.h>
# include <asm/page.h>
# include <xen/xencomm.h>
# include <xen/interface/xen.h>
2008-10-15 04:50:41 +04:00
# include <asm/xen/xencomm.h> /* for xencomm_is_phys_contiguous() */
2008-04-02 21:54:01 +04:00
static int xencomm_init ( struct xencomm_desc * desc ,
void * buffer , unsigned long bytes )
{
unsigned long recorded = 0 ;
int i = 0 ;
while ( ( recorded < bytes ) & & ( i < desc - > nr_addrs ) ) {
unsigned long vaddr = ( unsigned long ) buffer + recorded ;
unsigned long paddr ;
int offset ;
int chunksz ;
offset = vaddr % PAGE_SIZE ; /* handle partial pages */
chunksz = min ( PAGE_SIZE - offset , bytes - recorded ) ;
paddr = xencomm_vtop ( vaddr ) ;
if ( paddr = = ~ 0UL ) {
printk ( KERN_DEBUG " %s: couldn't translate vaddr %lx \n " ,
__func__ , vaddr ) ;
return - EINVAL ;
}
desc - > address [ i + + ] = paddr ;
recorded + = chunksz ;
}
if ( recorded < bytes ) {
printk ( KERN_DEBUG
" %s: could only translate %ld of %ld bytes \n " ,
__func__ , recorded , bytes ) ;
return - ENOSPC ;
}
/* mark remaining addresses invalid (just for safety) */
while ( i < desc - > nr_addrs )
desc - > address [ i + + ] = XENCOMM_INVALID ;
desc - > magic = XENCOMM_MAGIC ;
return 0 ;
}
static struct xencomm_desc * xencomm_alloc ( gfp_t gfp_mask ,
void * buffer , unsigned long bytes )
{
struct xencomm_desc * desc ;
unsigned long buffer_ulong = ( unsigned long ) buffer ;
unsigned long start = buffer_ulong & PAGE_MASK ;
unsigned long end = ( buffer_ulong + bytes ) | ~ PAGE_MASK ;
unsigned long nr_addrs = ( end - start + 1 ) > > PAGE_SHIFT ;
unsigned long size = sizeof ( * desc ) +
sizeof ( desc - > address [ 0 ] ) * nr_addrs ;
/*
* slab allocator returns at least sizeof ( void * ) aligned pointer .
* When sizeof ( * desc ) > sizeof ( void * ) , struct xencomm_desc might
* cross page boundary .
*/
if ( sizeof ( * desc ) > sizeof ( void * ) ) {
unsigned long order = get_order ( size ) ;
desc = ( struct xencomm_desc * ) __get_free_pages ( gfp_mask ,
order ) ;
if ( desc = = NULL )
return NULL ;
desc - > nr_addrs =
( ( PAGE_SIZE < < order ) - sizeof ( struct xencomm_desc ) ) /
sizeof ( * desc - > address ) ;
} else {
desc = kmalloc ( size , gfp_mask ) ;
if ( desc = = NULL )
return NULL ;
desc - > nr_addrs = nr_addrs ;
}
return desc ;
}
void xencomm_free ( struct xencomm_handle * desc )
{
if ( desc & & ! ( ( ulong ) desc & XENCOMM_INLINE_FLAG ) ) {
struct xencomm_desc * desc__ = ( struct xencomm_desc * ) desc ;
if ( sizeof ( * desc__ ) > sizeof ( void * ) ) {
unsigned long size = sizeof ( * desc__ ) +
sizeof ( desc__ - > address [ 0 ] ) * desc__ - > nr_addrs ;
unsigned long order = get_order ( size ) ;
free_pages ( ( unsigned long ) __va ( desc ) , order ) ;
} else
kfree ( __va ( desc ) ) ;
}
}
static int xencomm_create ( void * buffer , unsigned long bytes ,
struct xencomm_desc * * ret , gfp_t gfp_mask )
{
struct xencomm_desc * desc ;
int rc ;
pr_debug ( " %s: %p[%ld] \n " , __func__ , buffer , bytes ) ;
if ( bytes = = 0 ) {
/* don't create a descriptor; Xen recognizes NULL. */
BUG_ON ( buffer ! = NULL ) ;
* ret = NULL ;
return 0 ;
}
BUG_ON ( buffer = = NULL ) ; /* 'bytes' is non-zero */
desc = xencomm_alloc ( gfp_mask , buffer , bytes ) ;
if ( ! desc ) {
printk ( KERN_DEBUG " %s failure \n " , " xencomm_alloc " ) ;
return - ENOMEM ;
}
rc = xencomm_init ( desc , buffer , bytes ) ;
if ( rc ) {
printk ( KERN_DEBUG " %s failure: %d \n " , " xencomm_init " , rc ) ;
xencomm_free ( ( struct xencomm_handle * ) __pa ( desc ) ) ;
return rc ;
}
* ret = desc ;
return 0 ;
}
static struct xencomm_handle * xencomm_create_inline ( void * ptr )
{
unsigned long paddr ;
2008-10-15 04:50:41 +04:00
BUG_ON ( ! xencomm_is_phys_contiguous ( ( unsigned long ) ptr ) ) ;
2008-04-02 21:54:01 +04:00
paddr = ( unsigned long ) xencomm_pa ( ptr ) ;
BUG_ON ( paddr & XENCOMM_INLINE_FLAG ) ;
return ( struct xencomm_handle * ) ( paddr | XENCOMM_INLINE_FLAG ) ;
}
/* "mini" routine, for stack-based communications: */
static int xencomm_create_mini ( void * buffer ,
unsigned long bytes , struct xencomm_mini * xc_desc ,
struct xencomm_desc * * ret )
{
int rc = 0 ;
struct xencomm_desc * desc ;
BUG_ON ( ( ( unsigned long ) xc_desc ) % sizeof ( * xc_desc ) ! = 0 ) ;
desc = ( void * ) xc_desc ;
desc - > nr_addrs = XENCOMM_MINI_ADDRS ;
rc = xencomm_init ( desc , buffer , bytes ) ;
if ( ! rc )
* ret = desc ;
return rc ;
}
struct xencomm_handle * xencomm_map ( void * ptr , unsigned long bytes )
{
int rc ;
struct xencomm_desc * desc ;
2008-10-15 04:50:41 +04:00
if ( xencomm_is_phys_contiguous ( ( unsigned long ) ptr ) )
2008-04-02 21:54:01 +04:00
return xencomm_create_inline ( ptr ) ;
rc = xencomm_create ( ptr , bytes , & desc , GFP_KERNEL ) ;
if ( rc | | desc = = NULL )
return NULL ;
return xencomm_pa ( desc ) ;
}
struct xencomm_handle * __xencomm_map_no_alloc ( void * ptr , unsigned long bytes ,
struct xencomm_mini * xc_desc )
{
int rc ;
struct xencomm_desc * desc = NULL ;
2008-10-15 04:50:41 +04:00
if ( xencomm_is_phys_contiguous ( ( unsigned long ) ptr ) )
2008-04-02 21:54:01 +04:00
return xencomm_create_inline ( ptr ) ;
rc = xencomm_create_mini ( ptr , bytes , xc_desc ,
& desc ) ;
if ( rc )
return NULL ;
return xencomm_pa ( desc ) ;
}