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

Commit fb6958a5 authored by Cornelia Huck's avatar Cornelia Huck Committed by Linus Torvalds
Browse files

[PATCH] s390: multiple subchannel sets support



Add support for multiple subchannel sets.  Works with arbitrary devices in
subchannel set 1 and is transparent to device drivers.  Although currently
only two subchannel sets are available, this will work with the architectured
maximum number of subchannel sets as well.

Signed-off-by: default avatarCornelia Huck <cohuck@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 678a395b
Loading
Loading
Loading
Loading
+50 −36
Original line number Diff line number Diff line
/*
 *  drivers/s390/cio/blacklist.c
 *   S/390 common I/O routines -- blacklisting of specific devices
 *   $Revision: 1.35 $
 *   $Revision: 1.39 $
 *
 *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
 *			      IBM Corporation
@@ -35,10 +35,10 @@
 * These can be single devices or ranges of devices
 */

/* 65536 bits to indicate if a devno is blacklisted or not */
/* 65536 bits for each set to indicate if a devno is blacklisted or not */
#define __BL_DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \
			 (8*sizeof(long)))
static unsigned long bl_dev[__BL_DEV_WORDS];
static unsigned long bl_dev[__MAX_SSID + 1][__BL_DEV_WORDS];
typedef enum {add, free} range_action;

/*
@@ -46,21 +46,23 @@ typedef enum {add, free} range_action;
 * (Un-)blacklist the devices from-to
 */
static inline void
blacklist_range (range_action action, unsigned int from, unsigned int to)
blacklist_range (range_action action, unsigned int from, unsigned int to,
		 unsigned int ssid)
{
	if (!to)
		to = from;

	if (from > to || to > __MAX_SUBCHANNEL) {
	if (from > to || to > __MAX_SUBCHANNEL || ssid > __MAX_SSID) {
		printk (KERN_WARNING "Invalid blacklist range "
			"0x%04x to 0x%04x, skipping\n", from, to);
			"0.%x.%04x to 0.%x.%04x, skipping\n",
			ssid, from, ssid, to);
		return;
	}
	for (; from <= to; from++) {
		if (action == add)
			set_bit (from, bl_dev);
			set_bit (from, bl_dev[ssid]);
		else
			clear_bit (from, bl_dev);
			clear_bit (from, bl_dev[ssid]);
	}
}

@@ -70,7 +72,7 @@ blacklist_range (range_action action, unsigned int from, unsigned int to)
 * Shamelessly grabbed from dasd_devmap.c.
 */
static inline int
blacklist_busid(char **str, int *id0, int *id1, int *devno)
blacklist_busid(char **str, int *id0, int *ssid, int *devno)
{
	int val, old_style;
	char *sav;
@@ -87,7 +89,7 @@ blacklist_busid(char **str, int *id0, int *id1, int *devno)
		goto confused;
	val = simple_strtoul(*str, str, 16);
	if (old_style || (*str)[0] != '.') {
		*id0 = *id1 = 0;
		*id0 = *ssid = 0;
		if (val < 0 || val > 0xffff)
			goto confused;
		*devno = val;
@@ -106,7 +108,7 @@ blacklist_busid(char **str, int *id0, int *id1, int *devno)
	val = simple_strtoul(*str, str, 16);
	if (val < 0 || val > 0xff || (*str)++[0] != '.')
		goto confused;
	*id1 = val;
	*ssid = val;
	if (!isxdigit((*str)[0]))	/* We require at least one hex digit */
		goto confused;
	val = simple_strtoul(*str, str, 16);
@@ -126,7 +128,7 @@ blacklist_busid(char **str, int *id0, int *id1, int *devno)
static inline int
blacklist_parse_parameters (char *str, range_action action)
{
	unsigned int from, to, from_id0, to_id0, from_id1, to_id1;
	unsigned int from, to, from_id0, to_id0, from_ssid, to_ssid;

	while (*str != 0 && *str != '\n') {
		range_action ra = action;
@@ -143,23 +145,25 @@ blacklist_parse_parameters (char *str, range_action action)
		 */
		if (strncmp(str,"all,",4) == 0 || strcmp(str,"all") == 0 ||
		    strncmp(str,"all\n",4) == 0 || strncmp(str,"all ",4) == 0) {
			from = 0;
			to = __MAX_SUBCHANNEL;
			int j;

			str += 3;
			for (j=0; j <= __MAX_SSID; j++)
				blacklist_range(ra, 0, __MAX_SUBCHANNEL, j);
		} else {
			int rc;

			rc = blacklist_busid(&str, &from_id0,
					     &from_id1, &from);
					     &from_ssid, &from);
			if (rc)
				continue;
			to = from;
			to_id0 = from_id0;
			to_id1 = from_id1;
			to_ssid = from_ssid;
			if (*str == '-') {
				str++;
				rc = blacklist_busid(&str, &to_id0,
						     &to_id1, &to);
						     &to_ssid, &to);
				if (rc)
					continue;
			}
@@ -169,18 +173,19 @@ blacklist_parse_parameters (char *str, range_action action)
					strsep(&str, ",\n"));
				continue;
			}
			if ((from_id0 != to_id0) || (from_id1 != to_id1)) {
			if ((from_id0 != to_id0) ||
			    (from_ssid != to_ssid)) {
				printk(KERN_WARNING "invalid cio_ignore range "
					"%x.%x.%04x-%x.%x.%04x\n",
					from_id0, from_id1, from,
					to_id0, to_id1, to);
					from_id0, from_ssid, from,
					to_id0, to_ssid, to);
				continue;
			}
		}
		/* FIXME: ignoring id0 and id1 here. */
			pr_debug("blacklist_setup: adding range "
			 "from 0.0.%04x to 0.0.%04x\n", from, to);
		blacklist_range (ra, from, to);
				 "from %x.%x.%04x to %x.%x.%04x\n",
				 from_id0, from_ssid, from, to_id0, to_ssid, to);
			blacklist_range (ra, from, to, to_ssid);
		}
	}
	return 1;
}
@@ -214,9 +219,9 @@ __setup ("cio_ignore=", blacklist_setup);
 * Used by validate_subchannel()
 */
