summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-10-25 19:36:50 +0400
committerLinus Torvalds <torvalds@linux-foundation.org>2010-10-25 19:36:50 +0400
commitc4a6eb3b7d5b483de331313e7ea38a6891a3447a (patch)
treede415b67626308b1fa414e47f17959939c017c92 /drivers
parent33081adf8b89d5a716d7e1c60171768d39795b39 (diff)
parent96f4a70d8eb4d746b19d5b5510407c8ff0d00340 (diff)
downloadlinux-c4a6eb3b7d5b483de331313e7ea38a6891a3447a.tar.xz
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: (48 commits) [S390] topology: export cpu topology via proc/sysinfo [S390] topology: move topology sysinfo code [S390] topology: clean up facility detection [S390] cleanup facility list handling [S390] enable ARCH_DMA_ADDR_T_64BIT with 64BIT [S390] dasd: ignore unsolicited interrupts for DIAG [S390] kvm: Enable z196 instruction facilities [S390] dasd: fix unsolicited interrupt recognition [S390] dasd: fix use after free in dbf [S390] kvm: Fix badness at include/asm/mmu_context.h:83 [S390] cio: fix I/O cancel function [S390] topology: change default [S390] smp: use correct cpu address in print_cpu_info() [S390] remove ieee_instruction_pointer from thread_struct [S390] cleanup system call parameter setup [S390] correct alignment of cpuid structure [S390] cleanup lowcore access from external interrupts [S390] cleanup lowcore access from program checks [S390] pgtable: move pte_mkhuge() from hugetlb.h to pgtable.h [S390] fix SIGBUS handling ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/char/hvc_iucv.c4
-rw-r--r--drivers/s390/block/dasd.c24
-rw-r--r--drivers/s390/block/dasd_3990_erp.c3
-rw-r--r--drivers/s390/block/dasd_diag.c19
-rw-r--r--drivers/s390/block/dasd_diag.h4
-rw-r--r--drivers/s390/block/dasd_eckd.c68
-rw-r--r--drivers/s390/block/dasd_proc.c1
-rw-r--r--drivers/s390/char/sclp.c14
-rw-r--r--drivers/s390/char/vmlogrdr.c4
-rw-r--r--drivers/s390/cio/blacklist.c10
-rw-r--r--drivers/s390/cio/chp.c41
-rw-r--r--drivers/s390/cio/chp.h12
-rw-r--r--drivers/s390/cio/chsc.c291
-rw-r--r--drivers/s390/cio/chsc.h28
-rw-r--r--drivers/s390/cio/chsc_sch.c12
-rw-r--r--drivers/s390/cio/css.c50
-rw-r--r--drivers/s390/cio/device.c18
-rw-r--r--drivers/s390/cio/device_fsm.c41
-rw-r--r--drivers/s390/cio/device_pgid.c23
-rw-r--r--drivers/s390/cio/io_sch.h7
-rw-r--r--drivers/s390/crypto/ap_bus.c9
-rw-r--r--drivers/s390/kvm/kvm_virtio.c9
22 files changed, 378 insertions, 314 deletions
diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c
index 7b01bc609de3..c3425bb3a1f6 100644
--- a/drivers/char/hvc_iucv.c
+++ b/drivers/char/hvc_iucv.c
@@ -1303,13 +1303,11 @@ static int __init hvc_iucv_init(void)
if (rc) {
pr_err("Registering IUCV handlers failed with error code=%d\n",
rc);
- goto out_error_iucv;
+ goto out_error_hvc;
}
return 0;
-out_error_iucv:
- iucv_unregister(&hvc_iucv_handler, 0);
out_error_hvc:
for (i = 0; i < hvc_iucv_devices; i++)
if (hvc_iucv_table[i])
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index aa95f1001761..fb613d70c2cb 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -1099,16 +1099,30 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
cqr = (struct dasd_ccw_req *) intparm;
if (!cqr || ((scsw_cc(&irb->scsw) == 1) &&
(scsw_fctl(&irb->scsw) & SCSW_FCTL_START_FUNC) &&
- (scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND))) {
+ ((scsw_stctl(&irb->scsw) == SCSW_STCTL_STATUS_PEND) ||
+ (scsw_stctl(&irb->scsw) == (SCSW_STCTL_STATUS_PEND |
+ SCSW_STCTL_ALERT_STATUS))))) {
if (cqr && cqr->status == DASD_CQR_IN_IO)
cqr->status = DASD_CQR_QUEUED;
+ if (cqr)
+ memcpy(&cqr->irb, irb, sizeof(*irb));
device = dasd_device_from_cdev_locked(cdev);
- if (!IS_ERR(device)) {
- dasd_device_clear_timer(device);
- device->discipline->handle_unsolicited_interrupt(device,
- irb);
+ if (IS_ERR(device))
+ return;
+ /* ignore unsolicited interrupts for DIAG discipline */
+ if (device->discipline == dasd_diag_discipline_pointer) {
dasd_put_device(device);
+ return;
}
+ device->discipline->dump_sense_dbf(device, irb,
+ "unsolicited");
+ if ((device->features & DASD_FEATURE_ERPLOG))
+ device->discipline->dump_sense(device, cqr,
+ irb);
+ dasd_device_clear_timer(device);
+ device->discipline->handle_unsolicited_interrupt(device,
+ irb);
+ dasd_put_device(device);
return;
}
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c
index e82d427ff5eb..968c76cf7127 100644
--- a/drivers/s390/block/dasd_3990_erp.c
+++ b/drivers/s390/block/dasd_3990_erp.c
@@ -221,6 +221,7 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
ccw->cmd_code = CCW_CMD_DCTL;
ccw->count = 4;
ccw->cda = (__u32)(addr_t) DCTL_data;
+ dctl_cqr->flags = erp->flags;
dctl_cqr->function = dasd_3990_erp_DCTL;
dctl_cqr->refers = erp;
dctl_cqr->startdev = device;
@@ -1710,6 +1711,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
ccw->cda = cpa;
/* fill erp related fields */
+ erp->flags = default_erp->flags;
erp->function = dasd_3990_erp_action_1B_32;
erp->refers = default_erp->refers;
erp->startdev = device;
@@ -2354,6 +2356,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr)
ccw->cda = (long)(cqr->cpaddr);
}
+ erp->flags = cqr->flags;
erp->function = dasd_3990_erp_add_erp;
erp->refers = cqr;
erp->startdev = device;
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index 2b3bc3ec0541..266b34b55403 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -228,25 +228,22 @@ dasd_diag_term_IO(struct dasd_ccw_req * cqr)
}
/* Handle external interruption. */
-static void
-dasd_ext_handler(__u16 code)
+static void dasd_ext_handler(unsigned int ext_int_code,
+ unsigned int param32, unsigned long param64)
{
struct dasd_ccw_req *cqr, *next;
struct dasd_device *device;
unsigned long long expires;
unsigned long flags;
- u8 int_code, status;
addr_t ip;
int rc;
- int_code = *((u8 *) DASD_DIAG_LC_INT_CODE);
- status = *((u8 *) DASD_DIAG_LC_INT_STATUS);
- switch (int_code) {
+ switch (ext_int_code >> 24) {
case DASD_DIAG_CODE_31BIT:
- ip = (addr_t) *((u32 *) DASD_DIAG_LC_INT_PARM_31BIT);
+ ip = (addr_t) param32;
break;
case DASD_DIAG_CODE_64BIT:
- ip = (addr_t) *((u64 *) DASD_DIAG_LC_INT_PARM_64BIT);
+ ip = (addr_t) param64;
break;
default:
return;
@@ -281,7 +278,7 @@ dasd_ext_handler(__u16 code)
cqr->stopclk = get_clock();
expires = 0;
- if (status == 0) {
+ if ((ext_int_code & 0xff0000) == 0) {
cqr->status = DASD_CQR_SUCCESS;
/* Start first request on queue if possible -> fast_io. */
if (!list_empty(&device->ccw_queue)) {
@@ -296,8 +293,8 @@ dasd_ext_handler(__u16 code)
} else {
cqr->status = DASD_CQR_QUEUED;
DBF_DEV_EVENT(DBF_DEBUG, device, "interrupt status for "
- "request %p was %d (%d retries left)", cqr, status,
- cqr->retries);
+ "request %p was %d (%d retries left)", cqr,
+ (ext_int_code >> 16) & 0xff, cqr->retries);
dasd_diag_erp(device);
}
diff --git a/drivers/s390/block/dasd_diag.h b/drivers/s390/block/dasd_diag.h
index b8c78267ff3e..4f71fbe60c82 100644
--- a/drivers/s390/block/dasd_diag.h
+++ b/drivers/s390/block/dasd_diag.h
@@ -18,10 +18,6 @@
#define DEV_CLASS_FBA 0x01
#define DEV_CLASS_ECKD 0x04
-#define DASD_DIAG_LC_INT_CODE 132
-#define DASD_DIAG_LC_INT_STATUS 133
-#define DASD_DIAG_LC_INT_PARM_31BIT 128
-#define DASD_DIAG_LC_INT_PARM_64BIT 4536
#define DASD_DIAG_CODE_31BIT 0x03
#define DASD_DIAG_CODE_64BIT 0x07
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 59b4ecfb967b..50cf96389d2c 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1776,13 +1776,13 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device,
}
/* summary unit check */
- if ((scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK) &&
- (irb->ecw[7] == 0x0D)) {
+ sense = dasd_get_sense(irb);
+ if (sense && (sense[7] == 0x0D) &&
+ (scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK)) {
dasd_alias_handle_summary_unit_check(device, irb);
return;
}
- sense = dasd_get_sense(irb);
/* service information message SIM */
if (sense && !(sense[27] & DASD_SENSE_BIT_0) &&
((sense[6] & DASD_SIM_SENSE) == DASD_SIM_SENSE)) {
@@ -1791,26 +1791,15 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device,
return;
}
- if ((scsw_cc(&irb->scsw) == 1) &&
- (scsw_fctl(&irb->scsw) & SCSW_FCTL_START_FUNC) &&
- (scsw_actl(&irb->scsw) & SCSW_ACTL_START_PEND) &&
- (scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND)) {
+ if ((scsw_cc(&irb->scsw) == 1) && !sense &&
+ (scsw_fctl(&irb->scsw) == SCSW_FCTL_START_FUNC) &&
+ (scsw_actl(&irb->scsw) == SCSW_ACTL_START_PEND) &&
+ (scsw_stctl(&irb->scsw) == SCSW_STCTL_STATUS_PEND)) {
/* fake irb do nothing, they are handled elsewhere */
dasd_schedule_device_bh(device);
return;
}
- if (!sense) {
- /* just report other unsolicited interrupts */
- DBF_DEV_EVENT(DBF_ERR, device, "%s",
- "unsolicited interrupt received");
- } else {
- DBF_DEV_EVENT(DBF_ERR, device, "%s",
- "unsolicited interrupt received "
- "(sense available)");
- device->discipline->dump_sense_dbf(device, irb, "unsolicited");
- }
-
dasd_schedule_device_bh(device);
return;
};
@@ -3093,19 +3082,19 @@ dasd_eckd_dump_sense_dbf(struct dasd_device *device, struct irb *irb,
char *reason)
{
u64 *sense;
+ u64 *stat;
sense = (u64 *) dasd_get_sense(irb);
+ stat = (u64 *) &irb->scsw;
if (sense) {
- DBF_DEV_EVENT(DBF_EMERG, device,
- "%s: %s %02x%02x%02x %016llx %016llx %016llx "
- "%016llx", reason,
- scsw_is_tm(&irb->scsw) ? "t" : "c",
- scsw_cc(&irb->scsw), scsw_cstat(&irb->scsw),
- scsw_dstat(&irb->scsw), sense[0], sense[1],
- sense[2], sense[3]);
+ DBF_DEV_EVENT(DBF_EMERG, device, "%s: %016llx %08x : "
+ "%016llx %016llx %016llx %016llx",
+ reason, *stat, *((u32 *) (stat + 1)),
+ sense[0], sense[1], sense[2], sense[3]);
} else {
- DBF_DEV_EVENT(DBF_EMERG, device, "%s",
- "SORRY - NO VALID SENSE AVAILABLE\n");
+ DBF_DEV_EVENT(DBF_EMERG, device, "%s: %016llx %08x : %s",
+ reason, *stat, *((u32 *) (stat + 1)),
+ "NO VALID SENSE");
}
}
@@ -3131,9 +3120,12 @@ static void dasd_eckd_dump_sense_ccw(struct dasd_device *device,
" I/O status report for device %s:\n",
dev_name(&device->cdev->dev));
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " in req: %p CS: 0x%02X DS: 0x%02X CC: 0x%02X RC: %d\n",
- req, scsw_cstat(&irb->scsw), scsw_dstat(&irb->scsw),
- scsw_cc(&irb->scsw), req ? req->intrc : 0);
+ " in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X "
+ "CS:%02X RC:%d\n",
+ req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw),
+ scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw),
+ scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw),
+ req ? req->intrc : 0);
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
" device %s: Failing CCW: %p\n",
dev_name(&device->cdev->dev),
@@ -3234,11 +3226,13 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
" I/O status report for device %s:\n",
dev_name(&device->cdev->dev));
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " in req: %p CS: 0x%02X DS: 0x%02X CC: 0x%02X RC: %d "
- "fcxs: 0x%02X schxs: 0x%02X\n", req,
- scsw_cstat(&irb->scsw), scsw_dstat(&irb->scsw),
- scsw_cc(&irb->scsw), req->intrc,
- irb->scsw.tm.fcxs, irb->scsw.tm.schxs);
+ " in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X "
+ "CS:%02X fcxs:%02X schxs:%02X RC:%d\n",
+ req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw),
+ scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw),
+ scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw),
+ irb->scsw.tm.fcxs, irb->scsw.tm.schxs,
+ req ? req->intrc : 0);
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
" device %s: Failing TCW: %p\n",
dev_name(&device->cdev->dev),
@@ -3246,7 +3240,7 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
tsb = NULL;
sense = NULL;
- if (irb->scsw.tm.tcw && (irb->scsw.tm.fcxs == 0x01))
+ if (irb->scsw.tm.tcw && (irb->scsw.tm.fcxs & 0x01))
tsb = tcw_get_tsb(
(struct tcw *)(unsigned long)irb->scsw.tm.tcw);
@@ -3344,7 +3338,7 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
static void dasd_eckd_dump_sense(struct dasd_device *device,
struct dasd_ccw_req *req, struct irb *irb)
{
- if (req && scsw_is_tm(&req->irb.scsw))
+ if (scsw_is_tm(&irb->scsw))
dasd_eckd_dump_sense_tcw(device, req, irb);
else
dasd_eckd_dump_sense_ccw(device, req, irb);
diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
index 2eb025592809..c4a6a31bd9cd 100644
--- a/drivers/s390/block/dasd_proc.c
+++ b/drivers/s390/block/dasd_proc.c
@@ -251,7 +251,6 @@ static ssize_t dasd_stats_proc_write(struct file *file,
buffer = dasd_get_user_string(user_buf, user_len);
if (IS_ERR(buffer))
return PTR_ERR(buffer);
- DBF_EVENT(DBF_DEBUG, "/proc/dasd/statictics: '%s'\n", buffer);
/* check for valid verbs */
str = skip_spaces(buffer);
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index 5707a80b96b6..35cc4686b99b 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -395,16 +395,16 @@ __sclp_find_req(u32 sccb)
/* Handler for external interruption. Perform request post-processing.
* Prepare read event data request if necessary. Start processing of next
* request on queue. */
-static void
-sclp_interrupt_handler(__u16 code)
+static void sclp_interrupt_handler(unsigned int ext_int_code,
+ unsigned int param32, unsigned long param64)
{
struct sclp_req *req;
u32 finished_sccb;
u32 evbuf_pending;
spin_lock(&sclp_lock);
- finished_sccb = S390_lowcore.ext_params & 0xfffffff8;
- evbuf_pending = S390_lowcore.ext_params & 0x3;
+ finished_sccb = param32 & 0xfffffff8;
+ evbuf_pending = param32 & 0x3;
if (finished_sccb) {
del_timer(&sclp_request_timer);
sclp_running_state = sclp_running_state_reset_pending;
@@ -819,12 +819,12 @@ EXPORT_SYMBOL(sclp_reactivate);
/* Handler for external interruption used during initialization. Modify
* request state to done. */
-static void
-sclp_check_handler(__u16 code)
+static void sclp_check_handler(unsigned int ext_int_code,
+ unsigned int param32, unsigned long param64)
{
u32 finished_sccb;
- finished_sccb = S390_lowcore.ext_params & 0xfffffff8;
+ finished_sccb = param32 & 0xfffffff8;
/* Is this the interrupt we are waiting for? */
if (finished_sccb == 0)
return;
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index 0d6dc4b92cc2..9f661426e4a1 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -215,7 +215,7 @@ static void vmlogrdr_iucv_message_pending(struct iucv_path *path,
static int vmlogrdr_get_recording_class_AB(void)
{
- char cp_command[]="QUERY COMMAND RECORDING ";
+ static const char cp_command[] = "QUERY COMMAND RECORDING ";
char cp_response[80];
char *tail;
int len,i;
@@ -638,7 +638,7 @@ static ssize_t vmlogrdr_recording_status_show(struct device_driver *driver,
char *buf)
{
- char cp_command[] = "QUERY RECORDING ";
+ static const char cp_command[] = "QUERY RECORDING ";
int len;
cpcmd(cp_command, buf, 4096, NULL);
diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c
index 13cb60162e42..76058a5166ed 100644
--- a/drivers/s390/cio/blacklist.c
+++ b/drivers/s390/cio/blacklist.c
@@ -79,17 +79,15 @@ static int pure_hex(char **cp, unsigned int *val, int min_digit,
int max_digit, int max_val)
{
int diff;
- unsigned int value;
diff = 0;
*val = 0;
- while (isxdigit(**cp) && (diff <= max_digit)) {
+ while (diff <= max_digit) {
+ int value = hex_to_bin(**cp);
- if (isdigit(**cp))
- value = **cp - '0';
- else
- value = tolower(**cp) - 'a' + 10;
+ if (value < 0)
+ break;
*val = *val * 16 + value;
(*cp)++;
diff++;
diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c
index 6c9fa15aac7b..2d32233943a9 100644
--- a/drivers/s390/cio/chp.c
+++ b/drivers/s390/cio/chp.c
@@ -1,7 +1,7 @@
/*
* drivers/s390/cio/chp.c
*
- * Copyright IBM Corp. 1999,2007
+ * Copyright IBM Corp. 1999,2010
* Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
* Arnd Bergmann (arndb@de.ibm.com)
* Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
@@ -54,12 +54,6 @@ 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 channel_subsystems[chpid.cssid]->chps[chpid.id];
-}
-
/* Set vary state for given chpid. */
static void set_chp_logically_online(struct chp_id chpid, int onoff)
{
@@ -241,11 +235,13 @@ static ssize_t chp_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct channel_path *chp = to_channelpath(dev);
+ int status;
- if (!chp)
- return 0;
- return (chp_get_status(chp->chpid) ? sprintf(buf, "online\n") :
- sprintf(buf, "offline\n"));
+ mutex_lock(&chp->lock);
+ status = chp->state;
+ mutex_unlock(&chp->lock);
+
+ return status ? sprintf(buf, "online\n") : sprintf(buf, "offline\n");
}
static ssize_t chp_status_write(struct device *dev,
@@ -261,15 +257,18 @@ static ssize_t chp_status_write(struct device *dev,
if (!num_args)
return count;
- if (!strnicmp(cmd, "on", 2) || !strcmp(cmd, "1"))
+ if (!strnicmp(cmd, "on", 2) || !strcmp(cmd, "1")) {
+ mutex_lock(&cp->lock);
error = s390_vary_chpid(cp->chpid, 1);
- else if (!strnicmp(cmd, "off", 3) || !strcmp(cmd, "0"))
+ mutex_unlock(&cp->lock);
+ } else if (!strnicmp(cmd, "off", 3) || !strcmp(cmd, "0")) {
+ mutex_lock(&cp->lock);
error = s390_vary_chpid(cp->chpid, 0);
- else
+ mutex_unlock(&cp->lock);
+ } else
error = -EINVAL;
return error < 0 ? error : count;
-
}
static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write);
@@ -315,10 +314,12 @@ static ssize_t chp_type_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct channel_path *chp = to_channelpath(dev);
+ u8 type;
- if (!chp)
- return 0;
- return sprintf(buf, "%x\n", chp->desc.desc);
+ mutex_lock(&chp->lock);
+ type = chp->desc.desc;
+ mutex_unlock(&chp->lock);
+ return sprintf(buf, "%x\n", type);
}
static DEVICE_ATTR(type, 0444, chp_type_show, NULL);
@@ -395,6 +396,7 @@ int chp_new(struct chp_id chpid)
chp->state = 1;
chp->dev.parent = &channel_subsystems[chpid.cssid]->device;
chp->dev.release = chp_release;
+ mutex_init(&chp->lock);
/* Obtain channel path description and fill it in. */
ret = chsc_determine_base_channel_path_desc(chpid, &chp->desc);
@@ -464,7 +466,10 @@ void *chp_get_chp_desc(struct chp_id chpid)
desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL);
if (!desc)
return NULL;
+
+ mutex_lock(&chp->lock);
memcpy(desc, &chp->desc, sizeof(struct channel_path_desc));
+ mutex_unlock(&chp->lock);
return desc;
}
diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h
index 26c3d2246176..12b4903d6fe3 100644
--- a/drivers/s390/cio/chp.h
+++ b/drivers/s390/cio/chp.h
@@ -1,7 +1,7 @@
/*
* drivers/s390/cio/chp.h
*
- * Copyright IBM Corp. 2007
+ * Copyright IBM Corp. 2007,2010
* Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
*/
@@ -10,6 +10,7 @@
#include <linux/types.h>
#include <linux/device.h>
+#include <linux/mutex.h>
#include <asm/chpid.h>
#include "chsc.h"
#include "css.h"
@@ -40,16 +41,23 @@ static inline int chp_test_bit(u8 *bitmap, int num)
struct channel_path {
+ struct device dev;
struct chp_id chpid;
+ struct mutex lock; /* Serialize access to below members. */
int state;
struct channel_path_desc desc;
/* Channel-measurement related stuff: */
int cmg;
int shared;
void *cmg_chars;
- struct device dev;
};
+/* Return channel_path struct for given chpid. */
+static inline struct channel_path *chpid_to_chp(struct chp_id chpid)
+{
+ return channel_subsystems[chpid.cssid]->chps[chpid.id];
+}
+
int chp_get_status(struct chp_id chpid);
u8 chp_get_sch_opm(struct subchannel *sch);
int chp_is_registered(struct chp_id chpid);
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 4cbb1a6ca33c..1aaddea673e0 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -2,7 +2,7 @@
* drivers/s390/cio/chsc.c
* S/390 common I/O routines -- channel subsystem call
*
- * Copyright IBM Corp. 1999,2008
+ * Copyright IBM Corp. 1999,2010
* Author(s): Ingo Adlung (adlung@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
* Arnd Bergmann (arndb@de.ibm.com)
@@ -29,8 +29,8 @@
#include "chsc.h"
static void *sei_page;
-static DEFINE_SPINLOCK(siosl_lock);
-static DEFINE_SPINLOCK(sda_lock);
+static void *chsc_page;
+static DEFINE_SPINLOCK(chsc_page_lock);
/**
* chsc_error_from_response() - convert a chsc response to an error
@@ -85,17 +85,15 @@ struct chsc_ssd_area {
int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
{
- 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;
+ spin_lock_irq(&chsc_page_lock);
+ memset(chsc_page, 0, PAGE_SIZE);
+ ssd_area = chsc_page;
ssd_area->request.length = 0x0010;
ssd_area->request.code = 0x0004;
ssd_area->ssid = schid.ssid;
@@ -106,25 +104,25 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
/* Check response. */
if (ccode > 0) {
ret = (ccode == 3) ? -ENODEV : -EBUSY;
- goto out_free;
+ goto out;
}
ret = chsc_error_from_response(ssd_area->response.code);
if (ret != 0) {
CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n",
schid.ssid, schid.sch_no,
ssd_area->response.code);
- goto out_free;
+ goto out;
}
if (!ssd_area->sch_valid) {
ret = -ENODEV;
- goto out_free;
+ goto out;
}
/* Copy data */
ret = 0;
memset(ssd, 0, sizeof(struct chsc_ssd_info));
if ((ssd_area->st != SUBCHANNEL_TYPE_IO) &&
(ssd_area->st != SUBCHANNEL_TYPE_MSG))
- goto out_free;
+ goto out;
ssd->path_mask = ssd_area->path_mask;
ssd->fla_valid_mask = ssd_area->fla_valid_mask;
for (i = 0; i < 8; i++) {
@@ -136,8 +134,8 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
if (ssd_area->fla_valid_mask & mask)
ssd->fla[i] = ssd_area->fla[i];
}
-out_free:
- free_page(page);
+out:
+ spin_unlock_irq(&chsc_page_lock);
return ret;
}
@@ -497,6 +495,7 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data)
*/
int chsc_chp_vary(struct chp_id chpid, int on)
{
+ struct channel_path *chp = chpid_to_chp(chpid);
struct chp_link link;
memset(&link, 0, sizeof(struct chp_link));
@@ -506,11 +505,12 @@ int chsc_chp_vary(struct chp_id chpid, int on)
/*
* Redo PathVerification on the devices the chpid connects to
*/
-
- if (on)
+ if (on) {
+ /* Try to update the channel path descritor. */
+ chsc_determine_base_channel_path_desc(chpid, &chp->desc);
for_each_subchannel_staged(s390_subchannel_vary_chpid_on,
__s390_vary_chpid_on, &link);
- else
+ } else
for_each_subchannel_staged(s390_subchannel_vary_chpid_off,
NULL, &link);
@@ -552,7 +552,7 @@ cleanup:
return ret;
}
-int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
+int __chsc_do_secm(struct channel_subsystem *css, int enable)
{
struct {
struct chsc_header request;
@@ -573,7 +573,9 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
} __attribute__ ((packed)) *secm_area;
int ret, ccode;
- secm_area = page;
+ spin_lock_irq(&chsc_page_lock);
+ memset(chsc_page, 0, PAGE_SIZE);
+ secm_area = chsc_page;
secm_area->request.length = 0x0050;
secm_area->request.code = 0x0016;
@@ -584,8 +586,10 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
secm_area->operation_code = enable ? 0 : 1;
ccode = chsc(secm_area);
- if (ccode > 0)
- return (ccode == 3) ? -ENODEV : -EBUSY;
+ if (ccode > 0) {
+ ret = (ccode == 3) ? -ENODEV : -EBUSY;
+ goto out;
+ }
switch (secm_area->response.code) {
case 0x0102:
@@ -598,37 +602,32 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page)
if (ret != 0)
CIO_CRW_EVENT(2, "chsc: secm failed (rc=%04x)\n",
secm_area->response.code);
+out:
+ spin_unlock_irq(&chsc_page_lock);
return ret;
}
int
chsc_secm(struct channel_subsystem *css, int enable)
{
- void *secm_area;
int ret;
- secm_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!secm_area)
- return -ENOMEM;
-
if (enable && !css->cm_enabled) {
css->cub_addr1 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
css->cub_addr2 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!css->cub_addr1 || !css->cub_addr2) {
free_page((unsigned long)css->cub_addr1);
free_page((unsigned long)css->cub_addr2);
- free_page((unsigned long)secm_area);
return -ENOMEM;
}
}
- ret = __chsc_do_secm(css, enable, secm_area);
+ ret = __chsc_do_secm(css, enable);
if (!ret) {
css->cm_enabled = enable;
if (css->cm_enabled) {
ret = chsc_add_cmg_attr(css);
if (ret) {
- memset(secm_area, 0, PAGE_SIZE);
- __chsc_do_secm(css, 0, secm_area);
+ __chsc_do_secm(css, 0);
css->cm_enabled = 0;
}
} else
@@ -638,44 +637,24 @@ chsc_secm(struct channel_subsystem *css, int enable)
free_page((unsigned long)css->cub_addr1);
free_page((unsigned long)css->cub_addr2);
}
- free_page((unsigned long)secm_area);
return ret;
}
int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
- int c, int m,
- struct chsc_response_struct *resp)
+ int c, int m, void *page)
{
+ struct chsc_scpd *scpd_area;
int ccode, ret;
- struct {
- struct chsc_header request;
- u32 : 2;
- u32 m : 1;
- u32 c : 1;
- u32 fmt : 4;
- u32 cssid : 8;
- u32 : 4;
- u32 rfmt : 4;
- u32 first_chpid : 8;
- u32 : 24;
- u32 last_chpid : 8;
- u32 zeroes1;
- struct chsc_header response;
- u8 data[PAGE_SIZE - 20];
- } __attribute__ ((packed)) *scpd_area;
-
if ((rfmt == 1) && !css_general_characteristics.fcs)
return -EINVAL;
if ((rfmt == 2) && !css_general_characteristics.cib)
return -EINVAL;
- scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!scpd_area)
- return -ENOMEM;
+ memset(page, 0, PAGE_SIZE);
+ scpd_area = page;
scpd_area->request.length = 0x0010;
scpd_area->request.code = 0x0002;
-
scpd_area->cssid = chpid.cssid;
scpd_area->first_chpid = chpid.id;
scpd_area->last_chpid = chpid.id;
@@ -685,20 +664,13 @@ int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
scpd_area->rfmt = rfmt;
ccode = chsc(scpd_area);
- if (ccode > 0) {
- ret = (ccode == 3) ? -ENODEV : -EBUSY;
- goto out;
- }
+ if (ccode > 0)
+ return (ccode == 3) ? -ENODEV : -EBUSY;
ret = chsc_error_from_response(scpd_area->response.code);
- if (ret == 0)
- /* Success. */
- memcpy(resp, &scpd_area->response, scpd_area->response.length);
- else
+ if (ret)
CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n",
scpd_area->response.code);
-out:
- free_page((unsigned long)scpd_area);
return ret;
}
EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc);
@@ -707,17 +679,19 @@ int chsc_determine_base_channel_path_desc(struct chp_id chpid,
struct channel_path_desc *desc)
{
struct chsc_response_struct *chsc_resp;
+ struct chsc_scpd *scpd_area;
+ unsigned long flags;
int ret;
- chsc_resp = kzalloc(sizeof(*chsc_resp), GFP_KERNEL);
- if (!chsc_resp)
- return -ENOMEM;
- ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, chsc_resp);
+ spin_lock_irqsave(&chsc_page_lock, flags);
+ scpd_area = chsc_page;
+ ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, scpd_area);
if (ret)
- goto out_free;
+ goto out;
+ chsc_resp = (void *)&scpd_area->response;
memcpy(desc, &chsc_resp->data, sizeof(*desc));
-out_free:
- kfree(chsc_resp);
+out:
+ spin_unlock_irqrestore(&chsc_page_lock, flags);
return ret;
}
@@ -725,33 +699,22 @@ static void
chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
struct cmg_chars *chars)
{
- switch (chp->cmg) {
- case 2:
- case 3:
- chp->cmg_chars = kmalloc(sizeof(struct cmg_chars),
- GFP_KERNEL);
- if (chp->cmg_chars) {
- int i, mask;
- struct cmg_chars *cmg_chars;
-
- cmg_chars = chp->cmg_chars;
- for (i = 0; i < NR_MEASUREMENT_CHARS; i++) {
- mask = 0x80 >> (i + 3);
- if (cmcv & mask)
- cmg_chars->values[i] = chars->values[i];
- else
- cmg_chars->values[i] = 0;
- }
- }
- break;
- default:
- /* No cmg-dependent data. */
- break;
+ struct cmg_chars *cmg_chars;
+ int i, mask;
+
+ cmg_chars = chp->cmg_chars;
+ for (i = 0; i < NR_MEASUREMENT_CHARS; i++) {
+ mask = 0x80 >> (i + 3);
+ if (cmcv & mask)
+ cmg_chars->values[i] = chars->values[i];
+ else
+ cmg_chars->values[i] = 0;
}
}
int chsc_get_channel_measurement_chars(struct channel_path *chp)
{
+ struct cmg_chars *cmg_chars;
int ccode, ret;
struct {
@@ -775,13 +738,16 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp)
u32 data[NR_MEASUREMENT_CHARS];
} __attribute__ ((packed)) *scmc_area;
- scmc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!scmc_area)
+ chp->cmg_chars = NULL;
+ cmg_chars = kmalloc(sizeof(*cmg_chars), GFP_KERNEL);
+ if (!cmg_chars)
return -ENOMEM;
+ spin_lock_irq(&chsc_page_lock);
+ memset(chsc_page, 0, PAGE_SIZE);
+ scmc_area = chsc_page;
scmc_area->request.length = 0x0010;
scmc_area->request.code = 0x0022;
-
scmc_area->first_chpid = chp->chpid.id;
scmc_area->last_chpid = chp->chpid.id;
@@ -792,53 +758,65 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp)
}
ret = chsc_error_from_response(scmc_area->response.code);
- if (ret == 0) {
- /* Success. */
- if (!scmc_area->not_valid) {
- chp->cmg = scmc_area->cmg;
- chp->shared = scmc_area->shared;
- chsc_initialize_cmg_chars(chp, scmc_area->cmcv,
- (struct cmg_chars *)
- &scmc_area->data);
- } else {
- chp->cmg = -1;
- chp->shared = -1;
- }
- } else {
+ if (ret) {
CIO_CRW_EVENT(2, "chsc: scmc failed (rc=%04x)\n",
scmc_area->response.code);
+ goto out;
+ }
+ if (scmc_area->not_valid) {
+ chp->cmg = -1;
+ chp->shared = -1;
+ goto out;
}
+ chp->cmg = scmc_area->cmg;
+ chp->shared = scmc_area->shared;
+ if (chp->cmg != 2 && chp->cmg != 3) {
+ /* No cmg-dependent data. */
+ goto out;
+ }
+ chp->cmg_chars = cmg_chars;
+ chsc_initialize_cmg_chars(chp, scmc_area->cmcv,
+ (struct cmg_chars *) &scmc_area->data);
out:
- free_page((unsigned long)scmc_area);
+ spin_unlock_irq(&chsc_page_lock);
+ if (!chp->cmg_chars)
+ kfree(cmg_chars);
+
return ret;
}
-int __init chsc_alloc_sei_area(void)
+int __init chsc_init(void)
{
int ret;
sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!sei_page) {
- CIO_MSG_EVENT(0, "Can't allocate page for processing of "
- "chsc machine checks!\n");
- return -ENOMEM;
+ chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!sei_page || !chsc_page) {
+ ret = -ENOMEM;
+ goto out_err;
}
ret = crw_register_handler(CRW_RSC_CSS, chsc_process_crw);
if (ret)
- kfree(sei_page);
+ goto out_err;
+ return ret;
+out_err:
+ free_page((unsigned long)chsc_page);
+ free_page((unsigned long)sei_page);
return ret;
}
-void __init chsc_free_sei_area(void)
+void __init chsc_init_cleanup(void)
{
crw_unregister_handler(CRW_RSC_CSS);
- kfree(sei_page);
+ free_page((unsigned long)chsc_page);
+ free_page((unsigned long)sei_page);
}
int chsc_enable_facility(int operation_code)
{
+ unsigned long flags;
int ret;
- static struct {
+ struct {
struct chsc_header request;
u8 reserved1:4;
u8 format:4;
@@ -851,32 +829,33 @@ int chsc_enable_facility(int operation_code)
u32 reserved5:4;
u32 format2:4;
u32 reserved6:24;
- } __attribute__ ((packed, aligned(4096))) sda_area;
+ } __attribute__ ((packed)) *sda_area;
- spin_lock(&sda_lock);
- memset(&sda_area, 0, sizeof(sda_area));
- sda_area.request.length = 0x0400;
- sda_area.request.code = 0x0031;
- sda_area.operation_code = operation_code;
+ spin_lock_irqsave(&chsc_page_lock, flags);
+ memset(chsc_page, 0, PAGE_SIZE);
+ sda_area = chsc_page;
+ sda_area->request.length = 0x0400;
+ sda_area->request.code = 0x0031;
+ sda_area->operation_code = operation_code;
- ret = chsc(&sda_area);
+ ret = chsc(sda_area);
if (ret > 0) {
ret = (ret == 3) ? -ENODEV : -EBUSY;
goto out;
}
- switch (sda_area.response.code) {
+ switch (sda_area->response.code) {
case 0x0101:
ret = -EOPNOTSUPP;
break;
default:
- ret = chsc_error_from_response(sda_area.response.code);
+ ret = chsc_error_from_response(sda_area->response.code);
}
if (ret != 0)
CIO_CRW_EVENT(2, "chsc: sda (oc=%x) failed (rc=%04x)\n",
- operation_code, sda_area.response.code);
- out:
- spin_unlock(&sda_lock);
+ operation_code, sda_area->response.code);
+out:
+ spin_unlock_irqrestore(&chsc_page_lock, flags);
return ret;
}
@@ -895,13 +874,12 @@ chsc_determine_css_characteristics(void)
struct chsc_header response;
u32 reserved4;
u32 general_char[510];
- u32 chsc_char[518];
+ u32 chsc_char[508];
} __attribute__ ((packed)) *scsc_area;
- scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!scsc_area)
- return -ENOMEM;
-
+ spin_lock_irq(&chsc_page_lock);
+ memset(chsc_page, 0, PAGE_SIZE);
+ scsc_area = chsc_page;
scsc_area->request.length = 0x0010;
scsc_area->request.code = 0x0010;
@@ -921,7 +899,7 @@ chsc_determine_css_characteristics(void)
CIO_CRW_EVENT(2, "chsc: scsc failed (rc=%04x)\n",
scsc_area->response.code);
exit:
- free_page ((unsigned long) scsc_area);
+ spin_unlock_irq(&chsc_page_lock);
return result;
}
@@ -976,29 +954,29 @@ 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)
{
+ struct {
+ struct chsc_header request;
+ u32 word1;
+ struct subchannel_id sid;
+ u32 word3;
+ struct chsc_header response;
+ u32 word[11];
+ } __attribute__ ((packed)) *siosl_area;
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;
+ spin_lock_irqsave(&chsc_page_lock, flags);
+ memset(chsc_page, 0, PAGE_SIZE);
+ siosl_area = chsc_page;
+ siosl_area->request.length = 0x0010;
+ siosl_area->request.code = 0x0046;
+ siosl_area->word1 = 0x80000000;
+ siosl_area->sid = schid;
- ccode = chsc(&siosl_area);
+ ccode = chsc(siosl_area);
if (ccode > 0) {
if (ccode == 3)
rc = -ENODEV;
@@ -1008,17 +986,16 @@ int chsc_siosl(struct subchannel_id schid)
schid.ssid, schid.sch_no, ccode);
goto out;
}
- rc = chsc_error_from_response(siosl_area.response.code);
+ 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);
+ 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);
-
+ spin_unlock_irqrestore(&chsc_page_lock, flags);
return rc;
}
EXPORT_SYMBOL_GPL(chsc_siosl);
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index 5453013f094b..6693f5e3176f 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -57,21 +57,39 @@ struct chsc_ssd_info {
struct chp_id chpid[8];
u16 fla[8];
};
+
+struct chsc_scpd {
+ struct chsc_header request;
+ u32:2;
+ u32 m:1;
+ u32 c:1;
+ u32 fmt:4;
+ u32 cssid:8;
+ u32:4;
+ u32 rfmt:4;
+ u32 first_chpid:8;
+ u32:24;
+ u32 last_chpid:8;
+ u32 zeroes1;
+ struct chsc_header response;
+ u8 data[PAGE_SIZE - 20];
+} __attribute__ ((packed));
+
+
extern int chsc_get_ssd_info(struct subchannel_id schid,
struct chsc_ssd_info *ssd);
extern int chsc_determine_css_characteristics(void);
-extern int chsc_alloc_sei_area(void);
-extern void chsc_free_sei_area(void);
+extern int chsc_init(void);
+extern void chsc_init_cleanup(void);
extern int chsc_enable_facility(int);
struct channel_subsystem;
extern int chsc_secm(struct channel_subsystem *, int);
-int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page);
+int __chsc_do_secm(struct channel_subsystem *css, int enable);
int chsc_chp_vary(struct chp_id chpid, int on);
int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
- int c, int m,
- struct chsc_response_struct *resp);
+ int c, int m, void *page);
int chsc_determine_base_channel_path_desc(struct chp_id chpid,
struct channel_path_desc *desc);
void chsc_chp_online(struct chp_id chpid);
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
index f2b77e7bfc6f..3c3f3ffe2179 100644
--- a/drivers/s390/cio/chsc_sch.c
+++ b/drivers/s390/cio/chsc_sch.c
@@ -688,25 +688,31 @@ out_free:
static int chsc_ioctl_chpd(void __user *user_chpd)
{
+ struct chsc_scpd *scpd_area;
struct chsc_cpd_info *chpd;
int ret;
chpd = kzalloc(sizeof(*chpd), GFP_KERNEL);
- if (!chpd)
- return -ENOMEM;
+ scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!scpd_area || !chpd) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
if (copy_from_user(chpd, user_chpd, sizeof(*chpd))) {
ret = -EFAULT;
goto out_free;
}
ret = chsc_determine_channel_path_desc(chpd->chpid, chpd->fmt,
chpd->rfmt, chpd->c, chpd->m,
- &chpd->chpdb);
+ scpd_area);
if (ret)
goto out_free;
+ memcpy(&chpd->chpdb, &scpd_area->response, scpd_area->response.length);
if (copy_to_user(user_chpd, chpd, sizeof(*chpd)))
ret = -EFAULT;
out_free:
kfree(chpd);
+ free_page((unsigned long)scpd_area);
return ret;
}
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index ca8e1c240c3c..a5050e217150 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -1,7 +1,7 @@
/*
* driver for channel subsystem
*
- * Copyright IBM Corp. 2002, 2009
+ * Copyright IBM Corp. 2002, 2010
*
* Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
@@ -577,7 +577,7 @@ static int __unset_registered(struct device *dev, void *data)
return 0;
}
-void css_schedule_eval_all_unreg(void)
+static void css_schedule_eval_all_unreg(void)
{
unsigned long flags;
struct idset *unreg_set;
@@ -790,7 +790,6 @@ static struct notifier_block css_reboot_notifier = {
static int css_power_event(struct notifier_block *this, unsigned long event,
void *ptr)
{
- void *secm_area;
int ret, i;
switch (event) {
@@ -806,15 +805,8 @@ static int css_power_event(struct notifier_block *this, unsigned long event,
mutex_unlock(&css->mutex);
continue;
}
- secm_area = (void *)get_zeroed_page(GFP_KERNEL |
- GFP_DMA);
- if (secm_area) {
- if (__chsc_do_secm(css, 0, secm_area))
- ret = NOTIFY_BAD;
- free_page((unsigned long)secm_area);
- } else
+ if (__chsc_do_secm(css, 0))
ret = NOTIFY_BAD;
-
mutex_unlock(&css->mutex);
}
break;
@@ -830,15 +822,8 @@ static int css_power_event(struct notifier_block *this, unsigned long event,
mutex_unlock(&css->mutex);
continue;
}
- secm_area = (void *)get_zeroed_page(GFP_KERNEL |
- GFP_DMA);
- if (secm_area) {
- if (__chsc_do_secm(css, 1, secm_area))
- ret = NOTIFY_BAD;
- free_page((unsigned long)secm_area);
- } else
+ if (__chsc_do_secm(css, 1))
ret = NOTIFY_BAD;
-
mutex_unlock(&css->mutex);
}
/* search for subchannels, which appeared during hibernation */
@@ -863,14 +848,11 @@ static int __init css_bus_init(void)
{
int ret, i;
- ret = chsc_determine_css_characteristics();
- if (ret == -ENOMEM)
- goto out;
-
- ret = chsc_alloc_sei_area();
+ ret = chsc_init();
if (ret)
- goto out;
+ return ret;
+ chsc_determine_css_characteristics();
/* Try to enable MSS. */
ret = chsc_enable_facility(CHSC_SDA_OC_MSS);
if (ret)
@@ -956,9 +938,9 @@ out_unregister:
}
bus_unregister(&css_bus_type);
out:
- crw_unregister_handler(CRW_RSC_CSS);
- chsc_free_sei_area();
+ crw_unregister_handler(CRW_RSC_SCH);
idset_free(slow_subchannel_set);
+ chsc_init_cleanup();
pr_alert("The CSS device driver initialization failed with "
"errno=%d\n", ret);
return ret;
@@ -978,9 +960,9 @@ static void __init css_bus_cleanup(void)
device_unregister(&css->device);
}
bus_unregister(&css_bus_type);
- crw_unregister_handler(CRW_RSC_CSS);
- chsc_free_sei_area();
+ crw_unregister_handler(CRW_RSC_SCH);
idset_free(slow_subchannel_set);
+ chsc_init_cleanup();
isc_unregister(IO_SCH_ISC);
}
@@ -1048,7 +1030,16 @@ subsys_initcall_sync(channel_subsystem_init_sync);
void channel_subsystem_reinit(void)
{
+ struct channel_path *chp;
+ struct chp_id chpid;
+
chsc_enable_facility(CHSC_SDA_OC_MSS);
+ chp_id_for_each(&chpid) {
+ chp = chpid_to_chp(chpid);
+ if (!chp)
+ continue;
+ chsc_determine_base_channel_path_desc(chpid, &chp->desc);
+ }
}
#ifdef CONFIG_PROC_FS
@@ -1200,6 +1191,7 @@ static int css_pm_restore(struct device *dev)
struct subchannel *sch = to_subchannel(dev);
struct css_driver *drv;
+ css_update_ssd_info(sch);
if (!sch->dev.driver)
return 0;
drv = to_cssdriver(sch->dev.driver);
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 51bd3687d163..2ff8a22d4257 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -1147,6 +1147,7 @@ err:
static int io_subchannel_chp_event(struct subchannel *sch,
struct chp_link *link, int event)
{
+ struct ccw_device *cdev = sch_get_cdev(sch);
int mask;
mask = chp_ssd_get_mask(&sch->ssd_info, link);
@@ -1156,22 +1157,30 @@ static int io_subchannel_chp_event(struct subchannel *sch,
case CHP_VARY_OFF:
sch->opm &= ~mask;
sch->lpm &= ~mask;
+ if (cdev)
+ cdev->private->path_gone_mask |= mask;
io_subchannel_terminate_path(sch, mask);
break;
case CHP_VARY_ON:
sch->opm |= mask;
sch->lpm |= mask;
+ if (cdev)
+ cdev->private->path_new_mask |= mask;
io_subchannel_verify(sch);
break;
case CHP_OFFLINE:
if (cio_update_schib(sch))
return -ENODEV;
+ if (cdev)
+ cdev->private->path_gone_mask |= mask;
io_subchannel_terminate_path(sch, mask);
break;
case CHP_ONLINE:
if (cio_update_schib(sch))
return -ENODEV;
sch->lpm |= mask & sch->opm;
+ if (cdev)
+ cdev->private->path_new_mask |= mask;
io_subchannel_verify(sch);
break;
}
@@ -1196,6 +1205,7 @@ static void io_subchannel_quiesce(struct subchannel *sch)
cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO));
while (ret == -EBUSY) {
cdev->private->state = DEV_STATE_QUIESCE;
+ cdev->private->iretry = 255;
ret = ccw_device_cancel_halt_clear(cdev);
if (ret == -EBUSY) {
ccw_device_set_timeout(cdev, HZ/10);
@@ -1468,9 +1478,13 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
goto out;
break;
case IO_SCH_UNREG_ATTACH:
+ if (cdev->private->flags.resuming) {
+ /* Device will be handled later. */
+ rc = 0;
+ goto out;
+ }
/* Unregister ccw device. */
- if (!cdev->private->flags.resuming)
- ccw_device_unregister(cdev);
+ ccw_device_unregister(cdev);
break;
default:
break;
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index c9b852647f01..a845695ac314 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -174,7 +174,10 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
ret = cio_clear (sch);
return (ret == 0) ? -EBUSY : ret;
}
- panic("Can't stop i/o on subchannel.\n");
+ /* Function was unsuccessful */
+ CIO_MSG_EVENT(0, "0.%x.%04x: could not stop I/O\n",
+ cdev->private->dev_id.ssid, cdev->private->dev_id.devno);
+ return -EIO;
}
void ccw_device_update_sense_data(struct ccw_device *cdev)
@@ -349,9 +352,13 @@ out:
static void ccw_device_oper_notify(struct ccw_device *cdev)
{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+
if (ccw_device_notify(cdev, CIO_OPER) == NOTIFY_OK) {
/* Reenable channel measurements, if needed. */
ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF);
+ /* Save indication for new paths. */
+ cdev->private->path_new_mask = sch->vpm;
return;
}
/* Driver doesn't want device back. */
@@ -462,6 +469,32 @@ static void ccw_device_request_event(struct ccw_device *cdev, enum dev_event e)
}
}
+static void ccw_device_report_path_events(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ int path_event[8];
+ int chp, mask;
+
+ for (chp = 0, mask = 0x80; chp < 8; chp++, mask >>= 1) {
+ path_event[chp] = PE_NONE;
+ if (mask & cdev->private->path_gone_mask & ~(sch->vpm))
+ path_event[chp] |= PE_PATH_GONE;
+ if (mask & cdev->private->path_new_mask & sch->vpm)
+ path_event[chp] |= PE_PATH_AVAILABLE;
+ if (mask & cdev->private->pgid_reset_mask & sch->vpm)
+ path_event[chp] |= PE_PATHGROUP_ESTABLISHED;
+ }
+ if (cdev->online && cdev->drv->path_event)
+ cdev->drv->path_event(cdev, path_event);
+}
+
+static void ccw_device_reset_path_events(struct ccw_device *cdev)
+{
+ cdev->private->path_gone_mask = 0;
+ cdev->private->path_new_mask = 0;
+ cdev->private->pgid_reset_mask = 0;
+}
+
void
ccw_device_verify_done(struct ccw_device *cdev, int err)
{
@@ -498,6 +531,7 @@ callback:
&cdev->private->irb);
memset(&cdev->private->irb, 0, sizeof(struct irb));
}
+ ccw_device_report_path_events(cdev);
break;
case -ETIME:
case -EUSERS:
@@ -516,6 +550,7 @@ callback:
ccw_device_done(cdev, DEV_STATE_NOT_OPER);
break;
}
+ ccw_device_reset_path_events(cdev);
}
/*
@@ -734,13 +769,14 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event)
int ret;
ccw_device_set_timeout(cdev, 0);
+ cdev->private->iretry = 255;
ret = ccw_device_cancel_halt_clear(cdev);
if (ret == -EBUSY) {
ccw_device_set_timeout(cdev, 3*HZ);
cdev->private->state = DEV_STATE_TIMEOUT_KILL;
return;
}
- if (ret == -ENODEV)
+ if (ret)
dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
else if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
@@ -837,6 +873,7 @@ void ccw_device_kill_io(struct ccw_device *cdev)
{
int ret;
+ cdev->private->iretry = 255;
ret = ccw_device_cancel_halt_clear(cdev);
if (ret == -EBUSY) {
ccw_device_set_timeout(cdev, 3*HZ);
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 82a5ad0d63f6..07a4fd29f096 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -213,6 +213,17 @@ static void spid_start(struct ccw_device *cdev)
spid_do(cdev);
}
+static int pgid_is_reset(struct pgid *p)
+{
+ char *c;
+
+ for (c = (char *)p + 1; c < (char *)(p + 1); c++) {
+ if (*c != 0)
+ return 0;
+ }
+ return 1;
+}
+
static int pgid_cmp(struct pgid *p1, struct pgid *p2)
{
return memcmp((char *) p1 + 1, (char *) p2 + 1,
@@ -223,7 +234,7 @@ static int pgid_cmp(struct pgid *p1, struct pgid *p2)
* Determine pathgroup state from PGID data.
*/
static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
- int *mismatch, int *reserved, int *reset)
+ int *mismatch, int *reserved, u8 *reset)
{
struct pgid *pgid = &cdev->private->pgid[0];
struct pgid *first = NULL;
@@ -238,9 +249,8 @@ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
continue;
if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE)
*reserved = 1;
- if (pgid->inf.ps.state1 == SNID_STATE1_RESET) {
- /* A PGID was reset. */
- *reset = 1;
+ if (pgid_is_reset(pgid)) {
+ *reset |= lpm;
continue;
}
if (!first) {
@@ -307,7 +317,7 @@ static void snid_done(struct ccw_device *cdev, int rc)
struct pgid *pgid;
int mismatch = 0;
int reserved = 0;
- int reset = 0;
+ u8 reset = 0;
u8 donepm;
if (rc)
@@ -321,11 +331,12 @@ static void snid_done(struct ccw_device *cdev, int rc)
donepm = pgid_to_donepm(cdev);
sch->vpm = donepm & sch->opm;
cdev->private->pgid_todo_mask &= ~donepm;
+ cdev->private->pgid_reset_mask |= reset;
pgid_fill(cdev, pgid);
}
out:
CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x "
- "todo=%02x mism=%d rsvd=%d reset=%d\n", id->ssid,
+ "todo=%02x mism=%d rsvd=%d reset=%02x\n", id->ssid,
id->devno, rc, cdev->private->pgid_valid_mask, sch->vpm,
cdev->private->pgid_todo_mask, mismatch, reserved, reset);
switch (rc) {
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h
index 469ef93f2302..d024d2c21897 100644
--- a/drivers/s390/cio/io_sch.h
+++ b/drivers/s390/cio/io_sch.h
@@ -151,8 +151,11 @@ struct ccw_device_private {
struct subchannel_id schid; /* subchannel number */
struct ccw_request req; /* internal I/O request */
int iretry;
- u8 pgid_valid_mask; /* mask of valid PGIDs */
- u8 pgid_todo_mask; /* mask of PGIDs to be adjusted */
+ u8 pgid_valid_mask; /* mask of valid PGIDs */
+ u8 pgid_todo_mask; /* mask of PGIDs to be adjusted */
+ u8 pgid_reset_mask; /* mask of PGIDs which were reset */
+ u8 path_gone_mask; /* mask of paths, that became unavailable */
+ u8 path_new_mask; /* mask of paths, that became available */
struct {
unsigned int fast:1; /* post with "channel end" */
unsigned int repall:1; /* report every interrupt status */
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index 91c6028d7b74..8fd8c62455e9 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -154,14 +154,7 @@ static inline int ap_instructions_available(void)
*/
static int ap_interrupts_available(void)
{
- unsigned long long facility_bits[2];
-
- if (stfle(facility_bits, 2) <= 1)
- return 0;
- if (!(facility_bits[0] & (1ULL << 61)) ||
- !(facility_bits[1] & (1ULL << 62)))
- return 0;
- return 1;
+ return test_facility(1) && test_facility(2);
}
/**
diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c
index 5a46b8c5d68a..375aeeaf9ea5 100644
--- a/drivers/s390/kvm/kvm_virtio.c
+++ b/drivers/s390/kvm/kvm_virtio.c
@@ -372,21 +372,22 @@ static void hotplug_devices(struct work_struct *dummy)
/*
* we emulate the request_irq behaviour on top of s390 extints
*/
-static void kvm_extint_handler(u16 code)
+static void kvm_extint_handler(unsigned int ext_int_code,
+ unsigned int param32, unsigned long param64)
{
struct virtqueue *vq;
u16 subcode;
u32 param;
- subcode = S390_lowcore.cpu_addr;
+ subcode = ext_int_code >> 16;
if ((subcode & 0xff00) != VIRTIO_SUBCODE_64)
return;
/* The LSB might be overloaded, we have to mask it */
- vq = (struct virtqueue *)(S390_lowcore.ext_params2 & ~1UL);
+ vq = (struct virtqueue *)(param64 & ~1UL);
/* We use ext_params to decide what this interrupt means */
- param = S390_lowcore.ext_params & VIRTIO_PARAM_MASK;
+ param = param32 & VIRTIO_PARAM_MASK;
switch (param) {
case VIRTIO_PARAM_CONFIG_CHANGED: