2017-02-13 13:38:17 +01:00
// SPDX-License-Identifier: GPL-2.0
/*
* SCLP Store Data support and sysfs interface
*
* Copyright IBM Corp . 2017
*/
# define KMSG_COMPONENT "sclp_sd"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
# include <linux/completion.h>
# include <linux/kobject.h>
# include <linux/list.h>
# include <linux/printk.h>
# include <linux/slab.h>
# include <linux/vmalloc.h>
# include <linux/async.h>
# include <linux/export.h>
# include <linux/mutex.h>
# include <asm/pgalloc.h>
# include "sclp.h"
# define SD_EQ_STORE_DATA 0
# define SD_EQ_HALT 1
# define SD_EQ_SIZE 2
# define SD_DI_CONFIG 3
struct sclp_sd_evbuf {
struct evbuf_header hdr ;
u8 eq ;
u8 di ;
u8 rflags ;
u64 : 56 ;
u32 id ;
u16 : 16 ;
u8 fmt ;
u8 status ;
u64 sat ;
u64 sa ;
u32 esize ;
u32 dsize ;
} __packed ;
struct sclp_sd_sccb {
struct sccb_header hdr ;
struct sclp_sd_evbuf evbuf ;
} __packed __aligned ( PAGE_SIZE ) ;
/**
* struct sclp_sd_data - Result of a Store Data request
* @ esize_bytes : Resulting esize in bytes
* @ dsize_bytes : Resulting dsize in bytes
* @ data : Pointer to data - must be released using vfree ( )
*/
struct sclp_sd_data {
size_t esize_bytes ;
size_t dsize_bytes ;
void * data ;
} ;
/**
* struct sclp_sd_listener - Listener for asynchronous Store Data response
* @ list : For enqueueing this struct
* @ id : Event ID of response to listen for
* @ completion : Can be used to wait for response
* @ evbuf : Contains the resulting Store Data response after completion
*/
struct sclp_sd_listener {
struct list_head list ;
u32 id ;
struct completion completion ;
struct sclp_sd_evbuf evbuf ;
} ;
/**
* struct sclp_sd_file - Sysfs representation of a Store Data entity
* @ kobj : Kobject
* @ data_attr : Attribute for accessing data contents
* @ data_mutex : Mutex to serialize access and updates to @ data
* @ data : Data associated with this entity
* @ di : DI value associated with this entity
*/
struct sclp_sd_file {
struct kobject kobj ;
struct bin_attribute data_attr ;
struct mutex data_mutex ;
struct sclp_sd_data data ;
u8 di ;
} ;
# define to_sd_file(x) container_of(x, struct sclp_sd_file, kobj)
static struct kset * sclp_sd_kset ;
static struct sclp_sd_file * config_file ;
static LIST_HEAD ( sclp_sd_queue ) ;
static DEFINE_SPINLOCK ( sclp_sd_queue_lock ) ;
/**
* sclp_sd_listener_add ( ) - Add listener for Store Data responses
* @ listener : Listener to add
*/
static void sclp_sd_listener_add ( struct sclp_sd_listener * listener )
{
spin_lock_irq ( & sclp_sd_queue_lock ) ;
list_add_tail ( & listener - > list , & sclp_sd_queue ) ;
spin_unlock_irq ( & sclp_sd_queue_lock ) ;
}
/**
* sclp_sd_listener_remove ( ) - Remove listener for Store Data responses
* @ listener : Listener to remove
*/
static void sclp_sd_listener_remove ( struct sclp_sd_listener * listener )
{
spin_lock_irq ( & sclp_sd_queue_lock ) ;
list_del ( & listener - > list ) ;
spin_unlock_irq ( & sclp_sd_queue_lock ) ;
}
/**
* sclp_sd_listener_init ( ) - Initialize a Store Data response listener
* @ id : Event ID to listen for
*
* Initialize a listener for asynchronous Store Data responses . This listener
* can afterwards be used to wait for a specific response and to retrieve
* the associated response data .
*/
static void sclp_sd_listener_init ( struct sclp_sd_listener * listener , u32 id )
{
memset ( listener , 0 , sizeof ( * listener ) ) ;
listener - > id = id ;
init_completion ( & listener - > completion ) ;
}
/**
* sclp_sd_receiver ( ) - Receiver for Store Data events
* @ evbuf_hdr : Header of received events
*
* Process Store Data events and complete listeners with matching event IDs .
*/
static void sclp_sd_receiver ( struct evbuf_header * evbuf_hdr )
{
struct sclp_sd_evbuf * evbuf = ( struct sclp_sd_evbuf * ) evbuf_hdr ;
struct sclp_sd_listener * listener ;
int found = 0 ;
pr_debug ( " received event (id=0x%08x) \n " , evbuf - > id ) ;
spin_lock ( & sclp_sd_queue_lock ) ;
list_for_each_entry ( listener , & sclp_sd_queue , list ) {
if ( listener - > id ! = evbuf - > id )
continue ;
listener - > evbuf = * evbuf ;
complete ( & listener - > completion ) ;
found = 1 ;
break ;
}
spin_unlock ( & sclp_sd_queue_lock ) ;
if ( ! found )
pr_debug ( " unsolicited event (id=0x%08x) \n " , evbuf - > id ) ;
}
static struct sclp_register sclp_sd_register = {
. send_mask = EVTYP_STORE_DATA_MASK ,
. receive_mask = EVTYP_STORE_DATA_MASK ,
. receiver_fn = sclp_sd_receiver ,
} ;
/**
* sclp_sd_sync ( ) - Perform Store Data request synchronously
* @ page : Address of work page - must be below 2 GB
* @ eq : Input EQ value
* @ di : Input DI value
* @ sat : Input SAT value
* @ sa : Input SA value used to specify the address of the target buffer
* @ dsize_ptr : Optional pointer to input and output DSIZE value
* @ esize_ptr : Optional pointer to output ESIZE value
*
* Perform Store Data request with specified parameters and wait for completion .
*
* Return % 0 on success and store resulting DSIZE and ESIZE values in
* @ dsize_ptr and @ esize_ptr ( if provided ) . Return non - zero on error .
*/
static int sclp_sd_sync ( unsigned long page , u8 eq , u8 di , u64 sat , u64 sa ,
u32 * dsize_ptr , u32 * esize_ptr )
{
struct sclp_sd_sccb * sccb = ( void * ) page ;
struct sclp_sd_listener listener ;
struct sclp_sd_evbuf * evbuf ;
int rc ;
sclp_sd_listener_init ( & listener , ( u32 ) ( addr_t ) sccb ) ;
sclp_sd_listener_add ( & listener ) ;
/* Prepare SCCB */
memset ( sccb , 0 , PAGE_SIZE ) ;
sccb - > hdr . length = sizeof ( sccb - > hdr ) + sizeof ( sccb - > evbuf ) ;
evbuf = & sccb - > evbuf ;
evbuf - > hdr . length = sizeof ( * evbuf ) ;
evbuf - > hdr . type = EVTYP_STORE_DATA ;
evbuf - > eq = eq ;
evbuf - > di = di ;
evbuf - > id = listener . id ;
evbuf - > fmt = 1 ;
evbuf - > sat = sat ;
evbuf - > sa = sa ;
if ( dsize_ptr )
evbuf - > dsize = * dsize_ptr ;
/* Perform command */
pr_debug ( " request (eq=%d, di=%d, id=0x%08x) \n " , eq , di , listener . id ) ;
rc = sclp_sync_request ( SCLP_CMDW_WRITE_EVENT_DATA , sccb ) ;
pr_debug ( " request done (rc=%d) \n " , rc ) ;
if ( rc )
goto out ;
/* Evaluate response */
if ( sccb - > hdr . response_code = = 0x73f0 ) {
pr_debug ( " event not supported \n " ) ;
rc = - EIO ;
goto out_remove ;
}
if ( sccb - > hdr . response_code ! = 0x0020 | | ! ( evbuf - > hdr . flags & 0x80 ) ) {
rc = - EIO ;
goto out ;
}
if ( ! ( evbuf - > rflags & 0x80 ) ) {
rc = wait_for_completion_interruptible ( & listener . completion ) ;
if ( rc )
goto out ;
evbuf = & listener . evbuf ;
}
switch ( evbuf - > status ) {
case 0 :
if ( dsize_ptr )
* dsize_ptr = evbuf - > dsize ;
if ( esize_ptr )
* esize_ptr = evbuf - > esize ;
pr_debug ( " success (dsize=%u, esize=%u) \n " , evbuf - > dsize ,
evbuf - > esize ) ;
break ;
case 3 :
rc = - ENOENT ;
break ;
default :
rc = - EIO ;
break ;
}
out :
if ( rc & & rc ! = - ENOENT ) {
/* Provide some information about what went wrong */
pr_warn ( " Store Data request failed (eq=%d, di=%d, "
" response=0x%04x, flags=0x%02x, status=%d, rc=%d) \n " ,
eq , di , sccb - > hdr . response_code , evbuf - > hdr . flags ,
evbuf - > status , rc ) ;
}
out_remove :
sclp_sd_listener_remove ( & listener ) ;
return rc ;
}
/**
* sclp_sd_store_data ( ) - Obtain data for specified Store Data entity
* @ result : Resulting data
* @ di : DI value associated with this entity
*
* Perform a series of Store Data requests to obtain the size and contents of
* the specified Store Data entity .
*
* Return :
* % 0 : Success - result is stored in @ result . @ result - > data must be
* released using vfree ( ) after use .
* % - ENOENT : No data available for this entity
* % < 0 : Other error
*/
static int sclp_sd_store_data ( struct sclp_sd_data * result , u8 di )
{
u32 dsize = 0 , esize = 0 ;
unsigned long page , asce = 0 ;
void * data = NULL ;
int rc ;
page = __get_free_page ( GFP_KERNEL | GFP_DMA ) ;
if ( ! page )
return - ENOMEM ;
/* Get size */
rc = sclp_sd_sync ( page , SD_EQ_SIZE , di , 0 , 0 , & dsize , & esize ) ;
if ( rc )
goto out ;
if ( dsize = = 0 )
goto out_result ;
/* Allocate memory */
treewide: Use array_size() in vzalloc()
The vzalloc() function has no 2-factor argument form, so multiplication
factors need to be wrapped in array_size(). This patch replaces cases of:
vzalloc(a * b)
with:
vzalloc(array_size(a, b))
as well as handling cases of:
vzalloc(a * b * c)
with:
vzalloc(array3_size(a, b, c))
This does, however, attempt to ignore constant size factors like:
vzalloc(4 * 1024)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
vzalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
vzalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
vzalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
vzalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
vzalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
vzalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
vzalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
vzalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
vzalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
vzalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
vzalloc(
- sizeof(TYPE) * (COUNT_ID)
+ array_size(COUNT_ID, sizeof(TYPE))
, ...)
|
vzalloc(
- sizeof(TYPE) * COUNT_ID
+ array_size(COUNT_ID, sizeof(TYPE))
, ...)
|
vzalloc(
- sizeof(TYPE) * (COUNT_CONST)
+ array_size(COUNT_CONST, sizeof(TYPE))
, ...)
|
vzalloc(
- sizeof(TYPE) * COUNT_CONST
+ array_size(COUNT_CONST, sizeof(TYPE))
, ...)
|
vzalloc(
- sizeof(THING) * (COUNT_ID)
+ array_size(COUNT_ID, sizeof(THING))
, ...)
|
vzalloc(
- sizeof(THING) * COUNT_ID
+ array_size(COUNT_ID, sizeof(THING))
, ...)
|
vzalloc(
- sizeof(THING) * (COUNT_CONST)
+ array_size(COUNT_CONST, sizeof(THING))
, ...)
|
vzalloc(
- sizeof(THING) * COUNT_CONST
+ array_size(COUNT_CONST, sizeof(THING))
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
vzalloc(
- SIZE * COUNT
+ array_size(COUNT, SIZE)
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
vzalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
vzalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
vzalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
vzalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
vzalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
vzalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
vzalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
vzalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
vzalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
vzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
vzalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
vzalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
vzalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
vzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
vzalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vzalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vzalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vzalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vzalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vzalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vzalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vzalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
vzalloc(C1 * C2 * C3, ...)
|
vzalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants.
@@
expression E1, E2;
constant C1, C2;
@@
(
vzalloc(C1 * C2, ...)
|
vzalloc(
- E1 * E2
+ array_size(E1, E2)
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-12 14:27:37 -07:00
data = vzalloc ( array_size ( ( size_t ) dsize , PAGE_SIZE ) ) ;
2017-02-13 13:38:17 +01:00
if ( ! data ) {
rc = - ENOMEM ;
goto out ;
}
/* Get translation table for buffer */
asce = base_asce_alloc ( ( unsigned long ) data , dsize ) ;
if ( ! asce ) {
vfree ( data ) ;
rc = - ENOMEM ;
goto out ;
}
/* Get data */
rc = sclp_sd_sync ( page , SD_EQ_STORE_DATA , di , asce , ( u64 ) data , & dsize ,
& esize ) ;
if ( rc ) {
/* Cancel running request if interrupted */
if ( rc = = - ERESTARTSYS )
sclp_sd_sync ( page , SD_EQ_HALT , di , 0 , 0 , NULL , NULL ) ;
vfree ( data ) ;
goto out ;
}
out_result :
result - > esize_bytes = ( size_t ) esize * PAGE_SIZE ;
result - > dsize_bytes = ( size_t ) dsize * PAGE_SIZE ;
result - > data = data ;
out :
base_asce_free ( asce ) ;
free_page ( page ) ;
return rc ;
}
/**
* sclp_sd_data_reset ( ) - Reset Store Data result buffer
* @ data : Data buffer to reset
*
* Reset @ data to initial state and release associated memory .
*/
static void sclp_sd_data_reset ( struct sclp_sd_data * data )
{
vfree ( data - > data ) ;
data - > data = NULL ;
data - > dsize_bytes = 0 ;
data - > esize_bytes = 0 ;
}
/**
* sclp_sd_file_release ( ) - Release function for sclp_sd_file object
* @ kobj : Kobject embedded in sclp_sd_file object
*/
static void sclp_sd_file_release ( struct kobject * kobj )
{
struct sclp_sd_file * sd_file = to_sd_file ( kobj ) ;
sclp_sd_data_reset ( & sd_file - > data ) ;
kfree ( sd_file ) ;
}
/**
* sclp_sd_file_update ( ) - Update contents of sclp_sd_file object
* @ sd_file : Object to update
*
* Obtain the current version of data associated with the Store Data entity
* @ sd_file .
*
* On success , return % 0 and generate a KOBJ_CHANGE event to indicate that the
* data may have changed . Return non - zero otherwise .
*/
static int sclp_sd_file_update ( struct sclp_sd_file * sd_file )
{
const char * name = kobject_name ( & sd_file - > kobj ) ;
struct sclp_sd_data data ;
int rc ;
rc = sclp_sd_store_data ( & data , sd_file - > di ) ;
if ( rc ) {
if ( rc = = - ENOENT ) {
pr_info ( " No data is available for the %s data entity \n " ,
name ) ;
}
return rc ;
}
mutex_lock ( & sd_file - > data_mutex ) ;
sclp_sd_data_reset ( & sd_file - > data ) ;
sd_file - > data = data ;
mutex_unlock ( & sd_file - > data_mutex ) ;
pr_info ( " A %zu-byte %s data entity was retrieved \n " , data . dsize_bytes ,
name ) ;
kobject_uevent ( & sd_file - > kobj , KOBJ_CHANGE ) ;
return 0 ;
}
/**
* sclp_sd_file_update_async ( ) - Wrapper for asynchronous update call
* @ data : Object to update
*/
static void sclp_sd_file_update_async ( void * data , async_cookie_t cookie )
{
struct sclp_sd_file * sd_file = data ;
sclp_sd_file_update ( sd_file ) ;
}
/**
* reload_store ( ) - Store function for " reload " sysfs attribute
* @ kobj : Kobject of sclp_sd_file object
*
* Initiate a reload of the data associated with an sclp_sd_file object .
*/
static ssize_t reload_store ( struct kobject * kobj , struct kobj_attribute * attr ,
const char * buf , size_t count )
{
struct sclp_sd_file * sd_file = to_sd_file ( kobj ) ;
sclp_sd_file_update ( sd_file ) ;
return count ;
}
static struct kobj_attribute reload_attr = __ATTR_WO ( reload ) ;
static struct attribute * sclp_sd_file_default_attrs [ ] = {
& reload_attr . attr ,
NULL ,
} ;
static struct kobj_type sclp_sd_file_ktype = {
. sysfs_ops = & kobj_sysfs_ops ,
. release = sclp_sd_file_release ,
. default_attrs = sclp_sd_file_default_attrs ,
} ;
/**
* data_read ( ) - Read function for " read " sysfs attribute
* @ kobj : Kobject of sclp_sd_file object
* @ buffer : Target buffer
* @ off : Requested file offset
* @ size : Requested number of bytes
*
* Store the requested portion of the Store Data entity contents into the
* specified buffer . Return the number of bytes stored on success , or % 0
* on EOF .
*/
static ssize_t data_read ( struct file * file , struct kobject * kobj ,
struct bin_attribute * attr , char * buffer ,
loff_t off , size_t size )
{
struct sclp_sd_file * sd_file = to_sd_file ( kobj ) ;
size_t data_size ;
char * data ;
mutex_lock ( & sd_file - > data_mutex ) ;
data = sd_file - > data . data ;
data_size = sd_file - > data . dsize_bytes ;
if ( ! data | | off > = data_size ) {
size = 0 ;
} else {
if ( off + size > data_size )
size = data_size - off ;
memcpy ( buffer , data + off , size ) ;
}
mutex_unlock ( & sd_file - > data_mutex ) ;
return size ;
}
/**
* sclp_sd_file_create ( ) - Add a sysfs file representing a Store Data entity
* @ name : Name of file
* @ di : DI value associated with this entity
*
* Create a sysfs directory with the given @ name located under
*
* / sys / firmware / sclp_sd /
*
* The files in this directory can be used to access the contents of the Store
* Data entity associated with @ DI .
*
* Return pointer to resulting sclp_sd_file object on success , % NULL otherwise .
* The object must be freed by calling kobject_put ( ) on the embedded kobject
* pointer after use .
*/
static __init struct sclp_sd_file * sclp_sd_file_create ( const char * name , u8 di )
{
struct sclp_sd_file * sd_file ;
int rc ;
sd_file = kzalloc ( sizeof ( * sd_file ) , GFP_KERNEL ) ;
if ( ! sd_file )
return NULL ;
sd_file - > di = di ;
mutex_init ( & sd_file - > data_mutex ) ;
/* Create kobject located under /sys/firmware/sclp_sd/ */
sd_file - > kobj . kset = sclp_sd_kset ;
rc = kobject_init_and_add ( & sd_file - > kobj , & sclp_sd_file_ktype , NULL ,
" %s " , name ) ;
if ( rc ) {
kobject_put ( & sd_file - > kobj ) ;
return NULL ;
}
sysfs_bin_attr_init ( & sd_file - > data_attr ) ;
sd_file - > data_attr . attr . name = " data " ;
sd_file - > data_attr . attr . mode = 0444 ;
sd_file - > data_attr . read = data_read ;
rc = sysfs_create_bin_file ( & sd_file - > kobj , & sd_file - > data_attr ) ;
if ( rc ) {
kobject_put ( & sd_file - > kobj ) ;
return NULL ;
}
/*
* For completeness only - users interested in entity data should listen
* for KOBJ_CHANGE instead .
*/
kobject_uevent ( & sd_file - > kobj , KOBJ_ADD ) ;
/* Don't let a slow Store Data request delay further initialization */
async_schedule ( sclp_sd_file_update_async , sd_file ) ;
return sd_file ;
}
/**
* sclp_sd_init ( ) - Initialize sclp_sd support and register sysfs files
*/
static __init int sclp_sd_init ( void )
{
int rc ;
rc = sclp_register ( & sclp_sd_register ) ;
if ( rc )
return rc ;
/* Create kset named "sclp_sd" located under /sys/firmware/ */
rc = - ENOMEM ;
sclp_sd_kset = kset_create_and_add ( " sclp_sd " , NULL , firmware_kobj ) ;
if ( ! sclp_sd_kset )
goto err_kset ;
rc = - EINVAL ;
config_file = sclp_sd_file_create ( " config " , SD_DI_CONFIG ) ;
if ( ! config_file )
goto err_config ;
return 0 ;
err_config :
kset_unregister ( sclp_sd_kset ) ;
err_kset :
sclp_unregister ( & sclp_sd_register ) ;
return rc ;
}
device_initcall ( sclp_sd_init ) ;