2005-04-17 02:20:36 +04:00
/*
* dir . c - Operations for sysfs directories .
*/
# 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>
2006-12-20 12:52:44 +03:00
# include <asm/semaphore.h>
2005-04-17 02:20:36 +04:00
# include "sysfs.h"
DECLARE_RWSEM ( sysfs_rename_sem ) ;
2007-06-11 09:04:01 +04:00
spinlock_t sysfs_lock = SPIN_LOCK_UNLOCKED ;
2007-06-13 22:45:15 +04:00
spinlock_t kobj_sysfs_assoc_lock = SPIN_LOCK_UNLOCKED ;
2005-04-17 02:20:36 +04:00
2007-06-13 22:45:13 +04:00
static spinlock_t sysfs_ino_lock = SPIN_LOCK_UNLOCKED ;
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
* sd - > s_parent - > s_children .
*
* Locking :
* mutex_lock ( sd - > s_parent - > dentry - > d_inode - > i_mutex )
*/
static void sysfs_link_sibling ( struct sysfs_dirent * sd )
{
struct sysfs_dirent * parent_sd = sd - > s_parent ;
BUG_ON ( sd - > s_sibling ) ;
sd - > s_sibling = parent_sd - > s_children ;
parent_sd - > s_children = sd ;
}
/**
* sysfs_unlink_sibling - unlink sysfs_dirent from sibling list
* @ sd : sysfs_dirent of interest
*
* Unlink @ sd from its sibling list which starts from
* sd - > s_parent - > s_children .
*
* Locking :
* mutex_lock ( sd - > s_parent - > dentry - > d_inode - > i_mutex )
*/
static void sysfs_unlink_sibling ( struct sysfs_dirent * sd )
{
struct sysfs_dirent * * pos ;
for ( pos = & sd - > s_parent - > s_children ; * pos ; pos = & ( * pos ) - > s_sibling ) {
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 .
*/
struct sysfs_dirent * sysfs_get_active ( struct sysfs_dirent * sd )
{
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 .
*/
void sysfs_put_active ( struct sysfs_dirent * sd )
{
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
*/
void sysfs_deactivate ( struct sysfs_dirent * sd )
{
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 :
parent_sd = sd - > s_parent ;
2007-06-13 23:27:21 +04:00
if ( sysfs_type ( sd ) = = SYSFS_KOBJ_LINK )
2007-06-13 22:45:15 +04:00
sysfs_put ( sd - > s_elem . 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 ;
if ( sd ) {
2007-06-11 09:04:01 +04:00
/* sd->s_dentry is protected with sysfs_lock. This
* allows sysfs_drop_dentry ( ) to dereference it .
*/
spin_lock ( & sysfs_lock ) ;
/* The dentry might have been deleted or another
* lookup could have happened updating sd - > s_dentry to
* point the new dentry . Ignore if it isn ' t pointing
* to this dentry .
*/
if ( sd - > s_dentry = = dentry )
sd - > s_dentry = NULL ;
spin_unlock ( & sysfs_lock ) ;
2005-04-17 02:20:36 +04:00
sysfs_put ( sd ) ;
}
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 ;
struct sysfs_dirent * sd = NULL ;
if ( type & SYSFS_COPY_NAME ) {
name = dup_name = kstrdup ( name , GFP_KERNEL ) ;
if ( ! name )
goto err_out ;
}
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-06-13 22:45:15 +04:00
goto err_out ;
2005-04-17 02:20:36 +04:00
2007-06-13 22:45:15 +04:00
if ( sysfs_alloc_ino ( & sd - > s_ino ) )
goto err_out ;
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 ) ;
2006-08-03 20:06:25 +04:00
atomic_set ( & sd - > s_event , 1 ) ;
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
err_out :
kfree ( dup_name ) ;
kmem_cache_free ( sysfs_dir_cachep , sd ) ;
return NULL ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 22:45:16 +04:00
static void sysfs_attach_dentry ( struct sysfs_dirent * sd , struct dentry * dentry )
{
dentry - > d_op = & sysfs_dentry_ops ;
dentry - > d_fsdata = sysfs_get ( sd ) ;
/* protect sd->s_dentry against sysfs_d_iput */
spin_lock ( & sysfs_lock ) ;
sd - > s_dentry = dentry ;
spin_unlock ( & sysfs_lock ) ;
d_rehash ( dentry ) ;
}
2007-06-13 22:45:14 +04:00
void sysfs_attach_dirent ( struct sysfs_dirent * sd ,
struct sysfs_dirent * parent_sd , struct dentry * dentry )
2007-01-24 22:35:52 +03:00
{
2007-06-13 22:45:16 +04:00
if ( dentry )
sysfs_attach_dentry ( sd , dentry ) ;
2007-01-24 22:35:52 +03:00
2007-06-13 22:45:14 +04:00
if ( parent_sd ) {
sd - > s_parent = sysfs_get ( parent_sd ) ;
2007-06-13 22:45:18 +04:00
sysfs_link_sibling ( 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 :
* mutex_lock ( parent - > i_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 ;
for ( sd = parent_sd - > s_children ; sd ; sd = sd - > s_sibling )
if ( sysfs_type ( sd ) & & ! strcmp ( sd - > s_name , name ) )
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 :
* Kernel thread context ( may sleep )
*
* 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 ;
mutex_lock ( & parent_sd - > s_dentry - > d_inode - > i_mutex ) ;
sd = sysfs_find_dirent ( parent_sd , name ) ;
sysfs_get ( sd ) ;
mutex_unlock ( & parent_sd - > s_dentry - > d_inode - > i_mutex ) ;
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
{
2007-06-13 23:27:22 +04:00
struct dentry * parent = parent_sd - > s_dentry ;
2005-04-17 02:20:36 +04:00
int error ;
umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO ;
2007-06-13 22:45:14 +04:00
struct dentry * dentry ;
2007-06-13 22:45:17 +04:00
struct inode * inode ;
2007-06-13 22:45:14 +04:00
struct sysfs_dirent * sd ;
2005-04-17 02:20:36 +04:00
2007-06-13 22:45:14 +04:00
mutex_lock ( & parent - > d_inode - > i_mutex ) ;
2007-06-13 22:45:17 +04:00
/* allocate */
2007-06-13 22:45:14 +04:00
dentry = lookup_one_len ( name , parent , strlen ( name ) ) ;
if ( IS_ERR ( dentry ) ) {
error = PTR_ERR ( dentry ) ;
goto out_unlock ;
}
error = - EEXIST ;
2007-06-13 22:45:17 +04:00
if ( dentry - > d_inode )
2007-06-13 22:45:14 +04:00
goto out_dput ;
2007-06-13 22:45:14 +04:00
error = - ENOMEM ;
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 22:45:14 +04:00
goto out_drop ;
2007-06-13 22:45:15 +04:00
sd - > s_elem . dir . kobj = kobj ;
2007-06-13 22:45:14 +04:00
2007-06-13 22:45:17 +04:00
inode = sysfs_get_inode ( sd ) ;
2007-06-13 22:45:17 +04:00
if ( ! inode )
2007-06-13 22:45:14 +04:00
goto out_sput ;
2007-06-13 22:45:17 +04:00
if ( inode - > i_state & I_NEW ) {
inode - > i_op = & sysfs_dir_inode_operations ;
inode - > i_fop = & sysfs_dir_operations ;
/* directory inodes start off with i_nlink == 2 (for ".") */
inc_nlink ( inode ) ;
}
2007-06-13 22:45:17 +04:00
/* link in */
error = - EEXIST ;
2007-06-13 23:27:22 +04:00
if ( sysfs_find_dirent ( parent_sd , name ) )
2007-06-13 22:45:17 +04:00
goto out_iput ;
sysfs_instantiate ( dentry , inode ) ;
2007-06-13 22:45:14 +04:00
inc_nlink ( parent - > d_inode ) ;
2007-06-13 23:27:22 +04:00
sysfs_attach_dirent ( sd , parent_sd , dentry ) ;
2007-06-13 22:45:14 +04:00
2007-06-13 23:27:22 +04:00
* p_sd = sd ;
2007-06-13 22:45:14 +04:00
error = 0 ;
2007-06-13 22:45:17 +04:00
goto out_unlock ; /* pin directory dentry in core */
2007-06-13 22:45:14 +04:00
2007-06-13 22:45:17 +04:00
out_iput :
iput ( inode ) ;
2007-06-13 22:45:14 +04:00
out_sput :
sysfs_put ( sd ) ;
out_drop :
d_drop ( dentry ) ;
out_dput :
dput ( dentry ) ;
out_unlock :
mutex_unlock ( & parent - > d_inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
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-06-13 23:27:22 +04:00
* @ shadow_parent : parent object .
2005-04-17 02:20:36 +04:00
*/
2007-06-13 23:27:22 +04:00
int sysfs_create_dir ( struct kobject * kobj ,
struct sysfs_dirent * shadow_parent_sd )
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-06-13 23:27:22 +04:00
if ( shadow_parent_sd )
parent_sd = shadow_parent_sd ;
2007-01-24 22:35:52 +03:00
else 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 if ( sysfs_mount & & sysfs_mount - > mnt_sb )
2007-06-13 23:27:22 +04:00
parent_sd = sysfs_mount - > mnt_sb - > s_root - > d_fsdata ;
2005-04-17 02:20:36 +04:00
else
return - EFAULT ;
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 )
{
struct sysfs_dirent * parent_sd = dentry - > d_parent - > d_fsdata ;
struct sysfs_dirent * sd ;
2007-06-13 23:27:21 +04:00
struct bin_attribute * bin_attr ;
2007-06-13 22:45:17 +04:00
struct inode * inode ;
int found = 0 ;
2005-04-17 02:20:36 +04:00
2007-06-13 22:45:18 +04:00
for ( sd = parent_sd - > s_children ; sd ; sd = sd - > s_sibling ) {
2007-06-13 23:27:21 +04:00
if ( ( sysfs_type ( sd ) & SYSFS_NOT_PINNED ) & &
2007-06-13 22:45:17 +04:00
! strcmp ( sd - > s_name , dentry - > d_name . name ) ) {
found = 1 ;
2005-04-17 02:20:36 +04:00
break ;
}
}
2007-06-13 22:45:17 +04:00
/* no such entry */
if ( ! found )
return NULL ;
/* attach dentry and inode */
2007-06-13 22:45:17 +04:00
inode = sysfs_get_inode ( sd ) ;
2007-06-13 22:45:17 +04:00
if ( ! inode )
return ERR_PTR ( - ENOMEM ) ;
2007-06-13 22:45:17 +04:00
if ( inode - > i_state & I_NEW ) {
/* initialize inode according to type */
2007-06-13 23:27:21 +04:00
switch ( sysfs_type ( sd ) ) {
case SYSFS_KOBJ_ATTR :
2007-06-13 22:45:17 +04:00
inode - > i_size = PAGE_SIZE ;
inode - > i_fop = & sysfs_file_operations ;
2007-06-13 23:27:21 +04:00
break ;
case SYSFS_KOBJ_BIN_ATTR :
bin_attr = sd - > s_elem . bin_attr . bin_attr ;
2007-06-13 22:45:17 +04:00
inode - > i_size = bin_attr - > size ;
inode - > i_fop = & bin_fops ;
2007-06-13 23:27:21 +04:00
break ;
case SYSFS_KOBJ_LINK :
2007-06-13 22:45:17 +04:00
inode - > i_op = & sysfs_symlink_inode_operations ;
2007-06-13 23:27:21 +04:00
break ;
default :
BUG ( ) ;
}
2007-06-13 22:45:17 +04:00
}
2007-06-13 22:45:17 +04:00
sysfs_instantiate ( dentry , inode ) ;
sysfs_attach_dentry ( sd , dentry ) ;
return NULL ;
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:22 +04:00
struct dentry * parent = sd - > s_parent - > s_dentry ;
2005-04-17 02:20:36 +04:00
2006-01-10 02:59:24 +03:00
mutex_lock ( & parent - > d_inode - > i_mutex ) ;
2007-06-13 22:45:16 +04:00
2007-06-13 22:45:18 +04:00
sysfs_unlink_sibling ( sd ) ;
2007-06-13 23:27:22 +04:00
sd - > s_flags | = SYSFS_FLAG_REMOVED ;
2005-04-17 02:20:36 +04:00
2007-06-13 23:27:22 +04:00
pr_debug ( " o %s removing done \n " , sd - > s_name ) ;
2005-04-17 02:20:36 +04:00
2006-01-10 02:59:24 +03:00
mutex_unlock ( & parent - > d_inode - > i_mutex ) ;
2007-06-13 22:45:16 +04:00
2007-06-13 22:45:16 +04:00
sysfs_drop_dentry ( sd ) ;
2007-06-13 22:45:16 +04:00
sysfs_deactivate ( sd ) ;
sysfs_put ( sd ) ;
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 22:45:18 +04:00
struct sysfs_dirent * removed = NULL ;
struct sysfs_dirent * * pos ;
2007-06-13 23:27:22 +04:00
struct dentry * dir ;
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
dir = dir_sd - > s_dentry ;
pr_debug ( " sysfs %s: removing dir \n " , dir_sd - > s_name ) ;
mutex_lock ( & dir - > d_inode - > i_mutex ) ;
pos = & dir_sd - > s_children ;
2007-06-13 22:45:18 +04:00
while ( * pos ) {
struct sysfs_dirent * sd = * pos ;
2007-06-13 23:27:21 +04:00
if ( sysfs_type ( sd ) & & ( sysfs_type ( sd ) & SYSFS_NOT_PINNED ) ) {
2007-06-13 23:27:22 +04:00
sd - > s_flags | = SYSFS_FLAG_REMOVED ;
2007-06-13 22:45:18 +04:00
* pos = sd - > s_sibling ;
sd - > s_sibling = removed ;
removed = sd ;
} else
pos = & ( * pos ) - > s_sibling ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 23:27:22 +04:00
mutex_unlock ( & dir - > d_inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
2007-06-13 22:45:18 +04:00
while ( removed ) {
struct sysfs_dirent * sd = removed ;
removed = sd - > s_sibling ;
sd - > s_sibling = NULL ;
2007-06-13 22:45:16 +04:00
sysfs_drop_dentry ( sd ) ;
2007-06-13 22:45:16 +04:00
sysfs_deactivate ( sd ) ;
sysfs_put ( sd ) ;
}
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
spin_lock ( & kobj_sysfs_assoc_lock ) ;
2007-06-13 23:27:22 +04:00
kobj - > sd = NULL ;
2007-06-13 22:45:15 +04:00
spin_unlock ( & kobj_sysfs_assoc_lock ) ;
2007-06-13 23:27:22 +04:00
__sysfs_remove_dir ( sd ) ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 23:27:22 +04:00
int sysfs_rename_dir ( struct kobject * kobj , struct sysfs_dirent * new_parent_sd ,
2007-01-24 22:35:52 +03:00
const char * new_name )
2005-04-17 02:20:36 +04:00
{
2007-06-13 23:27:22 +04:00
struct sysfs_dirent * sd = kobj - > sd ;
struct dentry * new_parent = new_parent_sd - > s_dentry ;
2007-06-13 22:45:15 +04:00
struct dentry * new_dentry ;
char * dup_name ;
2007-06-13 22:45:14 +04:00
int error ;
2005-04-17 02:20:36 +04:00
2007-06-13 23:27:22 +04:00
if ( ! new_parent_sd )
2007-01-24 22:35:52 +03:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
down_write ( & sysfs_rename_sem ) ;
2007-01-24 22:35:52 +03:00
mutex_lock ( & new_parent - > d_inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
2007-01-24 22:35:52 +03:00
new_dentry = lookup_one_len ( new_name , new_parent , strlen ( new_name ) ) ;
2007-06-13 22:45:14 +04:00
if ( IS_ERR ( new_dentry ) ) {
error = PTR_ERR ( new_dentry ) ;
goto out_unlock ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 22:45:14 +04:00
/* By allowing two different directories with the same
* d_parent we allow this routine to move between different
* shadows of the same directory
*/
error = - EINVAL ;
2007-06-13 23:27:22 +04:00
if ( sd - > s_parent - > s_dentry - > d_inode ! = new_parent - > d_inode | |
2007-06-13 22:45:14 +04:00
new_dentry - > d_parent - > d_inode ! = new_parent - > d_inode | |
2007-06-13 23:27:22 +04:00
new_dentry = = sd - > s_dentry )
2007-06-13 22:45:14 +04:00
goto out_dput ;
error = - EEXIST ;
if ( new_dentry - > d_inode )
goto out_dput ;
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 )
goto out_drop ;
2007-06-13 22:45:14 +04:00
error = kobject_set_name ( kobj , " %s " , new_name ) ;
if ( error )
2007-06-13 22:45:15 +04:00
goto out_free ;
2007-06-13 22:45:14 +04:00
2007-06-13 22:45:15 +04:00
kfree ( sd - > s_name ) ;
sd - > s_name = new_name ;
/* move under the new parent */
2007-06-13 22:45:14 +04:00
d_add ( new_dentry , NULL ) ;
2007-06-13 23:27:22 +04:00
d_move ( sd - > s_dentry , new_dentry ) ;
2007-06-13 22:45:14 +04:00
2007-06-13 22:45:18 +04:00
sysfs_unlink_sibling ( sd ) ;
2007-06-13 23:27:22 +04:00
sysfs_get ( new_parent_sd ) ;
2007-06-13 22:45:17 +04:00
sysfs_put ( sd - > s_parent ) ;
2007-06-13 23:27:22 +04:00
sd - > s_parent = new_parent_sd ;
2007-06-13 22:45:18 +04:00
sysfs_link_sibling ( sd ) ;
2007-06-13 22:45:14 +04:00
error = 0 ;
goto out_unlock ;
2007-06-13 22:45:15 +04:00
out_free :
kfree ( dup_name ) ;
2007-06-13 22:45:14 +04:00
out_drop :
d_drop ( new_dentry ) ;
out_dput :
dput ( new_dentry ) ;
out_unlock :
2007-01-24 22:35:52 +03:00
mutex_unlock ( & new_parent - > d_inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
up_write ( & sysfs_rename_sem ) ;
return error ;
}
2006-11-20 19:07:51 +03:00
int sysfs_move_dir ( struct kobject * kobj , struct kobject * new_parent )
{
struct dentry * old_parent_dentry , * new_parent_dentry , * new_dentry ;
struct sysfs_dirent * new_parent_sd , * sd ;
int error ;
old_parent_dentry = kobj - > parent ?
2007-06-13 23:27:22 +04:00
kobj - > parent - > sd - > s_dentry : sysfs_mount - > mnt_sb - > s_root ;
2007-01-08 22:16:44 +03:00
new_parent_dentry = new_parent ?
2007-06-13 23:27:22 +04:00
new_parent - > sd - > s_dentry : sysfs_mount - > mnt_sb - > s_root ;
2006-11-20 19:07:51 +03:00
2007-03-06 12:42:03 +03:00
if ( old_parent_dentry - > d_inode = = new_parent_dentry - > d_inode )
return 0 ; /* nothing to move */
2006-11-20 19:07:51 +03:00
again :
mutex_lock ( & old_parent_dentry - > d_inode - > i_mutex ) ;
if ( ! mutex_trylock ( & new_parent_dentry - > d_inode - > i_mutex ) ) {
mutex_unlock ( & old_parent_dentry - > d_inode - > i_mutex ) ;
goto again ;
}
new_parent_sd = new_parent_dentry - > d_fsdata ;
2007-06-13 23:27:22 +04:00
sd = kobj - > sd ;
2006-11-20 19:07:51 +03:00
new_dentry = lookup_one_len ( kobj - > name , new_parent_dentry ,
strlen ( kobj - > name ) ) ;
if ( IS_ERR ( new_dentry ) ) {
error = PTR_ERR ( new_dentry ) ;
goto out ;
} else
error = 0 ;
d_add ( new_dentry , NULL ) ;
2007-06-13 23:27:22 +04:00
d_move ( sd - > s_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
out :
mutex_unlock ( & new_parent_dentry - > d_inode - > i_mutex ) ;
mutex_unlock ( & old_parent_dentry - > d_inode - > i_mutex ) ;
return error ;
}
2005-04-17 02:20:36 +04:00
static int sysfs_dir_open ( struct inode * inode , struct file * file )
{
2006-12-08 13:36:36 +03:00
struct dentry * dentry = file - > f_path . dentry ;
2005-04-17 02:20:36 +04:00
struct sysfs_dirent * parent_sd = dentry - > d_fsdata ;
2007-06-13 22:45:14 +04:00
struct sysfs_dirent * sd ;
2005-04-17 02:20:36 +04:00
2006-01-10 02:59:24 +03:00
mutex_lock ( & dentry - > d_inode - > i_mutex ) ;
2007-06-13 22:45:15 +04:00
sd = sysfs_new_dirent ( " _DIR_ " , 0 , 0 ) ;
2007-06-13 22:45:14 +04:00
if ( sd )
sysfs_attach_dirent ( sd , parent_sd , NULL ) ;
2006-01-10 02:59:24 +03:00
mutex_unlock ( & dentry - > d_inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
2007-06-13 22:45:14 +04:00
file - > private_data = sd ;
return sd ? 0 : - ENOMEM ;
2005-04-17 02:20:36 +04:00
}
static int sysfs_dir_close ( struct inode * inode , struct file * file )
{
2006-12-08 13:36:36 +03:00
struct dentry * dentry = file - > f_path . dentry ;
2005-04-17 02:20:36 +04:00
struct sysfs_dirent * cursor = file - > private_data ;
2006-01-10 02:59:24 +03:00
mutex_lock ( & dentry - > d_inode - > i_mutex ) ;
2007-06-13 22:45:18 +04:00
sysfs_unlink_sibling ( cursor ) ;
2006-01-10 02:59:24 +03:00
mutex_unlock ( & dentry - > d_inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
release_sysfs_dirent ( cursor ) ;
return 0 ;
}
/* 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 ;
struct sysfs_dirent * cursor = filp - > private_data ;
2007-06-13 22:45:18 +04:00
struct sysfs_dirent * * pos ;
2005-04-17 02:20:36 +04:00
ino_t ino ;
int i = filp - > f_pos ;
switch ( i ) {
case 0 :
2007-06-11 09:02:45 +04:00
ino = parent_sd - > s_ino ;
2005-04-17 02:20:36 +04:00
if ( filldir ( dirent , " . " , 1 , i , ino , DT_DIR ) < 0 )
break ;
filp - > f_pos + + ;
i + + ;
/* fallthrough */
case 1 :
2007-06-13 22:45:14 +04:00
if ( parent_sd - > s_parent )
ino = parent_sd - > s_parent - > s_ino ;
else
ino = parent_sd - > s_ino ;
2005-04-17 02:20:36 +04:00
if ( filldir ( dirent , " .. " , 2 , i , ino , DT_DIR ) < 0 )
break ;
filp - > f_pos + + ;
i + + ;
/* fallthrough */
default :
2007-06-13 22:45:18 +04:00
pos = & parent_sd - > s_children ;
while ( * pos ! = cursor )
pos = & ( * pos ) - > s_sibling ;
/* unlink cursor */
* pos = cursor - > s_sibling ;
2006-06-26 11:24:40 +04:00
if ( filp - > f_pos = = 2 )
2007-06-13 22:45:18 +04:00
pos = & parent_sd - > s_children ;
2006-06-26 11:24:40 +04:00
2007-06-13 22:45:18 +04:00
for ( ; * pos ; pos = & ( * pos ) - > s_sibling ) {
struct sysfs_dirent * next = * pos ;
2005-04-17 02:20:36 +04:00
const char * name ;
int len ;
2007-06-13 23:27:21 +04:00
if ( ! sysfs_type ( next ) )
2005-04-17 02:20:36 +04:00
continue ;
2007-06-13 22:45:15 +04:00
name = next - > s_name ;
2005-04-17 02:20:36 +04:00
len = strlen ( name ) ;
2007-06-11 09:02:45 +04:00
ino = next - > s_ino ;
2005-04-17 02:20:36 +04:00
if ( filldir ( dirent , name , len , filp - > f_pos , ino ,
dt_type ( next ) ) < 0 )
2007-06-13 22:45:18 +04:00
break ;
2005-04-17 02:20:36 +04:00
filp - > f_pos + + ;
}
2007-06-13 22:45:18 +04:00
/* put cursor back in */
cursor - > s_sibling = * pos ;
* pos = cursor ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
static loff_t sysfs_dir_lseek ( struct file * file , loff_t offset , int origin )
{
2006-12-08 13:36:36 +03:00
struct dentry * dentry = file - > f_path . dentry ;
2005-04-17 02:20:36 +04:00
2006-01-10 02:59:24 +03:00
mutex_lock ( & dentry - > d_inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
switch ( origin ) {
case 1 :
offset + = file - > f_pos ;
case 0 :
if ( offset > = 0 )
break ;
default :
2006-12-08 13:36:36 +03:00
mutex_unlock ( & file - > f_path . dentry - > d_inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
if ( offset ! = file - > f_pos ) {
file - > f_pos = offset ;
if ( file - > f_pos > = 2 ) {
struct sysfs_dirent * sd = dentry - > d_fsdata ;
struct sysfs_dirent * cursor = file - > private_data ;
2007-06-13 22:45:18 +04:00
struct sysfs_dirent * * pos ;
2005-04-17 02:20:36 +04:00
loff_t n = file - > f_pos - 2 ;
2007-06-13 22:45:18 +04:00
sysfs_unlink_sibling ( cursor ) ;
pos = & sd - > s_children ;
while ( n & & * pos ) {
struct sysfs_dirent * next = * pos ;
2007-06-13 23:27:21 +04:00
if ( sysfs_type ( next ) )
2005-04-17 02:20:36 +04:00
n - - ;
2007-06-13 22:45:18 +04:00
pos = & ( * pos ) - > s_sibling ;
2005-04-17 02:20:36 +04:00
}
2007-06-13 22:45:18 +04:00
cursor - > s_sibling = * pos ;
* pos = cursor ;
2005-04-17 02:20:36 +04:00
}
}
2006-01-10 02:59:24 +03:00
mutex_unlock ( & dentry - > d_inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
return offset ;
}
2007-01-24 22:35:52 +03:00
/**
* sysfs_make_shadowed_dir - Setup so a directory can be shadowed
* @ kobj : object we ' re creating shadow of .
*/
int sysfs_make_shadowed_dir ( struct kobject * kobj ,
void * ( * follow_link ) ( struct dentry * , struct nameidata * ) )
{
struct inode * inode ;
struct inode_operations * i_op ;
2007-06-13 23:27:22 +04:00
inode = kobj - > sd - > s_dentry - > d_inode ;
2007-01-24 22:35:52 +03:00
if ( inode - > i_op ! = & sysfs_dir_inode_operations )
return - EINVAL ;
i_op = kmalloc ( sizeof ( * i_op ) , GFP_KERNEL ) ;
if ( ! i_op )
return - ENOMEM ;
memcpy ( i_op , & sysfs_dir_inode_operations , sizeof ( * i_op ) ) ;
i_op - > follow_link = follow_link ;
/* Locking of inode->i_op?
* Since setting i_op is a single word write and they
* are atomic we should be ok here .
*/
inode - > i_op = i_op ;
return 0 ;
}
/**
* sysfs_create_shadow_dir - create a shadow directory for an object .
* @ kobj : object we ' re creating directory for .
*
* sysfs_make_shadowed_dir must already have been called on this
* directory .
*/
2007-06-13 23:27:22 +04:00
struct sysfs_dirent * sysfs_create_shadow_dir ( struct kobject * kobj )
2007-01-24 22:35:52 +03:00
{
2007-06-13 23:27:22 +04:00
struct dentry * dir = kobj - > sd - > s_dentry ;
2007-06-13 22:45:14 +04:00
struct inode * inode = dir - > d_inode ;
struct dentry * parent = dir - > d_parent ;
struct sysfs_dirent * parent_sd = parent - > d_fsdata ;
struct dentry * shadow ;
2007-01-24 22:35:52 +03:00
struct sysfs_dirent * sd ;
2007-06-13 23:27:22 +04:00
sd = ERR_PTR ( - EINVAL ) ;
2007-01-24 22:35:52 +03:00
if ( ! sysfs_is_shadowed_inode ( inode ) )
goto out ;
shadow = d_alloc ( parent , & dir - > d_name ) ;
if ( ! shadow )
goto nomem ;
2007-06-13 22:45:15 +04:00
sd = sysfs_new_dirent ( " _SHADOW_ " , inode - > i_mode , SYSFS_DIR ) ;
2007-01-24 22:35:52 +03:00
if ( ! sd )
goto nomem ;
2007-06-13 22:45:15 +04:00
sd - > s_elem . dir . kobj = kobj ;
2007-06-13 22:45:14 +04:00
/* point to parent_sd but don't attach to it */
sd - > s_parent = sysfs_get ( parent_sd ) ;
2007-06-13 22:45:14 +04:00
sysfs_attach_dirent ( sd , NULL , shadow ) ;
2007-01-24 22:35:52 +03:00
d_instantiate ( shadow , igrab ( inode ) ) ;
inc_nlink ( inode ) ;
inc_nlink ( parent - > d_inode ) ;
dget ( shadow ) ; /* Extra count - pin the dentry in core */
out :
2007-06-13 23:27:22 +04:00
return sd ;
2007-01-24 22:35:52 +03:00
nomem :
dput ( shadow ) ;
2007-06-13 23:27:22 +04:00
sd = ERR_PTR ( - ENOMEM ) ;
2007-01-24 22:35:52 +03:00
goto out ;
}
/**
* sysfs_remove_shadow_dir - remove an object ' s directory .
2007-06-13 23:27:22 +04:00
* @ shadow_sd : sysfs_dirent of shadow directory
2007-01-24 22:35:52 +03:00
*
* 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 .
*/
2007-06-13 23:27:22 +04:00
void sysfs_remove_shadow_dir ( struct sysfs_dirent * shadow_sd )
2007-01-24 22:35:52 +03:00
{
2007-06-13 23:27:22 +04:00
__sysfs_remove_dir ( shadow_sd ) ;
2007-01-24 22:35:52 +03:00
}
2006-03-28 13:56:42 +04:00
const struct file_operations sysfs_dir_operations = {
2005-04-17 02:20:36 +04:00
. open = sysfs_dir_open ,
. release = sysfs_dir_close ,
. llseek = sysfs_dir_lseek ,
. read = generic_read_dir ,
. readdir = sysfs_readdir ,
} ;