2006-06-23 02:05:06 -07:00
/*
* Hypervisor filesystem for Linux on s390 . Diag 204 and 224
* implementation .
*
2008-12-25 13:39:39 +01:00
* Copyright IBM Corp . 2006 , 2008
2006-06-23 02:05:06 -07:00
* Author ( s ) : Michael Holzheu < holzheu @ de . ibm . com >
*/
2008-12-25 13:39:39 +01:00
# define KMSG_COMPONENT "hypfs"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2006-06-23 02:05:06 -07:00
# include <linux/types.h>
# include <linux/errno.h>
2009-03-31 19:16:02 +02:00
# include <linux/slab.h>
2006-06-23 02:05:06 -07:00
# include <linux/string.h>
# include <linux/vmalloc.h>
2010-05-17 10:00:20 +02:00
# include <linux/mm.h>
2015-08-20 17:28:44 +02:00
# include <asm/diag.h>
2006-06-23 02:05:06 -07:00
# include <asm/ebcdic.h>
# include "hypfs.h"
# define LPAR_NAME_LEN 8 /* lpar name len in diag 204 data */
# define CPU_NAME_LEN 16 /* type name len of cpus in diag224 name table */
# define TMP_SIZE 64 /* size of temporary buffers */
2010-05-17 10:00:20 +02:00
# define DBFS_D204_HDR_VERSION 0
2006-06-23 02:05:06 -07:00
/* diag 204 subcodes */
enum diag204_sc {
SUBC_STIB4 = 4 ,
SUBC_RSI = 5 ,
SUBC_STIB6 = 6 ,
SUBC_STIB7 = 7
} ;
/* The two available diag 204 data formats */
enum diag204_format {
INFO_SIMPLE = 0 ,
INFO_EXT = 0x00010000
} ;
/* bit is set in flags, when physical cpu info is included in diag 204 data */
# define LPAR_PHYS_FLG 0x80
static char * diag224_cpu_names ; /* diag 224 name table */
static enum diag204_sc diag204_store_sc ; /* used subcode for store */
static enum diag204_format diag204_info_type ; /* used diag 204 data format */
static void * diag204_buf ; /* 4K aligned buffer for diag204 data */
static void * diag204_buf_vmalloc ; /* vmalloc pointer for diag204 data */
static int diag204_buf_pages ; /* number of pages for diag204 data */
2010-05-17 10:00:20 +02:00
static struct dentry * dbfs_d204_file ;
2006-06-23 02:05:06 -07:00
/*
* DIAG 204 data structures and member access functions .
*
* Since we have two different diag 204 data formats for old and new s390
* machines , we do not access the structs directly , but use getter functions for
* each struct member instead . This should make the code more readable .
*/
/* Time information block */
struct info_blk_hdr {
__u8 npar ;
__u8 flags ;
__u16 tslice ;
__u16 phys_cpus ;
__u16 this_part ;
__u64 curtod ;
} __attribute__ ( ( packed ) ) ;
struct x_info_blk_hdr {
__u8 npar ;
__u8 flags ;
__u16 tslice ;
__u16 phys_cpus ;
__u16 this_part ;
__u64 curtod1 ;
__u64 curtod2 ;
char reserved [ 40 ] ;
} __attribute__ ( ( packed ) ) ;
static inline int info_blk_hdr__size ( enum diag204_format type )
{
if ( type = = INFO_SIMPLE )
return sizeof ( struct info_blk_hdr ) ;
else /* INFO_EXT */
return sizeof ( struct x_info_blk_hdr ) ;
}
static inline __u8 info_blk_hdr__npar ( enum diag204_format type , void * hdr )
{
if ( type = = INFO_SIMPLE )
return ( ( struct info_blk_hdr * ) hdr ) - > npar ;
else /* INFO_EXT */
return ( ( struct x_info_blk_hdr * ) hdr ) - > npar ;
}
static inline __u8 info_blk_hdr__flags ( enum diag204_format type , void * hdr )
{
if ( type = = INFO_SIMPLE )
return ( ( struct info_blk_hdr * ) hdr ) - > flags ;
else /* INFO_EXT */
return ( ( struct x_info_blk_hdr * ) hdr ) - > flags ;
}
static inline __u16 info_blk_hdr__pcpus ( enum diag204_format type , void * hdr )
{
if ( type = = INFO_SIMPLE )
return ( ( struct info_blk_hdr * ) hdr ) - > phys_cpus ;
else /* INFO_EXT */
return ( ( struct x_info_blk_hdr * ) hdr ) - > phys_cpus ;
}
/* Partition header */
struct part_hdr {
__u8 pn ;
__u8 cpus ;
char reserved [ 6 ] ;
char part_name [ LPAR_NAME_LEN ] ;
} __attribute__ ( ( packed ) ) ;
struct x_part_hdr {
__u8 pn ;
__u8 cpus ;
__u8 rcpus ;
__u8 pflag ;
__u32 mlu ;
char part_name [ LPAR_NAME_LEN ] ;
char lpc_name [ 8 ] ;
char os_name [ 8 ] ;
__u64 online_cs ;
__u64 online_es ;
__u8 upid ;
char reserved1 [ 3 ] ;
__u32 group_mlu ;
char group_name [ 8 ] ;
char reserved2 [ 32 ] ;
} __attribute__ ( ( packed ) ) ;
static inline int part_hdr__size ( enum diag204_format type )
{
if ( type = = INFO_SIMPLE )
return sizeof ( struct part_hdr ) ;
else /* INFO_EXT */
return sizeof ( struct x_part_hdr ) ;
}
static inline __u8 part_hdr__rcpus ( enum diag204_format type , void * hdr )
{
if ( type = = INFO_SIMPLE )
return ( ( struct part_hdr * ) hdr ) - > cpus ;
else /* INFO_EXT */
return ( ( struct x_part_hdr * ) hdr ) - > rcpus ;
}
static inline void part_hdr__part_name ( enum diag204_format type , void * hdr ,
char * name )
{
if ( type = = INFO_SIMPLE )
memcpy ( name , ( ( struct part_hdr * ) hdr ) - > part_name ,
LPAR_NAME_LEN ) ;
else /* INFO_EXT */
memcpy ( name , ( ( struct x_part_hdr * ) hdr ) - > part_name ,
LPAR_NAME_LEN ) ;
EBCASC ( name , LPAR_NAME_LEN ) ;
name [ LPAR_NAME_LEN ] = 0 ;
2009-12-18 17:43:27 +01:00
strim ( name ) ;
2006-06-23 02:05:06 -07:00
}
struct cpu_info {
__u16 cpu_addr ;
char reserved1 [ 2 ] ;
__u8 ctidx ;
__u8 cflag ;
__u16 weight ;
__u64 acc_time ;
__u64 lp_time ;
} __attribute__ ( ( packed ) ) ;
struct x_cpu_info {
__u16 cpu_addr ;
char reserved1 [ 2 ] ;
__u8 ctidx ;
__u8 cflag ;
__u16 weight ;
__u64 acc_time ;
__u64 lp_time ;
__u16 min_weight ;
__u16 cur_weight ;
__u16 max_weight ;
char reseved2 [ 2 ] ;
__u64 online_time ;
__u64 wait_time ;
__u32 pma_weight ;
__u32 polar_weight ;
char reserved3 [ 40 ] ;
} __attribute__ ( ( packed ) ) ;
/* CPU info block */
static inline int cpu_info__size ( enum diag204_format type )
{
if ( type = = INFO_SIMPLE )
return sizeof ( struct cpu_info ) ;
else /* INFO_EXT */
return sizeof ( struct x_cpu_info ) ;
}
static inline __u8 cpu_info__ctidx ( enum diag204_format type , void * hdr )
{
if ( type = = INFO_SIMPLE )
return ( ( struct cpu_info * ) hdr ) - > ctidx ;
else /* INFO_EXT */
return ( ( struct x_cpu_info * ) hdr ) - > ctidx ;
}
static inline __u16 cpu_info__cpu_addr ( enum diag204_format type , void * hdr )
{
if ( type = = INFO_SIMPLE )
return ( ( struct cpu_info * ) hdr ) - > cpu_addr ;
else /* INFO_EXT */
return ( ( struct x_cpu_info * ) hdr ) - > cpu_addr ;
}
static inline __u64 cpu_info__acc_time ( enum diag204_format type , void * hdr )
{
if ( type = = INFO_SIMPLE )
return ( ( struct cpu_info * ) hdr ) - > acc_time ;
else /* INFO_EXT */
return ( ( struct x_cpu_info * ) hdr ) - > acc_time ;
}
static inline __u64 cpu_info__lp_time ( enum diag204_format type , void * hdr )
{
if ( type = = INFO_SIMPLE )
return ( ( struct cpu_info * ) hdr ) - > lp_time ;
else /* INFO_EXT */
return ( ( struct x_cpu_info * ) hdr ) - > lp_time ;
}
static inline __u64 cpu_info__online_time ( enum diag204_format type , void * hdr )
{
if ( type = = INFO_SIMPLE )
return 0 ; /* online_time not available in simple info */
else /* INFO_EXT */
return ( ( struct x_cpu_info * ) hdr ) - > online_time ;
}
/* Physical header */
struct phys_hdr {
char reserved1 [ 1 ] ;
__u8 cpus ;
char reserved2 [ 6 ] ;
char mgm_name [ 8 ] ;
} __attribute__ ( ( packed ) ) ;
struct x_phys_hdr {
char reserved1 [ 1 ] ;
__u8 cpus ;
char reserved2 [ 6 ] ;
char mgm_name [ 8 ] ;
char reserved3 [ 80 ] ;
} __attribute__ ( ( packed ) ) ;
static inline int phys_hdr__size ( enum diag204_format type )
{
if ( type = = INFO_SIMPLE )
return sizeof ( struct phys_hdr ) ;
else /* INFO_EXT */
return sizeof ( struct x_phys_hdr ) ;
}
static inline __u8 phys_hdr__cpus ( enum diag204_format type , void * hdr )
{
if ( type = = INFO_SIMPLE )
return ( ( struct phys_hdr * ) hdr ) - > cpus ;
else /* INFO_EXT */
return ( ( struct x_phys_hdr * ) hdr ) - > cpus ;
}
/* Physical CPU info block */
struct phys_cpu {
__u16 cpu_addr ;
char reserved1 [ 2 ] ;
__u8 ctidx ;
char reserved2 [ 3 ] ;
__u64 mgm_time ;
char reserved3 [ 8 ] ;
} __attribute__ ( ( packed ) ) ;
struct x_phys_cpu {
__u16 cpu_addr ;
char reserved1 [ 2 ] ;
__u8 ctidx ;
char reserved2 [ 3 ] ;
__u64 mgm_time ;
char reserved3 [ 80 ] ;
} __attribute__ ( ( packed ) ) ;
static inline int phys_cpu__size ( enum diag204_format type )
{
if ( type = = INFO_SIMPLE )
return sizeof ( struct phys_cpu ) ;
else /* INFO_EXT */
return sizeof ( struct x_phys_cpu ) ;
}
static inline __u16 phys_cpu__cpu_addr ( enum diag204_format type , void * hdr )
{
if ( type = = INFO_SIMPLE )
return ( ( struct phys_cpu * ) hdr ) - > cpu_addr ;
else /* INFO_EXT */
return ( ( struct x_phys_cpu * ) hdr ) - > cpu_addr ;
}
static inline __u64 phys_cpu__mgm_time ( enum diag204_format type , void * hdr )
{
if ( type = = INFO_SIMPLE )
return ( ( struct phys_cpu * ) hdr ) - > mgm_time ;
else /* INFO_EXT */
return ( ( struct x_phys_cpu * ) hdr ) - > mgm_time ;
}
static inline __u64 phys_cpu__ctidx ( enum diag204_format type , void * hdr )
{
if ( type = = INFO_SIMPLE )
return ( ( struct phys_cpu * ) hdr ) - > ctidx ;
else /* INFO_EXT */
return ( ( struct x_phys_cpu * ) hdr ) - > ctidx ;
}
/* Diagnose 204 functions */
2015-08-20 17:28:44 +02:00
static inline int __diag204 ( unsigned long subcode , unsigned long size , void * addr )
2006-06-23 02:05:06 -07:00
{
register unsigned long _subcode asm ( " 0 " ) = subcode ;
register unsigned long _size asm ( " 1 " ) = size ;
2006-09-28 16:56:43 +02:00
asm volatile (
" diag %2,%0,0x204 \n "
" 0: \n "
EX_TABLE ( 0 b , 0 b )
: " +d " ( _subcode ) , " +d " ( _size ) : " d " ( addr ) : " memory " ) ;
2006-06-23 02:05:06 -07:00
if ( _subcode )
return - 1 ;
2006-09-28 16:56:43 +02:00
return _size ;
2006-06-23 02:05:06 -07:00
}
2015-08-20 17:28:44 +02:00
static int diag204 ( unsigned long subcode , unsigned long size , void * addr )
{
diag_stat_inc ( DIAG_STAT_X204 ) ;
return __diag204 ( subcode , size , addr ) ;
}
2006-06-23 02:05:06 -07:00
/*
* For the old diag subcode 4 with simple data format we have to use real
* memory . If we use subcode 6 or 7 with extended data format , we can ( and
* should ) use vmalloc , since we need a lot of memory in that case . Currently
* up to 93 pages !
*/
static void diag204_free_buffer ( void )
{
if ( ! diag204_buf )
return ;
if ( diag204_buf_vmalloc ) {
vfree ( diag204_buf_vmalloc ) ;
diag204_buf_vmalloc = NULL ;
} else {
free_pages ( ( unsigned long ) diag204_buf , 0 ) ;
}
diag204_buf = NULL ;
}
2010-05-17 10:00:20 +02:00
static void * page_align_ptr ( void * ptr )
{
return ( void * ) PAGE_ALIGN ( ( unsigned long ) ptr ) ;
}
2006-06-23 02:05:06 -07:00
static void * diag204_alloc_vbuf ( int pages )
{
/* The buffer has to be page aligned! */
diag204_buf_vmalloc = vmalloc ( PAGE_SIZE * ( pages + 1 ) ) ;
if ( ! diag204_buf_vmalloc )
return ERR_PTR ( - ENOMEM ) ;
2010-05-17 10:00:20 +02:00
diag204_buf = page_align_ptr ( diag204_buf_vmalloc ) ;
2006-06-23 02:05:06 -07:00
diag204_buf_pages = pages ;
return diag204_buf ;
}
static void * diag204_alloc_rbuf ( void )
{
diag204_buf = ( void * ) __get_free_pages ( GFP_KERNEL , 0 ) ;
2006-12-15 17:18:10 +01:00
if ( ! diag204_buf )
2006-06-23 02:05:06 -07:00
return ERR_PTR ( - ENOMEM ) ;
diag204_buf_pages = 1 ;
return diag204_buf ;
}
static void * diag204_get_buffer ( enum diag204_format fmt , int * pages )
{
if ( diag204_buf ) {
* pages = diag204_buf_pages ;
return diag204_buf ;
}
if ( fmt = = INFO_SIMPLE ) {
* pages = 1 ;
return diag204_alloc_rbuf ( ) ;
} else { /* INFO_EXT */
2006-09-28 16:55:28 +02:00
* pages = diag204 ( ( unsigned long ) SUBC_RSI |
( unsigned long ) INFO_EXT , 0 , NULL ) ;
2006-06-23 02:05:06 -07:00
if ( * pages < = 0 )
return ERR_PTR ( - ENOSYS ) ;
else
return diag204_alloc_vbuf ( * pages ) ;
}
}
/*
* diag204_probe ( ) has to find out , which type of diagnose 204 implementation
* we have on our machine . Currently there are three possible scanarios :
* - subcode 4 + simple data format ( only one page )
* - subcode 4 - 6 + extended data format
* - subcode 4 - 7 + extended data format
*
* Subcode 5 is used to retrieve the size of the data , provided by subcodes
* 6 and 7. Subcode 7 basically has the same function as subcode 6. In addition
* to subcode 6 it provides also information about secondary cpus .
* In order to get as much information as possible , we first try
* subcode 7 , then 6 and if both fail , we use subcode 4.
*/
static int diag204_probe ( void )
{
void * buf ;
int pages , rc ;
buf = diag204_get_buffer ( INFO_EXT , & pages ) ;
if ( ! IS_ERR ( buf ) ) {
2006-09-20 15:58:47 +02:00
if ( diag204 ( ( unsigned long ) SUBC_STIB7 |
( unsigned long ) INFO_EXT , pages , buf ) > = 0 ) {
2006-06-23 02:05:06 -07:00
diag204_store_sc = SUBC_STIB7 ;
diag204_info_type = INFO_EXT ;
goto out ;
}
2006-09-20 15:58:47 +02:00
if ( diag204 ( ( unsigned long ) SUBC_STIB6 |
( unsigned long ) INFO_EXT , pages , buf ) > = 0 ) {
2009-10-14 12:43:44 +02:00
diag204_store_sc = SUBC_STIB6 ;
2006-06-23 02:05:06 -07:00
diag204_info_type = INFO_EXT ;
goto out ;
}
diag204_free_buffer ( ) ;
}
/* subcodes 6 and 7 failed, now try subcode 4 */
buf = diag204_get_buffer ( INFO_SIMPLE , & pages ) ;
if ( IS_ERR ( buf ) ) {
rc = PTR_ERR ( buf ) ;
goto fail_alloc ;
}
2006-09-20 15:58:47 +02:00
if ( diag204 ( ( unsigned long ) SUBC_STIB4 |
( unsigned long ) INFO_SIMPLE , pages , buf ) > = 0 ) {
2006-06-23 02:05:06 -07:00
diag204_store_sc = SUBC_STIB4 ;
diag204_info_type = INFO_SIMPLE ;
goto out ;
} else {
rc = - ENOSYS ;
goto fail_store ;
}
out :
rc = 0 ;
fail_store :
diag204_free_buffer ( ) ;
fail_alloc :
return rc ;
}
2010-05-17 10:00:20 +02:00
static int diag204_do_store ( void * buf , int pages )
{
int rc ;
rc = diag204 ( ( unsigned long ) diag204_store_sc |
( unsigned long ) diag204_info_type , pages , buf ) ;
return rc < 0 ? - ENOSYS : 0 ;
}
2006-06-23 02:05:06 -07:00
static void * diag204_store ( void )
{
void * buf ;
2010-05-17 10:00:20 +02:00
int pages , rc ;
2006-06-23 02:05:06 -07:00
buf = diag204_get_buffer ( diag204_info_type , & pages ) ;
if ( IS_ERR ( buf ) )
goto out ;
2010-05-17 10:00:20 +02:00
rc = diag204_do_store ( buf , pages ) ;
if ( rc )
return ERR_PTR ( rc ) ;
2006-06-23 02:05:06 -07:00
out :
return buf ;
}
/* Diagnose 224 functions */
2007-05-31 17:38:01 +02:00
static int diag224 ( void * ptr )
2006-06-23 02:05:06 -07:00
{
2010-02-26 22:37:41 +01:00
int rc = - EOPNOTSUPP ;
2007-05-31 17:38:01 +02:00
2015-08-20 17:28:44 +02:00
diag_stat_inc ( DIAG_STAT_X224 ) ;
2007-05-31 17:38:01 +02:00
asm volatile (
" diag %1,%2,0x224 \n "
" 0: lhi %0,0x0 \n "
" 1: \n "
EX_TABLE ( 0 b , 1 b )
: " +d " ( rc ) : " d " ( 0 ) , " d " ( ptr ) : " memory " ) ;
return rc ;
2006-06-23 02:05:06 -07:00
}
static int diag224_get_name_table ( void )
{
/* memory must be below 2GB */
diag224_cpu_names = kmalloc ( PAGE_SIZE , GFP_KERNEL | GFP_DMA ) ;
if ( ! diag224_cpu_names )
return - ENOMEM ;
2007-05-31 17:38:01 +02:00
if ( diag224 ( diag224_cpu_names ) ) {
kfree ( diag224_cpu_names ) ;
2010-02-26 22:37:41 +01:00
return - EOPNOTSUPP ;
2007-05-31 17:38:01 +02:00
}
2006-06-23 02:05:06 -07:00
EBCASC ( diag224_cpu_names + 16 , ( * diag224_cpu_names + 1 ) * 16 ) ;
return 0 ;
}
static void diag224_delete_name_table ( void )
{
kfree ( diag224_cpu_names ) ;
}
static int diag224_idx2name ( int index , char * name )
{
memcpy ( name , diag224_cpu_names + ( ( index + 1 ) * CPU_NAME_LEN ) ,
CPU_NAME_LEN ) ;
name [ CPU_NAME_LEN ] = 0 ;
2009-12-18 17:43:27 +01:00
strim ( name ) ;
2006-06-23 02:05:06 -07:00
return 0 ;
}
2010-05-17 10:00:20 +02:00
struct dbfs_d204_hdr {
u64 len ; /* Length of d204 buffer without header */
u16 version ; /* Version of header */
u8 sc ; /* Used subcode */
char reserved [ 53 ] ;
} __attribute__ ( ( packed ) ) ;
struct dbfs_d204 {
struct dbfs_d204_hdr hdr ; /* 64 byte header */
char buf [ ] ; /* d204 buffer */
} __attribute__ ( ( packed ) ) ;
2011-01-05 12:47:43 +01:00
static int dbfs_d204_create ( void * * data , void * * data_free_ptr , size_t * size )
2010-05-17 10:00:20 +02:00
{
struct dbfs_d204 * d204 ;
int rc , buf_size ;
2011-01-05 12:47:43 +01:00
void * base ;
2010-05-17 10:00:20 +02:00
buf_size = PAGE_SIZE * ( diag204_buf_pages + 1 ) + sizeof ( d204 - > hdr ) ;
2011-05-28 10:36:21 -07:00
base = vzalloc ( buf_size ) ;
2011-01-05 12:47:43 +01:00
if ( ! base )
return - ENOMEM ;
d204 = page_align_ptr ( base + sizeof ( d204 - > hdr ) ) - sizeof ( d204 - > hdr ) ;
rc = diag204_do_store ( d204 - > buf , diag204_buf_pages ) ;
if ( rc ) {
vfree ( base ) ;
return rc ;
2010-05-17 10:00:20 +02:00
}
d204 - > hdr . version = DBFS_D204_HDR_VERSION ;
d204 - > hdr . len = PAGE_SIZE * diag204_buf_pages ;
d204 - > hdr . sc = diag204_store_sc ;
2011-01-05 12:47:43 +01:00
* data = d204 ;
* data_free_ptr = base ;
* size = d204 - > hdr . len + sizeof ( struct dbfs_d204_hdr ) ;
2010-05-17 10:00:20 +02:00
return 0 ;
}
2011-01-05 12:47:43 +01:00
static struct hypfs_dbfs_file dbfs_file_d204 = {
. name = " diag_204 " ,
. data_create = dbfs_d204_create ,
. data_free = vfree ,
2010-05-17 10:00:20 +02:00
} ;
2006-06-23 02:05:06 -07:00
__init int hypfs_diag_init ( void )
{
int rc ;
if ( diag204_probe ( ) ) {
2008-12-25 13:39:39 +01:00
pr_err ( " The hardware system does not support hypfs \n " ) ;
2006-06-23 02:05:06 -07:00
return - ENODATA ;
}
2010-05-17 10:00:20 +02:00
if ( diag204_info_type = = INFO_EXT ) {
2011-01-05 12:47:43 +01:00
rc = hypfs_dbfs_create_file ( & dbfs_file_d204 ) ;
2010-05-17 10:00:20 +02:00
if ( rc )
2010-10-29 16:50:39 +02:00
return rc ;
2010-05-17 10:00:20 +02:00
}
2010-10-29 16:50:39 +02:00
if ( MACHINE_IS_LPAR ) {
rc = diag224_get_name_table ( ) ;
if ( rc ) {
pr_err ( " The hardware system does not provide all "
" functions required by hypfs \n " ) ;
debugfs_remove ( dbfs_d204_file ) ;
return rc ;
}
}
return 0 ;
2006-06-23 02:05:06 -07:00
}
2006-09-20 15:59:12 +02:00
void hypfs_diag_exit ( void )
2006-06-23 02:05:06 -07:00
{
2010-05-17 10:00:20 +02:00
debugfs_remove ( dbfs_d204_file ) ;
2006-06-23 02:05:06 -07:00
diag224_delete_name_table ( ) ;
diag204_free_buffer ( ) ;
2011-01-05 12:47:43 +01:00
hypfs_dbfs_remove_file ( & dbfs_file_d204 ) ;
2006-06-23 02:05:06 -07:00
}
/*
* Functions to create the directory structure
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
2013-07-19 17:10:21 +04:00
static int hypfs_create_cpu_files ( struct dentry * cpus_dir , void * cpu_info )
2006-06-23 02:05:06 -07:00
{
struct dentry * cpu_dir ;
char buffer [ TMP_SIZE ] ;
void * rc ;
snprintf ( buffer , TMP_SIZE , " %d " , cpu_info__cpu_addr ( diag204_info_type ,
cpu_info ) ) ;
2013-07-19 18:05:21 +04:00
cpu_dir = hypfs_mkdir ( cpus_dir , buffer ) ;
rc = hypfs_create_u64 ( cpu_dir , " mgmtime " ,
2006-06-23 02:05:06 -07:00
cpu_info__acc_time ( diag204_info_type , cpu_info ) -
cpu_info__lp_time ( diag204_info_type , cpu_info ) ) ;
if ( IS_ERR ( rc ) )
return PTR_ERR ( rc ) ;
2013-07-19 18:05:21 +04:00
rc = hypfs_create_u64 ( cpu_dir , " cputime " ,
2006-06-23 02:05:06 -07:00
cpu_info__lp_time ( diag204_info_type , cpu_info ) ) ;
if ( IS_ERR ( rc ) )
return PTR_ERR ( rc ) ;
if ( diag204_info_type = = INFO_EXT ) {
2013-07-19 18:05:21 +04:00
rc = hypfs_create_u64 ( cpu_dir , " onlinetime " ,
2006-06-23 02:05:06 -07:00
cpu_info__online_time ( diag204_info_type ,
cpu_info ) ) ;
if ( IS_ERR ( rc ) )
return PTR_ERR ( rc ) ;
}
diag224_idx2name ( cpu_info__ctidx ( diag204_info_type , cpu_info ) , buffer ) ;
2013-07-19 18:05:21 +04:00
rc = hypfs_create_str ( cpu_dir , " type " , buffer ) ;
2013-06-01 11:59:51 +02:00
return PTR_RET ( rc ) ;
2006-06-23 02:05:06 -07:00
}
2013-07-19 17:10:21 +04:00
static void * hypfs_create_lpar_files ( struct dentry * systems_dir , void * part_hdr )
2006-06-23 02:05:06 -07:00
{
struct dentry * cpus_dir ;
struct dentry * lpar_dir ;
char lpar_name [ LPAR_NAME_LEN + 1 ] ;
void * cpu_info ;
int i ;
part_hdr__part_name ( diag204_info_type , part_hdr , lpar_name ) ;
lpar_name [ LPAR_NAME_LEN ] = 0 ;
2013-07-19 18:05:21 +04:00
lpar_dir = hypfs_mkdir ( systems_dir , lpar_name ) ;
2006-06-23 02:05:06 -07:00
if ( IS_ERR ( lpar_dir ) )
return lpar_dir ;
2013-07-19 18:05:21 +04:00
cpus_dir = hypfs_mkdir ( lpar_dir , " cpus " ) ;
2006-06-23 02:05:06 -07:00
if ( IS_ERR ( cpus_dir ) )
return cpus_dir ;
cpu_info = part_hdr + part_hdr__size ( diag204_info_type ) ;
for ( i = 0 ; i < part_hdr__rcpus ( diag204_info_type , part_hdr ) ; i + + ) {
int rc ;
2013-07-19 17:10:21 +04:00
rc = hypfs_create_cpu_files ( cpus_dir , cpu_info ) ;
2006-06-23 02:05:06 -07:00
if ( rc )
return ERR_PTR ( rc ) ;
cpu_info + = cpu_info__size ( diag204_info_type ) ;
}
return cpu_info ;
}
2013-07-19 17:10:21 +04:00
static int hypfs_create_phys_cpu_files ( struct dentry * cpus_dir , void * cpu_info )
2006-06-23 02:05:06 -07:00
{
struct dentry * cpu_dir ;
char buffer [ TMP_SIZE ] ;
void * rc ;
snprintf ( buffer , TMP_SIZE , " %i " , phys_cpu__cpu_addr ( diag204_info_type ,
cpu_info ) ) ;
2013-07-19 18:05:21 +04:00
cpu_dir = hypfs_mkdir ( cpus_dir , buffer ) ;
2006-06-23 02:05:06 -07:00
if ( IS_ERR ( cpu_dir ) )
return PTR_ERR ( cpu_dir ) ;
2013-07-19 18:05:21 +04:00
rc = hypfs_create_u64 ( cpu_dir , " mgmtime " ,
2006-06-23 02:05:06 -07:00
phys_cpu__mgm_time ( diag204_info_type , cpu_info ) ) ;
if ( IS_ERR ( rc ) )
return PTR_ERR ( rc ) ;
diag224_idx2name ( phys_cpu__ctidx ( diag204_info_type , cpu_info ) , buffer ) ;
2013-07-19 18:05:21 +04:00
rc = hypfs_create_str ( cpu_dir , " type " , buffer ) ;
2013-06-01 11:59:51 +02:00
return PTR_RET ( rc ) ;
2006-06-23 02:05:06 -07:00
}
2013-07-19 17:10:21 +04:00
static void * hypfs_create_phys_files ( struct dentry * parent_dir , void * phys_hdr )
2006-06-23 02:05:06 -07:00
{
int i ;
void * cpu_info ;
struct dentry * cpus_dir ;
2013-07-19 18:05:21 +04:00
cpus_dir = hypfs_mkdir ( parent_dir , " cpus " ) ;
2006-06-23 02:05:06 -07:00
if ( IS_ERR ( cpus_dir ) )
return cpus_dir ;
cpu_info = phys_hdr + phys_hdr__size ( diag204_info_type ) ;
for ( i = 0 ; i < phys_hdr__cpus ( diag204_info_type , phys_hdr ) ; i + + ) {
int rc ;
2013-07-19 17:10:21 +04:00
rc = hypfs_create_phys_cpu_files ( cpus_dir , cpu_info ) ;
2006-06-23 02:05:06 -07:00
if ( rc )
return ERR_PTR ( rc ) ;
cpu_info + = phys_cpu__size ( diag204_info_type ) ;
}
return cpu_info ;
}
2013-07-19 17:10:21 +04:00
int hypfs_diag_create_files ( struct dentry * root )
2006-06-23 02:05:06 -07:00
{
struct dentry * systems_dir , * hyp_dir ;
void * time_hdr , * part_hdr ;
int i , rc ;
void * buffer , * ptr ;
buffer = diag204_store ( ) ;
if ( IS_ERR ( buffer ) )
return PTR_ERR ( buffer ) ;
2013-07-19 18:05:21 +04:00
systems_dir = hypfs_mkdir ( root , " systems " ) ;
2006-06-23 02:05:06 -07:00
if ( IS_ERR ( systems_dir ) ) {
rc = PTR_ERR ( systems_dir ) ;
goto err_out ;
}
time_hdr = ( struct x_info_blk_hdr * ) buffer ;
part_hdr = time_hdr + info_blk_hdr__size ( diag204_info_type ) ;
for ( i = 0 ; i < info_blk_hdr__npar ( diag204_info_type , time_hdr ) ; i + + ) {
2013-07-19 17:10:21 +04:00
part_hdr = hypfs_create_lpar_files ( systems_dir , part_hdr ) ;
2006-06-23 02:05:06 -07:00
if ( IS_ERR ( part_hdr ) ) {
rc = PTR_ERR ( part_hdr ) ;
goto err_out ;
}
}
if ( info_blk_hdr__flags ( diag204_info_type , time_hdr ) & LPAR_PHYS_FLG ) {
2013-07-19 17:10:21 +04:00
ptr = hypfs_create_phys_files ( root , part_hdr ) ;
2006-06-23 02:05:06 -07:00
if ( IS_ERR ( ptr ) ) {
rc = PTR_ERR ( ptr ) ;
goto err_out ;
}
}
2013-07-19 18:05:21 +04:00
hyp_dir = hypfs_mkdir ( root , " hyp " ) ;
2006-06-23 02:05:06 -07:00
if ( IS_ERR ( hyp_dir ) ) {
rc = PTR_ERR ( hyp_dir ) ;
goto err_out ;
}
2013-07-19 18:05:21 +04:00
ptr = hypfs_create_str ( hyp_dir , " type " , " LPAR Hypervisor " ) ;
2006-06-23 02:05:06 -07:00
if ( IS_ERR ( ptr ) ) {
rc = PTR_ERR ( ptr ) ;
goto err_out ;
}
rc = 0 ;
err_out :
return rc ;
}