int
is_blacklisted (int devno)
is_blacklisted (int ssid, int devno)
{
	return test_bit (devno, bl_dev);
	return test_bit (devno, bl_dev[ssid]);
}

#ifdef CONFIG_PROC_FS
@@ -283,6 +288,7 @@ blacklist_parse_proc_parameters (char *buf)
/* Iterator struct for all devices. */
struct ccwdev_iter {
	int devno;
	int ssid;
	int in_range;
};

@@ -291,13 +297,14 @@ cio_ignore_proc_seq_start(struct seq_file *s, loff_t *offset)
{
	struct ccwdev_iter *iter;

	if (*offset > __MAX_SUBCHANNEL)
	if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
		return NULL;
	iter = kmalloc(sizeof(struct ccwdev_iter), GFP_KERNEL);
	if (!iter)
		return ERR_PTR(-ENOMEM);
	memset(iter, 0, sizeof(struct ccwdev_iter));
	iter->devno = *offset;
	iter->ssid = *offset / (__MAX_SUBCHANNEL + 1);
	iter->devno = *offset % (__MAX_SUBCHANNEL + 1);
	return iter;
}

@@ -313,9 +320,15 @@ cio_ignore_proc_seq_next(struct seq_file *s, void *it, loff_t *offset)
{
	struct ccwdev_iter *iter;

	if (*offset > __MAX_SUBCHANNEL)
	if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
		return NULL;
	iter = (struct ccwdev_iter *)it;
	if (iter->devno == __MAX_SUBCHANNEL) {
		iter->devno = 0;
		iter->ssid++;
		if (iter->ssid > __MAX_SSID)
			return NULL;
	} else
		iter->devno++;
	(*offset)++;
	return iter;
@@ -327,23 +340,24 @@ cio_ignore_proc_seq_show(struct seq_file *s, void *it)
	struct ccwdev_iter *iter;

	iter = (struct ccwdev_iter *)it;
	if (!is_blacklisted(iter->devno))
	if (!is_blacklisted(iter->ssid, iter->devno))
		/* Not blacklisted, nothing to output. */
		return 0;
	if (!iter->in_range) {
		/* First device in range. */
		if ((iter->devno == __MAX_SUBCHANNEL) ||
		    !is_blacklisted(iter->devno + 1))
		    !is_blacklisted(iter->ssid, iter->devno + 1))
			/* Singular device. */
			return seq_printf(s, "0.0.%04x\n", iter->devno);
			return seq_printf(s, "0.%x.%04x\n",
					  iter->ssid, iter->devno);
		iter->in_range = 1;
		return seq_printf(s, "0.0.%04x-", iter->devno);
		return seq_printf(s, "0.%x.%04x-", iter->ssid, iter->devno);
	}
	if ((iter->devno == __MAX_SUBCHANNEL) ||
	    !is_blacklisted(iter->devno + 1)) {
	    !is_blacklisted(iter->ssid, iter->devno + 1)) {
		/* Last device in range. */
		iter->in_range = 0;
		return seq_printf(s, "0.0.%04x\n", iter->devno);
		return seq_printf(s, "0.%x.%04x\n", iter->ssid, iter->devno);
	}
	return 0;
}
+1 −1
Original line number Diff line number Diff line
#ifndef S390_BLACKLIST_H
#define S390_BLACKLIST_H

