2005-04-16 15:20:36 -07:00
/*
* S / 390 common I / O routines - - blacklisting of specific devices
*
2013-04-30 17:16:17 +02:00
* Copyright IBM Corp . 1999 , 2013
2005-04-16 15:20:36 -07:00
* Author ( s ) : Ingo Adlung ( adlung @ de . ibm . com )
2006-01-14 13:21:04 -08:00
* Cornelia Huck ( cornelia . huck @ de . ibm . com )
2005-04-16 15:20:36 -07:00
* Arnd Bergmann ( arndb @ de . ibm . com )
*/
2008-12-25 13:39:36 +01:00
# define KMSG_COMPONENT "cio"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2005-04-16 15:20:36 -07:00
# include <linux/init.h>
# include <linux/vmalloc.h>
# include <linux/proc_fs.h>
2006-01-06 00:19:24 -08:00
# include <linux/seq_file.h>
2005-04-16 15:20:36 -07:00
# include <linux/ctype.h>
# include <linux/device.h>
# include <asm/uaccess.h>
2013-04-30 17:16:17 +02:00
# include <asm/cio.h>
# include <asm/ipl.h>
2005-04-16 15:20:36 -07:00
# include "blacklist.h"
# include "cio.h"
# include "cio_debug.h"
# include "css.h"
2008-10-10 21:33:06 +02:00
# include "device.h"
2005-04-16 15:20:36 -07:00
/*
* " Blacklisting " of certain devices :
* Device numbers given in the commandline as cio_ignore = . . . won ' t be known
* to Linux .
*
* These can be single devices or ranges of devices
*/
2006-01-06 00:19:25 -08:00
/* 65536 bits for each set to indicate if a devno is blacklisted or not */
2006-01-06 00:19:21 -08:00
# define __BL_DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \
2005-04-16 15:20:36 -07:00
( 8 * sizeof ( long ) ) )
2006-01-06 00:19:25 -08:00
static unsigned long bl_dev [ __MAX_SSID + 1 ] [ __BL_DEV_WORDS ] ;
2005-04-16 15:20:36 -07:00
typedef enum { add , free } range_action ;
/*
* Function : blacklist_range
* ( Un - ) blacklist the devices from - to
*/
2008-05-07 09:22:55 +02:00
static int blacklist_range ( range_action action , unsigned int from_ssid ,
unsigned int to_ssid , unsigned int from ,
unsigned int to , int msgtrigger )
2005-04-16 15:20:36 -07:00
{
2008-05-07 09:22:55 +02:00
if ( ( from_ssid > to_ssid ) | | ( ( from_ssid = = to_ssid ) & & ( from > to ) ) ) {
if ( msgtrigger )
2008-12-25 13:39:36 +01:00
pr_warning ( " 0.%x.%04x to 0.%x.%04x is not a valid "
" range for cio_ignore \n " , from_ssid , from ,
to_ssid , to ) ;
2008-05-07 09:22:55 +02:00
return 1 ;
2005-04-16 15:20:36 -07:00
}
2008-05-07 09:22:55 +02:00
while ( ( from_ssid < to_ssid ) | | ( ( from_ssid = = to_ssid ) & &
( from < = to ) ) ) {
2005-04-16 15:20:36 -07:00
if ( action = = add )
2008-05-07 09:22:55 +02:00
set_bit ( from , bl_dev [ from_ssid ] ) ;
2005-04-16 15:20:36 -07:00
else
2008-05-07 09:22:55 +02:00
clear_bit ( from , bl_dev [ from_ssid ] ) ;
from + + ;
if ( from > __MAX_SUBCHANNEL ) {
from_ssid + + ;
from = 0 ;
}
2005-04-16 15:20:36 -07:00
}
2008-05-07 09:22:55 +02:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2008-05-07 09:22:55 +02:00
static int pure_hex ( char * * cp , unsigned int * val , int min_digit ,
int max_digit , int max_val )
2005-04-16 15:20:36 -07:00
{
2008-05-07 09:22:55 +02:00
int diff ;
2005-04-16 15:20:36 -07:00
2008-05-07 09:22:55 +02:00
diff = 0 ;
* val = 0 ;
2005-04-16 15:20:36 -07:00
2010-10-25 16:10:25 +02:00
while ( diff < = max_digit ) {
int value = hex_to_bin ( * * cp ) ;
2008-05-07 09:22:55 +02:00
2010-10-25 16:10:25 +02:00
if ( value < 0 )
break ;
2008-05-07 09:22:55 +02:00
* val = * val * 16 + value ;
( * cp ) + + ;
diff + + ;
2005-04-16 15:20:36 -07:00
}
2008-05-07 09:22:55 +02:00
if ( ( diff < min_digit ) | | ( diff > max_digit ) | | ( * val > max_val ) )
return 1 ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2008-06-10 10:03:19 +02:00
static int parse_busid ( char * str , unsigned int * cssid , unsigned int * ssid ,
unsigned int * devno , int msgtrigger )
2005-04-16 15:20:36 -07:00
{
2008-05-07 09:22:55 +02:00
char * str_work ;
int val , rc , ret ;
rc = 1 ;
if ( * str = = ' \0 ' )
goto out ;
/* old style */
str_work = str ;
val = simple_strtoul ( str , & str_work , 16 ) ;
if ( * str_work = = ' \0 ' ) {
if ( val < = __MAX_SUBCHANNEL ) {
* devno = val ;
* ssid = 0 ;
* cssid = 0 ;
rc = 0 ;
2005-04-16 15:20:36 -07:00
}
2008-05-07 09:22:55 +02:00
goto out ;
}
2005-04-16 15:20:36 -07:00
2008-05-07 09:22:55 +02:00
/* new style */
str_work = str ;
ret = pure_hex ( & str_work , cssid , 1 , 2 , __MAX_CSSID ) ;
if ( ret | | ( str_work [ 0 ] ! = ' . ' ) )
goto out ;
str_work + + ;
ret = pure_hex ( & str_work , ssid , 1 , 1 , __MAX_SSID ) ;
if ( ret | | ( str_work [ 0 ] ! = ' . ' ) )
goto out ;
str_work + + ;
ret = pure_hex ( & str_work , devno , 4 , 4 , __MAX_SUBCHANNEL ) ;
if ( ret | | ( str_work [ 0 ] ! = ' \0 ' ) )
goto out ;
rc = 0 ;
out :
if ( rc & & msgtrigger )
2008-12-25 13:39:36 +01:00
pr_warning ( " %s is not a valid device for the cio_ignore "
" kernel parameter \n " , str ) ;
2008-05-07 09:22:55 +02:00
return rc ;
}
2005-04-16 15:20:36 -07:00
2008-05-07 09:22:55 +02:00
static int blacklist_parse_parameters ( char * str , range_action action ,
int msgtrigger )
{
2008-06-10 10:03:19 +02:00
unsigned int from_cssid , to_cssid , from_ssid , to_ssid , from , to ;
2008-05-07 09:22:55 +02:00
int rc , totalrc ;
char * parm ;
range_action ra ;
totalrc = 0 ;
while ( ( parm = strsep ( & str , " , " ) ) ) {
rc = 0 ;
ra = action ;
if ( * parm = = ' ! ' ) {
if ( ra = = add )
ra = free ;
else
ra = add ;
parm + + ;
}
if ( strcmp ( parm , " all " ) = = 0 ) {
from_cssid = 0 ;
from_ssid = 0 ;
from = 0 ;
to_cssid = __MAX_CSSID ;
to_ssid = __MAX_SSID ;
to = __MAX_SUBCHANNEL ;
2013-04-30 17:16:17 +02:00
} else if ( strcmp ( parm , " ipldev " ) = = 0 ) {
if ( ipl_info . type = = IPL_TYPE_CCW ) {
from_cssid = 0 ;
from_ssid = ipl_info . data . ccw . dev_id . ssid ;
from = ipl_info . data . ccw . dev_id . devno ;
} else if ( ipl_info . type = = IPL_TYPE_FCP | |
ipl_info . type = = IPL_TYPE_FCP_DUMP ) {
from_cssid = 0 ;
from_ssid = ipl_info . data . fcp . dev_id . ssid ;
from = ipl_info . data . fcp . dev_id . devno ;
} else {
continue ;
}
to_cssid = from_cssid ;
to_ssid = from_ssid ;
to = from ;
2013-04-30 17:17:34 +02:00
} else if ( strcmp ( parm , " condev " ) = = 0 ) {
if ( console_devno = = - 1 )
continue ;
from_cssid = to_cssid = 0 ;
from_ssid = to_ssid = 0 ;
from = to = console_devno ;
2008-05-07 09:22:55 +02:00
} else {
rc = parse_busid ( strsep ( & parm , " - " ) , & from_cssid ,
& from_ssid , & from , msgtrigger ) ;
if ( ! rc ) {
if ( parm ! = NULL )
rc = parse_busid ( parm , & to_cssid ,
& to_ssid , & to ,
msgtrigger ) ;
else {
to_cssid = from_cssid ;
to_ssid = from_ssid ;
to = from ;
}
2005-04-16 15:20:36 -07:00
}
}
2008-05-07 09:22:55 +02:00
if ( ! rc ) {
rc = blacklist_range ( ra , from_ssid , to_ssid , from , to ,
msgtrigger ) ;
if ( rc )
2008-10-10 21:33:06 +02:00
totalrc = - EINVAL ;
2008-05-07 09:22:55 +02:00
} else
2008-10-10 21:33:06 +02:00
totalrc = - EINVAL ;
2005-04-16 15:20:36 -07:00
}
2008-05-07 09:22:55 +02:00
return totalrc ;
2005-04-16 15:20:36 -07:00
}
static int __init
blacklist_setup ( char * str )
{
2008-05-07 09:22:55 +02:00
CIO_MSG_EVENT ( 6 , " Reading blacklist parameters \n " ) ;
if ( blacklist_parse_parameters ( str , add , 1 ) )
return 0 ;
return 1 ;
2005-04-16 15:20:36 -07:00
}
__setup ( " cio_ignore= " , blacklist_setup ) ;
/* Checking if devices are blacklisted */
/*
* Function : is_blacklisted
* Returns 1 if the given devicenumber can be found in the blacklist ,
* otherwise 0.
* Used by validate_subchannel ( )
*/
int
2006-01-06 00:19:25 -08:00
is_blacklisted ( int ssid , int devno )
2005-04-16 15:20:36 -07:00
{
2006-01-06 00:19:25 -08:00
return test_bit ( devno , bl_dev [ ssid ] ) ;
2005-04-16 15:20:36 -07:00
}
# ifdef CONFIG_PROC_FS
/*
* Function : blacklist_parse_proc_parameters
* parse the stuff which is piped to / proc / cio_ignore
*/
2008-05-07 09:22:55 +02:00
static int blacklist_parse_proc_parameters ( char * buf )
2005-04-16 15:20:36 -07:00
{
2008-05-07 09:22:55 +02:00
int rc ;
char * parm ;
parm = strsep ( & buf , " " ) ;
2013-11-26 15:00:37 +01:00
if ( strcmp ( " free " , parm ) = = 0 ) {
2008-05-07 09:22:55 +02:00
rc = blacklist_parse_parameters ( buf , free , 0 ) ;
2013-11-26 15:00:37 +01:00
css_schedule_eval_all_unreg ( 0 ) ;
} else if ( strcmp ( " add " , parm ) = = 0 )
2008-05-07 09:22:55 +02:00
rc = blacklist_parse_parameters ( buf , add , 0 ) ;
2008-10-10 21:33:06 +02:00
else if ( strcmp ( " purge " , parm ) = = 0 )
return ccw_purge_blacklisted ( ) ;
2008-05-07 09:22:55 +02:00
else
2008-10-10 21:33:06 +02:00
return - EINVAL ;
2005-04-16 15:20:36 -07:00
2008-05-07 09:22:55 +02:00
return rc ;
2005-04-16 15:20:36 -07:00
}
2006-01-06 00:19:24 -08:00
/* Iterator struct for all devices. */
struct ccwdev_iter {
int devno ;
2006-01-06 00:19:25 -08:00
int ssid ;
2006-01-06 00:19:24 -08:00
int in_range ;
} ;
static void *
cio_ignore_proc_seq_start ( struct seq_file * s , loff_t * offset )
2005-04-16 15:20:36 -07:00
{
2009-10-06 10:34:00 +02:00
struct ccwdev_iter * iter = s - > private ;
2006-01-06 00:19:24 -08:00
2006-01-06 00:19:25 -08:00
if ( * offset > = ( __MAX_SUBCHANNEL + 1 ) * ( __MAX_SSID + 1 ) )
2006-01-06 00:19:24 -08:00
return NULL ;
2009-10-06 10:34:00 +02:00
memset ( iter , 0 , sizeof ( * iter ) ) ;
2006-01-06 00:19:25 -08:00
iter - > ssid = * offset / ( __MAX_SUBCHANNEL + 1 ) ;
iter - > devno = * offset % ( __MAX_SUBCHANNEL + 1 ) ;
2006-01-06 00:19:24 -08:00
return iter ;
}
static void
cio_ignore_proc_seq_stop ( struct seq_file * s , void * it )
{
}
2005-04-16 15:20:36 -07:00
2006-01-06 00:19:24 -08:00
static void *
cio_ignore_proc_seq_next ( struct seq_file * s , void * it , loff_t * offset )
{
struct ccwdev_iter * iter ;
2006-01-06 00:19:25 -08:00
if ( * offset > = ( __MAX_SUBCHANNEL + 1 ) * ( __MAX_SSID + 1 ) )
2006-01-06 00:19:24 -08:00
return NULL ;
2006-01-06 00:19:26 -08:00
iter = it ;
2006-01-06 00:19:25 -08:00
if ( iter - > devno = = __MAX_SUBCHANNEL ) {
iter - > devno = 0 ;
iter - > ssid + + ;
if ( iter - > ssid > __MAX_SSID )
return NULL ;
} else
iter - > devno + + ;
2006-01-06 00:19:24 -08:00
( * offset ) + + ;
return iter ;
2005-04-16 15:20:36 -07:00
}
2006-01-06 00:19:24 -08:00
static int
cio_ignore_proc_seq_show ( struct seq_file * s , void * it )
{
struct ccwdev_iter * iter ;
2006-01-06 00:19:26 -08:00
iter = it ;
2006-01-06 00:19:25 -08:00
if ( ! is_blacklisted ( iter - > ssid , iter - > devno ) )
2006-01-06 00:19:24 -08:00
/* Not blacklisted, nothing to output. */
return 0 ;
if ( ! iter - > in_range ) {
/* First device in range. */
if ( ( iter - > devno = = __MAX_SUBCHANNEL ) | |
2015-04-15 16:18:14 -07:00
! is_blacklisted ( iter - > ssid , iter - > devno + 1 ) ) {
2006-01-06 00:19:24 -08:00
/* Singular device. */
2015-04-15 16:18:14 -07:00
seq_printf ( s , " 0.%x.%04x \n " , iter - > ssid , iter - > devno ) ;
return 0 ;
}
2006-01-06 00:19:24 -08:00
iter - > in_range = 1 ;
2015-04-15 16:18:14 -07:00
seq_printf ( s , " 0.%x.%04x- " , iter - > ssid , iter - > devno ) ;
return 0 ;
2006-01-06 00:19:24 -08:00
}
if ( ( iter - > devno = = __MAX_SUBCHANNEL ) | |
2006-01-06 00:19:25 -08:00
! is_blacklisted ( iter - > ssid , iter - > devno + 1 ) ) {
2006-01-06 00:19:24 -08:00
/* Last device in range. */
iter - > in_range = 0 ;
2015-04-15 16:18:14 -07:00
seq_printf ( s , " 0.%x.%04x \n " , iter - > ssid , iter - > devno ) ;
2006-01-06 00:19:24 -08:00
}
return 0 ;
}
static ssize_t
cio_ignore_write ( struct file * file , const char __user * user_buf ,
size_t user_len , loff_t * offset )
2005-04-16 15:20:36 -07:00
{
char * buf ;
2009-03-26 15:24:16 +01:00
ssize_t rc , ret , i ;
2005-04-16 15:20:36 -07:00
2006-01-06 00:19:24 -08:00
if ( * offset )
return - EINVAL ;
2005-04-16 15:20:36 -07:00
if ( user_len > 65536 )
user_len = 65536 ;
2011-05-28 10:36:21 -07:00
buf = vzalloc ( user_len + 1 ) ; /* maybe better use the stack? */
2005-04-16 15:20:36 -07:00
if ( buf = = NULL )
return - ENOMEM ;
2008-05-07 09:22:55 +02:00
2005-04-16 15:20:36 -07:00
if ( strncpy_from_user ( buf , user_buf , user_len ) < 0 ) {
2008-05-07 09:22:55 +02:00
rc = - EFAULT ;
goto out_free ;
2005-04-16 15:20:36 -07:00
}
2008-05-07 09:22:55 +02:00
i = user_len - 1 ;
while ( ( i > = 0 ) & & ( isspace ( buf [ i ] ) | | ( buf [ i ] = = 0 ) ) ) {
buf [ i ] = ' \0 ' ;
i - - ;
}
ret = blacklist_parse_proc_parameters ( buf ) ;
if ( ret )
2008-10-10 21:33:06 +02:00
rc = ret ;
2008-05-07 09:22:55 +02:00
else
rc = user_len ;
2005-04-16 15:20:36 -07:00
2008-05-07 09:22:55 +02:00
out_free :
2005-04-16 15:20:36 -07:00
vfree ( buf ) ;
2008-05-07 09:22:55 +02:00
return rc ;
2005-04-16 15:20:36 -07:00
}
2008-01-26 14:11:29 +01:00
static const struct seq_operations cio_ignore_proc_seq_ops = {
2006-01-06 00:19:24 -08:00
. start = cio_ignore_proc_seq_start ,
. stop = cio_ignore_proc_seq_stop ,
. next = cio_ignore_proc_seq_next ,
. show = cio_ignore_proc_seq_show ,
} ;
static int
cio_ignore_proc_open ( struct inode * inode , struct file * file )
{
2009-10-06 10:34:00 +02:00
return seq_open_private ( file , & cio_ignore_proc_seq_ops ,
sizeof ( struct ccwdev_iter ) ) ;
2006-01-06 00:19:24 -08:00
}
2007-02-12 00:55:34 -08:00
static const struct file_operations cio_ignore_proc_fops = {
2006-01-06 00:19:24 -08:00
. open = cio_ignore_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
2009-10-06 10:34:00 +02:00
. release = seq_release_private ,
2006-01-06 00:19:24 -08:00
. write = cio_ignore_write ,
} ;
2005-04-16 15:20:36 -07:00
static int
cio_ignore_proc_init ( void )
{
struct proc_dir_entry * entry ;
2008-04-29 01:02:20 -07:00
entry = proc_create ( " cio_ignore " , S_IFREG | S_IRUGO | S_IWUSR , NULL ,
& cio_ignore_proc_fops ) ;
2005-04-16 15:20:36 -07:00
if ( ! entry )
2006-04-10 22:53:45 -07:00
return - ENOENT ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
__initcall ( cio_ignore_proc_init ) ;
# endif /* CONFIG_PROC_FS */