2010-03-18 23:31:34 +03:00
/*
2011-06-06 20:22:23 +04:00
* Xen PCI - handle PCI ( INTx ) and MSI infrastructure calls for PV , HVM and
* initial domain support . We also handle the DSDT _PRT callbacks for GSI ' s
* used in HVM and initial domain mode ( PV does not parse ACPI , so it has no
* concept of GSIs ) . Under PV we hook under the pnbbios API for IRQs and
* 0xcf8 PCI configuration read / write .
2010-03-18 23:31:34 +03:00
*
* Author : Ryan Wilson < hap9 @ epoch . ncsc . mil >
2011-06-06 20:22:23 +04:00
* Konrad Rzeszutek Wilk < konrad . wilk @ oracle . com >
* Stefano Stabellini < stefano . stabellini @ eu . citrix . com >
2010-03-18 23:31:34 +03:00
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/pci.h>
# include <linux/acpi.h>
# include <linux/io.h>
2010-10-21 20:40:08 +04:00
# include <asm/io_apic.h>
2010-03-18 23:31:34 +03:00
# include <asm/pci_x86.h>
# include <asm/xen/hypervisor.h>
2010-06-24 20:50:18 +04:00
# include <xen/features.h>
2010-03-18 23:31:34 +03:00
# include <xen/events.h>
# include <asm/xen/pci.h>
2011-07-06 18:16:21 +04:00
static int xen_pcifront_enable_irq ( struct pci_dev * dev )
{
int rc ;
int share = 1 ;
int pirq ;
u8 gsi ;
rc = pci_read_config_byte ( dev , PCI_INTERRUPT_LINE , & gsi ) ;
if ( rc < 0 ) {
dev_warn ( & dev - > dev , " Xen PCI: failed to read interrupt line: %d \n " ,
rc ) ;
return rc ;
}
2011-07-06 23:15:23 +04:00
/* In PV DomU the Xen PCI backend puts the PIRQ in the interrupt line.*/
pirq = gsi ;
2011-07-06 18:16:21 +04:00
if ( gsi < NR_IRQS_LEGACY )
share = 0 ;
rc = xen_bind_pirq_gsi_to_irq ( gsi , pirq , share , " pcifront " ) ;
if ( rc < 0 ) {
dev_warn ( & dev - > dev , " Xen PCI: failed to bind GSI%d (PIRQ%d) to IRQ: %d \n " ,
gsi , pirq , rc ) ;
return rc ;
}
dev - > irq = rc ;
dev_info ( & dev - > dev , " Xen PCI mapped GSI%d to IRQ%d \n " , gsi , dev - > irq ) ;
return 0 ;
}
2010-06-24 19:42:04 +04:00
# ifdef CONFIG_ACPI
2011-07-06 20:42:43 +04:00
static int xen_register_pirq ( u32 gsi , int gsi_override , int triggering ,
2011-07-06 23:15:23 +04:00
bool set_pirq )
2010-06-24 19:42:04 +04:00
{
2011-07-06 20:42:43 +04:00
int rc , pirq = - 1 , irq = - 1 ;
2010-06-24 19:42:04 +04:00
struct physdev_map_pirq map_irq ;
int shareable = 0 ;
char * name ;
2011-07-06 23:15:23 +04:00
if ( set_pirq )
pirq = gsi ;
2011-07-06 18:16:21 +04:00
map_irq . domid = DOMID_SELF ;
map_irq . type = MAP_PIRQ_TYPE_GSI ;
map_irq . index = gsi ;
map_irq . pirq = pirq ;
rc = HYPERVISOR_physdev_op ( PHYSDEVOP_map_pirq , & map_irq ) ;
if ( rc ) {
printk ( KERN_WARNING " xen map irq failed %d \n " , rc ) ;
return - 1 ;
}
2011-07-06 18:48:22 +04:00
if ( triggering = = ACPI_EDGE_SENSITIVE ) {
shareable = 0 ;
name = " ioapic-edge " ;
} else {
shareable = 1 ;
name = " ioapic-level " ;
}
if ( gsi_override > = 0 )
gsi = gsi_override ;
2011-07-06 20:42:43 +04:00
irq = xen_bind_pirq_gsi_to_irq ( gsi , map_irq . pirq , shareable , name ) ;
2011-07-06 18:48:22 +04:00
if ( irq < 0 )
goto out ;
2011-07-06 20:42:43 +04:00
printk ( KERN_DEBUG " xen: --> pirq=%d -> irq=%d (gsi=%d) \n " , map_irq . pirq , irq , gsi ) ;
2011-07-06 18:16:21 +04:00
out :
return irq ;
}
2011-07-06 20:42:43 +04:00
static int acpi_register_gsi_xen_hvm ( struct device * dev , u32 gsi ,
int trigger , int polarity )
{
if ( ! xen_hvm_domain ( ) )
return - 1 ;
2011-07-06 23:15:23 +04:00
return xen_register_pirq ( gsi , - 1 /* no GSI override */ , trigger ,
false /* no mapping of GSI to PIRQ */ ) ;
2011-07-06 20:42:43 +04:00
}
# ifdef CONFIG_XEN_DOM0
2011-07-06 18:16:21 +04:00
static int xen_register_gsi ( u32 gsi , int gsi_override , int triggering , int polarity )
{
int rc , irq ;
struct physdev_setup_gsi setup_gsi ;
if ( ! xen_pv_domain ( ) )
return - 1 ;
printk ( KERN_DEBUG " xen: registering gsi %u triggering %d polarity %d \n " ,
gsi , triggering , polarity ) ;
2011-07-06 20:42:43 +04:00
irq = xen_register_pirq ( gsi , gsi_override , triggering , true ) ;
2011-07-06 18:16:21 +04:00
setup_gsi . gsi = gsi ;
setup_gsi . triggering = ( triggering = = ACPI_EDGE_SENSITIVE ? 0 : 1 ) ;
setup_gsi . polarity = ( polarity = = ACPI_ACTIVE_HIGH ? 0 : 1 ) ;
rc = HYPERVISOR_physdev_op ( PHYSDEVOP_setup_gsi , & setup_gsi ) ;
if ( rc = = - EEXIST )
printk ( KERN_INFO " Already setup the GSI :%d \n " , gsi ) ;
else if ( rc ) {
printk ( KERN_ERR " Failed to setup GSI :%d, err_code:%d \n " ,
gsi , rc ) ;
}
return irq ;
}
static int acpi_register_gsi_xen ( struct device * dev , u32 gsi ,
int trigger , int polarity )
{
return xen_register_gsi ( gsi , - 1 /* no GSI override */ , trigger , polarity ) ;
}
# endif
2011-07-06 18:41:47 +04:00
# endif
2011-07-06 18:16:21 +04:00
2010-03-18 23:31:34 +03:00
# if defined(CONFIG_PCI_MSI)
# include <linux/msi.h>
2010-07-01 20:10:39 +04:00
# include <asm/msidef.h>
2010-03-18 23:31:34 +03:00
struct xen_pci_frontend_ops * xen_pci_frontend ;
EXPORT_SYMBOL_GPL ( xen_pci_frontend ) ;
2011-07-06 18:16:21 +04:00
static int xen_setup_msi_irqs ( struct pci_dev * dev , int nvec , int type )
{
int irq , ret , i ;
struct msi_desc * msidesc ;
int * v ;
v = kzalloc ( sizeof ( int ) * max ( 1 , nvec ) , GFP_KERNEL ) ;
if ( ! v )
return - ENOMEM ;
if ( type = = PCI_CAP_ID_MSIX )
ret = xen_pci_frontend_enable_msix ( dev , v , nvec ) ;
else
ret = xen_pci_frontend_enable_msi ( dev , v ) ;
if ( ret )
goto error ;
i = 0 ;
list_for_each_entry ( msidesc , & dev - > msi_list , list ) {
irq = xen_bind_pirq_msi_to_irq ( dev , msidesc , v [ i ] , 0 ,
( type = = PCI_CAP_ID_MSIX ) ?
" pcifront-msi-x " :
" pcifront-msi " ,
DOMID_SELF ) ;
2011-09-29 21:26:45 +04:00
if ( irq < 0 ) {
ret = irq ;
2011-07-06 18:16:21 +04:00
goto free ;
2011-09-29 21:26:45 +04:00
}
2011-07-06 18:16:21 +04:00
i + + ;
}
kfree ( v ) ;
return 0 ;
error :
dev_err ( & dev - > dev , " Xen PCI frontend has not registered MSI/MSI-X support! \n " ) ;
free :
kfree ( v ) ;
return ret ;
}
2010-12-01 17:51:44 +03:00
# define XEN_PIRQ_MSI_DATA (MSI_DATA_TRIGGER_EDGE | \
MSI_DATA_LEVEL_ASSERT | ( 3 < < 8 ) | MSI_DATA_VECTOR ( 0 ) )
2010-07-01 20:10:39 +04:00
static void xen_msi_compose_msg ( struct pci_dev * pdev , unsigned int pirq ,
struct msi_msg * msg )
{
/* We set vector == 0 to tell the hypervisor we don't care about it,
* but we want a pirq setup instead .
* We use the dest_id field to pass the pirq that we want . */
msg - > address_hi = MSI_ADDR_BASE_HI | MSI_ADDR_EXT_DEST_ID ( pirq ) ;
msg - > address_lo =
MSI_ADDR_BASE_LO |
MSI_ADDR_DEST_MODE_PHYSICAL |
MSI_ADDR_REDIRECTION_CPU |
MSI_ADDR_DEST_ID ( pirq ) ;
2010-12-01 17:51:44 +03:00
msg - > data = XEN_PIRQ_MSI_DATA ;
2010-07-01 20:10:39 +04:00
}
static int xen_hvm_setup_msi_irqs ( struct pci_dev * dev , int nvec , int type )
{
2011-02-18 19:43:32 +03:00
int irq , pirq ;
2010-07-01 20:10:39 +04:00
struct msi_desc * msidesc ;
struct msi_msg msg ;
list_for_each_entry ( msidesc , & dev - > msi_list , list ) {
2010-12-01 17:51:44 +03:00
__read_msi_msg ( msidesc , & msg ) ;
pirq = MSI_ADDR_EXT_DEST_ID ( msg . address_hi ) |
( ( msg . address_lo > > MSI_ADDR_DEST_ID_SHIFT ) & 0xff ) ;
2011-02-18 19:43:32 +03:00
if ( msg . data ! = XEN_PIRQ_MSI_DATA | |
xen_irq_from_pirq ( pirq ) < 0 ) {
pirq = xen_allocate_pirq_msi ( dev , msidesc ) ;
2011-09-29 21:26:45 +04:00
if ( pirq < 0 ) {
irq = - ENODEV ;
2010-12-01 17:51:44 +03:00
goto error ;
2011-09-29 21:26:45 +04:00
}
2011-02-18 19:43:32 +03:00
xen_msi_compose_msg ( dev , pirq , & msg ) ;
__write_msi_msg ( msidesc , & msg ) ;
dev_dbg ( & dev - > dev , " xen: msi bound to pirq=%d \n " , pirq ) ;
} else {
dev_dbg ( & dev - > dev ,
" xen: msi already bound to pirq=%d \n " , pirq ) ;
2010-12-01 17:51:44 +03:00
}
2011-02-18 19:43:36 +03:00
irq = xen_bind_pirq_msi_to_irq ( dev , msidesc , pirq , 0 ,
2011-02-18 19:43:32 +03:00
( type = = PCI_CAP_ID_MSIX ) ?
2011-04-14 19:17:36 +04:00
" msi-x " : " msi " ,
DOMID_SELF ) ;
2011-02-18 19:43:32 +03:00
if ( irq < 0 )
2010-07-01 20:10:39 +04:00
goto error ;
2011-02-18 19:43:32 +03:00
dev_dbg ( & dev - > dev ,
" xen: msi --> pirq=%d --> irq=%d \n " , pirq , irq ) ;
2010-07-01 20:10:39 +04:00
}
return 0 ;
error :
2011-02-18 19:43:32 +03:00
dev_err ( & dev - > dev ,
" Xen PCI frontend has not registered MSI/MSI-X support! \n " ) ;
2011-09-29 21:26:45 +04:00
return irq ;
2010-07-01 20:10:39 +04:00
}
2011-02-18 19:43:26 +03:00
# ifdef CONFIG_XEN_DOM0
2011-09-22 12:17:57 +04:00
static bool __read_mostly pci_seg_supported = true ;
2010-10-11 18:30:09 +04:00
static int xen_initdom_setup_msi_irqs ( struct pci_dev * dev , int nvec , int type )
{
2011-02-18 20:06:55 +03:00
int ret = 0 ;
2010-10-11 18:30:09 +04:00
struct msi_desc * msidesc ;
list_for_each_entry ( msidesc , & dev - > msi_list , list ) {
2011-02-18 20:06:55 +03:00
struct physdev_map_pirq map_irq ;
2011-04-14 19:17:36 +04:00
domid_t domid ;
domid = ret = xen_find_device_domain_owner ( dev ) ;
/* N.B. Casting int's -ENODEV to uint16_t results in 0xFFED,
* hence check ret value for < 0. */
if ( ret < 0 )
domid = DOMID_SELF ;
2011-02-18 20:06:55 +03:00
memset ( & map_irq , 0 , sizeof ( map_irq ) ) ;
2011-04-14 19:17:36 +04:00
map_irq . domid = domid ;
2011-09-22 12:17:57 +04:00
map_irq . type = MAP_PIRQ_TYPE_MSI_SEG ;
2011-02-18 20:06:55 +03:00
map_irq . index = - 1 ;
map_irq . pirq = - 1 ;
2011-09-22 12:17:57 +04:00
map_irq . bus = dev - > bus - > number |
( pci_domain_nr ( dev - > bus ) < < 16 ) ;
2011-02-18 20:06:55 +03:00
map_irq . devfn = dev - > devfn ;
if ( type = = PCI_CAP_ID_MSIX ) {
int pos ;
u32 table_offset , bir ;
pos = pci_find_capability ( dev , PCI_CAP_ID_MSIX ) ;
pci_read_config_dword ( dev , pos + PCI_MSIX_TABLE ,
& table_offset ) ;
bir = ( u8 ) ( table_offset & PCI_MSIX_FLAGS_BIRMASK ) ;
map_irq . table_base = pci_resource_start ( dev , bir ) ;
map_irq . entry_nr = msidesc - > msi_attrib . entry_nr ;
}
2011-09-22 12:17:57 +04:00
ret = - EINVAL ;
if ( pci_seg_supported )
ret = HYPERVISOR_physdev_op ( PHYSDEVOP_map_pirq ,
& map_irq ) ;
if ( ret = = - EINVAL & & ! pci_domain_nr ( dev - > bus ) ) {
map_irq . type = MAP_PIRQ_TYPE_MSI ;
map_irq . index = - 1 ;
map_irq . pirq = - 1 ;
map_irq . bus = dev - > bus - > number ;
ret = HYPERVISOR_physdev_op ( PHYSDEVOP_map_pirq ,
& map_irq ) ;
if ( ret ! = - EINVAL )
pci_seg_supported = false ;
}
2011-02-18 20:06:55 +03:00
if ( ret ) {
2011-04-14 19:17:36 +04:00
dev_warn ( & dev - > dev , " xen map irq failed %d for %d domain \n " ,
ret , domid ) ;
2011-02-18 20:06:55 +03:00
goto out ;
}
ret = xen_bind_pirq_msi_to_irq ( dev , msidesc ,
map_irq . pirq , map_irq . index ,
( type = = PCI_CAP_ID_MSIX ) ?
2011-04-14 19:17:36 +04:00
" msi-x " : " msi " ,
domid ) ;
2011-02-18 20:06:55 +03:00
if ( ret < 0 )
goto out ;
2010-10-11 18:30:09 +04:00
}
2011-02-18 20:06:55 +03:00
ret = 0 ;
out :
return ret ;
2010-10-11 18:30:09 +04:00
}
2010-03-18 23:31:34 +03:00
# endif
2011-07-06 18:16:21 +04:00
static void xen_teardown_msi_irqs ( struct pci_dev * dev )
2010-03-18 23:31:34 +03:00
{
2011-07-06 18:16:21 +04:00
struct msi_desc * msidesc ;
2010-03-18 23:31:34 +03:00
2011-07-06 18:16:21 +04:00
msidesc = list_entry ( dev - > msi_list . next , struct msi_desc , list ) ;
if ( msidesc - > msi_attrib . is_msix )
xen_pci_frontend_disable_msix ( dev ) ;
else
xen_pci_frontend_disable_msi ( dev ) ;
2010-03-18 23:31:34 +03:00
2011-07-06 18:16:21 +04:00
/* Free the IRQ's and the msidesc using the generic code. */
default_teardown_msi_irqs ( dev ) ;
}
2011-03-10 19:08:07 +03:00
2011-07-06 18:16:21 +04:00
static void xen_teardown_msi_irq ( unsigned int irq )
{
xen_destroy_irq ( irq ) ;
}
2010-03-18 23:31:34 +03:00
2011-07-06 18:16:21 +04:00
# endif
2011-01-11 20:20:13 +03:00
2010-03-18 23:31:34 +03:00
int __init pci_xen_init ( void )
{
if ( ! xen_pv_domain ( ) | | xen_initial_domain ( ) )
return - ENODEV ;
printk ( KERN_INFO " PCI: setting up Xen PCI frontend stub \n " ) ;
pcibios_set_cache_line_size ( ) ;
pcibios_enable_irq = xen_pcifront_enable_irq ;
pcibios_disable_irq = NULL ;
# ifdef CONFIG_ACPI
/* Keep ACPI out of the picture */
acpi_noirq = 1 ;
# endif
# ifdef CONFIG_PCI_MSI
x86_msi . setup_msi_irqs = xen_setup_msi_irqs ;
x86_msi . teardown_msi_irq = xen_teardown_msi_irq ;
x86_msi . teardown_msi_irqs = xen_teardown_msi_irqs ;
# endif
return 0 ;
}
2010-06-24 20:50:18 +04:00
int __init pci_xen_hvm_init ( void )
{
if ( ! xen_feature ( XENFEAT_hvm_pirqs ) )
return 0 ;
# ifdef CONFIG_ACPI
/*
* We don ' t want to change the actual ACPI delivery model ,
* just how GSIs get registered .
*/
__acpi_register_gsi = acpi_register_gsi_xen_hvm ;
# endif
2010-07-01 20:10:39 +04:00
# ifdef CONFIG_PCI_MSI
x86_msi . setup_msi_irqs = xen_hvm_setup_msi_irqs ;
x86_msi . teardown_msi_irq = xen_teardown_msi_irq ;
# endif
2010-06-24 20:50:18 +04:00
return 0 ;
}
2010-09-02 17:51:39 +04:00
# ifdef CONFIG_XEN_DOM0
static __init void xen_setup_acpi_sci ( void )
{
int rc ;
int trigger , polarity ;
int gsi = acpi_sci_override_gsi ;
2011-07-06 17:43:16 +04:00
int irq = - 1 ;
int gsi_override = - 1 ;
2010-09-02 17:51:39 +04:00
if ( ! gsi )
return ;
rc = acpi_get_override_irq ( gsi , & trigger , & polarity ) ;
if ( rc ) {
printk ( KERN_WARNING " xen: acpi_get_override_irq failed for acpi "
" sci, rc=%d \n " , rc ) ;
return ;
}
trigger = trigger ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE ;
polarity = polarity ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH ;
2011-06-06 20:22:23 +04:00
2010-09-02 17:51:39 +04:00
printk ( KERN_INFO " xen: sci override: global_irq=%d trigger=%d "
" polarity=%d \n " , gsi , trigger , polarity ) ;
2011-07-06 17:43:16 +04:00
/* Before we bind the GSI to a Linux IRQ, check whether
* we need to override it with bus_irq ( IRQ ) value . Usually for
* IRQs below IRQ_LEGACY_IRQ this holds IRQ = = GSI , as so :
* ACPI : INT_SRC_OVR ( bus 0 bus_irq 9 global_irq 9 low level )
* but there are oddballs where the IRQ ! = GSI :
* ACPI : INT_SRC_OVR ( bus 0 bus_irq 9 global_irq 20 low level )
* which ends up being : gsi_to_irq [ 9 ] = = 20
* ( which is what acpi_gsi_to_irq ends up calling when starting the
* the ACPI interpreter and keels over since IRQ 9 has not been
* setup as we had setup IRQ 20 for it ) .
*/
if ( acpi_gsi_to_irq ( gsi , & irq ) = = 0 ) {
2011-07-06 21:03:35 +04:00
/* Use the provided value if it's valid. */
if ( irq > = 0 )
2011-07-06 17:43:16 +04:00
gsi_override = irq ;
}
gsi = xen_register_gsi ( gsi , gsi_override , trigger , polarity ) ;
2010-09-02 17:51:39 +04:00
printk ( KERN_INFO " xen: acpi sci %d \n " , gsi ) ;
return ;
}
2011-06-09 17:49:13 +04:00
int __init pci_xen_initial_domain ( void )
2010-09-02 17:51:39 +04:00
{
2011-07-06 23:15:23 +04:00
int irq ;
2011-06-09 17:49:13 +04:00
2010-10-11 18:30:09 +04:00
# ifdef CONFIG_PCI_MSI
x86_msi . setup_msi_irqs = xen_initdom_setup_msi_irqs ;
x86_msi . teardown_msi_irq = xen_teardown_msi_irq ;
# endif
2010-09-02 17:51:39 +04:00
xen_setup_acpi_sci ( ) ;
__acpi_register_gsi = acpi_register_gsi_xen ;
/* Pre-allocate legacy irqs */
for ( irq = 0 ; irq < NR_IRQS_LEGACY ; irq + + ) {
int trigger , polarity ;
if ( acpi_get_override_irq ( irq , & trigger , & polarity ) = = - 1 )
continue ;
2011-07-06 17:43:16 +04:00
xen_register_pirq ( irq , - 1 /* no GSI override */ ,
2011-07-06 20:42:43 +04:00
trigger ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE ,
2011-07-06 23:15:23 +04:00
true /* Map GSI to PIRQ */ ) ;
2010-09-02 17:51:39 +04:00
}
2011-06-06 22:20:35 +04:00
if ( 0 = = nr_ioapics ) {
2011-07-06 23:15:23 +04:00
for ( irq = 0 ; irq < NR_IRQS_LEGACY ; irq + + )
xen_bind_pirq_gsi_to_irq ( irq , irq , 0 , " xt-pic " ) ;
2011-06-06 22:20:35 +04:00
}
2011-06-09 17:49:13 +04:00
return 0 ;
2010-09-02 17:51:39 +04:00
}
2010-11-08 22:13:35 +03:00
struct xen_device_domain_owner {
domid_t domain ;
struct pci_dev * dev ;
struct list_head list ;
} ;
static DEFINE_SPINLOCK ( dev_domain_list_spinlock ) ;
static struct list_head dev_domain_list = LIST_HEAD_INIT ( dev_domain_list ) ;
static struct xen_device_domain_owner * find_device ( struct pci_dev * dev )
{
struct xen_device_domain_owner * owner ;
list_for_each_entry ( owner , & dev_domain_list , list ) {
if ( owner - > dev = = dev )
return owner ;
}
return NULL ;
}
int xen_find_device_domain_owner ( struct pci_dev * dev )
{
struct xen_device_domain_owner * owner ;
int domain = - ENODEV ;
spin_lock ( & dev_domain_list_spinlock ) ;
owner = find_device ( dev ) ;
if ( owner )
domain = owner - > domain ;
spin_unlock ( & dev_domain_list_spinlock ) ;
return domain ;
}
EXPORT_SYMBOL_GPL ( xen_find_device_domain_owner ) ;
int xen_register_device_domain_owner ( struct pci_dev * dev , uint16_t domain )
{
struct xen_device_domain_owner * owner ;
owner = kzalloc ( sizeof ( struct xen_device_domain_owner ) , GFP_KERNEL ) ;
if ( ! owner )
return - ENODEV ;
spin_lock ( & dev_domain_list_spinlock ) ;
if ( find_device ( dev ) ) {
spin_unlock ( & dev_domain_list_spinlock ) ;
kfree ( owner ) ;
return - EEXIST ;
}
owner - > domain = domain ;
owner - > dev = dev ;
list_add_tail ( & owner - > list , & dev_domain_list ) ;
spin_unlock ( & dev_domain_list_spinlock ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( xen_register_device_domain_owner ) ;
int xen_unregister_device_domain_owner ( struct pci_dev * dev )
{
struct xen_device_domain_owner * owner ;
spin_lock ( & dev_domain_list_spinlock ) ;
owner = find_device ( dev ) ;
if ( ! owner ) {
spin_unlock ( & dev_domain_list_spinlock ) ;
return - ENODEV ;
}
list_del ( & owner - > list ) ;
spin_unlock ( & dev_domain_list_spinlock ) ;
kfree ( owner ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( xen_unregister_device_domain_owner ) ;
2011-05-16 21:47:30 +04:00
# endif