2005-04-17 02:20:36 +04:00
/*
2007-09-20 12:31:38 +04:00
* fs / sysfs / dir . c - sysfs core and dir operation implementation
*
* Copyright ( c ) 2001 - 3 Patrick Mochel
* Copyright ( c ) 2007 SUSE Linux Products GmbH
* Copyright ( c ) 2007 Tejun Heo < teheo @ suse . de >
*
* This file is released under the GPLv2 .
*
* Please see Documentation / filesystems / sysfs . txt for more information .
2005-04-17 02:20:36 +04:00
*/
# undef DEBUG
# include <linux/fs.h>
# include <linux/mount.h>
# include <linux/module.h>
# include <linux/kobject.h>
2005-06-23 11:09:12 +04:00
# include <linux/namei.h>
2007-06-13 22:45:13 +04:00
# include <linux/idr.h>
2007-06-13 22:45:18 +04:00
# include <linux/completion.h>
2007-07-26 18:53:53 +04:00
# include <linux/mutex.h>
2008-03-14 05:41:52 +03:00
# include <linux/slab.h>
2009-11-04 13:50:06 +03:00
# include <linux/security.h>
2005-04-17 02:20:36 +04:00
# include "sysfs.h"
2007-06-13 23:27:23 +04:00
DEFINE_MUTEX ( sysfs_mutex ) ;
2007-10-17 10:30:25 +04:00
DEFINE_SPINLOCK ( sysfs_assoc_lock ) ;
2005-04-17 02:20:36 +04:00
2007-10-17 10:30:25 +04:00
static DEFINE_SPINLOCK ( sysfs_ino_lock ) ;
2007-06-13 22:45:13 +04:00
static DEFINE_IDA ( sysfs_ino_ida ) ;
2007-06-13 22:45:18 +04:00
/**
* sysfs_link_sibling - link sysfs_dirent into sibling list
* @ sd : sysfs_dirent of interest
*
* Link @ sd into its sibling list which starts from
2007-09-20 11:05:12 +04:00
* sd - > s_parent - > s_dir . children .
2007-06-13 22:45:18 +04:00
*
* Locking :
2007-06-13 23:27:23 +04:00
* mutex_lock ( sysfs_mutex )
2007-06-13 22:45:18 +04:00
*/
2007-08-02 16:38:03 +04:00
static void sysfs_link_sibling ( struct sysfs_dirent * sd )
2007-06-13 22:45:18 +04:00
{
struct sysfs_dirent * parent_sd = sd - > s_parent ;
2007-08-20 16:36:30 +04:00
struct sysfs_dirent * * pos ;
2007-06-13 22:45:18 +04:00
BUG_ON ( sd - > s_sibling ) ;
2007-08-20 16:36:30 +04:00
/* Store directory entries in order by ino. This allows
* readdir to properly restart without having to add a
2007-09-20 11:05:12 +04:00
* cursor into the s_dir . children list .
2007-08-20 16:36:30 +04:00
*/
2007-09-20 11:05:12 +04:00
for ( pos = & parent_sd - > s_dir . children ; * pos ; pos = & ( * pos ) - > s_sibling ) {
2007-08-20 16:36:30 +04:00
if ( sd - > s_ino < ( * pos ) - > s_ino )
break ;
}
sd - > s_sibling = * pos ;
* pos = sd ;
2007-06-13 22:45:18 +04:00
}
/**
* sysfs_unlink_sibling - unlink sysfs_dirent from sibling list
* @ sd : sysfs_dirent of interest
*
* Unlink @ sd from its sibling list which starts from
2007-09-20 11:05:12 +04:00
* sd - > s_parent - > s_dir . children .
2007-06-13 22:45:18 +04:00
*
* Locking :
2007-06-13 23:27:23 +04:00
* mutex_lock ( sysfs_mutex )
2007-06-13 22:45:18 +04:00
*/
2007-08-02 16:38:03 +04:00
static void sysfs_unlink_sibling ( struct sysfs_dirent * sd )
2007-06-13 22:45:18 +04:00
{
struct sysfs_dirent * * pos ;
2007-09-20 11:05:12 +04:00
for ( pos = & sd - > s_parent - > s_dir . children ; * pos ;
pos = & ( * pos ) - > s_sibling ) {
2007-06-13 22:45:18 +04:00
if ( * pos = = sd ) {
* pos = sd - > s_sibling ;
sd - > s_sibling = NULL ;
break ;
}
}
}
2007-06-13 22:45:18 +04:00
/**
* sysfs_get_active - get an active reference to sysfs_dirent
* @ sd : sysfs_dirent to get an active reference to
*
* Get an active reference of @ sd . This function is noop if @ sd
* is NULL .
*
* RETURNS :
* Pointer to @ sd on success , NULL on failure .
*/
2010-02-12 02:18:38 +03:00
struct sysfs_dirent * sysfs_get_active ( struct sysfs_dirent * sd )
2007-06-13 22:45:18 +04:00
{
2007-06-13 22:45:18 +04:00
if ( unlikely ( ! sd ) )
return NULL ;
while ( 1 ) {
int v , t ;
v = atomic_read ( & sd - > s_active ) ;
if ( unlikely ( v < 0 ) )
return NULL ;
t = atomic_cmpxchg ( & sd - > s_active , v , v + 1 ) ;
2010-01-03 00:37:12 +03:00
if ( likely ( t = = v ) ) {
rwsem_acquire_read ( & sd - > dep_map , 0 , 1 , _RET_IP_ ) ;
2007-06-13 22:45:18 +04:00
return sd ;
2010-01-03 00:37:12 +03:00
}
2007-06-13 22:45:18 +04:00
if ( t < 0 )
return NULL ;
cpu_relax ( ) ;
2007-06-13 22:45:18 +04:00
}
}
/**
* sysfs_put_active - put an active reference to sysfs_dirent
* @ sd : sysfs_dirent to put an active reference to
*
* Put an active reference to @ sd . This function is noop if @ sd
* is NULL .
*/
2010-02-12 02:18:38 +03:00
void sysfs_put_active ( struct sysfs_dirent * sd )
2007-06-13 22:45:18 +04:00
{
2007-06-13 22:45:18 +04:00
struct completion * cmpl ;
int v ;
if ( unlikely ( ! sd ) )
return ;
2010-01-03 00:37:12 +03:00
rwsem_release ( & sd - > dep_map , 1 , _RET_IP_ ) ;
2007-06-13 22:45:18 +04:00
v = atomic_dec_return ( & sd - > s_active ) ;
if ( likely ( v ! = SD_DEACTIVATED_BIAS ) )
return ;
/* atomic_dec_return() is a mb(), we'll always see the updated
2007-06-13 22:45:18 +04:00
* sd - > s_sibling .
2007-06-13 22:45:18 +04:00
*/
2007-06-13 22:45:18 +04:00
cmpl = ( void * ) sd - > s_sibling ;
2007-06-13 22:45:18 +04:00
complete ( cmpl ) ;
2007-06-13 22:45:18 +04:00
}
/**
* sysfs_deactivate - deactivate sysfs_dirent
* @ sd : sysfs_dirent to deactivate
*
2007-06-13 22:45:18 +04:00
* Deny new active references and drain existing ones .
2007-06-13 22:45:18 +04:00
*/
2007-06-13 23:27:24 +04:00
static void sysfs_deactivate ( struct sysfs_dirent * sd )
2007-06-13 22:45:18 +04:00
{
2007-06-13 22:45:18 +04:00
DECLARE_COMPLETION_ONSTACK ( wait ) ;
int v ;
2007-06-13 22:45:18 +04:00
2007-06-13 23:27:22 +04:00
BUG_ON ( sd - > s_sibling | | ! ( sd - > s_flags & SYSFS_FLAG_REMOVED ) ) ;
2010-02-12 02:20:00 +03:00
if ( ! ( sysfs_type ( sd ) & SYSFS_ACTIVE_REF ) )
return ;
2007-06-13 22:45:18 +04:00
sd - > s_sibling = ( void * ) & wait ;
2007-06-13 22:45:18 +04:00
2010-01-03 00:37:12 +03:00
rwsem_acquire ( & sd - > dep_map , 0 , 0 , _RET_IP_ ) ;
2007-06-13 22:45:18 +04:00
/* atomic_add_return() is a mb(), put_active() will always see
2007-06-13 22:45:18 +04:00
* the updated sd - > s_sibling .
2007-06-13 22:45:18 +04:00
*/
2007-06-13 22:45:18 +04:00
v = atomic_add_return ( SD_DEACTIVATED_BIAS , & sd - > s_active ) ;
2010-01-03 00:37:12 +03:00
if ( v ! = SD_DEACTIVATED_BIAS ) {
lock_contended ( & sd - > dep_map , _RET_IP_ ) ;
2007-06-13 22:45:18 +04:00
wait_for_completion ( & wait ) ;
2010-01-03 00:37:12 +03:00
}
2007-06-13 22:45:18 +04:00
2007-06-13 22:45:18 +04:00
sd - > s_sibling = NULL ;
2010-01-03 00:37:12 +03:00
lock_acquired ( & sd - > dep_map , _RET_IP_ ) ;
rwsem_release ( & sd - > dep_map , 1 , _RET_IP_ ) ;
2007-06-13 22:45:18 +04:00
}
2007-06-13 22:45:17 +04:00
static int sysfs_alloc_ino ( ino_t * pino )
2007-06-13 22:45:13 +04:00
{
int ino , rc ;
retry :
spin_lock ( & sysfs_ino_lock ) ;
rc = ida_get_new_above ( & sysfs_ino_ida , 2 , & ino ) ;
spin_unlock ( & sysfs_ino_lock ) ;
if ( rc = = - EAGAIN ) {
if ( ida_pre_get ( & sysfs_ino_ida , GFP_KERNEL ) )
goto retry ;
rc = - ENOMEM ;
}
* pino = ino ;
return rc ;
}
static void sysfs_free_ino ( ino_t ino )
{
spin_lock ( & sysfs_ino_lock ) ;
ida_remove ( & sysfs_ino_ida , ino ) ;
spin_unlock ( & sysfs_ino_lock ) ;
}
2007-06-13 22:45:13 +04:00
void release_sysfs_dirent ( struct sysfs_dirent * sd )
{
2007-06-13 22:45:14 +04:00
struct sysfs_dirent * parent_sd ;
repeat :
2007-06-13 23:27:23 +04:00
/* Moving/renaming is always done while holding reference.
* sd - > s_parent won ' t change beneath us .
*/
2007-06-13 22:45:14 +04:00
parent_sd = sd - > s_parent ;
2007-06-13 23:27:21 +04:00
if ( sysfs_type ( sd ) = = SYSFS_KOBJ_LINK )
2007-09-20 11:05:11 +04:00
sysfs_put ( sd - > s_symlink . target_sd ) ;
2007-06-13 23:27:21 +04:00
if ( sysfs_type ( sd ) & SYSFS_COPY_NAME )
2007-06-13 22:45:15 +04:00
kfree ( sd - > s_name ) ;
2009-11-04 13:50:06 +03:00
if ( sd - > s_iattr & & sd - > s_iattr - > ia_secdata )
security_release_secctx ( sd - > s_iattr - > ia_secdata ,
sd - > s_iattr - > ia_secdata_len ) ;
2007-06-13 22:45:13 +04:00
kfree ( sd - > s_iattr ) ;
2007-06-13 22:45:13 +04:00
sysfs_free_ino ( sd - > s_ino ) ;
2007-06-13 22:45:13 +04:00
kmem_cache_free ( sysfs_dir_cachep , sd ) ;
2007-06-13 22:45:14 +04:00
sd = parent_sd ;
if ( sd & & atomic_dec_and_test ( & sd - > s_count ) )
goto repeat ;
2007-06-13 22:45:13 +04:00
}
2009-11-08 10:27:01 +03:00
static int sysfs_dentry_delete ( struct dentry * dentry )
{
struct sysfs_dirent * sd = dentry - > d_fsdata ;
return ! ! ( sd - > s_flags & SYSFS_FLAG_REMOVED ) ;
}
static int sysfs_dentry_revalidate ( struct dentry * dentry , struct nameidata * nd )
{
struct sysfs_dirent * sd = dentry - > d_fsdata ;
int is_dir ;
mutex_lock ( & sysfs_mutex ) ;
/* The sysfs dirent has been deleted */
if ( sd - > s_flags & SYSFS_FLAG_REMOVED )
goto out_bad ;
2009-11-21 03:08:56 +03:00
/* The sysfs dirent has been moved? */
if ( dentry - > d_parent - > d_fsdata ! = sd - > s_parent )
goto out_bad ;
/* The sysfs dirent has been renamed */
if ( strcmp ( dentry - > d_name . name , sd - > s_name ) ! = 0 )
goto out_bad ;
2009-11-08 10:27:01 +03:00
mutex_unlock ( & sysfs_mutex ) ;
out_valid :
return 1 ;
out_bad :
/* Remove the dentry from the dcache hashes.
* If this is a deleted dentry we use d_drop instead of d_delete
* so sysfs doesn ' t need to cope with negative dentries .
2009-11-21 03:08:56 +03:00
*
* If this is a dentry that has simply been renamed we
* use d_drop to remove it from the dcache lookup on its
* old parent . If this dentry persists later when a lookup
* is performed at its new name the dentry will be readded
* to the dcache hashes .
2009-11-08 10:27:01 +03:00
*/
is_dir = ( sysfs_type ( sd ) = = SYSFS_DIR ) ;
mutex_unlock ( & sysfs_mutex ) ;
if ( is_dir ) {
/* If we have submounts we must allow the vfs caches
* to lie about the state of the filesystem to prevent
* leaks and other nasty things .
*/
if ( have_submounts ( dentry ) )
goto out_valid ;
shrink_dcache_parent ( dentry ) ;
}
d_drop ( dentry ) ;
return 0 ;
}
2009-11-08 10:27:00 +03:00
static void sysfs_dentry_iput ( struct dentry * dentry , struct inode * inode )
2005-04-17 02:20:36 +04:00
{
struct sysfs_dirent * sd = dentry - > d_fsdata ;
2007-08-20 16:36:30 +04:00
sysfs_put ( sd ) ;
2005-04-17 02:20:36 +04:00
iput ( inode ) ;
}
2009-02-20 09:01:46 +03:00
static const struct dentry_operations sysfs_dentry_ops = {
2009-11-08 10:27:01 +03:00
. d_revalidate = sysfs_dentry_revalidate ,
. d_delete = sysfs_dentry_delete ,
2009-11-08 10:27:00 +03:00
. d_iput = sysfs_dentry_iput ,
2005-04-17 02:20:36 +04:00
} ;
2007-06-13 22:45:15 +04:00
struct sysfs_dirent * sysfs_new_dirent ( const char * name , umode_t mode , int type )
2005-04-17 02:20:36 +04:00
{
2007-06-13 22:45:15 +04:00
char * dup_name = NULL ;
2007-07-14 06:03:35 +04:00
struct sysfs_dirent * sd ;
2007-06-13 22:45:15 +04:00
if ( type & SYSFS_COPY_NAME ) {
name = dup_name = kstrdup ( name , GFP_KERNEL ) ;
if ( ! name )
2007-07-14 06:03:35 +04:00
return NULL ;
2007-06-13 22:45:15 +04:00
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:03 +03:00
sd = kmem_cache_zalloc ( sysfs_dir_cachep , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! sd )
2007-07-14 06:03:35 +04:00
goto err_out1 ;
2005-04-17 02:20:36 +04:00
2007-06-13 22:45:15 +04:00
if ( sysfs_alloc_ino ( & sd - > s_ino ) )
2007-07-14 06:03:35 +04:00
goto err_out2 ;
2007-06-13 22:45:13 +04:00
2005-04-17 02:20:36 +04:00
atomic_set ( & sd - > s_count , 1 ) ;
2007-06-13 22:45:18 +04:00
atomic_set ( & sd - > s_active , 0 ) ;
2007-06-13 22:45:14 +04:00
2007-06-13 22:45:15 +04:00
sd - > s_name = name ;
2007-06-13 22:45:14 +04:00
sd - > s_mode = mode ;
2007-06-13 23:27:21 +04:00
sd - > s_flags = type ;
2005-04-17 02:20:36 +04:00
return sd ;
2007-06-13 22:45:15 +04:00
2007-07-14 06:03:35 +04:00
err_out2 :
2007-06-13 22:45:15 +04:00
kmem_cache_free ( sysfs_dir_cachep , sd ) ;
2007-07-14 06:03:35 +04:00
err_out1 :
kfree ( dup_name ) ;
2007-06-13 22:45:15 +04:00
return NULL ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 23:27:23 +04:00
/**
2007-06-13 23:27:24 +04:00
* sysfs_addrm_start - prepare for sysfs_dirent add / remove
* @ acxt : pointer to sysfs_addrm_cxt to be used
* @ parent_sd : parent sysfs_dirent
2007-06-13 23:27:23 +04:00
*
2007-06-13 23:27:24 +04:00
* This function is called when the caller is about to add or
* remove sysfs_dirent under @ parent_sd . This function acquires
2009-11-21 03:08:55 +03:00
* sysfs_mutex . @ acxt is used to keep and pass context to
2007-06-13 23:27:24 +04:00
* other addrm functions .
2007-06-13 23:27:23 +04:00
*
* LOCKING :
2007-06-13 23:27:24 +04:00
* Kernel thread context ( may sleep ) . sysfs_mutex is locked on
2009-11-21 03:08:55 +03:00
* return .
2007-06-13 23:27:23 +04:00
*/
2007-06-13 23:27:24 +04:00
void sysfs_addrm_start ( struct sysfs_addrm_cxt * acxt ,
struct sysfs_dirent * parent_sd )
2007-01-24 22:35:52 +03:00
{
2007-06-13 23:27:24 +04:00
memset ( acxt , 0 , sizeof ( * acxt ) ) ;
acxt - > parent_sd = parent_sd ;
mutex_lock ( & sysfs_mutex ) ;
}
/**
2008-06-10 13:09:08 +04:00
* __sysfs_add_one - add sysfs_dirent to parent without warning
2007-06-13 23:27:24 +04:00
* @ acxt : addrm context to use
* @ sd : sysfs_dirent to be added
*
* Get @ acxt - > parent_sd and set sd - > s_parent to it and increment
2007-09-20 11:05:09 +04:00
* nlink of parent inode if @ sd is a directory and link into the
* children list of the parent .
2007-06-13 23:27:24 +04:00
*
* This function should be called between calls to
* sysfs_addrm_start ( ) and sysfs_addrm_finish ( ) and should be
* passed the same @ acxt as passed to sysfs_addrm_start ( ) .
*
* LOCKING :
* Determined by sysfs_addrm_start ( ) .
2007-08-02 16:38:03 +04:00
*
* RETURNS :
* 0 on success , - EEXIST if entry with the given name already
* exists .
2007-06-13 23:27:24 +04:00
*/
2008-06-10 13:09:08 +04:00
int __sysfs_add_one ( struct sysfs_addrm_cxt * acxt , struct sysfs_dirent * sd )
2007-06-13 23:27:24 +04:00
{
2009-11-21 03:08:51 +03:00
struct sysfs_inode_attrs * ps_iattr ;
2008-05-15 09:34:16 +04:00
if ( sysfs_find_dirent ( acxt - > parent_sd , sd - > s_name ) )
2007-08-02 16:38:03 +04:00
return - EEXIST ;
2007-06-13 23:27:24 +04:00
sd - > s_parent = sysfs_get ( acxt - > parent_sd ) ;
2007-08-02 16:38:03 +04:00
sysfs_link_sibling ( sd ) ;
2007-08-02 16:38:03 +04:00
2009-11-21 03:08:51 +03:00
/* Update timestamps on the parent */
ps_iattr = acxt - > parent_sd - > s_iattr ;
if ( ps_iattr ) {
struct iattr * ps_iattrs = & ps_iattr - > ia_iattr ;
ps_iattrs - > ia_ctime = ps_iattrs - > ia_mtime = CURRENT_TIME ;
}
2007-08-02 16:38:03 +04:00
return 0 ;
2007-06-13 23:27:24 +04:00
}
2009-02-12 20:56:59 +03:00
/**
* sysfs_pathname - return full path to sysfs dirent
* @ sd : sysfs_dirent whose path we want
* @ path : caller allocated buffer
*
* Gives the name " / " to the sysfs_root entry ; any path returned
* is relative to wherever sysfs is mounted .
*
* XXX : does no error checking on @ path size
*/
static char * sysfs_pathname ( struct sysfs_dirent * sd , char * path )
{
if ( sd - > s_parent ) {
sysfs_pathname ( sd - > s_parent , path ) ;
strcat ( path , " / " ) ;
}
strcat ( path , sd - > s_name ) ;
return path ;
}
2008-06-10 13:09:08 +04:00
/**
* sysfs_add_one - add sysfs_dirent to parent
* @ acxt : addrm context to use
* @ sd : sysfs_dirent to be added
*
* Get @ acxt - > parent_sd and set sd - > s_parent to it and increment
* nlink of parent inode if @ sd is a directory and link into the
* children list of the parent .
*
* This function should be called between calls to
* sysfs_addrm_start ( ) and sysfs_addrm_finish ( ) and should be
* passed the same @ acxt as passed to sysfs_addrm_start ( ) .
*
* LOCKING :
* Determined by sysfs_addrm_start ( ) .
*
* RETURNS :
* 0 on success , - EEXIST if entry with the given name already
* exists .
*/
int sysfs_add_one ( struct sysfs_addrm_cxt * acxt , struct sysfs_dirent * sd )
{
int ret ;
ret = __sysfs_add_one ( acxt , sd ) ;
2009-02-12 20:56:59 +03:00
if ( ret = = - EEXIST ) {
char * path = kzalloc ( PATH_MAX , GFP_KERNEL ) ;
WARN ( 1 , KERN_WARNING
" sysfs: cannot create duplicate filename '%s' \n " ,
( path = = NULL ) ? sd - > s_name :
strcat ( strcat ( sysfs_pathname ( acxt - > parent_sd , path ) , " / " ) ,
sd - > s_name ) ) ;
kfree ( path ) ;
}
2008-06-10 13:09:08 +04:00
return ret ;
}
2007-06-13 23:27:24 +04:00
/**
* sysfs_remove_one - remove sysfs_dirent from parent
* @ acxt : addrm context to use
2008-01-08 20:11:24 +03:00
* @ sd : sysfs_dirent to be removed
2007-06-13 23:27:24 +04:00
*
* Mark @ sd removed and drop nlink of parent inode if @ sd is a
2007-09-20 11:05:09 +04:00
* directory . @ sd is unlinked from the children list .
2007-06-13 23:27:24 +04:00
*
* This function should be called between calls to
* sysfs_addrm_start ( ) and sysfs_addrm_finish ( ) and should be
* passed the same @ acxt as passed to sysfs_addrm_start ( ) .
*
* LOCKING :
* Determined by sysfs_addrm_start ( ) .
*/
void sysfs_remove_one ( struct sysfs_addrm_cxt * acxt , struct sysfs_dirent * sd )
{
2009-11-21 03:08:51 +03:00
struct sysfs_inode_attrs * ps_iattr ;
2007-08-02 16:38:03 +04:00
BUG_ON ( sd - > s_flags & SYSFS_FLAG_REMOVED ) ;
sysfs_unlink_sibling ( sd ) ;
2007-06-13 23:27:24 +04:00
2009-11-21 03:08:51 +03:00
/* Update timestamps on the parent */
ps_iattr = acxt - > parent_sd - > s_iattr ;
if ( ps_iattr ) {
struct iattr * ps_iattrs = & ps_iattr - > ia_iattr ;
ps_iattrs - > ia_ctime = ps_iattrs - > ia_mtime = CURRENT_TIME ;
}
2007-06-13 23:27:24 +04:00
sd - > s_flags | = SYSFS_FLAG_REMOVED ;
sd - > s_sibling = acxt - > removed ;
acxt - > removed = sd ;
2007-06-13 23:27:24 +04:00
}
2007-06-13 23:27:24 +04:00
/**
* sysfs_addrm_finish - finish up sysfs_dirent add / remove
* @ acxt : addrm context to finish up
*
* Finish up sysfs_dirent add / remove . Resources acquired by
* sysfs_addrm_start ( ) are released and removed sysfs_dirents are
2009-11-21 03:08:55 +03:00
* cleaned up .
2007-06-13 23:27:24 +04:00
*
* LOCKING :
2009-11-21 03:08:55 +03:00
* sysfs_mutex is released .
2007-06-13 23:27:24 +04:00
*/
2007-08-02 16:38:03 +04:00
void sysfs_addrm_finish ( struct sysfs_addrm_cxt * acxt )
2007-06-13 23:27:24 +04:00
{
/* release resources acquired by sysfs_addrm_start() */
mutex_unlock ( & sysfs_mutex ) ;
/* kill removed sysfs_dirents */
while ( acxt - > removed ) {
struct sysfs_dirent * sd = acxt - > removed ;
acxt - > removed = sd - > s_sibling ;
sd - > s_sibling = NULL ;
sysfs_deactivate ( sd ) ;
2009-03-04 22:57:20 +03:00
unmap_bin_file ( sd ) ;
2007-06-13 23:27:24 +04:00
sysfs_put ( sd ) ;
2007-06-13 22:45:14 +04:00
}
2007-01-24 22:35:52 +03:00
}
2007-06-13 23:27:22 +04:00
/**
* sysfs_find_dirent - find sysfs_dirent with the given name
* @ parent_sd : sysfs_dirent to search under
* @ name : name to look for
*
* Look for sysfs_dirent with name @ name under @ parent_sd .
2006-03-09 17:10:14 +03:00
*
2007-06-13 23:27:22 +04:00
* LOCKING :
2007-06-13 23:27:23 +04:00
* mutex_lock ( sysfs_mutex )
2006-03-09 17:10:14 +03:00
*
2007-06-13 23:27:22 +04:00
* RETURNS :
* Pointer to sysfs_dirent if found , NULL if not .
2006-03-09 17:10:14 +03:00
*/
2007-06-13 23:27:22 +04:00
struct sysfs_dirent * sysfs_find_dirent ( struct sysfs_dirent * parent_sd ,
const unsigned char * name )
2006-03-09 17:10:14 +03:00
{
2007-06-13 23:27:22 +04:00
struct sysfs_dirent * sd ;
2007-09-20 11:05:12 +04:00
for ( sd = parent_sd - > s_dir . children ; sd ; sd = sd - > s_sibling )
2007-08-20 16:36:30 +04:00
if ( ! strcmp ( sd - > s_name , name ) )
2007-06-13 23:27:22 +04:00
return sd ;
return NULL ;
}
2006-03-09 17:10:14 +03:00
2007-06-13 23:27:22 +04:00
/**
* sysfs_get_dirent - find and get sysfs_dirent with the given name
* @ parent_sd : sysfs_dirent to search under
* @ name : name to look for
*
* Look for sysfs_dirent with name @ name under @ parent_sd and get
* it if found .
*
* LOCKING :
2007-06-13 23:27:23 +04:00
* Kernel thread context ( may sleep ) . Grabs sysfs_mutex .
2007-06-13 23:27:22 +04:00
*
* RETURNS :
* Pointer to sysfs_dirent if found , NULL if not .
*/
struct sysfs_dirent * sysfs_get_dirent ( struct sysfs_dirent * parent_sd ,
const unsigned char * name )
{
struct sysfs_dirent * sd ;
2007-06-13 23:27:23 +04:00
mutex_lock ( & sysfs_mutex ) ;
2007-06-13 23:27:22 +04:00
sd = sysfs_find_dirent ( parent_sd , name ) ;
sysfs_get ( sd ) ;
2007-06-13 23:27:23 +04:00
mutex_unlock ( & sysfs_mutex ) ;
2007-06-13 23:27:22 +04:00
return sd ;
2006-03-09 17:10:14 +03:00
}
2008-07-16 02:58:04 +04:00
EXPORT_SYMBOL_GPL ( sysfs_get_dirent ) ;
2006-03-09 17:10:14 +03:00
2007-06-13 23:27:22 +04:00
static int create_dir ( struct kobject * kobj , struct sysfs_dirent * parent_sd ,
const char * name , struct sysfs_dirent * * p_sd )
2005-04-17 02:20:36 +04:00
{
umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO ;
2007-06-13 23:27:25 +04:00
struct sysfs_addrm_cxt acxt ;
2007-06-13 22:45:14 +04:00
struct sysfs_dirent * sd ;
2007-08-02 16:38:03 +04:00
int rc ;
2005-04-17 02:20:36 +04:00
2007-06-13 22:45:17 +04:00
/* allocate */
2007-06-13 22:45:15 +04:00
sd = sysfs_new_dirent ( name , mode , SYSFS_DIR ) ;
2007-06-13 22:45:14 +04:00
if ( ! sd )
2007-06-13 23:27:25 +04:00
return - ENOMEM ;
2007-09-20 11:05:11 +04:00
sd - > s_dir . kobj = kobj ;
2007-06-13 22:45:14 +04:00
2007-06-13 22:45:17 +04:00
/* link in */
2007-06-13 23:27:25 +04:00
sysfs_addrm_start ( & acxt , parent_sd ) ;
2007-08-02 16:38:03 +04:00
rc = sysfs_add_one ( & acxt , sd ) ;
sysfs_addrm_finish ( & acxt ) ;
2007-07-18 11:38:11 +04:00
2007-08-02 16:38:03 +04:00
if ( rc = = 0 )
* p_sd = sd ;
else
2007-07-18 11:38:11 +04:00
sysfs_put ( sd ) ;
2007-06-13 22:45:14 +04:00
2007-08-02 16:38:03 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 23:27:22 +04:00
int sysfs_create_subdir ( struct kobject * kobj , const char * name ,
struct sysfs_dirent * * p_sd )
2005-04-17 02:20:36 +04:00
{
2007-06-13 23:27:22 +04:00
return create_dir ( kobj , kobj - > sd , name , p_sd ) ;
2005-04-17 02:20:36 +04:00
}
/**
* sysfs_create_dir - create a directory for an object .
* @ kobj : object we ' re creating directory for .
*/
2007-07-31 14:15:08 +04:00
int sysfs_create_dir ( struct kobject * kobj )
2005-04-17 02:20:36 +04:00
{
2007-06-13 23:27:22 +04:00
struct sysfs_dirent * parent_sd , * sd ;
2005-04-17 02:20:36 +04:00
int error = 0 ;
BUG_ON ( ! kobj ) ;
2007-07-31 14:15:08 +04:00
if ( kobj - > parent )
2007-06-13 23:27:22 +04:00
parent_sd = kobj - > parent - > sd ;
2005-04-17 02:20:36 +04:00
else
2007-08-20 16:36:30 +04:00
parent_sd = & sysfs_root ;
2005-04-17 02:20:36 +04:00
2007-06-13 23:27:22 +04:00
error = create_dir ( kobj , parent_sd , kobject_name ( kobj ) , & sd ) ;
2005-04-17 02:20:36 +04:00
if ( ! error )
2007-06-13 23:27:22 +04:00
kobj - > sd = sd ;
2005-04-17 02:20:36 +04:00
return error ;
}
static struct dentry * sysfs_lookup ( struct inode * dir , struct dentry * dentry ,
struct nameidata * nd )
{
2007-07-31 14:15:08 +04:00
struct dentry * ret = NULL ;
2007-08-02 16:38:02 +04:00
struct sysfs_dirent * parent_sd = dentry - > d_parent - > d_fsdata ;
struct sysfs_dirent * sd ;
2007-06-13 22:45:17 +04:00
struct inode * inode ;
2005-04-17 02:20:36 +04:00
2007-07-31 14:15:08 +04:00
mutex_lock ( & sysfs_mutex ) ;
2007-08-20 16:36:30 +04:00
sd = sysfs_find_dirent ( parent_sd , dentry - > d_name . name ) ;
2005-04-17 02:20:36 +04:00
2007-06-13 22:45:17 +04:00
/* no such entry */
2008-01-16 06:06:14 +03:00
if ( ! sd ) {
ret = ERR_PTR ( - ENOENT ) ;
2007-07-31 14:15:08 +04:00
goto out_unlock ;
2008-01-16 06:06:14 +03:00
}
2007-06-13 22:45:17 +04:00
/* attach dentry and inode */
2007-06-13 22:45:17 +04:00
inode = sysfs_get_inode ( sd ) ;
2007-07-31 14:15:08 +04:00
if ( ! inode ) {
ret = ERR_PTR ( - ENOMEM ) ;
goto out_unlock ;
}
2007-06-13 23:27:23 +04:00
2007-09-20 11:05:11 +04:00
/* instantiate and hash dentry */
2009-11-21 03:08:56 +03:00
ret = d_find_alias ( inode ) ;
if ( ! ret ) {
dentry - > d_op = & sysfs_dentry_ops ;
dentry - > d_fsdata = sysfs_get ( sd ) ;
d_add ( dentry , inode ) ;
} else {
d_move ( ret , dentry ) ;
iput ( inode ) ;
}
2007-06-13 22:45:17 +04:00
2007-07-31 14:15:08 +04:00
out_unlock :
2007-06-13 23:27:23 +04:00
mutex_unlock ( & sysfs_mutex ) ;
2007-07-31 14:15:08 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2007-02-12 11:55:40 +03:00
const struct inode_operations sysfs_dir_inode_operations = {
2005-04-17 02:20:36 +04:00
. lookup = sysfs_lookup ,
2009-11-21 03:08:53 +03:00
. permission = sysfs_permission ,
2005-05-31 09:09:14 +04:00
. setattr = sysfs_setattr ,
2009-11-21 03:08:53 +03:00
. getattr = sysfs_getattr ,
2009-09-09 22:25:37 +04:00
. setxattr = sysfs_setxattr ,
2005-04-17 02:20:36 +04:00
} ;
2007-06-13 23:27:22 +04:00
static void remove_dir ( struct sysfs_dirent * sd )
2005-04-17 02:20:36 +04:00
{
2007-06-13 23:27:24 +04:00
struct sysfs_addrm_cxt acxt ;
2005-04-17 02:20:36 +04:00
2007-06-13 23:27:24 +04:00
sysfs_addrm_start ( & acxt , sd - > s_parent ) ;
sysfs_remove_one ( & acxt , sd ) ;
sysfs_addrm_finish ( & acxt ) ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 23:27:22 +04:00
void sysfs_remove_subdir ( struct sysfs_dirent * sd )
2005-04-17 02:20:36 +04:00
{
2007-06-13 23:27:22 +04:00
remove_dir ( sd ) ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 23:27:22 +04:00
static void __sysfs_remove_dir ( struct sysfs_dirent * dir_sd )
2005-04-17 02:20:36 +04:00
{
2007-06-13 23:27:24 +04:00
struct sysfs_addrm_cxt acxt ;
2007-06-13 22:45:18 +04:00
struct sysfs_dirent * * pos ;
2005-04-17 02:20:36 +04:00
2007-06-13 23:27:22 +04:00
if ( ! dir_sd )
2005-04-17 02:20:36 +04:00
return ;
2007-06-13 23:27:22 +04:00
pr_debug ( " sysfs %s: removing dir \n " , dir_sd - > s_name ) ;
2007-06-13 23:27:24 +04:00
sysfs_addrm_start ( & acxt , dir_sd ) ;
2007-09-20 11:05:12 +04:00
pos = & dir_sd - > s_dir . children ;
2007-06-13 22:45:18 +04:00
while ( * pos ) {
struct sysfs_dirent * sd = * pos ;
2007-08-20 16:36:30 +04:00
if ( sysfs_type ( sd ) ! = SYSFS_DIR )
2007-06-13 23:27:24 +04:00
sysfs_remove_one ( & acxt , sd ) ;
2007-08-02 16:38:03 +04:00
else
2007-06-13 22:45:18 +04:00
pos = & ( * pos ) - > s_sibling ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 23:27:24 +04:00
sysfs_addrm_finish ( & acxt ) ;
2007-06-13 22:45:16 +04:00
2007-06-13 23:27:22 +04:00
remove_dir ( dir_sd ) ;
2007-01-24 22:35:52 +03:00
}
/**
* sysfs_remove_dir - remove an object ' s directory .
* @ kobj : object .
*
* The only thing special about this is that we remove any files in
* the directory before we remove the directory , and we ' ve inlined
* what used to be sysfs_rmdir ( ) below , instead of calling separately .
*/
void sysfs_remove_dir ( struct kobject * kobj )
{
2007-06-13 23:27:22 +04:00
struct sysfs_dirent * sd = kobj - > sd ;
2007-06-13 22:45:15 +04:00
2007-06-13 23:27:23 +04:00
spin_lock ( & sysfs_assoc_lock ) ;
2007-06-13 23:27:22 +04:00
kobj - > sd = NULL ;
2007-06-13 23:27:23 +04:00
spin_unlock ( & sysfs_assoc_lock ) ;
2007-06-13 22:45:15 +04:00
2007-06-13 23:27:22 +04:00
__sysfs_remove_dir ( sd ) ;
2005-04-17 02:20:36 +04:00
}
2009-11-21 03:08:57 +03:00
int sysfs_rename ( struct sysfs_dirent * sd ,
struct sysfs_dirent * new_parent_sd , const char * new_name )
2005-04-17 02:20:36 +04:00
{
2007-06-13 23:27:25 +04:00
const char * dup_name = NULL ;
2007-06-13 22:45:14 +04:00
int error ;
2005-04-17 02:20:36 +04:00
2009-11-21 03:08:56 +03:00
mutex_lock ( & sysfs_mutex ) ;
2007-08-20 16:36:30 +04:00
2007-08-20 16:36:31 +04:00
error = 0 ;
2009-11-21 03:08:57 +03:00
if ( ( sd - > s_parent = = new_parent_sd ) & &
( strcmp ( sd - > s_name , new_name ) = = 0 ) )
2007-08-20 16:36:31 +04:00
goto out ; /* nothing to rename */
error = - EEXIST ;
2009-11-21 03:08:57 +03:00
if ( sysfs_find_dirent ( new_parent_sd , new_name ) )
2009-11-21 03:08:56 +03:00
goto out ;
2007-06-13 22:45:14 +04:00
2008-07-04 05:05:28 +04:00
/* rename sysfs_dirent */
2009-11-21 03:08:57 +03:00
if ( strcmp ( sd - > s_name , new_name ) ! = 0 ) {
error = - ENOMEM ;
new_name = dup_name = kstrdup ( new_name , GFP_KERNEL ) ;
if ( ! new_name )
goto out ;
dup_name = sd - > s_name ;
sd - > s_name = new_name ;
}
2007-06-13 22:45:15 +04:00
2009-11-21 03:08:57 +03:00
/* Remove from old parent's list and insert into new parent's list. */
if ( sd - > s_parent ! = new_parent_sd ) {
sysfs_unlink_sibling ( sd ) ;
sysfs_get ( new_parent_sd ) ;
sysfs_put ( sd - > s_parent ) ;
sd - > s_parent = new_parent_sd ;
sysfs_link_sibling ( sd ) ;
}
2007-06-13 22:45:15 +04:00
2007-06-13 22:45:14 +04:00
error = 0 ;
2009-11-21 03:08:56 +03:00
out :
2007-08-20 16:36:31 +04:00
mutex_unlock ( & sysfs_mutex ) ;
2007-06-13 23:27:25 +04:00
kfree ( dup_name ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2009-11-21 03:08:57 +03:00
int sysfs_rename_dir ( struct kobject * kobj , const char * new_name )
{
return sysfs_rename ( kobj - > sd , kobj - > sd - > s_parent , new_name ) ;
}
2007-06-13 23:27:25 +04:00
int sysfs_move_dir ( struct kobject * kobj , struct kobject * new_parent_kobj )
2006-11-20 19:07:51 +03:00
{
2007-06-13 23:27:25 +04:00
struct sysfs_dirent * sd = kobj - > sd ;
struct sysfs_dirent * new_parent_sd ;
2006-11-20 19:07:51 +03:00
2007-06-13 23:27:25 +04:00
BUG_ON ( ! sd - > s_parent ) ;
2009-11-21 03:08:57 +03:00
new_parent_sd = new_parent_kobj & & new_parent_kobj - > sd ?
2009-10-06 17:33:35 +04:00
new_parent_kobj - > sd : & sysfs_root ;
2007-06-13 23:27:25 +04:00
2009-11-21 03:08:57 +03:00
return sysfs_rename ( sd , new_parent_sd , sd - > s_name ) ;
2006-11-20 19:07:51 +03:00
}
2005-04-17 02:20:36 +04:00
/* Relationship between s_mode and the DT_xxx types */
static inline unsigned char dt_type ( struct sysfs_dirent * sd )
{
return ( sd - > s_mode > > 12 ) & 15 ;
}
2010-01-02 01:43:53 +03:00
static int sysfs_dir_release ( struct inode * inode , struct file * filp )
{
sysfs_put ( filp - > private_data ) ;
return 0 ;
}
static struct sysfs_dirent * sysfs_dir_pos ( struct sysfs_dirent * parent_sd ,
ino_t ino , struct sysfs_dirent * pos )
{
if ( pos ) {
int valid = ! ( pos - > s_flags & SYSFS_FLAG_REMOVED ) & &
pos - > s_parent = = parent_sd & &
ino = = pos - > s_ino ;
sysfs_put ( pos ) ;
if ( valid )
return pos ;
}
pos = NULL ;
if ( ( ino > 1 ) & & ( ino < INT_MAX ) ) {
pos = parent_sd - > s_dir . children ;
while ( pos & & ( ino > pos - > s_ino ) )
pos = pos - > s_sibling ;
}
return pos ;
}
static struct sysfs_dirent * sysfs_dir_next_pos ( struct sysfs_dirent * parent_sd ,
ino_t ino , struct sysfs_dirent * pos )
{
pos = sysfs_dir_pos ( parent_sd , ino , pos ) ;
if ( pos )
pos = pos - > s_sibling ;
return pos ;
}
2005-04-17 02:20:36 +04:00
static int sysfs_readdir ( struct file * filp , void * dirent , filldir_t filldir )
{
2006-12-08 13:36:36 +03:00
struct dentry * dentry = filp - > f_path . dentry ;
2005-04-17 02:20:36 +04:00
struct sysfs_dirent * parent_sd = dentry - > d_fsdata ;
2010-01-02 01:43:53 +03:00
struct sysfs_dirent * pos = filp - > private_data ;
2005-04-17 02:20:36 +04:00
ino_t ino ;
2007-08-20 16:36:30 +04:00
if ( filp - > f_pos = = 0 ) {
ino = parent_sd - > s_ino ;
if ( filldir ( dirent , " . " , 1 , filp - > f_pos , ino , DT_DIR ) = = 0 )
2005-04-17 02:20:36 +04:00
filp - > f_pos + + ;
2007-08-20 16:36:30 +04:00
}
if ( filp - > f_pos = = 1 ) {
if ( parent_sd - > s_parent )
ino = parent_sd - > s_parent - > s_ino ;
else
ino = parent_sd - > s_ino ;
if ( filldir ( dirent , " .. " , 2 , filp - > f_pos , ino , DT_DIR ) = = 0 )
2005-04-17 02:20:36 +04:00
filp - > f_pos + + ;
2007-08-20 16:36:30 +04:00
}
2010-01-02 01:43:53 +03:00
mutex_lock ( & sysfs_mutex ) ;
for ( pos = sysfs_dir_pos ( parent_sd , filp - > f_pos , pos ) ;
pos ;
pos = sysfs_dir_next_pos ( parent_sd , filp - > f_pos , pos ) ) {
const char * name ;
unsigned int type ;
int len , ret ;
name = pos - > s_name ;
len = strlen ( name ) ;
ino = pos - > s_ino ;
type = dt_type ( pos ) ;
filp - > f_pos = ino ;
filp - > private_data = sysfs_get ( pos ) ;
2005-04-17 02:20:36 +04:00
2007-06-13 23:27:23 +04:00
mutex_unlock ( & sysfs_mutex ) ;
2010-01-02 01:43:53 +03:00
ret = filldir ( dirent , name , len , filp - > f_pos , ino , type ) ;
mutex_lock ( & sysfs_mutex ) ;
if ( ret < 0 )
break ;
}
mutex_unlock ( & sysfs_mutex ) ;
if ( ( filp - > f_pos > 1 ) & & ! pos ) { /* EOF */
filp - > f_pos = INT_MAX ;
filp - > private_data = NULL ;
2005-04-17 02:20:36 +04:00
}
2007-08-20 16:36:30 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-08-20 16:36:30 +04:00
2006-03-28 13:56:42 +04:00
const struct file_operations sysfs_dir_operations = {
2005-04-17 02:20:36 +04:00
. read = generic_read_dir ,
. readdir = sysfs_readdir ,
2010-01-02 01:43:53 +03:00
. release = sysfs_dir_release ,
2008-09-03 23:53:01 +04:00
. llseek = generic_file_llseek ,
2005-04-17 02:20:36 +04:00
} ;