Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5b890987 authored by Michael Ernst's avatar Michael Ernst Committed by Martin Schwidefsky
Browse files

[S390] cio: Fix parsing mechanism for blacklisted devices.



New format cssid.ssid.devno is now parsed correctly.

Signed-off-by: default avatarMichael Ernst <mernst@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 139b83dd
Loading
Loading
Loading
Loading
+170 −154
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@

#include <asm/cio.h>
#include <asm/uaccess.h>
#include <asm/cio.h>

#include "blacklist.h"
#include "cio.h"
@@ -43,163 +44,169 @@ typedef enum {add, free} range_action;
 * Function: blacklist_range
 * (Un-)blacklist the devices from-to
 */
static void
blacklist_range (range_action action, unsigned int from, unsigned int to,
		 unsigned int ssid)
static int blacklist_range(range_action action, unsigned int from_ssid,
			   unsigned int to_ssid, unsigned int from,
			   unsigned int to, int msgtrigger)
{
	if (!to)
		to = from;

	if (from > to || to > __MAX_SUBCHANNEL || ssid > __MAX_SSID) {
		printk (KERN_WARNING "cio: Invalid blacklist range "
			"0.%x.%04x to 0.%x.%04x, skipping\n",
			ssid, from, ssid, to);
		return;
	if ((from_ssid > to_ssid) || ((from_ssid == to_ssid) && (from > to))) {
		if (msgtrigger)
			printk(KERN_WARNING "cio: Invalid cio_ignore range "
			       "0.%x.%04x-0.%x.%04x\n", from_ssid, from,
			       to_ssid, to);
		return 1;
	}
	for (; from <= to; from++) {

	while ((from_ssid < to_ssid) || ((from_ssid == to_ssid) &&
	       (from <= to))) {
		if (action == add)
			set_bit (from, bl_dev[ssid]);
			set_bit(from, bl_dev[from_ssid]);
		else
			clear_bit (from, bl_dev[ssid]);
			clear_bit(from, bl_dev[from_ssid]);
		from++;
		if (from > __MAX_SUBCHANNEL) {
			from_ssid++;
			from = 0;
		}
	}

/*
 * Function: blacklist_busid
 * Get devno/busid from given string.
 * Shamelessly grabbed from dasd_devmap.c.
 */
static int
blacklist_busid(char **str, int *id0, int *ssid, int *devno)
	return 0;
}

static int pure_hex(char **cp, unsigned int *val, int min_digit,
		    int max_digit, int max_val)
{
	int val, old_style;
	char *sav;
	int diff;
	unsigned int value;

	sav = *str;
	diff = 0;
	*val = 0;

	/* 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] != '.') {
		*id0 = *ssid = 0;
		if (val < 0 || val > 0xffff)
			goto confused;
		*devno = val;
		if ((*str)[0] != ',' && (*str)[0] != '-' &&
		    (*str)[0] != '\n' && (*str)[0] != '\0')
			goto confused;
		return 0;
	while (isxdigit(**cp) && (diff <= max_digit)) {

		if (isdigit(**cp))
			value = **cp - '0';
		else
			value = tolower(**cp) - 'a' + 10;
		*val = *val * 16 + value;
		(*cp)++;
		diff++;
	}
	/* 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;
	*ssid = val;
	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");
	printk(KERN_WARNING "cio: Invalid cio_ignore parameter '%s'\n", sav);

	if ((diff < min_digit) || (diff > max_digit) || (*val > max_val))
		return 1;

	return 0;
}

static int
blacklist_parse_parameters (char *str, range_action action)
static int parse_busid(char *str, int *cssid, int *ssid, int *devno,
		       int msgtrigger)
{
	int from, to, from_id0, to_id0, from_ssid, to_ssid;

	while (*str != 0 && *str != '\n') {
		range_action ra = action;
		while(*str == ',')
			str++;
		if (*str == '!') {
			ra = !action;
			++str;
	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;
		}
		goto out;
	}

		/*
		 * 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) {
			int j;
	/* 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)
		printk(KERN_WARNING "cio: Invalid cio_ignore device '%s'\n",
		       str);

	return rc;
}

			str += 3;
			for (j=0; j <= __MAX_SSID; j++)
				blacklist_range(ra, 0, __MAX_SUBCHANNEL, j);
static int blacklist_parse_parameters(char *str, range_action action,
				      int msgtrigger)
{
	int from_cssid, to_cssid, from_ssid, to_ssid, from, to;
	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 {
			int rc;

			rc = blacklist_busid(&str, &from_id0,
					     &from_ssid, &from);
			if (rc)
				continue;
			to = from;
			to_id0 = from_id0;
			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;
			if (*str == '-') {
				str++;
				rc = blacklist_busid(&str, &to_id0,
						     &to_ssid, &to);
				if (rc)
					continue;
			}
			if (*str == '-') {
				printk(KERN_WARNING "cio: invalid cio_ignore "
					"parameter '%s'\n",
					strsep(&str, ",\n"));
				continue;
					to = from;
				}
			if ((from_id0 != to_id0) ||
			    (from_ssid != to_ssid)) {
				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);
				continue;
			}
			blacklist_range (ra, from, to, to_ssid);
		}
		if (!rc) {
			rc = blacklist_range(ra, from_ssid, to_ssid, from, to,
					     msgtrigger);
			if (rc)
				totalrc = 1;
		} else
			totalrc = 1;
	}
	return 1;

	return totalrc;
}

/* 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)
{
	return blacklist_parse_parameters (str, add);
	CIO_MSG_EVENT(6, "Reading blacklist parameters\n");
	if (blacklist_parse_parameters(str, add, 1))
		return 0;
	return 1;
}

__setup ("cio_ignore=", blacklist_setup);
@@ -223,27 +230,23 @@ is_blacklisted (int ssid, int devno)
 * Function: blacklist_parse_proc_parameters
 * parse the stuff which is piped to /proc/cio_ignore
 */
static void
blacklist_parse_proc_parameters (char *buf)
static int 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 {
		printk (KERN_WARNING "cio: cio_ignore: Parse error; \n"
			KERN_WARNING "try using 'free all|<devno-range>,"
				     "<devno-range>,...'\n"
			KERN_WARNING "or 'add <devno-range>,"
				     "<devno-range>,...'\n");
		return;
	}
	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);
	else
		return 1;

	css_schedule_reprobe();

	return rc;
}

/* Iterator struct for all devices. */
@@ -327,6 +330,8 @@ cio_ignore_write(struct file *file, const char __user *user_buf,
		 size_t user_len, loff_t *offset)
{
	char *buf;
	size_t i;
	ssize_t rc, ret;

	if (*offset)
		return -EINVAL;
@@ -335,16 +340,27 @@ cio_ignore_write(struct file *file, const char __user *user_buf,
	buf = vmalloc (user_len + 1); /* maybe better use the stack? */
	if (buf == NULL)
		return -ENOMEM;
	memset(buf, 0, user_len + 1);

	if (strncpy_from_user (buf, user_buf, user_len) < 0) {
		vfree (buf);
		return -EFAULT;
		rc = -EFAULT;
		goto out_free;
	}
	buf[user_len] = '\0';

	blacklist_parse_proc_parameters (buf);
	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)
		rc = -EINVAL;
	else
		rc = user_len;

out_free:
	vfree (buf);
	return user_len;
	return rc;
}

static const struct seq_operations cio_ignore_proc_seq_ops = {