extern int is_blacklisted (int devno);
extern int is_blacklisted (int ssid, int devno);

#endif
+60 −8
Original line number Diff line number Diff line
/*
 *  drivers/s390/cio/chsc.c
 *   S/390 common I/O routines -- channel subsystem call
 *   $Revision: 1.120 $
 *   $Revision: 1.126 $
 *
 *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
 *			      IBM Corporation
@@ -75,7 +75,9 @@ chsc_get_sch_desc_irq(struct subchannel *sch, void *page)

	struct {
		struct chsc_header request;
		u16 reserved1;
		u16 reserved1a:10;
		u16 ssid:2;
		u16 reserved1b:4;
		u16 f_sch;	  /* first subchannel */
		u16 reserved2;
		u16 l_sch;	  /* last subchannel */
@@ -102,6 +104,7 @@ chsc_get_sch_desc_irq(struct subchannel *sch, void *page)
		.code   = 0x0004,
	};

	ssd_area->ssid = sch->schid.ssid;
	ssd_area->f_sch = sch->schid.sch_no;
	ssd_area->l_sch = sch->schid.sch_no;

@@ -145,8 +148,8 @@ chsc_get_sch_desc_irq(struct subchannel *sch, void *page)
	 */
	if (ssd_area->st > 3) { /* uhm, that looks strange... */
		CIO_CRW_EVENT(0, "Strange subchannel type %d"
			      " for sch %04x\n", ssd_area->st,
			      sch->schid.sch_no);
			      " for sch 0.%x.%04x\n", ssd_area->st,
			      sch->schid.ssid, sch->schid.sch_no);
		/*
		 * There may have been a new subchannel type defined in the
		 * time since this code was written; since we don't know which
@@ -155,8 +158,9 @@ chsc_get_sch_desc_irq(struct subchannel *sch, void *page)
		return 0;
	} else {
		const char *type[4] = {"I/O", "chsc", "message", "ADM"};
		CIO_CRW_EVENT(6, "ssd: sch %04x is %s subchannel\n",
			      sch->schid.sch_no, type[ssd_area->st]);
		CIO_CRW_EVENT(6, "ssd: sch 0.%x.%04x is %s subchannel\n",
			      sch->schid.ssid, sch->schid.sch_no,
			      type[ssd_area->st]);

		sch->ssd_info.valid = 1;
		sch->ssd_info.type = ssd_area->st;
@@ -364,7 +368,7 @@ s390_process_res_acc_new_sch(struct subchannel_id schid)
	 * that beast may be on we'll have to do a stsch
	 * on all devices, grr...
	 */
	if (stsch(schid, &schib))
	if (stsch_err(schid, &schib))
		/* We're through */
		return need_rescan ? -EAGAIN : -ENXIO;

@@ -818,7 +822,7 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data)
		put_device(&sch->dev);
		return 0;
	}
	if (stsch(schid, &schib))
	if (stsch_err(schid, &schib))
		/* We're through */
		return -ENXIO;
	/* Put it on the slow path. */
@@ -1078,6 +1082,54 @@ chsc_alloc_sei_area(void)
	return (sei_page ? 0 : -ENOMEM);
}

int __init
chsc_enable_facility(int operation_code)
{
	int ret;
	struct {
		struct chsc_header request;
		u8 reserved1:4;
		u8 format:4;
		u8 reserved2;
		u16 operation_code;
		u32 reserved3;
		u32 reserved4;
		u32 operation_data_area[252];
		struct chsc_header response;
		u32 reserved5:4;
		u32 format2:4;
		u32 reserved6:24;
	} *sda_area;

	sda_area = (void *)get_zeroed_page(GFP_KERNEL|GFP_DMA);
	if (!sda_area)
		return -ENOMEM;
	sda_area->request = (struct chsc_header) {
		.length = 0x0400,
		.code = 0x0031,
	};
	sda_area->operation_code = operation_code;

	ret = chsc(sda_area);
	if (ret > 0) {
		ret = (ret == 3) ? -ENODEV : -EBUSY;
		goto out;
	}
	switch (sda_area->response.code) {
	case 0x0003: /* invalid request block */
	case 0x0007:
		ret = -EINVAL;
		break;
	case 0x0004: /* command not provided */
	case 0x0101: /* facility not provided */
		ret = -EOPNOTSUPP;
		break;
	}
 out:
	free_page((unsigned long)sda_area);
	return ret;
}

subsys_initcall(chsc_alloc_sei_area);

struct css_general_char css_general_characteristics;
+4 −0
Original line number Diff line number Diff line
@@ -5,6 +5,8 @@
#define CHSC_SEI_ACC_LINKADDR     2
#define CHSC_SEI_ACC_FULLLINKADDR 3

