[S390] Cleanup of CHSC event handling.

Change CHSC event handling to be more easily extensible.

Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 0260f12..c6db7f4 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -461,144 +461,136 @@
 	return (u16) (lir->indesc[0]&0x000000ff);
 }
 
-int
-chsc_process_crw(void)
+struct chsc_sei_area {
+	struct chsc_header request;
+	u32 reserved1;
+	u32 reserved2;
+	u32 reserved3;
+	struct chsc_header response;
+	u32 reserved4;
+	u8  flags;
+	u8  vf;		/* validity flags */
+	u8  rs;		/* reporting source */
+	u8  cc;		/* content code */
+	u16 fla;	/* full link address */
+	u16 rsid;	/* reporting source id */
+	u32 reserved5;
+	u32 reserved6;
+	u8 ccdf[4096 - 16 - 24];	/* content-code dependent field */
+	/* ccdf has to be big enough for a link-incident record */
+} __attribute__ ((packed));
+
+static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area)
 {
-	int chpid, ret;
+	int chpid;
+
+	CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n",
+		      sei_area->rs, sei_area->rsid);
+	if (sei_area->rs != 4)
+		return 0;
+	chpid = __get_chpid_from_lir(sei_area->ccdf);
+	if (chpid < 0)
+		CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n");
+	else
+		s390_set_chpid_offline(chpid);
+
+	return 0;
+}
+
+static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
+{
 	struct res_acc_data res_data;
-	struct {
-		struct chsc_header request;
-		u32 reserved1;
-		u32 reserved2;
-		u32 reserved3;
-		struct chsc_header response;
-		u32 reserved4;
-		u8  flags;
-		u8  vf;		/* validity flags */
-		u8  rs;		/* reporting source */
-		u8  cc;		/* content code */
-		u16 fla;	/* full link address */
-		u16 rsid;	/* reporting source id */
-		u32 reserved5;
-		u32 reserved6;
-		u32 ccdf[96];	/* content-code dependent field */
-		/* ccdf has to be big enough for a link-incident record */
-	} __attribute__ ((packed)) *sei_area;
+	struct device *dev;
+	int status;
+	int rc;
+
+	CIO_CRW_EVENT(4, "chsc: resource accessibility event (rs=%02x, "
+		      "rs_id=%04x)\n", sei_area->rs, sei_area->rsid);
+	if (sei_area->rs != 4)
+		return 0;
+	/* allocate a new channel path structure, if needed */
+	status = get_chp_status(sei_area->rsid);
+	if (status < 0)
+		new_channel_path(sei_area->rsid);
+	else if (!status)
+		return 0;
+	dev = get_device(&css[0]->chps[sei_area->rsid]->dev);
+	memset(&res_data, 0, sizeof(struct res_acc_data));
+	res_data.chp = to_channelpath(dev);
+	if ((sei_area->vf & 0xc0) != 0) {
+		res_data.fla = sei_area->fla;
+		if ((sei_area->vf & 0xc0) == 0xc0)
+			/* full link address */
+			res_data.fla_mask = 0xffff;
+		else
+			/* link address */
+			res_data.fla_mask = 0xff00;
+	}
+	rc = s390_process_res_acc(&res_data);
+	put_device(dev);
+
+	return rc;
+}
+
+static int chsc_process_sei(struct chsc_sei_area *sei_area)
+{
+	int rc;
+
+	/* Check if we might have lost some information. */
+	if (sei_area->flags & 0x40)
+		CIO_CRW_EVENT(2, "chsc: event overflow\n");
+	/* which kind of information was stored? */
+	rc = 0;
+	switch (sei_area->cc) {
+	case 1: /* link incident*/
+		rc = chsc_process_sei_link_incident(sei_area);
+		break;
+	case 2: /* i/o resource accessibiliy */
+		rc = chsc_process_sei_res_acc(sei_area);
+		break;
+	default: /* other stuff */
+		CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n",
+			      sei_area->cc);
+		break;
+	}
+
+	return rc;
+}
+
+int chsc_process_crw(void)
+{
+	struct chsc_sei_area *sei_area;
+	int ret;
+	int rc;
 
 	if (!sei_page)
 		return 0;
-	/*
-	 * build the chsc request block for store event information
-	 * and do the call
-	 * This function is only called by the machine check handler thread,
-	 * so we don't need locking for the sei_page.
-	 */
+	/* Access to sei_page is serialized through machine check handler
+	 * thread, so no need for locking. */
 	sei_area = sei_page;
 
 	CIO_TRACE_EVENT( 2, "prcss");
 	ret = 0;
 	do {
-		int ccode, status;
-		struct device *dev;
 		memset(sei_area, 0, sizeof(*sei_area));
-		memset(&res_data, 0, sizeof(struct res_acc_data));
 		sei_area->request.length = 0x0010;
 		sei_area->request.code = 0x000e;
+		if (chsc(sei_area))
+			break;
 
-		ccode = chsc(sei_area);
-		if (ccode > 0)
-			return 0;
-
-		switch (sei_area->response.code) {
-			/* for debug purposes, check for problems */
-		case 0x0001:
-			CIO_CRW_EVENT(4, "chsc_process_crw: event information "
-					"successfully stored\n");
-			break; /* everything ok */
-		case 0x0002:
-			CIO_CRW_EVENT(2,
-				      "chsc_process_crw: invalid command!\n");
-			return 0;
-		case 0x0003:
-			CIO_CRW_EVENT(2, "chsc_process_crw: error in chsc "
-				      "request block!\n");
-			return 0;
-		case 0x0005:
-			CIO_CRW_EVENT(2, "chsc_process_crw: no event "
-				      "information stored\n");
-			return 0;
-		default:
-			CIO_CRW_EVENT(2, "chsc_process_crw: chsc response %d\n",
+		if (sei_area->response.code == 0x0001) {
+			CIO_CRW_EVENT(4, "chsc: sei successful\n");
+			rc = chsc_process_sei(sei_area);
+			if (rc)
+				ret = rc;
+		} else {
+			CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n",
 				      sei_area->response.code);
-			return 0;
-		}
-
-		/* Check if we might have lost some information. */
-		if (sei_area->flags & 0x40)
-			CIO_CRW_EVENT(2, "chsc_process_crw: Event information "
-				       "has been lost due to overflow!\n");
-
-		if (sei_area->rs != 4) {
-			CIO_CRW_EVENT(2, "chsc_process_crw: reporting source "
-				      "(%04X) isn't a chpid!\n",
-				      sei_area->rsid);
-			continue;
-		}
-
-		/* which kind of information was stored? */
-		switch (sei_area->cc) {
-		case 1: /* link incident*/
-			CIO_CRW_EVENT(4, "chsc_process_crw: "
-				      "channel subsystem reports link incident,"
-				      " reporting source is chpid %x\n",
-				      sei_area->rsid);
-			chpid = __get_chpid_from_lir(sei_area->ccdf);
-			if (chpid < 0)
-				CIO_CRW_EVENT(4, "%s: Invalid LIR, skipping\n",
-					      __FUNCTION__);
-			else
-				s390_set_chpid_offline(chpid);
-			break;
-			
-		case 2: /* i/o resource accessibiliy */
-			CIO_CRW_EVENT(4, "chsc_process_crw: "
-				      "channel subsystem reports some I/O "
-				      "devices may have become accessible\n");
-			pr_debug("Data received after sei: \n");
-			pr_debug("Validity flags: %x\n", sei_area->vf);
-			
-			/* allocate a new channel path structure, if needed */
-			status = get_chp_status(sei_area->rsid);
-			if (status < 0)
-				new_channel_path(sei_area->rsid);
-			else if (!status)
-				break;
-			dev = get_device(&css[0]->chps[sei_area->rsid]->dev);
-			res_data.chp = to_channelpath(dev);
-			pr_debug("chpid: %x", sei_area->rsid);
-			if ((sei_area->vf & 0xc0) != 0) {
-				res_data.fla = sei_area->fla;
-				if ((sei_area->vf & 0xc0) == 0xc0) {
-					pr_debug(" full link addr: %x",
-						 sei_area->fla);
-					res_data.fla_mask = 0xffff;
-				} else {
-					pr_debug(" link addr: %x",
-						 sei_area->fla);
-					res_data.fla_mask = 0xff00;
-				}
-			}
-			ret = s390_process_res_acc(&res_data);
-			pr_debug("\n\n");
-			put_device(dev);
-			break;
-			
-		default: /* other stuff */
-			CIO_CRW_EVENT(4, "chsc_process_crw: event %d\n",
-				      sei_area->cc);
+			ret = 0;
 			break;
 		}
 	} while (sei_area->flags & 0x80);
+
 	return ret;
 }