/*
* File...........: linux/drivers/s390/block/dasd_devmap.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Horst Hummel <Horst.Hummel@de.ibm.com>
* Carsten Otte <Cotte@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
*
* Device mapping and dasd= parameter parsing functions. All devmap
* functions may not be called from interrupt context. In particular
* dasd_get_device is a no-no from interrupt context.
*
*/
#include <linux/ctype.h>
#include <linux/init.h>
#include <linux/module.h>
#include <asm/debug.h>
#include <asm/uaccess.h>
/* This is ugly... */
#define PRINTK_HEADER "dasd_devmap:"
#include "dasd_int.h"
kmem_cache_t *dasd_page_cache;
EXPORT_SYMBOL_GPL(dasd_page_cache);
/*
* dasd_devmap_t is used to store the features and the relation
* between device number and device index. To find a dasd_devmap_t
* that corresponds to a device number of a device index each
* dasd_devmap_t is added to two linked lists, one to search by
* the device number and one to search by the device index. As
* soon as big minor numbers are available the device index list
* can be removed since the device number will then be identical
* to the device index.
*/
struct dasd_devmap {
struct list_head list;
char bus_id[BUS_ID_SIZE];
unsigned int devindex;
unsigned short features;
struct dasd_device *device;
struct dasd_uid uid;
};
/*
* dasd_servermap is used to store the server_id of all storage servers
* accessed by DASD device driver.
*/
struct dasd_servermap {
struct list_head list;
struct server_id {
char vendor[4];
char serial[15];
} sid;
};
static struct list_head dasd_serverlist;
/*
* Parameter parsing functions for dasd= parameter. The syntax is:
* <devno> : (0x)?[0-9a-fA-F]+
* <busid> : [0-0a-f]\.[0-9a-f]\.(0x)?[0-9a-fA-F]+
* <feature> : ro
* <feature_list> : \(<feature>(:<feature>)*\)
* <devno-range> : <devno>(-<devno>)?<feature_list>?
* <busid-range> : <busid>(-<busid>)?<feature_list>?
* <devices> : <devno-range>|<busid-range>
* <dasd_module> : dasd_diag_mod|dasd_eckd_mod|dasd_fba_mod
*
* <dasd> : autodetect|probeonly|<devices>(,<devices>)*
*/
int dasd_probeonly = 0; /* is true, when probeonly mode is active */
int dasd_autodetect = 0; /* is true, when autodetection is active */
int dasd_nopav = 0; /* is true, when PAV is disabled */
EXPORT_SYMBOL_GPL(dasd_nopav);
/*
* char *dasd[] is intended to hold the ranges supplied by the dasd= statement
* it is named 'dasd' to directly be filled by insmod with the comma separated
* strings when running as a module.
*/
static char *dasd[256];
module_param_array(dasd, charp, NULL, 0);
/*
* Single spinlock to protect devmap and servermap structures and lists.
*/
static DEFINE_SPINLOCK(dasd_devmap_lock);
/*
* Hash lists for devmap structures.
*/
static struct list_head dasd_hashlists[256];
int dasd_max_devindex;
static struct dasd_devmap *dasd_add_busid(char *, int);
static inline int
dasd_hash_busid(char *bus_id)
{
int hash, i;
hash = 0;
for (i = 0; (i < BUS_ID_SIZE) && *bus_id; i++, bus_id++)
hash += *bus_id;
return hash & 0xff;
}
#ifndef MODULE
/*
* The parameter parsing functions for builtin-drivers are called
* before kmalloc works. Store the pointers to the parameters strings
* into dasd[] for later processing.
*/
static int __init
dasd_call_setup(char *str)
{
static int count = 0;
if (count < 256)
dasd[count++] = str;
return 1;
}
__setup ("dasd=", dasd_call_setup);
#endif /* #ifndef MODULE */
/*
* Read a device busid/devno from a string.
*/
static inline int
dasd_busid(char **str, int *id0, int *id1, int *devno)
{
int val, old_style;
/* 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 */
return -EINVAL;
val = simple_strtoul(*str, str, 16);
if (old_style || (*str)[0] != '.') {
*id0 = *id1 = 0;
if (val < 0 || val > 0xffff)
return -EINVAL;
*devno = val;
return 0;
}
/* New style x.y.z busid */
if (val < 0 || val > 0xff)
return -EINVAL;
*id0 = val;
(*str)++;
if (!isxdigit((*str)[0])) /* We require at least one hex digit */
return -EINVAL;
val = simple_strtoul(*str, str, 16);
if (val < 0 || val > 0xff || (*str)++[0] != '.')
return -EINVAL;
*id1 = val;
if (!isxdigit((*str)[0])) /* We require at least one hex digit */
return -EINVAL;
val = simple_strtoul(*str, str, 16);
if (val