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>
2005-04-17 02:20:36 +04:00
# include "sysfs.h"
2007-06-13 23:27:23 +04:00
DEFINE_MUTEX ( sysfs_mutex ) ;
2007-08-20 16:36:30 +04:00
DEFINE_MUTEX ( sysfs_rename_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 23:27:25 +04:00
/**
* sysfs_get_dentry - get dentry for the given sysfs_dirent
* @ sd : sysfs_dirent of interest
*
* Get dentry for @ sd . Dentry is looked up if currently not
2007-08-20 16:36:30 +04:00
* present . This function descends from the root looking up
* dentry for each step .
2007-06-13 23:27:25 +04:00
*
* LOCKING :
2007-08-20 16:36:30 +04:00
* mutex_lock ( sysfs_rename_mutex )
2007-06-13 23:27:25 +04:00
*
* RETURNS :
* Pointer to found dentry on success , ERR_PTR ( ) value on error .
*/
struct dentry * sysfs_get_dentry ( struct sysfs_dirent * sd )
{
2007-08-20 16:36:30 +04:00
struct dentry * dentry = dget ( sysfs_sb - > s_root ) ;
2007-06-13 23:27:25 +04:00
2007-08-20 16:36:30 +04:00
while ( dentry - > d_fsdata ! = sd ) {
struct sysfs_dirent * cur ;
struct dentry * parent ;
2007-06-13 23:27:25 +04:00
2007-08-20 16:36:30 +04:00
/* find the first ancestor which hasn't been looked up */
cur = sd ;
while ( cur - > s_parent ! = dentry - > d_fsdata )
2007-06-13 23:27:25 +04:00
cur = cur - > s_parent ;
/* look it up */
2007-08-20 16:36:30 +04:00
parent = dentry ;
mutex_lock ( & parent - > d_inode - > i_mutex ) ;
2007-10-17 10:25:38 +04:00
dentry = lookup_one_noperm ( cur - > s_name , parent ) ;
2007-08-20 16:36:30 +04:00
mutex_unlock ( & parent - > d_inode - > i_mutex ) ;
dput ( parent ) ;
2007-06-13 23:27:25 +04:00
2007-08-20 16:36:30 +04:00
if ( IS_ERR ( dentry ) )
break ;
2007-06-13 23:27:25 +04:00
}
return dentry ;
}
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 .
*/
2007-10-24 20:23:32 +04:00
static 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 ) ;
if ( likely ( t = = v ) )
return sd ;
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 .
*/
2007-10-24 20:23:32 +04:00
static 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 ;
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_get_active_two - get active references to sysfs_dirent and parent
* @ sd : sysfs_dirent of interest
*
* Get active reference to @ sd and its parent . Parent ' s active
* reference is grabbed first . This function is noop if @ sd is
* NULL .
*
* RETURNS :
* Pointer to @ sd on success , NULL on failure .
*/
struct sysfs_dirent * sysfs_get_active_two ( struct sysfs_dirent * sd )
{
if ( sd ) {
if ( sd - > s_parent & & unlikely ( ! sysfs_get_active ( sd - > s_parent ) ) )
return NULL ;
if ( unlikely ( ! sysfs_get_active ( sd ) ) ) {
sysfs_put_active ( sd - > s_parent ) ;
return NULL ;
}
}
return sd ;
}
/**
* sysfs_put_active_two - put active references to sysfs_dirent and parent
* @ sd : sysfs_dirent of interest
*
* Put active references to @ sd and its parent . This function is
* noop if @ sd is NULL .
*/
void sysfs_put_active_two ( struct sysfs_dirent * sd )
{
if ( sd ) {
sysfs_put_active ( sd ) ;
sysfs_put_active ( sd - > s_parent ) ;
}
}
/**
* 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 ) ) ;
2007-06-13 22:45:18 +04:00
sd - > s_sibling = ( void * ) & wait ;
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 ) ;
if ( v ! = SD_DEACTIVATED_BIAS )
wait_for_completion ( & wait ) ;
2007-06-13 22:45:18 +04:00
sd - > s_sibling = NULL ;
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 ) ;
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
}
2005-04-17 02:20:36 +04:00
static void sysfs_d_iput ( struct dentry * dentry , struct inode * inode )
{
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 ) ;
}
static struct dentry_operations sysfs_dentry_ops = {
. d_iput = sysfs_d_iput ,
} ;
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:24 +04:00
static int sysfs_ilookup_test ( struct inode * inode , void * arg )
{
struct sysfs_dirent * sd = arg ;
return inode - > i_ino = = sd - > s_ino ;
}
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
* sysfs_mutex , grabs inode for @ parent_sd if available and lock
* i_mutex of it . @ acxt is used to keep and pass context to
* 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
* return . i_mutex of parent inode is locked on return if
* available .
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
struct inode * inode ;
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 ;
/* Lookup parent inode. inode initialization and I_NEW
* clearing are protected by sysfs_mutex . By grabbing it and
* looking up with _nowait variant , inode state can be
* determined reliably .
*/
mutex_lock ( & sysfs_mutex ) ;
inode = ilookup5_nowait ( sysfs_sb , parent_sd - > s_ino , sysfs_ilookup_test ,
parent_sd ) ;
if ( inode & & ! ( inode - > i_state & I_NEW ) ) {
/* parent inode available */
acxt - > parent_inode = inode ;
/* sysfs_mutex is below i_mutex in lock hierarchy.
* First , trylock i_mutex . If fails , unlock
* sysfs_mutex and lock them in order .
*/
if ( ! mutex_trylock ( & inode - > i_mutex ) ) {
mutex_unlock ( & sysfs_mutex ) ;
mutex_lock ( & inode - > i_mutex ) ;
mutex_lock ( & sysfs_mutex ) ;
}
} else
iput ( inode ) ;
}
/**
* 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
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
*/
2007-08-02 16:38:03 +04:00
int sysfs_add_one ( struct sysfs_addrm_cxt * acxt , struct sysfs_dirent * sd )
2007-06-13 23:27:24 +04:00
{
2007-09-13 13:53:13 +04:00
if ( sysfs_find_dirent ( acxt - > parent_sd , sd - > s_name ) ) {
printk ( KERN_WARNING " sysfs: duplicate filename '%s' "
" can not be created \n " , sd - > s_name ) ;
WARN_ON ( 1 ) ;
2007-08-02 16:38:03 +04:00
return - EEXIST ;
2007-09-13 13:53:13 +04:00
}
2007-08-02 16:38:03 +04:00
2007-06-13 23:27:24 +04:00
sd - > s_parent = sysfs_get ( acxt - > parent_sd ) ;
if ( sysfs_type ( sd ) = = SYSFS_DIR & & acxt - > parent_inode )
inc_nlink ( acxt - > parent_inode ) ;
acxt - > cnt + + ;
2007-08-02 16:38:03 +04:00
sysfs_link_sibling ( sd ) ;
2007-08-02 16:38:03 +04:00
return 0 ;
2007-06-13 23:27:24 +04:00
}
/**
* sysfs_remove_one - remove sysfs_dirent from parent
* @ acxt : addrm context to use
* @ sd : sysfs_dirent to be added
*
* 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 )
{
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
sd - > s_flags | = SYSFS_FLAG_REMOVED ;
sd - > s_sibling = acxt - > removed ;
acxt - > removed = sd ;
if ( sysfs_type ( sd ) = = SYSFS_DIR & & acxt - > parent_inode )
drop_nlink ( acxt - > parent_inode ) ;
acxt - > cnt + + ;
}
2007-06-13 23:27:24 +04:00
/**
* sysfs_drop_dentry - drop dentry for the specified sysfs_dirent
* @ sd : target sysfs_dirent
*
* Drop dentry for @ sd . @ sd must have been unlinked from its
* parent on entry to this function such that it can ' t be looked
* up anymore .
*/
static void sysfs_drop_dentry ( struct sysfs_dirent * sd )
{
struct inode * inode ;
2007-08-20 16:36:30 +04:00
struct dentry * dentry ;
inode = ilookup ( sysfs_sb , sd - > s_ino ) ;
if ( ! inode )
return ;
2007-06-13 23:27:24 +04:00
2007-08-20 16:36:30 +04:00
/* Drop any existing dentries associated with sd.
*
* For the dentry to be properly freed we need to grab a
* reference to the dentry under the dcache lock , unhash it ,
* and then put it . The playing with the dentry count allows
* dput to immediately free the dentry if it is not in use .
2007-06-13 23:27:24 +04:00
*/
2007-08-20 16:36:30 +04:00
repeat :
2007-06-13 23:27:24 +04:00
spin_lock ( & dcache_lock ) ;
2007-08-20 16:36:30 +04:00
list_for_each_entry ( dentry , & inode - > i_dentry , d_alias ) {
if ( d_unhashed ( dentry ) )
continue ;
dget_locked ( dentry ) ;
2007-06-13 23:27:24 +04:00
spin_lock ( & dentry - > d_lock ) ;
__d_drop ( dentry ) ;
spin_unlock ( & dentry - > d_lock ) ;
2007-08-20 16:36:30 +04:00
spin_unlock ( & dcache_lock ) ;
dput ( dentry ) ;
goto repeat ;
2007-06-13 23:27:24 +04:00
}
spin_unlock ( & dcache_lock ) ;
/* adjust nlink and update timestamp */
2007-08-20 16:36:30 +04:00
mutex_lock ( & inode - > i_mutex ) ;
2007-06-13 23:27:24 +04:00
2007-08-20 16:36:30 +04:00
inode - > i_ctime = CURRENT_TIME ;
drop_nlink ( inode ) ;
if ( sysfs_type ( sd ) = = SYSFS_DIR )
2007-06-13 23:27:24 +04:00
drop_nlink ( inode ) ;
2007-08-20 16:36:30 +04:00
mutex_unlock ( & inode - > i_mutex ) ;
iput ( inode ) ;
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
* cleaned up . Timestamps on the parent inode are updated .
*
* LOCKING :
* All mutexes acquired by sysfs_addrm_start ( ) are released .
*/
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 ) ;
if ( acxt - > parent_inode ) {
struct inode * inode = acxt - > parent_inode ;
/* if added/removed, update timestamps on the parent */
if ( acxt - > cnt )
inode - > i_ctime = inode - > i_mtime = CURRENT_TIME ;
mutex_unlock ( & inode - > i_mutex ) ;
iput ( inode ) ;
}
/* kill removed sysfs_dirents */
while ( acxt - > removed ) {
struct sysfs_dirent * sd = acxt - > removed ;
acxt - > removed = sd - > s_sibling ;
sd - > s_sibling = NULL ;
sysfs_drop_dentry ( sd ) ;
sysfs_deactivate ( sd ) ;
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
}
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 */
2007-08-02 16:38:02 +04:00
if ( ! sd )
2007-07-31 14:15:08 +04:00
goto out_unlock ;
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 */
dentry - > d_op = & sysfs_dentry_ops ;
dentry - > d_fsdata = sysfs_get ( sd ) ;
2007-08-20 16:36:29 +04:00
d_instantiate ( dentry , inode ) ;
2007-09-20 11:05:11 +04:00
d_rehash ( dentry ) ;
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 ,
2005-05-31 09:09:14 +04:00
. setattr = sysfs_setattr ,
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
}
2007-07-31 14:15:08 +04:00
int sysfs_rename_dir ( struct kobject * kobj , const char * new_name )
2005-04-17 02:20:36 +04:00
{
2007-08-20 16:36:31 +04:00
struct sysfs_dirent * sd = kobj - > sd ;
2007-07-31 14:15:08 +04:00
struct dentry * parent = NULL ;
2007-06-13 23:27:25 +04:00
struct dentry * old_dentry = NULL , * new_dentry = NULL ;
const char * dup_name = NULL ;
2007-06-13 22:45:14 +04:00
int error ;
2005-04-17 02:20:36 +04:00
2007-08-20 16:36:30 +04:00
mutex_lock ( & sysfs_rename_mutex ) ;
2007-08-20 16:36:31 +04:00
error = 0 ;
if ( strcmp ( sd - > s_name , new_name ) = = 0 )
goto out ; /* nothing to rename */
2007-08-02 16:38:02 +04:00
/* get the original dentry */
2007-06-13 23:27:25 +04:00
old_dentry = sysfs_get_dentry ( sd ) ;
if ( IS_ERR ( old_dentry ) ) {
error = PTR_ERR ( old_dentry ) ;
2007-08-20 16:36:31 +04:00
goto out ;
2007-06-13 23:27:25 +04:00
}
2007-08-02 16:38:02 +04:00
parent = old_dentry - > d_parent ;
2005-04-17 02:20:36 +04:00
2007-07-31 14:15:08 +04:00
/* lock parent and get dentry for new name */
mutex_lock ( & parent - > d_inode - > i_mutex ) ;
2007-08-20 16:36:31 +04:00
mutex_lock ( & sysfs_mutex ) ;
2005-04-17 02:20:36 +04:00
2007-08-20 16:36:31 +04:00
error = - EEXIST ;
if ( sysfs_find_dirent ( sd - > s_parent , new_name ) )
2007-06-13 23:27:25 +04:00
goto out_unlock ;
2007-06-13 22:45:14 +04:00
2007-08-20 16:36:31 +04:00
error = - ENOMEM ;
new_dentry = d_alloc_name ( parent , new_name ) ;
if ( ! new_dentry )
2007-06-13 23:27:25 +04:00
goto out_unlock ;
2007-06-13 22:45:14 +04:00
2007-06-13 22:45:15 +04:00
/* rename kobject and sysfs_dirent */
error = - ENOMEM ;
new_name = dup_name = kstrdup ( new_name , GFP_KERNEL ) ;
if ( ! new_name )
2007-08-20 16:36:31 +04:00
goto out_unlock ;
2007-06-13 22:45:15 +04:00
2007-06-13 22:45:14 +04:00
error = kobject_set_name ( kobj , " %s " , new_name ) ;
if ( error )
2007-08-20 16:36:31 +04:00
goto out_unlock ;
2007-06-13 22:45:14 +04:00
2007-06-13 23:27:25 +04:00
dup_name = sd - > s_name ;
2007-06-13 22:45:15 +04:00
sd - > s_name = new_name ;
2007-08-02 16:38:02 +04:00
/* rename */
2007-06-13 22:45:14 +04:00
d_add ( new_dentry , NULL ) ;
2007-08-20 16:36:30 +04:00
d_move ( old_dentry , new_dentry ) ;
2007-06-13 22:45:14 +04:00
error = 0 ;
out_unlock :
2007-08-20 16:36:31 +04:00
mutex_unlock ( & sysfs_mutex ) ;
2007-07-31 14:15:08 +04:00
mutex_unlock ( & parent - > d_inode - > i_mutex ) ;
2007-06-13 23:27:25 +04:00
kfree ( dup_name ) ;
dput ( old_dentry ) ;
dput ( new_dentry ) ;
2007-08-20 16:36:31 +04:00
out :
2007-08-20 16:36:30 +04:00
mutex_unlock ( & sysfs_rename_mutex ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
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 ;
struct dentry * old_parent , * new_parent = NULL ;
struct dentry * old_dentry = NULL , * new_dentry = NULL ;
2006-11-20 19:07:51 +03:00
int error ;
2007-08-20 16:36:30 +04:00
mutex_lock ( & sysfs_rename_mutex ) ;
2007-06-13 23:27:25 +04:00
BUG_ON ( ! sd - > s_parent ) ;
new_parent_sd = new_parent_kobj - > sd ? new_parent_kobj - > sd : & sysfs_root ;
2007-08-20 16:36:31 +04:00
error = 0 ;
if ( sd - > s_parent = = new_parent_sd )
goto out ; /* nothing to move */
2007-06-13 23:27:25 +04:00
/* get dentries */
old_dentry = sysfs_get_dentry ( sd ) ;
if ( IS_ERR ( old_dentry ) ) {
error = PTR_ERR ( old_dentry ) ;
2007-08-20 16:36:31 +04:00
goto out ;
2007-06-13 23:27:25 +04:00
}
2007-08-20 16:36:30 +04:00
old_parent = old_dentry - > d_parent ;
2007-06-13 23:27:25 +04:00
new_parent = sysfs_get_dentry ( new_parent_sd ) ;
if ( IS_ERR ( new_parent ) ) {
error = PTR_ERR ( new_parent ) ;
2007-08-20 16:36:31 +04:00
goto out ;
2007-06-13 23:27:25 +04:00
}
2006-11-20 19:07:51 +03:00
again :
2007-06-13 23:27:25 +04:00
mutex_lock ( & old_parent - > d_inode - > i_mutex ) ;
if ( ! mutex_trylock ( & new_parent - > d_inode - > i_mutex ) ) {
mutex_unlock ( & old_parent - > d_inode - > i_mutex ) ;
2006-11-20 19:07:51 +03:00
goto again ;
}
2007-08-20 16:36:31 +04:00
mutex_lock ( & sysfs_mutex ) ;
2006-11-20 19:07:51 +03:00
2007-08-20 16:36:31 +04:00
error = - EEXIST ;
if ( sysfs_find_dirent ( new_parent_sd , sd - > s_name ) )
2007-06-13 23:27:25 +04:00
goto out_unlock ;
2007-08-20 16:36:31 +04:00
error = - ENOMEM ;
new_dentry = d_alloc_name ( new_parent , sd - > s_name ) ;
if ( ! new_dentry )
goto out_unlock ;
error = 0 ;
2006-11-20 19:07:51 +03:00
d_add ( new_dentry , NULL ) ;
2007-08-20 16:36:30 +04:00
d_move ( old_dentry , new_dentry ) ;
2006-11-20 19:07:51 +03:00
dput ( new_dentry ) ;
/* Remove from old parent's list and insert into new parent's list. */
2007-06-13 22:45:18 +04:00
sysfs_unlink_sibling ( sd ) ;
2007-06-13 22:45:17 +04:00
sysfs_get ( new_parent_sd ) ;
sysfs_put ( sd - > s_parent ) ;
sd - > s_parent = new_parent_sd ;
2007-06-13 22:45:18 +04:00
sysfs_link_sibling ( sd ) ;
2006-11-20 19:07:51 +03:00
2007-06-13 23:27:25 +04:00
out_unlock :
2007-08-20 16:36:31 +04:00
mutex_unlock ( & sysfs_mutex ) ;
2007-06-13 23:27:25 +04:00
mutex_unlock ( & new_parent - > d_inode - > i_mutex ) ;
mutex_unlock ( & old_parent - > d_inode - > i_mutex ) ;
2007-08-20 16:36:31 +04:00
out :
2007-06-13 23:27:25 +04:00
dput ( new_parent ) ;
dput ( old_dentry ) ;
dput ( new_dentry ) ;
2007-08-20 16:36:30 +04:00
mutex_unlock ( & sysfs_rename_mutex ) ;
2006-11-20 19:07:51 +03:00
return error ;
}
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 ;
}
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 ;
2007-08-20 16:36:30 +04:00
struct sysfs_dirent * pos ;
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
}
if ( ( filp - > f_pos > 1 ) & & ( filp - > f_pos < INT_MAX ) ) {
mutex_lock ( & sysfs_mutex ) ;
2007-06-13 22:45:18 +04:00
2007-08-20 16:36:30 +04:00
/* Skip the dentries we have already reported */
2007-09-20 11:05:12 +04:00
pos = parent_sd - > s_dir . children ;
2007-08-20 16:36:30 +04:00
while ( pos & & ( filp - > f_pos > pos - > s_ino ) )
pos = pos - > s_sibling ;
2007-06-13 23:27:23 +04:00
2007-08-20 16:36:30 +04:00
for ( ; pos ; pos = pos - > s_sibling ) {
const char * name ;
int len ;
2005-04-17 02:20:36 +04:00
2007-08-20 16:36:30 +04:00
name = pos - > s_name ;
len = strlen ( name ) ;
filp - > f_pos = ino = pos - > s_ino ;
2005-04-17 02:20:36 +04:00
2007-08-20 16:36:30 +04:00
if ( filldir ( dirent , name , len , filp - > f_pos , ino ,
dt_type ( pos ) ) < 0 )
2005-04-17 02:20:36 +04:00
break ;
}
2007-08-20 16:36:30 +04:00
if ( ! pos )
filp - > f_pos = INT_MAX ;
2007-06-13 23:27:23 +04:00
mutex_unlock ( & sysfs_mutex ) ;
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 ,
} ;