2016-11-17 02:16:13 +05:30
/*
* File attributes for Mediated devices
*
* Copyright ( c ) 2016 , NVIDIA CORPORATION . All rights reserved .
* Author : Neo Jia < cjia @ nvidia . com >
* Kirti Wankhede < kwankhede @ nvidia . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/sysfs.h>
# include <linux/ctype.h>
# include <linux/device.h>
# include <linux/slab.h>
# include <linux/uuid.h>
# include <linux/mdev.h>
# include "mdev_private.h"
/* Static functions */
static ssize_t mdev_type_attr_show ( struct kobject * kobj ,
struct attribute * __attr , char * buf )
{
struct mdev_type_attribute * attr = to_mdev_type_attr ( __attr ) ;
struct mdev_type * type = to_mdev_type ( kobj ) ;
ssize_t ret = - EIO ;
if ( attr - > show )
ret = attr - > show ( kobj , type - > parent - > dev , buf ) ;
return ret ;
}
static ssize_t mdev_type_attr_store ( struct kobject * kobj ,
struct attribute * __attr ,
const char * buf , size_t count )
{
struct mdev_type_attribute * attr = to_mdev_type_attr ( __attr ) ;
struct mdev_type * type = to_mdev_type ( kobj ) ;
ssize_t ret = - EIO ;
if ( attr - > store )
ret = attr - > store ( & type - > kobj , type - > parent - > dev , buf , count ) ;
return ret ;
}
static const struct sysfs_ops mdev_type_sysfs_ops = {
. show = mdev_type_attr_show ,
. store = mdev_type_attr_store ,
} ;
static ssize_t create_store ( struct kobject * kobj , struct device * dev ,
const char * buf , size_t count )
{
char * str ;
uuid_le uuid ;
int ret ;
if ( ( count < UUID_STRING_LEN ) | | ( count > UUID_STRING_LEN + 1 ) )
return - EINVAL ;
str = kstrndup ( buf , count , GFP_KERNEL ) ;
if ( ! str )
return - ENOMEM ;
ret = uuid_le_to_bin ( str , & uuid ) ;
kfree ( str ) ;
if ( ret )
return ret ;
ret = mdev_device_create ( kobj , dev , uuid ) ;
if ( ret )
return ret ;
return count ;
}
MDEV_TYPE_ATTR_WO ( create ) ;
static void mdev_type_release ( struct kobject * kobj )
{
struct mdev_type * type = to_mdev_type ( kobj ) ;
pr_debug ( " Releasing group %s \n " , kobj - > name ) ;
kfree ( type ) ;
}
static struct kobj_type mdev_type_ktype = {
. sysfs_ops = & mdev_type_sysfs_ops ,
. release = mdev_type_release ,
} ;
2016-12-30 08:13:38 -07:00
struct mdev_type * add_mdev_supported_type ( struct mdev_parent * parent ,
2016-11-17 02:16:13 +05:30
struct attribute_group * group )
{
struct mdev_type * type ;
int ret ;
if ( ! group - > name ) {
pr_err ( " %s: Type name empty! \n " , __func__ ) ;
return ERR_PTR ( - EINVAL ) ;
}
type = kzalloc ( sizeof ( * type ) , GFP_KERNEL ) ;
if ( ! type )
return ERR_PTR ( - ENOMEM ) ;
type - > kobj . kset = parent - > mdev_types_kset ;
ret = kobject_init_and_add ( & type - > kobj , & mdev_type_ktype , NULL ,
" %s-%s " , dev_driver_string ( parent - > dev ) ,
group - > name ) ;
if ( ret ) {
kfree ( type ) ;
return ERR_PTR ( ret ) ;
}
ret = sysfs_create_file ( & type - > kobj , & mdev_type_attr_create . attr ) ;
if ( ret )
goto attr_create_failed ;
type - > devices_kobj = kobject_create_and_add ( " devices " , & type - > kobj ) ;
if ( ! type - > devices_kobj ) {
ret = - ENOMEM ;
goto attr_devices_failed ;
}
ret = sysfs_create_files ( & type - > kobj ,
( const struct attribute * * ) group - > attrs ) ;
if ( ret ) {
ret = - ENOMEM ;
goto attrs_failed ;
}
type - > group = group ;
type - > parent = parent ;
return type ;
attrs_failed :
kobject_put ( type - > devices_kobj ) ;
attr_devices_failed :
sysfs_remove_file ( & type - > kobj , & mdev_type_attr_create . attr ) ;
attr_create_failed :
kobject_del ( & type - > kobj ) ;
kobject_put ( & type - > kobj ) ;
return ERR_PTR ( ret ) ;
}
static void remove_mdev_supported_type ( struct mdev_type * type )
{
sysfs_remove_files ( & type - > kobj ,
( const struct attribute * * ) type - > group - > attrs ) ;
kobject_put ( type - > devices_kobj ) ;
sysfs_remove_file ( & type - > kobj , & mdev_type_attr_create . attr ) ;
kobject_del ( & type - > kobj ) ;
kobject_put ( & type - > kobj ) ;
}
2016-12-30 08:13:38 -07:00
static int add_mdev_supported_type_groups ( struct mdev_parent * parent )
2016-11-17 02:16:13 +05:30
{
int i ;
for ( i = 0 ; parent - > ops - > supported_type_groups [ i ] ; i + + ) {
struct mdev_type * type ;
type = add_mdev_supported_type ( parent ,
parent - > ops - > supported_type_groups [ i ] ) ;
if ( IS_ERR ( type ) ) {
struct mdev_type * ltype , * tmp ;
list_for_each_entry_safe ( ltype , tmp , & parent - > type_list ,
next ) {
list_del ( & ltype - > next ) ;
remove_mdev_supported_type ( ltype ) ;
}
return PTR_ERR ( type ) ;
}
list_add ( & type - > next , & parent - > type_list ) ;
}
return 0 ;
}
/* mdev sysfs functions */
2016-12-30 08:13:38 -07:00
void parent_remove_sysfs_files ( struct mdev_parent * parent )
2016-11-17 02:16:13 +05:30
{
struct mdev_type * type , * tmp ;
list_for_each_entry_safe ( type , tmp , & parent - > type_list , next ) {
list_del ( & type - > next ) ;
remove_mdev_supported_type ( type ) ;
}
sysfs_remove_groups ( & parent - > dev - > kobj , parent - > ops - > dev_attr_groups ) ;
kset_unregister ( parent - > mdev_types_kset ) ;
}
2016-12-30 08:13:38 -07:00
int parent_create_sysfs_files ( struct mdev_parent * parent )
2016-11-17 02:16:13 +05:30
{
int ret ;
parent - > mdev_types_kset = kset_create_and_add ( " mdev_supported_types " ,
NULL , & parent - > dev - > kobj ) ;
if ( ! parent - > mdev_types_kset )
return - ENOMEM ;
INIT_LIST_HEAD ( & parent - > type_list ) ;
ret = sysfs_create_groups ( & parent - > dev - > kobj ,
parent - > ops - > dev_attr_groups ) ;
if ( ret )
goto create_err ;
ret = add_mdev_supported_type_groups ( parent ) ;
if ( ret )
sysfs_remove_groups ( & parent - > dev - > kobj ,
parent - > ops - > dev_attr_groups ) ;
else
return ret ;
create_err :
kset_unregister ( parent - > mdev_types_kset ) ;
return ret ;
}
static ssize_t remove_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
unsigned long val ;
if ( kstrtoul ( buf , 0 , & val ) < 0 )
return - EINVAL ;
if ( val & & device_remove_file_self ( dev , attr ) ) {
int ret ;
ret = mdev_device_remove ( dev , false ) ;
if ( ret ) {
device_create_file ( dev , attr ) ;
return ret ;
}
}
return count ;
}
static DEVICE_ATTR_WO ( remove ) ;
static const struct attribute * mdev_device_attrs [ ] = {
& dev_attr_remove . attr ,
NULL ,
} ;
int mdev_create_sysfs_files ( struct device * dev , struct mdev_type * type )
{
int ret ;
ret = sysfs_create_files ( & dev - > kobj , mdev_device_attrs ) ;
if ( ret )
return ret ;
ret = sysfs_create_link ( type - > devices_kobj , & dev - > kobj , dev_name ( dev ) ) ;
if ( ret )
goto device_link_failed ;
ret = sysfs_create_link ( & dev - > kobj , & type - > kobj , " mdev_type " ) ;
if ( ret )
goto type_link_failed ;
return ret ;
type_link_failed :
sysfs_remove_link ( type - > devices_kobj , dev_name ( dev ) ) ;
device_link_failed :
sysfs_remove_files ( & dev - > kobj , mdev_device_attrs ) ;
return ret ;
}
void mdev_remove_sysfs_files ( struct device * dev , struct mdev_type * type )
{
sysfs_remove_link ( & dev - > kobj , " mdev_type " ) ;
sysfs_remove_link ( type - > devices_kobj , dev_name ( dev ) ) ;
sysfs_remove_files ( & dev - > kobj , mdev_device_attrs ) ;
}