Commit 3b1bea01 authored by Jan Höppner's avatar Jan Höppner Committed by Martin Schwidefsky

s390/dasd: Improve parameter list parsing

The function dasd_busid() still uses simple_strtoul() to convert a
string to an integer value. This function is obsolete for quite some
time already and should be replaced.

The whole parameter parsing semantic still relies somewhat on the fact,
that simple_strtoul() parses a string containing literals without
complains and just returns the parsed integer value plus the residual
string. kstrtoint(), however, would return -EINVAL in such a case.
Since we want to get rid of simple_strtoul() and now have a nice dasd[]
containing only single elements, we can clean up and simplify a few
things.

Replace simple_strtoul() with kstrtouint(), improve and simplify the
overall parameter parsing by the following:
- instead of residual strings return proper error codes
- remove dasd_parse_next_element() and decide directly what sort of
  element is being parsed
- if we parse a device or a range of devices, split that element into
  separate bits with a new function
- remove warning about invalid ending as it doesn't apply anymore
- annotate all parsing functions and data that can be freed after
  initialisation with __init and __initdata respectively
- clean up bits and pieces while at it
Reviewed-by: default avatarStefan Haberland <sth@linux.vnet.ibm.com>
Reviewed-by: default avatarSebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: default avatarJan Höppner <hoeppner@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 09762dcb
...@@ -107,7 +107,7 @@ dasd_hash_busid(const char *bus_id) ...@@ -107,7 +107,7 @@ dasd_hash_busid(const char *bus_id)
#ifndef MODULE #ifndef MODULE
static int __init dasd_call_setup(char *opt) static int __init dasd_call_setup(char *opt)
{ {
static int i; static int i __initdata;
char *tmp; char *tmp;
while (i < DASD_MAX_PARAMS) { while (i < DASD_MAX_PARAMS) {
...@@ -129,14 +129,13 @@ __setup ("dasd=", dasd_call_setup); ...@@ -129,14 +129,13 @@ __setup ("dasd=", dasd_call_setup);
/* /*
* Read a device busid/devno from a string. * Read a device busid/devno from a string.
*/ */
static int static int __init dasd_busid(char *str, int *id0, int *id1, int *devno)
dasd_busid(char **str, int *id0, int *id1, int *devno)
{ {
int val, old_style; unsigned int val;
char *tok;
/* Interpret ipldev busid */ /* Interpret ipldev busid */
if (strncmp(DASD_IPLDEV, *str, strlen(DASD_IPLDEV)) == 0) { if (strncmp(DASD_IPLDEV, str, strlen(DASD_IPLDEV)) == 0) {
if (ipl_info.type != IPL_TYPE_CCW) { if (ipl_info.type != IPL_TYPE_CCW) {
pr_err("The IPL device is not a CCW device\n"); pr_err("The IPL device is not a CCW device\n");
return -EINVAL; return -EINVAL;
...@@ -144,63 +143,50 @@ dasd_busid(char **str, int *id0, int *id1, int *devno) ...@@ -144,63 +143,50 @@ dasd_busid(char **str, int *id0, int *id1, int *devno)
*id0 = 0; *id0 = 0;
*id1 = ipl_info.data.ccw.dev_id.ssid; *id1 = ipl_info.data.ccw.dev_id.ssid;
*devno = ipl_info.data.ccw.dev_id.devno; *devno = ipl_info.data.ccw.dev_id.devno;
*str += strlen(DASD_IPLDEV);
return 0; return 0;
} }
/* check for leading '0x' */
old_style = 0; /* Old style 0xXXXX or XXXX */
if ((*str)[0] == '0' && (*str)[1] == 'x') { if (!kstrtouint(str, 16, &val)) {
*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; *id0 = *id1 = 0;
if (val < 0 || val > 0xffff) if (val < 0 || val > 0xffff)
return -EINVAL; return -EINVAL;
*devno = val; *devno = val;
return 0; return 0;
} }
/* New style x.y.z busid */ /* New style x.y.z busid */
if (val < 0 || val > 0xff) tok = strsep(&str, ".");
if (kstrtouint(tok, 16, &val) || val > 0xff)
return -EINVAL; return -EINVAL;
*id0 = val; *id0 = val;
(*str)++;
if (!isxdigit((*str)[0])) /* We require at least one hex digit */ tok = strsep(&str, ".");
return -EINVAL; if (kstrtouint(tok, 16, &val) || val > 0xff)
val = simple_strtoul(*str, str, 16);
if (val < 0 || val > 0xff || (*str)++[0] != '.')
return -EINVAL; return -EINVAL;
*id1 = val; *id1 = val;
if (!isxdigit((*str)[0])) /* We require at least one hex digit */
return -EINVAL; tok = strsep(&str, ".");
val = simple_strtoul(*str, str, 16); if (kstrtouint(tok, 16, &val) || val > 0xffff)
if (val < 0 || val > 0xffff)
return -EINVAL; return -EINVAL;
*devno = val; *devno = val;
return 0; return 0;
} }
/* /*
* Read colon separated list of dasd features. Currently there is * Read colon separated list of dasd features.
* only one: "ro" for read-only devices. The default feature set
* is empty (value 0).
*/ */
static int static int __init dasd_feature_list(char *str)
dasd_feature_list(char *str, char **endp)
{ {
int features, len, rc; int features, len, rc;
features = 0;
rc = 0; rc = 0;
if (*str != '(') {
*endp = str; if (!str)
return DASD_FEATURE_DEFAULT; return DASD_FEATURE_DEFAULT;
}
str++;
features = 0;
while (1) { while (1) {
for (len = 0; for (len = 0;
...@@ -225,15 +211,8 @@ dasd_feature_list(char *str, char **endp) ...@@ -225,15 +211,8 @@ dasd_feature_list(char *str, char **endp)
break; break;
str++; str++;
} }
if (*str != ')') {
pr_warn("A closing parenthesis ')' is missing in the dasd= parameter\n"); return rc ? : features;
rc = -EINVAL;
} else
str++;
*endp = str;
if (rc != 0)
return rc;
return features;
} }
/* /*
...@@ -242,48 +221,38 @@ dasd_feature_list(char *str, char **endp) ...@@ -242,48 +221,38 @@ dasd_feature_list(char *str, char **endp)
* action and return a pointer to the residual string. If the first element * action and return a pointer to the residual string. If the first element
* could not be matched to any keyword then return an error code. * could not be matched to any keyword then return an error code.
*/ */
static char * static int __init dasd_parse_keyword(char *keyword)
dasd_parse_keyword( char *parsestring ) { {
int length = strlen(keyword);
char *nextcomma, *residual_str;
int length;
nextcomma = strchr(parsestring,','); if (strncmp("autodetect", keyword, length) == 0) {
if (nextcomma) {
length = nextcomma - parsestring;
residual_str = nextcomma + 1;
} else {
length = strlen(parsestring);
residual_str = parsestring + length;
}
if (strncmp("autodetect", parsestring, length) == 0) {
dasd_autodetect = 1; dasd_autodetect = 1;
pr_info("The autodetection mode has been activated\n"); pr_info("The autodetection mode has been activated\n");
return residual_str; return 0;
} }
if (strncmp("probeonly", parsestring, length) == 0) { if (strncmp("probeonly", keyword, length) == 0) {
dasd_probeonly = 1; dasd_probeonly = 1;
pr_info("The probeonly mode has been activated\n"); pr_info("The probeonly mode has been activated\n");
return residual_str; return 0;
} }
if (strncmp("nopav", parsestring, length) == 0) { if (strncmp("nopav", keyword, length) == 0) {
if (MACHINE_IS_VM) if (MACHINE_IS_VM)
pr_info("'nopav' is not supported on z/VM\n"); pr_info("'nopav' is not supported on z/VM\n");
else { else {
dasd_nopav = 1; dasd_nopav = 1;
pr_info("PAV support has be deactivated\n"); pr_info("PAV support has be deactivated\n");
} }
return residual_str; return 0;
} }
if (strncmp("nofcx", parsestring, length) == 0) { if (strncmp("nofcx", keyword, length) == 0) {
dasd_nofcx = 1; dasd_nofcx = 1;
pr_info("High Performance FICON support has been " pr_info("High Performance FICON support has been "
"deactivated\n"); "deactivated\n");
return residual_str; return 0;
} }
if (strncmp("fixedbuffers", parsestring, length) == 0) { if (strncmp("fixedbuffers", keyword, length) == 0) {
if (dasd_page_cache) if (dasd_page_cache)
return residual_str; return 0;
dasd_page_cache = dasd_page_cache =
kmem_cache_create("dasd_page_cache", PAGE_SIZE, kmem_cache_create("dasd_page_cache", PAGE_SIZE,
PAGE_SIZE, SLAB_CACHE_DMA, PAGE_SIZE, SLAB_CACHE_DMA,
...@@ -294,73 +263,97 @@ dasd_parse_keyword( char *parsestring ) { ...@@ -294,73 +263,97 @@ dasd_parse_keyword( char *parsestring ) {
else else
DBF_EVENT(DBF_INFO, "%s", DBF_EVENT(DBF_INFO, "%s",
"turning on fixed buffer mode"); "turning on fixed buffer mode");
return residual_str; return 0;
} }
return ERR_PTR(-EINVAL);
return -EINVAL;
} }
/* /*
* Try to interprete the first element on the comma separated parse string * Split a string of a device range into its pieces and return the from, to, and
* as a device number or a range of devices. If the interpretation is * feature parts separately.
* successful, create the matching dasd_devmap entries and return a pointer * e.g.:
* to the residual string. * 0.0.1234-0.0.5678(ro:erplog) -> from: 0.0.1234 to: 0.0.5678 features: ro:erplog
* If interpretation fails or in case of an error, return an error code. * 0.0.8765(raw) -> from: 0.0.8765 to: null features: raw
* 0x4321 -> from: 0x4321 to: null features: null
*/ */
static char * static int __init dasd_evaluate_range_param(char *range, char **from_str,
dasd_parse_range( char *parsestring ) { char **to_str, char **features_str)
{
int rc = 0;
/* Do we have a range or a single device? */
if (strchr(range, '-')) {
*from_str = strsep(&range, "-");
*to_str = strsep(&range, "(");
*features_str = strsep(&range, ")");
} else {
*from_str = strsep(&range, "(");
*features_str = strsep(&range, ")");
}
if (*features_str && !range) {
pr_warn("A closing parenthesis ')' is missing in the dasd= parameter\n");
rc = -EINVAL;
}
return rc;
}
/*
* Try to interprete the range string as a device number or a range of devices.
* If the interpretation is successful, create the matching dasd_devmap entries.
* If interpretation fails or in case of an error, return an error code.
*/
static int __init dasd_parse_range(const char *range)
{
struct dasd_devmap *devmap; struct dasd_devmap *devmap;
int from, from_id0, from_id1; int from, from_id0, from_id1;
int to, to_id0, to_id1; int to, to_id0, to_id1;
int features, rc; int features;
char bus_id[DASD_BUS_ID_SIZE+1], *str; char bus_id[DASD_BUS_ID_SIZE + 1];
char *features_str = NULL;
char *from_str = NULL;
char *to_str = NULL;
size_t len = strlen(range) + 1;
char tmp[len];
strlcpy(tmp, range, len);
if (dasd_evaluate_range_param(tmp, &from_str, &to_str, &features_str))
goto out_err;
if (dasd_busid(from_str, &from_id0, &from_id1, &from))
goto out_err;
str = parsestring;
rc = dasd_busid(&str, &from_id0, &from_id1, &from);
if (rc == 0) {
to = from; to = from;
to_id0 = from_id0; to_id0 = from_id0;
to_id1 = from_id1; to_id1 = from_id1;
if (*str == '-') { if (to_str) {
str++; if (dasd_busid(to_str, &to_id0, &to_id1, &to))
rc = dasd_busid(&str, &to_id0, &to_id1, &to); goto out_err;
if (from_id0 != to_id0 || from_id1 != to_id1 || from > to) {
pr_err("%s is not a valid device range\n", range);
goto out_err;
} }
} }
if (rc == 0 &&
(from_id0 != to_id0 || from_id1 != to_id1 || from > to)) features = dasd_feature_list(features_str);
rc = -EINVAL;
if (rc) {
pr_err("%s is not a valid device range\n", parsestring);
return ERR_PTR(rc);
}
features = dasd_feature_list(str, &str);
if (features < 0) if (features < 0)
return ERR_PTR(-EINVAL); goto out_err;
/* each device in dasd= parameter should be set initially online */ /* each device in dasd= parameter should be set initially online */
features |= DASD_FEATURE_INITIAL_ONLINE; features |= DASD_FEATURE_INITIAL_ONLINE;
while (from <= to) { while (from <= to) {
sprintf(bus_id, "%01x.%01x.%04x", sprintf(bus_id, "%01x.%01x.%04x", from_id0, from_id1, from++);
from_id0, from_id1, from++);
devmap = dasd_add_busid(bus_id, features); devmap = dasd_add_busid(bus_id, features);
if (IS_ERR(devmap)) if (IS_ERR(devmap))
return (char *)devmap; return PTR_ERR(devmap);
} }
if (*str == ',')
return str + 1;
if (*str == '\0')
return str;
pr_warn("The dasd= parameter value %s has an invalid ending\n", str);
return ERR_PTR(-EINVAL);
}
static char * return 0;
dasd_parse_next_element( char *parsestring ) {
char * residual_str; out_err:
residual_str = dasd_parse_keyword(parsestring); return -EINVAL;
if (!IS_ERR(residual_str))
return residual_str;
residual_str = dasd_parse_range(parsestring);
return residual_str;
} }
/* /*
...@@ -369,30 +362,27 @@ dasd_parse_next_element( char *parsestring ) { ...@@ -369,30 +362,27 @@ dasd_parse_next_element( char *parsestring ) {
* keywords and device ranges. The parameters in that list will be stored as * keywords and device ranges. The parameters in that list will be stored as
* separate elementes in dasd[]. * separate elementes in dasd[].
*/ */
int int __init dasd_parse(void)
dasd_parse(void)
{ {
int rc, i; int rc, i;
char *parsestring; char *cur;
rc = 0; rc = 0;
for (i = 0; i < DASD_MAX_PARAMS; i++) { for (i = 0; i < DASD_MAX_PARAMS; i++) {
if (dasd[i] == NULL) cur = dasd[i];
break; if (!cur)
parsestring = dasd[i];
/* loop over the comma separated list in the parsestring */
while (*parsestring) {
parsestring = dasd_parse_next_element(parsestring);
if(IS_ERR(parsestring)) {
rc = PTR_ERR(parsestring);
break; break;
} if (*cur == '\0')
} continue;
if (rc) {
DBF_EVENT(DBF_ALERT, "%s", "invalid range found"); rc = dasd_parse_keyword(cur);
if (rc)
rc = dasd_parse_range(cur);
if (rc)
break; break;
} }
}
return rc; return rc;
} }
......
...@@ -805,7 +805,7 @@ struct dasd_device *dasd_device_from_devindex(int); ...@@ -805,7 +805,7 @@ struct dasd_device *dasd_device_from_devindex(int);
void dasd_add_link_to_gendisk(struct gendisk *, struct dasd_device *); void dasd_add_link_to_gendisk(struct gendisk *, struct dasd_device *);
struct dasd_device *dasd_device_from_gendisk(struct gendisk *); struct dasd_device *dasd_device_from_gendisk(struct gendisk *);
int dasd_parse(void); int dasd_parse(void) __init;
int dasd_busid_known(const char *); int dasd_busid_known(const char *);
/* externals in dasd_gendisk.c */ /* externals in dasd_gendisk.c */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment