summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-08-10 14:01:26 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2010-08-10 14:01:26 -0700
commit0d6ffdb8f151a2b685c7b45bde7ab2d49fc1bb00 (patch)
tree1768c906e87750dc897a2f113e335886d9b906e8
parent7233e392760b3493095d3d5885cb15e44493d74a (diff)
parent7c8faa86290c1a2607d6b768a0b874ec392a5c2a (diff)
Merge branch 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6
* 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6: [S390] dasd: tunable missing interrupt handler [S390] dasd: allocate fallback cqr for reserve/release [S390] topology: use default MC domain initializer [S390] initrd: change default load address [S390] cmm, smsgiucv_app: convert sender to uppercase [S390] cmm: add missing __init/__exit annotations [S390] cio: use all available paths for some internal I/O [S390] ccwreq: add ability to use all paths [S390] cio: ccw_device_online_store return -EINVAL in case of missing driver [S390] cio: Log the response from the unit check handler [S390] cio: CHSC SIOSL Support
-rw-r--r--arch/s390/include/asm/ccwdev.h2
-rw-r--r--arch/s390/include/asm/topology.h2
-rw-r--r--arch/s390/kernel/head.S2
-rw-r--r--arch/s390/mm/cmm.c11
-rw-r--r--drivers/s390/block/dasd_devmap.c44
-rw-r--r--drivers/s390/block/dasd_diag.c6
-rw-r--r--drivers/s390/block/dasd_eckd.c94
-rw-r--r--drivers/s390/block/dasd_eckd.h7
-rw-r--r--drivers/s390/block/dasd_fba.c4
-rw-r--r--drivers/s390/block/dasd_int.h8
-rw-r--r--drivers/s390/cio/ccwreq.c16
-rw-r--r--drivers/s390/cio/chsc.c48
-rw-r--r--drivers/s390/cio/chsc.h2
-rw-r--r--drivers/s390/cio/device.c47
-rw-r--r--drivers/s390/cio/device_pgid.c3
-rw-r--r--drivers/s390/cio/io_sch.h10
-rw-r--r--drivers/s390/net/smsgiucv_app.c7
17 files changed, 275 insertions, 38 deletions
diff --git a/arch/s390/include/asm/ccwdev.h b/arch/s390/include/asm/ccwdev.h
index 1c0030f9b89..f3ba0fa98de 100644
--- a/arch/s390/include/asm/ccwdev.h
+++ b/arch/s390/include/asm/ccwdev.h
@@ -208,6 +208,8 @@ extern void ccw_device_get_id(struct ccw_device *, struct ccw_dev_id *);
extern struct ccw_device *ccw_device_probe_console(void);
extern int ccw_device_force_console(void);
+int ccw_device_siosl(struct ccw_device *);
+
// FIXME: these have to go
extern int _ccw_device_get_subchannel_number(struct ccw_device *);
diff --git a/arch/s390/include/asm/topology.h b/arch/s390/include/asm/topology.h
index dc8a67297d0..831bd033ea7 100644
--- a/arch/s390/include/asm/topology.h
+++ b/arch/s390/include/asm/topology.h
@@ -30,8 +30,6 @@ static inline void s390_init_cpu_topology(void)
};
#endif
-#define SD_MC_INIT SD_CPU_INIT
-
#include <asm-generic/topology.h>
#endif /* _ASM_S390_TOPOLOGY_H */
diff --git a/arch/s390/kernel/head.S b/arch/s390/kernel/head.S
index 51838ad42d5..db1696e210a 100644
--- a/arch/s390/kernel/head.S
+++ b/arch/s390/kernel/head.S
@@ -366,7 +366,7 @@ iplstart:
l %r1,.Lstartup
br %r1
-.Linitrd:.long _end + 0x400000 # default address of initrd
+.Linitrd:.long _end # default address of initrd
.Lparm: .long PARMAREA
.Lstartup: .long startup
.Lreset:.byte 0xc3,0xc8,0xc1,0xd5,0xc7,0xc5,0x40,0xd9,0xc4,0xd9,0x40
diff --git a/arch/s390/mm/cmm.c b/arch/s390/mm/cmm.c
index eb6a2ef5f82..a9550dca3e4 100644
--- a/arch/s390/mm/cmm.c
+++ b/arch/s390/mm/cmm.c
@@ -427,7 +427,7 @@ static struct notifier_block cmm_power_notifier = {
.notifier_call = cmm_power_event,
};
-static int cmm_init(void)
+static int __init cmm_init(void)
{
int rc = -ENOMEM;
@@ -435,6 +435,13 @@ static int cmm_init(void)
if (!cmm_sysctl_header)
goto out_sysctl;
#ifdef CONFIG_CMM_IUCV
+ /* convert sender to uppercase characters */
+ if (sender) {
+ int len = strlen(sender);
+ while (len--)
+ sender[len] = toupper(sender[len]);
+ }
+
rc = smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
if (rc < 0)
goto out_smsg;
@@ -467,7 +474,7 @@ out_sysctl:
}
module_init(cmm_init);
-static void cmm_exit(void)
+static void __exit cmm_exit(void)
{
unregister_sysctl_table(cmm_sysctl_header);
#ifdef CONFIG_CMM_IUCV
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index bed7b4634cc..8d41f3ed38d 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -1083,6 +1083,49 @@ dasd_eer_store(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(eer_enabled, 0644, dasd_eer_show, dasd_eer_store);
+/*
+ * expiration time for default requests
+ */
+static ssize_t
+dasd_expires_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct dasd_device *device;
+ int len;
+
+ device = dasd_device_from_cdev(to_ccwdev(dev));
+ if (IS_ERR(device))
+ return -ENODEV;
+ len = snprintf(buf, PAGE_SIZE, "%lu\n", device->default_expires);
+ dasd_put_device(device);
+ return len;
+}
+
+static ssize_t
+dasd_expires_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct dasd_device *device;
+ unsigned long val;
+
+ device = dasd_device_from_cdev(to_ccwdev(dev));
+ if (IS_ERR(device))
+ return -ENODEV;
+
+ if ((strict_strtoul(buf, 10, &val) != 0) ||
+ (val > DASD_EXPIRES_MAX) || val == 0) {
+ dasd_put_device(device);
+ return -EINVAL;
+ }
+
+ if (val)
+ device->default_expires = val;
+
+ dasd_put_device(device);
+ return count;
+}
+
+static DEVICE_ATTR(expires, 0644, dasd_expires_show, dasd_expires_store);
+
static struct attribute * dasd_attrs[] = {
&dev_attr_readonly.attr,
&dev_attr_discipline.attr,
@@ -1094,6 +1137,7 @@ static struct attribute * dasd_attrs[] = {
&dev_attr_eer_enabled.attr,
&dev_attr_erplog.attr,
&dev_attr_failfast.attr,
+ &dev_attr_expires.attr,
NULL,
};
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index 687f323cdc3..2b3bc3ec054 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -43,7 +43,7 @@ MODULE_LICENSE("GPL");
sizeof(struct dasd_diag_req)) / \
sizeof(struct dasd_diag_bio)) / 2)
#define DIAG_MAX_RETRIES 32
-#define DIAG_TIMEOUT 50 * HZ
+#define DIAG_TIMEOUT 50
static struct dasd_discipline dasd_diag_discipline;
@@ -360,6 +360,8 @@ dasd_diag_check_device(struct dasd_device *device)
goto out;
}
+ device->default_expires = DIAG_TIMEOUT;
+
/* Figure out position of label block */
switch (private->rdc_data.vdev_class) {
case DEV_CLASS_FBA:
@@ -563,7 +565,7 @@ static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev,
cqr->startdev = memdev;
cqr->memdev = memdev;
cqr->block = block;
- cqr->expires = DIAG_TIMEOUT;
+ cqr->expires = memdev->default_expires * HZ;
cqr->status = DASD_CQR_FILLED;
return cqr;
}
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index ab84da5592e..66360c24bd4 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -82,6 +82,14 @@ static struct ccw_driver dasd_eckd_driver; /* see below */
#define INIT_CQR_UNFORMATTED 1
#define INIT_CQR_ERROR 2
+/* emergency request for reserve/release */
+static struct {
+ struct dasd_ccw_req cqr;
+ struct ccw1 ccw;
+ char data[32];
+} *dasd_reserve_req;
+static DEFINE_MUTEX(dasd_reserve_mutex);
+
/* initial attempt at a probe function. this can be simplified once
* the other detection code is gone */
@@ -1107,8 +1115,9 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
struct dasd_eckd_private *private;
struct dasd_block *block;
struct dasd_uid temp_uid;
- int is_known, rc;
+ int is_known, rc, i;
int readonly;
+ unsigned long value;
if (!ccw_device_is_pathgroup(device->cdev)) {
dev_warn(&device->cdev->dev,
@@ -1143,6 +1152,18 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
if (rc)
goto out_err1;
+ /* set default timeout */
+ device->default_expires = DASD_EXPIRES;
+ if (private->gneq) {
+ value = 1;
+ for (i = 0; i < private->gneq->timeout.value; i++)
+ value = 10 * value;
+ value = value * private->gneq->timeout.number;
+ /* do not accept useless values */
+ if (value != 0 && value <= DASD_EXPIRES_MAX)
+ device->default_expires = value;
+ }
+
/* Generate device unique id */
rc = dasd_eckd_generate_uid(device);
if (rc)
@@ -1973,7 +1994,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single(
cqr->startdev = startdev;
cqr->memdev = startdev;
cqr->block = block;
- cqr->expires = 5 * 60 * HZ; /* 5 minutes */
+ cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */
cqr->lpm = private->path_data.ppm;
cqr->retries = 256;
cqr->buildclk = get_clock();
@@ -2150,7 +2171,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track(
cqr->startdev = startdev;
cqr->memdev = startdev;
cqr->block = block;
- cqr->expires = 5 * 60 * HZ; /* 5 minutes */
+ cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */
cqr->lpm = private->path_data.ppm;
cqr->retries = 256;
cqr->buildclk = get_clock();
@@ -2398,7 +2419,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
cqr->startdev = startdev;
cqr->memdev = startdev;
cqr->block = block;
- cqr->expires = 5 * 60 * HZ; /* 5 minutes */
+ cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */
cqr->lpm = private->path_data.ppm;
cqr->retries = 256;
cqr->buildclk = get_clock();
@@ -2645,15 +2666,23 @@ dasd_eckd_release(struct dasd_device *device)
struct dasd_ccw_req *cqr;
int rc;
struct ccw1 *ccw;
+ int useglobal;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
+ useglobal = 0;
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device);
if (IS_ERR(cqr)) {
- DBF_DEV_EVENT(DBF_WARNING, device, "%s",
- "Could not allocate initialization request");
- return PTR_ERR(cqr);
+ mutex_lock(&dasd_reserve_mutex);
+ useglobal = 1;
+ cqr = &dasd_reserve_req->cqr;
+ memset(cqr, 0, sizeof(*cqr));
+ memset(&dasd_reserve_req->ccw, 0,
+ sizeof(dasd_reserve_req->ccw));
+ cqr->cpaddr = &dasd_reserve_req->ccw;
+ cqr->data = &dasd_reserve_req->data;
+ cqr->magic = DASD_ECKD_MAGIC;
}
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_RELEASE;
@@ -2671,7 +2700,10 @@ dasd_eckd_release(struct dasd_device *device)
rc = dasd_sleep_on_immediatly(cqr);
- dasd_sfree_request(cqr, cqr->memdev);
+ if (useglobal)
+ mutex_unlock(&dasd_reserve_mutex);
+ else
+ dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
@@ -2687,15 +2719,23 @@ dasd_eckd_reserve(struct dasd_device *device)
struct dasd_ccw_req *cqr;
int rc;
struct ccw1 *ccw;
+ int useglobal;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
+ useglobal = 0;
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device);
if (IS_ERR(cqr)) {
- DBF_DEV_EVENT(DBF_WARNING, device, "%s",
- "Could not allocate initialization request");
- return PTR_ERR(cqr);
+ mutex_lock(&dasd_reserve_mutex);
+ useglobal = 1;
+ cqr = &dasd_reserve_req->cqr;
+ memset(cqr, 0, sizeof(*cqr));
+ memset(&dasd_reserve_req->ccw, 0,
+ sizeof(dasd_reserve_req->ccw));
+ cqr->cpaddr = &dasd_reserve_req->ccw;
+ cqr->data = &dasd_reserve_req->data;
+ cqr->magic = DASD_ECKD_MAGIC;
}
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_RESERVE;
@@ -2713,7 +2753,10 @@ dasd_eckd_reserve(struct dasd_device *device)
rc = dasd_sleep_on_immediatly(cqr);
- dasd_sfree_request(cqr, cqr->memdev);
+ if (useglobal)
+ mutex_unlock(&dasd_reserve_mutex);
+ else
+ dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
@@ -2728,15 +2771,23 @@ dasd_eckd_steal_lock(struct dasd_device *device)
struct dasd_ccw_req *cqr;
int rc;
struct ccw1 *ccw;
+ int useglobal;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
+ useglobal = 0;
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device);
if (IS_ERR(cqr)) {
- DBF_DEV_EVENT(DBF_WARNING, device, "%s",
- "Could not allocate initialization request");
- return PTR_ERR(cqr);
+ mutex_lock(&dasd_reserve_mutex);
+ useglobal = 1;
+ cqr = &dasd_reserve_req->cqr;
+ memset(cqr, 0, sizeof(*cqr));
+ memset(&dasd_reserve_req->ccw, 0,
+ sizeof(dasd_reserve_req->ccw));
+ cqr->cpaddr = &dasd_reserve_req->ccw;
+ cqr->data = &dasd_reserve_req->data;
+ cqr->magic = DASD_ECKD_MAGIC;
}
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_SLCK;
@@ -2754,7 +2805,10 @@ dasd_eckd_steal_lock(struct dasd_device *device)
rc = dasd_sleep_on_immediatly(cqr);
- dasd_sfree_request(cqr, cqr->memdev);
+ if (useglobal)
+ mutex_unlock(&dasd_reserve_mutex);
+ else
+ dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
@@ -3488,10 +3542,15 @@ dasd_eckd_init(void)
int ret;
ASCEBC(dasd_eckd_discipline.ebcname, 4);
+ dasd_reserve_req = kmalloc(sizeof(*dasd_reserve_req),
+ GFP_KERNEL | GFP_DMA);
+ if (!dasd_reserve_req)
+ return -ENOMEM;
ret = ccw_driver_register(&dasd_eckd_driver);
if (!ret)
wait_for_device_probe();
-
+ else
+ kfree(dasd_reserve_req);
return ret;
}
@@ -3499,6 +3558,7 @@ static void __exit
dasd_eckd_cleanup(void)
{
ccw_driver_unregister(&dasd_eckd_driver);
+ kfree(dasd_reserve_req);
}
module_init(dasd_eckd_init);
diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h
index dd6385a5af1..0eb49655a6c 100644
--- a/drivers/s390/block/dasd_eckd.h
+++ b/drivers/s390/block/dasd_eckd.h
@@ -320,7 +320,12 @@ struct dasd_gneq {
__u8 identifier:2;
__u8 reserved:6;
} __attribute__ ((packed)) flags;
- __u8 reserved[7];
+ __u8 reserved[5];
+ struct {
+ __u8 value:2;
+ __u8 number:6;
+ } __attribute__ ((packed)) timeout;
+ __u8 reserved3;
__u16 subsystemID;
__u8 reserved2[22];
} __attribute__ ((packed));
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
index 37282b90eec..bec5486e0e6 100644
--- a/drivers/s390/block/dasd_fba.c
+++ b/drivers/s390/block/dasd_fba.c
@@ -163,6 +163,8 @@ dasd_fba_check_characteristics(struct dasd_device *device)
return rc;
}
+ device->default_expires = DASD_EXPIRES;
+
readonly = dasd_device_is_ro(device);
if (readonly)
set_bit(DASD_FLAG_DEVICE_RO, &device->flags);
@@ -370,7 +372,7 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev,
cqr->startdev = memdev;
cqr->memdev = memdev;
cqr->block = block;
- cqr->expires = 5 * 60 * HZ; /* 5 minutes */
+ cqr->expires = memdev->default_expires * HZ; /* default 5 minutes */
cqr->retries = 32;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index 49b431d135e..500678d7116 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -186,7 +186,7 @@ struct dasd_ccw_req {
/* ... and how */
unsigned long starttime; /* jiffies time of request start */
- int expires; /* expiration period in jiffies */
+ unsigned long expires; /* expiration period in jiffies */
char lpm; /* logical path mask */
void *data; /* pointer to data area */
@@ -224,6 +224,9 @@ struct dasd_ccw_req {
#define DASD_CQR_CLEARED 0x84 /* request was cleared */
#define DASD_CQR_SUCCESS 0x85 /* request was successful */
+/* default expiration time*/
+#define DASD_EXPIRES 300
+#define DASD_EXPIRES_MAX 40000000
/* per dasd_ccw_req flags */
#define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */
@@ -404,6 +407,9 @@ struct dasd_device {
/* hook for alias management */
struct list_head alias_list;
+
+ /* default expiration time in s */
+ unsigned long default_expires;
};
struct dasd_block {
diff --git a/drivers/s390/cio/ccwreq.c b/drivers/s390/cio/ccwreq.c
index 7f206ed44fd..d15f8b4d78b 100644
--- a/drivers/s390/cio/ccwreq.c
+++ b/drivers/s390/cio/ccwreq.c
@@ -38,9 +38,13 @@ static u16 ccwreq_next_path(struct ccw_device *cdev)
{
struct ccw_request *req = &cdev->private->req;
+ if (!req->singlepath) {
+ req->mask = 0;
+ goto out;
+ }
req->retries = req->maxretries;
req->mask = lpm_adjust(req->mask >>= 1, req->lpm);
-
+out:
return req->mask;
}
@@ -113,8 +117,12 @@ void ccw_request_start(struct ccw_device *cdev)
{
struct ccw_request *req = &cdev->private->req;
- /* Try all paths twice to counter link flapping. */
- req->mask = 0x8080;
+ if (req->singlepath) {
+ /* Try all paths twice to counter link flapping. */
+ req->mask = 0x8080;
+ } else
+ req->mask = req->lpm;
+
req->retries = req->maxretries;
req->mask = lpm_adjust(req->mask, req->lpm);
req->drc = 0;
@@ -182,6 +190,8 @@ static enum io_status ccwreq_status(struct ccw_device *cdev, struct irb *lcirb)
/* Ask the driver what to do */
if (cdev->drv && cdev->drv->uc_handler) {
todo = cdev->drv->uc_handler(cdev, lcirb);
+ CIO_TRACE_EVENT(2, "uc_response");
+ CIO_HEX_EVENT(2, &todo, sizeof(todo));
switch (todo) {
case UC_TODO_RETRY:
return IO_STATUS_ERROR;
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 407d0e9adfa..4cbb1a6ca33 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -29,6 +29,7 @@
#include "chsc.h"
static void *sei_page;
+static DEFINE_SPINLOCK(siosl_lock);
static DEFINE_SPINLOCK(sda_lock);
/**
@@ -48,6 +49,7 @@ int chsc_error_from_response(int response)
case 0x0007:
case 0x0008:
case 0x000a:
+ case 0x0104:
return -EINVAL;
case 0x0004:
return -EOPNOTSUPP;
@@ -974,3 +976,49 @@ int chsc_sstpi(void *page, void *result, size_t size)
return (rr->response.code == 0x0001) ? 0 : -EIO;
}
+static struct {
+ struct chsc_header request;
+ u32 word1;
+ struct subchannel_id sid;
+ u32 word3;
+ struct chsc_header response;
+ u32 word[11];
+} __attribute__ ((packed)) siosl_area __attribute__ ((__aligned__(PAGE_SIZE)));
+
+int chsc_siosl(struct subchannel_id schid)
+{
+ unsigned long flags;
+ int ccode;
+ int rc;
+
+ spin_lock_irqsave(&siosl_lock, flags);
+ memset(&siosl_area, 0, sizeof(siosl_area));
+ siosl_area.request.length = 0x0010;
+ siosl_area.request.code = 0x0046;
+ siosl_area.word1 = 0x80000000;
+ siosl_area.sid = schid;
+
+ ccode = chsc(&siosl_area);
+ if (ccode > 0) {
+ if (ccode == 3)
+ rc = -ENODEV;
+ else
+ rc = -EBUSY;
+ CIO_MSG_EVENT(2, "chsc: chsc failed for 0.%x.%04x (ccode=%d)\n",
+ schid.ssid, schid.sch_no, ccode);
+ goto out;
+ }
+ rc = chsc_error_from_response(siosl_area.response.code);
+ if (rc)
+ CIO_MSG_EVENT(2, "chsc: siosl failed for 0.%x.%04x (rc=%04x)\n",
+ schid.ssid, schid.sch_no,
+ siosl_area.response.code);
+ else
+ CIO_MSG_EVENT(4, "chsc: siosl succeeded for 0.%x.%04x\n",
+ schid.ssid, schid.sch_no);
+out:
+ spin_unlock_irqrestore(&siosl_lock, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(chsc_siosl);
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index 37aa611d4ac..5453013f094 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -80,4 +80,6 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp);
int chsc_error_from_response(int response);
+int chsc_siosl(struct subchannel_id schid);
+
#endif
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 6d229f3523a..51bd3687d16 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -36,6 +36,7 @@
#include "ioasm.h"
#include "io_sch.h"
#include "blacklist.h"
+#include "chsc.h"
static struct timer_list recovery_timer;
static DEFINE_SPINLOCK(recovery_lock);
@@ -486,9 +487,11 @@ static int online_store_handle_offline(struct ccw_device *cdev)
spin_lock_irq(cdev->ccwlock);
ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL);
spin_unlock_irq(cdev->ccwlock);
- } else if (cdev->online && cdev->drv && cdev->drv->set_offline)
+ return 0;
+ }
+ if (cdev->drv && cdev->drv->set_offline)
return ccw_device_set_offline(cdev);
- return 0;
+ return -EINVAL;
}
static int online_store_recog_and_online(struct ccw_device *cdev)
@@ -505,8 +508,8 @@ static int online_store_recog_and_online(struct ccw_device *cdev)
return -EAGAIN;
}
if (cdev->drv && cdev->drv->set_online)
- ccw_device_set_online(cdev);
- return 0;
+ return ccw_device_set_online(cdev);
+ return -EINVAL;
}
static int online_store_handle_online(struct ccw_device *cdev, int force)
@@ -598,6 +601,25 @@ available_show (struct device *dev, struct device_attribute *attr, char *buf)
}
}
+static ssize_t
+initiate_logging(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct subchannel *sch = to_subchannel(dev);
+ int rc;
+
+ rc = chsc_siosl(sch->schid);
+ if (rc < 0) {
+ pr_warning("Logging for subchannel 0.%x.%04x failed with "
+ "errno=%d\n",
+ sch->schid.ssid, sch->schid.sch_no, rc);
+ return rc;
+ }
+ pr_notice("Logging for subchannel 0.%x.%04x was triggered\n",
+ sch->schid.ssid, sch->schid.sch_no);
+ return count;
+}
+
static DEVICE_ATTR(chpids, 0444, chpids_show, NULL);
static DEVICE_ATTR(pimpampom, 0444, pimpampom_show, NULL);
static DEVICE_ATTR(devtype, 0444, devtype_show, NULL);
@@ -605,10 +627,12 @@ static DEVICE_ATTR(cutype, 0444, cutype_show, NULL);
static DEVICE_ATTR(modalias, 0444, modalias_show, NULL);
static DEVICE_ATTR(online, 0644, online_show, online_store);
static DEVICE_ATTR(availability, 0444, available_show, NULL);
+static DEVICE_ATTR(logging, 0200, NULL, initiate_logging);
static struct attribute *io_subchannel_attrs[] = {
&dev_attr_chpids.attr,
&dev_attr_pimpampom.attr,
+ &dev_attr_logging.attr,
NULL,
};
@@ -2036,6 +2060,21 @@ void ccw_device_sched_todo(struct ccw_device *cdev, enum cdev_todo todo)
}
}
+/**
+ * ccw_device_siosl() - initiate logging
+ * @cdev: ccw device
+ *
+ * This function is used to invoke model-dependent logging within the channel
+ * subsystem.
+ */
+int ccw_device_siosl(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+
+ return chsc_siosl(sch->schid);
+}
+EXPORT_SYMBOL_GPL(ccw_device_siosl);
+
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(ccw_device_set_online);
EXPORT_SYMBOL(ccw_device_set_offline);
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 6facb5499a6..82a5ad0d63f 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -208,6 +208,7 @@ static void spid_start(struct ccw_device *cdev)
req->timeout = PGID_TIMEOUT;
req->maxretries = PGID_RETRIES;
req->lpm = 0x80;
+ req->singlepath = 1;
req->callback = spid_callback;
spid_do(cdev);
}
@@ -420,6 +421,7 @@ static void verify_start(struct ccw_device *cdev)
req->timeout = PGID_TIMEOUT;
req->maxretries = PGID_RETRIES;
req->lpm = 0x80;
+ req->singlepath = 1;
if (cdev->private->flags.pgroup) {
CIO_TRACE_EVENT(4, "snid");
CIO_HEX_EVENT(4, devid, sizeof(*devid));
@@ -507,6 +509,7 @@ void ccw_device_disband_start(struct ccw_device *cdev)
req->timeout = PGID_TIMEOUT;
req->maxretries = PGID_RETRIES;
req->lpm = sch->schib.pmcw.pam & sch->opm;
+ req->singlepath = 1;
req->callback = disband_callback;
fn = SPID_FUNC_DISBAND;
if (cdev->private->flags.mpath)
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h
index b9ce712a7f2..469ef93f230 100644
--- a/drivers/s390/cio/io_sch.h
+++ b/drivers/s390/cio/io_sch.h
@@ -92,11 +92,12 @@ enum io_status {
* @filter: optional callback to adjust request status based on IRB data
* @callback: final callback
* @data: user-defined pointer passed to all callbacks
+ * @singlepath: if set, use only one path from @lpm per start I/O
+ * @cancel: non-zero if request was cancelled
+ * @done: non-zero if request was finished
* @mask: current path mask
* @retries: current number of retries
* @drc: delayed return code
- * @cancel: non-zero if request was cancelled
- * @done: non-zero if request was finished
*/
struct ccw_request {
struct ccw1 *cp;
@@ -108,12 +109,13 @@ struct ccw_request {
enum io_status);
void (*callback)(struct ccw_device *, void *, int);
void *data;
+ unsigned int singlepath:1;
/* These fields are used internally. */
+ unsigned int cancel:1;
+ unsigned int done:1;
u16 mask;
u16 retries;
int drc;
- int cancel:1;
- int done:1;
} __attribute__((packed));
/*
diff --git a/drivers/s390/net/smsgiucv_app.c b/drivers/s390/net/smsgiucv_app.c
index 13768879020..4d2ea400042 100644
--- a/drivers/s390/net/smsgiucv_app.c
+++ b/drivers/s390/net/smsgiucv_app.c
@@ -180,6 +180,13 @@ static int __init smsgiucv_app_init(void)
goto fail_put_driver;
}
+ /* convert sender to uppercase characters */
+ if (sender) {
+ int len = strlen(sender);
+ while (len--)
+ sender[len] = toupper(sender[len]);
+ }
+
/* register with the smsgiucv device driver */
rc = smsg_register_callback(SMSG_PREFIX, smsg_app_callback);
if (rc) {