2020-03-26 17:01:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* vDPA bus .
*
* Copyright ( c ) 2020 , Red Hat . All rights reserved .
* Author : Jason Wang < jasowang @ redhat . com >
*
*/
# include <linux/module.h>
# include <linux/idr.h>
# include <linux/slab.h>
# include <linux/vdpa.h>
static DEFINE_IDA ( vdpa_index_ida ) ;
static int vdpa_dev_probe ( struct device * d )
{
struct vdpa_device * vdev = dev_to_vdpa ( d ) ;
struct vdpa_driver * drv = drv_to_vdpa ( vdev - > dev . driver ) ;
int ret = 0 ;
if ( drv & & drv - > probe )
ret = drv - > probe ( vdev ) ;
return ret ;
}
static int vdpa_dev_remove ( struct device * d )
{
struct vdpa_device * vdev = dev_to_vdpa ( d ) ;
struct vdpa_driver * drv = drv_to_vdpa ( vdev - > dev . driver ) ;
if ( drv & & drv - > remove )
drv - > remove ( vdev ) ;
return 0 ;
}
static struct bus_type vdpa_bus = {
. name = " vdpa " ,
. probe = vdpa_dev_probe ,
. remove = vdpa_dev_remove ,
} ;
static void vdpa_release_dev ( struct device * d )
{
struct vdpa_device * vdev = dev_to_vdpa ( d ) ;
const struct vdpa_config_ops * ops = vdev - > config ;
if ( ops - > free )
ops - > free ( vdev ) ;
ida_simple_remove ( & vdpa_index_ida , vdev - > index ) ;
kfree ( vdev ) ;
}
/**
* __vdpa_alloc_device - allocate and initilaize a vDPA device
* This allows driver to some prepartion after device is
* initialized but before registered .
* @ parent : the parent device
* @ config : the bus operations that is supported by this device
2020-08-04 19:20:42 +03:00
* @ nvqs : number of virtqueues supported by this device
2020-03-26 17:01:21 +03:00
* @ size : size of the parent structure that contains private data
*
2020-05-27 09:05:28 +03:00
* Driver should use vdpa_alloc_device ( ) wrapper macro instead of
2020-03-26 17:01:21 +03:00
* using this directly .
*
* Returns an error when parent / config / dma_dev is not set or fail to get
* ida .
*/
struct vdpa_device * __vdpa_alloc_device ( struct device * parent ,
const struct vdpa_config_ops * config ,
2020-08-04 19:20:42 +03:00
int nvqs ,
2020-03-26 17:01:21 +03:00
size_t size )
{
struct vdpa_device * vdev ;
int err = - EINVAL ;
if ( ! config )
goto err ;
if ( ! ! config - > dma_map ! = ! ! config - > dma_unmap )
goto err ;
err = - ENOMEM ;
vdev = kzalloc ( size , GFP_KERNEL ) ;
if ( ! vdev )
goto err ;
err = ida_simple_get ( & vdpa_index_ida , 0 , 0 , GFP_KERNEL ) ;
if ( err < 0 )
goto err_ida ;
vdev - > dev . bus = & vdpa_bus ;
vdev - > dev . parent = parent ;
vdev - > dev . release = vdpa_release_dev ;
vdev - > index = err ;
vdev - > config = config ;
2020-07-27 17:51:55 +03:00
vdev - > features_valid = false ;
2020-08-04 19:20:42 +03:00
vdev - > nvqs = nvqs ;
2020-03-26 17:01:21 +03:00
err = dev_set_name ( & vdev - > dev , " vdpa%u " , vdev - > index ) ;
if ( err )
goto err_name ;
device_initialize ( & vdev - > dev ) ;
return vdev ;
err_name :
ida_simple_remove ( & vdpa_index_ida , vdev - > index ) ;
err_ida :
kfree ( vdev ) ;
err :
return ERR_PTR ( err ) ;
}
EXPORT_SYMBOL_GPL ( __vdpa_alloc_device ) ;
/**
* vdpa_register_device - register a vDPA device
2020-04-13 12:37:38 +03:00
* Callers must have a succeed call of vdpa_alloc_device ( ) before .
2020-03-26 17:01:21 +03:00
* @ vdev : the vdpa device to be registered to vDPA bus
*
* Returns an error when fail to add to vDPA bus
*/
int vdpa_register_device ( struct vdpa_device * vdev )
{
return device_add ( & vdev - > dev ) ;
}
EXPORT_SYMBOL_GPL ( vdpa_register_device ) ;
/**
* vdpa_unregister_device - unregister a vDPA device
* @ vdev : the vdpa device to be unregisted from vDPA bus
*/
void vdpa_unregister_device ( struct vdpa_device * vdev )
{
device_unregister ( & vdev - > dev ) ;
}
EXPORT_SYMBOL_GPL ( vdpa_unregister_device ) ;
/**
* __vdpa_register_driver - register a vDPA device driver
* @ drv : the vdpa device driver to be registered
* @ owner : module owner of the driver
*
* Returns an err when fail to do the registration
*/
int __vdpa_register_driver ( struct vdpa_driver * drv , struct module * owner )
{
drv - > driver . bus = & vdpa_bus ;
drv - > driver . owner = owner ;
return driver_register ( & drv - > driver ) ;
}
EXPORT_SYMBOL_GPL ( __vdpa_register_driver ) ;
/**
* vdpa_unregister_driver - unregister a vDPA device driver
* @ drv : the vdpa device driver to be unregistered
*/
void vdpa_unregister_driver ( struct vdpa_driver * drv )
{
driver_unregister ( & drv - > driver ) ;
}
EXPORT_SYMBOL_GPL ( vdpa_unregister_driver ) ;
static int vdpa_init ( void )
{
return bus_register ( & vdpa_bus ) ;
}
static void __exit vdpa_exit ( void )
{
bus_unregister ( & vdpa_bus ) ;
ida_destroy ( & vdpa_index_ida ) ;
}
core_initcall ( vdpa_init ) ;
module_exit ( vdpa_exit ) ;
MODULE_AUTHOR ( " Jason Wang <jasowang@redhat.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;