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 )
*/
# 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"
/*
* " 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
*/
2007-02-05 23:18:53 +03:00
static void
2006-01-06 11:19:25 +03:00
blacklist_range ( range_action action , unsigned int from , unsigned int to ,
unsigned int ssid )
2005-04-17 02:20:36 +04:00
{
if ( ! to )
to = from ;
2006-01-06 11:19:25 +03:00
if ( from > to | | to > __MAX_SUBCHANNEL | | ssid > __MAX_SSID ) {
2007-07-27 14:29:19 +04:00
printk ( KERN_WARNING " cio: Invalid blacklist range "
2006-01-06 11:19:25 +03:00
" 0.%x.%04x to 0.%x.%04x, skipping \n " ,
ssid , from , ssid , to ) ;
2005-04-17 02:20:36 +04:00
return ;
}
for ( ; from < = to ; from + + ) {
if ( action = = add )
2006-01-06 11:19:25 +03:00
set_bit ( from , bl_dev [ ssid ] ) ;
2005-04-17 02:20:36 +04:00
else
2006-01-06 11:19:25 +03:00
clear_bit ( from , bl_dev [ ssid ] ) ;
2005-04-17 02:20:36 +04:00
}
}
/*
* Function : blacklist_busid
* Get devno / busid from given string .
* Shamelessly grabbed from dasd_devmap . c .
*/
2007-02-05 23:18:53 +03:00
static int
2006-01-06 11:19:25 +03:00
blacklist_busid ( char * * str , int * id0 , int * ssid , int * devno )
2005-04-17 02:20:36 +04:00
{
int val , old_style ;
char * sav ;
sav = * str ;
/* check for leading '0x' */
old_style = 0 ;
if ( ( * str ) [ 0 ] = = ' 0 ' & & ( * str ) [ 1 ] = = ' x ' ) {
* str + = 2 ;
old_style = 1 ;
}
if ( ! isxdigit ( ( * str ) [ 0 ] ) ) /* We require at least one hex digit */
goto confused ;
val = simple_strtoul ( * str , str , 16 ) ;
if ( old_style | | ( * str ) [ 0 ] ! = ' . ' ) {
2006-01-06 11:19:25 +03:00
* id0 = * ssid = 0 ;
2005-04-17 02:20:36 +04:00
if ( val < 0 | | val > 0xffff )
goto confused ;
* devno = val ;
if ( ( * str ) [ 0 ] ! = ' , ' & & ( * str ) [ 0 ] ! = ' - ' & &
( * str ) [ 0 ] ! = ' \n ' & & ( * str ) [ 0 ] ! = ' \0 ' )
goto confused ;
return 0 ;
}
/* New style x.y.z busid */
if ( val < 0 | | val > 0xff )
goto confused ;
* id0 = val ;
( * str ) + + ;
if ( ! isxdigit ( ( * str ) [ 0 ] ) ) /* We require at least one hex digit */
goto confused ;
val = simple_strtoul ( * str , str , 16 ) ;
if ( val < 0 | | val > 0xff | | ( * str ) + + [ 0 ] ! = ' . ' )
goto confused ;
2006-01-06 11:19:25 +03:00
* ssid = val ;
2005-04-17 02:20:36 +04:00
if ( ! isxdigit ( ( * str ) [ 0 ] ) ) /* We require at least one hex digit */
goto confused ;
val = simple_strtoul ( * str , str , 16 ) ;
if ( val < 0 | | val > 0xffff )
goto confused ;
* devno = val ;
if ( ( * str ) [ 0 ] ! = ' , ' & & ( * str ) [ 0 ] ! = ' - ' & &
( * str ) [ 0 ] ! = ' \n ' & & ( * str ) [ 0 ] ! = ' \0 ' )
goto confused ;
return 0 ;
confused :
strsep ( str , " , \n " ) ;
2007-07-27 14:29:19 +04:00
printk ( KERN_WARNING " cio: Invalid cio_ignore parameter '%s' \n " , sav ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
blacklist_parse_parameters ( char * str , range_action action )
{
2007-02-05 23:16:47 +03:00
int from , to , from_id0 , to_id0 , from_ssid , to_ssid ;
2005-04-17 02:20:36 +04:00
while ( * str ! = 0 & & * str ! = ' \n ' ) {
range_action ra = action ;
while ( * str = = ' , ' )
str + + ;
if ( * str = = ' ! ' ) {
ra = ! action ;
+ + str ;
}
/*
* Since we have to parse the proc commands and the
* kernel arguments we have to check four cases
*/
if ( strncmp ( str , " all, " , 4 ) = = 0 | | strcmp ( str , " all " ) = = 0 | |
strncmp ( str , " all \n " , 4 ) = = 0 | | strncmp ( str , " all " , 4 ) = = 0 ) {
2006-01-06 11:19:25 +03:00
int j ;
2005-04-17 02:20:36 +04:00
str + = 3 ;
2006-01-06 11:19:25 +03:00
for ( j = 0 ; j < = __MAX_SSID ; j + + )
blacklist_range ( ra , 0 , __MAX_SUBCHANNEL , j ) ;
2005-04-17 02:20:36 +04:00
} else {
int rc ;
rc = blacklist_busid ( & str , & from_id0 ,
2006-01-06 11:19:25 +03:00
& from_ssid , & from ) ;
2005-04-17 02:20:36 +04:00
if ( rc )
continue ;
to = from ;
to_id0 = from_id0 ;
2006-01-06 11:19:25 +03:00
to_ssid = from_ssid ;
2005-04-17 02:20:36 +04:00
if ( * str = = ' - ' ) {
str + + ;
rc = blacklist_busid ( & str , & to_id0 ,
2006-01-06 11:19:25 +03:00
& to_ssid , & to ) ;
2005-04-17 02:20:36 +04:00
if ( rc )
continue ;
}
if ( * str = = ' - ' ) {
2007-07-27 14:29:19 +04:00
printk ( KERN_WARNING " cio: invalid cio_ignore "
2005-04-17 02:20:36 +04:00
" parameter '%s' \n " ,
strsep ( & str , " , \n " ) ) ;
continue ;
}
2006-01-06 11:19:25 +03:00
if ( ( from_id0 ! = to_id0 ) | |
( from_ssid ! = to_ssid ) ) {
2007-07-27 14:29:19 +04:00
printk ( KERN_WARNING " cio: invalid cio_ignore "
" range %x.%x.%04x-%x.%x.%04x \n " ,
from_id0 , from_ssid , from ,
to_id0 , to_ssid , to ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
2006-01-06 11:19:25 +03:00
blacklist_range ( ra , from , to , to_ssid ) ;
2005-04-17 02:20:36 +04:00
}
}
return 1 ;
}
/* Parsing the commandline for blacklist parameters, e.g. to blacklist
* bus ids 0.0 .1234 , 0.0 .1235 and 0.0 .1236 , you could use any of :
* - cio_ignore = 1234 - 1236
* - cio_ignore = 0x1234 - 0x1235 , 1236
* - cio_ignore = 0x1234 , 1235 - 1236
* - cio_ignore = 1236 cio_ignore = 1234 - 0x1236
* - cio_ignore = 1234 cio_ignore = 1236 cio_ignore = 0x1235
* - cio_ignore = 0.0 .1234 - 0.0 .1236
* - cio_ignore = 0.0 .1234 , 0x1235 , 1236
* - . . .
*/
static int __init
blacklist_setup ( char * str )
{
CIO_MSG_EVENT ( 6 , " Reading blacklist parameters \n " ) ;
return blacklist_parse_parameters ( str , add ) ;
}
__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
*/
2007-02-05 23:18:53 +03:00
static void
2005-04-17 02:20:36 +04:00
blacklist_parse_proc_parameters ( char * buf )
{
if ( strncmp ( buf , " free " , 5 ) = = 0 ) {
blacklist_parse_parameters ( buf + 5 , free ) ;
} else if ( strncmp ( buf , " add " , 4 ) = = 0 ) {
/*
* We don ' t need to check for known devices since
* css_probe_device will handle this correctly .
*/
blacklist_parse_parameters ( buf + 4 , add ) ;
} else {
2007-07-27 14:29:19 +04:00
printk ( KERN_WARNING " cio: cio_ignore: Parse error; \n "
2005-04-17 02:20:36 +04:00
KERN_WARNING " try using 'free all|<devno-range>, "
" <devno-range>,...' \n "
KERN_WARNING " or 'add <devno-range>, "
" <devno-range>,...' \n " ) ;
return ;
}
2006-06-29 16:57:03 +04:00
css_schedule_reprobe ( ) ;
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 ;
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 ;
if ( strncpy_from_user ( buf , user_buf , user_len ) < 0 ) {
vfree ( buf ) ;
return - EFAULT ;
}
buf [ user_len ] = ' \0 ' ;
blacklist_parse_proc_parameters ( buf ) ;
vfree ( buf ) ;
return user_len ;
}
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 ;
entry = create_proc_entry ( " cio_ignore " , S_IFREG | S_IRUGO | S_IWUSR ,
2008-04-29 12:01:44 +04:00
NULL ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry )
2006-04-11 09:53:45 +04:00
return - ENOENT ;
2005-04-17 02:20:36 +04:00
2006-01-06 11:19:24 +03:00
entry - > proc_fops = & cio_ignore_proc_fops ;
2005-04-17 02:20:36 +04:00
2006-04-11 09:53:45 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
__initcall ( cio_ignore_proc_init ) ;
# endif /* CONFIG_PROC_FS */