summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-04-27 09:15:31 -0700
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-04-27 09:15:31 -0700
commitda8ac5e0fab11d0e84be4e49aaaa828c52d17097 (patch)
treeeade52afcbb5eb31d2d8869fc66e8223a7681a6f /drivers
parent32f15dc5e6252f03aa2e04a2b140827a8297f21f (diff)
parentcb629a01bb5bca951287e761c590a5686c6ca416 (diff)
downloadblackbird-op-linux-da8ac5e0fab11d0e84be4e49aaaa828c52d17097.tar.gz
blackbird-op-linux-da8ac5e0fab11d0e84be4e49aaaa828c52d17097.zip
Merge branch 'for-linus' of git://git390.osdl.marist.edu/pub/scm/linux-2.6
* 'for-linus' of git://git390.osdl.marist.edu/pub/scm/linux-2.6: (38 commits) [S390] SPIN_LOCK_UNLOCKED cleanup in drivers/s390 [S390] Clean up smp code in preparation for some larger changes. [S390] Remove debugging junk. [S390] Switch etr from tasklet to workqueue. [S390] split page_test_and_clear_dirty. [S390] Processor degradation notification. [S390] vtime: cleanup per_cpu usage. [S390] crypto: cleanup. [S390] sclp: fix coding style. [S390] vmlogrdr: stop IUCV connection in vmlogrdr_release. [S390] sclp: initialize early. [S390] ctc: kmalloc->kzalloc/casting cleanups. [S390] zfcpdump support. [S390] dasd: Add ipldev parameter. [S390] dasd: Add sysfs attribute status and generate uevents. [S390] Improved kernel stack overflow checking. [S390] Get rid of console setup functions. [S390] No execute support cleanup. [S390] Minor fault path optimization. [S390] Use generic bug. ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/s390/block/dasd.c3
-rw-r--r--drivers/s390/block/dasd_devmap.c58
-rw-r--r--drivers/s390/char/Makefile5
-rw-r--r--drivers/s390/char/con3215.c7
-rw-r--r--drivers/s390/char/con3270.c7
-rw-r--r--drivers/s390/char/sclp.c10
-rw-r--r--drivers/s390/char/sclp.h72
-rw-r--r--drivers/s390/char/sclp_chp.c196
-rw-r--r--drivers/s390/char/sclp_config.c75
-rw-r--r--drivers/s390/char/sclp_cpi.c4
-rw-r--r--drivers/s390/char/sclp_quiesce.c2
-rw-r--r--drivers/s390/char/sclp_rw.c16
-rw-r--r--drivers/s390/char/sclp_sdias.c255
-rw-r--r--drivers/s390/char/sclp_tty.c6
-rw-r--r--drivers/s390/char/sclp_vt220.c8
-rw-r--r--drivers/s390/char/vmlogrdr.c9
-rw-r--r--drivers/s390/char/zcore.c651
-rw-r--r--drivers/s390/cio/Makefile2
-rw-r--r--drivers/s390/cio/ccwgroup.c33
-rw-r--r--drivers/s390/cio/chp.c683
-rw-r--r--drivers/s390/cio/chp.h53
-rw-r--r--drivers/s390/cio/chsc.c1024
-rw-r--r--drivers/s390/cio/chsc.h42
-rw-r--r--drivers/s390/cio/cio.c52
-rw-r--r--drivers/s390/cio/cio.h17
-rw-r--r--drivers/s390/cio/cmf.c2
-rw-r--r--drivers/s390/cio/css.c201
-rw-r--r--drivers/s390/cio/css.h16
-rw-r--r--drivers/s390/cio/device.c246
-rw-r--r--drivers/s390/cio/device_fsm.c8
-rw-r--r--drivers/s390/cio/device_ops.c7
-rw-r--r--drivers/s390/cio/idset.c112
-rw-r--r--drivers/s390/cio/idset.h25
-rw-r--r--drivers/s390/cio/ioasm.h5
-rw-r--r--drivers/s390/net/ctcmain.c23
-rw-r--r--drivers/s390/s390mach.c25
-rw-r--r--drivers/s390/sysinfo.c18
37 files changed, 2781 insertions, 1197 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index eb5dc62f0d9c..e71929db8b06 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -398,6 +398,9 @@ dasd_change_state(struct dasd_device *device)
if (device->state == device->target)
wake_up(&dasd_init_waitq);
+
+ /* let user-space know that the device status changed */
+ kobject_uevent(&device->cdev->dev.kobj, KOBJ_CHANGE);
}
/*
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index ed70852cc915..6a89cefe99bb 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -19,6 +19,7 @@
#include <asm/debug.h>
#include <asm/uaccess.h>
+#include <asm/ipl.h>
/* This is ugly... */
#define PRINTK_HEADER "dasd_devmap:"
@@ -133,6 +134,8 @@ dasd_call_setup(char *str)
__setup ("dasd=", dasd_call_setup);
#endif /* #ifndef MODULE */
+#define DASD_IPLDEV "ipldev"
+
/*
* Read a device busid/devno from a string.
*/
@@ -141,6 +144,20 @@ dasd_busid(char **str, int *id0, int *id1, int *devno)
{
int val, old_style;
+ /* Interpret ipldev busid */
+ if (strncmp(DASD_IPLDEV, *str, strlen(DASD_IPLDEV)) == 0) {
+ if (ipl_info.type != IPL_TYPE_CCW) {
+ MESSAGE(KERN_ERR, "%s", "ipl device is not a ccw "
+ "device");
+ return -EINVAL;
+ }
+ *id0 = 0;
+ *id1 = ipl_info.data.ccw.dev_id.ssid;
+ *devno = ipl_info.data.ccw.dev_id.devno;
+ *str += strlen(DASD_IPLDEV);
+
+ return 0;
+ }
/* check for leading '0x' */
old_style = 0;
if ((*str)[0] == '0' && (*str)[1] == 'x') {
@@ -829,6 +846,46 @@ dasd_discipline_show(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL);
static ssize_t
+dasd_device_status_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct dasd_device *device;
+ ssize_t len;
+
+ device = dasd_device_from_cdev(to_ccwdev(dev));
+ if (!IS_ERR(device)) {
+ switch (device->state) {
+ case DASD_STATE_NEW:
+ len = snprintf(buf, PAGE_SIZE, "new\n");
+ break;
+ case DASD_STATE_KNOWN:
+ len = snprintf(buf, PAGE_SIZE, "detected\n");
+ break;
+ case DASD_STATE_BASIC:
+ len = snprintf(buf, PAGE_SIZE, "basic\n");
+ break;
+ case DASD_STATE_UNFMT:
+ len = snprintf(buf, PAGE_SIZE, "unformatted\n");
+ break;
+ case DASD_STATE_READY:
+ len = snprintf(buf, PAGE_SIZE, "ready\n");
+ break;
+ case DASD_STATE_ONLINE:
+ len = snprintf(buf, PAGE_SIZE, "online\n");
+ break;
+ default:
+ len = snprintf(buf, PAGE_SIZE, "no stat\n");
+ break;
+ }
+ dasd_put_device(device);
+ } else
+ len = snprintf(buf, PAGE_SIZE, "unknown\n");
+ return len;
+}
+
+static DEVICE_ATTR(status, 0444, dasd_device_status_show, NULL);
+
+static ssize_t
dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct dasd_devmap *devmap;
@@ -939,6 +996,7 @@ static DEVICE_ATTR(eer_enabled, 0644, dasd_eer_show, dasd_eer_store);
static struct attribute * dasd_attrs[] = {
&dev_attr_readonly.attr,
&dev_attr_discipline.attr,
+ &dev_attr_status.attr,
&dev_attr_alias.attr,
&dev_attr_vendor.attr,
&dev_attr_uid.attr,
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index 293e667b50f2..c210784bdf46 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -3,7 +3,7 @@
#
obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \
- sclp_info.o
+ sclp_info.o sclp_config.o sclp_chp.o
obj-$(CONFIG_TN3270) += raw3270.o
obj-$(CONFIG_TN3270_CONSOLE) += con3270.o
@@ -29,3 +29,6 @@ obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o
obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o
obj-$(CONFIG_MONREADER) += monreader.o
obj-$(CONFIG_MONWRITER) += monwriter.o
+
+zcore_mod-objs := sclp_sdias.o zcore.o
+obj-$(CONFIG_ZFCPDUMP) += zcore_mod.o
diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c
index 9a328f14a641..6000bdee4082 100644
--- a/drivers/s390/char/con3215.c
+++ b/drivers/s390/char/con3215.c
@@ -813,12 +813,6 @@ con3215_unblank(void)
spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
}
-static int __init
-con3215_consetup(struct console *co, char *options)
-{
- return 0;
-}
-
/*
* The console structure for the 3215 console
*/
@@ -827,7 +821,6 @@ static struct console con3215 = {
.write = con3215_write,
.device = con3215_device,
.unblank = con3215_unblank,
- .setup = con3215_consetup,
.flags = CON_PRINTBUFFER,
};
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c
index 8e7f2d7633d6..fd3479119eb4 100644
--- a/drivers/s390/char/con3270.c
+++ b/drivers/s390/char/con3270.c
@@ -555,12 +555,6 @@ con3270_unblank(void)
spin_unlock_irqrestore(&cp->view.lock, flags);
}
-static int __init
-con3270_consetup(struct console *co, char *options)
-{
- return 0;
-}
-
/*
* The console structure for the 3270 console
*/
@@ -569,7 +563,6 @@ static struct console con3270 = {
.write = con3270_write,
.device = con3270_device,
.unblank = con3270_unblank,
- .setup = con3270_consetup,
.flags = CON_PRINTBUFFER,
};
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index f171de3b0b11..fa62e6944057 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -15,6 +15,7 @@
#include <linux/timer.h>
#include <linux/reboot.h>
#include <linux/jiffies.h>
+#include <linux/init.h>
#include <asm/types.h>
#include <asm/s390_ext.h>
@@ -510,7 +511,7 @@ sclp_state_change_cb(struct evbuf_header *evbuf)
}
static struct sclp_register sclp_state_change_event = {
- .receive_mask = EvTyp_StateChange_Mask,
+ .receive_mask = EVTYP_STATECHANGE_MASK,
.receiver_fn = sclp_state_change_cb
};
@@ -930,3 +931,10 @@ sclp_init(void)
sclp_init_mask(1);
return 0;
}
+
+static __init int sclp_initcall(void)
+{
+ return sclp_init();
+}
+
+arch_initcall(sclp_initcall);
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index 7d29ab45a6ed..87ac4a3ad49d 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -19,33 +19,37 @@
#define MAX_KMEM_PAGES (sizeof(unsigned long) << 3)
#define MAX_CONSOLE_PAGES 4
-#define EvTyp_OpCmd 0x01
-#define EvTyp_Msg 0x02
-#define EvTyp_StateChange 0x08
-#define EvTyp_PMsgCmd 0x09
-#define EvTyp_CntlProgOpCmd 0x20
-#define EvTyp_CntlProgIdent 0x0B
-#define EvTyp_SigQuiesce 0x1D
-#define EvTyp_VT220Msg 0x1A
-
-#define EvTyp_OpCmd_Mask 0x80000000
-#define EvTyp_Msg_Mask 0x40000000
-#define EvTyp_StateChange_Mask 0x01000000
-#define EvTyp_PMsgCmd_Mask 0x00800000
-#define EvTyp_CtlProgOpCmd_Mask 0x00000001
-#define EvTyp_CtlProgIdent_Mask 0x00200000
-#define EvTyp_SigQuiesce_Mask 0x00000008
-#define EvTyp_VT220Msg_Mask 0x00000040
-
-#define GnrlMsgFlgs_DOM 0x8000
-#define GnrlMsgFlgs_SndAlrm 0x4000
-#define GnrlMsgFlgs_HoldMsg 0x2000
-
-#define LnTpFlgs_CntlText 0x8000
-#define LnTpFlgs_LabelText 0x4000
-#define LnTpFlgs_DataText 0x2000
-#define LnTpFlgs_EndText 0x1000
-#define LnTpFlgs_PromptText 0x0800
+#define EVTYP_OPCMD 0x01
+#define EVTYP_MSG 0x02
+#define EVTYP_STATECHANGE 0x08
+#define EVTYP_PMSGCMD 0x09
+#define EVTYP_CNTLPROGOPCMD 0x20
+#define EVTYP_CNTLPROGIDENT 0x0B
+#define EVTYP_SIGQUIESCE 0x1D
+#define EVTYP_VT220MSG 0x1A
+#define EVTYP_CONFMGMDATA 0x04
+#define EVTYP_SDIAS 0x1C
+
+#define EVTYP_OPCMD_MASK 0x80000000
+#define EVTYP_MSG_MASK 0x40000000
+#define EVTYP_STATECHANGE_MASK 0x01000000
+#define EVTYP_PMSGCMD_MASK 0x00800000
+#define EVTYP_CTLPROGOPCMD_MASK 0x00000001
+#define EVTYP_CTLPROGIDENT_MASK 0x00200000
+#define EVTYP_SIGQUIESCE_MASK 0x00000008
+#define EVTYP_VT220MSG_MASK 0x00000040
+#define EVTYP_CONFMGMDATA_MASK 0x10000000
+#define EVTYP_SDIAS_MASK 0x00000010
+
+#define GNRLMSGFLGS_DOM 0x8000
+#define GNRLMSGFLGS_SNDALRM 0x4000
+#define GNRLMSGFLGS_HOLDMSG 0x2000
+
+#define LNTPFLGS_CNTLTEXT 0x8000
+#define LNTPFLGS_LABELTEXT 0x4000
+#define LNTPFLGS_DATATEXT 0x2000
+#define LNTPFLGS_ENDTEXT 0x1000
+#define LNTPFLGS_PROMPTTEXT 0x0800
typedef unsigned int sclp_cmdw_t;
@@ -56,15 +60,15 @@ typedef unsigned int sclp_cmdw_t;
#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001
#define GDS_ID_MDSMU 0x1310
-#define GDS_ID_MDSRouteInfo 0x1311
-#define GDS_ID_AgUnWrkCorr 0x1549
-#define GDS_ID_SNACondReport 0x1532
+#define GDS_ID_MDSROUTEINFO 0x1311
+#define GDS_ID_AGUNWRKCORR 0x1549
+#define GDS_ID_SNACONDREPORT 0x1532
#define GDS_ID_CPMSU 0x1212
-#define GDS_ID_RoutTargInstr 0x154D
-#define GDS_ID_OpReq 0x8070
-#define GDS_ID_TextCmd 0x1320
+#define GDS_ID_ROUTTARGINSTR 0x154D
+#define GDS_ID_OPREQ 0x8070
+#define GDS_ID_TEXTCMD 0x1320
-#define GDS_KEY_SelfDefTextMsg 0x31
+#define GDS_KEY_SELFDEFTEXTMSG 0x31
typedef u32 sccb_mask_t; /* ATTENTION: assumes 32bit mask !!! */
diff --git a/drivers/s390/char/sclp_chp.c b/drivers/s390/char/sclp_chp.c
new file mode 100644
index 000000000000..a66b914519b5
--- /dev/null
+++ b/drivers/s390/char/sclp_chp.c
@@ -0,0 +1,196 @@
+/*
+ * drivers/s390/char/sclp_chp.c
+ *
+ * Copyright IBM Corp. 2007
+ * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/types.h>
+#include <linux/gfp.h>
+#include <linux/errno.h>
+#include <linux/completion.h>
+#include <asm/sclp.h>
+#include <asm/chpid.h>
+
+#include "sclp.h"
+
+#define TAG "sclp_chp: "
+
+#define SCLP_CMDW_CONFIGURE_CHANNEL_PATH 0x000f0001
+#define SCLP_CMDW_DECONFIGURE_CHANNEL_PATH 0x000e0001
+#define SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION 0x00030001
+
+static inline sclp_cmdw_t get_configure_cmdw(struct chp_id chpid)
+{
+ return SCLP_CMDW_CONFIGURE_CHANNEL_PATH | chpid.id << 8;
+}
+
+static inline sclp_cmdw_t get_deconfigure_cmdw(struct chp_id chpid)
+{
+ return SCLP_CMDW_DECONFIGURE_CHANNEL_PATH | chpid.id << 8;
+}
+
+static void chp_callback(struct sclp_req *req, void *data)
+{
+ struct completion *completion = data;
+
+ complete(completion);
+}
+
+struct chp_cfg_sccb {
+ struct sccb_header header;
+ u8 ccm;
+ u8 reserved[6];
+ u8 cssid;
+} __attribute__((packed));
+
+struct chp_cfg_data {
+ struct chp_cfg_sccb sccb;
+ struct sclp_req req;
+ struct completion completion;
+} __attribute__((packed));
+
+static int do_configure(sclp_cmdw_t cmd)
+{
+ struct chp_cfg_data *data;
+ int rc;
+
+ /* Prepare sccb. */
+ data = (struct chp_cfg_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+ data->sccb.header.length = sizeof(struct chp_cfg_sccb);
+ data->req.command = cmd;
+ data->req.sccb = &(data->sccb);
+ data->req.status = SCLP_REQ_FILLED;
+ data->req.callback = chp_callback;
+ data->req.callback_data = &(data->completion);
+ init_completion(&data->completion);
+
+ /* Perform sclp request. */
+ rc = sclp_add_request(&(data->req));
+ if (rc)
+ goto out;
+ wait_for_completion(&data->completion);
+
+ /* Check response .*/
+ if (data->req.status != SCLP_REQ_DONE) {
+ printk(KERN_WARNING TAG "configure channel-path request failed "
+ "(status=0x%02x)\n", data->req.status);
+ rc = -EIO;
+ goto out;
+ }
+ switch (data->sccb.header.response_code) {
+ case 0x0020:
+ case 0x0120:
+ case 0x0440:
+ case 0x0450:
+ break;
+ default:
+ printk(KERN_WARNING TAG "configure channel-path failed "
+ "(cmd=0x%08x, response=0x%04x)\n", cmd,
+ data->sccb.header.response_code);
+ rc = -EIO;
+ break;
+ }
+out:
+ free_page((unsigned long) data);
+
+ return rc;
+}
+
+/**
+ * sclp_chp_configure - perform configure channel-path sclp command
+ * @chpid: channel-path ID
+ *
+ * Perform configure channel-path command sclp command for specified chpid.
+ * Return 0 after command successfully finished, non-zero otherwise.
+ */
+int sclp_chp_configure(struct chp_id chpid)
+{
+ return do_configure(get_configure_cmdw(chpid));
+}
+
+/**
+ * sclp_chp_deconfigure - perform deconfigure channel-path sclp command
+ * @chpid: channel-path ID
+ *
+ * Perform deconfigure channel-path command sclp command for specified chpid
+ * and wait for completion. On success return 0. Return non-zero otherwise.
+ */
+int sclp_chp_deconfigure(struct chp_id chpid)
+{
+ return do_configure(get_deconfigure_cmdw(chpid));
+}
+
+struct chp_info_sccb {
+ struct sccb_header header;
+ u8 recognized[SCLP_CHP_INFO_MASK_SIZE];
+ u8 standby[SCLP_CHP_INFO_MASK_SIZE];
+ u8 configured[SCLP_CHP_INFO_MASK_SIZE];
+ u8 ccm;
+ u8 reserved[6];
+ u8 cssid;
+} __attribute__((packed));
+
+struct chp_info_data {
+ struct chp_info_sccb sccb;
+ struct sclp_req req;
+ struct completion completion;
+} __attribute__((packed));
+
+/**
+ * sclp_chp_read_info - perform read channel-path information sclp command
+ * @info: resulting channel-path information data
+ *
+ * Perform read channel-path information sclp command and wait for completion.
+ * On success, store channel-path information in @info and return 0. Return
+ * non-zero otherwise.
+ */
+int sclp_chp_read_info(struct sclp_chp_info *info)
+{
+ struct chp_info_data *data;
+ int rc;
+
+ /* Prepare sccb. */
+ data = (struct chp_info_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+ data->sccb.header.length = sizeof(struct chp_info_sccb);
+ data->req.command = SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION;
+ data->req.sccb = &(data->sccb);
+ data->req.status = SCLP_REQ_FILLED;
+ data->req.callback = chp_callback;
+ data->req.callback_data = &(data->completion);
+ init_completion(&data->completion);
+
+ /* Perform sclp request. */
+ rc = sclp_add_request(&(data->req));
+ if (rc)
+ goto out;
+ wait_for_completion(&data->completion);
+
+ /* Check response .*/
+ if (data->req.status != SCLP_REQ_DONE) {
+ printk(KERN_WARNING TAG "read channel-path info request failed "
+ "(status=0x%02x)\n", data->req.status);
+ rc = -EIO;
+ goto out;
+ }
+ if (data->sccb.header.response_code != 0x0010) {
+ printk(KERN_WARNING TAG "read channel-path info failed "
+ "(response=0x%04x)\n", data->sccb.header.response_code);
+ rc = -EIO;
+ goto out;
+ }
+ memcpy(info->recognized, data->sccb.recognized,
+ SCLP_CHP_INFO_MASK_SIZE);
+ memcpy(info->standby, data->sccb.standby,
+ SCLP_CHP_INFO_MASK_SIZE);
+ memcpy(info->configured, data->sccb.configured,
+ SCLP_CHP_INFO_MASK_SIZE);
+out:
+ free_page((unsigned long) data);
+
+ return rc;
+}
diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c
new file mode 100644
index 000000000000..5322e5e54a98
--- /dev/null
+++ b/drivers/s390/char/sclp_config.c
@@ -0,0 +1,75 @@
+/*
+ * drivers/s390/char/sclp_config.c
+ *
+ * Copyright IBM Corp. 2007
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
+ */
+
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/cpu.h>
+#include <linux/sysdev.h>
+#include <linux/workqueue.h>
+#include "sclp.h"
+
+#define TAG "sclp_config: "
+
+struct conf_mgm_data {
+ u8 reserved;
+ u8 ev_qualifier;
+} __attribute__((packed));
+
+#define EV_QUAL_CAP_CHANGE 3
+
+static struct work_struct sclp_cpu_capability_work;
+
+static void sclp_cpu_capability_notify(struct work_struct *work)
+{
+ int cpu;
+ struct sys_device *sysdev;
+
+ printk(KERN_WARNING TAG "cpu capability changed.\n");
+ lock_cpu_hotplug();
+ for_each_online_cpu(cpu) {
+ sysdev = get_cpu_sysdev(cpu);
+ kobject_uevent(&sysdev->kobj, KOBJ_CHANGE);
+ }
+ unlock_cpu_hotplug();
+}
+
+static void sclp_conf_receiver_fn(struct evbuf_header *evbuf)
+{
+ struct conf_mgm_data *cdata;
+
+ cdata = (struct conf_mgm_data *)(evbuf + 1);
+ if (cdata->ev_qualifier == EV_QUAL_CAP_CHANGE)
+ schedule_work(&sclp_cpu_capability_work);
+}
+
+static struct sclp_register sclp_conf_register =
+{
+ .receive_mask = EVTYP_CONFMGMDATA_MASK,
+ .receiver_fn = sclp_conf_receiver_fn,
+};
+
+static int __init sclp_conf_init(void)
+{
+ int rc;
+
+ INIT_WORK(&sclp_cpu_capability_work, sclp_cpu_capability_notify);
+
+ rc = sclp_register(&sclp_conf_register);
+ if (rc) {
+ printk(KERN_ERR TAG "failed to register (%d).\n", rc);
+ return rc;
+ }
+
+ if (!(sclp_conf_register.sclp_receive_mask & EVTYP_CONFMGMDATA_MASK)) {
+ printk(KERN_WARNING TAG "no configuration management.\n");
+ sclp_unregister(&sclp_conf_register);
+ rc = -ENOSYS;
+ }
+ return rc;
+}
+
+__initcall(sclp_conf_init);
diff --git a/drivers/s390/char/sclp_cpi.c b/drivers/s390/char/sclp_cpi.c
index 65aa2c85737f..29fe2a5ec2fe 100644
--- a/drivers/s390/char/sclp_cpi.c
+++ b/drivers/s390/char/sclp_cpi.c
@@ -46,7 +46,7 @@ struct cpi_sccb {
/* Event type structure for write message and write priority message */
static struct sclp_register sclp_cpi_event =
{
- .send_mask = EvTyp_CtlProgIdent_Mask
+ .send_mask = EVTYP_CTLPROGIDENT_MASK
};
MODULE_LICENSE("GPL");
@@ -201,7 +201,7 @@ cpi_module_init(void)
"console.\n");
return -EINVAL;
}
- if (!(sclp_cpi_event.sclp_send_mask & EvTyp_CtlProgIdent_Mask)) {
+ if (!(sclp_cpi_event.sclp_send_mask & EVTYP_CTLPROGIDENT_MASK)) {
printk(KERN_WARNING "cpi: no control program identification "
"support\n");
sclp_unregister(&sclp_cpi_event);
diff --git a/drivers/s390/char/sclp_quiesce.c b/drivers/s390/char/sclp_quiesce.c
index baa8fe669ed2..45ff25e787cb 100644
--- a/drivers/s390/char/sclp_quiesce.c
+++ b/drivers/s390/char/sclp_quiesce.c
@@ -43,7 +43,7 @@ sclp_quiesce_handler(struct evbuf_header *evbuf)
}
static struct sclp_register sclp_quiesce_event = {
- .receive_mask = EvTyp_SigQuiesce_Mask,
+ .receive_mask = EVTYP_SIGQUIESCE_MASK,
.receiver_fn = sclp_quiesce_handler
};
diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c
index 2486783ea58e..bbd5b8b66f42 100644
--- a/drivers/s390/char/sclp_rw.c
+++ b/drivers/s390/char/sclp_rw.c
@@ -30,7 +30,7 @@
/* Event type structure for write message and write priority message */
static struct sclp_register sclp_rw_event = {
- .send_mask = EvTyp_Msg_Mask | EvTyp_PMsgCmd_Mask
+ .send_mask = EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK
};
/*
@@ -64,7 +64,7 @@ sclp_make_buffer(void *page, unsigned short columns, unsigned short htab)
memset(sccb, 0, sizeof(struct write_sccb));
sccb->header.length = sizeof(struct write_sccb);
sccb->msg_buf.header.length = sizeof(struct msg_buf);
- sccb->msg_buf.header.type = EvTyp_Msg;
+ sccb->msg_buf.header.type = EVTYP_MSG;
sccb->msg_buf.mdb.header.length = sizeof(struct mdb);
sccb->msg_buf.mdb.header.type = 1;
sccb->msg_buf.mdb.header.tag = 0xD4C4C240; /* ebcdic "MDB " */
@@ -114,7 +114,7 @@ sclp_initialize_mto(struct sclp_buffer *buffer, int max_len)
memset(mto, 0, sizeof(struct mto));
mto->length = sizeof(struct mto);
mto->type = 4; /* message text object */
- mto->line_type_flags = LnTpFlgs_EndText; /* end text */
+ mto->line_type_flags = LNTPFLGS_ENDTEXT; /* end text */
/* set pointer to first byte after struct mto. */
buffer->current_line = (char *) (mto + 1);
@@ -215,7 +215,7 @@ sclp_write(struct sclp_buffer *buffer, const unsigned char *msg, int count)
case '\a': /* bell, one for several times */
/* set SCLP sound alarm bit in General Object */
buffer->sccb->msg_buf.mdb.go.general_msg_flags |=
- GnrlMsgFlgs_SndAlrm;
+ GNRLMSGFLGS_SNDALRM;
break;
case '\t': /* horizontal tabulator */
/* check if new mto needs to be created */
@@ -452,12 +452,12 @@ sclp_emit_buffer(struct sclp_buffer *buffer,
return -EIO;
sccb = buffer->sccb;
- if (sclp_rw_event.sclp_send_mask & EvTyp_Msg_Mask)
+ if (sclp_rw_event.sclp_send_mask & EVTYP_MSG_MASK)
/* Use normal write message */
- sccb->msg_buf.header.type = EvTyp_Msg;
- else if (sclp_rw_event.sclp_send_mask & EvTyp_PMsgCmd_Mask)
+ sccb->msg_buf.header.type = EVTYP_MSG;
+ else if (sclp_rw_event.sclp_send_mask & EVTYP_PMSGCMD_MASK)
/* Use write priority message */
- sccb->msg_buf.header.type = EvTyp_PMsgCmd;
+ sccb->msg_buf.header.type = EVTYP_PMSGCMD;
else
return -ENOSYS;
buffer->request.command = SCLP_CMDW_WRITE_EVENT_DATA;
diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c
new file mode 100644
index 000000000000..52283daddaef
--- /dev/null
+++ b/drivers/s390/char/sclp_sdias.c
@@ -0,0 +1,255 @@
+/*
+ * Sclp "store data in absolut storage"
+ *
+ * Copyright IBM Corp. 2003,2007
+ * Author(s): Michael Holzheu
+ */
+
+#include <linux/sched.h>
+#include <asm/sclp.h>
+#include <asm/debug.h>
+#include <asm/ipl.h>
+#include "sclp.h"
+#include "sclp_rw.h"
+
+#define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x)
+#define ERROR_MSG(x...) printk ( KERN_ALERT "SDIAS: " x )
+
+#define SDIAS_RETRIES 300
+#define SDIAS_SLEEP_TICKS 50
+
+#define EQ_STORE_DATA 0x0
+#define EQ_SIZE 0x1
+#define DI_FCP_DUMP 0x0
+#define ASA_SIZE_32 0x0
+#define ASA_SIZE_64 0x1
+#define EVSTATE_ALL_STORED 0x0
+#define EVSTATE_NO_DATA 0x3
+#define EVSTATE_PART_STORED 0x10
+
+static struct debug_info *sdias_dbf;
+
+static struct sclp_register sclp_sdias_register = {
+ .send_mask = EVTYP_SDIAS_MASK,
+};
+
+struct sdias_evbuf {
+ struct evbuf_header hdr;
+ u8 event_qual;
+ u8 data_id;
+ u64 reserved2;
+ u32 event_id;
+ u16 reserved3;
+ u8 asa_size;
+ u8 event_status;
+ u32 reserved4;
+ u32 blk_cnt;
+ u64 asa;
+ u32 reserved5;
+ u32 fbn;
+ u32 reserved6;
+ u32 lbn;
+ u16 reserved7;
+ u16 dbs;
+} __attribute__((packed));
+
+struct sdias_sccb {
+ struct sccb_header hdr;
+ struct sdias_evbuf evbuf;
+} __attribute__((packed));
+
+static struct sdias_sccb sccb __attribute__((aligned(4096)));
+
+static int sclp_req_done;
+static wait_queue_head_t sdias_wq;
+static DEFINE_MUTEX(sdias_mutex);
+
+static void sdias_callback(struct sclp_req *request, void *data)
+{
+ struct sdias_sccb *sccb;
+
+ sccb = (struct sdias_sccb *) request->sccb;
+ sclp_req_done = 1;
+ wake_up(&sdias_wq); /* Inform caller, that request is complete */
+ TRACE("callback done\n");
+}
+
+static int sdias_sclp_send(struct sclp_req *req)
+{
+ int retries;
+ int rc;
+
+ for (retries = SDIAS_RETRIES; retries; retries--) {
+ sclp_req_done = 0;
+ TRACE("add request\n");
+ rc = sclp_add_request(req);
+ if (rc) {
+ /* not initiated, wait some time and retry */
+ set_current_state(TASK_INTERRUPTIBLE);
+ TRACE("add request failed: rc = %i\n",rc);
+ schedule_timeout(SDIAS_SLEEP_TICKS);
+ continue;
+ }
+ /* initiated, wait for completion of service call */
+ wait_event(sdias_wq, (sclp_req_done == 1));
+ if (req->status == SCLP_REQ_FAILED) {
+ TRACE("sclp request failed\n");
+ rc = -EIO;
+ continue;
+ }
+ TRACE("request done\n");
+ break;
+ }
+ return rc;
+}
+
+/*
+ * Get number of blocks (4K) available in the HSA
+ */
+int sclp_sdias_blk_count(void)
+{
+ struct sclp_req request;
+ int rc;
+
+ mutex_lock(&sdias_mutex);
+
+ memset(&sccb, 0, sizeof(sccb));
+ memset(&request, 0, sizeof(request));
+
+ sccb.hdr.length = sizeof(sccb);
+ sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf);
+ sccb.evbuf.hdr.type = EVTYP_SDIAS;
+ sccb.evbuf.event_qual = EQ_SIZE;
+ sccb.evbuf.data_id = DI_FCP_DUMP;
+ sccb.evbuf.event_id = 4712;
+ sccb.evbuf.dbs = 1;
+
+ request.sccb = &sccb;
+ request.command = SCLP_CMDW_WRITE_EVENT_DATA;
+ request.status = SCLP_REQ_FILLED;
+ request.callback = sdias_callback;
+
+ rc = sdias_sclp_send(&request);
+ if (rc) {
+ ERROR_MSG("sclp_send failed for get_nr_blocks\n");
+ goto out;
+ }
+ if (sccb.hdr.response_code != 0x0020) {
+ TRACE("send failed: %x\n", sccb.hdr.response_code);
+ rc = -EIO;
+ goto out;
+ }
+
+ switch (sccb.evbuf.event_status) {
+ case 0:
+ rc = sccb.evbuf.blk_cnt;
+ break;
+ default:
+ ERROR_MSG("SCLP error: %x\n", sccb.evbuf.event_status);
+ rc = -EIO;
+ goto out;
+ }
+ TRACE("%i blocks\n", rc);
+out:
+ mutex_unlock(&sdias_mutex);
+ return rc;
+}
+
+/*
+ * Copy from HSA to absolute storage (not reentrant):
+ *
+ * @dest : Address of buffer where data should be copied
+ * @start_blk: Start Block (beginning with 1)
+ * @nr_blks : Number of 4K blocks to copy
+ *
+ * Return Value: 0 : Requested 'number' of blocks of data copied
+ * <0: ERROR - negative event status
+ */
+int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
+{
+ struct sclp_req request;
+ int rc;
+
+ mutex_lock(&sdias_mutex);
+
+ memset(&sccb, 0, sizeof(sccb));
+ memset(&request, 0, sizeof(request));
+
+ sccb.hdr.length = sizeof(sccb);
+ sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf);
+ sccb.evbuf.hdr.type = EVTYP_SDIAS;
+ sccb.evbuf.hdr.flags = 0;
+ sccb.evbuf.event_qual = EQ_STORE_DATA;
+ sccb.evbuf.data_id = DI_FCP_DUMP;
+ sccb.evbuf.event_id = 4712;
+#ifdef __s390x__
+ sccb.evbuf.asa_size = ASA_SIZE_64;
+#else
+ sccb.evbuf.asa_size = ASA_SIZE_32;
+#endif
+ sccb.evbuf.event_status = 0;
+ sccb.evbuf.blk_cnt = nr_blks;
+ sccb.evbuf.asa = (unsigned long)dest;
+ sccb.evbuf.fbn = start_blk;
+ sccb.evbuf.lbn = 0;
+ sccb.evbuf.dbs = 1;
+
+ request.sccb = &sccb;
+ request.command = SCLP_CMDW_WRITE_EVENT_DATA;
+ request.status = SCLP_REQ_FILLED;
+ request.callback = sdias_callback;
+
+ rc = sdias_sclp_send(&request);
+ if (rc) {
+ ERROR_MSG("sclp_send failed: %x\n", rc);
+ goto out;
+ }
+ if (sccb.hdr.response_code != 0x0020) {
+ TRACE("copy failed: %x\n", sccb.hdr.response_code);
+ rc = -EIO;
+ goto out;
+ }
+
+ switch (sccb.evbuf.event_status) {
+ case EVSTATE_ALL_STORED:
+ TRACE("all stored\n");
+ case EVSTATE_PART_STORED:
+ TRACE("part stored: %i\n", sccb.evbuf.blk_cnt);
+ break;
+ case EVSTATE_NO_DATA:
+ TRACE("no data\n");
+ default:
+ ERROR_MSG("Error from SCLP while copying hsa. "
+ "Event status = %x\n",
+ sccb.evbuf.event_status);
+ rc = -EIO;
+ }
+out:
+ mutex_unlock(&sdias_mutex);
+ return rc;
+}
+
+int __init sdias_init(void)
+{
+ int rc;
+
+ if (ipl_info.type != IPL_TYPE_FCP_DUMP)
+ return 0;
+ sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long));
+ debug_register_view(sdias_dbf, &debug_sprintf_view);
+ debug_set_level(sdias_dbf, 6);
+ rc = sclp_register(&sclp_sdias_register);
+ if (rc) {
+ ERROR_MSG("sclp register failed\n");
+ return rc;
+ }
+ init_waitqueue_head(&sdias_wq);
+ TRACE("init done\n");
+ return 0;
+}
+
+void __exit sdias_exit(void)
+{
+ debug_unregister(sdias_dbf);
+ sclp_unregister(&sclp_sdias_register);
+}
diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c
index 076816b9d528..e3b3d390b4a3 100644
--- a/drivers/s390/char/sclp_tty.c
+++ b/drivers/s390/char/sclp_tty.c
@@ -648,7 +648,7 @@ sclp_eval_textcmd(struct gds_subvector *start,
subvec = start;
while (subvec < end) {
subvec = find_gds_subvector(subvec, end,
- GDS_KEY_SelfDefTextMsg);
+ GDS_KEY_SELFDEFTEXTMSG);
if (!subvec)
break;
sclp_eval_selfdeftextmsg((struct gds_subvector *)(subvec + 1),
@@ -664,7 +664,7 @@ sclp_eval_cpmsu(struct gds_vector *start, struct gds_vector *end)
vec = start;
while (vec < end) {
- vec = find_gds_vector(vec, end, GDS_ID_TextCmd);
+ vec = find_gds_vector(vec, end, GDS_ID_TEXTCMD);
if (!vec)
break;
sclp_eval_textcmd((struct gds_subvector *)(vec + 1),
@@ -703,7 +703,7 @@ sclp_tty_state_change(struct sclp_register *reg)
static struct sclp_register sclp_input_event =
{
- .receive_mask = EvTyp_OpCmd_Mask | EvTyp_PMsgCmd_Mask,
+ .receive_mask = EVTYP_OPCMD_MASK | EVTYP_PMSGCMD_MASK,
.state_change_fn = sclp_tty_state_change,
.receiver_fn = sclp_tty_receiver
};
diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c
index f77dc33b5f8d..726334757bbf 100644
--- a/drivers/s390/char/sclp_vt220.c
+++ b/drivers/s390/char/sclp_vt220.c
@@ -99,8 +99,8 @@ static void sclp_vt220_emit_current(void);
/* Registration structure for our interest in SCLP event buffers */
static struct sclp_register sclp_vt220_register = {
- .send_mask = EvTyp_VT220Msg_Mask,
- .receive_mask = EvTyp_VT220Msg_Mask,
+ .send_mask = EVTYP_VT220MSG_MASK,
+ .receive_mask = EVTYP_VT220MSG_MASK,
.state_change_fn = NULL,
.receiver_fn = sclp_vt220_receiver_fn
};
@@ -202,7 +202,7 @@ sclp_vt220_callback(struct sclp_req *request, void *data)
static int
__sclp_vt220_emit(struct sclp_vt220_request *request)
{
- if (!(sclp_vt220_register.sclp_send_mask & EvTyp_VT220Msg_Mask)) {
+ if (!(sclp_vt220_register.sclp_send_mask & EVTYP_VT220MSG_MASK)) {
request->sclp_req.status = SCLP_REQ_FAILED;
return -EIO;
}
@@ -284,7 +284,7 @@ sclp_vt220_initialize_page(void *page)
sccb->header.length = sizeof(struct sclp_vt220_sccb);
sccb->header.function_code = SCLP_NORMAL_WRITE;
sccb->header.response_code = 0x0000;
- sccb->evbuf.type = EvTyp_VT220Msg;
+ sccb->evbuf.type = EVTYP_VT220MSG;
sccb->evbuf.length = sizeof(struct evbuf_header);
return request;
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index b87d3b019936..a5a00e9ae4d0 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -125,7 +125,7 @@ static struct vmlogrdr_priv_t sys_ser[] = {
.recording_name = "EREP",
.minor_num = 0,
.buffer_free = 1,
- .priv_lock = SPIN_LOCK_UNLOCKED,
+ .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[0].priv_lock),
.autorecording = 1,
.autopurge = 1,
},
@@ -134,7 +134,7 @@ static struct vmlogrdr_priv_t sys_ser[] = {
.recording_name = "ACCOUNT",
.minor_num = 1,
.buffer_free = 1,
- .priv_lock = SPIN_LOCK_UNLOCKED,
+ .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[1].priv_lock),
.autorecording = 1,
.autopurge = 1,
},
@@ -143,7 +143,7 @@ static struct vmlogrdr_priv_t sys_ser[] = {
.recording_name = "SYMPTOM",
.minor_num = 2,
.buffer_free = 1,
- .priv_lock = SPIN_LOCK_UNLOCKED,
+ .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[2].priv_lock),
.autorecording = 1,
.autopurge = 1,
}
@@ -385,6 +385,9 @@ static int vmlogrdr_release (struct inode *inode, struct file *filp)
struct vmlogrdr_priv_t * logptr = filp->private_data;
+ iucv_path_sever(logptr->path, NULL);
+ kfree(logptr->path);
+ logptr->path = NULL;
if (logptr->autorecording) {
ret = vmlogrdr_recording(logptr,0,logptr->autopurge);
if (ret)
diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c
new file mode 100644
index 000000000000..89d439316a53
--- /dev/null
+++ b/drivers/s390/char/zcore.c
@@ -0,0 +1,651 @@
+/*
+ * zcore module to export memory content and register sets for creating system
+ * dumps on SCSI disks (zfcpdump). The "zcore/mem" debugfs file shows the same
+ * dump format as s390 standalone dumps.
+ *
+ * For more information please refer to Documentation/s390/zfcpdump.txt
+ *
+ * Copyright IBM Corp. 2003,2007
+ * Author(s): Michael Holzheu
+ */
+
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/utsname.h>
+#include <linux/debugfs.h>
+#include <asm/ipl.h>
+#include <asm/sclp.h>
+#include <asm/setup.h>
+#include <asm/sigp.h>
+#include <asm/uaccess.h>
+#include <asm/debug.h>
+#include <asm/processor.h>
+#include <asm/irqflags.h>
+
+#define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x)
+#define MSG(x...) printk( KERN_ALERT x )
+#define ERROR_MSG(x...) printk ( KERN_ALERT "DUMP: " x )
+
+#define TO_USER 0
+#define TO_KERNEL 1
+
+enum arch_id {
+ ARCH_S390 = 0,
+ ARCH_S390X = 1,
+};
+
+/* dump system info */
+
+struct sys_info {
+ enum arch_id arch;
+ unsigned long sa_base;
+ u32 sa_size;
+ int cpu_map[NR_CPUS];
+ unsigned long mem_size;
+ union save_area lc_mask;
+};
+
+static struct sys_info sys_info;
+static struct debug_info *zcore_dbf;
+static int hsa_available;
+static struct dentry *zcore_dir;
+static struct dentry *zcore_file;
+
+/*
+ * Copy memory from HSA to kernel or user memory (not reentrant):
+ *
+ * @dest: Kernel or user buffer where memory should be copied to
+ * @src: Start address within HSA where data should be copied
+ * @count: Size of buffer, which should be copied
+ * @mode: Either TO_KERNEL or TO_USER
+ */
+static int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode)
+{
+ int offs, blk_num;
+ static char buf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
+
+ if (count == 0)
+ return 0;
+
+ /* copy first block */
+ offs = 0;
+ if ((src % PAGE_SIZE) != 0) {
+ blk_num = src / PAGE_SIZE + 2;
+ if (sclp_sdias_copy(buf, blk_num, 1)) {
+ TRACE("sclp_sdias_copy() failed\n");
+ return -EIO;
+ }
+ offs = min((PAGE_SIZE - (src % PAGE_SIZE)), count);
+ if (mode == TO_USER) {
+ if (copy_to_user((__force __user void*) dest,
+ buf + (src % PAGE_SIZE), offs))
+ return -EFAULT;
+ } else
+ memcpy(dest, buf + (src % PAGE_SIZE), offs);
+ }
+ if (offs == count)
+ goto out;
+
+ /* copy middle */
+ for (; (offs + PAGE_SIZE) <= count; offs += PAGE_SIZE) {
+ blk_num = (src + offs) / PAGE_SIZE + 2;
+ if (sclp_sdias_copy(buf, blk_num, 1)) {
+ TRACE("sclp_sdias_copy() failed\n");
+ return -EIO;
+ }
+ if (mode == TO_USER) {
+ if (copy_to_user((__force __user void*) dest + offs,
+ buf, PAGE_SIZE))
+ return -EFAULT;
+ } else
+ memcpy(dest + offs, buf, PAGE_SIZE);
+ }
+ if (offs == count)
+ goto out;
+
+ /* copy last block */
+ blk_num = (src + offs) / PAGE_SIZE + 2;
+ if (sclp_sdias_copy(buf, blk_num, 1)) {
+ TRACE("sclp_sdias_copy() failed\n");
+ return -EIO;
+ }
+ if (mode == TO_USER) {
+ if (copy_to_user((__force __user void*) dest + offs, buf,
+ PAGE_SIZE))
+ return -EFAULT;
+ } else
+ memcpy(dest + offs, buf, count - offs);
+out:
+ return 0;
+}
+
+static int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count)
+{
+ return memcpy_hsa((void __force *) dest, src, count, TO_USER);
+}
+
+static int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count)
+{
+ return memcpy_hsa(dest, src, count, TO_KERNEL);
+}
+
+static int memcpy_real(void *dest, unsigned long src, size_t count)
+{
+ unsigned long flags;
+ int rc = -EFAULT;
+ register unsigned long _dest asm("2") = (unsigned long) dest;
+ register unsigned long _len1 asm("3") = (unsigned long) count;
+ register unsigned long _src asm("4") = src;
+ register unsigned long _len2 asm("5") = (unsigned long) count;
+
+ if (count == 0)
+ return 0;
+ flags = __raw_local_irq_stnsm(0xf8); /* switch to real mode */
+ asm volatile (
+ "0: mvcle %1,%2,0x0\n"
+ "1: jo 0b\n"
+ " lhi %0,0x0\n"
+ "2:\n"
+ EX_TABLE(1b,2b)
+ : "+d" (rc)
+ : "d" (_dest), "d" (_src), "d" (_len1), "d" (_len2)
+ : "cc", "memory");
+ __raw_local_irq_ssm(flags);
+
+ return rc;
+}
+
+static int memcpy_real_user(__user void *dest, unsigned long src, size_t count)
+{
+ static char buf[4096];
+ int offs = 0, size;
+
+ while (offs < count) {
+ size = min(sizeof(buf), count - offs);
+ if (memcpy_real(buf, src + offs, size))
+ return -EFAULT;
+ if (copy_to_user(dest + offs, buf, size))
+ return -EFAULT;
+ offs += size;
+ }
+ return 0;
+}
+
+#ifdef __s390x__
+/*
+ * Convert s390x (64 bit) cpu info to s390 (32 bit) cpu info
+ */
+static void __init s390x_to_s390_regs(union save_area *out, union save_area *in,
+ int cpu)
+{
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ out->s390.gp_regs[i] = in->s390x.gp_regs[i] & 0x00000000ffffffff;
+ out->s390.acc_regs[i] = in->s390x.acc_regs[i];
+ out->s390.ctrl_regs[i] =
+ in->s390x.ctrl_regs[i] & 0x00000000ffffffff;
+ }
+ /* locore for 31 bit has only space for fpregs 0,2,4,6 */
+ out->s390.fp_regs[0] = in->s390x.fp_regs[0];
+ out->s390.fp_regs[1] = in->s390x.fp_regs[2];
+ out->s390.fp_regs[2] = in->s390x.fp_regs[4];
+ out->s390.fp_regs[3] = in->s390x.fp_regs[6];
+ memcpy(&(out->s390.psw[0]), &(in->s390x.psw[0]), 4);
+ out->s390.psw[1] |= 0x8; /* set bit 12 */
+ memcpy(&(out->s390.psw[4]),&(in->s390x.psw[12]), 4);
+ out->s390.psw[4] |= 0x80; /* set (31bit) addressing bit */
+ out->s390.pref_reg = in->s390x.pref_reg;
+ out->s390.timer = in->s390x.timer;
+ out->s390.clk_cmp = in->s390x.clk_cmp;
+}
+
+static void __init s390x_to_s390_save_areas(void)
+{
+ int i = 1;
+ static union save_area tmp;
+
+ while (zfcpdump_save_areas[i]) {
+ s390x_to_s390_regs(&tmp, zfcpdump_save_areas[i], i);
+ memcpy(zfcpdump_save_areas[i], &tmp, sizeof(tmp));
+ i++;
+ }
+}
+
+#endif /* __s390x__ */
+
+static int __init init_cpu_info(enum arch_id arch)
+{
+ union save_area *sa;
+
+ /* get info for boot cpu from lowcore, stored in the HSA */
+
+ sa = kmalloc(sizeof(*sa), GFP_KERNEL);
+ if (!sa) {
+ ERROR_MSG("kmalloc failed: %s: %i\n",__FUNCTION__, __LINE__);
+ return -ENOMEM;
+ }
+ if (memcpy_hsa_kernel(sa, sys_info.sa_base, sys_info.sa_size) < 0) {
+ ERROR_MSG("could not copy from HSA\n");
+ kfree(sa);
+ return -EIO;
+ }
+ zfcpdump_save_areas[0] = sa;
+
+#ifdef __s390x__
+ /* convert s390x regs to s390, if we are dumping an s390 Linux */
+
+ if (arch == ARCH_S390)
+ s390x_to_s390_save_areas();
+#endif
+
+ return 0;
+}
+
+static DEFINE_MUTEX(zcore_mutex);
+
+#define DUMP_VERSION 0x3
+#define DUMP_MAGIC 0xa8190173618f23fdULL
+#define DUMP_ARCH_S390X 2
+#define DUMP_ARCH_S390 1
+#define HEADER_SIZE 4096
+
+/* dump header dumped according to s390 crash dump format */
+
+struct zcore_header {
+ u64 magic;
+ u32 version;
+ u32 header_size;
+ u32 dump_level;
+ u32 page_size;
+ u64 mem_size;
+ u64 mem_start;
+ u64 mem_end;
+ u32 num_pages;
+ u32 pad1;
+ u64 tod;
+ cpuid_t cpu_id;
+ u32 arch_id;
+ u32 build_arch;
+ char pad2[4016];
+} __attribute__((packed,__aligned__(16)));
+
+static struct zcore_header zcore_header = {
+ .magic = DUMP_MAGIC,
+ .version = DUMP_VERSION,
+ .header_size = 4096,
+ .dump_level = 0,
+ .page_size = PAGE_SIZE,
+ .mem_start = 0,
+#ifdef __s390x__
+ .build_arch = DUMP_ARCH_S390X,
+#else
+ .build_arch = DUMP_ARCH_S390,
+#endif
+};
+
+/*
+ * Copy lowcore info to buffer. Use map in order to copy only register parts.
+ *
+ * @buf: User buffer
+ * @sa: Pointer to save area
+ * @sa_off: Offset in save area to copy
+ * @len: Number of bytes to copy
+ */
+static int copy_lc(void __user *buf, void *sa, int sa_off, int len)
+{
+ int i;
+ char *lc_mask = (char*)&sys_info.lc_mask;
+
+ for (i = 0; i < len; i++) {
+ if (!lc_mask[i + sa_off])
+ continue;
+ if (copy_to_user(buf + i, sa + sa_off + i, 1))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/*
+ * Copy lowcores info to memory, if necessary
+ *
+ * @buf: User buffer
+ * @addr: Start address of buffer in dump memory
+ * @count: Size of buffer
+ */
+static int zcore_add_lc(char __user *buf, unsigned long start, size_t count)
+{
+ unsigned long end;
+ int i = 0;
+
+ if (count == 0)
+ return 0;
+
+ end = start + count;
+ while (zfcpdump_save_areas[i]) {
+ unsigned long cp_start, cp_end; /* copy range */
+ unsigned long sa_start, sa_end; /* save area range */
+ unsigned long prefix;
+ unsigned long sa_off, len, buf_off;
+
+ if (sys_info.arch == ARCH_S390)
+ prefix = zfcpdump_save_areas[i]->s390.pref_reg;
+ else
+ prefix = zfcpdump_save_areas[i]->s390x.pref_reg;
+
+ sa_start = prefix + sys_info.sa_base;
+ sa_end = prefix + sys_info.sa_base + sys_info.sa_size;
+
+ if ((end < sa_start) || (start > sa_end))
+ goto next;
+ cp_start = max(start, sa_start);
+ cp_end = min(end, sa_end);
+
+ buf_off = cp_start - start;
+ sa_off = cp_start - sa_start;
+ len = cp_end - cp_start;
+
+ TRACE("copy_lc for: %lx\n", start);
+ if (copy_lc(buf + buf_off, zfcpdump_save_areas[i], sa_off, len))
+ return -EFAULT;
+next:
+ i++;
+ }
+ return 0;
+}
+
+/*
+ * Read routine for zcore character device
+ * First 4K are dump header
+ * Next 32MB are HSA Memory
+ * Rest is read from absolute Memory
+ */
+static ssize_t zcore_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ unsigned long mem_start; /* Start address in memory */
+ size_t mem_offs; /* Offset in dump memory */
+ size_t hdr_count; /* Size of header part of output buffer */
+ size_t size;
+ int rc;
+
+ mutex_lock(&zcore_mutex);
+
+ if (*ppos > (sys_info.mem_size + HEADER_SIZE)) {
+ rc = -EINVAL;
+ goto fail;
+ }
+
+ count = min(count, (size_t) (sys_info.mem_size + HEADER_SIZE - *ppos));
+
+ /* Copy dump header */
+ if (*ppos < HEADER_SIZE) {
+ size = min(count, (size_t) (HEADER_SIZE - *ppos));
+ if (copy_to_user(buf, &zcore_header + *ppos, size)) {
+ rc = -EFAULT;
+ goto fail;
+ }
+ hdr_count = size;
+ mem_start = 0;
+ } else {
+ hdr_count = 0;
+ mem_start = *ppos - HEADER_SIZE;
+ }
+
+ mem_offs = 0;
+
+ /* Copy from HSA data */
+ if (*ppos < (ZFCPDUMP_HSA_SIZE + HEADER_SIZE)) {
+ size = min((count - hdr_count), (size_t) (ZFCPDUMP_HSA_SIZE
+ - mem_start));
+ rc = memcpy_hsa_user(buf + hdr_count, mem_start, size);
+ if (rc)
+ goto fail;
+
+ mem_offs += size;
+ }
+
+ /* Copy from real mem */
+ size = count - mem_offs - hdr_count;
+ rc = memcpy_real_user(buf + hdr_count + mem_offs, mem_start + mem_offs,
+ size);
+ if (rc)
+ goto fail;
+
+ /*
+ * Since s390 dump analysis tools like lcrash or crash
+ * expect register sets in the prefix pages of the cpus,
+ * we copy them into the read buffer, if necessary.
+ * buf + hdr_count: Start of memory part of output buffer
+ * mem_start: Start memory address to copy from
+ * count - hdr_count: Size of memory area to copy
+ */
+ if (zcore_add_lc(buf + hdr_count, mem_start, count - hdr_count)) {
+ rc = -EFAULT;
+ goto fail;
+ }
+ *ppos += count;
+fail:
+ mutex_unlock(&zcore_mutex);
+ return (rc < 0) ? rc : count;
+}
+
+static int zcore_open(struct inode *inode, struct file *filp)
+{
+ if (!hsa_available)
+ return -ENODATA;
+ else
+ return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
+}
+
+static int zcore_release(struct inode *inode, struct file *filep)
+{
+ diag308(DIAG308_REL_HSA, NULL);
+ hsa_available = 0;
+ return 0;
+}
+
+static loff_t zcore_lseek(struct file *file, loff_t offset, int orig)
+{
+ loff_t rc;
+
+ mutex_lock(&zcore_mutex);
+ switch (orig) {
+ case 0:
+ file->f_pos = offset;
+ rc = file->f_pos;
+ break;
+ case 1:
+ file->f_pos += offset;
+ rc = file->f_pos;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&zcore_mutex);
+ return rc;
+}
+
+static struct file_operations zcore_fops = {
+ .owner = THIS_MODULE,
+ .llseek = zcore_lseek,
+ .read = zcore_read,
+ .open = zcore_open,
+ .release = zcore_release,
+};
+
+
+static void __init set_s390_lc_mask(union save_area *map)
+{
+ memset(&map->s390.ext_save, 0xff, sizeof(map->s390.ext_save));
+ memset(&map->s390.timer, 0xff, sizeof(map->s390.timer));
+ memset(&map->s390.clk_cmp, 0xff, sizeof(map->s390.clk_cmp));
+ memset(&map->s390.psw, 0xff, sizeof(map->s390.psw));
+ memset(&map->s390.pref_reg, 0xff, sizeof(map->s390.pref_reg));
+ memset(&map->s390.acc_regs, 0xff, sizeof(map->s390.acc_regs));
+ memset(&map->s390.fp_regs, 0xff, sizeof(map->s390.fp_regs));
+ memset(&map->s390.gp_regs, 0xff, sizeof(map->s390.gp_regs));
+ memset(&map->s390.ctrl_regs, 0xff, sizeof(map->s390.ctrl_regs));
+}
+
+static void __init set_s390x_lc_mask(union save_area *map)
+{
+ memset(&map->s390x.fp_regs, 0xff, sizeof(map->s390x.fp_regs));
+ memset(&map->s390x.gp_regs, 0xff, sizeof(map->s390x.gp_regs));
+ memset(&map->s390x.psw, 0xff, sizeof(map->s390x.psw));
+ memset(&map->s390x.pref_reg, 0xff, sizeof(map->s390x.pref_reg));
+ memset(&map->s390x.fp_ctrl_reg, 0xff, sizeof(map->s390x.fp_ctrl_reg));
+ memset(&map->s390x.tod_reg, 0xff, sizeof(map->s390x.tod_reg));
+ memset(&map->s390x.timer, 0xff, sizeof(map->s390x.timer));
+ memset(&map->s390x.clk_cmp, 0xff, sizeof(map->s390x.clk_cmp));
+ memset(&map->s390x.acc_regs, 0xff, sizeof(map->s390x.acc_regs));
+ memset(&map->s390x.ctrl_regs, 0xff, sizeof(map->s390x.ctrl_regs));
+}
+
+/*
+ * Initialize dump globals for a given architecture
+ */
+static int __init sys_info_init(enum arch_id arch)
+{
+ switch (arch) {
+ case ARCH_S390X:
+ MSG("DETECTED 'S390X (64 bit) OS'\n");
+ sys_info.sa_base = SAVE_AREA_BASE_S390X;
+ sys_info.sa_size = sizeof(struct save_area_s390x);
+ set_s390x_lc_mask(&sys_info.lc_mask);
+ break;
+ case ARCH_S390:
+ MSG("DETECTED 'S390 (32 bit) OS'\n");
+ sys_info.sa_base = SAVE_AREA_BASE_S390;
+ sys_info.sa_size = sizeof(struct save_area_s390);
+ set_s390_lc_mask(&sys_info.lc_mask);
+ break;
+ default:
+ ERROR_MSG("unknown architecture 0x%x.\n",arch);
+ return -EINVAL;
+ }
+ sys_info.arch = arch;
+ if (init_cpu_info(arch)) {
+ ERROR_MSG("get cpu info failed\n");
+ return -ENOMEM;
+ }
+ sys_info.mem_size = real_memory_size;
+
+ return 0;
+}
+
+static int __init check_sdias(void)
+{
+ int rc, act_hsa_size;
+
+ rc = sclp_sdias_blk_count();
+ if (rc < 0) {
+ ERROR_MSG("Could not determine HSA size\n");
+ return rc;
+ }
+ act_hsa_size = (rc - 1) * PAGE_SIZE;
+ if (act_hsa_size < ZFCPDUMP_HSA_SIZE) {
+ ERROR_MSG("HSA size too small: %i\n", act_hsa_size);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void __init zcore_header_init(int arch, struct zcore_header *hdr)
+{
+ if (arch == ARCH_S390X)
+ hdr->arch_id = DUMP_ARCH_S390X;
+ else
+ hdr->arch_id = DUMP_ARCH_S390;
+ hdr->mem_size = sys_info.mem_size;
+ hdr->mem_end = sys_info.mem_size;
+ hdr->num_pages = sys_info.mem_size / PAGE_SIZE;
+ hdr->tod = get_clock();
+ get_cpu_id(&hdr->cpu_id);
+}
+
+extern int sdias_init(void);
+
+static int __init zcore_init(void)
+{
+ unsigned char arch;
+ int rc;
+
+ if (ipl_info.type != IPL_TYPE_FCP_DUMP)
+ return -ENODATA;
+
+ zcore_dbf = debug_register("zcore", 4, 1, 4 * sizeof(long));
+ debug_register_view(zcore_dbf, &debug_sprintf_view);
+ debug_set_level(zcore_dbf, 6);
+
+ TRACE("devno: %x\n", ipl_info.data.fcp.dev_id.devno);
+ TRACE("wwpn: %llx\n", (unsigned long long) ipl_info.data.fcp.wwpn);
+ TRACE("lun: %llx\n", (unsigned long long) ipl_info.data.fcp.lun);
+
+ rc = sdias_init();
+ if (rc)
+ goto fail;
+
+ rc = check_sdias();
+ if (rc) {
+ ERROR_MSG("Dump initialization failed\n");
+ goto fail;
+ }
+
+ rc = memcpy_hsa_kernel(&arch, __LC_AR_MODE_ID, 1);
+ if (rc) {
+ ERROR_MSG("sdial memcpy for arch id failed\n");
+ goto fail;
+ }
+
+#ifndef __s390x__
+ if (arch == ARCH_S390X) {
+ ERROR_MSG("32 bit dumper can't dump 64 bit system!\n");
+ rc = -EINVAL;
+ goto fail;
+ }
+#endif
+
+ rc = sys_info_init(arch);
+ if (rc) {
+ ERROR_MSG("arch init failed\n");
+ goto fail;
+ }
+
+ zcore_header_init(arch, &zcore_header);
+
+ zcore_dir = debugfs_create_dir("zcore" , NULL);
+ if (!zcore_dir) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+ zcore_file = debugfs_create_file("mem", S_IRUSR, zcore_dir, NULL,
+ &zcore_fops);
+ if (!zcore_file) {
+ debugfs_remove(zcore_dir);
+ rc = -ENOMEM;
+ goto fail;
+ }
+ hsa_available = 1;
+ return 0;
+
+fail:
+ diag308(DIAG308_REL_HSA, NULL);
+ return rc;
+}
+
+extern void sdias_exit(void);
+
+static void __exit zcore_exit(void)
+{
+ debug_unregister(zcore_dbf);
+ sdias_exit();
+ diag308(DIAG308_REL_HSA, NULL);
+}
+
+MODULE_AUTHOR("Copyright IBM Corp. 2003,2007");
+MODULE_DESCRIPTION("zcore module for zfcpdump support");
+MODULE_LICENSE("GPL");
+
+subsys_initcall(zcore_init);
+module_exit(zcore_exit);
diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile
index c490c2a1c2fc..cfaf77b320f5 100644
--- a/drivers/s390/cio/Makefile
+++ b/drivers/s390/cio/Makefile
@@ -2,7 +2,7 @@
# Makefile for the S/390 common i/o drivers
#
-obj-y += airq.o blacklist.o chsc.o cio.o css.o
+obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o
ccw_device-objs += device.o device_fsm.o device_ops.o
ccw_device-objs += device_id.o device_pgid.o device_status.o
obj-y += ccw_device.o cmf.o
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index 5aeb68e732b0..e5ccda63e883 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -75,8 +75,10 @@ static void ccwgroup_ungroup_callback(struct device *dev)
{
struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
+ mutex_lock(&gdev->reg_mutex);
__ccwgroup_remove_symlinks(gdev);
device_unregister(dev);
+ mutex_unlock(&gdev->reg_mutex);
}
static ssize_t
@@ -173,7 +175,8 @@ ccwgroup_create(struct device *root,
return -ENOMEM;
atomic_set(&gdev->onoff, 0);
-
+ mutex_init(&gdev->reg_mutex);
+ mutex_lock(&gdev->reg_mutex);
for (i = 0; i < argc; i++) {
gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]);
@@ -183,12 +186,12 @@ ccwgroup_create(struct device *root,
|| gdev->cdev[i]->id.driver_info !=
gdev->cdev[0]->id.driver_info) {
rc = -EINVAL;
- goto free_dev;
+ goto error;
}
/* Don't allow a device to belong to more than one group. */
if (gdev->cdev[i]->dev.driver_data) {
rc = -EINVAL;
- goto free_dev;
+ goto error;
}
gdev->cdev[i]->dev.driver_data = gdev;
}
@@ -203,9 +206,8 @@ ccwgroup_create(struct device *root,
gdev->cdev[0]->dev.bus_id);
rc = device_register(&gdev->dev);
-
if (rc)
- goto free_dev;
+ goto error;
get_device(&gdev->dev);
rc = device_create_file(&gdev->dev, &dev_attr_ungroup);
@@ -216,6 +218,7 @@ ccwgroup_create(struct device *root,
rc = __ccwgroup_create_symlinks(gdev);
if (!rc) {
+ mutex_unlock(&gdev->reg_mutex);
put_device(&gdev->dev);
return 0;
}
@@ -224,19 +227,12 @@ ccwgroup_create(struct device *root,
error:
for (i = 0; i < argc; i++)
if (gdev->cdev[i]) {
- put_device(&gdev->cdev[i]->dev);
- gdev->cdev[i]->dev.driver_data = NULL;
- }
- put_device(&gdev->dev);
- return rc;
-free_dev:
- for (i = 0; i < argc; i++)
- if (gdev->cdev[i]) {
if (gdev->cdev[i]->dev.driver_data == gdev)
gdev->cdev[i]->dev.driver_data = NULL;
put_device(&gdev->cdev[i]->dev);
}
- kfree(gdev);
+ mutex_unlock(&gdev->reg_mutex);
+ put_device(&gdev->dev);
return rc;
}
@@ -422,8 +418,12 @@ ccwgroup_driver_unregister (struct ccwgroup_driver *cdriver)
get_driver(&cdriver->driver);
while ((dev = driver_find_device(&cdriver->driver, NULL, NULL,
__ccwgroup_match_all))) {
- __ccwgroup_remove_symlinks(to_ccwgroupdev(dev));
+ struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
+
+ mutex_lock(&gdev->reg_mutex);
+ __ccwgroup_remove_symlinks(gdev);
device_unregister(dev);
+ mutex_unlock(&gdev->reg_mutex);
put_device(dev);
}
put_driver(&cdriver->driver);
@@ -444,8 +444,10 @@ __ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev)
if (cdev->dev.driver_data) {
gdev = (struct ccwgroup_device *)cdev->dev.driver_data;
if (get_device(&gdev->dev)) {
+ mutex_lock(&gdev->reg_mutex);
if (device_is_registered(&gdev->dev))
return gdev;
+ mutex_unlock(&gdev->reg_mutex);
put_device(&gdev->dev);
}
return NULL;
@@ -465,6 +467,7 @@ ccwgroup_remove_ccwdev(struct ccw_device *cdev)
if (gdev) {
__ccwgroup_remove_symlinks(gdev);
device_unregister(&gdev->dev);
+ mutex_unlock(&gdev->reg_mutex);
put_device(&gdev->dev);
}
}
diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c
new file mode 100644
index 000000000000..ac289e6eadfe
--- /dev/null
+++ b/drivers/s390/cio/chp.c
@@ -0,0 +1,683 @@
+/*
+ * drivers/s390/cio/chp.c
+ *
+ * Copyright IBM Corp. 1999,2007
+ * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
+ * Arnd Bergmann (arndb@de.ibm.com)
+ * Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/bug.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <asm/errno.h>
+#include <asm/chpid.h>
+#include <asm/sclp.h>
+
+#include "cio.h"
+#include "css.h"
+#include "ioasm.h"
+#include "cio_debug.h"
+#include "chp.h"
+
+#define to_channelpath(device) container_of(device, struct channel_path, dev)
+#define CHP_INFO_UPDATE_INTERVAL 1*HZ
+
+enum cfg_task_t {
+ cfg_none,
+ cfg_configure,
+ cfg_deconfigure
+};
+
+/* Map for pending configure tasks. */
+static enum cfg_task_t chp_cfg_task[__MAX_CSSID + 1][__MAX_CHPID + 1];
+static DEFINE_MUTEX(cfg_lock);
+static int cfg_busy;
+
+/* Map for channel-path status. */
+static struct sclp_chp_info chp_info;
+static DEFINE_MUTEX(info_lock);
+
+/* Time after which channel-path status may be outdated. */
+static unsigned long chp_info_expires;
+
+/* Workqueue to perform pending configure tasks. */
+static struct workqueue_struct *chp_wq;
+static struct work_struct cfg_work;
+
+/* Wait queue for configure completion events. */
+static wait_queue_head_t cfg_wait_queue;
+
+/* Return channel_path struct for given chpid. */
+static inline struct channel_path *chpid_to_chp(struct chp_id chpid)
+{
+ return css[chpid.cssid]->chps[chpid.id];
+}
+
+/* Set vary state for given chpid. */
+static void set_chp_logically_online(struct chp_id chpid, int onoff)
+{
+ chpid_to_chp(chpid)->state = onoff;
+}
+
+/* On succes return 0 if channel-path is varied offline, 1 if it is varied
+ * online. Return -ENODEV if channel-path is not registered. */
+int chp_get_status(struct chp_id chpid)
+{
+ return (chpid_to_chp(chpid) ? chpid_to_chp(chpid)->state : -ENODEV);
+}
+
+/**
+ * chp_get_sch_opm - return opm for subchannel
+ * @sch: subchannel
+ *
+ * Calculate and return the operational path mask (opm) based on the chpids
+ * used by the subchannel and the status of the associated channel-paths.
+ */
+u8 chp_get_sch_opm(struct subchannel *sch)
+{
+ struct chp_id chpid;
+ int opm;
+ int i;
+
+ opm = 0;
+ chp_id_init(&chpid);
+ for (i=0; i < 8; i++) {
+ opm <<= 1;
+ chpid.id = sch->schib.pmcw.chpid[i];
+ if (chp_get_status(chpid) != 0)
+ opm |= 1;
+ }
+ return opm;
+}
+
+/**
+ * chp_is_registered - check if a channel-path is registered
+ * @chpid: channel-path ID
+ *
+ * Return non-zero if a channel-path with the given chpid is registered,
+ * zero otherwise.
+ */
+int chp_is_registered(struct chp_id chpid)
+{
+ return chpid_to_chp(chpid) != NULL;
+}
+
+/*
+ * Function: s390_vary_chpid
+ * Varies the specified chpid online or offline
+ */
+static int s390_vary_chpid(struct chp_id chpid, int on)
+{
+ char dbf_text[15];
+ int status;
+
+ sprintf(dbf_text, on?"varyon%x.%02x":"varyoff%x.%02x", chpid.cssid,
+ chpid.id);
+ CIO_TRACE_EVENT( 2, dbf_text);
+
+ status = chp_get_status(chpid);
+ if (status < 0) {
+ printk(KERN_ERR "Can't vary unknown chpid %x.%02x\n",
+ chpid.cssid, chpid.id);
+ return -EINVAL;
+ }
+
+ if (!on && !status) {
+ printk(KERN_ERR "chpid %x.%02x is already offline\n",
+ chpid.cssid, chpid.id);
+ return -EINVAL;
+ }
+
+ set_chp_logically_online(chpid, on);
+ chsc_chp_vary(chpid, on);
+ return 0;
+}
+
+/*
+ * Channel measurement related functions
+ */
+static ssize_t chp_measurement_chars_read(struct kobject *kobj, char *buf,
+ loff_t off, size_t count)
+{
+ struct channel_path *chp;
+ unsigned int size;
+
+ chp = to_channelpath(container_of(kobj, struct device, kobj));
+ if (!chp->cmg_chars)
+ return 0;
+
+ size = sizeof(struct cmg_chars);
+
+ if (off > size)
+ return 0;
+ if (off + count > size)
+ count = size - off;
+ memcpy(buf, chp->cmg_chars + off, count);
+ return count;
+}
+
+static struct bin_attribute chp_measurement_chars_attr = {
+ .attr = {
+ .name = "measurement_chars",
+ .mode = S_IRUSR,
+ .owner = THIS_MODULE,
+ },
+ .size = sizeof(struct cmg_chars),
+ .read = chp_measurement_chars_read,
+};
+
+static void chp_measurement_copy_block(struct cmg_entry *buf,
+ struct channel_subsystem *css,
+ struct chp_id chpid)
+{
+ void *area;
+ struct cmg_entry *entry, reference_buf;
+ int idx;
+
+ if (chpid.id < 128) {
+ area = css->cub_addr1;
+ idx = chpid.id;
+ } else {
+ area = css->cub_addr2;
+ idx = chpid.id - 128;
+ }
+ entry = area + (idx * sizeof(struct cmg_entry));
+ do {
+ memcpy(buf, entry, sizeof(*entry));
+ memcpy(&reference_buf, entry, sizeof(*entry));
+ } while (reference_buf.values[0] != buf->values[0]);
+}
+
+static ssize_t chp_measurement_read(struct kobject *kobj, char *buf,
+ loff_t off, size_t count)
+{
+ struct channel_path *chp;
+ struct channel_subsystem *css;
+ unsigned int size;
+
+ chp = to_channelpath(container_of(kobj, struct device, kobj));
+ css = to_css(chp->dev.parent);
+
+ size = sizeof(struct cmg_entry);
+
+ /* Only allow single reads. */
+ if (off || count < size)
+ return 0;
+ chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->chpid);
+ count = size;
+ return count;
+}
+
+static struct bin_attribute chp_measurement_attr = {
+ .attr = {
+ .name = "measurement",
+ .mode = S_IRUSR,
+ .owner = THIS_MODULE,
+ },
+ .size = sizeof(struct cmg_entry),
+ .read = chp_measurement_read,
+};
+
+void chp_remove_cmg_attr(struct channel_path *chp)
+{
+ device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr);
+ device_remove_bin_file(&chp->dev, &chp_measurement_attr);
+}
+
+int chp_add_cmg_attr(struct channel_path *chp)
+{
+ int ret;
+
+ ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr);
+ if (ret)
+ return ret;
+ ret = device_create_bin_file(&chp->dev, &chp_measurement_attr);
+ if (ret)
+ device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr);
+ return ret;
+}
+
+/*
+ * Files for the channel path entries.
+ */
+static ssize_t chp_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct channel_path *chp = container_of(dev, struct channel_path, dev);
+
+ if (!chp)
+ return 0;
+ return (chp_get_status(chp->chpid) ? sprintf(buf, "online\n") :
+ sprintf(buf, "offline\n"));
+}
+
+static ssize_t chp_status_write(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct channel_path *cp = container_of(dev, struct channel_path, dev);
+ char cmd[10];
+ int num_args;
+ int error;
+
+ num_args = sscanf(buf, "%5s", cmd);
+ if (!num_args)
+ return count;
+
+ if (!strnicmp(cmd, "on", 2) || !strcmp(cmd, "1"))
+ error = s390_vary_chpid(cp->chpid, 1);
+ else if (!strnicmp(cmd, "off", 3) || !strcmp(cmd, "0"))
+ error = s390_vary_chpid(cp->chpid, 0);
+ else
+ error = -EINVAL;
+
+ return error < 0 ? error : count;
+
+}
+
+static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write);
+
+static ssize_t chp_configure_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct channel_path *cp;
+ int status;
+
+ cp = container_of(dev, struct channel_path, dev);
+ status = chp_info_get_status(cp->chpid);
+ if (status < 0)
+ return status;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", status);
+}
+
+static int cfg_wait_idle(void);
+
+static ssize_t chp_configure_write(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct channel_path *cp;
+ int val;
+ char delim;
+
+ if (sscanf(buf, "%d %c", &val, &delim) != 1)
+ return -EINVAL;
+ if (val != 0 && val != 1)
+ return -EINVAL;
+ cp = container_of(dev, struct channel_path, dev);
+ chp_cfg_schedule(cp->chpid, val);
+ cfg_wait_idle();
+
+ return count;
+}
+
+static DEVICE_ATTR(configure, 0644, chp_configure_show, chp_configure_write);
+
+static ssize_t chp_type_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct channel_path *chp = container_of(dev, struct channel_path, dev);
+
+ if (!chp)
+ return 0;
+ return sprintf(buf, "%x\n", chp->desc.desc);
+}
+
+static DEVICE_ATTR(type, 0444, chp_type_show, NULL);
+
+static ssize_t chp_cmg_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct channel_path *chp = to_channelpath(dev);
+
+ if (!chp)
+ return 0;
+ if (chp->cmg == -1) /* channel measurements not available */
+ return sprintf(buf, "unknown\n");
+ return sprintf(buf, "%x\n", chp->cmg);
+}
+
+static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL);
+
+static ssize_t chp_shared_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct channel_path *chp = to_channelpath(dev);
+
+ if (!chp)
+ return 0;
+ if (chp->shared == -1) /* channel measurements not available */
+ return sprintf(buf, "unknown\n");
+ return sprintf(buf, "%x\n", chp->shared);
+}
+
+static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL);
+
+static struct attribute * chp_attrs[] = {
+ &dev_attr_status.attr,
+ &dev_attr_configure.attr,
+ &dev_attr_type.attr,
+ &dev_attr_cmg.attr,
+ &dev_attr_shared.attr,
+ NULL,
+};
+
+static struct attribute_group chp_attr_group = {
+ .attrs = chp_attrs,
+};
+
+static void chp_release(struct device *dev)
+{
+ struct channel_path *cp;
+
+ cp = container_of(dev, struct channel_path, dev);
+ kfree(cp);
+}
+
+/**
+ * chp_new - register a new channel-path
+ * @chpid - channel-path ID
+ *
+ * Create and register data structure representing new channel-path. Return
+ * zero on success, non-zero otherwise.
+ */
+int chp_new(struct chp_id chpid)
+{
+ struct channel_path *chp;
+ int ret;
+
+ if (chp_is_registered(chpid))
+ return 0;
+ chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL);
+ if (!chp)
+ return -ENOMEM;
+
+ /* fill in status, etc. */
+ chp->chpid = chpid;
+ chp->state = 1;
+ chp->dev.parent = &css[chpid.cssid]->device;
+ chp->dev.release = chp_release;
+ snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp%x.%02x", chpid.cssid,
+ chpid.id);
+
+ /* Obtain channel path description and fill it in. */
+ ret = chsc_determine_channel_path_description(chpid, &chp->desc);
+ if (ret)
+ goto out_free;
+ if ((chp->desc.flags & 0x80) == 0) {
+ ret = -ENODEV;
+ goto out_free;
+ }
+ /* Get channel-measurement characteristics. */
+ if (css_characteristics_avail && css_chsc_characteristics.scmc
+ && css_chsc_characteristics.secm) {
+ ret = chsc_get_channel_measurement_chars(chp);
+ if (ret)
+ goto out_free;
+ } else {
+ static int msg_done;
+
+ if (!msg_done) {
+ printk(KERN_WARNING "cio: Channel measurements not "
+ "available, continuing.\n");
+ msg_done = 1;
+ }
+ chp->cmg = -1;
+ }
+
+ /* make it known to the system */
+ ret = device_register(&chp->dev);
+ if (ret) {
+ printk(KERN_WARNING "%s: could not register %x.%02x\n",
+ __func__, chpid.cssid, chpid.id);
+ goto out_free;
+ }
+ ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group);
+ if (ret) {
+ device_unregister(&chp->dev);
+ goto out_free;
+ }
+ mutex_lock(&css[chpid.cssid]->mutex);
+ if (css[chpid.cssid]->cm_enabled) {
+ ret = chp_add_cmg_attr(chp);
+ if (ret) {
+ sysfs_remove_group(&chp->dev.kobj, &chp_attr_group);
+ device_unregister(&chp->dev);
+ mutex_unlock(&css[chpid.cssid]->mutex);
+ goto out_free;
+ }
+ }
+ css[chpid.cssid]->chps[chpid.id] = chp;
+ mutex_unlock(&css[chpid.cssid]->mutex);
+ return ret;
+out_free:
+ kfree(chp);
+ return ret;
+}
+
+/**
+ * chp_get_chp_desc - return newly allocated channel-path description
+ * @chpid: channel-path ID
+ *
+ * On success return a newly allocated copy of the channel-path description
+ * data associated with the given channel-path ID. Return %NULL on error.
+ */
+void *chp_get_chp_desc(struct chp_id chpid)
+{
+ struct channel_path *chp;
+ struct channel_path_desc *desc;
+
+ chp = chpid_to_chp(chpid);
+ if (!chp)
+ return NULL;
+ desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL);
+ if (!desc)
+ return NULL;
+ memcpy(desc, &chp->desc, sizeof(struct channel_path_desc));
+ return desc;
+}
+
+/**
+ * chp_process_crw - process channel-path status change
+ * @id: channel-path ID number
+ * @status: non-zero if channel-path has become available, zero otherwise
+ *
+ * Handle channel-report-words indicating that the status of a channel-path
+ * has changed.
+ */
+void chp_process_crw(int id, int status)
+{
+ struct chp_id chpid;
+
+ chp_id_init(&chpid);
+ chpid.id = id;
+ if (status) {
+ if (!chp_is_registered(chpid))
+ chp_new(chpid);
+ chsc_chp_online(chpid);
+ } else
+ chsc_chp_offline(chpid);
+}
+
+static inline int info_bit_num(struct chp_id id)
+{
+ return id.id + id.cssid * (__MAX_CHPID + 1);
+}
+
+/* Force chp_info refresh on next call to info_validate(). */
+static void info_expire(void)
+{
+ mutex_lock(&info_lock);
+ chp_info_expires = jiffies - 1;
+ mutex_unlock(&info_lock);
+}
+
+/* Ensure that chp_info is up-to-date. */
+static int info_update(void)
+{
+ int rc;
+
+ mutex_lock(&info_lock);
+ rc = 0;
+ if (time_after(jiffies, chp_info_expires)) {
+ /* Data is too old, update. */
+ rc = sclp_chp_read_info(&chp_info);
+ chp_info_expires = jiffies + CHP_INFO_UPDATE_INTERVAL ;
+ }
+ mutex_unlock(&info_lock);
+
+ return rc;
+}
+
+/**
+ * chp_info_get_status - retrieve configure status of a channel-path
+ * @chpid: channel-path ID
+ *
+ * On success, return 0 for standby, 1 for configured, 2 for reserved,
+ * 3 for not recognized. Return negative error code on error.
+ */
+int chp_info_get_status(struct chp_id chpid)
+{
+ int rc;
+ int bit;
+
+ rc = info_update();
+ if (rc)
+ return rc;
+
+ bit = info_bit_num(chpid);
+ mutex_lock(&info_lock);
+ if (!chp_test_bit(chp_info.recognized, bit))
+ rc = CHP_STATUS_NOT_RECOGNIZED;
+ else if (chp_test_bit(chp_info.configured, bit))
+ rc = CHP_STATUS_CONFIGURED;
+ else if (chp_test_bit(chp_info.standby, bit))
+ rc = CHP_STATUS_STANDBY;
+ else
+ rc = CHP_STATUS_RESERVED;
+ mutex_unlock(&info_lock);
+
+ return rc;
+}
+
+/* Return configure task for chpid. */
+static enum cfg_task_t cfg_get_task(struct chp_id chpid)
+{
+ return chp_cfg_task[chpid.cssid][chpid.id];
+}
+
+/* Set configure task for chpid. */
+static void cfg_set_task(struct chp_id chpid, enum cfg_task_t cfg)
+{
+ chp_cfg_task[chpid.cssid][chpid.id] = cfg;
+}
+
+/* Perform one configure/deconfigure request. Reschedule work function until
+ * last request. */
+static void cfg_func(struct work_struct *work)
+{
+ struct chp_id chpid;
+ enum cfg_task_t t;
+
+ mutex_lock(&cfg_lock);
+ t = cfg_none;
+ chp_id_for_each(&chpid) {
+ t = cfg_get_task(chpid);
+ if (t != cfg_none) {
+ cfg_set_task(chpid, cfg_none);
+ break;
+ }
+ }
+ mutex_unlock(&cfg_lock);
+
+ switch (t) {
+ case cfg_configure:
+ sclp_chp_configure(chpid);
+ info_expire();
+ chsc_chp_online(chpid);
+ break;
+ case cfg_deconfigure:
+ sclp_chp_deconfigure(chpid);
+ info_expire();
+ chsc_chp_offline(chpid);
+ break;
+ case cfg_none:
+ /* Get updated information after last change. */
+ info_update();
+ mutex_lock(&cfg_lock);
+ cfg_busy = 0;
+ mutex_unlock(&cfg_lock);
+ wake_up_interruptible(&cfg_wait_queue);
+ return;
+ }
+ queue_work(chp_wq, &cfg_work);
+}
+
+/**
+ * chp_cfg_schedule - schedule chpid configuration request
+ * @chpid - channel-path ID
+ * @configure - Non-zero for configure, zero for deconfigure
+ *
+ * Schedule a channel-path configuration/deconfiguration request.
+ */
+void chp_cfg_schedule(struct chp_id chpid, int configure)
+{
+ CIO_MSG_EVENT(2, "chp_cfg_sched%x.%02x=%d\n", chpid.cssid, chpid.id,
+ configure);
+ mutex_lock(&cfg_lock);
+ cfg_set_task(chpid, configure ? cfg_configure : cfg_deconfigure);
+ cfg_busy = 1;
+ mutex_unlock(&cfg_lock);
+ queue_work(chp_wq, &cfg_work);
+}
+
+/**
+ * chp_cfg_cancel_deconfigure - cancel chpid deconfiguration request
+ * @chpid - channel-path ID
+ *
+ * Cancel an active channel-path deconfiguration request if it has not yet
+ * been performed.
+ */
+void chp_cfg_cancel_deconfigure(struct chp_id chpid)
+{
+ CIO_MSG_EVENT(2, "chp_cfg_cancel:%x.%02x\n", chpid.cssid, chpid.id);
+ mutex_lock(&cfg_lock);
+ if (cfg_get_task(chpid) == cfg_deconfigure)
+ cfg_set_task(chpid, cfg_none);
+ mutex_unlock(&cfg_lock);
+}
+
+static int cfg_wait_idle(void)
+{
+ if (wait_event_interruptible(cfg_wait_queue, !cfg_busy))
+ return -ERESTARTSYS;
+ return 0;
+}
+
+static int __init chp_init(void)
+{
+ struct chp_id chpid;
+
+ chp_wq = create_singlethread_workqueue("cio_chp");
+ if (!chp_wq)
+ return -ENOMEM;
+ INIT_WORK(&cfg_work, cfg_func);
+ init_waitqueue_head(&cfg_wait_queue);
+ if (info_update())
+ return 0;
+ /* Register available channel-paths. */
+ chp_id_for_each(&chpid) {
+ if (chp_info_get_status(chpid) != CHP_STATUS_NOT_RECOGNIZED)
+ chp_new(chpid);
+ }
+
+ return 0;
+}
+
+subsys_initcall(chp_init);
diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h
new file mode 100644
index 000000000000..65286563c592
--- /dev/null
+++ b/drivers/s390/cio/chp.h
@@ -0,0 +1,53 @@
+/*
+ * drivers/s390/cio/chp.h
+ *
+ * Copyright IBM Corp. 2007
+ * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#ifndef S390_CHP_H
+#define S390_CHP_H S390_CHP_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <asm/chpid.h>
+#include "chsc.h"
+
+#define CHP_STATUS_STANDBY 0
+#define CHP_STATUS_CONFIGURED 1
+#define CHP_STATUS_RESERVED 2
+#define CHP_STATUS_NOT_RECOGNIZED 3
+
+static inline int chp_test_bit(u8 *bitmap, int num)
+{
+ int byte = num >> 3;
+ int mask = 128 >> (num & 7);
+
+ return (bitmap[byte] & mask) ? 1 : 0;
+}
+
+
+struct channel_path {
+ struct chp_id chpid;
+ int state;
+ struct channel_path_desc desc;
+ /* Channel-measurement related stuff: */
+ int cmg;
+ int shared;
+ void *cmg_chars;
+ struct device dev;
+};
+
+int chp_get_status(struct chp_id chpid);
+u8 chp_get_sch_opm(struct subchannel *sch);
+int chp_is_registered(struct chp_id chpid);
+void *chp_get_chp_desc(struct chp_id chpid);
+void chp_process_crw(int id, int available);
+void chp_remove_cmg_attr(struct channel_path *chp);
+int chp_add_cmg_attr(struct channel_path *chp);
+int chp_new(struct chp_id chpid);
+void chp_cfg_schedule(struct chp_id chpid, int configure);
+void chp_cfg_cancel_deconfigure(struct chp_id chpid);
+int chp_info_get_status(struct chp_id chpid);
+
+#endif /* S390_CHP_H */
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 6f05a44e3817..ea92ac4d6577 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -15,202 +15,124 @@
#include <linux/device.h>
#include <asm/cio.h>
+#include <asm/chpid.h>
#include "css.h"
#include "cio.h"
#include "cio_debug.h"
#include "ioasm.h"
+#include "chp.h"
#include "chsc.h"
static void *sei_page;
-static int new_channel_path(int chpid);
-
-static inline void
-set_chp_logically_online(int chp, int onoff)
-{
- css[0]->chps[chp]->state = onoff;
-}
-
-static int
-get_chp_status(int chp)
-{
- return (css[0]->chps[chp] ? css[0]->chps[chp]->state : -ENODEV);
-}
-
-void
-chsc_validate_chpids(struct subchannel *sch)
-{
- int mask, chp;
-
- for (chp = 0; chp <= 7; chp++) {
- mask = 0x80 >> chp;
- if (!get_chp_status(sch->schib.pmcw.chpid[chp]))
- /* disable using this path */
- sch->opm &= ~mask;
- }
-}
-
-void
-chpid_is_actually_online(int chp)
-{
- int state;
-
- state = get_chp_status(chp);
- if (state < 0) {
- need_rescan = 1;
- queue_work(slow_path_wq, &slow_path_work);
- } else
- WARN_ON(!state);
-}
+struct chsc_ssd_area {
+ struct chsc_header request;
+ u16 :10;
+ u16 ssid:2;
+ u16 :4;
+ u16 f_sch; /* first subchannel */
+ u16 :16;
+ u16 l_sch; /* last subchannel */
+ u32 :32;
+ struct chsc_header response;
+ u32 :32;
+ u8 sch_valid : 1;
+ u8 dev_valid : 1;
+ u8 st : 3; /* subchannel type */
+ u8 zeroes : 3;
+ u8 unit_addr; /* unit address */
+ u16 devno; /* device number */
+ u8 path_mask;
+ u8 fla_valid_mask;
+ u16 sch; /* subchannel */
+ u8 chpid[8]; /* chpids 0-7 */
+ u16 fla[8]; /* full link addresses 0-7 */
+} __attribute__ ((packed));
-/* FIXME: this is _always_ called for every subchannel. shouldn't we
- * process more than one at a time? */
-static int
-chsc_get_sch_desc_irq(struct subchannel *sch, void *page)
+int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
{
- int ccode, j;
-
- struct {
- struct chsc_header request;
- u16 reserved1a:10;
- u16 ssid:2;
- u16 reserved1b:4;
- u16 f_sch; /* first subchannel */
- u16 reserved2;
- u16 l_sch; /* last subchannel */
- u32 reserved3;
- struct chsc_header response;
- u32 reserved4;
- u8 sch_valid : 1;
- u8 dev_valid : 1;
- u8 st : 3; /* subchannel type */
- u8 zeroes : 3;
- u8 unit_addr; /* unit address */
- u16 devno; /* device number */
- u8 path_mask;
- u8 fla_valid_mask;
- u16 sch; /* subchannel */
- u8 chpid[8]; /* chpids 0-7 */
- u16 fla[8]; /* full link addresses 0-7 */
- } __attribute__ ((packed)) *ssd_area;
-
- ssd_area = page;
+ unsigned long page;
+ struct chsc_ssd_area *ssd_area;
+ int ccode;
+ int ret;
+ int i;
+ int mask;
+ page = get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!page)
+ return -ENOMEM;
+ ssd_area = (struct chsc_ssd_area *) page;
ssd_area->request.length = 0x0010;
ssd_area->request.code = 0x0004;
-
- ssd_area->ssid = sch->schid.ssid;
- ssd_area->f_sch = sch->schid.sch_no;
- ssd_area->l_sch = sch->schid.sch_no;
+ ssd_area->ssid = schid.ssid;
+ ssd_area->f_sch = schid.sch_no;
+ ssd_area->l_sch = schid.sch_no;
ccode = chsc(ssd_area);
+ /* Check response. */
if (ccode > 0) {
- pr_debug("chsc returned with ccode = %d\n", ccode);
- return (ccode == 3) ? -ENODEV : -EBUSY;
+ ret = (ccode == 3) ? -ENODEV : -EBUSY;
+ goto out_free;
}
-
- switch (ssd_area->response.code) {
- case 0x0001: /* everything ok */
- break;
- case 0x0002:
- CIO_CRW_EVENT(2, "Invalid command!\n");
- return -EINVAL;
- case 0x0003:
- CIO_CRW_EVENT(2, "Error in chsc request block!\n");
- return -EINVAL;
- case 0x0004:
- CIO_CRW_EVENT(2, "Model does not provide ssd\n");
- return -EOPNOTSUPP;
- default:
- CIO_CRW_EVENT(2, "Unknown CHSC response %d\n",
+ if (ssd_area->response.code != 0x0001) {
+ CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n",
+ schid.ssid, schid.sch_no,
ssd_area->response.code);
- return -EIO;
+ ret = -EIO;
+ goto out_free;
}
-
- /*
- * ssd_area->st stores the type of the detected
- * subchannel, with the following definitions:
- *
- * 0: I/O subchannel: All fields have meaning
- * 1: CHSC subchannel: Only sch_val, st and sch
- * have meaning
- * 2: Message subchannel: All fields except unit_addr
- * have meaning
- * 3: ADM subchannel: Only sch_val, st and sch
- * have meaning
- *
- * Other types are currently undefined.
- */
- if (ssd_area->st > 3) { /* uhm, that looks strange... */
- CIO_CRW_EVENT(0, "Strange subchannel type %d"
- " 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
- * fields have meaning and what to do with it we just jump out
- */
- return 0;
- } else {
- const char *type[4] = {"I/O", "chsc", "message", "ADM"};
- 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;
+ if (!ssd_area->sch_valid) {
+ ret = -ENODEV;
+ goto out_free;
}
-
- if (ssd_area->st == 0 || ssd_area->st == 2) {
- for (j = 0; j < 8; j++) {
- if (!((0x80 >> j) & ssd_area->path_mask &
- ssd_area->fla_valid_mask))
- continue;
- sch->ssd_info.chpid[j] = ssd_area->chpid[j];
- sch->ssd_info.fla[j] = ssd_area->fla[j];
+ /* Copy data */
+ ret = 0;
+ memset(ssd, 0, sizeof(struct chsc_ssd_info));
+ if ((ssd_area->st != 0) && (ssd_area->st != 2))
+ goto out_free;
+ ssd->path_mask = ssd_area->path_mask;
+ ssd->fla_valid_mask = ssd_area->fla_valid_mask;
+ for (i = 0; i < 8; i++) {
+ mask = 0x80 >> i;
+ if (ssd_area->path_mask & mask) {
+ chp_id_init(&ssd->chpid[i]);
+ ssd->chpid[i].id = ssd_area->chpid[i];
}
+ if (ssd_area->fla_valid_mask & mask)
+ ssd->fla[i] = ssd_area->fla[i];
}
- return 0;
+out_free:
+ free_page(page);
+ return ret;
}
-int
-css_get_ssd_info(struct subchannel *sch)
+static int check_for_io_on_path(struct subchannel *sch, int mask)
{
- int ret;
- void *page;
+ int cc;
- page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!page)
- return -ENOMEM;
- spin_lock_irq(sch->lock);
- ret = chsc_get_sch_desc_irq(sch, page);
- if (ret) {
- static int cio_chsc_err_msg;
-
- if (!cio_chsc_err_msg) {
- printk(KERN_ERR
- "chsc_get_sch_descriptions:"
- " Error %d while doing chsc; "
- "processing some machine checks may "
- "not work\n", ret);
- cio_chsc_err_msg = 1;
- }
- }
- spin_unlock_irq(sch->lock);
- free_page((unsigned long)page);
- if (!ret) {
- int j, chpid, mask;
- /* Allocate channel path structures, if needed. */
- for (j = 0; j < 8; j++) {
- mask = 0x80 >> j;
- chpid = sch->ssd_info.chpid[j];
- if ((sch->schib.pmcw.pim & mask) &&
- (get_chp_status(chpid) < 0))
- new_channel_path(chpid);
- }
+ cc = stsch(sch->schid, &sch->schib);
+ if (cc)
+ return 0;
+ if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == mask)
+ return 1;
+ return 0;
+}
+
+static void terminate_internal_io(struct subchannel *sch)
+{
+ if (cio_clear(sch)) {
+ /* Recheck device in case clear failed. */
+ sch->lpm = 0;
+ if (device_trigger_verify(sch) != 0)
+ css_schedule_eval(sch->schid);
+ return;
}
- return ret;
+ /* Request retry of internal operation. */
+ device_set_intretry(sch);
+ /* Call handler. */
+ if (sch->driver && sch->driver->termination)
+ sch->driver->termination(&sch->dev);
}
static int
@@ -219,7 +141,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
int j;
int mask;
struct subchannel *sch;
- struct channel_path *chpid;
+ struct chp_id *chpid;
struct schib schib;
sch = to_subchannel(dev);
@@ -243,106 +165,50 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
if (sch->schib.pmcw.pim == 0x80)
goto out_unreg;
- if ((sch->schib.scsw.actl & SCSW_ACTL_DEVACT) &&
- (sch->schib.scsw.actl & SCSW_ACTL_SCHACT) &&
- (sch->schib.pmcw.lpum == mask)) {
- int cc;
-
- cc = cio_clear(sch);
- if (cc == -ENODEV)
+ if (check_for_io_on_path(sch, mask)) {
+ if (device_is_online(sch))
+ device_kill_io(sch);
+ else {
+ terminate_internal_io(sch);
+ /* Re-start path verification. */
+ if (sch->driver && sch->driver->verify)
+ sch->driver->verify(&sch->dev);
+ }
+ } else {
+ /* trigger path verification. */
+ if (sch->driver && sch->driver->verify)
+ sch->driver->verify(&sch->dev);
+ else if (sch->lpm == mask)
goto out_unreg;
- /* Request retry of internal operation. */
- device_set_intretry(sch);
- /* Call handler. */
- if (sch->driver && sch->driver->termination)
- sch->driver->termination(&sch->dev);
- goto out_unlock;
}
- /* trigger path verification. */
- if (sch->driver && sch->driver->verify)
- sch->driver->verify(&sch->dev);
- else if (sch->lpm == mask)
- goto out_unreg;
-out_unlock:
spin_unlock_irq(sch->lock);
return 0;
+
out_unreg:
- spin_unlock_irq(sch->lock);
sch->lpm = 0;
- if (css_enqueue_subchannel_slow(sch->schid)) {
- css_clear_subchannel_slow_list();
- need_rescan = 1;
- }
+ spin_unlock_irq(sch->lock);
+ css_schedule_eval(sch->schid);
return 0;
}
-static void
-s390_set_chpid_offline( __u8 chpid)
+void chsc_chp_offline(struct chp_id chpid)
{
char dbf_txt[15];
- struct device *dev;
- sprintf(dbf_txt, "chpr%x", chpid);
+ sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id);
CIO_TRACE_EVENT(2, dbf_txt);
- if (get_chp_status(chpid) <= 0)
+ if (chp_get_status(chpid) <= 0)
return;
- dev = get_device(&css[0]->chps[chpid]->dev);
- bus_for_each_dev(&css_bus_type, NULL, to_channelpath(dev),
+ bus_for_each_dev(&css_bus_type, NULL, &chpid,
s390_subchannel_remove_chpid);
-
- if (need_rescan || css_slow_subchannels_exist())
- queue_work(slow_path_wq, &slow_path_work);
- put_device(dev);
-}
-
-struct res_acc_data {
- struct channel_path *chp;
- u32 fla_mask;
- u16 fla;
-};
-
-static int
-s390_process_res_acc_sch(struct res_acc_data *res_data, struct subchannel *sch)
-{
- int found;
- int chp;
- int ccode;
-
- found = 0;
- for (chp = 0; chp <= 7; chp++)
- /*
- * check if chpid is in information updated by ssd
- */
- if (sch->ssd_info.valid &&
- sch->ssd_info.chpid[chp] == res_data->chp->id &&
- (sch->ssd_info.fla[chp] & res_data->fla_mask)
- == res_data->fla) {
- found = 1;
- break;
- }
-
- if (found == 0)
- return 0;
-
- /*
- * Do a stsch to update our subchannel structure with the
- * new path information and eventually check for logically
- * offline chpids.
- */
- ccode = stsch(sch->schid, &sch->schib);
- if (ccode > 0)
- return 0;
-
- return 0x80 >> chp;
}
static int
s390_process_res_acc_new_sch(struct subchannel_id schid)
{
struct schib schib;
- int ret;
/*
* We don't know the device yet, but since a path
* may be available now to the device we'll have
@@ -353,14 +219,35 @@ s390_process_res_acc_new_sch(struct subchannel_id schid)
*/
if (stsch_err(schid, &schib))
/* We're through */
- return need_rescan ? -EAGAIN : -ENXIO;
+ return -ENXIO;
/* Put it on the slow path. */
- ret = css_enqueue_subchannel_slow(schid);
- if (ret) {
- css_clear_subchannel_slow_list();
- need_rescan = 1;
- return -EAGAIN;
+ css_schedule_eval(schid);
+ return 0;
+}
+
+struct res_acc_data {
+ struct chp_id chpid;
+ u32 fla_mask;
+ u16 fla;
+};
+
+static int get_res_chpid_mask(struct chsc_ssd_info *ssd,
+ struct res_acc_data *data)
+{
+ int i;
+ int mask;
+
+ for (i = 0; i < 8; i++) {
+ mask = 0x80 >> i;
+ if (!(ssd->path_mask & mask))
+ continue;
+ if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid))
+ continue;
+ if ((ssd->fla_valid_mask & mask) &&
+ ((ssd->fla[i] & data->fla_mask) != data->fla))
+ continue;
+ return mask;
}
return 0;
}
@@ -379,14 +266,11 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
return s390_process_res_acc_new_sch(schid);
spin_lock_irq(sch->lock);
-
- chp_mask = s390_process_res_acc_sch(res_data, sch);
-
- if (chp_mask == 0) {
- spin_unlock_irq(sch->lock);
- put_device(&sch->dev);
- return 0;
- }
+ chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data);
+ if (chp_mask == 0)
+ goto out;
+ if (stsch(sch->schid, &sch->schib))
+ goto out;
old_lpm = sch->lpm;
sch->lpm = ((sch->schib.pmcw.pim &
sch->schib.pmcw.pam &
@@ -396,20 +280,18 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
device_trigger_reprobe(sch);
else if (sch->driver && sch->driver->verify)
sch->driver->verify(&sch->dev);
-
+out:
spin_unlock_irq(sch->lock);
put_device(&sch->dev);
return 0;
}
-
-static int
-s390_process_res_acc (struct res_acc_data *res_data)
+static void s390_process_res_acc (struct res_acc_data *res_data)
{
- int rc;
char dbf_txt[15];
- sprintf(dbf_txt, "accpr%x", res_data->chp->id);
+ sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid,
+ res_data->chpid.id);
CIO_TRACE_EVENT( 2, dbf_txt);
if (res_data->fla != 0) {
sprintf(dbf_txt, "fla%x", res_data->fla);
@@ -423,12 +305,7 @@ s390_process_res_acc (struct res_acc_data *res_data)
* The more information we have (info), the less scanning
* will we have to do.
*/
- rc = for_each_subchannel(__s390_process_res_acc, res_data);
- if (css_slow_subchannels_exist())
- rc = -EAGAIN;
- else if (rc != -EAGAIN)
- rc = 0;
- return rc;
+ for_each_subchannel(__s390_process_res_acc, res_data);
}
static int
@@ -480,43 +357,45 @@ struct chsc_sei_area {
/* 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)
+static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area)
{
- int chpid;
+ struct chp_id chpid;
+ int id;
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)
+ return;
+ id = __get_chpid_from_lir(sei_area->ccdf);
+ if (id < 0)
CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n");
- else
- s390_set_chpid_offline(chpid);
-
- return 0;
+ else {
+ chp_id_init(&chpid);
+ chpid.id = id;
+ chsc_chp_offline(chpid);
+ }
}
-static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
+static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
{
struct res_acc_data res_data;
- struct device *dev;
+ struct chp_id chpid;
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;
+ return;
+ chp_id_init(&chpid);
+ chpid.id = sei_area->rsid;
/* allocate a new channel path structure, if needed */
- status = get_chp_status(sei_area->rsid);
+ status = chp_get_status(chpid);
if (status < 0)
- new_channel_path(sei_area->rsid);
+ chp_new(chpid);
else if (!status)
- return 0;
- dev = get_device(&css[0]->chps[sei_area->rsid]->dev);
+ return;
memset(&res_data, 0, sizeof(struct res_acc_data));
- res_data.chp = to_channelpath(dev);
+ res_data.chpid = chpid;
if ((sei_area->vf & 0xc0) != 0) {
res_data.fla = sei_area->fla;
if ((sei_area->vf & 0xc0) == 0xc0)
@@ -526,51 +405,82 @@ static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
/* link address */
res_data.fla_mask = 0xff00;
}
- rc = s390_process_res_acc(&res_data);
- put_device(dev);
-
- return rc;
+ s390_process_res_acc(&res_data);
}
-static int chsc_process_sei(struct chsc_sei_area *sei_area)
+struct chp_config_data {
+ u8 map[32];
+ u8 op;
+ u8 pc;
+};
+
+static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
{
- int rc;
+ struct chp_config_data *data;
+ struct chp_id chpid;
+ int num;
+
+ CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n");
+ if (sei_area->rs != 0)
+ return;
+ data = (struct chp_config_data *) &(sei_area->ccdf);
+ chp_id_init(&chpid);
+ for (num = 0; num <= __MAX_CHPID; num++) {
+ if (!chp_test_bit(data->map, num))
+ continue;
+ chpid.id = num;
+ printk(KERN_WARNING "cio: processing configure event %d for "
+ "chpid %x.%02x\n", data->op, chpid.cssid, chpid.id);
+ switch (data->op) {
+ case 0:
+ chp_cfg_schedule(chpid, 1);
+ break;
+ case 1:
+ chp_cfg_schedule(chpid, 0);
+ break;
+ case 2:
+ chp_cfg_cancel_deconfigure(chpid);
+ break;
+ }
+ }
+}
+static void chsc_process_sei(struct chsc_sei_area *sei_area)
+{
/* Check if we might have lost some information. */
- if (sei_area->flags & 0x40)
+ if (sei_area->flags & 0x40) {
CIO_CRW_EVENT(2, "chsc: event overflow\n");
+ css_schedule_eval_all();
+ }
/* which kind of information was stored? */
- rc = 0;
switch (sei_area->cc) {
case 1: /* link incident*/
- rc = chsc_process_sei_link_incident(sei_area);
+ chsc_process_sei_link_incident(sei_area);
break;
case 2: /* i/o resource accessibiliy */
- rc = chsc_process_sei_res_acc(sei_area);
+ chsc_process_sei_res_acc(sei_area);
+ break;
+ case 8: /* channel-path-configuration notification */
+ chsc_process_sei_chp_config(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)
+void chsc_process_crw(void)
{
struct chsc_sei_area *sei_area;
- int ret;
- int rc;
if (!sei_page)
- return 0;
+ return;
/* 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 {
memset(sei_area, 0, sizeof(*sei_area));
sei_area->request.length = 0x0010;
@@ -580,37 +490,26 @@ int chsc_process_crw(void)
if (sei_area->response.code == 0x0001) {
CIO_CRW_EVENT(4, "chsc: sei successful\n");
- rc = chsc_process_sei(sei_area);
- if (rc)
- ret = rc;
+ chsc_process_sei(sei_area);
} else {
CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n",
sei_area->response.code);
- ret = 0;
break;
}
} while (sei_area->flags & 0x80);
-
- return ret;
}
static int
__chp_add_new_sch(struct subchannel_id schid)
{
struct schib schib;
- int ret;
if (stsch_err(schid, &schib))
/* We're through */
- return need_rescan ? -EAGAIN : -ENXIO;
+ return -ENXIO;
/* Put it on the slow path. */
- ret = css_enqueue_subchannel_slow(schid);
- if (ret) {
- css_clear_subchannel_slow_list();
- need_rescan = 1;
- return -EAGAIN;
- }
+ css_schedule_eval(schid);
return 0;
}
@@ -619,10 +518,10 @@ static int
__chp_add(struct subchannel_id schid, void *data)
{
int i, mask;
- struct channel_path *chp;
+ struct chp_id *chpid;
struct subchannel *sch;
- chp = data;
+ chpid = data;
sch = get_subchannel_by_schid(schid);
if (!sch)
/* Check if the subchannel is now available. */
@@ -631,7 +530,7 @@ __chp_add(struct subchannel_id schid, void *data)
for (i=0; i<8; i++) {
mask = 0x80 >> i;
if ((sch->schib.pmcw.pim & mask) &&
- (sch->schib.pmcw.chpid[i] == chp->id)) {
+ (sch->schib.pmcw.chpid[i] == chpid->id)) {
if (stsch(sch->schid, &sch->schib) != 0) {
/* Endgame. */
spin_unlock_irq(sch->lock);
@@ -657,122 +556,58 @@ __chp_add(struct subchannel_id schid, void *data)
return 0;
}
-static int
-chp_add(int chpid)
+void chsc_chp_online(struct chp_id chpid)
{
- int rc;
char dbf_txt[15];
- struct device *dev;
- if (!get_chp_status(chpid))
- return 0; /* no need to do the rest */
-
- sprintf(dbf_txt, "cadd%x", chpid);
+ sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id);
CIO_TRACE_EVENT(2, dbf_txt);
- dev = get_device(&css[0]->chps[chpid]->dev);
- rc = for_each_subchannel(__chp_add, to_channelpath(dev));
- if (css_slow_subchannels_exist())
- rc = -EAGAIN;
- if (rc != -EAGAIN)
- rc = 0;
- put_device(dev);
- return rc;
+ if (chp_get_status(chpid) != 0)
+ for_each_subchannel(__chp_add, &chpid);
}
-/*
- * Handling of crw machine checks with channel path source.
- */
-int
-chp_process_crw(int chpid, int on)
-{
- if (on == 0) {
- /* Path has gone. We use the link incident routine.*/
- s390_set_chpid_offline(chpid);
- return 0; /* De-register is async anyway. */
- }
- /*
- * Path has come. Allocate a new channel path structure,
- * if needed.
- */
- if (get_chp_status(chpid) < 0)
- new_channel_path(chpid);
- /* Avoid the extra overhead in process_rec_acc. */
- return chp_add(chpid);
-}
-
-static int check_for_io_on_path(struct subchannel *sch, int index)
-{
- int cc;
-
- cc = stsch(sch->schid, &sch->schib);
- if (cc)
- return 0;
- if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == (0x80 >> index))
- return 1;
- return 0;
-}
-
-static void terminate_internal_io(struct subchannel *sch)
-{
- if (cio_clear(sch)) {
- /* Recheck device in case clear failed. */
- sch->lpm = 0;
- if (device_trigger_verify(sch) != 0) {
- if(css_enqueue_subchannel_slow(sch->schid)) {
- css_clear_subchannel_slow_list();
- need_rescan = 1;
- }
- }
- return;
- }
- /* Request retry of internal operation. */
- device_set_intretry(sch);
- /* Call handler. */
- if (sch->driver && sch->driver->termination)
- sch->driver->termination(&sch->dev);
-}
-
-static void
-__s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on)
+static void __s390_subchannel_vary_chpid(struct subchannel *sch,
+ struct chp_id chpid, int on)
{
int chp, old_lpm;
+ int mask;
unsigned long flags;
- if (!sch->ssd_info.valid)
- return;
-
spin_lock_irqsave(sch->lock, flags);
old_lpm = sch->lpm;
for (chp = 0; chp < 8; chp++) {
- if (sch->ssd_info.chpid[chp] != chpid)
+ mask = 0x80 >> chp;
+ if (!(sch->ssd_info.path_mask & mask))
+ continue;
+ if (!chp_id_is_equal(&sch->ssd_info.chpid[chp], &chpid))
continue;
if (on) {
- sch->opm |= (0x80 >> chp);
- sch->lpm |= (0x80 >> chp);
+ sch->opm |= mask;
+ sch->lpm |= mask;
if (!old_lpm)
device_trigger_reprobe(sch);
else if (sch->driver && sch->driver->verify)
sch->driver->verify(&sch->dev);
break;
}
- sch->opm &= ~(0x80 >> chp);
- sch->lpm &= ~(0x80 >> chp);
- if (check_for_io_on_path(sch, chp)) {
+ sch->opm &= ~mask;
+ sch->lpm &= ~mask;
+ if (check_for_io_on_path(sch, mask)) {
if (device_is_online(sch))
/* Path verification is done after killing. */
device_kill_io(sch);
- else
+ else {
/* Kill and retry internal I/O. */
terminate_internal_io(sch);
- } else if (!sch->lpm) {
- if (device_trigger_verify(sch) != 0) {
- if (css_enqueue_subchannel_slow(sch->schid)) {
- css_clear_subchannel_slow_list();
- need_rescan = 1;
- }
+ /* Re-start path verification. */
+ if (sch->driver && sch->driver->verify)
+ sch->driver->verify(&sch->dev);
}
+ } else if (!sch->lpm) {
+ if (device_trigger_verify(sch) != 0)
+ css_schedule_eval(sch->schid);
} else if (sch->driver && sch->driver->verify)
sch->driver->verify(&sch->dev);
break;
@@ -780,11 +615,10 @@ __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on)
spin_unlock_irqrestore(sch->lock, flags);
}
-static int
-s390_subchannel_vary_chpid_off(struct device *dev, void *data)
+static int s390_subchannel_vary_chpid_off(struct device *dev, void *data)
{
struct subchannel *sch;
- __u8 *chpid;
+ struct chp_id *chpid;
sch = to_subchannel(dev);
chpid = data;
@@ -793,11 +627,10 @@ s390_subchannel_vary_chpid_off(struct device *dev, void *data)
return 0;
}
-static int
-s390_subchannel_vary_chpid_on(struct device *dev, void *data)
+static int s390_subchannel_vary_chpid_on(struct device *dev, void *data)
{
struct subchannel *sch;
- __u8 *chpid;
+ struct chp_id *chpid;
sch = to_subchannel(dev);
chpid = data;
@@ -821,40 +654,17 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data)
/* We're through */
return -ENXIO;
/* Put it on the slow path. */
- if (css_enqueue_subchannel_slow(schid)) {
- css_clear_subchannel_slow_list();
- need_rescan = 1;
- return -EAGAIN;
- }
+ css_schedule_eval(schid);
return 0;
}
-/*
- * Function: s390_vary_chpid
- * Varies the specified chpid online or offline
+/**
+ * chsc_chp_vary - propagate channel-path vary operation to subchannels
+ * @chpid: channl-path ID
+ * @on: non-zero for vary online, zero for vary offline
*/
-static int
-s390_vary_chpid( __u8 chpid, int on)
+int chsc_chp_vary(struct chp_id chpid, int on)
{
- char dbf_text[15];
- int status;
-
- sprintf(dbf_text, on?"varyon%x":"varyoff%x", chpid);
- CIO_TRACE_EVENT( 2, dbf_text);
-
- status = get_chp_status(chpid);
- if (status < 0) {
- printk(KERN_ERR "Can't vary unknown chpid %02X\n", chpid);
- return -EINVAL;
- }
-
- if (!on && !status) {
- printk(KERN_ERR "chpid %x is already offline\n", chpid);
- return -EINVAL;
- }
-
- set_chp_logically_online(chpid, on);
-
/*
* Redo PathVerification on the devices the chpid connects to
*/
@@ -865,118 +675,9 @@ s390_vary_chpid( __u8 chpid, int on)
if (on)
/* Scan for new devices on varied on path. */
for_each_subchannel(__s390_vary_chpid_on, NULL);
- if (need_rescan || css_slow_subchannels_exist())
- queue_work(slow_path_wq, &slow_path_work);
return 0;
}
-/*
- * Channel measurement related functions
- */
-static ssize_t
-chp_measurement_chars_read(struct kobject *kobj, char *buf, loff_t off,
- size_t count)
-{
- struct channel_path *chp;
- unsigned int size;
-
- chp = to_channelpath(container_of(kobj, struct device, kobj));
- if (!chp->cmg_chars)
- return 0;
-
- size = sizeof(struct cmg_chars);
-
- if (off > size)
- return 0;
- if (off + count > size)
- count = size - off;
- memcpy(buf, chp->cmg_chars + off, count);
- return count;
-}
-
-static struct bin_attribute chp_measurement_chars_attr = {
- .attr = {
- .name = "measurement_chars",
- .mode = S_IRUSR,
- .owner = THIS_MODULE,
- },
- .size = sizeof(struct cmg_chars),
- .read = chp_measurement_chars_read,
-};
-
-static void
-chp_measurement_copy_block(struct cmg_entry *buf,
- struct channel_subsystem *css, int chpid)
-{
- void *area;
- struct cmg_entry *entry, reference_buf;
- int idx;
-
- if (chpid < 128) {
- area = css->cub_addr1;
- idx = chpid;
- } else {
- area = css->cub_addr2;
- idx = chpid - 128;
- }
- entry = area + (idx * sizeof(struct cmg_entry));
- do {
- memcpy(buf, entry, sizeof(*entry));
- memcpy(&reference_buf, entry, sizeof(*entry));
- } while (reference_buf.values[0] != buf->values[0]);
-}
-
-static ssize_t
-chp_measurement_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
-{
- struct channel_path *chp;
- struct channel_subsystem *css;
- unsigned int size;
-
- chp = to_channelpath(container_of(kobj, struct device, kobj));
- css = to_css(chp->dev.parent);
-
- size = sizeof(struct cmg_entry);
-
- /* Only allow single reads. */
- if (off || count < size)
- return 0;
- chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->id);
- count = size;
- return count;
-}
-
-static struct bin_attribute chp_measurement_attr = {
- .attr = {
- .name = "measurement",
- .mode = S_IRUSR,
- .owner = THIS_MODULE,
- },
- .size = sizeof(struct cmg_entry),
- .read = chp_measurement_read,
-};
-
-static void
-chsc_remove_chp_cmg_attr(struct channel_path *chp)
-{
- device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr);
- device_remove_bin_file(&chp->dev, &chp_measurement_attr);
-}
-
-static int
-chsc_add_chp_cmg_attr(struct channel_path *chp)
-{
- int ret;
-
- ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr);
- if (ret)
- return ret;
- ret = device_create_bin_file(&chp->dev, &chp_measurement_attr);
- if (ret)
- device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr);
- return ret;
-}
-
static void
chsc_remove_cmg_attr(struct channel_subsystem *css)
{
@@ -985,7 +686,7 @@ chsc_remove_cmg_attr(struct channel_subsystem *css)
for (i = 0; i <= __MAX_CHPID; i++) {
if (!css->chps[i])
continue;
- chsc_remove_chp_cmg_attr(css->chps[i]);
+ chp_remove_cmg_attr(css->chps[i]);
}
}
@@ -998,7 +699,7 @@ chsc_add_cmg_attr(struct channel_subsystem *css)
for (i = 0; i <= __MAX_CHPID; i++) {
if (!css->chps[i])
continue;
- ret = chsc_add_chp_cmg_attr(css->chps[i]);
+ ret = chp_add_cmg_attr(css->chps[i]);
if (ret)
goto cleanup;
}
@@ -1007,12 +708,11 @@ cleanup:
for (--i; i >= 0; i--) {
if (!css->chps[i])
continue;
- chsc_remove_chp_cmg_attr(css->chps[i]);
+ chp_remove_cmg_attr(css->chps[i]);
}
return ret;
}
-
static int
__chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
{
@@ -1118,7 +818,7 @@ chsc_secm(struct channel_subsystem *css, int enable)
} else
chsc_remove_cmg_attr(css);
}
- if (enable && !css->cm_enabled) {
+ if (!css->cm_enabled) {
free_page((unsigned long)css->cub_addr1);
free_page((unsigned long)css->cub_addr2);
}
@@ -1127,109 +827,8 @@ chsc_secm(struct channel_subsystem *css, int enable)
return ret;
}
-/*
- * Files for the channel path entries.
- */
-static ssize_t
-chp_status_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct channel_path *chp = container_of(dev, struct channel_path, dev);
-
- if (!chp)
- return 0;
- return (get_chp_status(chp->id) ? sprintf(buf, "online\n") :
- sprintf(buf, "offline\n"));
-}
-
-static ssize_t
-chp_status_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct channel_path *cp = container_of(dev, struct channel_path, dev);
- char cmd[10];
- int num_args;
- int error;
-
- num_args = sscanf(buf, "%5s", cmd);
- if (!num_args)
- return count;
-
- if (!strnicmp(cmd, "on", 2))
- error = s390_vary_chpid(cp->id, 1);
- else if (!strnicmp(cmd, "off", 3))
- error = s390_vary_chpid(cp->id, 0);
- else
- error = -EINVAL;
-
- return error < 0 ? error : count;
-
-}
-
-static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write);
-
-static ssize_t
-chp_type_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct channel_path *chp = container_of(dev, struct channel_path, dev);
-
- if (!chp)
- return 0;
- return sprintf(buf, "%x\n", chp->desc.desc);
-}
-
-static DEVICE_ATTR(type, 0444, chp_type_show, NULL);
-
-static ssize_t
-chp_cmg_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct channel_path *chp = to_channelpath(dev);
-
- if (!chp)
- return 0;
- if (chp->cmg == -1) /* channel measurements not available */
- return sprintf(buf, "unknown\n");
- return sprintf(buf, "%x\n", chp->cmg);
-}
-
-static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL);
-
-static ssize_t
-chp_shared_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct channel_path *chp = to_channelpath(dev);
-
- if (!chp)
- return 0;
- if (chp->shared == -1) /* channel measurements not available */
- return sprintf(buf, "unknown\n");
- return sprintf(buf, "%x\n", chp->shared);
-}
-
-static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL);
-
-static struct attribute * chp_attrs[] = {
- &dev_attr_status.attr,
- &dev_attr_type.attr,
- &dev_attr_cmg.attr,
- &dev_attr_shared.attr,
- NULL,
-};
-
-static struct attribute_group chp_attr_group = {
- .attrs = chp_attrs,
-};
-
-static void
-chp_release(struct device *dev)
-{
- struct channel_path *cp;
-
- cp = container_of(dev, struct channel_path, dev);
- kfree(cp);
-}
-
-static int
-chsc_determine_channel_path_description(int chpid,
- struct channel_path_desc *desc)
+int chsc_determine_channel_path_description(struct chp_id chpid,
+ struct channel_path_desc *desc)
{
int ccode, ret;
@@ -1252,8 +851,8 @@ chsc_determine_channel_path_description(int chpid,
scpd_area->request.length = 0x0010;
scpd_area->request.code = 0x0002;
- scpd_area->first_chpid = chpid;
- scpd_area->last_chpid = chpid;
+ scpd_area->first_chpid = chpid.id;
+ scpd_area->last_chpid = chpid.id;
ccode = chsc(scpd_area);
if (ccode > 0) {
@@ -1316,8 +915,7 @@ chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
}
}
-static int
-chsc_get_channel_measurement_chars(struct channel_path *chp)
+int chsc_get_channel_measurement_chars(struct channel_path *chp)
{
int ccode, ret;
@@ -1349,8 +947,8 @@ chsc_get_channel_measurement_chars(struct channel_path *chp)
scmc_area->request.length = 0x0010;
scmc_area->request.code = 0x0022;
- scmc_area->first_chpid = chp->id;
- scmc_area->last_chpid = chp->id;
+ scmc_area->first_chpid = chp->chpid.id;
+ scmc_area->last_chpid = chp->chpid.id;
ccode = chsc(scmc_area);
if (ccode > 0) {
@@ -1392,94 +990,6 @@ out:
return ret;
}
-/*
- * Entries for chpids on the system bus.
- * This replaces /proc/chpids.
- */
-static int
-new_channel_path(int chpid)
-{
- struct channel_path *chp;
- int ret;
-
- chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL);
- if (!chp)
- return -ENOMEM;
-
- /* fill in status, etc. */
- chp->id = chpid;
- chp->state = 1;
- chp->dev.parent = &css[0]->device;
- chp->dev.release = chp_release;
- snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp0.%x", chpid);
-
- /* Obtain channel path description and fill it in. */
- ret = chsc_determine_channel_path_description(chpid, &chp->desc);
- if (ret)
- goto out_free;
- /* Get channel-measurement characteristics. */
- if (css_characteristics_avail && css_chsc_characteristics.scmc
- && css_chsc_characteristics.secm) {
- ret = chsc_get_channel_measurement_chars(chp);
- if (ret)
- goto out_free;
- } else {
- static int msg_done;
-
- if (!msg_done) {
- printk(KERN_WARNING "cio: Channel measurements not "
- "available, continuing.\n");
- msg_done = 1;
- }
- chp->cmg = -1;
- }
-
- /* make it known to the system */
- ret = device_register(&chp->dev);
- if (ret) {
- printk(KERN_WARNING "%s: could not register %02x\n",
- __func__, chpid);
- goto out_free;
- }
- ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group);
- if (ret) {
- device_unregister(&chp->dev);
- goto out_free;
- }
- mutex_lock(&css[0]->mutex);
- if (css[0]->cm_enabled) {
- ret = chsc_add_chp_cmg_attr(chp);
- if (ret) {
- sysfs_remove_group(&chp->dev.kobj, &chp_attr_group);
- device_unregister(&chp->dev);
- mutex_unlock(&css[0]->mutex);
- goto out_free;
- }
- }
- css[0]->chps[chpid] = chp;
- mutex_unlock(&css[0]->mutex);
- return ret;
-out_free:
- kfree(chp);
- return ret;
-}
-
-void *
-chsc_get_chp_desc(struct subchannel *sch, int chp_no)
-{
- struct channel_path *chp;
- struct channel_path_desc *desc;
-
- chp = css[0]->chps[sch->schib.pmcw.chpid[chp_no]];
- if (!chp)
- return NULL;
- desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL);
- if (!desc)
- return NULL;
- memcpy(desc, &chp->desc, sizeof(struct channel_path_desc));
- return desc;
-}
-
static int __init
chsc_alloc_sei_area(void)
{
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index 0fb2b024208f..2ad81d11cf7b 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -1,9 +1,10 @@
#ifndef S390_CHSC_H
#define S390_CHSC_H
-#define CHSC_SEI_ACC_CHPID 1
-#define CHSC_SEI_ACC_LINKADDR 2
-#define CHSC_SEI_ACC_FULLLINKADDR 3
+#include <linux/types.h>
+#include <linux/device.h>
+#include <asm/chpid.h>
+#include "schid.h"
#define CHSC_SDA_OC_MSS 0x2
@@ -33,23 +34,9 @@ struct channel_path_desc {
u8 chpp;
} __attribute__ ((packed));
-struct channel_path {
- int id;
- int state;
- struct channel_path_desc desc;
- /* Channel-measurement related stuff: */
- int cmg;
- int shared;
- void *cmg_chars;
- struct device dev;
-};
+struct channel_path;
-extern void s390_process_css( void );
-extern void chsc_validate_chpids(struct subchannel *);
-extern void chpid_is_actually_online(int);
-extern int css_get_ssd_info(struct subchannel *);
-extern int chsc_process_crw(void);
-extern int chp_process_crw(int, int);
+extern void chsc_process_crw(void);
struct css_general_char {
u64 : 41;
@@ -82,15 +69,26 @@ struct css_chsc_char {
extern struct css_general_char css_general_characteristics;
extern struct css_chsc_char css_chsc_characteristics;
+struct chsc_ssd_info {
+ u8 path_mask;
+ u8 fla_valid_mask;
+ struct chp_id chpid[8];
+ u16 fla[8];
+};
+extern int chsc_get_ssd_info(struct subchannel_id schid,
+ struct chsc_ssd_info *ssd);
extern int chsc_determine_css_characteristics(void);
extern int css_characteristics_avail;
-extern void *chsc_get_chp_desc(struct subchannel*, int);
-
extern int chsc_enable_facility(int);
struct channel_subsystem;
extern int chsc_secm(struct channel_subsystem *, int);
-#define to_channelpath(device) container_of(device, struct channel_path, dev)
+int chsc_chp_vary(struct chp_id chpid, int on);
+int chsc_determine_channel_path_description(struct chp_id chpid,
+ struct channel_path_desc *desc);
+void chsc_chp_online(struct chp_id chpid);
+void chsc_chp_offline(struct chp_id chpid);
+int chsc_get_channel_measurement_chars(struct channel_path *chp);
#endif
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 9cb129ab5be5..ea1defba5693 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -22,6 +22,7 @@
#include <asm/setup.h>
#include <asm/reset.h>
#include <asm/ipl.h>
+#include <asm/chpid.h>
#include "airq.h"
#include "cio.h"
#include "css.h"
@@ -29,6 +30,7 @@
#include "ioasm.h"
#include "blacklist.h"
#include "cio_debug.h"
+#include "chp.h"
#include "../s390mach.h"
debug_info_t *cio_debug_msg_id;
@@ -592,9 +594,10 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
err = -ENODEV;
goto out;
}
- sch->opm = 0xff;
- if (!cio_is_console(sch->schid))
- chsc_validate_chpids(sch);
+ if (cio_is_console(sch->schid))
+ sch->opm = 0xff;
+ else
+ sch->opm = chp_get_sch_opm(sch);
sch->lpm = sch->schib.pmcw.pam & sch->opm;
CIO_DEBUG(KERN_INFO, 0,
@@ -954,6 +957,7 @@ static void css_reset(void)
{
int i, ret;
unsigned long long timeout;
+ struct chp_id chpid;
/* Reset subchannels. */
for_each_subchannel(__shutdown_subchannel_easy, NULL);
@@ -963,8 +967,10 @@ static void css_reset(void)
__ctl_set_bit(14, 28);
/* Temporarily reenable machine checks. */
local_mcck_enable();
+ chp_id_init(&chpid);
for (i = 0; i <= __MAX_CHPID; i++) {
- ret = rchp(i);
+ chpid.id = i;
+ ret = rchp(chpid);
if ((ret == 0) || (ret == 2))
/*
* rchp either succeeded, or another rchp is already
@@ -1048,37 +1054,19 @@ void reipl_ccw_dev(struct ccw_dev_id *devid)
do_reipl_asm(*((__u32*)&schid));
}
-static struct schib __initdata ipl_schib;
-
-/*
- * ipl_save_parameters gets called very early. It is not allowed to access
- * anything in the bss section at all. The bss section is not cleared yet,
- * but may contain some ipl parameters written by the firmware.
- * These parameters (if present) are copied to 0x2000.
- * To avoid corruption of the ipl parameters, all variables used by this
- * function must reside on the stack or in the data section.
- */
-void ipl_save_parameters(void)
+int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo)
{
struct subchannel_id schid;
- unsigned int *ipl_ptr;
- void *src, *dst;
+ struct schib schib;
schid = *(struct subchannel_id *)__LC_SUBCHANNEL_ID;
if (!schid.one)
- return;
- if (stsch(schid, &ipl_schib))
- return;
- if (!ipl_schib.pmcw.dnv)
- return;
- ipl_devno = ipl_schib.pmcw.dev;
- ipl_flags |= IPL_DEVNO_VALID;
- if (!ipl_schib.pmcw.qf)
- return;
- ipl_flags |= IPL_PARMBLOCK_VALID;
- ipl_ptr = (unsigned int *)__LC_IPL_PARMBLOCK_PTR;
- src = (void *)(unsigned long)*ipl_ptr;
- dst = (void *)IPL_PARMBLOCK_ORIGIN;
- memmove(dst, src, PAGE_SIZE);
- *ipl_ptr = IPL_PARMBLOCK_ORIGIN;
+ return -ENODEV;
+ if (stsch(schid, &schib))
+ return -ENODEV;
+ if (!schib.pmcw.dnv)
+ return -ENODEV;
+ iplinfo->devno = schib.pmcw.dev;
+ iplinfo->is_qdio = schib.pmcw.qf;
+ return 0;
}
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 35154a210357..7446c39951a7 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -1,18 +1,11 @@
#ifndef S390_CIO_H
#define S390_CIO_H
-#include "schid.h"
#include <linux/mutex.h>
-
-/*
- * where we put the ssd info
- */
-struct ssd_info {
- __u8 valid:1;
- __u8 type:7; /* subchannel type */
- __u8 chpid[8]; /* chpids */
- __u16 fla[8]; /* full link addresses */
-} __attribute__ ((packed));
+#include <linux/device.h>
+#include <asm/chpid.h>
+#include "chsc.h"
+#include "schid.h"
/*
* path management control word
@@ -108,7 +101,7 @@ struct subchannel {
struct schib schib; /* subchannel information block */
struct orb orb; /* operation request block */
struct ccw1 sense_ccw; /* static ccw for sense command */
- struct ssd_info ssd_info; /* subchannel description */
+ struct chsc_ssd_info ssd_info; /* subchannel description */
struct device dev; /* entry in device tree */
struct css_driver *driver;
} __attribute__ ((aligned(8)));
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index 90b22faabbf7..28abd697be1a 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -476,7 +476,7 @@ struct cmb_area {
};
static struct cmb_area cmb_area = {
- .lock = SPIN_LOCK_UNLOCKED,
+ .lock = __SPIN_LOCK_UNLOCKED(cmb_area.lock),
.list = LIST_HEAD_INIT(cmb_area.list),
.num_channels = 1024,
};
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index fe0ace7aece8..27c6d9e55b23 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -20,8 +20,9 @@
#include "ioasm.h"
#include "chsc.h"
#include "device.h"
+#include "idset.h"
+#include "chp.h"
-int need_rescan = 0;
int css_init_done = 0;
static int need_reprobe = 0;
static int max_ssid = 0;
@@ -125,8 +126,52 @@ void css_sch_device_unregister(struct subchannel *sch)
mutex_unlock(&sch->reg_mutex);
}
-static int
-css_register_subchannel(struct subchannel *sch)
+static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw)
+{
+ int i;
+ int mask;
+
+ memset(ssd, 0, sizeof(struct chsc_ssd_info));
+ ssd->path_mask = pmcw->pim;
+ for (i = 0; i < 8; i++) {
+ mask = 0x80 >> i;
+ if (pmcw->pim & mask) {
+ chp_id_init(&ssd->chpid[i]);
+ ssd->chpid[i].id = pmcw->chpid[i];
+ }
+ }
+}
+
+static void ssd_register_chpids(struct chsc_ssd_info *ssd)
+{
+ int i;
+ int mask;
+
+ for (i = 0; i < 8; i++) {
+ mask = 0x80 >> i;
+ if (ssd->path_mask & mask)
+ if (!chp_is_registered(ssd->chpid[i]))
+ chp_new(ssd->chpid[i]);
+ }
+}
+
+void css_update_ssd_info(struct subchannel *sch)
+{
+ int ret;
+
+ if (cio_is_console(sch->schid)) {
+ /* Console is initialized too early for functions requiring
+ * memory allocation. */
+ ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw);
+ } else {
+ ret = chsc_get_ssd_info(sch->schid, &sch->ssd_info);
+ if (ret)
+ ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw);
+ ssd_register_chpids(&sch->ssd_info);
+ }
+}
+
+static int css_register_subchannel(struct subchannel *sch)
{
int ret;
@@ -135,9 +180,7 @@ css_register_subchannel(struct subchannel *sch)
sch->dev.bus = &css_bus_type;
sch->dev.release = &css_subchannel_release;
sch->dev.groups = subch_attr_groups;
-
- css_get_ssd_info(sch);
-
+ css_update_ssd_info(sch);
/* make it known to the system */
ret = css_sch_device_register(sch);
if (ret) {
@@ -306,7 +349,7 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
return css_probe_device(schid);
}
-static int css_evaluate_subchannel(struct subchannel_id schid, int slow)
+static void css_evaluate_subchannel(struct subchannel_id schid, int slow)
{
struct subchannel *sch;
int ret;
@@ -317,53 +360,66 @@ static int css_evaluate_subchannel(struct subchannel_id schid, int slow)
put_device(&sch->dev);
} else
ret = css_evaluate_new_subchannel(schid, slow);
-
- return ret;
+ if (ret == -EAGAIN)
+ css_schedule_eval(schid);
}
-static int
-css_rescan_devices(struct subchannel_id schid, void *data)
+static struct idset *slow_subchannel_set;
+static spinlock_t slow_subchannel_lock;
+
+static int __init slow_subchannel_init(void)
{
- return css_evaluate_subchannel(schid, 1);
+ spin_lock_init(&slow_subchannel_lock);
+ slow_subchannel_set = idset_sch_new();
+ if (!slow_subchannel_set) {
+ printk(KERN_WARNING "cio: could not allocate slow subchannel "
+ "set\n");
+ return -ENOMEM;
+ }
+ return 0;
}
-struct slow_subchannel {
- struct list_head slow_list;
- struct subchannel_id schid;
-};
-
-static LIST_HEAD(slow_subchannels_head);
-static DEFINE_SPINLOCK(slow_subchannel_lock);
+subsys_initcall(slow_subchannel_init);
-static void
-css_trigger_slow_path(struct work_struct *unused)
+static void css_slow_path_func(struct work_struct *unused)
{
- CIO_TRACE_EVENT(4, "slowpath");
-
- if (need_rescan) {
- need_rescan = 0;
- for_each_subchannel(css_rescan_devices, NULL);
- return;
- }
+ struct subchannel_id schid;
+ CIO_TRACE_EVENT(4, "slowpath");
spin_lock_irq(&slow_subchannel_lock);
- while (!list_empty(&slow_subchannels_head)) {
- struct slow_subchannel *slow_sch =
- list_entry(slow_subchannels_head.next,
- struct slow_subchannel, slow_list);
-
- list_del_init(slow_subchannels_head.next);
+ init_subchannel_id(&schid);
+ while (idset_sch_get_first(slow_subchannel_set, &schid)) {
+ idset_sch_del(slow_subchannel_set, schid);
spin_unlock_irq(&slow_subchannel_lock);
- css_evaluate_subchannel(slow_sch->schid, 1);
+ css_evaluate_subchannel(schid, 1);
spin_lock_irq(&slow_subchannel_lock);
- kfree(slow_sch);
}
spin_unlock_irq(&slow_subchannel_lock);
}
-DECLARE_WORK(slow_path_work, css_trigger_slow_path);
+static DECLARE_WORK(slow_path_work, css_slow_path_func);
struct workqueue_struct *slow_path_wq;
+void css_schedule_eval(struct subchannel_id schid)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&slow_subchannel_lock, flags);
+ idset_sch_add(slow_subchannel_set, schid);
+ queue_work(slow_path_wq, &slow_path_work);
+ spin_unlock_irqrestore(&slow_subchannel_lock, flags);
+}
+
+void css_schedule_eval_all(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&slow_subchannel_lock, flags);
+ idset_fill(slow_subchannel_set);
+ queue_work(slow_path_wq, &slow_path_work);
+ spin_unlock_irqrestore(&slow_subchannel_lock, flags);
+}
+
/* Reprobe subchannel if unregistered. */
static int reprobe_subchannel(struct subchannel_id schid, void *data)
{
@@ -426,33 +482,14 @@ void css_schedule_reprobe(void)
EXPORT_SYMBOL_GPL(css_schedule_reprobe);
/*
- * Rescan for new devices. FIXME: This is slow.
- * This function is called when we have lost CRWs due to overflows and we have
- * to do subchannel housekeeping.
- */
-void
-css_reiterate_subchannels(void)
-{
- css_clear_subchannel_slow_list();
- need_rescan = 1;
-}
-
-/*
* Called from the machine check handler for subchannel report words.
*/
-int
-css_process_crw(int rsid1, int rsid2)
+void css_process_crw(int rsid1, int rsid2)
{
- int ret;
struct subchannel_id mchk_schid;
CIO_CRW_EVENT(2, "source is subchannel %04X, subsystem id %x\n",
rsid1, rsid2);
-
- if (need_rescan)
- /* We need to iterate all subchannels anyway. */
- return -EAGAIN;
-
init_subchannel_id(&mchk_schid);
mchk_schid.sch_no = rsid1;
if (rsid2 != 0)
@@ -463,14 +500,7 @@ css_process_crw(int rsid1, int rsid2)
* use stsch() to find out if the subchannel in question has come
* or gone.
*/
- ret = css_evaluate_subchannel(mchk_schid, 0);
- if (ret == -EAGAIN) {
- if (css_enqueue_subchannel_slow(mchk_schid)) {
- css_clear_subchannel_slow_list();
- need_rescan = 1;
- }
- }
- return ret;
+ css_evaluate_subchannel(mchk_schid, 0);
}
static int __init
@@ -745,47 +775,6 @@ struct bus_type css_bus_type = {
subsys_initcall(init_channel_subsystem);
-int
-css_enqueue_subchannel_slow(struct subchannel_id schid)
-{
- struct slow_subchannel *new_slow_sch;
- unsigned long flags;
-
- new_slow_sch = kzalloc(sizeof(struct slow_subchannel), GFP_ATOMIC);
- if (!new_slow_sch)
- return -ENOMEM;
- new_slow_sch->schid = schid;
- spin_lock_irqsave(&slow_subchannel_lock, flags);
- list_add_tail(&new_slow_sch->slow_list, &slow_subchannels_head);
- spin_unlock_irqrestore(&slow_subchannel_lock, flags);
- return 0;
-}
-
-void
-css_clear_subchannel_slow_list(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&slow_subchannel_lock, flags);
- while (!list_empty(&slow_subchannels_head)) {
- struct slow_subchannel *slow_sch =
- list_entry(slow_subchannels_head.next,
- struct slow_subchannel, slow_list);
-
- list_del_init(slow_subchannels_head.next);
- kfree(slow_sch);
- }
- spin_unlock_irqrestore(&slow_subchannel_lock, flags);
-}
-
-
-
-int
-css_slow_subchannels_exist(void)
-{
- return (!list_empty(&slow_subchannels_head));
-}
-
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(css_bus_type);
EXPORT_SYMBOL_GPL(css_characteristics_avail);
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index ca2bab932a8a..71fcfdc42800 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -4,8 +4,11 @@
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/types.h>
#include <asm/cio.h>
+#include <asm/chpid.h>
#include "schid.h"
@@ -143,13 +146,12 @@ extern void css_sch_device_unregister(struct subchannel *);
extern struct subchannel * get_subchannel_by_schid(struct subchannel_id);
extern int css_init_done;
extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
-extern int css_process_crw(int, int);
+extern void css_process_crw(int, int);
extern void css_reiterate_subchannels(void);
+void css_update_ssd_info(struct subchannel *sch);
#define __MAX_SUBCHANNEL 65535
#define __MAX_SSID 3
-#define __MAX_CHPID 255
-#define __MAX_CSSID 0
struct channel_subsystem {
u8 cssid;
@@ -185,16 +187,12 @@ int device_trigger_verify(struct subchannel *sch);
void device_kill_pending_timer(struct subchannel *);
/* Helper functions to build lists for the slow path. */
-extern int css_enqueue_subchannel_slow(struct subchannel_id schid);
-void css_walk_subchannel_slow_list(void (*fn)(unsigned long));
-void css_clear_subchannel_slow_list(void);
-int css_slow_subchannels_exist(void);
-extern int need_rescan;
+void css_schedule_eval(struct subchannel_id schid);
+void css_schedule_eval_all(void);
int sch_is_pseudo_sch(struct subchannel *);
extern struct workqueue_struct *slow_path_wq;
-extern struct work_struct slow_path_work;
int subchannel_add_files (struct device *);
extern struct attribute_group *subch_attr_groups[];
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index e322111fb369..03355902c582 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -56,13 +56,12 @@ ccw_bus_match (struct device * dev, struct device_driver * drv)
/* Store modalias string delimited by prefix/suffix string into buffer with
* specified size. Return length of resulting string (excluding trailing '\0')
* even if string doesn't fit buffer (snprintf semantics). */
-static int snprint_alias(char *buf, size_t size, const char *prefix,
+static int snprint_alias(char *buf, size_t size,
struct ccw_device_id *id, const char *suffix)
{
int len;
- len = snprintf(buf, size, "%sccw:t%04Xm%02X", prefix, id->cu_type,
- id->cu_model);
+ len = snprintf(buf, size, "ccw:t%04Xm%02X", id->cu_type, id->cu_model);
if (len > size)
return len;
buf += len;
@@ -85,53 +84,40 @@ static int ccw_uevent(struct device *dev, char **envp, int num_envp,
struct ccw_device *cdev = to_ccwdev(dev);
struct ccw_device_id *id = &(cdev->id);
int i = 0;
- int len;
+ int len = 0;
+ int ret;
+ char modalias_buf[30];
/* CU_TYPE= */
- len = snprintf(buffer, buffer_size, "CU_TYPE=%04X", id->cu_type) + 1;
- if (len > buffer_size || i >= num_envp)
- return -ENOMEM;
- envp[i++] = buffer;
- buffer += len;
- buffer_size -= len;
+ ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
+ "CU_TYPE=%04X", id->cu_type);
+ if (ret)
+ return ret;
/* CU_MODEL= */
- len = snprintf(buffer, buffer_size, "CU_MODEL=%02X", id->cu_model) + 1;
- if (len > buffer_size || i >= num_envp)
- return -ENOMEM;
- envp[i++] = buffer;
- buffer += len;
- buffer_size -= len;
+ ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
+ "CU_MODEL=%02X", id->cu_model);
+ if (ret)
+ return ret;
/* The next two can be zero, that's ok for us */
/* DEV_TYPE= */
- len = snprintf(buffer, buffer_size, "DEV_TYPE=%04X", id->dev_type) + 1;
- if (len > buffer_size || i >= num_envp)
- return -ENOMEM;
- envp[i++] = buffer;
- buffer += len;
- buffer_size -= len;
+ ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
+ "DEV_TYPE=%04X", id->dev_type);
+ if (ret)
+ return ret;
/* DEV_MODEL= */
- len = snprintf(buffer, buffer_size, "DEV_MODEL=%02X",
- (unsigned char) id->dev_model) + 1;
- if (len > buffer_size || i >= num_envp)
- return -ENOMEM;
- envp[i++] = buffer;
- buffer += len;
- buffer_size -= len;
+ ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
+ "DEV_MODEL=%02X", id->dev_model);
+ if (ret)
+ return ret;
/* MODALIAS= */
- len = snprint_alias(buffer, buffer_size, "MODALIAS=", id, "") + 1;
- if (len > buffer_size || i >= num_envp)
- return -ENOMEM;
- envp[i++] = buffer;
- buffer += len;
- buffer_size -= len;
-
- envp[i] = NULL;
-
- return 0;
+ snprint_alias(modalias_buf, sizeof(modalias_buf), id, "");
+ ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
+ "MODALIAS=%s", modalias_buf);
+ return ret;
}
struct bus_type ccw_bus_type;
@@ -230,12 +216,18 @@ static ssize_t
chpids_show (struct device * dev, struct device_attribute *attr, char * buf)
{
struct subchannel *sch = to_subchannel(dev);
- struct ssd_info *ssd = &sch->ssd_info;
+ struct chsc_ssd_info *ssd = &sch->ssd_info;
ssize_t ret = 0;
int chp;
+ int mask;
- for (chp = 0; chp < 8; chp++)
- ret += sprintf (buf+ret, "%02x ", ssd->chpid[chp]);
+ for (chp = 0; chp < 8; chp++) {
+ mask = 0x80 >> chp;
+ if (ssd->path_mask & mask)
+ ret += sprintf(buf + ret, "%02x ", ssd->chpid[chp].id);
+ else
+ ret += sprintf(buf + ret, "00 ");
+ }
ret += sprintf (buf+ret, "\n");
return min((ssize_t)PAGE_SIZE, ret);
}
@@ -280,7 +272,7 @@ modalias_show (struct device *dev, struct device_attribute *attr, char *buf)
struct ccw_device_id *id = &(cdev->id);
int len;
- len = snprint_alias(buf, PAGE_SIZE, "", id, "\n") + 1;
+ len = snprint_alias(buf, PAGE_SIZE, id, "\n") + 1;
return len > PAGE_SIZE ? PAGE_SIZE : len;
}
@@ -298,16 +290,10 @@ int ccw_device_is_orphan(struct ccw_device *cdev)
return sch_is_pseudo_sch(to_subchannel(cdev->dev.parent));
}
-static void ccw_device_unregister(struct work_struct *work)
+static void ccw_device_unregister(struct ccw_device *cdev)
{
- struct ccw_device_private *priv;
- struct ccw_device *cdev;
-
- priv = container_of(work, struct ccw_device_private, kick_work);
- cdev = priv->cdev;
if (test_and_clear_bit(1, &cdev->private->registered))
- device_unregister(&cdev->dev);
- put_device(&cdev->dev);
+ device_del(&cdev->dev);
}
static void
@@ -324,11 +310,8 @@ ccw_device_remove_disconnected(struct ccw_device *cdev)
spin_lock_irqsave(cdev->ccwlock, flags);
cdev->private->state = DEV_STATE_NOT_OPER;
spin_unlock_irqrestore(cdev->ccwlock, flags);
- if (get_device(&cdev->dev)) {
- PREPARE_WORK(&cdev->private->kick_work,
- ccw_device_unregister);
- queue_work(ccw_device_work, &cdev->private->kick_work);
- }
+ ccw_device_unregister(cdev);
+ put_device(&cdev->dev);
return ;
}
sch = to_subchannel(cdev->dev.parent);
@@ -413,11 +396,60 @@ ccw_device_set_online(struct ccw_device *cdev)
return (ret == 0) ? -ENODEV : ret;
}
-static ssize_t
-online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+static void online_store_handle_offline(struct ccw_device *cdev)
+{
+ if (cdev->private->state == DEV_STATE_DISCONNECTED)
+ ccw_device_remove_disconnected(cdev);
+ else if (cdev->drv && cdev->drv->set_offline)
+ ccw_device_set_offline(cdev);
+}
+
+static int online_store_recog_and_online(struct ccw_device *cdev)
+{
+ int ret;
+
+ /* Do device recognition, if needed. */
+ if (cdev->id.cu_type == 0) {
+ ret = ccw_device_recognition(cdev);
+ if (ret) {
+ printk(KERN_WARNING"Couldn't start recognition "
+ "for device %s (ret=%d)\n",
+ cdev->dev.bus_id, ret);
+ return ret;
+ }
+ wait_event(cdev->private->wait_q,
+ cdev->private->flags.recog_done);
+ }
+ if (cdev->drv && cdev->drv->set_online)
+ ccw_device_set_online(cdev);
+ return 0;
+}
+static void online_store_handle_online(struct ccw_device *cdev, int force)
+{
+ int ret;
+
+ ret = online_store_recog_and_online(cdev);
+ if (ret)
+ return;
+ if (force && cdev->private->state == DEV_STATE_BOXED) {
+ ret = ccw_device_stlck(cdev);
+ if (ret) {
+ printk(KERN_WARNING"ccw_device_stlck for device %s "
+ "returned %d!\n", cdev->dev.bus_id, ret);
+ return;
+ }
+ if (cdev->id.cu_type == 0)
+ cdev->private->state = DEV_STATE_NOT_OPER;
+ online_store_recog_and_online(cdev);
+ }
+
+}
+
+static ssize_t online_store (struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct ccw_device *cdev = to_ccwdev(dev);
- int i, force, ret;
+ int i, force;
char *tmp;
if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0)
@@ -434,51 +466,17 @@ online_store (struct device *dev, struct device_attribute *attr, const char *buf
force = 0;
i = simple_strtoul(buf, &tmp, 16);
}
- if (i == 1) {
- /* Do device recognition, if needed. */
- if (cdev->id.cu_type == 0) {
- ret = ccw_device_recognition(cdev);
- if (ret) {
- printk(KERN_WARNING"Couldn't start recognition "
- "for device %s (ret=%d)\n",
- cdev->dev.bus_id, ret);
- goto out;
- }
- wait_event(cdev->private->wait_q,
- cdev->private->flags.recog_done);
- }
- if (cdev->drv && cdev->drv->set_online)
- ccw_device_set_online(cdev);
- } else if (i == 0) {
- if (cdev->private->state == DEV_STATE_DISCONNECTED)
- ccw_device_remove_disconnected(cdev);
- else if (cdev->drv && cdev->drv->set_offline)
- ccw_device_set_offline(cdev);
- }
- if (force && cdev->private->state == DEV_STATE_BOXED) {
- ret = ccw_device_stlck(cdev);
- if (ret) {
- printk(KERN_WARNING"ccw_device_stlck for device %s "
- "returned %d!\n", cdev->dev.bus_id, ret);
- goto out;
- }
- /* Do device recognition, if needed. */
- if (cdev->id.cu_type == 0) {
- cdev->private->state = DEV_STATE_NOT_OPER;
- ret = ccw_device_recognition(cdev);
- if (ret) {
- printk(KERN_WARNING"Couldn't start recognition "
- "for device %s (ret=%d)\n",
- cdev->dev.bus_id, ret);
- goto out;
- }
- wait_event(cdev->private->wait_q,
- cdev->private->flags.recog_done);
- }
- if (cdev->drv && cdev->drv->set_online)
- ccw_device_set_online(cdev);
+
+ switch (i) {
+ case 0:
+ online_store_handle_offline(cdev);
+ break;
+ case 1:
+ online_store_handle_online(cdev, force);
+ break;
+ default:
+ count = -EINVAL;
}
- out:
if (cdev->drv)
module_put(cdev->drv->owner);
atomic_set(&cdev->private->onoff, 0);
@@ -548,17 +546,10 @@ static struct attribute_group ccwdev_attr_group = {
.attrs = ccwdev_attrs,
};
-static int
-device_add_files (struct device *dev)
-{
- return sysfs_create_group(&dev->kobj, &ccwdev_attr_group);
-}
-
-static void
-device_remove_files(struct device *dev)
-{
- sysfs_remove_group(&dev->kobj, &ccwdev_attr_group);
-}
+struct attribute_group *ccwdev_attr_groups[] = {
+ &ccwdev_attr_group,
+ NULL,
+};
/* this is a simple abstraction for device_register that sets the
* correct bus type and adds the bus specific files */
@@ -573,10 +564,6 @@ static int ccw_device_register(struct ccw_device *cdev)
return ret;
set_bit(1, &cdev->private->registered);
- if ((ret = device_add_files(dev))) {
- if (test_and_clear_bit(1, &cdev->private->registered))
- device_del(dev);
- }
return ret;
}
@@ -648,10 +635,6 @@ ccw_device_add_changed(struct work_struct *work)
return;
}
set_bit(1, &cdev->private->registered);
- if (device_add_files(&cdev->dev)) {
- if (test_and_clear_bit(1, &cdev->private->registered))
- device_unregister(&cdev->dev);
- }
}
void ccw_device_do_unreg_rereg(struct work_struct *work)
@@ -664,9 +647,7 @@ void ccw_device_do_unreg_rereg(struct work_struct *work)
cdev = priv->cdev;
sch = to_subchannel(cdev->dev.parent);
- device_remove_files(&cdev->dev);
- if (test_and_clear_bit(1, &cdev->private->registered))
- device_del(&cdev->dev);
+ ccw_device_unregister(cdev);
PREPARE_WORK(&cdev->private->kick_work,
ccw_device_add_changed);
queue_work(ccw_device_work, &cdev->private->kick_work);
@@ -705,6 +686,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch,
cdev->dev.parent = &sch->dev;
cdev->dev.release = ccw_device_release;
INIT_LIST_HEAD(&cdev->private->kick_work.entry);
+ cdev->dev.groups = ccwdev_attr_groups;
/* Do first half of device_register. */
device_initialize(&cdev->dev);
if (!get_device(&sch->dev)) {
@@ -736,6 +718,7 @@ static int io_subchannel_recog(struct ccw_device *, struct subchannel *);
static void sch_attach_device(struct subchannel *sch,
struct ccw_device *cdev)
{
+ css_update_ssd_info(sch);
spin_lock_irq(sch->lock);
sch->dev.driver_data = cdev;
cdev->private->schid = sch->schid;
@@ -871,7 +854,7 @@ io_subchannel_register(struct work_struct *work)
priv = container_of(work, struct ccw_device_private, kick_work);
cdev = priv->cdev;
sch = to_subchannel(cdev->dev.parent);
-
+ css_update_ssd_info(sch);
/*
* io_subchannel_register() will also be called after device
* recognition has been done for a boxed device (which will already
@@ -1133,15 +1116,8 @@ io_subchannel_remove (struct subchannel *sch)
sch->dev.driver_data = NULL;
cdev->private->state = DEV_STATE_NOT_OPER;
spin_unlock_irqrestore(cdev->ccwlock, flags);
- /*
- * Put unregistration on workqueue to avoid livelocks on the css bus
- * semaphore.
- */
- if (get_device(&cdev->dev)) {
- PREPARE_WORK(&cdev->private->kick_work,
- ccw_device_unregister);
- queue_work(ccw_device_work, &cdev->private->kick_work);
- }
+ ccw_device_unregister(cdev);
+ put_device(&cdev->dev);
return 0;
}
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 089a3ddd6265..898ec3b2bebb 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -15,6 +15,7 @@
#include <asm/ccwdev.h>
#include <asm/cio.h>
+#include <asm/chpid.h>
#include "cio.h"
#include "cio_debug.h"
@@ -22,6 +23,7 @@
#include "device.h"
#include "chsc.h"
#include "ioasm.h"
+#include "chp.h"
int
device_is_online(struct subchannel *sch)
@@ -210,14 +212,18 @@ static void
__recover_lost_chpids(struct subchannel *sch, int old_lpm)
{
int mask, i;
+ struct chp_id chpid;
+ chp_id_init(&chpid);
for (i = 0; i<8; i++) {
mask = 0x80 >> i;
if (!(sch->lpm & mask))
continue;
if (old_lpm & mask)
continue;
- chpid_is_actually_online(sch->schib.pmcw.chpid[i]);
+ chpid.id = sch->schib.pmcw.chpid[i];
+ if (!chp_is_registered(chpid))
+ css_schedule_eval_all();
}
}
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index 7c7775aae38a..16f59fcb66b1 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -16,12 +16,14 @@
#include <asm/ccwdev.h>
#include <asm/idals.h>
+#include <asm/chpid.h>
#include "cio.h"
#include "cio_debug.h"
#include "css.h"
#include "chsc.h"
#include "device.h"
+#include "chp.h"
int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags)
{
@@ -606,9 +608,12 @@ void *
ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no)
{
struct subchannel *sch;
+ struct chp_id chpid;
sch = to_subchannel(cdev->dev.parent);
- return chsc_get_chp_desc(sch, chp_no);
+ chp_id_init(&chpid);
+ chpid.id = sch->schib.pmcw.chpid[chp_no];
+ return chp_get_chp_desc(chpid);
}
// FIXME: these have to go:
diff --git a/drivers/s390/cio/idset.c b/drivers/s390/cio/idset.c
new file mode 100644
index 000000000000..16ea828e99f7
--- /dev/null
+++ b/drivers/s390/cio/idset.c
@@ -0,0 +1,112 @@
+/*
+ * drivers/s390/cio/idset.c
+ *
+ * Copyright IBM Corp. 2007
+ * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/slab.h>
+#include <asm/bitops.h>
+#include "idset.h"
+#include "css.h"
+
+struct idset {
+ int num_ssid;
+ int num_id;
+ unsigned long bitmap[0];
+};
+
+static inline unsigned long bitmap_size(int num_ssid, int num_id)
+{
+ return __BITOPS_WORDS(num_ssid * num_id) * sizeof(unsigned long);
+}
+
+static struct idset *idset_new(int num_ssid, int num_id)
+{
+ struct idset *set;
+
+ set = kzalloc(sizeof(struct idset) + bitmap_size(num_ssid, num_id),
+ GFP_KERNEL);
+ if (set) {
+ set->num_ssid = num_ssid;
+ set->num_id = num_id;
+ }
+ return set;
+}
+
+void idset_free(struct idset *set)
+{
+ kfree(set);
+}
+
+void idset_clear(struct idset *set)
+{
+ memset(set->bitmap, 0, bitmap_size(set->num_ssid, set->num_id));
+}
+
+void idset_fill(struct idset *set)
+{
+ memset(set->bitmap, 0xff, bitmap_size(set->num_ssid, set->num_id));
+}
+
+static inline void idset_add(struct idset *set, int ssid, int id)
+{
+ set_bit(ssid * set->num_id + id, set->bitmap);
+}
+
+static inline void idset_del(struct idset *set, int ssid, int id)
+{
+ clear_bit(ssid * set->num_id + id, set->bitmap);
+}
+
+static inline int idset_contains(struct idset *set, int ssid, int id)
+{
+ return test_bit(ssid * set->num_id + id, set->bitmap);
+}
+
+static inline int idset_get_first(struct idset *set, int *ssid, int *id)
+{
+ int bitnum;
+
+ bitnum = find_first_bit(set->bitmap, set->num_ssid * set->num_id);
+ if (bitnum >= set->num_ssid * set->num_id)
+ return 0;
+ *ssid = bitnum / set->num_id;
+ *id = bitnum % set->num_id;
+ return 1;
+}
+
+struct idset *idset_sch_new(void)
+{
+ return idset_new(__MAX_SSID + 1, __MAX_SUBCHANNEL + 1);
+}
+
+void idset_sch_add(struct idset *set, struct subchannel_id schid)
+{
+ idset_add(set, schid.ssid, schid.sch_no);
+}
+
+void idset_sch_del(struct idset *set, struct subchannel_id schid)
+{
+ idset_del(set, schid.ssid, schid.sch_no);
+}
+
+int idset_sch_contains(struct idset *set, struct subchannel_id schid)
+{
+ return idset_contains(set, schid.ssid, schid.sch_no);
+}
+
+int idset_sch_get_first(struct idset *set, struct subchannel_id *schid)
+{
+ int ssid = 0;
+ int id = 0;
+ int rc;
+
+ rc = idset_get_first(set, &ssid, &id);
+ if (rc) {
+ init_subchannel_id(schid);
+ schid->ssid = ssid;
+ schid->sch_no = id;
+ }
+ return rc;
+}
diff --git a/drivers/s390/cio/idset.h b/drivers/s390/cio/idset.h
new file mode 100644
index 000000000000..144466ab8c15
--- /dev/null
+++ b/drivers/s390/cio/idset.h
@@ -0,0 +1,25 @@
+/*
+ * drivers/s390/cio/idset.h
+ *
+ * Copyright IBM Corp. 2007
+ * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#ifndef S390_IDSET_H
+#define S390_IDSET_H S390_IDSET_H
+
+#include "schid.h"
+
+struct idset;
+
+void idset_free(struct idset *set);
+void idset_clear(struct idset *set);
+void idset_fill(struct idset *set);
+
+struct idset *idset_sch_new(void);
+void idset_sch_add(struct idset *set, struct subchannel_id id);
+void idset_sch_del(struct idset *set, struct subchannel_id id);
+int idset_sch_contains(struct idset *set, struct subchannel_id id);
+int idset_sch_get_first(struct idset *set, struct subchannel_id *id);
+
+#endif /* S390_IDSET_H */
diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h
index ad6d82940069..7153dd959082 100644
--- a/drivers/s390/cio/ioasm.h
+++ b/drivers/s390/cio/ioasm.h
@@ -1,6 +1,7 @@
#ifndef S390_CIO_IOASM_H
#define S390_CIO_IOASM_H
+#include <asm/chpid.h>
#include "schid.h"
/*
@@ -189,9 +190,9 @@ static inline int chsc(void *chsc_area)
return cc;
}
-static inline int rchp(int chpid)
+static inline int rchp(struct chp_id chpid)
{
- register unsigned int reg1 asm ("1") = chpid;
+ register struct chp_id reg1 asm ("1") = chpid;
int ccode;
asm volatile(
diff --git a/drivers/s390/net/ctcmain.c b/drivers/s390/net/ctcmain.c
index 0d6d5fcc128b..570a960bfb5b 100644
--- a/drivers/s390/net/ctcmain.c
+++ b/drivers/s390/net/ctcmain.c
@@ -1638,21 +1638,19 @@ add_channel(struct ccw_device *cdev, enum channel_types type)
struct channel *ch;
DBF_TEXT(trace, 2, __FUNCTION__);
- if ((ch =
- (struct channel *) kmalloc(sizeof (struct channel),
- GFP_KERNEL)) == NULL) {
+ ch = kzalloc(sizeof(struct channel), GFP_KERNEL);
+ if (!ch) {
ctc_pr_warn("ctc: Out of memory in add_channel\n");
return -1;
}
- memset(ch, 0, sizeof (struct channel));
- if ((ch->ccw = kmalloc(8*sizeof(struct ccw1),
- GFP_KERNEL | GFP_DMA)) == NULL) {
+ /* assure all flags and counters are reset */
+ ch->ccw = kzalloc(8 * sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);
+ if (!ch->ccw) {
kfree(ch);
ctc_pr_warn("ctc: Out of memory in add_channel\n");
return -1;
}
- memset(ch->ccw, 0, 8*sizeof(struct ccw1)); // assure all flags and counters are reset
/**
* "static" ccws are used in the following way:
@@ -1692,15 +1690,14 @@ add_channel(struct ccw_device *cdev, enum channel_types type)
return -1;
}
fsm_newstate(ch->fsm, CH_STATE_IDLE);
- if ((ch->irb = kmalloc(sizeof (struct irb),
- GFP_KERNEL)) == NULL) {
+ ch->irb = kzalloc(sizeof(struct irb), GFP_KERNEL);
+ if (!ch->irb) {
ctc_pr_warn("ctc: Out of memory in add_channel\n");
kfree_fsm(ch->fsm);
kfree(ch->ccw);
kfree(ch);
return -1;
}
- memset(ch->irb, 0, sizeof (struct irb));
while (*c && less_than((*c)->id, ch->id))
c = &(*c)->next;
if (*c && (!strncmp((*c)->id, ch->id, CTC_ID_SIZE))) {
@@ -2745,14 +2742,13 @@ ctc_probe_device(struct ccwgroup_device *cgdev)
if (!get_device(&cgdev->dev))
return -ENODEV;
- priv = kmalloc(sizeof (struct ctc_priv), GFP_KERNEL);
+ priv = kzalloc(sizeof(struct ctc_priv), GFP_KERNEL);
if (!priv) {
ctc_pr_err("%s: Out of memory\n", __func__);
put_device(&cgdev->dev);
return -ENOMEM;
}
- memset(priv, 0, sizeof (struct ctc_priv));
rc = ctc_add_files(&cgdev->dev);
if (rc) {
kfree(priv);
@@ -2793,10 +2789,9 @@ ctc_init_netdevice(struct net_device * dev, int alloc_device,
DBF_TEXT(setup, 3, __FUNCTION__);
if (alloc_device) {
- dev = kmalloc(sizeof (struct net_device), GFP_KERNEL);
+ dev = kzalloc(sizeof(struct net_device), GFP_KERNEL);
if (!dev)
return NULL;
- memset(dev, 0, sizeof (struct net_device));
}
dev->priv = privptr;
diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c
index 806bb1a921eb..644a06eba828 100644
--- a/drivers/s390/s390mach.c
+++ b/drivers/s390/s390mach.c
@@ -21,6 +21,7 @@
#include "cio/cio.h"
#include "cio/chsc.h"
#include "cio/css.h"
+#include "cio/chp.h"
#include "s390mach.h"
static struct semaphore m_sem;
@@ -44,14 +45,13 @@ static int
s390_collect_crw_info(void *param)
{
struct crw crw[2];
- int ccode, ret, slow;
+ int ccode;
struct semaphore *sem;
unsigned int chain;
sem = (struct semaphore *)param;
repeat:
down_interruptible(sem);
- slow = 0;
chain = 0;
while (1) {
if (unlikely(chain > 1)) {
@@ -84,9 +84,8 @@ repeat:
/* Check for overflows. */
if (crw[chain].oflw) {
pr_debug("%s: crw overflow detected!\n", __FUNCTION__);
- css_reiterate_subchannels();
+ css_schedule_eval_all();
chain = 0;
- slow = 1;
continue;
}
switch (crw[chain].rsc) {
@@ -94,10 +93,7 @@ repeat:
if (crw[0].chn && !chain)
break;
pr_debug("source is subchannel %04X\n", crw[0].rsid);
- ret = css_process_crw (crw[0].rsid,
- chain ? crw[1].rsid : 0);
- if (ret == -EAGAIN)
- slow = 1;
+ css_process_crw(crw[0].rsid, chain ? crw[1].rsid : 0);
break;
case CRW_RSC_MONITOR:
pr_debug("source is monitoring facility\n");
@@ -116,28 +112,23 @@ repeat:
}
switch (crw[0].erc) {
case CRW_ERC_IPARM: /* Path has come. */
- ret = chp_process_crw(crw[0].rsid, 1);
+ chp_process_crw(crw[0].rsid, 1);
break;
case CRW_ERC_PERRI: /* Path has gone. */
case CRW_ERC_PERRN:
- ret = chp_process_crw(crw[0].rsid, 0);
+ chp_process_crw(crw[0].rsid, 0);
break;
default:
pr_debug("Don't know how to handle erc=%x\n",
crw[0].erc);
- ret = 0;
}
- if (ret == -EAGAIN)
- slow = 1;
break;
case CRW_RSC_CONFIG:
pr_debug("source is configuration-alert facility\n");
break;
case CRW_RSC_CSS:
pr_debug("source is channel subsystem\n");
- ret = chsc_process_crw();
- if (ret == -EAGAIN)
- slow = 1;
+ chsc_process_crw();
break;
default:
pr_debug("unknown source\n");
@@ -146,8 +137,6 @@ repeat:
/* chain is always 0 or 1 here. */
chain = crw[chain].chn ? chain + 1 : 0;
}
- if (slow)
- queue_work(slow_path_wq, &slow_path_work);
goto repeat;
return 0;
}
diff --git a/drivers/s390/sysinfo.c b/drivers/s390/sysinfo.c
index 090743d2f914..19343f9675c3 100644
--- a/drivers/s390/sysinfo.c
+++ b/drivers/s390/sysinfo.c
@@ -357,6 +357,24 @@ static __init int create_proc_sysinfo(void)
__initcall(create_proc_sysinfo);
+int get_cpu_capability(unsigned int *capability)
+{
+ struct sysinfo_1_2_2 *info;
+ int rc;
+
+ info = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ rc = stsi(info, 1, 2, 2);
+ if (rc == -ENOSYS)
+ goto out;
+ rc = 0;
+ *capability = info->capability;
+out:
+ free_page((unsigned long) info);
+ return rc;
+}
+
/*
* CPU capability might have changed. Therefore recalculate loops_per_jiffy.
*/
OpenPOWER on IntegriCloud