2005-04-17 02:20:36 +04:00
/*
2007-09-20 12:31:38 +04:00
* fs / sysfs / file . c - sysfs regular ( text ) file 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
*/
# include <linux/module.h>
# include <linux/kobject.h>
2005-06-23 11:09:12 +04:00
# include <linux/namei.h>
2006-03-20 09:53:53 +03:00
# include <linux/poll.h>
2006-12-20 12:52:44 +03:00
# include <linux/list.h>
2007-07-26 15:03:54 +04:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include "sysfs.h"
2007-04-14 00:15:19 +04:00
# define to_sattr(a) container_of(a,struct subsys_attribute, attr)
2005-04-17 02:20:36 +04:00
2005-06-24 09:05:21 +04:00
/*
2005-04-17 02:20:36 +04:00
* Subsystem file operations .
* These operations allow subsystems to have files that can be
* read / written .
*/
static ssize_t
subsys_attr_show ( struct kobject * kobj , struct attribute * attr , char * page )
{
2007-04-14 00:15:19 +04:00
struct kset * kset = to_kset ( kobj ) ;
2005-04-17 02:20:36 +04:00
struct subsys_attribute * sattr = to_sattr ( attr ) ;
2005-04-29 10:22:00 +04:00
ssize_t ret = - EIO ;
2005-04-17 02:20:36 +04:00
if ( sattr - > show )
2007-04-14 00:15:19 +04:00
ret = sattr - > show ( kset , page ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
static ssize_t
subsys_attr_store ( struct kobject * kobj , struct attribute * attr ,
const char * page , size_t count )
{
2007-04-14 00:15:19 +04:00
struct kset * kset = to_kset ( kobj ) ;
2005-04-17 02:20:36 +04:00
struct subsys_attribute * sattr = to_sattr ( attr ) ;
2005-04-29 10:22:00 +04:00
ssize_t ret = - EIO ;
2005-04-17 02:20:36 +04:00
if ( sattr - > store )
2007-04-14 00:15:19 +04:00
ret = sattr - > store ( kset , page , count ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
static struct sysfs_ops subsys_sysfs_ops = {
. show = subsys_attr_show ,
. store = subsys_attr_store ,
} ;
2007-09-20 11:05:12 +04:00
/*
* There ' s one sysfs_buffer for each open file and one
* sysfs_open_dirent for each sysfs_dirent with one or more open
* files .
*
* filp - > private_data points to sysfs_buffer and
* sysfs_dirent - > s_attr . open points to sysfs_open_dirent . s_attr . open
* is protected by sysfs_open_dirent_lock .
*/
static spinlock_t sysfs_open_dirent_lock = SPIN_LOCK_UNLOCKED ;
struct sysfs_open_dirent {
atomic_t refcnt ;
2007-09-20 11:05:12 +04:00
atomic_t event ;
wait_queue_head_t poll ;
2007-09-20 11:05:12 +04:00
struct list_head buffers ; /* goes through sysfs_buffer.list */
} ;
2007-06-13 22:45:16 +04:00
struct sysfs_buffer {
size_t count ;
loff_t pos ;
char * page ;
struct sysfs_ops * ops ;
2007-07-26 15:03:54 +04:00
struct mutex mutex ;
2007-06-13 22:45:16 +04:00
int needs_read_fill ;
int event ;
2007-09-20 11:05:12 +04:00
struct list_head list ;
2007-06-13 22:45:16 +04:00
} ;
2005-04-17 02:20:36 +04:00
/**
* fill_read_buffer - allocate and fill buffer from object .
* @ dentry : dentry pointer .
* @ buffer : data buffer for file .
*
* Allocate @ buffer - > page , if it hasn ' t been already , then call the
* kobject ' s show ( ) method to fill the buffer with this attribute ' s
* data .
2007-01-02 10:48:08 +03:00
* This is called only once , on the file ' s first read unless an error
* is returned .
2005-04-17 02:20:36 +04:00
*/
static int fill_read_buffer ( struct dentry * dentry , struct sysfs_buffer * buffer )
{
2007-06-13 22:45:16 +04:00
struct sysfs_dirent * attr_sd = dentry - > d_fsdata ;
2007-09-20 11:05:11 +04:00
struct kobject * kobj = attr_sd - > s_parent - > s_dir . kobj ;
2005-04-17 02:20:36 +04:00
struct sysfs_ops * ops = buffer - > ops ;
int ret = 0 ;
ssize_t count ;
if ( ! buffer - > page )
buffer - > page = ( char * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! buffer - > page )
return - ENOMEM ;
2007-06-13 22:45:16 +04:00
/* need attr_sd for attr and ops, its parent for kobj */
if ( ! sysfs_get_active_two ( attr_sd ) )
return - ENODEV ;
2007-09-20 11:05:12 +04:00
buffer - > event = atomic_read ( & attr_sd - > s_attr . open - > event ) ;
2007-09-20 11:05:11 +04:00
count = ops - > show ( kobj , attr_sd - > s_attr . attr , buffer - > page ) ;
2007-06-13 22:45:16 +04:00
sysfs_put_active_two ( attr_sd ) ;
2007-11-22 01:55:19 +03:00
/*
* The code works fine with PAGE_SIZE return but it ' s likely to
* indicate truncated result or overflow in normal use cases .
*/
BUG_ON ( count > = ( ssize_t ) PAGE_SIZE ) ;
2007-01-02 10:48:08 +03:00
if ( count > = 0 ) {
buffer - > needs_read_fill = 0 ;
2005-04-17 02:20:36 +04:00
buffer - > count = count ;
2007-01-02 10:48:08 +03:00
} else {
2005-04-17 02:20:36 +04:00
ret = count ;
2007-01-02 10:48:08 +03:00
}
2005-04-17 02:20:36 +04:00
return ret ;
}
/**
* sysfs_read_file - read an attribute .
* @ file : file pointer .
* @ buf : buffer to fill .
* @ count : number of bytes to read .
* @ ppos : starting offset in file .
*
* Userspace wants to read an attribute file . The attribute descriptor
* is in the file ' s - > d_fsdata . The target object is in the directory ' s
* - > d_fsdata .
*
* We call fill_read_buffer ( ) to allocate and fill the buffer from the
* object ' s show ( ) method exactly once ( if the read is happening from
* the beginning of the file ) . That should fill the entire buffer with
* all the data the object has to offer for that attribute .
* We then call flush_read_buffer ( ) to copy the buffer to userspace
* in the increments specified .
*/
static ssize_t
sysfs_read_file ( struct file * file , char __user * buf , size_t count , loff_t * ppos )
{
struct sysfs_buffer * buffer = file - > private_data ;
ssize_t retval = 0 ;
2007-07-26 15:03:54 +04:00
mutex_lock ( & buffer - > mutex ) ;
2005-04-17 02:20:36 +04:00
if ( buffer - > needs_read_fill ) {
2007-06-13 22:45:16 +04:00
retval = fill_read_buffer ( file - > f_path . dentry , buffer ) ;
2007-03-15 22:51:28 +03:00
if ( retval )
2005-04-17 02:20:36 +04:00
goto out ;
}
2006-10-03 12:16:06 +04:00
pr_debug ( " %s: count = %zd, ppos = %lld, buf = %s \n " ,
__FUNCTION__ , count , * ppos , buffer - > page ) ;
2007-05-09 13:33:32 +04:00
retval = simple_read_from_buffer ( buf , count , ppos , buffer - > page ,
buffer - > count ) ;
2005-04-17 02:20:36 +04:00
out :
2007-07-26 15:03:54 +04:00
mutex_unlock ( & buffer - > mutex ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
/**
* fill_write_buffer - copy buffer from userspace .
* @ buffer : data buffer for file .
2005-05-01 19:59:26 +04:00
* @ buf : data from user .
2005-04-17 02:20:36 +04:00
* @ count : number of bytes in @ userbuf .
*
* Allocate @ buffer - > page if it hasn ' t been already , then
* copy the user - supplied buffer into it .
*/
static int
fill_write_buffer ( struct sysfs_buffer * buffer , const char __user * buf , size_t count )
{
int error ;
if ( ! buffer - > page )
buffer - > page = ( char * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! buffer - > page )
return - ENOMEM ;
if ( count > = PAGE_SIZE )
2006-04-01 03:37:06 +04:00
count = PAGE_SIZE - 1 ;
2005-04-17 02:20:36 +04:00
error = copy_from_user ( buffer - > page , buf , count ) ;
buffer - > needs_read_fill = 1 ;
2006-10-22 21:17:47 +04:00
/* if buf is assumed to contain a string, terminate it by \0,
so e . g . sscanf ( ) can scan the string easily */
buffer - > page [ count ] = 0 ;
2005-04-17 02:20:36 +04:00
return error ? - EFAULT : count ;
}
/**
* flush_write_buffer - push buffer to kobject .
2005-06-24 09:05:21 +04:00
* @ dentry : dentry to the attribute
2005-04-17 02:20:36 +04:00
* @ buffer : data buffer for file .
2005-06-24 09:05:21 +04:00
* @ count : number of bytes
2005-04-17 02:20:36 +04:00
*
* Get the correct pointers for the kobject and the attribute we ' re
* dealing with , then call the store ( ) method for the attribute ,
* passing the buffer that we acquired in fill_write_buffer ( ) .
*/
2007-06-13 22:45:16 +04:00
static int
2005-04-17 02:20:36 +04:00
flush_write_buffer ( struct dentry * dentry , struct sysfs_buffer * buffer , size_t count )
{
2007-06-13 22:45:15 +04:00
struct sysfs_dirent * attr_sd = dentry - > d_fsdata ;
2007-09-20 11:05:11 +04:00
struct kobject * kobj = attr_sd - > s_parent - > s_dir . kobj ;
2005-04-17 02:20:36 +04:00
struct sysfs_ops * ops = buffer - > ops ;
2007-06-13 22:45:16 +04:00
int rc ;
/* need attr_sd for attr and ops, its parent for kobj */
if ( ! sysfs_get_active_two ( attr_sd ) )
return - ENODEV ;
2007-09-20 11:05:11 +04:00
rc = ops - > store ( kobj , attr_sd - > s_attr . attr , buffer - > page , count ) ;
2007-06-13 22:45:16 +04:00
sysfs_put_active_two ( attr_sd ) ;
2005-04-17 02:20:36 +04:00
2007-06-13 22:45:16 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
}
/**
* sysfs_write_file - write an attribute .
* @ file : file pointer
* @ buf : data to write
* @ count : number of bytes
* @ ppos : starting offset
*
* Similar to sysfs_read_file ( ) , though working in the opposite direction .
* We allocate and fill the data from the user in fill_write_buffer ( ) ,
* then push it to the kobject in flush_write_buffer ( ) .
* There is no easy way for us to know if userspace is only doing a partial
* write , so we don ' t support them . We expect the entire buffer to come
* on the first write .
* Hint : if you ' re writing a value , first read the file , modify only the
* the value you ' re changing , then write entire buffer back .
*/
static ssize_t
sysfs_write_file ( struct file * file , const char __user * buf , size_t count , loff_t * ppos )
{
struct sysfs_buffer * buffer = file - > private_data ;
ssize_t len ;
2007-07-26 15:03:54 +04:00
mutex_lock ( & buffer - > mutex ) ;
2005-04-17 02:20:36 +04:00
len = fill_write_buffer ( buffer , buf , count ) ;
if ( len > 0 )
2006-12-08 13:36:36 +03:00
len = flush_write_buffer ( file - > f_path . dentry , buffer , len ) ;
2005-04-17 02:20:36 +04:00
if ( len > 0 )
* ppos + = len ;
2007-07-26 15:03:54 +04:00
mutex_unlock ( & buffer - > mutex ) ;
2005-04-17 02:20:36 +04:00
return len ;
}
2007-09-20 11:05:12 +04:00
/**
* sysfs_get_open_dirent - get or create sysfs_open_dirent
* @ sd : target sysfs_dirent
* @ buffer : sysfs_buffer for this instance of open
*
* If @ sd - > s_attr . open exists , increment its reference count ;
* otherwise , create one . @ buffer is chained to the buffers
* list .
*
* LOCKING :
* Kernel thread context ( may sleep ) .
*
* RETURNS :
* 0 on success , - errno on failure .
*/
static int sysfs_get_open_dirent ( struct sysfs_dirent * sd ,
struct sysfs_buffer * buffer )
{
struct sysfs_open_dirent * od , * new_od = NULL ;
retry :
spin_lock ( & sysfs_open_dirent_lock ) ;
if ( ! sd - > s_attr . open & & new_od ) {
sd - > s_attr . open = new_od ;
new_od = NULL ;
}
od = sd - > s_attr . open ;
if ( od ) {
atomic_inc ( & od - > refcnt ) ;
list_add_tail ( & buffer - > list , & od - > buffers ) ;
}
spin_unlock ( & sysfs_open_dirent_lock ) ;
if ( od ) {
kfree ( new_od ) ;
return 0 ;
}
/* not there, initialize a new one and retry */
new_od = kmalloc ( sizeof ( * new_od ) , GFP_KERNEL ) ;
if ( ! new_od )
return - ENOMEM ;
atomic_set ( & new_od - > refcnt , 0 ) ;
2007-09-20 11:05:12 +04:00
atomic_set ( & new_od - > event , 1 ) ;
init_waitqueue_head ( & new_od - > poll ) ;
2007-09-20 11:05:12 +04:00
INIT_LIST_HEAD ( & new_od - > buffers ) ;
goto retry ;
}
/**
* sysfs_put_open_dirent - put sysfs_open_dirent
* @ sd : target sysfs_dirent
* @ buffer : associated sysfs_buffer
*
* Put @ sd - > s_attr . open and unlink @ buffer from the buffers list .
* If reference count reaches zero , disassociate and free it .
*
* LOCKING :
* None .
*/
static void sysfs_put_open_dirent ( struct sysfs_dirent * sd ,
struct sysfs_buffer * buffer )
{
struct sysfs_open_dirent * od = sd - > s_attr . open ;
spin_lock ( & sysfs_open_dirent_lock ) ;
list_del ( & buffer - > list ) ;
if ( atomic_dec_and_test ( & od - > refcnt ) )
sd - > s_attr . open = NULL ;
else
od = NULL ;
spin_unlock ( & sysfs_open_dirent_lock ) ;
kfree ( od ) ;
}
2006-12-20 12:52:44 +03:00
static int sysfs_open_file ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2007-06-13 22:45:15 +04:00
struct sysfs_dirent * attr_sd = file - > f_path . dentry - > d_fsdata ;
2007-09-20 11:05:11 +04:00
struct kobject * kobj = attr_sd - > s_parent - > s_dir . kobj ;
2005-04-17 02:20:36 +04:00
struct sysfs_buffer * buffer ;
struct sysfs_ops * ops = NULL ;
2007-06-13 22:45:16 +04:00
int error ;
2005-04-17 02:20:36 +04:00
2007-06-13 22:45:16 +04:00
/* need attr_sd for attr and ops, its parent for kobj */
if ( ! sysfs_get_active_two ( attr_sd ) )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
/* if the kobject has no ktype, then we assume that it is a subsystem
* itself , and use ops for it .
*/
if ( kobj - > kset & & kobj - > kset - > ktype )
ops = kobj - > kset - > ktype - > sysfs_ops ;
else if ( kobj - > ktype )
ops = kobj - > ktype - > sysfs_ops ;
else
ops = & subsys_sysfs_ops ;
2007-06-13 22:45:16 +04:00
error = - EACCES ;
2005-04-17 02:20:36 +04:00
/* No sysfs operations, either from having no subsystem,
* or the subsystem have no operations .
*/
if ( ! ops )
2007-06-13 22:45:17 +04:00
goto err_out ;
2005-04-17 02:20:36 +04:00
/* File needs write support.
* The inode ' s perms must say it ' s ok ,
* and we must have a store method .
*/
if ( file - > f_mode & FMODE_WRITE ) {
if ( ! ( inode - > i_mode & S_IWUGO ) | | ! ops - > store )
2007-06-13 22:45:17 +04:00
goto err_out ;
2005-04-17 02:20:36 +04:00
}
/* File needs read support.
* The inode ' s perms must say it ' s ok , and we there
* must be a show method for it .
*/
if ( file - > f_mode & FMODE_READ ) {
if ( ! ( inode - > i_mode & S_IRUGO ) | | ! ops - > show )
2007-06-13 22:45:17 +04:00
goto err_out ;
2005-04-17 02:20:36 +04:00
}
/* No error? Great, allocate a buffer for the file, and store it
* it in file - > private_data for easy access .
*/
2007-06-13 22:45:16 +04:00
error = - ENOMEM ;
2006-02-22 13:18:15 +03:00
buffer = kzalloc ( sizeof ( struct sysfs_buffer ) , GFP_KERNEL ) ;
2007-06-13 22:45:16 +04:00
if ( ! buffer )
2007-06-13 22:45:17 +04:00
goto err_out ;
2005-04-17 02:20:36 +04:00
2007-07-26 15:03:54 +04:00
mutex_init ( & buffer - > mutex ) ;
2007-06-13 22:45:16 +04:00
buffer - > needs_read_fill = 1 ;
buffer - > ops = ops ;
file - > private_data = buffer ;
2007-09-20 11:05:12 +04:00
/* make sure we have open dirent struct */
error = sysfs_get_open_dirent ( attr_sd , buffer ) ;
if ( error )
goto err_free ;
2007-09-20 11:05:10 +04:00
/* open succeeded, put active references */
2007-06-13 22:45:16 +04:00
sysfs_put_active_two ( attr_sd ) ;
return 0 ;
2007-09-20 11:05:12 +04:00
err_free :
kfree ( buffer ) ;
2007-06-13 22:45:17 +04:00
err_out :
2007-06-13 22:45:16 +04:00
sysfs_put_active_two ( attr_sd ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2007-09-20 11:05:12 +04:00
static int sysfs_release ( struct inode * inode , struct file * filp )
2005-04-17 02:20:36 +04:00
{
2007-09-20 11:05:12 +04:00
struct sysfs_dirent * sd = filp - > f_path . dentry - > d_fsdata ;
2007-06-13 22:45:16 +04:00
struct sysfs_buffer * buffer = filp - > private_data ;
2005-04-17 02:20:36 +04:00
2007-09-20 11:05:12 +04:00
sysfs_put_open_dirent ( sd , buffer ) ;
2007-09-20 11:05:10 +04:00
if ( buffer - > page )
free_page ( ( unsigned long ) buffer - > page ) ;
kfree ( buffer ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-03-20 09:53:53 +03:00
/* Sysfs attribute files are pollable. The idea is that you read
* the content and then you use ' poll ' or ' select ' to wait for
* the content to change . When the content changes ( assuming the
* manager for the kobject supports notification ) , poll will
* return POLLERR | POLLPRI , and select will return the fd whether
* it is waiting for read , write , or exceptions .
* Once poll / select indicates that the value has changed , you
* need to close and re - open the file , as simply seeking and reading
* again will not get new data , or reset the state of ' poll ' .
* Reminder : this only works for attributes which actively support
* it , and it is not possible to test an attribute from userspace
2007-08-11 00:51:07 +04:00
* to see if it supports poll ( Neither ' poll ' nor ' select ' return
2006-03-20 09:53:53 +03:00
* an appropriate error code ) . When in doubt , set a suitable timeout value .
*/
static unsigned int sysfs_poll ( struct file * filp , poll_table * wait )
{
struct sysfs_buffer * buffer = filp - > private_data ;
2007-06-13 22:45:16 +04:00
struct sysfs_dirent * attr_sd = filp - > f_path . dentry - > d_fsdata ;
2007-09-20 11:05:12 +04:00
struct sysfs_open_dirent * od = attr_sd - > s_attr . open ;
2007-06-13 22:45:16 +04:00
/* need parent for the kobj, grab both */
if ( ! sysfs_get_active_two ( attr_sd ) )
goto trigger ;
2006-03-20 09:53:53 +03:00
2007-09-20 11:05:12 +04:00
poll_wait ( filp , & od - > poll , wait ) ;
2006-03-20 09:53:53 +03:00
2007-06-13 22:45:16 +04:00
sysfs_put_active_two ( attr_sd ) ;
2007-09-20 11:05:12 +04:00
if ( buffer - > event ! = atomic_read ( & od - > event ) )
2007-06-13 22:45:16 +04:00
goto trigger ;
2006-03-20 09:53:53 +03:00
2007-06-13 22:45:16 +04:00
return 0 ;
trigger :
buffer - > needs_read_fill = 1 ;
return POLLERR | POLLPRI ;
2006-03-20 09:53:53 +03:00
}
2007-06-13 23:27:25 +04:00
void sysfs_notify ( struct kobject * k , char * dir , char * attr )
2006-03-20 09:53:53 +03:00
{
2007-06-13 23:27:25 +04:00
struct sysfs_dirent * sd = k - > sd ;
2006-03-20 09:53:53 +03:00
2007-06-13 23:27:25 +04:00
mutex_lock ( & sysfs_mutex ) ;
if ( sd & & dir )
sd = sysfs_find_dirent ( sd , dir ) ;
if ( sd & & attr )
sd = sysfs_find_dirent ( sd , attr ) ;
if ( sd ) {
2007-09-20 11:05:12 +04:00
struct sysfs_open_dirent * od ;
spin_lock ( & sysfs_open_dirent_lock ) ;
od = sd - > s_attr . open ;
if ( od ) {
atomic_inc ( & od - > event ) ;
wake_up_interruptible ( & od - > poll ) ;
}
spin_unlock ( & sysfs_open_dirent_lock ) ;
2006-03-20 09:53:53 +03:00
}
2007-06-13 23:27:25 +04:00
mutex_unlock ( & sysfs_mutex ) ;
2006-03-20 09:53:53 +03:00
}
EXPORT_SYMBOL_GPL ( sysfs_notify ) ;
2006-03-28 13:56:42 +04:00
const struct file_operations sysfs_file_operations = {
2005-04-17 02:20:36 +04:00
. read = sysfs_read_file ,
. write = sysfs_write_file ,
. llseek = generic_file_llseek ,
. open = sysfs_open_file ,
. release = sysfs_release ,
2006-03-20 09:53:53 +03:00
. poll = sysfs_poll ,
2005-04-17 02:20:36 +04:00
} ;
2007-06-13 23:27:22 +04:00
int sysfs_add_file ( struct sysfs_dirent * dir_sd , const struct attribute * attr ,
int type )
2005-04-17 02:20:36 +04:00
{
umode_t mode = ( attr - > mode & S_IALLUGO ) | S_IFREG ;
2007-06-13 23:27:24 +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 23:27:23 +04:00
sd = sysfs_new_dirent ( attr - > name , mode , type ) ;
if ( ! sd )
return - ENOMEM ;
2007-09-20 11:05:11 +04:00
sd - > s_attr . attr = ( void * ) attr ;
2005-04-17 02:20:36 +04:00
2007-06-13 23:27:24 +04:00
sysfs_addrm_start ( & acxt , dir_sd ) ;
2007-08-02 16:38:03 +04:00
rc = sysfs_add_one ( & acxt , sd ) ;
sysfs_addrm_finish ( & acxt ) ;
2007-06-13 22:45:14 +04:00
2007-08-02 16:38:03 +04:00
if ( rc )
2007-07-18 11:38:11 +04:00
sysfs_put ( sd ) ;
2007-06-13 23:27:23 +04:00
2007-08-02 16:38:03 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
}
/**
* sysfs_create_file - create an attribute file for an object .
* @ kobj : object we ' re creating for .
2007-10-20 05:14:32 +04:00
* @ attr : attribute descriptor .
2005-04-17 02:20:36 +04:00
*/
int sysfs_create_file ( struct kobject * kobj , const struct attribute * attr )
{
2007-06-13 23:27:22 +04:00
BUG_ON ( ! kobj | | ! kobj - > sd | | ! attr ) ;
2005-04-17 02:20:36 +04:00
2007-06-13 23:27:22 +04:00
return sysfs_add_file ( kobj - > sd , attr , SYSFS_KOBJ_ATTR ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-20 23:02:44 +03:00
/**
* sysfs_add_file_to_group - add an attribute file to a pre - existing group .
* @ kobj : object we ' re acting for .
* @ attr : attribute descriptor .
* @ group : group name .
*/
int sysfs_add_file_to_group ( struct kobject * kobj ,
const struct attribute * attr , const char * group )
{
2007-06-13 23:27:22 +04:00
struct sysfs_dirent * dir_sd ;
2007-02-20 23:02:44 +03:00
int error ;
2007-06-13 23:27:22 +04:00
dir_sd = sysfs_get_dirent ( kobj - > sd , group ) ;
if ( ! dir_sd )
return - ENOENT ;
error = sysfs_add_file ( dir_sd , attr , SYSFS_KOBJ_ATTR ) ;
sysfs_put ( dir_sd ) ;
2007-02-20 23:02:44 +03:00
return error ;
}
EXPORT_SYMBOL_GPL ( sysfs_add_file_to_group ) ;
2005-04-19 08:57:32 +04:00
/**
* sysfs_chmod_file - update the modified mode value on an object attribute .
* @ kobj : object we ' re acting for .
* @ attr : attribute descriptor .
* @ mode : file permissions .
*
*/
int sysfs_chmod_file ( struct kobject * kobj , struct attribute * attr , mode_t mode )
{
2007-06-13 23:27:25 +04:00
struct sysfs_dirent * victim_sd = NULL ;
struct dentry * victim = NULL ;
2005-07-29 23:13:35 +04:00
struct inode * inode ;
struct iattr newattrs ;
2007-06-13 23:27:25 +04:00
int rc ;
rc = - ENOENT ;
victim_sd = sysfs_get_dirent ( kobj - > sd , attr - > name ) ;
if ( ! victim_sd )
goto out ;
2007-08-20 16:36:30 +04:00
mutex_lock ( & sysfs_rename_mutex ) ;
2007-06-13 23:27:25 +04:00
victim = sysfs_get_dentry ( victim_sd ) ;
2007-08-20 16:36:30 +04:00
mutex_unlock ( & sysfs_rename_mutex ) ;
2007-06-13 23:27:25 +04:00
if ( IS_ERR ( victim ) ) {
rc = PTR_ERR ( victim ) ;
victim = NULL ;
goto out ;
2005-04-19 08:57:32 +04:00
}
2007-06-13 23:27:25 +04:00
inode = victim - > d_inode ;
2007-09-20 11:05:10 +04:00
2007-06-13 23:27:25 +04:00
mutex_lock ( & inode - > i_mutex ) ;
2007-09-20 11:05:10 +04:00
2007-06-13 23:27:25 +04:00
newattrs . ia_mode = ( mode & S_IALLUGO ) | ( inode - > i_mode & ~ S_IALLUGO ) ;
newattrs . ia_valid = ATTR_MODE | ATTR_CTIME ;
rc = notify_change ( victim , & newattrs ) ;
2007-09-20 11:05:10 +04:00
if ( rc = = 0 ) {
mutex_lock ( & sysfs_mutex ) ;
victim_sd - > s_mode = newattrs . ia_mode ;
mutex_unlock ( & sysfs_mutex ) ;
}
2007-06-13 23:27:25 +04:00
mutex_unlock ( & inode - > i_mutex ) ;
out :
dput ( victim ) ;
sysfs_put ( victim_sd ) ;
return rc ;
2005-04-19 08:57:32 +04:00
}
EXPORT_SYMBOL_GPL ( sysfs_chmod_file ) ;
2005-04-17 02:20:36 +04:00
/**
* sysfs_remove_file - remove an object attribute .
* @ kobj : object we ' re acting for .
* @ attr : attribute descriptor .
*
* Hash the attribute name and kill the victim .
*/
void sysfs_remove_file ( struct kobject * kobj , const struct attribute * attr )
{
2007-06-13 23:27:22 +04:00
sysfs_hash_and_remove ( kobj - > sd , attr - > name ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-20 23:02:44 +03:00
/**
* sysfs_remove_file_from_group - remove an attribute file from a group .
* @ kobj : object we ' re acting for .
* @ attr : attribute descriptor .
* @ group : group name .
*/
void sysfs_remove_file_from_group ( struct kobject * kobj ,
const struct attribute * attr , const char * group )
{
2007-06-13 23:27:22 +04:00
struct sysfs_dirent * dir_sd ;
2007-02-20 23:02:44 +03:00
2007-06-13 23:27:22 +04:00
dir_sd = sysfs_get_dirent ( kobj - > sd , group ) ;
if ( dir_sd ) {
sysfs_hash_and_remove ( dir_sd , attr - > name ) ;
sysfs_put ( dir_sd ) ;
2007-02-20 23:02:44 +03:00
}
}
EXPORT_SYMBOL_GPL ( sysfs_remove_file_from_group ) ;
2007-03-15 22:50:34 +03:00
struct sysfs_schedule_callback_struct {
struct kobject * kobj ;
void ( * func ) ( void * ) ;
void * data ;
2007-04-26 11:12:04 +04:00
struct module * owner ;
2007-03-15 22:50:34 +03:00
struct work_struct work ;
} ;
static void sysfs_schedule_callback_work ( struct work_struct * work )
{
struct sysfs_schedule_callback_struct * ss = container_of ( work ,
struct sysfs_schedule_callback_struct , work ) ;
( ss - > func ) ( ss - > data ) ;
kobject_put ( ss - > kobj ) ;
2007-04-26 11:12:04 +04:00
module_put ( ss - > owner ) ;
2007-03-15 22:50:34 +03:00
kfree ( ss ) ;
}
/**
* sysfs_schedule_callback - helper to schedule a callback for a kobject
* @ kobj : object we ' re acting for .
* @ func : callback function to invoke later .
* @ data : argument to pass to @ func .
2007-04-26 11:12:04 +04:00
* @ owner : module owning the callback code
2007-03-15 22:50:34 +03:00
*
* sysfs attribute methods must not unregister themselves or their parent
* kobject ( which would amount to the same thing ) . Attempts to do so will
* deadlock , since unregistration is mutually exclusive with driver
* callbacks .
*
* Instead methods can call this routine , which will attempt to allocate
* and schedule a workqueue request to call back @ func with @ data as its
* argument in the workqueue ' s process context . @ kobj will be pinned
* until @ func returns .
*
* Returns 0 if the request was submitted , - ENOMEM if storage could not
2007-04-26 11:12:04 +04:00
* be allocated , - ENODEV if a reference to @ owner isn ' t available .
2007-03-15 22:50:34 +03:00
*/
int sysfs_schedule_callback ( struct kobject * kobj , void ( * func ) ( void * ) ,
2007-04-26 11:12:04 +04:00
void * data , struct module * owner )
2007-03-15 22:50:34 +03:00
{
struct sysfs_schedule_callback_struct * ss ;
2007-04-26 11:12:04 +04:00
if ( ! try_module_get ( owner ) )
return - ENODEV ;
2007-03-15 22:50:34 +03:00
ss = kmalloc ( sizeof ( * ss ) , GFP_KERNEL ) ;
2007-04-26 11:12:04 +04:00
if ( ! ss ) {
module_put ( owner ) ;
2007-03-15 22:50:34 +03:00
return - ENOMEM ;
2007-04-26 11:12:04 +04:00
}
2007-03-15 22:50:34 +03:00
kobject_get ( kobj ) ;
ss - > kobj = kobj ;
ss - > func = func ;
ss - > data = data ;
2007-04-26 11:12:04 +04:00
ss - > owner = owner ;
2007-03-15 22:50:34 +03:00
INIT_WORK ( & ss - > work , sysfs_schedule_callback_work ) ;
schedule_work ( & ss - > work ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( sysfs_schedule_callback ) ;
2007-02-20 23:02:44 +03:00
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL_GPL ( sysfs_create_file ) ;
EXPORT_SYMBOL_GPL ( sysfs_remove_file ) ;