2019-05-23 12:14:55 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2013-06-11 21:38:59 +04:00
/*
* A simple sysfs interface for the generic PWM framework
*
* Copyright ( C ) 2013 H Hartley Sweeten < hsweeten @ visionengravers . com >
*
* Based on previous work by Lars Poeschel < poeschel @ lemonage . de >
*/
# include <linux/device.h>
# include <linux/mutex.h>
# include <linux/err.h>
# include <linux/slab.h>
# include <linux/kdev_t.h>
# include <linux/pwm.h>
struct pwm_export {
struct device child ;
struct pwm_device * pwm ;
2016-03-30 23:03:27 +03:00
struct mutex lock ;
2019-05-31 12:55:00 +03:00
struct pwm_state suspend ;
2013-06-11 21:38:59 +04:00
} ;
static struct pwm_export * child_to_pwm_export ( struct device * child )
{
return container_of ( child , struct pwm_export , child ) ;
}
static struct pwm_device * child_to_pwm_device ( struct device * child )
{
struct pwm_export * export = child_to_pwm_export ( child ) ;
return export - > pwm ;
}
2015-10-27 00:32:37 +03:00
static ssize_t period_show ( struct device * child ,
struct device_attribute * attr ,
char * buf )
2013-06-11 21:38:59 +04:00
{
const struct pwm_device * pwm = child_to_pwm_device ( child ) ;
2016-04-14 22:17:43 +03:00
struct pwm_state state ;
2013-06-11 21:38:59 +04:00
2016-04-14 22:17:43 +03:00
pwm_get_state ( pwm , & state ) ;
2022-08-26 20:07:15 +03:00
return sysfs_emit ( buf , " %llu \n " , state . period ) ;
2013-06-11 21:38:59 +04:00
}
2015-10-27 00:32:37 +03:00
static ssize_t period_store ( struct device * child ,
struct device_attribute * attr ,
const char * buf , size_t size )
2013-06-11 21:38:59 +04:00
{
2016-03-30 23:03:27 +03:00
struct pwm_export * export = child_to_pwm_export ( child ) ;
struct pwm_device * pwm = export - > pwm ;
2016-04-14 22:17:43 +03:00
struct pwm_state state ;
2020-06-03 01:31:16 +03:00
u64 val ;
2013-06-11 21:38:59 +04:00
int ret ;
2020-06-03 01:31:16 +03:00
ret = kstrtou64 ( buf , 0 , & val ) ;
2013-06-11 21:38:59 +04:00
if ( ret )
return ret ;
2016-03-30 23:03:27 +03:00
mutex_lock ( & export - > lock ) ;
2016-04-14 22:17:43 +03:00
pwm_get_state ( pwm , & state ) ;
state . period = val ;
ret = pwm_apply_state ( pwm , & state ) ;
2016-03-30 23:03:27 +03:00
mutex_unlock ( & export - > lock ) ;
2013-06-11 21:38:59 +04:00
return ret ? : size ;
}
2015-10-27 00:32:37 +03:00
static ssize_t duty_cycle_show ( struct device * child ,
struct device_attribute * attr ,
char * buf )
2013-06-11 21:38:59 +04:00
{
const struct pwm_device * pwm = child_to_pwm_device ( child ) ;
2016-04-14 22:17:43 +03:00
struct pwm_state state ;
pwm_get_state ( pwm , & state ) ;
2013-06-11 21:38:59 +04:00
2022-08-26 20:07:15 +03:00
return sysfs_emit ( buf , " %llu \n " , state . duty_cycle ) ;
2013-06-11 21:38:59 +04:00
}
2015-10-27 00:32:37 +03:00
static ssize_t duty_cycle_store ( struct device * child ,
struct device_attribute * attr ,
const char * buf , size_t size )
2013-06-11 21:38:59 +04:00
{
2016-03-30 23:03:27 +03:00
struct pwm_export * export = child_to_pwm_export ( child ) ;
struct pwm_device * pwm = export - > pwm ;
2016-04-14 22:17:43 +03:00
struct pwm_state state ;
2020-08-24 17:55:39 +03:00
u64 val ;
2013-06-11 21:38:59 +04:00
int ret ;
2020-08-24 17:55:39 +03:00
ret = kstrtou64 ( buf , 0 , & val ) ;
2013-06-11 21:38:59 +04:00
if ( ret )
return ret ;
2016-03-30 23:03:27 +03:00
mutex_lock ( & export - > lock ) ;
2016-04-14 22:17:43 +03:00
pwm_get_state ( pwm , & state ) ;
state . duty_cycle = val ;
ret = pwm_apply_state ( pwm , & state ) ;
2016-03-30 23:03:27 +03:00
mutex_unlock ( & export - > lock ) ;
2013-06-11 21:38:59 +04:00
return ret ? : size ;
}
2015-10-27 00:32:37 +03:00
static ssize_t enable_show ( struct device * child ,
struct device_attribute * attr ,
char * buf )
2013-06-11 21:38:59 +04:00
{
const struct pwm_device * pwm = child_to_pwm_device ( child ) ;
2016-04-14 22:17:43 +03:00
struct pwm_state state ;
2013-06-11 21:38:59 +04:00
2016-04-14 22:17:43 +03:00
pwm_get_state ( pwm , & state ) ;
2022-08-26 20:07:15 +03:00
return sysfs_emit ( buf , " %d \n " , state . enabled ) ;
2013-06-11 21:38:59 +04:00
}
2015-10-27 00:32:37 +03:00
static ssize_t enable_store ( struct device * child ,
struct device_attribute * attr ,
const char * buf , size_t size )
2013-06-11 21:38:59 +04:00
{
2016-03-30 23:03:27 +03:00
struct pwm_export * export = child_to_pwm_export ( child ) ;
struct pwm_device * pwm = export - > pwm ;
2016-04-14 22:17:43 +03:00
struct pwm_state state ;
2013-06-11 21:38:59 +04:00
int val , ret ;
ret = kstrtoint ( buf , 0 , & val ) ;
if ( ret )
return ret ;
2016-03-30 23:03:27 +03:00
mutex_lock ( & export - > lock ) ;
2016-04-14 22:17:43 +03:00
pwm_get_state ( pwm , & state ) ;
2013-06-11 21:38:59 +04:00
switch ( val ) {
case 0 :
2016-04-14 22:17:43 +03:00
state . enabled = false ;
2013-06-11 21:38:59 +04:00
break ;
case 1 :
2016-04-14 22:17:43 +03:00
state . enabled = true ;
2013-06-11 21:38:59 +04:00
break ;
default :
ret = - EINVAL ;
2016-04-14 22:17:43 +03:00
goto unlock ;
2013-06-11 21:38:59 +04:00
}
2016-06-08 04:58:23 +03:00
ret = pwm_apply_state ( pwm , & state ) ;
2016-03-30 23:03:27 +03:00
2016-04-14 22:17:43 +03:00
unlock :
mutex_unlock ( & export - > lock ) ;
2013-06-11 21:38:59 +04:00
return ret ? : size ;
}
2015-10-27 00:32:37 +03:00
static ssize_t polarity_show ( struct device * child ,
struct device_attribute * attr ,
char * buf )
2013-06-11 21:38:59 +04:00
{
const struct pwm_device * pwm = child_to_pwm_device ( child ) ;
2015-07-20 10:56:05 +03:00
const char * polarity = " unknown " ;
2016-04-14 22:17:43 +03:00
struct pwm_state state ;
pwm_get_state ( pwm , & state ) ;
2013-06-11 21:38:59 +04:00
2016-04-14 22:17:43 +03:00
switch ( state . polarity ) {
2015-07-20 10:56:05 +03:00
case PWM_POLARITY_NORMAL :
polarity = " normal " ;
break ;
case PWM_POLARITY_INVERSED :
polarity = " inversed " ;
break ;
}
2022-08-26 20:07:15 +03:00
return sysfs_emit ( buf , " %s \n " , polarity ) ;
2013-06-11 21:38:59 +04:00
}
2015-10-27 00:32:37 +03:00
static ssize_t polarity_store ( struct device * child ,
struct device_attribute * attr ,
const char * buf , size_t size )
2013-06-11 21:38:59 +04:00
{
2016-03-30 23:03:27 +03:00
struct pwm_export * export = child_to_pwm_export ( child ) ;
struct pwm_device * pwm = export - > pwm ;
2013-06-11 21:38:59 +04:00
enum pwm_polarity polarity ;
2016-04-14 22:17:43 +03:00
struct pwm_state state ;
2013-06-11 21:38:59 +04:00
int ret ;
if ( sysfs_streq ( buf , " normal " ) )
polarity = PWM_POLARITY_NORMAL ;
else if ( sysfs_streq ( buf , " inversed " ) )
polarity = PWM_POLARITY_INVERSED ;
else
return - EINVAL ;
2016-03-30 23:03:27 +03:00
mutex_lock ( & export - > lock ) ;
2016-04-14 22:17:43 +03:00
pwm_get_state ( pwm , & state ) ;
state . polarity = polarity ;
ret = pwm_apply_state ( pwm , & state ) ;
2016-03-30 23:03:27 +03:00
mutex_unlock ( & export - > lock ) ;
2013-06-11 21:38:59 +04:00
return ret ? : size ;
}
2016-06-08 12:21:25 +03:00
static ssize_t capture_show ( struct device * child ,
struct device_attribute * attr ,
char * buf )
{
struct pwm_device * pwm = child_to_pwm_device ( child ) ;
struct pwm_capture result ;
int ret ;
ret = pwm_capture ( pwm , & result , jiffies_to_msecs ( HZ ) ) ;
if ( ret )
return ret ;
2022-08-26 20:07:15 +03:00
return sysfs_emit ( buf , " %u %u \n " , result . period , result . duty_cycle ) ;
2016-06-08 12:21:25 +03:00
}
2015-10-27 00:32:37 +03:00
static DEVICE_ATTR_RW ( period ) ;
static DEVICE_ATTR_RW ( duty_cycle ) ;
static DEVICE_ATTR_RW ( enable ) ;
static DEVICE_ATTR_RW ( polarity ) ;
2016-06-08 12:21:25 +03:00
static DEVICE_ATTR_RO ( capture ) ;
2013-06-11 21:38:59 +04:00
static struct attribute * pwm_attrs [ ] = {
& dev_attr_period . attr ,
& dev_attr_duty_cycle . attr ,
& dev_attr_enable . attr ,
& dev_attr_polarity . attr ,
2016-06-08 12:21:25 +03:00
& dev_attr_capture . attr ,
2013-06-11 21:38:59 +04:00
NULL
} ;
2013-12-04 14:29:53 +04:00
ATTRIBUTE_GROUPS ( pwm ) ;
2013-06-11 21:38:59 +04:00
static void pwm_export_release ( struct device * child )
{
struct pwm_export * export = child_to_pwm_export ( child ) ;
kfree ( export ) ;
}
static int pwm_export_child ( struct device * parent , struct pwm_device * pwm )
{
struct pwm_export * export ;
2018-10-01 16:23:57 +03:00
char * pwm_prop [ 2 ] ;
2013-06-11 21:38:59 +04:00
int ret ;
if ( test_and_set_bit ( PWMF_EXPORTED , & pwm - > flags ) )
return - EBUSY ;
export = kzalloc ( sizeof ( * export ) , GFP_KERNEL ) ;
if ( ! export ) {
clear_bit ( PWMF_EXPORTED , & pwm - > flags ) ;
return - ENOMEM ;
}
export - > pwm = pwm ;
2016-03-30 23:03:27 +03:00
mutex_init ( & export - > lock ) ;
2013-06-11 21:38:59 +04:00
export - > child . release = pwm_export_release ;
export - > child . parent = parent ;
export - > child . devt = MKDEV ( 0 , 0 ) ;
2013-12-04 14:29:53 +04:00
export - > child . groups = pwm_groups ;
2013-06-11 21:38:59 +04:00
dev_set_name ( & export - > child , " pwm%u " , pwm - > hwpwm ) ;
ret = device_register ( & export - > child ) ;
if ( ret ) {
clear_bit ( PWMF_EXPORTED , & pwm - > flags ) ;
2018-03-08 12:57:37 +03:00
put_device ( & export - > child ) ;
export = NULL ;
2013-06-11 21:38:59 +04:00
return ret ;
}
2018-10-01 16:23:57 +03:00
pwm_prop [ 0 ] = kasprintf ( GFP_KERNEL , " EXPORT=pwm%u " , pwm - > hwpwm ) ;
pwm_prop [ 1 ] = NULL ;
kobject_uevent_env ( & parent - > kobj , KOBJ_CHANGE , pwm_prop ) ;
kfree ( pwm_prop [ 0 ] ) ;
2013-06-11 21:38:59 +04:00
return 0 ;
}
static int pwm_unexport_match ( struct device * child , void * data )
{
return child_to_pwm_device ( child ) = = data ;
}
static int pwm_unexport_child ( struct device * parent , struct pwm_device * pwm )
{
struct device * child ;
2018-10-01 16:23:57 +03:00
char * pwm_prop [ 2 ] ;
2013-06-11 21:38:59 +04:00
if ( ! test_and_clear_bit ( PWMF_EXPORTED , & pwm - > flags ) )
return - ENODEV ;
child = device_find_child ( parent , pwm , pwm_unexport_match ) ;
if ( ! child )
return - ENODEV ;
2018-10-01 16:23:57 +03:00
pwm_prop [ 0 ] = kasprintf ( GFP_KERNEL , " UNEXPORT=pwm%u " , pwm - > hwpwm ) ;
pwm_prop [ 1 ] = NULL ;
kobject_uevent_env ( & parent - > kobj , KOBJ_CHANGE , pwm_prop ) ;
kfree ( pwm_prop [ 0 ] ) ;
2013-06-11 21:38:59 +04:00
/* for device_find_child() */
put_device ( child ) ;
device_unregister ( child ) ;
pwm_put ( pwm ) ;
return 0 ;
}
2015-10-27 00:32:37 +03:00
static ssize_t export_store ( struct device * parent ,
struct device_attribute * attr ,
const char * buf , size_t len )
2013-06-11 21:38:59 +04:00
{
struct pwm_chip * chip = dev_get_drvdata ( parent ) ;
struct pwm_device * pwm ;
unsigned int hwpwm ;
int ret ;
ret = kstrtouint ( buf , 0 , & hwpwm ) ;
if ( ret < 0 )
return ret ;
if ( hwpwm > = chip - > npwm )
return - ENODEV ;
pwm = pwm_request_from_chip ( chip , hwpwm , " sysfs " ) ;
if ( IS_ERR ( pwm ) )
return PTR_ERR ( pwm ) ;
ret = pwm_export_child ( parent , pwm ) ;
if ( ret < 0 )
pwm_put ( pwm ) ;
return ret ? : len ;
}
2015-10-27 00:32:37 +03:00
static DEVICE_ATTR_WO ( export ) ;
2013-06-11 21:38:59 +04:00
2015-10-27 00:32:37 +03:00
static ssize_t unexport_store ( struct device * parent ,
struct device_attribute * attr ,
const char * buf , size_t len )
2013-06-11 21:38:59 +04:00
{
struct pwm_chip * chip = dev_get_drvdata ( parent ) ;
unsigned int hwpwm ;
int ret ;
ret = kstrtouint ( buf , 0 , & hwpwm ) ;
if ( ret < 0 )
return ret ;
if ( hwpwm > = chip - > npwm )
return - ENODEV ;
ret = pwm_unexport_child ( parent , & chip - > pwms [ hwpwm ] ) ;
return ret ? : len ;
}
2015-10-27 00:32:37 +03:00
static DEVICE_ATTR_WO ( unexport ) ;
2013-06-11 21:38:59 +04:00
2013-07-25 02:05:39 +04:00
static ssize_t npwm_show ( struct device * parent , struct device_attribute * attr ,
char * buf )
2013-06-11 21:38:59 +04:00
{
const struct pwm_chip * chip = dev_get_drvdata ( parent ) ;
2022-08-26 20:07:15 +03:00
return sysfs_emit ( buf , " %u \n " , chip - > npwm ) ;
2013-06-11 21:38:59 +04:00
}
2013-07-25 02:05:39 +04:00
static DEVICE_ATTR_RO ( npwm ) ;
2013-06-11 21:38:59 +04:00
2013-07-25 02:05:39 +04:00
static struct attribute * pwm_chip_attrs [ ] = {
& dev_attr_export . attr ,
& dev_attr_unexport . attr ,
& dev_attr_npwm . attr ,
NULL ,
2013-06-11 21:38:59 +04:00
} ;
2013-07-25 02:05:39 +04:00
ATTRIBUTE_GROUPS ( pwm_chip ) ;
2013-06-11 21:38:59 +04:00
2019-05-31 12:55:00 +03:00
/* takes export->lock on success */
static struct pwm_export * pwm_class_get_state ( struct device * parent ,
struct pwm_device * pwm ,
struct pwm_state * state )
{
struct device * child ;
struct pwm_export * export ;
if ( ! test_bit ( PWMF_EXPORTED , & pwm - > flags ) )
return NULL ;
child = device_find_child ( parent , pwm , pwm_unexport_match ) ;
if ( ! child )
return NULL ;
export = child_to_pwm_export ( child ) ;
put_device ( child ) ; /* for device_find_child() */
mutex_lock ( & export - > lock ) ;
pwm_get_state ( pwm , state ) ;
return export ;
}
static int pwm_class_apply_state ( struct pwm_export * export ,
struct pwm_device * pwm ,
struct pwm_state * state )
{
int ret = pwm_apply_state ( pwm , state ) ;
/* release lock taken in pwm_class_get_state */
mutex_unlock ( & export - > lock ) ;
return ret ;
}
static int pwm_class_resume_npwm ( struct device * parent , unsigned int npwm )
{
struct pwm_chip * chip = dev_get_drvdata ( parent ) ;
unsigned int i ;
int ret = 0 ;
for ( i = 0 ; i < npwm ; i + + ) {
struct pwm_device * pwm = & chip - > pwms [ i ] ;
struct pwm_state state ;
struct pwm_export * export ;
export = pwm_class_get_state ( parent , pwm , & state ) ;
if ( ! export )
continue ;
2023-05-12 19:47:36 +03:00
/* If pwmchip was not enabled before suspend, do nothing. */
if ( ! export - > suspend . enabled ) {
/* release lock taken in pwm_class_get_state */
mutex_unlock ( & export - > lock ) ;
continue ;
}
2019-05-31 12:55:00 +03:00
state . enabled = export - > suspend . enabled ;
ret = pwm_class_apply_state ( export , pwm , & state ) ;
if ( ret < 0 )
break ;
}
return ret ;
}
2022-08-26 20:07:13 +03:00
static int pwm_class_suspend ( struct device * parent )
2019-05-31 12:55:00 +03:00
{
struct pwm_chip * chip = dev_get_drvdata ( parent ) ;
unsigned int i ;
int ret = 0 ;
for ( i = 0 ; i < chip - > npwm ; i + + ) {
struct pwm_device * pwm = & chip - > pwms [ i ] ;
struct pwm_state state ;
struct pwm_export * export ;
export = pwm_class_get_state ( parent , pwm , & state ) ;
if ( ! export )
continue ;
2023-05-12 19:47:36 +03:00
/*
* If pwmchip was not enabled before suspend , save
* state for resume time and do nothing else .
*/
2019-05-31 12:55:00 +03:00
export - > suspend = state ;
2023-05-12 19:47:36 +03:00
if ( ! state . enabled ) {
/* release lock taken in pwm_class_get_state */
mutex_unlock ( & export - > lock ) ;
continue ;
}
2019-05-31 12:55:00 +03:00
state . enabled = false ;
ret = pwm_class_apply_state ( export , pwm , & state ) ;
if ( ret < 0 ) {
/*
* roll back the PWM devices that were disabled by
* this suspend function .
*/
pwm_class_resume_npwm ( parent , i ) ;
break ;
}
}
return ret ;
}
2022-08-26 20:07:13 +03:00
static int pwm_class_resume ( struct device * parent )
2019-05-31 12:55:00 +03:00
{
struct pwm_chip * chip = dev_get_drvdata ( parent ) ;
return pwm_class_resume_npwm ( parent , chip - > npwm ) ;
}
2022-08-26 20:07:13 +03:00
static DEFINE_SIMPLE_DEV_PM_OPS ( pwm_class_pm_ops , pwm_class_suspend , pwm_class_resume ) ;
2019-05-31 12:55:00 +03:00
2013-06-11 21:38:59 +04:00
static struct class pwm_class = {
2015-07-20 10:58:09 +03:00
. name = " pwm " ,
. dev_groups = pwm_chip_groups ,
2022-08-26 20:07:13 +03:00
. pm = pm_sleep_ptr ( & pwm_class_pm_ops ) ,
2013-06-11 21:38:59 +04:00
} ;
static int pwmchip_sysfs_match ( struct device * parent , const void * data )
{
return dev_get_drvdata ( parent ) = = data ;
}
void pwmchip_sysfs_export ( struct pwm_chip * chip )
{
struct device * parent ;
/*
* If device_create ( ) fails the pwm_chip is still usable by
2019-03-12 12:25:47 +03:00
* the kernel it ' s just not exported .
2013-06-11 21:38:59 +04:00
*/
parent = device_create ( & pwm_class , chip - > dev , MKDEV ( 0 , 0 ) , chip ,
" pwmchip%d " , chip - > base ) ;
if ( IS_ERR ( parent ) ) {
dev_warn ( chip - > dev ,
" device_create failed for pwm_chip sysfs export \n " ) ;
}
}
void pwmchip_sysfs_unexport ( struct pwm_chip * chip )
2016-08-10 00:57:46 +03:00
{
struct device * parent ;
unsigned int i ;
parent = class_find_device ( & pwm_class , NULL , chip ,
pwmchip_sysfs_match ) ;
if ( ! parent )
return ;
for ( i = 0 ; i < chip - > npwm ; i + + ) {
struct pwm_device * pwm = & chip - > pwms [ i ] ;
if ( test_bit ( PWMF_EXPORTED , & pwm - > flags ) )
pwm_unexport_child ( parent , pwm ) ;
}
2016-11-01 13:46:39 +03:00
put_device ( parent ) ;
pwm: Fix deadlock warning when removing PWM device
This patch fixes deadlock warning if removing PWM device
when CONFIG_PROVE_LOCKING is enabled.
This issue can be reproceduced by the following steps on
the R-Car H3 Salvator-X board if the backlight is disabled:
# cd /sys/class/pwm/pwmchip0
# echo 0 > export
# ls
device export npwm power pwm0 subsystem uevent unexport
# cd device/driver
# ls
bind e6e31000.pwm uevent unbind
# echo e6e31000.pwm > unbind
[ 87.659974] ======================================================
[ 87.666149] WARNING: possible circular locking dependency detected
[ 87.672327] 5.0.0 #7 Not tainted
[ 87.675549] ------------------------------------------------------
[ 87.681723] bash/2986 is trying to acquire lock:
[ 87.686337] 000000005ea0e178 (kn->count#58){++++}, at: kernfs_remove_by_name_ns+0x50/0xa0
[ 87.694528]
[ 87.694528] but task is already holding lock:
[ 87.700353] 000000006313b17c (pwm_lock){+.+.}, at: pwmchip_remove+0x28/0x13c
[ 87.707405]
[ 87.707405] which lock already depends on the new lock.
[ 87.707405]
[ 87.715574]
[ 87.715574] the existing dependency chain (in reverse order) is:
[ 87.723048]
[ 87.723048] -> #1 (pwm_lock){+.+.}:
[ 87.728017] __mutex_lock+0x70/0x7e4
[ 87.732108] mutex_lock_nested+0x1c/0x24
[ 87.736547] pwm_request_from_chip.part.6+0x34/0x74
[ 87.741940] pwm_request_from_chip+0x20/0x40
[ 87.746725] export_store+0x6c/0x1f4
[ 87.750820] dev_attr_store+0x18/0x28
[ 87.754998] sysfs_kf_write+0x54/0x64
[ 87.759175] kernfs_fop_write+0xe4/0x1e8
[ 87.763615] __vfs_write+0x40/0x184
[ 87.767619] vfs_write+0xa8/0x19c
[ 87.771448] ksys_write+0x58/0xbc
[ 87.775278] __arm64_sys_write+0x18/0x20
[ 87.779721] el0_svc_common+0xd0/0x124
[ 87.783986] el0_svc_compat_handler+0x1c/0x24
[ 87.788858] el0_svc_compat+0x8/0x18
[ 87.792947]
[ 87.792947] -> #0 (kn->count#58){++++}:
[ 87.798260] lock_acquire+0xc4/0x22c
[ 87.802353] __kernfs_remove+0x258/0x2c4
[ 87.806790] kernfs_remove_by_name_ns+0x50/0xa0
[ 87.811836] remove_files.isra.1+0x38/0x78
[ 87.816447] sysfs_remove_group+0x48/0x98
[ 87.820971] sysfs_remove_groups+0x34/0x4c
[ 87.825583] device_remove_attrs+0x6c/0x7c
[ 87.830197] device_del+0x11c/0x33c
[ 87.834201] device_unregister+0x14/0x2c
[ 87.838638] pwmchip_sysfs_unexport+0x40/0x4c
[ 87.843509] pwmchip_remove+0xf4/0x13c
[ 87.847773] rcar_pwm_remove+0x28/0x34
[ 87.852039] platform_drv_remove+0x24/0x64
[ 87.856651] device_release_driver_internal+0x18c/0x21c
[ 87.862391] device_release_driver+0x14/0x1c
[ 87.867175] unbind_store+0xe0/0x124
[ 87.871265] drv_attr_store+0x20/0x30
[ 87.875442] sysfs_kf_write+0x54/0x64
[ 87.879618] kernfs_fop_write+0xe4/0x1e8
[ 87.884055] __vfs_write+0x40/0x184
[ 87.888057] vfs_write+0xa8/0x19c
[ 87.891887] ksys_write+0x58/0xbc
[ 87.895716] __arm64_sys_write+0x18/0x20
[ 87.900154] el0_svc_common+0xd0/0x124
[ 87.904417] el0_svc_compat_handler+0x1c/0x24
[ 87.909289] el0_svc_compat+0x8/0x18
[ 87.913378]
[ 87.913378] other info that might help us debug this:
[ 87.913378]
[ 87.921374] Possible unsafe locking scenario:
[ 87.921374]
[ 87.927286] CPU0 CPU1
[ 87.931808] ---- ----
[ 87.936331] lock(pwm_lock);
[ 87.939293] lock(kn->count#58);
[ 87.945120] lock(pwm_lock);
[ 87.950599] lock(kn->count#58);
[ 87.953908]
[ 87.953908] *** DEADLOCK ***
[ 87.953908]
[ 87.959821] 4 locks held by bash/2986:
[ 87.963563] #0: 00000000ace7bc30 (sb_writers#6){.+.+}, at: vfs_write+0x188/0x19c
[ 87.971044] #1: 00000000287991b2 (&of->mutex){+.+.}, at: kernfs_fop_write+0xb4/0x1e8
[ 87.978872] #2: 00000000f739d016 (&dev->mutex){....}, at: device_release_driver_internal+0x40/0x21c
[ 87.988001] #3: 000000006313b17c (pwm_lock){+.+.}, at: pwmchip_remove+0x28/0x13c
[ 87.995481]
[ 87.995481] stack backtrace:
[ 87.999836] CPU: 0 PID: 2986 Comm: bash Not tainted 5.0.0 #7
[ 88.005489] Hardware name: Renesas Salvator-X board based on r8a7795 ES1.x (DT)
[ 88.012791] Call trace:
[ 88.015235] dump_backtrace+0x0/0x190
[ 88.018891] show_stack+0x14/0x1c
[ 88.022204] dump_stack+0xb0/0xec
[ 88.025514] print_circular_bug.isra.32+0x1d0/0x2e0
[ 88.030385] __lock_acquire+0x1318/0x1864
[ 88.034388] lock_acquire+0xc4/0x22c
[ 88.037958] __kernfs_remove+0x258/0x2c4
[ 88.041874] kernfs_remove_by_name_ns+0x50/0xa0
[ 88.046398] remove_files.isra.1+0x38/0x78
[ 88.050487] sysfs_remove_group+0x48/0x98
[ 88.054490] sysfs_remove_groups+0x34/0x4c
[ 88.058580] device_remove_attrs+0x6c/0x7c
[ 88.062671] device_del+0x11c/0x33c
[ 88.066154] device_unregister+0x14/0x2c
[ 88.070070] pwmchip_sysfs_unexport+0x40/0x4c
[ 88.074421] pwmchip_remove+0xf4/0x13c
[ 88.078163] rcar_pwm_remove+0x28/0x34
[ 88.081906] platform_drv_remove+0x24/0x64
[ 88.085996] device_release_driver_internal+0x18c/0x21c
[ 88.091215] device_release_driver+0x14/0x1c
[ 88.095478] unbind_store+0xe0/0x124
[ 88.099048] drv_attr_store+0x20/0x30
[ 88.102704] sysfs_kf_write+0x54/0x64
[ 88.106359] kernfs_fop_write+0xe4/0x1e8
[ 88.110275] __vfs_write+0x40/0x184
[ 88.113757] vfs_write+0xa8/0x19c
[ 88.117065] ksys_write+0x58/0xbc
[ 88.120374] __arm64_sys_write+0x18/0x20
[ 88.124291] el0_svc_common+0xd0/0x124
[ 88.128034] el0_svc_compat_handler+0x1c/0x24
[ 88.132384] el0_svc_compat+0x8/0x18
The sysfs unexport in pwmchip_remove() is completely asymmetric
to what we do in pwmchip_add_with_polarity() and commit 0733424c9ba9
("pwm: Unexport children before chip removal") is a strong indication
that this was wrong to begin with. We should just move
pwmchip_sysfs_unexport() where it belongs, which is right after
pwmchip_sysfs_unexport_children(). In that case, we do not need
separate functions anymore either.
We also really want to remove sysfs irrespective of whether or not
the chip will be removed as a result of pwmchip_remove(). We can only
assume that the driver will be gone after that, so we shouldn't leave
any dangling sysfs files around.
This warning disappears if we move pwmchip_sysfs_unexport() to
the top of pwmchip_remove(), pwmchip_sysfs_unexport_children().
That way it is also outside of the pwm_lock section, which indeed
doesn't seem to be needed.
Moving the pwmchip_sysfs_export() call outside of that section also
seems fine and it'd be perfectly symmetric with pwmchip_remove() again.
So, this patch fixes them.
Signed-off-by: Phong Hoang <phong.hoang.wz@renesas.com>
[shimoda: revise the commit log and code]
Fixes: 76abbdde2d95 ("pwm: Add sysfs interface")
Fixes: 0733424c9ba9 ("pwm: Unexport children before chip removal")
Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Tested-by: Hoan Nguyen An <na-hoan@jinso.co.jp>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Simon Horman <horms+renesas@verge.net.au>
Reviewed-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
2019-03-19 13:40:08 +03:00
device_unregister ( parent ) ;
2016-08-10 00:57:46 +03:00
}
2013-06-11 21:38:59 +04:00
static int __init pwm_sysfs_init ( void )
{
return class_register ( & pwm_class ) ;
}
subsys_initcall ( pwm_sysfs_init ) ;