2005-04-17 02:20:36 +04:00
/*
* drivers / s390 / cio / blacklist . c
* S / 390 common I / O routines - - blacklisting of specific devices
*
* Copyright ( C ) 1999 - 2002 IBM Deutschland Entwicklung GmbH ,
* IBM Corporation
* Author ( s ) : Ingo Adlung ( adlung @ de . ibm . com )
2006-01-15 00:21:04 +03:00
* Cornelia Huck ( cornelia . huck @ de . ibm . com )
2005-04-17 02:20:36 +04:00
* Arnd Bergmann ( arndb @ de . ibm . com )
*/
2008-12-25 15:39:36 +03:00
# define KMSG_COMPONENT "cio"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
# include <linux/vmalloc.h>
# include <linux/slab.h>
# include <linux/proc_fs.h>
2006-01-06 11:19:24 +03:00
# include <linux/seq_file.h>
2005-04-17 02:20:36 +04:00
# include <linux/ctype.h>
# include <linux/device.h>
# include <asm/cio.h>
# include <asm/uaccess.h>
# include "blacklist.h"
# include "cio.h"
# include "cio_debug.h"
# include "css.h"
2008-10-10 23:33:06 +04:00
# include "device.h"
2005-04-17 02:20:36 +04: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 11:19:25 +03:00
/* 65536 bits for each set to indicate if a devno is blacklisted or not */
2006-01-06 11:19:21 +03:00
# define __BL_DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \
2005-04-17 02:20:36 +04:00
( 8 * sizeof ( long ) ) )
2006-01-06 11:19:25 +03:00
static unsigned long bl_dev [ __MAX_SSID + 1 ] [ __BL_DEV_WORDS ] ;
2005-04-17 02:20:36 +04:00
typedef enum { add , free } range_action ;
/*
* Function : blacklist_range
* ( Un - ) blacklist the devices from - to
*/
2008-05-07 11:22:55 +04: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-17 02:20:36 +04:00
{
2008-05-07 11:22:55 +04:00
if ( ( from_ssid > to_ssid ) | | ( ( from_ssid = = to_ssid ) & & ( from > to ) ) ) {
if ( msgtrigger )
2008-12-25 15:39:36 +03: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 11:22:55 +04:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
2008-05-07 11:22:55 +04:00
while ( ( from_ssid < to_ssid ) | | ( ( from_ssid = = to_ssid ) & &
( from < = to ) ) ) {
2005-04-17 02:20:36 +04:00
if ( action = = add )
2008-05-07 11:22:55 +04:00
set_bit ( from , bl_dev [ from_ssid ] ) ;
2005-04-17 02:20:36 +04:00
else
2008-05-07 11:22:55 +04:00
clear_bit ( from , bl_dev [ from_ssid ] ) ;
from + + ;
if ( from > __MAX_SUBCHANNEL ) {
from_ssid + + ;
from = 0 ;
}
2005-04-17 02:20:36 +04:00
}
2008-05-07 11:22:55 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-05-07 11:22:55 +04:00
static int pure_hex ( char * * cp , unsigned int * val , int min_digit ,
int max_digit , int max_val )
2005-04-17 02:20:36 +04:00
{
2008-05-07 11:22:55 +04:00
int diff ;
unsigned int value ;
2005-04-17 02:20:36 +04:00
2008-05-07 11:22:55 +04:00
diff = 0 ;
* val = 0 ;
2005-04-17 02:20:36 +04:00
2008-05-07 11:22:55 +04:00
while ( isxdigit ( * * cp ) & & ( diff < = max_digit ) ) {
if ( isdigit ( * * cp ) )
value = * * cp - ' 0 ' ;
else
value = tolower ( * * cp ) - ' a ' + 10 ;
* val = * val * 16 + value ;
( * cp ) + + ;
diff + + ;
2005-04-17 02:20:36 +04:00
}
2008-05-07 11:22:55 +04:00
if ( ( diff < min_digit ) | | ( diff > max_digit ) | | ( * val > max_val ) )
return 1 ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-06-10 12:03:19 +04:00
static int parse_busid ( char * str , unsigned int * cssid , unsigned int * ssid ,
unsigned int * devno , int msgtrigger )
2005-04-17 02:20:36 +04:00
{
2008-05-07 11:22:55 +04: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-17 02:20:36 +04:00
}
2008-05-07 11:22:55 +04:00
goto out ;
}
2005-04-17 02:20:36 +04:00
2008-05-07 11:22:55 +04: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 15:39:36 +03:00
pr_warning ( " %s is not a valid device for the cio_ignore "
" kernel parameter \n " , str ) ;
2008-05-07 11:22:55 +04:00
return rc ;
}
2005-04-17 02:20:36 +04:00
2008-05-07 11:22:55 +04:00
static int blacklist_parse_parameters ( char * str , range_action action ,
int msgtrigger )
{
2008-06-10 12:03:19 +04:00
unsigned int from_cssid , to_cssid , from_ssid , to_ssid , from , to ;
2008-05-07 11:22:55 +04: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 ;
} 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-17 02:20:36 +04:00
}
}
2008-05-07 11:22:55 +04:00
if ( ! rc ) {
rc = blacklist_range ( ra , from_ssid , to_ssid , from , to ,
msgtrigger ) ;
if ( rc )
2008-10-10 23:33:06 +04:00
totalrc = - EINVAL ;
2008-05-07 11:22:55 +04:00
} else
2008-10-10 23:33:06 +04:00
totalrc = - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2008-05-07 11:22:55 +04:00
return totalrc ;
2005-04-17 02:20:36 +04:00
}
static int __init
blacklist_setup ( char * str )
{
2008-05-07 11:22:55 +04:00
CIO_MSG_EVENT ( 6 , " Reading blacklist parameters \n " ) ;
if ( blacklist_parse_parameters ( str , add , 1 ) )
return 0 ;
return 1 ;
2005-04-17 02:20:36 +04: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 11:19:25 +03:00
is_blacklisted ( int ssid , int devno )
2005-04-17 02:20:36 +04:00
{
2006-01-06 11:19:25 +03:00
return test_bit ( devno , bl_dev [ ssid ] ) ;
2005-04-17 02:20:36 +04:00
}
# ifdef CONFIG_PROC_FS
/*
* Function : blacklist_parse_proc_parameters
* parse the stuff which is piped to / proc / cio_ignore
*/
2008-05-07 11:22:55 +04:00
static int blacklist_parse_proc_parameters ( char * buf )
2005-04-17 02:20:36 +04:00
{
2008-05-07 11:22:55 +04:00
int rc ;
char * parm ;
parm = strsep ( & buf , " " ) ;
if ( strcmp ( " free " , parm ) = = 0 )
rc = blacklist_parse_parameters ( buf , free , 0 ) ;
else if ( strcmp ( " add " , parm ) = = 0 )
rc = blacklist_parse_parameters ( buf , add , 0 ) ;
2008-10-10 23:33:06 +04:00
else if ( strcmp ( " purge " , parm ) = = 0 )
return ccw_purge_blacklisted ( ) ;
2008-05-07 11:22:55 +04:00
else
2008-10-10 23:33:06 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-06-29 16:57:03 +04:00
css_schedule_reprobe ( ) ;
2008-05-07 11:22:55 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
}
2006-01-06 11:19:24 +03:00
/* Iterator struct for all devices. */
struct ccwdev_iter {
int devno ;
2006-01-06 11:19:25 +03:00
int ssid ;
2006-01-06 11:19:24 +03:00
int in_range ;
} ;
static void *
cio_ignore_proc_seq_start ( struct seq_file * s , loff_t * offset )
2005-04-17 02:20:36 +04:00
{
2006-01-06 11:19:24 +03:00
struct ccwdev_iter * iter ;
2006-01-06 11:19:25 +03:00
if ( * offset > = ( __MAX_SUBCHANNEL + 1 ) * ( __MAX_SSID + 1 ) )
2006-01-06 11:19:24 +03:00
return NULL ;
2006-01-06 11:19:26 +03:00
iter = kzalloc ( sizeof ( struct ccwdev_iter ) , GFP_KERNEL ) ;
2006-01-06 11:19:24 +03:00
if ( ! iter )
return ERR_PTR ( - ENOMEM ) ;
2006-01-06 11:19:25 +03:00
iter - > ssid = * offset / ( __MAX_SUBCHANNEL + 1 ) ;
iter - > devno = * offset % ( __MAX_SUBCHANNEL + 1 ) ;
2006-01-06 11:19:24 +03:00
return iter ;
}
static void
cio_ignore_proc_seq_stop ( struct seq_file * s , void * it )
{
if ( ! IS_ERR ( it ) )
kfree ( it ) ;
}
2005-04-17 02:20:36 +04:00
2006-01-06 11:19:24 +03:00
static void *
cio_ignore_proc_seq_next ( struct seq_file * s , void * it , loff_t * offset )
{
struct ccwdev_iter * iter ;
2006-01-06 11:19:25 +03:00
if ( * offset > = ( __MAX_SUBCHANNEL + 1 ) * ( __MAX_SSID + 1 ) )
2006-01-06 11:19:24 +03:00
return NULL ;
2006-01-06 11:19:26 +03:00
iter = it ;
2006-01-06 11:19:25 +03:00
if ( iter - > devno = = __MAX_SUBCHANNEL ) {
iter - > devno = 0 ;
iter - > ssid + + ;
if ( iter - > ssid > __MAX_SSID )
return NULL ;
} else
iter - > devno + + ;
2006-01-06 11:19:24 +03:00
( * offset ) + + ;
return iter ;
2005-04-17 02:20:36 +04:00
}
2006-01-06 11:19:24 +03:00
static int
cio_ignore_proc_seq_show ( struct seq_file * s , void * it )
{
struct ccwdev_iter * iter ;
2006-01-06 11:19:26 +03:00
iter = it ;
2006-01-06 11:19:25 +03:00
if ( ! is_blacklisted ( iter - > ssid , iter - > devno ) )
2006-01-06 11:19:24 +03:00
/* Not blacklisted, nothing to output. */
return 0 ;
if ( ! iter - > in_range ) {
/* First device in range. */
if ( ( iter - > devno = = __MAX_SUBCHANNEL ) | |
2006-01-06 11:19:25 +03:00
! is_blacklisted ( iter - > ssid , iter - > devno + 1 ) )
2006-01-06 11:19:24 +03:00
/* Singular device. */
2006-01-06 11:19:25 +03:00
return seq_printf ( s , " 0.%x.%04x \n " ,
iter - > ssid , iter - > devno ) ;
2006-01-06 11:19:24 +03:00
iter - > in_range = 1 ;
2006-01-06 11:19:25 +03:00
return seq_printf ( s , " 0.%x.%04x- " , iter - > ssid , iter - > devno ) ;
2006-01-06 11:19:24 +03:00
}
if ( ( iter - > devno = = __MAX_SUBCHANNEL ) | |
2006-01-06 11:19:25 +03:00
! is_blacklisted ( iter - > ssid , iter - > devno + 1 ) ) {
2006-01-06 11:19:24 +03:00
/* Last device in range. */
iter - > in_range = 0 ;
2006-01-06 11:19:25 +03:00
return seq_printf ( s , " 0.%x.%04x \n " , iter - > ssid , iter - > devno ) ;
2006-01-06 11:19:24 +03: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-17 02:20:36 +04:00
{
char * buf ;
2009-03-26 17:24:16 +03:00
ssize_t rc , ret , i ;
2005-04-17 02:20:36 +04:00
2006-01-06 11:19:24 +03:00
if ( * offset )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( user_len > 65536 )
user_len = 65536 ;
buf = vmalloc ( user_len + 1 ) ; /* maybe better use the stack? */
if ( buf = = NULL )
return - ENOMEM ;
2008-05-07 11:22:55 +04:00
memset ( buf , 0 , user_len + 1 ) ;
2005-04-17 02:20:36 +04:00
if ( strncpy_from_user ( buf , user_buf , user_len ) < 0 ) {
2008-05-07 11:22:55 +04:00
rc = - EFAULT ;
goto out_free ;
2005-04-17 02:20:36 +04:00
}
2008-05-07 11:22:55 +04: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 23:33:06 +04:00
rc = ret ;
2008-05-07 11:22:55 +04:00
else
rc = user_len ;
2005-04-17 02:20:36 +04:00
2008-05-07 11:22:55 +04:00
out_free :
2005-04-17 02:20:36 +04:00
vfree ( buf ) ;
2008-05-07 11:22:55 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
}
2008-01-26 16:11:29 +03:00
static const struct seq_operations cio_ignore_proc_seq_ops = {
2006-01-06 11:19:24 +03: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 )
{
return seq_open ( file , & cio_ignore_proc_seq_ops ) ;
}
2007-02-12 11:55:34 +03:00
static const struct file_operations cio_ignore_proc_fops = {
2006-01-06 11:19:24 +03:00
. open = cio_ignore_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
. write = cio_ignore_write ,
} ;
2005-04-17 02:20:36 +04:00
static int
cio_ignore_proc_init ( void )
{
struct proc_dir_entry * entry ;
2008-04-29 12:02:20 +04:00
entry = proc_create ( " cio_ignore " , S_IFREG | S_IRUGO | S_IWUSR , NULL ,
& cio_ignore_proc_fops ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry )
2006-04-11 09:53:45 +04:00
return - ENOENT ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
__initcall ( cio_ignore_proc_init ) ;
# endif /* CONFIG_PROC_FS */