#define CHSC_SDA_OC_MSS   0x2

struct chsc_header {
	u16 length;
	u16 code;
@@ -64,6 +66,8 @@ extern int css_characteristics_avail;

extern void *chsc_get_chp_desc(struct subchannel*, int);

extern int chsc_enable_facility(int);

#define to_channelpath(dev) container_of(dev, struct channel_path, dev)

#endif
+21 −14
Original line number Diff line number Diff line
/*
 *  drivers/s390/cio/cio.c
 *   S/390 common I/O routines -- low level i/o calls
 *   $Revision: 1.135 $
 *   $Revision: 1.138 $
 *
 *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
 *			      IBM Corporation
@@ -166,7 +166,8 @@ cio_start_handle_notoper(struct subchannel *sch, __u8 lpm)
	stsch (sch->schid, &sch->schib);

	CIO_MSG_EVENT(0, "cio_start: 'not oper' status for "
		      "subchannel %04x!\n", sch->schid.sch_no);
		      "subchannel 0.%x.%04x!\n", sch->schid.ssid,
		      sch->schid.sch_no);
	sprintf(dbf_text, "no%s", sch->dev.bus_id);
	CIO_TRACE_EVENT(0, dbf_text);
	CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib));
@@ -522,15 +523,18 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
	spin_lock_init(&sch->lock);

	/* Set a name for the subchannel */
	snprintf (sch->dev.bus_id, BUS_ID_SIZE, "0.0.%04x", schid.sch_no);
	snprintf (sch->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x", schid.ssid,
		  schid.sch_no);

	/*
	 * The first subchannel that is not-operational (ccode==3)
	 *  indicates that there aren't any more devices available.
	 * If stsch gets an exception, it means the current subchannel set
	 *  is not valid.
	 */
	ccode = stsch (schid, &sch->schib);
	ccode = stsch_err (schid, &sch->schib);
	if (ccode)
		return -ENXIO;
		return (ccode == 3) ? -ENXIO : ccode;

	sch->schid = schid;
	/* Copy subchannel type from path management control word. */
@@ -541,9 +545,9 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
	 */
	if (sch->st != 0) {
		CIO_DEBUG(KERN_INFO, 0,
			  "Subchannel %04X reports "
			  "Subchannel 0.%x.%04x reports "
			  "non-I/O subchannel type %04X\n",
			  sch->schid.sch_no, sch->st);
			  sch->schid.ssid, sch->schid.sch_no, sch->st);
		/* We stop here for non-io subchannels. */
		return sch->st;
	}
@@ -554,16 +558,18 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
		return -ENODEV;

	/* Devno is valid. */
	if (is_blacklisted (sch->schib.pmcw.dev)) {
	if (is_blacklisted (sch->schid.ssid, sch->schib.pmcw.dev)) {
		/*
		 * This device must not be known to Linux. So we simply
		 * say that there is no device and return ENODEV.
		 */
		CIO_MSG_EVENT(0, "Blacklisted device detected "
			      "at devno %04X\n", sch->schib.pmcw.dev);
			      "at devno %04X, subchannel set %x\n",
			      sch->schib.pmcw.dev, sch->schid.ssid);
		return -ENODEV;
	}
	sch->opm = 0xff;
	if (!cio_is_console(sch->schid))
		chsc_validate_chpids(sch);
	sch->lpm = sch->schib.pmcw.pim &
		sch->schib.pmcw.pam &
@@ -571,9 +577,10 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
		sch->opm;

	CIO_DEBUG(KERN_INFO, 0,
		  "Detected device %04X on subchannel %04X"
		  "Detected device %04x on subchannel 0.%x.%04X"
		  " - PIM = %02X, PAM = %02X, POM = %02X\n",
		  sch->schib.pmcw.dev, sch->schid.sch_no, sch->schib.pmcw.pim,
		  sch->schib.pmcw.dev, sch->schid.ssid,
		  sch->schid.sch_no, sch->schib.pmcw.pim,
		  sch->schib.pmcw.pam, sch->schib.pmcw.pom);

	/*
@@ -693,7 +700,7 @@ wait_cons_dev (void)
static int
cio_test_for_console(struct subchannel_id schid, void *data)
{
	if (stsch(schid, &console_subchannel.schib) != 0)
	if (stsch_err(schid, &console_subchannel.schib) != 0)
		return -ENXIO;
	if (console_subchannel.schib.pmcw.dnv &&
	    console_subchannel.schib.pmcw.dev ==
@@ -841,7 +848,7 @@ __shutdown_subchannel_easy(struct subchannel_id schid, void *data)
{
	struct schib schib;

	if (stsch(schid, &schib))
	if (stsch_err(schid, &schib))
		return -ENXIO;
	if (!schib.pmcw.ena)
		return 0;
Loading