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>
2008-03-05 02:09:07 +03:00
# include <linux/kallsyms.h>
2008-03-14 05:41:52 +03:00
# include <linux/slab.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>
2013-10-02 01:42:02 +04:00
# include <linux/seq_file.h>
2005-04-17 02:20:36 +04:00
# include "sysfs.h"
2013-11-28 23:54:34 +04:00
# include "../kernfs/kernfs-internal.h"
2013-11-28 23:54:21 +04:00
2013-10-02 01:41:57 +04:00
/*
2013-12-11 23:11:53 +04:00
* Determine ktype - > sysfs_ops for the given kernfs_node . This function
2013-10-02 01:41:57 +04:00
* must be called while holding an active reference .
*/
2013-12-11 23:11:53 +04:00
static const struct sysfs_ops * sysfs_file_ops ( struct kernfs_node * kn )
2013-10-02 01:41:57 +04:00
{
2013-12-11 23:11:54 +04:00
struct kobject * kobj = kn - > parent - > priv ;
2013-10-02 01:41:57 +04:00
2013-12-11 23:11:56 +04:00
if ( kn - > flags & KERNFS_LOCKDEP )
2013-12-11 23:11:53 +04:00
lockdep_assert_held ( kn ) ;
2013-10-02 01:41:57 +04:00
return kobj - > ktype ? kobj - > ktype - > sysfs_ops : NULL ;
}
2013-10-02 01:42:02 +04:00
/*
* Reads on sysfs are handled through seq_file , which takes care of hairy
* details like buffering and seeking . The following function pipes
* sysfs_ops - > show ( ) result through seq_file .
2005-04-17 02:20:36 +04:00
*/
2013-11-28 23:54:16 +04:00
static int sysfs_kf_seq_show ( struct seq_file * sf , void * v )
2005-04-17 02:20:36 +04:00
{
2013-12-11 23:11:55 +04:00
struct kernfs_open_file * of = sf - > private ;
2013-12-11 23:11:54 +04:00
struct kobject * kobj = of - > kn - > parent - > priv ;
2013-12-11 23:11:53 +04:00
const struct sysfs_ops * ops = sysfs_file_ops ( of - > kn ) ;
2005-04-17 02:20:36 +04:00
ssize_t count ;
2013-11-28 23:54:16 +04:00
char * buf ;
2005-04-17 02:20:36 +04:00
2013-10-02 01:42:02 +04:00
/* acquire buffer and ensure that it's >= PAGE_SIZE */
count = seq_get_buf ( sf , & buf ) ;
if ( count < PAGE_SIZE ) {
seq_commit ( sf , - 1 ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
2013-10-02 01:42:02 +04:00
/*
2013-11-28 23:54:16 +04:00
* Invoke show ( ) . Control may reach here via seq file lseek even
* if @ ops - > show ( ) isn ' t implemented .
2013-10-02 01:42:02 +04:00
*/
2013-11-28 23:54:16 +04:00
if ( ops - > show ) {
2013-12-11 23:11:53 +04:00
count = ops - > show ( kobj , of - > kn - > priv , buf ) ;
2013-11-28 23:54:16 +04:00
if ( count < 0 )
return count ;
}
2007-06-13 22:45:16 +04:00
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 .
*/
2008-03-05 02:09:07 +03:00
if ( count > = ( ssize_t ) PAGE_SIZE ) {
print_symbol ( " fill_read_buffer: %s returned bad count \n " ,
( unsigned long ) ops - > show ) ;
/* Try to struggle along */
count = PAGE_SIZE - 1 ;
}
2013-10-02 01:42:02 +04:00
seq_commit ( sf , count ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2013-12-11 23:11:55 +04:00
static ssize_t sysfs_kf_bin_read ( struct kernfs_open_file * of , char * buf ,
2013-11-28 23:54:16 +04:00
size_t count , loff_t pos )
2013-10-02 01:42:06 +04:00
{
2013-12-11 23:11:53 +04:00
struct bin_attribute * battr = of - > kn - > priv ;
2013-12-11 23:11:54 +04:00
struct kobject * kobj = of - > kn - > parent - > priv ;
2013-11-28 23:54:16 +04:00
loff_t size = file_inode ( of - > file ) - > i_size ;
2013-10-02 01:42:06 +04:00
2013-11-28 23:54:16 +04:00
if ( ! count )
2013-10-02 01:42:06 +04:00
return 0 ;
if ( size ) {
2013-11-28 23:54:16 +04:00
if ( pos > size )
2013-10-02 01:42:06 +04:00
return 0 ;
2013-11-28 23:54:16 +04:00
if ( pos + count > size )
count = size - pos ;
2013-10-02 01:42:06 +04:00
}
2013-11-28 23:54:16 +04:00
if ( ! battr - > read )
return - EIO ;
return battr - > read ( of - > file , kobj , battr , buf , pos , count ) ;
}
2013-11-28 23:54:17 +04:00
/* kernfs write callback for regular sysfs files */
2013-12-11 23:11:55 +04:00
static ssize_t sysfs_kf_write ( struct kernfs_open_file * of , char * buf ,
2013-11-28 23:54:17 +04:00
size_t count , loff_t pos )
2005-04-17 02:20:36 +04:00
{
2013-12-11 23:11:53 +04:00
const struct sysfs_ops * ops = sysfs_file_ops ( of - > kn ) ;
2013-12-11 23:11:54 +04:00
struct kobject * kobj = of - > kn - > parent - > priv ;
2007-06-13 22:45:16 +04:00
2013-11-28 23:54:17 +04:00
if ( ! count )
return 0 ;
2007-06-13 22:45:16 +04:00
2013-12-11 23:11:53 +04:00
return ops - > store ( kobj , of - > kn - > priv , buf , count ) ;
2013-11-28 23:54:17 +04:00
}
2013-10-02 01:42:05 +04:00
2013-11-28 23:54:17 +04:00
/* kernfs write callback for bin sysfs files */
2013-12-11 23:11:55 +04:00
static ssize_t sysfs_kf_bin_write ( struct kernfs_open_file * of , char * buf ,
2013-11-28 23:54:17 +04:00
size_t count , loff_t pos )
{
2013-12-11 23:11:53 +04:00
struct bin_attribute * battr = of - > kn - > priv ;
2013-12-11 23:11:54 +04:00
struct kobject * kobj = of - > kn - > parent - > priv ;
2013-11-28 23:54:17 +04:00
loff_t size = file_inode ( of - > file ) - > i_size ;
2013-10-02 01:42:05 +04:00
2013-11-28 23:54:17 +04:00
if ( size ) {
if ( size < = pos )
return 0 ;
count = min_t ( ssize_t , count , size - pos ) ;
2013-10-02 01:42:05 +04:00
}
2013-11-28 23:54:17 +04:00
if ( ! count )
return 0 ;
2007-06-13 22:45:16 +04:00
2013-11-28 23:54:17 +04:00
if ( ! battr - > write )
return - EIO ;
2005-04-17 02:20:36 +04:00
2013-11-28 23:54:17 +04:00
return battr - > write ( of - > file , kobj , battr , buf , pos , count ) ;
2005-04-17 02:20:36 +04:00
}
2013-12-11 23:11:55 +04:00
static int sysfs_kf_bin_mmap ( struct kernfs_open_file * of ,
2013-11-28 23:54:18 +04:00
struct vm_area_struct * vma )
{
2013-12-11 23:11:53 +04:00
struct bin_attribute * battr = of - > kn - > priv ;
2013-12-11 23:11:54 +04:00
struct kobject * kobj = of - > kn - > parent - > priv ;
2013-11-28 23:54:18 +04:00
return battr - > mmap ( of - > file , kobj , battr , vma ) ;
}
2013-12-11 23:11:53 +04:00
void sysfs_notify ( struct kobject * kobj , const char * dir , const char * attr )
2006-03-20 09:53:53 +03:00
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * kn = kobj - > sd , * tmp ;
2007-06-13 23:27:25 +04:00
2013-12-11 23:11:53 +04:00
if ( kn & & dir )
kn = kernfs_find_and_get ( kn , dir ) ;
2013-11-28 23:54:27 +04:00
else
2013-12-11 23:11:53 +04:00
kernfs_get ( kn ) ;
2013-11-28 23:54:27 +04:00
2013-12-11 23:11:53 +04:00
if ( kn & & attr ) {
tmp = kernfs_find_and_get ( kn , attr ) ;
kernfs_put ( kn ) ;
kn = tmp ;
2013-11-28 23:54:27 +04:00
}
2007-06-13 23:27:25 +04:00
2013-12-11 23:11:53 +04:00
if ( kn ) {
kernfs_notify ( kn ) ;
kernfs_put ( kn ) ;
2013-11-28 23:54:27 +04:00
}
2006-03-20 09:53:53 +03:00
}
EXPORT_SYMBOL_GPL ( sysfs_notify ) ;
2013-11-28 23:54:21 +04:00
static const struct kernfs_ops sysfs_file_kfops_empty = {
} ;
static const struct kernfs_ops sysfs_file_kfops_ro = {
. seq_show = sysfs_kf_seq_show ,
} ;
static const struct kernfs_ops sysfs_file_kfops_wo = {
. write = sysfs_kf_write ,
} ;
static const struct kernfs_ops sysfs_file_kfops_rw = {
. seq_show = sysfs_kf_seq_show ,
. write = sysfs_kf_write ,
} ;
static const struct kernfs_ops sysfs_bin_kfops_ro = {
. read = sysfs_kf_bin_read ,
} ;
static const struct kernfs_ops sysfs_bin_kfops_wo = {
. write = sysfs_kf_bin_write ,
} ;
static const struct kernfs_ops sysfs_bin_kfops_rw = {
. read = sysfs_kf_bin_read ,
. write = sysfs_kf_bin_write ,
2013-12-10 18:29:17 +04:00
} ;
static const struct kernfs_ops sysfs_bin_kfops_mmap = {
. read = sysfs_kf_bin_read ,
. write = sysfs_kf_bin_write ,
2013-11-28 23:54:21 +04:00
. mmap = sysfs_kf_bin_mmap ,
} ;
2013-12-11 23:11:53 +04:00
int sysfs_add_file_mode_ns ( struct kernfs_node * parent ,
2013-11-28 23:54:23 +04:00
const struct attribute * attr , bool is_bin ,
2013-11-28 23:54:24 +04:00
umode_t mode , const void * ns )
2005-04-17 02:20:36 +04:00
{
2013-11-28 23:54:29 +04:00
struct lock_class_key * key = NULL ;
2013-11-28 23:54:21 +04:00
const struct kernfs_ops * ops ;
2013-12-11 23:11:53 +04:00
struct kernfs_node * kn ;
2013-11-28 23:54:22 +04:00
loff_t size ;
2005-04-17 02:20:36 +04:00
2013-11-28 23:54:23 +04:00
if ( ! is_bin ) {
2013-12-11 23:11:53 +04:00
struct kobject * kobj = parent - > priv ;
2013-11-28 23:54:21 +04:00
const struct sysfs_ops * sysfs_ops = kobj - > ktype - > sysfs_ops ;
/* every kobject with an attribute needs a ktype assigned */
if ( WARN ( ! sysfs_ops , KERN_ERR
" missing sysfs attribute operations for kobject: %s \n " ,
kobject_name ( kobj ) ) )
return - EINVAL ;
if ( sysfs_ops - > show & & sysfs_ops - > store )
ops = & sysfs_file_kfops_rw ;
else if ( sysfs_ops - > show )
ops = & sysfs_file_kfops_ro ;
else if ( sysfs_ops - > store )
ops = & sysfs_file_kfops_wo ;
else
ops = & sysfs_file_kfops_empty ;
2013-11-28 23:54:22 +04:00
size = PAGE_SIZE ;
2013-11-28 23:54:21 +04:00
} else {
struct bin_attribute * battr = ( void * ) attr ;
2013-12-10 18:29:17 +04:00
if ( battr - > mmap )
ops = & sysfs_bin_kfops_mmap ;
else if ( battr - > read & & battr - > write )
2013-11-28 23:54:21 +04:00
ops = & sysfs_bin_kfops_rw ;
else if ( battr - > read )
ops = & sysfs_bin_kfops_ro ;
else if ( battr - > write )
ops = & sysfs_bin_kfops_wo ;
else
ops = & sysfs_file_kfops_empty ;
2013-11-28 23:54:22 +04:00
size = battr - > size ;
2013-11-28 23:54:21 +04:00
}
2013-11-28 23:54:29 +04:00
# ifdef CONFIG_DEBUG_LOCK_ALLOC
if ( ! attr - > ignore_lockdep )
key = attr - > key ? : ( struct lock_class_key * ) & attr - > skey ;
# endif
2013-12-12 01:02:57 +04:00
kn = __kernfs_create_file ( parent , attr - > name , mode , size , ops ,
( void * ) attr , ns , true , key ) ;
2013-12-11 23:11:53 +04:00
if ( IS_ERR ( kn ) ) {
if ( PTR_ERR ( kn ) = = - EEXIST )
sysfs_warn_dup ( parent , attr - > name ) ;
return PTR_ERR ( kn ) ;
2013-11-28 23:54:24 +04:00
}
return 0 ;
}
2013-12-11 23:11:53 +04:00
int sysfs_add_file ( struct kernfs_node * parent , const struct attribute * attr ,
2013-11-28 23:54:23 +04:00
bool is_bin )
2008-03-21 04:47:52 +03:00
{
2013-12-11 23:11:53 +04:00
return sysfs_add_file_mode_ns ( parent , attr , is_bin , attr - > mode , NULL ) ;
2008-03-21 04:47:52 +03:00
}
2005-04-17 02:20:36 +04:00
/**
2013-09-12 06:29:04 +04:00
* sysfs_create_file_ns - create an attribute file for an object with custom ns
* @ kobj : object we ' re creating for
* @ attr : attribute descriptor
* @ ns : namespace the new file should belong to
2005-04-17 02:20:36 +04:00
*/
2013-09-12 06:29:04 +04:00
int sysfs_create_file_ns ( struct kobject * kobj , const struct attribute * attr ,
const void * ns )
2005-04-17 02:20:36 +04:00
{
2007-06-13 23:27:22 +04:00
BUG_ON ( ! kobj | | ! kobj - > sd | | ! attr ) ;
2005-04-17 02:20:36 +04:00
2013-11-28 23:54:23 +04:00
return sysfs_add_file_mode_ns ( kobj - > sd , attr , false , attr - > mode , ns ) ;
2005-04-17 02:20:36 +04:00
}
2013-09-12 06:29:04 +04:00
EXPORT_SYMBOL_GPL ( sysfs_create_file_ns ) ;
2005-04-17 02:20:36 +04:00
2010-01-05 14:48:01 +03:00
int sysfs_create_files ( struct kobject * kobj , const struct attribute * * ptr )
{
int err = 0 ;
int i ;
for ( i = 0 ; ptr [ i ] & & ! err ; i + + )
err = sysfs_create_file ( kobj , ptr [ i ] ) ;
if ( err )
while ( - - i > = 0 )
sysfs_remove_file ( kobj , ptr [ i ] ) ;
return err ;
}
2013-08-22 03:17:47 +04:00
EXPORT_SYMBOL_GPL ( sysfs_create_files ) ;
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 )
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * parent ;
2007-02-20 23:02:44 +03:00
int error ;
2013-11-28 23:54:30 +04:00
if ( group ) {
2013-12-11 23:11:53 +04:00
parent = kernfs_find_and_get ( kobj - > sd , group ) ;
2013-11-28 23:54:30 +04:00
} else {
2013-12-11 23:11:53 +04:00
parent = kobj - > sd ;
kernfs_get ( parent ) ;
2013-11-28 23:54:30 +04:00
}
2008-01-03 03:44:05 +03:00
2013-12-11 23:11:53 +04:00
if ( ! parent )
2007-06-13 23:27:22 +04:00
return - ENOENT ;
2013-12-11 23:11:53 +04:00
error = sysfs_add_file ( parent , attr , false ) ;
kernfs_put ( parent ) ;
2007-06-13 23:27:22 +04:00
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 .
*
*/
2010-07-02 18:54:05 +04:00
int sysfs_chmod_file ( struct kobject * kobj , const struct attribute * attr ,
2011-07-24 11:40:40 +04:00
umode_t mode )
2005-04-19 08:57:32 +04:00
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * kn ;
2005-07-29 23:13:35 +04:00
struct iattr newattrs ;
2007-06-13 23:27:25 +04:00
int rc ;
2013-12-11 23:11:53 +04:00
kn = kernfs_find_and_get ( kobj - > sd , attr - > name ) ;
if ( ! kn )
2013-11-24 02:21:52 +04:00
return - ENOENT ;
2007-09-20 11:05:10 +04:00
2013-12-11 23:11:54 +04:00
newattrs . ia_mode = ( mode & S_IALLUGO ) | ( kn - > mode & ~ S_IALLUGO ) ;
2009-11-08 10:27:02 +03:00
newattrs . ia_valid = ATTR_MODE ;
2007-09-20 11:05:10 +04:00
2013-12-11 23:11:53 +04:00
rc = kernfs_setattr ( kn , & newattrs ) ;
2013-11-24 02:21:52 +04:00
2013-12-11 23:11:53 +04:00
kernfs_put ( kn ) ;
2007-06-13 23:27:25 +04:00
return rc ;
2005-04-19 08:57:32 +04:00
}
EXPORT_SYMBOL_GPL ( sysfs_chmod_file ) ;
2005-04-17 02:20:36 +04:00
/**
2013-09-12 06:29:04 +04:00
* sysfs_remove_file_ns - remove an object attribute with a custom ns tag
* @ kobj : object we ' re acting for
* @ attr : attribute descriptor
* @ ns : namespace tag of the file to remove
2005-04-17 02:20:36 +04:00
*
2013-09-12 06:29:04 +04:00
* Hash the attribute name and namespace tag and kill the victim .
2005-04-17 02:20:36 +04:00
*/
2013-09-12 06:29:04 +04:00
void sysfs_remove_file_ns ( struct kobject * kobj , const struct attribute * attr ,
const void * ns )
2005-04-17 02:20:36 +04:00
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * parent = kobj - > sd ;
2011-10-13 01:53:38 +04:00
2013-12-11 23:11:53 +04:00
kernfs_remove_by_name_ns ( parent , attr - > name , ns ) ;
2005-04-17 02:20:36 +04:00
}
2013-09-12 06:29:04 +04:00
EXPORT_SYMBOL_GPL ( sysfs_remove_file_ns ) ;
2005-04-17 02:20:36 +04:00
2013-08-22 03:28:26 +04:00
void sysfs_remove_files ( struct kobject * kobj , const struct attribute * * ptr )
2010-01-05 14:48:01 +03:00
{
int i ;
for ( i = 0 ; ptr [ i ] ; i + + )
sysfs_remove_file ( kobj , ptr [ i ] ) ;
}
2013-08-22 03:17:47 +04:00
EXPORT_SYMBOL_GPL ( sysfs_remove_files ) ;
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 )
{
2013-12-11 23:11:53 +04:00
struct kernfs_node * parent ;
2007-02-20 23:02:44 +03:00
2013-11-28 23:54:30 +04:00
if ( group ) {
2013-12-11 23:11:53 +04:00
parent = kernfs_find_and_get ( kobj - > sd , group ) ;
2013-11-28 23:54:30 +04:00
} else {
2013-12-11 23:11:53 +04:00
parent = kobj - > sd ;
kernfs_get ( parent ) ;
2013-11-28 23:54:30 +04:00
}
2013-12-11 23:11:53 +04:00
if ( parent ) {
kernfs_remove_by_name ( parent , attr - > name ) ;
kernfs_put ( parent ) ;
2007-02-20 23:02:44 +03:00
}
}
EXPORT_SYMBOL_GPL ( sysfs_remove_file_from_group ) ;
2013-10-02 01:42:09 +04:00
/**
* sysfs_create_bin_file - create binary file for object .
* @ kobj : object .
* @ attr : attribute descriptor .
*/
int sysfs_create_bin_file ( struct kobject * kobj ,
const struct bin_attribute * attr )
{
BUG_ON ( ! kobj | | ! kobj - > sd | | ! attr ) ;
2013-11-28 23:54:23 +04:00
return sysfs_add_file ( kobj - > sd , & attr - > attr , true ) ;
2013-10-02 01:42:09 +04:00
}
EXPORT_SYMBOL_GPL ( sysfs_create_bin_file ) ;
/**
* sysfs_remove_bin_file - remove binary file for object .
* @ kobj : object .
* @ attr : attribute descriptor .
*/
void sysfs_remove_bin_file ( struct kobject * kobj ,
const struct bin_attribute * attr )
{
2013-11-24 02:21:49 +04:00
kernfs_remove_by_name ( kobj - > sd , attr - > attr . name ) ;
2013-10-02 01:42:09 +04:00
}
EXPORT_SYMBOL_GPL ( sysfs_remove_bin_file ) ;
2014-01-14 01:51:36 +04:00
struct sysfs_schedule_callback_struct {
struct list_head workq_list ;
struct kobject * kobj ;
void ( * func ) ( void * ) ;
void * data ;
struct module * owner ;
struct work_struct work ;
} ;
static struct workqueue_struct * sysfs_workqueue ;
static DEFINE_MUTEX ( sysfs_workq_mutex ) ;
static LIST_HEAD ( sysfs_workq ) ;
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 ) ;
module_put ( ss - > owner ) ;
mutex_lock ( & sysfs_workq_mutex ) ;
list_del ( & ss - > workq_list ) ;
mutex_unlock ( & sysfs_workq_mutex ) ;
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 .
* @ owner : module owning the callback code
*
* 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
* be allocated , - ENODEV if a reference to @ owner isn ' t available ,
* - EAGAIN if a callback has already been scheduled for @ kobj .
*/
int sysfs_schedule_callback ( struct kobject * kobj , void ( * func ) ( void * ) ,
void * data , struct module * owner )
{
struct sysfs_schedule_callback_struct * ss , * tmp ;
if ( ! try_module_get ( owner ) )
return - ENODEV ;
mutex_lock ( & sysfs_workq_mutex ) ;
list_for_each_entry_safe ( ss , tmp , & sysfs_workq , workq_list )
if ( ss - > kobj = = kobj ) {
module_put ( owner ) ;
mutex_unlock ( & sysfs_workq_mutex ) ;
return - EAGAIN ;
}
mutex_unlock ( & sysfs_workq_mutex ) ;
if ( sysfs_workqueue = = NULL ) {
sysfs_workqueue = create_singlethread_workqueue ( " sysfsd " ) ;
if ( sysfs_workqueue = = NULL ) {
module_put ( owner ) ;
return - ENOMEM ;
}
}
ss = kmalloc ( sizeof ( * ss ) , GFP_KERNEL ) ;
if ( ! ss ) {
module_put ( owner ) ;
return - ENOMEM ;
}
kobject_get ( kobj ) ;
ss - > kobj = kobj ;
ss - > func = func ;
ss - > data = data ;
ss - > owner = owner ;
INIT_WORK ( & ss - > work , sysfs_schedule_callback_work ) ;
INIT_LIST_HEAD ( & ss - > workq_list ) ;
mutex_lock ( & sysfs_workq_mutex ) ;
list_add_tail ( & ss - > workq_list , & sysfs_workq ) ;
mutex_unlock ( & sysfs_workq_mutex ) ;
queue_work ( sysfs_workqueue , & ss - > work ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( sysfs_schedule_callback ) ;