2018-01-26 21:50:27 +03:00
// SPDX-License-Identifier: GPL-2.0
2020-07-29 23:12:19 +03:00
/*
2017-03-27 12:44:59 +03:00
* configfs to configure the PCI endpoint
*
* Copyright ( C ) 2017 Texas Instruments
* Author : Kishon Vijay Abraham I < kishon @ ti . com >
*/
# include <linux/module.h>
2018-01-30 23:56:57 +03:00
# include <linux/idr.h>
2017-03-27 12:44:59 +03:00
# include <linux/slab.h>
# include <linux/pci-epc.h>
# include <linux/pci-epf.h>
# include <linux/pci-ep-cfs.h>
2018-01-30 23:56:57 +03:00
static DEFINE_IDR ( functions_idr ) ;
static DEFINE_MUTEX ( functions_mutex ) ;
2017-03-27 12:44:59 +03:00
static struct config_group * functions_group ;
static struct config_group * controllers_group ;
struct pci_epf_group {
struct config_group group ;
2021-02-01 22:57:59 +03:00
struct config_group primary_epc_group ;
struct config_group secondary_epc_group ;
struct delayed_work cfs_work ;
2017-03-27 12:44:59 +03:00
struct pci_epf * epf ;
2018-01-30 23:56:57 +03:00
int index ;
2017-03-27 12:44:59 +03:00
} ;
struct pci_epc_group {
struct config_group group ;
struct pci_epc * epc ;
bool start ;
} ;
static inline struct pci_epf_group * to_pci_epf_group ( struct config_item * item )
{
return container_of ( to_config_group ( item ) , struct pci_epf_group , group ) ;
}
static inline struct pci_epc_group * to_pci_epc_group ( struct config_item * item )
{
return container_of ( to_config_group ( item ) , struct pci_epc_group , group ) ;
}
2021-02-01 22:57:59 +03:00
static int pci_secondary_epc_epf_link ( struct config_item * epf_item ,
struct config_item * epc_item )
{
int ret ;
struct pci_epf_group * epf_group = to_pci_epf_group ( epf_item - > ci_parent ) ;
struct pci_epc_group * epc_group = to_pci_epc_group ( epc_item ) ;
struct pci_epc * epc = epc_group - > epc ;
struct pci_epf * epf = epf_group - > epf ;
ret = pci_epc_add_epf ( epc , epf , SECONDARY_INTERFACE ) ;
if ( ret )
return ret ;
ret = pci_epf_bind ( epf ) ;
if ( ret ) {
pci_epc_remove_epf ( epc , epf , SECONDARY_INTERFACE ) ;
return ret ;
}
2024-03-27 12:13:37 +03:00
/* Send any pending EPC initialization complete to the EPF driver */
pci_epc_notify_pending_init ( epc , epf ) ;
2021-02-01 22:57:59 +03:00
return 0 ;
}
static void pci_secondary_epc_epf_unlink ( struct config_item * epc_item ,
struct config_item * epf_item )
{
struct pci_epf_group * epf_group = to_pci_epf_group ( epf_item - > ci_parent ) ;
struct pci_epc_group * epc_group = to_pci_epc_group ( epc_item ) ;
struct pci_epc * epc ;
struct pci_epf * epf ;
WARN_ON_ONCE ( epc_group - > start ) ;
epc = epc_group - > epc ;
epf = epf_group - > epf ;
pci_epf_unbind ( epf ) ;
pci_epc_remove_epf ( epc , epf , SECONDARY_INTERFACE ) ;
}
static struct configfs_item_operations pci_secondary_epc_item_ops = {
. allow_link = pci_secondary_epc_epf_link ,
. drop_link = pci_secondary_epc_epf_unlink ,
} ;
static const struct config_item_type pci_secondary_epc_type = {
. ct_item_ops = & pci_secondary_epc_item_ops ,
. ct_owner = THIS_MODULE ,
} ;
static struct config_group
* pci_ep_cfs_add_secondary_group ( struct pci_epf_group * epf_group )
{
struct config_group * secondary_epc_group ;
secondary_epc_group = & epf_group - > secondary_epc_group ;
config_group_init_type_name ( secondary_epc_group , " secondary " ,
& pci_secondary_epc_type ) ;
configfs_register_group ( & epf_group - > group , secondary_epc_group ) ;
return secondary_epc_group ;
}
static int pci_primary_epc_epf_link ( struct config_item * epf_item ,
struct config_item * epc_item )
{
int ret ;
struct pci_epf_group * epf_group = to_pci_epf_group ( epf_item - > ci_parent ) ;
struct pci_epc_group * epc_group = to_pci_epc_group ( epc_item ) ;
struct pci_epc * epc = epc_group - > epc ;
struct pci_epf * epf = epf_group - > epf ;
ret = pci_epc_add_epf ( epc , epf , PRIMARY_INTERFACE ) ;
if ( ret )
return ret ;
ret = pci_epf_bind ( epf ) ;
if ( ret ) {
pci_epc_remove_epf ( epc , epf , PRIMARY_INTERFACE ) ;
return ret ;
}
2024-03-27 12:13:37 +03:00
/* Send any pending EPC initialization complete to the EPF driver */
pci_epc_notify_pending_init ( epc , epf ) ;
2021-02-01 22:57:59 +03:00
return 0 ;
}
static void pci_primary_epc_epf_unlink ( struct config_item * epc_item ,
struct config_item * epf_item )
{
struct pci_epf_group * epf_group = to_pci_epf_group ( epf_item - > ci_parent ) ;
struct pci_epc_group * epc_group = to_pci_epc_group ( epc_item ) ;
struct pci_epc * epc ;
struct pci_epf * epf ;
WARN_ON_ONCE ( epc_group - > start ) ;
epc = epc_group - > epc ;
epf = epf_group - > epf ;
pci_epf_unbind ( epf ) ;
pci_epc_remove_epf ( epc , epf , PRIMARY_INTERFACE ) ;
}
static struct configfs_item_operations pci_primary_epc_item_ops = {
. allow_link = pci_primary_epc_epf_link ,
. drop_link = pci_primary_epc_epf_unlink ,
} ;
static const struct config_item_type pci_primary_epc_type = {
. ct_item_ops = & pci_primary_epc_item_ops ,
. ct_owner = THIS_MODULE ,
} ;
static struct config_group
* pci_ep_cfs_add_primary_group ( struct pci_epf_group * epf_group )
{
struct config_group * primary_epc_group = & epf_group - > primary_epc_group ;
config_group_init_type_name ( primary_epc_group , " primary " ,
& pci_primary_epc_type ) ;
configfs_register_group ( & epf_group - > group , primary_epc_group ) ;
return primary_epc_group ;
}
2017-03-27 12:44:59 +03:00
static ssize_t pci_epc_start_store ( struct config_item * item , const char * page ,
size_t len )
{
int ret ;
bool start ;
struct pci_epc * epc ;
struct pci_epc_group * epc_group = to_pci_epc_group ( item ) ;
epc = epc_group - > epc ;
2021-09-16 02:01:26 +03:00
if ( kstrtobool ( page , & start ) < 0 )
return - EINVAL ;
2017-03-27 12:44:59 +03:00
2023-06-02 14:47:50 +03:00
if ( start = = epc_group - > start )
return - EALREADY ;
2017-03-27 12:44:59 +03:00
if ( ! start ) {
pci_epc_stop ( epc ) ;
2020-02-26 08:52:23 +03:00
epc_group - > start = 0 ;
2017-03-27 12:44:59 +03:00
return len ;
}
ret = pci_epc_start ( epc ) ;
if ( ret ) {
dev_err ( & epc - > dev , " failed to start endpoint controller \n " ) ;
return - EINVAL ;
}
epc_group - > start = start ;
return len ;
}
static ssize_t pci_epc_start_show ( struct config_item * item , char * page )
{
2021-09-01 08:09:17 +03:00
return sysfs_emit ( page , " %d \n " , to_pci_epc_group ( item ) - > start ) ;
2017-03-27 12:44:59 +03:00
}
CONFIGFS_ATTR ( pci_epc_ , start ) ;
static struct configfs_attribute * pci_epc_attrs [ ] = {
& pci_epc_attr_start ,
NULL ,
} ;
static int pci_epc_epf_link ( struct config_item * epc_item ,
struct config_item * epf_item )
{
int ret ;
struct pci_epf_group * epf_group = to_pci_epf_group ( epf_item ) ;
struct pci_epc_group * epc_group = to_pci_epc_group ( epc_item ) ;
2017-12-14 16:01:45 +03:00
struct pci_epc * epc = epc_group - > epc ;
struct pci_epf * epf = epf_group - > epf ;
2017-03-27 12:44:59 +03:00
2021-02-01 22:57:58 +03:00
ret = pci_epc_add_epf ( epc , epf , PRIMARY_INTERFACE ) ;
2017-12-14 16:01:45 +03:00
if ( ret )
2020-02-24 12:53:38 +03:00
return ret ;
2017-12-14 16:01:45 +03:00
2017-03-27 12:44:59 +03:00
ret = pci_epf_bind ( epf ) ;
2020-02-24 12:53:38 +03:00
if ( ret ) {
2021-02-01 22:57:58 +03:00
pci_epc_remove_epf ( epc , epf , PRIMARY_INTERFACE ) ;
2020-02-24 12:53:38 +03:00
return ret ;
}
2017-03-27 12:44:59 +03:00
2024-03-27 12:13:37 +03:00
/* Send any pending EPC initialization complete to the EPF driver */
pci_epc_notify_pending_init ( epc , epf ) ;
2017-03-27 12:44:59 +03:00
return 0 ;
}
static void pci_epc_epf_unlink ( struct config_item * epc_item ,
struct config_item * epf_item )
{
struct pci_epc * epc ;
struct pci_epf * epf ;
struct pci_epf_group * epf_group = to_pci_epf_group ( epf_item ) ;
struct pci_epc_group * epc_group = to_pci_epc_group ( epc_item ) ;
WARN_ON_ONCE ( epc_group - > start ) ;
epc = epc_group - > epc ;
epf = epf_group - > epf ;
pci_epf_unbind ( epf ) ;
2021-02-01 22:57:58 +03:00
pci_epc_remove_epf ( epc , epf , PRIMARY_INTERFACE ) ;
2017-03-27 12:44:59 +03:00
}
static struct configfs_item_operations pci_epc_item_ops = {
. allow_link = pci_epc_epf_link ,
. drop_link = pci_epc_epf_unlink ,
} ;
2017-10-16 18:18:45 +03:00
static const struct config_item_type pci_epc_type = {
2017-03-27 12:44:59 +03:00
. ct_item_ops = & pci_epc_item_ops ,
. ct_attrs = pci_epc_attrs ,
. ct_owner = THIS_MODULE ,
} ;
struct config_group * pci_ep_cfs_add_epc_group ( const char * name )
{
int ret ;
struct pci_epc * epc ;
struct config_group * group ;
struct pci_epc_group * epc_group ;
epc_group = kzalloc ( sizeof ( * epc_group ) , GFP_KERNEL ) ;
if ( ! epc_group ) {
ret = - ENOMEM ;
goto err ;
}
group = & epc_group - > group ;
config_group_init_type_name ( group , name , & pci_epc_type ) ;
ret = configfs_register_group ( controllers_group , group ) ;
if ( ret ) {
pr_err ( " failed to register configfs group for %s \n " , name ) ;
goto err_register_group ;
}
epc = pci_epc_get ( name ) ;
if ( IS_ERR ( epc ) ) {
ret = PTR_ERR ( epc ) ;
goto err_epc_get ;
}
epc_group - > epc = epc ;
return group ;
err_epc_get :
configfs_unregister_group ( group ) ;
err_register_group :
kfree ( epc_group ) ;
err :
return ERR_PTR ( ret ) ;
}
EXPORT_SYMBOL ( pci_ep_cfs_add_epc_group ) ;
void pci_ep_cfs_remove_epc_group ( struct config_group * group )
{
struct pci_epc_group * epc_group ;
if ( ! group )
return ;
epc_group = container_of ( group , struct pci_epc_group , group ) ;
pci_epc_put ( epc_group - > epc ) ;
configfs_unregister_group ( & epc_group - > group ) ;
kfree ( epc_group ) ;
}
EXPORT_SYMBOL ( pci_ep_cfs_remove_epc_group ) ;
# define PCI_EPF_HEADER_R(_name) \
static ssize_t pci_epf_ # # _name # # _show ( struct config_item * item , char * page ) \
{ \
struct pci_epf * epf = to_pci_epf_group ( item ) - > epf ; \
if ( WARN_ON_ONCE ( ! epf - > header ) ) \
return - EINVAL ; \
2021-09-01 08:09:17 +03:00
return sysfs_emit ( page , " 0x%04x \n " , epf - > header - > _name ) ; \
2017-03-27 12:44:59 +03:00
}
# define PCI_EPF_HEADER_W_u32(_name) \
static ssize_t pci_epf_ # # _name # # _store ( struct config_item * item , \
const char * page , size_t len ) \
{ \
u32 val ; \
struct pci_epf * epf = to_pci_epf_group ( item ) - > epf ; \
if ( WARN_ON_ONCE ( ! epf - > header ) ) \
return - EINVAL ; \
2021-09-16 02:01:26 +03:00
if ( kstrtou32 ( page , 0 , & val ) < 0 ) \
return - EINVAL ; \
2017-03-27 12:44:59 +03:00
epf - > header - > _name = val ; \
return len ; \
}
# define PCI_EPF_HEADER_W_u16(_name) \
static ssize_t pci_epf_ # # _name # # _store ( struct config_item * item , \
const char * page , size_t len ) \
{ \
u16 val ; \
struct pci_epf * epf = to_pci_epf_group ( item ) - > epf ; \
if ( WARN_ON_ONCE ( ! epf - > header ) ) \
return - EINVAL ; \
2021-09-16 02:01:26 +03:00
if ( kstrtou16 ( page , 0 , & val ) < 0 ) \
return - EINVAL ; \
2017-03-27 12:44:59 +03:00
epf - > header - > _name = val ; \
return len ; \
}
# define PCI_EPF_HEADER_W_u8(_name) \
static ssize_t pci_epf_ # # _name # # _store ( struct config_item * item , \
const char * page , size_t len ) \
{ \
u8 val ; \
struct pci_epf * epf = to_pci_epf_group ( item ) - > epf ; \
if ( WARN_ON_ONCE ( ! epf - > header ) ) \
return - EINVAL ; \
2021-09-16 02:01:26 +03:00
if ( kstrtou8 ( page , 0 , & val ) < 0 ) \
return - EINVAL ; \
2017-03-27 12:44:59 +03:00
epf - > header - > _name = val ; \
return len ; \
}
static ssize_t pci_epf_msi_interrupts_store ( struct config_item * item ,
const char * page , size_t len )
{
u8 val ;
2021-09-16 02:01:26 +03:00
if ( kstrtou8 ( page , 0 , & val ) < 0 )
return - EINVAL ;
2017-03-27 12:44:59 +03:00
to_pci_epf_group ( item ) - > epf - > msi_interrupts = val ;
return len ;
}
static ssize_t pci_epf_msi_interrupts_show ( struct config_item * item ,
char * page )
{
2021-09-01 08:09:17 +03:00
return sysfs_emit ( page , " %d \n " ,
to_pci_epf_group ( item ) - > epf - > msi_interrupts ) ;
2017-03-27 12:44:59 +03:00
}
2018-07-19 11:32:12 +03:00
static ssize_t pci_epf_msix_interrupts_store ( struct config_item * item ,
const char * page , size_t len )
{
u16 val ;
2021-09-16 02:01:26 +03:00
if ( kstrtou16 ( page , 0 , & val ) < 0 )
return - EINVAL ;
2018-07-19 11:32:12 +03:00
to_pci_epf_group ( item ) - > epf - > msix_interrupts = val ;
return len ;
}
static ssize_t pci_epf_msix_interrupts_show ( struct config_item * item ,
char * page )
{
2021-09-01 08:09:17 +03:00
return sysfs_emit ( page , " %d \n " ,
to_pci_epf_group ( item ) - > epf - > msix_interrupts ) ;
2018-07-19 11:32:12 +03:00
}
2017-03-27 12:44:59 +03:00
PCI_EPF_HEADER_R ( vendorid )
PCI_EPF_HEADER_W_u16 ( vendorid )
PCI_EPF_HEADER_R ( deviceid )
PCI_EPF_HEADER_W_u16 ( deviceid )
PCI_EPF_HEADER_R ( revid )
PCI_EPF_HEADER_W_u8 ( revid )
PCI_EPF_HEADER_R ( progif_code )
PCI_EPF_HEADER_W_u8 ( progif_code )
PCI_EPF_HEADER_R ( subclass_code )
PCI_EPF_HEADER_W_u8 ( subclass_code )
PCI_EPF_HEADER_R ( baseclass_code )
PCI_EPF_HEADER_W_u8 ( baseclass_code )
PCI_EPF_HEADER_R ( cache_line_size )
PCI_EPF_HEADER_W_u8 ( cache_line_size )
PCI_EPF_HEADER_R ( subsys_vendor_id )
PCI_EPF_HEADER_W_u16 ( subsys_vendor_id )
PCI_EPF_HEADER_R ( subsys_id )
PCI_EPF_HEADER_W_u16 ( subsys_id )
PCI_EPF_HEADER_R ( interrupt_pin )
PCI_EPF_HEADER_W_u8 ( interrupt_pin )
CONFIGFS_ATTR ( pci_epf_ , vendorid ) ;
CONFIGFS_ATTR ( pci_epf_ , deviceid ) ;
CONFIGFS_ATTR ( pci_epf_ , revid ) ;
CONFIGFS_ATTR ( pci_epf_ , progif_code ) ;
CONFIGFS_ATTR ( pci_epf_ , subclass_code ) ;
CONFIGFS_ATTR ( pci_epf_ , baseclass_code ) ;
CONFIGFS_ATTR ( pci_epf_ , cache_line_size ) ;
CONFIGFS_ATTR ( pci_epf_ , subsys_vendor_id ) ;
CONFIGFS_ATTR ( pci_epf_ , subsys_id ) ;
CONFIGFS_ATTR ( pci_epf_ , interrupt_pin ) ;
CONFIGFS_ATTR ( pci_epf_ , msi_interrupts ) ;
2018-07-19 11:32:12 +03:00
CONFIGFS_ATTR ( pci_epf_ , msix_interrupts ) ;
2017-03-27 12:44:59 +03:00
static struct configfs_attribute * pci_epf_attrs [ ] = {
& pci_epf_attr_vendorid ,
& pci_epf_attr_deviceid ,
& pci_epf_attr_revid ,
& pci_epf_attr_progif_code ,
& pci_epf_attr_subclass_code ,
& pci_epf_attr_baseclass_code ,
& pci_epf_attr_cache_line_size ,
& pci_epf_attr_subsys_vendor_id ,
& pci_epf_attr_subsys_id ,
& pci_epf_attr_interrupt_pin ,
& pci_epf_attr_msi_interrupts ,
2018-07-19 11:32:12 +03:00
& pci_epf_attr_msix_interrupts ,
2017-03-27 12:44:59 +03:00
NULL ,
} ;
2021-08-19 15:33:38 +03:00
static int pci_epf_vepf_link ( struct config_item * epf_pf_item ,
struct config_item * epf_vf_item )
{
struct pci_epf_group * epf_vf_group = to_pci_epf_group ( epf_vf_item ) ;
struct pci_epf_group * epf_pf_group = to_pci_epf_group ( epf_pf_item ) ;
struct pci_epf * epf_pf = epf_pf_group - > epf ;
struct pci_epf * epf_vf = epf_vf_group - > epf ;
return pci_epf_add_vepf ( epf_pf , epf_vf ) ;
}
static void pci_epf_vepf_unlink ( struct config_item * epf_pf_item ,
struct config_item * epf_vf_item )
{
struct pci_epf_group * epf_vf_group = to_pci_epf_group ( epf_vf_item ) ;
struct pci_epf_group * epf_pf_group = to_pci_epf_group ( epf_pf_item ) ;
struct pci_epf * epf_pf = epf_pf_group - > epf ;
struct pci_epf * epf_vf = epf_vf_group - > epf ;
pci_epf_remove_vepf ( epf_pf , epf_vf ) ;
}
2017-03-27 12:44:59 +03:00
static void pci_epf_release ( struct config_item * item )
{
struct pci_epf_group * epf_group = to_pci_epf_group ( item ) ;
2018-01-30 23:56:57 +03:00
mutex_lock ( & functions_mutex ) ;
idr_remove ( & functions_idr , epf_group - > index ) ;
mutex_unlock ( & functions_mutex ) ;
2017-03-27 12:44:59 +03:00
pci_epf_destroy ( epf_group - > epf ) ;
kfree ( epf_group ) ;
}
static struct configfs_item_operations pci_epf_ops = {
2021-08-19 15:33:38 +03:00
. allow_link = pci_epf_vepf_link ,
. drop_link = pci_epf_vepf_unlink ,
2017-03-27 12:44:59 +03:00
. release = pci_epf_release ,
} ;
2017-10-16 18:18:45 +03:00
static const struct config_item_type pci_epf_type = {
2017-03-27 12:44:59 +03:00
. ct_item_ops = & pci_epf_ops ,
. ct_attrs = pci_epf_attrs ,
. ct_owner = THIS_MODULE ,
} ;
2023-04-15 05:35:27 +03:00
/**
* pci_epf_type_add_cfs ( ) - Help function drivers to expose function specific
* attributes in configfs
* @ epf : the EPF device that has to be configured using configfs
* @ group : the parent configfs group ( corresponding to entries in
* pci_epf_device_id )
*
* Invoke to expose function specific attributes in configfs .
*
* Return : A pointer to a config_group structure or NULL if the function driver
* does not have anything to expose ( attributes configured by user ) or if
* the function driver does not implement the add_cfs ( ) method .
*
* Returns an error pointer if this function is called for an unbound EPF device
* or if the EPF driver add_cfs ( ) method fails .
*/
static struct config_group * pci_epf_type_add_cfs ( struct pci_epf * epf ,
struct config_group * group )
{
struct config_group * epf_type_group ;
if ( ! epf - > driver ) {
dev_err ( & epf - > dev , " epf device not bound to driver \n " ) ;
2023-05-15 10:43:47 +03:00
return ERR_PTR ( - ENODEV ) ;
2023-04-15 05:35:27 +03:00
}
if ( ! epf - > driver - > ops - > add_cfs )
return NULL ;
mutex_lock ( & epf - > lock ) ;
epf_type_group = epf - > driver - > ops - > add_cfs ( epf , group ) ;
mutex_unlock ( & epf - > lock ) ;
return epf_type_group ;
}
PCI: endpoint: Automatically create a function specific attributes group
A PCI endpoint function driver can define function specific attributes
under its function configfs directory using the add_cfs() endpoint driver
operation. This is done by tying up the mkdir operation for the function
configfs directory to a call to the add_cfs() operation. However, there
are no checks preventing the user from repeatedly creating function
specific attribute directories with different names, resulting in the same
endpoint specific attributes group being added multiple times, which also
result in an invalid reference counting for the attribute groups. E.g.,
using the pci-epf-ntb function driver as an example, the user creates the
function as follows:
$ modprobe pci-epf-ntb
$ cd /sys/kernel/config/pci_ep/functions/pci_epf_ntb
$ mkdir func0
$ tree func0
func0/
|-- baseclass_code
|-- cache_line_size
|-- ...
`-- vendorid
$ mkdir func0/attrs
$ tree func0
func0/
|-- attrs
| |-- db_count
| |-- mw1
| |-- mw2
| |-- mw3
| |-- mw4
| |-- num_mws
| `-- spad_count
|-- baseclass_code
|-- cache_line_size
|-- ...
`-- vendorid
At this point, the function can be started by linking the EP controller.
However, if the user mistakenly creates again a directory:
$ mkdir func0/attrs2
$ tree func0
func0/
|-- attrs
| |-- db_count
| |-- mw1
| |-- mw2
| |-- mw3
| |-- mw4
| |-- num_mws
| `-- spad_count
|-- attrs2
| |-- db_count
| |-- mw1
| |-- mw2
| |-- mw3
| |-- mw4
| |-- num_mws
| `-- spad_count
|-- baseclass_code
|-- cache_line_size
|-- ...
`-- vendorid
The endpoint function specific attributes are duplicated and cause a crash
when the endpoint function device is torn down:
refcount_t: addition on 0; use-after-free.
WARNING: CPU: 2 PID: 834 at lib/refcount.c:25 refcount_warn_saturate+0xc8/0x144
CPU: 2 PID: 834 Comm: rmdir Not tainted 6.3.0-rc1 #1
Hardware name: Pine64 RockPro64 v2.1 (DT)
pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
...
Call trace:
refcount_warn_saturate+0xc8/0x144
config_item_get+0x7c/0x80
configfs_rmdir+0x17c/0x30c
vfs_rmdir+0x8c/0x204
do_rmdir+0x158/0x184
__arm64_sys_unlinkat+0x64/0x80
invoke_syscall+0x48/0x114
...
Fix this by modifying pci_epf_cfs_work() to execute the new function
pci_ep_cfs_add_type_group() which itself calls pci_epf_type_add_cfs() to
obtain the function specific attribute group and the group name (directory
name) from the endpoint function driver. If the function driver defines an
attribute group, pci_ep_cfs_add_type_group() then proceeds to register this
group using configfs_register_group(), thus automatically exposing the
function type specific configfs attributes to the user. E.g.:
$ modprobe pci-epf-ntb
$ cd /sys/kernel/config/pci_ep/functions/pci_epf_ntb
$ mkdir func0
$ tree func0
func0/
|-- baseclass_code
|-- cache_line_size
|-- ...
|-- pci_epf_ntb.0
| |-- db_count
| |-- mw1
| |-- mw2
| |-- mw3
| |-- mw4
| |-- num_mws
| `-- spad_count
|-- primary
|-- ...
`-- vendorid
With this change, there is no need for the user to create or delete
directories in the endpoint function attributes directory. The
pci_epf_type_group_ops group operations are thus removed.
Also update the documentation for the pci-epf-ntb and pci-epf-vntb function
drivers to reflect this change, removing the explanations showing the need
to manually create the sub-directory for the function specific attributes.
Link: https://lore.kernel.org/r/20230415023542.77601-2-dlemoal@kernel.org
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
2023-04-15 05:35:26 +03:00
static void pci_ep_cfs_add_type_group ( struct pci_epf_group * epf_group )
{
struct config_group * group ;
group = pci_epf_type_add_cfs ( epf_group - > epf , & epf_group - > group ) ;
if ( ! group )
return ;
if ( IS_ERR ( group ) ) {
dev_err ( & epf_group - > epf - > dev ,
" failed to create epf type specific attributes \n " ) ;
return ;
}
configfs_register_group ( & epf_group - > group , group ) ;
}
2021-02-01 22:57:59 +03:00
static void pci_epf_cfs_work ( struct work_struct * work )
{
struct pci_epf_group * epf_group ;
struct config_group * group ;
epf_group = container_of ( work , struct pci_epf_group , cfs_work . work ) ;
group = pci_ep_cfs_add_primary_group ( epf_group ) ;
if ( IS_ERR ( group ) ) {
pr_err ( " failed to create 'primary' EPC interface \n " ) ;
return ;
}
group = pci_ep_cfs_add_secondary_group ( epf_group ) ;
if ( IS_ERR ( group ) ) {
pr_err ( " failed to create 'secondary' EPC interface \n " ) ;
return ;
}
PCI: endpoint: Automatically create a function specific attributes group
A PCI endpoint function driver can define function specific attributes
under its function configfs directory using the add_cfs() endpoint driver
operation. This is done by tying up the mkdir operation for the function
configfs directory to a call to the add_cfs() operation. However, there
are no checks preventing the user from repeatedly creating function
specific attribute directories with different names, resulting in the same
endpoint specific attributes group being added multiple times, which also
result in an invalid reference counting for the attribute groups. E.g.,
using the pci-epf-ntb function driver as an example, the user creates the
function as follows:
$ modprobe pci-epf-ntb
$ cd /sys/kernel/config/pci_ep/functions/pci_epf_ntb
$ mkdir func0
$ tree func0
func0/
|-- baseclass_code
|-- cache_line_size
|-- ...
`-- vendorid
$ mkdir func0/attrs
$ tree func0
func0/
|-- attrs
| |-- db_count
| |-- mw1
| |-- mw2
| |-- mw3
| |-- mw4
| |-- num_mws
| `-- spad_count
|-- baseclass_code
|-- cache_line_size
|-- ...
`-- vendorid
At this point, the function can be started by linking the EP controller.
However, if the user mistakenly creates again a directory:
$ mkdir func0/attrs2
$ tree func0
func0/
|-- attrs
| |-- db_count
| |-- mw1
| |-- mw2
| |-- mw3
| |-- mw4
| |-- num_mws
| `-- spad_count
|-- attrs2
| |-- db_count
| |-- mw1
| |-- mw2
| |-- mw3
| |-- mw4
| |-- num_mws
| `-- spad_count
|-- baseclass_code
|-- cache_line_size
|-- ...
`-- vendorid
The endpoint function specific attributes are duplicated and cause a crash
when the endpoint function device is torn down:
refcount_t: addition on 0; use-after-free.
WARNING: CPU: 2 PID: 834 at lib/refcount.c:25 refcount_warn_saturate+0xc8/0x144
CPU: 2 PID: 834 Comm: rmdir Not tainted 6.3.0-rc1 #1
Hardware name: Pine64 RockPro64 v2.1 (DT)
pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
...
Call trace:
refcount_warn_saturate+0xc8/0x144
config_item_get+0x7c/0x80
configfs_rmdir+0x17c/0x30c
vfs_rmdir+0x8c/0x204
do_rmdir+0x158/0x184
__arm64_sys_unlinkat+0x64/0x80
invoke_syscall+0x48/0x114
...
Fix this by modifying pci_epf_cfs_work() to execute the new function
pci_ep_cfs_add_type_group() which itself calls pci_epf_type_add_cfs() to
obtain the function specific attribute group and the group name (directory
name) from the endpoint function driver. If the function driver defines an
attribute group, pci_ep_cfs_add_type_group() then proceeds to register this
group using configfs_register_group(), thus automatically exposing the
function type specific configfs attributes to the user. E.g.:
$ modprobe pci-epf-ntb
$ cd /sys/kernel/config/pci_ep/functions/pci_epf_ntb
$ mkdir func0
$ tree func0
func0/
|-- baseclass_code
|-- cache_line_size
|-- ...
|-- pci_epf_ntb.0
| |-- db_count
| |-- mw1
| |-- mw2
| |-- mw3
| |-- mw4
| |-- num_mws
| `-- spad_count
|-- primary
|-- ...
`-- vendorid
With this change, there is no need for the user to create or delete
directories in the endpoint function attributes directory. The
pci_epf_type_group_ops group operations are thus removed.
Also update the documentation for the pci-epf-ntb and pci-epf-vntb function
drivers to reflect this change, removing the explanations showing the need
to manually create the sub-directory for the function specific attributes.
Link: https://lore.kernel.org/r/20230415023542.77601-2-dlemoal@kernel.org
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
2023-04-15 05:35:26 +03:00
pci_ep_cfs_add_type_group ( epf_group ) ;
2021-02-01 22:57:59 +03:00
}
2017-03-27 12:44:59 +03:00
static struct config_group * pci_epf_make ( struct config_group * group ,
const char * name )
{
struct pci_epf_group * epf_group ;
struct pci_epf * epf ;
2018-01-30 23:56:57 +03:00
char * epf_name ;
int index , err ;
2017-03-27 12:44:59 +03:00
epf_group = kzalloc ( sizeof ( * epf_group ) , GFP_KERNEL ) ;
if ( ! epf_group )
return ERR_PTR ( - ENOMEM ) ;
2018-01-30 23:56:57 +03:00
mutex_lock ( & functions_mutex ) ;
index = idr_alloc ( & functions_idr , epf_group , 0 , 0 , GFP_KERNEL ) ;
mutex_unlock ( & functions_mutex ) ;
if ( index < 0 ) {
err = index ;
goto free_group ;
}
epf_group - > index = index ;
2017-03-27 12:44:59 +03:00
config_group_init_type_name ( & epf_group - > group , name , & pci_epf_type ) ;
2018-01-30 23:56:57 +03:00
epf_name = kasprintf ( GFP_KERNEL , " %s.%d " ,
group - > cg_item . ci_name , epf_group - > index ) ;
if ( ! epf_name ) {
err = - ENOMEM ;
goto remove_idr ;
}
epf = pci_epf_create ( epf_name ) ;
2017-03-27 12:44:59 +03:00
if ( IS_ERR ( epf ) ) {
pr_err ( " failed to create endpoint function device \n " ) ;
2018-01-30 23:56:57 +03:00
err = - EINVAL ;
goto free_name ;
2017-03-27 12:44:59 +03:00
}
2021-02-01 22:58:02 +03:00
epf - > group = & epf_group - > group ;
2017-03-27 12:44:59 +03:00
epf_group - > epf = epf ;
2018-01-30 23:56:57 +03:00
kfree ( epf_name ) ;
2021-02-01 22:57:59 +03:00
INIT_DELAYED_WORK ( & epf_group - > cfs_work , pci_epf_cfs_work ) ;
queue_delayed_work ( system_wq , & epf_group - > cfs_work ,
msecs_to_jiffies ( 1 ) ) ;
2017-03-27 12:44:59 +03:00
return & epf_group - > group ;
2018-01-30 23:56:57 +03:00
free_name :
kfree ( epf_name ) ;
remove_idr :
mutex_lock ( & functions_mutex ) ;
idr_remove ( & functions_idr , epf_group - > index ) ;
mutex_unlock ( & functions_mutex ) ;
free_group :
kfree ( epf_group ) ;
return ERR_PTR ( err ) ;
2017-03-27 12:44:59 +03:00
}
static void pci_epf_drop ( struct config_group * group , struct config_item * item )
{
config_item_put ( item ) ;
}
static struct configfs_group_operations pci_epf_group_ops = {
. make_group = & pci_epf_make ,
. drop_item = & pci_epf_drop ,
} ;
2017-10-16 18:18:45 +03:00
static const struct config_item_type pci_epf_group_type = {
2017-03-27 12:44:59 +03:00
. ct_group_ops = & pci_epf_group_ops ,
. ct_owner = THIS_MODULE ,
} ;
struct config_group * pci_ep_cfs_add_epf_group ( const char * name )
{
struct config_group * group ;
group = configfs_register_default_group ( functions_group , name ,
& pci_epf_group_type ) ;
if ( IS_ERR ( group ) )
pr_err ( " failed to register configfs group for %s function \n " ,
name ) ;
return group ;
}
EXPORT_SYMBOL ( pci_ep_cfs_add_epf_group ) ;
void pci_ep_cfs_remove_epf_group ( struct config_group * group )
{
if ( IS_ERR_OR_NULL ( group ) )
return ;
configfs_unregister_default_group ( group ) ;
}
EXPORT_SYMBOL ( pci_ep_cfs_remove_epf_group ) ;
2017-10-16 18:18:45 +03:00
static const struct config_item_type pci_functions_type = {
2017-03-27 12:44:59 +03:00
. ct_owner = THIS_MODULE ,
} ;
2017-10-16 18:18:45 +03:00
static const struct config_item_type pci_controllers_type = {
2017-03-27 12:44:59 +03:00
. ct_owner = THIS_MODULE ,
} ;
2017-10-16 18:18:45 +03:00
static const struct config_item_type pci_ep_type = {
2017-03-27 12:44:59 +03:00
. ct_owner = THIS_MODULE ,
} ;
static struct configfs_subsystem pci_ep_cfs_subsys = {
. su_group = {
. cg_item = {
. ci_namebuf = " pci_ep " ,
. ci_type = & pci_ep_type ,
} ,
} ,
. su_mutex = __MUTEX_INITIALIZER ( pci_ep_cfs_subsys . su_mutex ) ,
} ;
static int __init pci_ep_cfs_init ( void )
{
int ret ;
struct config_group * root = & pci_ep_cfs_subsys . su_group ;
config_group_init ( root ) ;
ret = configfs_register_subsystem ( & pci_ep_cfs_subsys ) ;
if ( ret ) {
pr_err ( " Error %d while registering subsystem %s \n " ,
ret , root - > cg_item . ci_namebuf ) ;
goto err ;
}
functions_group = configfs_register_default_group ( root , " functions " ,
& pci_functions_type ) ;
if ( IS_ERR ( functions_group ) ) {
ret = PTR_ERR ( functions_group ) ;
pr_err ( " Error %d while registering functions group \n " ,
ret ) ;
goto err_functions_group ;
}
controllers_group =
configfs_register_default_group ( root , " controllers " ,
& pci_controllers_type ) ;
if ( IS_ERR ( controllers_group ) ) {
ret = PTR_ERR ( controllers_group ) ;
pr_err ( " Error %d while registering controllers group \n " ,
ret ) ;
goto err_controllers_group ;
}
return 0 ;
err_controllers_group :
configfs_unregister_default_group ( functions_group ) ;
err_functions_group :
configfs_unregister_subsystem ( & pci_ep_cfs_subsys ) ;
err :
return ret ;
}
module_init ( pci_ep_cfs_init ) ;
static void __exit pci_ep_cfs_exit ( void )
{
configfs_unregister_default_group ( controllers_group ) ;
configfs_unregister_default_group ( functions_group ) ;
configfs_unregister_subsystem ( & pci_ep_cfs_subsys ) ;
}
module_exit ( pci_ep_cfs_exit ) ;
MODULE_DESCRIPTION ( " PCI EP CONFIGFS " ) ;
MODULE_AUTHOR ( " Kishon Vijay Abraham I <kishon@ti.com> " ) ;