summaryrefslogtreecommitdiff
path: root/drivers/s390/cio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/cio')
-rw-r--r--drivers/s390/cio/airq.c1
-rw-r--r--drivers/s390/cio/ccwgroup.c3
-rw-r--r--drivers/s390/cio/chp.c179
-rw-r--r--drivers/s390/cio/chp.h3
-rw-r--r--drivers/s390/cio/chsc.c137
-rw-r--r--drivers/s390/cio/chsc.h21
-rw-r--r--drivers/s390/cio/chsc_sch.c1
-rw-r--r--drivers/s390/cio/cio.c7
-rw-r--r--drivers/s390/cio/cio.h2
-rw-r--r--drivers/s390/cio/cio_inject.c2
-rw-r--r--drivers/s390/cio/cmf.c15
-rw-r--r--drivers/s390/cio/crw.c5
-rw-r--r--drivers/s390/cio/css.c25
-rw-r--r--drivers/s390/cio/css.h15
-rw-r--r--drivers/s390/cio/device.c44
-rw-r--r--drivers/s390/cio/device_fsm.c2
-rw-r--r--drivers/s390/cio/device_id.c3
-rw-r--r--drivers/s390/cio/device_ops.c2
-rw-r--r--drivers/s390/cio/eadm_sch.c2
-rw-r--r--drivers/s390/cio/idset.c12
-rw-r--r--drivers/s390/cio/ioasm.c115
-rw-r--r--drivers/s390/cio/qdio.h9
-rw-r--r--drivers/s390/cio/qdio_main.c30
-rw-r--r--drivers/s390/cio/qdio_setup.c21
-rw-r--r--drivers/s390/cio/qdio_thinint.c2
-rw-r--r--drivers/s390/cio/scm.c2
-rw-r--r--drivers/s390/cio/trace.h4
-rw-r--r--drivers/s390/cio/vfio_ccw_cp.c9
-rw-r--r--drivers/s390/cio/vfio_ccw_drv.c7
-rw-r--r--drivers/s390/cio/vfio_ccw_private.h2
30 files changed, 412 insertions, 270 deletions
diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c
index a108f2bf5b33..51f1cb31e4aa 100644
--- a/drivers/s390/cio/airq.c
+++ b/drivers/s390/cio/airq.c
@@ -90,7 +90,6 @@ static irqreturn_t do_airq_interrupt(int irq, void *dummy)
struct airq_struct *airq;
struct hlist_head *head;
- set_cpu_flag(CIF_NOHZ_DELAY);
tpi_info = &get_irq_regs()->tpi_info;
trace_s390_cio_adapter_int(tpi_info);
head = &airq_lists[tpi_info->isc];
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index b72f672a7720..7bcf8b98b8dd 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -147,7 +147,7 @@ static ssize_t ccwgroup_online_show(struct device *dev,
online = (gdev->state == CCWGROUP_ONLINE) ? 1 : 0;
- return scnprintf(buf, PAGE_SIZE, "%d\n", online);
+ return sysfs_emit(buf, "%d\n", online);
}
/*
@@ -550,4 +550,5 @@ void ccwgroup_remove_ccwdev(struct ccw_device *cdev)
put_device(&gdev->dev);
}
EXPORT_SYMBOL(ccwgroup_remove_ccwdev);
+MODULE_DESCRIPTION("ccwgroup bus driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c
index 675d7ed82356..4f01b1929240 100644
--- a/drivers/s390/cio/chp.c
+++ b/drivers/s390/cio/chp.c
@@ -127,10 +127,9 @@ static int s390_vary_chpid(struct chp_id chpid, int on)
/*
* Channel measurement related functions
*/
-static ssize_t chp_measurement_chars_read(struct file *filp,
- struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t off, size_t count)
+static ssize_t measurement_chars_read(struct file *filp, struct kobject *kobj,
+ const struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
{
struct channel_path *chp;
struct device *device;
@@ -143,87 +142,92 @@ static ssize_t chp_measurement_chars_read(struct file *filp,
return memory_read_from_buffer(buf, count, &off, &chp->cmg_chars,
sizeof(chp->cmg_chars));
}
+static const BIN_ATTR_ADMIN_RO(measurement_chars, sizeof(struct cmg_chars));
-static const struct bin_attribute chp_measurement_chars_attr = {
- .attr = {
- .name = "measurement_chars",
- .mode = S_IRUSR,
- },
- .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)
+static ssize_t measurement_chars_full_read(struct file *filp,
+ struct kobject *kobj,
+ const struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
{
- void *area;
- struct cmg_entry *entry, reference_buf;
- int idx;
+ struct channel_path *chp = to_channelpath(kobj_to_dev(kobj));
- 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]);
+ return memory_read_from_buffer(buf, count, &off, &chp->cmcb,
+ sizeof(chp->cmcb));
}
+static BIN_ATTR_ADMIN_RO(measurement_chars_full, sizeof(struct cmg_cmcb));
-static ssize_t chp_measurement_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t off, size_t count)
+static ssize_t chp_measurement_copy_block(void *buf, loff_t off, size_t count,
+ struct kobject *kobj, bool extended)
{
struct channel_path *chp;
struct channel_subsystem *css;
struct device *device;
unsigned int size;
+ void *area, *entry;
+ int id, idx;
device = kobj_to_dev(kobj);
chp = to_channelpath(device);
css = to_css(chp->dev.parent);
+ id = chp->chpid.id;
- size = sizeof(struct cmg_entry);
+ if (extended) {
+ /* Check if extended measurement data is available. */
+ if (!chp->extended)
+ return 0;
+
+ size = sizeof(struct cmg_ext_entry);
+ area = css->ecub[id / CSS_ECUES_PER_PAGE];
+ idx = id % CSS_ECUES_PER_PAGE;
+ } else {
+ size = sizeof(struct cmg_entry);
+ area = css->cub[id / CSS_CUES_PER_PAGE];
+ idx = id % CSS_CUES_PER_PAGE;
+ }
+ entry = area + (idx * size);
/* 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;
+
+ memcpy(buf, entry, size);
+
+ return size;
+}
+
+static ssize_t measurement_read(struct file *filp, struct kobject *kobj,
+ const struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ return chp_measurement_copy_block(buf, off, count, kobj, false);
}
+static const BIN_ATTR_ADMIN_RO(measurement, sizeof(struct cmg_entry));
-static const struct bin_attribute chp_measurement_attr = {
- .attr = {
- .name = "measurement",
- .mode = S_IRUSR,
- },
- .size = sizeof(struct cmg_entry),
- .read = chp_measurement_read,
+static ssize_t ext_measurement_read(struct file *filp, struct kobject *kobj,
+ const struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ return chp_measurement_copy_block(buf, off, count, kobj, true);
+}
+static const BIN_ATTR_ADMIN_RO(ext_measurement, sizeof(struct cmg_ext_entry));
+
+static const struct bin_attribute *measurement_attrs[] = {
+ &bin_attr_measurement_chars,
+ &bin_attr_measurement_chars_full,
+ &bin_attr_measurement,
+ &bin_attr_ext_measurement,
+ NULL,
};
+BIN_ATTRIBUTE_GROUPS(measurement);
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);
+ device_remove_groups(&chp->dev, measurement_groups);
}
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;
+ return device_add_groups(&chp->dev, measurement_groups);
}
/*
@@ -239,7 +243,7 @@ static ssize_t chp_status_show(struct device *dev,
status = chp->state;
mutex_unlock(&chp->lock);
- return status ? sprintf(buf, "online\n") : sprintf(buf, "offline\n");
+ return status ? sysfs_emit(buf, "online\n") : sysfs_emit(buf, "offline\n");
}
static ssize_t chp_status_write(struct device *dev,
@@ -320,7 +324,7 @@ static ssize_t chp_type_show(struct device *dev, struct device_attribute *attr,
mutex_lock(&chp->lock);
type = chp->desc.desc;
mutex_unlock(&chp->lock);
- return sprintf(buf, "%x\n", type);
+ return sysfs_emit(buf, "%x\n", type);
}
static DEVICE_ATTR(type, 0444, chp_type_show, NULL);
@@ -333,8 +337,8 @@ static ssize_t chp_cmg_show(struct device *dev, struct device_attribute *attr,
if (!chp)
return 0;
if (chp->cmg == -1) /* channel measurements not available */
- return sprintf(buf, "unknown\n");
- return sprintf(buf, "%d\n", chp->cmg);
+ return sysfs_emit(buf, "unknown\n");
+ return sysfs_emit(buf, "%d\n", chp->cmg);
}
static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL);
@@ -347,8 +351,8 @@ static ssize_t chp_shared_show(struct device *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);
+ return sysfs_emit(buf, "unknown\n");
+ return sysfs_emit(buf, "%x\n", chp->shared);
}
static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL);
@@ -361,7 +365,7 @@ static ssize_t chp_chid_show(struct device *dev, struct device_attribute *attr,
mutex_lock(&chp->lock);
if (chp->desc_fmt1.flags & 0x10)
- rc = sprintf(buf, "%04x\n", chp->desc_fmt1.chid);
+ rc = sysfs_emit(buf, "%04x\n", chp->desc_fmt1.chid);
else
rc = 0;
mutex_unlock(&chp->lock);
@@ -378,7 +382,7 @@ static ssize_t chp_chid_external_show(struct device *dev,
mutex_lock(&chp->lock);
if (chp->desc_fmt1.flags & 0x10)
- rc = sprintf(buf, "%x\n", chp->desc_fmt1.flags & 0x8 ? 1 : 0);
+ rc = sysfs_emit(buf, "%x\n", chp->desc_fmt1.flags & 0x8 ? 1 : 0);
else
rc = 0;
mutex_unlock(&chp->lock);
@@ -394,15 +398,44 @@ static ssize_t chp_esc_show(struct device *dev,
ssize_t rc;
mutex_lock(&chp->lock);
- rc = sprintf(buf, "%x\n", chp->desc_fmt1.esc);
+ rc = sysfs_emit(buf, "%x\n", chp->desc_fmt1.esc);
mutex_unlock(&chp->lock);
return rc;
}
static DEVICE_ATTR(esc, 0444, chp_esc_show, NULL);
+static char apply_max_suffix(unsigned long *value, unsigned long base)
+{
+ static char suffixes[] = { 0, 'K', 'M', 'G', 'T' };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(suffixes) - 1; i++) {
+ if (*value < base || *value % base != 0)
+ break;
+ *value /= base;
+ }
+
+ return suffixes[i];
+}
+
+static ssize_t speed_bps_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct channel_path *chp = to_channelpath(dev);
+ unsigned long speed = chp->speed;
+ char suffix;
+
+ suffix = apply_max_suffix(&speed, 1000);
+
+ return suffix ? sysfs_emit(buf, "%lu%c\n", speed, suffix) :
+ sysfs_emit(buf, "%lu\n", speed);
+}
+
+static DEVICE_ATTR_RO(speed_bps);
+
static ssize_t util_string_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr, char *buf,
+ const struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
struct channel_path *chp = to_channelpath(kobj_to_dev(kobj));
@@ -415,10 +448,10 @@ static ssize_t util_string_read(struct file *filp, struct kobject *kobj,
return rc;
}
-static BIN_ATTR_RO(util_string,
- sizeof(((struct channel_path_desc_fmt3 *)0)->util_str));
+static const BIN_ATTR_RO(util_string,
+ sizeof(((struct channel_path_desc_fmt3 *)0)->util_str));
-static struct bin_attribute *chp_bin_attrs[] = {
+static const struct bin_attribute *const chp_bin_attrs[] = {
&bin_attr_util_string,
NULL,
};
@@ -432,11 +465,12 @@ static struct attribute *chp_attrs[] = {
&dev_attr_chid.attr,
&dev_attr_chid_external.attr,
&dev_attr_esc.attr,
+ &dev_attr_speed_bps.attr,
NULL,
};
-static struct attribute_group chp_attr_group = {
+static const struct attribute_group chp_attr_group = {
.attrs = chp_attrs,
- .bin_attrs = chp_bin_attrs,
+ .bin_attrs_new = chp_bin_attrs,
};
static const struct attribute_group *chp_attr_groups[] = {
&chp_attr_group,
@@ -661,7 +695,8 @@ static int info_update(void)
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 ;
+ if (!rc)
+ chp_info_expires = jiffies + CHP_INFO_UPDATE_INTERVAL;
}
mutex_unlock(&info_lock);
diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h
index 7ee9eba0abcb..391b52a7474c 100644
--- a/drivers/s390/cio/chp.h
+++ b/drivers/s390/cio/chp.h
@@ -51,7 +51,10 @@ struct channel_path {
/* Channel-measurement related stuff: */
int cmg;
int shared;
+ int extended;
+ unsigned long speed;
struct cmg_chars cmg_chars;
+ struct cmg_cmcb cmcb;
};
/* Return channel_path struct for given chpid. */
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 44ea76f9e1de..e6462317abd0 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -24,7 +24,6 @@
#include <asm/crw.h>
#include <asm/isc.h>
#include <asm/ebcdic.h>
-#include <asm/ap.h>
#include "css.h"
#include "cio.h"
@@ -40,6 +39,20 @@ static DEFINE_SPINLOCK(chsc_page_lock);
#define SEI_VF_FLA 0xc0 /* VF flag for Full Link Address */
#define SEI_RS_CHPID 0x4 /* 4 in RS field indicates CHPID */
+static BLOCKING_NOTIFIER_HEAD(chsc_notifiers);
+
+int chsc_notifier_register(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&chsc_notifiers, nb);
+}
+EXPORT_SYMBOL(chsc_notifier_register);
+
+int chsc_notifier_unregister(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&chsc_notifiers, nb);
+}
+EXPORT_SYMBOL(chsc_notifier_unregister);
+
/**
* chsc_error_from_response() - convert a chsc response to an error
* @response: chsc response code
@@ -363,7 +376,7 @@ struct lir {
#define PARAMS_LEN 10 /* PARAMS=xx,xxxxxx */
#define NODEID_LEN 35 /* NODEID=tttttt/mdl,mmm.ppssssssssssss,xxxx */
-/* Copy EBCIDC text, convert to ASCII and optionally add delimiter. */
+/* Copy EBCDIC text, convert to ASCII and optionally add delimiter. */
static char *store_ebcdic(char *dest, const char *src, unsigned long len,
char delim)
{
@@ -581,7 +594,8 @@ static void chsc_process_sei_ap_cfg_chg(struct chsc_sei_nt0_area *sei_area)
if (sei_area->rs != 5)
return;
- ap_bus_cfg_chg();
+ blocking_notifier_call_chain(&chsc_notifiers,
+ CHSC_NOTIFY_AP_CFG, NULL);
}
static void chsc_process_sei_fces_event(struct chsc_sei_nt0_area *sei_area)
@@ -857,22 +871,22 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable)
struct {
struct chsc_header request;
u32 operation_code : 2;
- u32 : 30;
+ u32 : 1;
+ u32 e : 1;
+ u32 : 28;
u32 key : 4;
u32 : 28;
- u32 zeroes1;
- dma32_t cub_addr1;
- u32 zeroes2;
- dma32_t cub_addr2;
- u32 reserved[13];
+ dma64_t cub[CSS_NUM_CUB_PAGES];
+ dma64_t ecub[CSS_NUM_ECUB_PAGES];
+ u32 reserved[5];
struct chsc_header response;
u32 status : 8;
u32 : 4;
u32 fmt : 4;
u32 : 16;
- } *secm_area;
+ } __packed *secm_area;
unsigned long flags;
- int ret, ccode;
+ int ret, ccode, i;
spin_lock_irqsave(&chsc_page_lock, flags);
memset(chsc_page, 0, PAGE_SIZE);
@@ -881,8 +895,12 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable)
secm_area->request.code = 0x0016;
secm_area->key = PAGE_DEFAULT_KEY >> 4;
- secm_area->cub_addr1 = virt_to_dma32(css->cub_addr1);
- secm_area->cub_addr2 = virt_to_dma32(css->cub_addr2);
+ secm_area->e = 1;
+
+ for (i = 0; i < CSS_NUM_CUB_PAGES; i++)
+ secm_area->cub[i] = (__force dma64_t)virt_to_dma32(css->cub[i]);
+ for (i = 0; i < CSS_NUM_ECUB_PAGES; i++)
+ secm_area->ecub[i] = virt_to_dma64(css->ecub[i]);
secm_area->operation_code = enable ? 0 : 1;
@@ -908,19 +926,47 @@ out:
return ret;
}
+static int cub_alloc(struct channel_subsystem *css)
+{
+ int i;
+
+ for (i = 0; i < CSS_NUM_CUB_PAGES; i++) {
+ css->cub[i] = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!css->cub[i])
+ return -ENOMEM;
+ }
+ for (i = 0; i < CSS_NUM_ECUB_PAGES; i++) {
+ css->ecub[i] = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!css->ecub[i])
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void cub_free(struct channel_subsystem *css)
+{
+ int i;
+
+ for (i = 0; i < CSS_NUM_CUB_PAGES; i++) {
+ free_page((unsigned long)css->cub[i]);
+ css->cub[i] = NULL;
+ }
+ for (i = 0; i < CSS_NUM_ECUB_PAGES; i++) {
+ free_page((unsigned long)css->ecub[i]);
+ css->ecub[i] = NULL;
+ }
+}
+
int
chsc_secm(struct channel_subsystem *css, int enable)
{
int ret;
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);
- return -ENOMEM;
- }
+ ret = cub_alloc(css);
+ if (ret)
+ goto out;
}
ret = __chsc_do_secm(css, enable);
if (!ret) {
@@ -934,10 +980,11 @@ chsc_secm(struct channel_subsystem *css, int enable)
} else
chsc_remove_cmg_attr(css);
}
- if (!css->cm_enabled) {
- free_page((unsigned long)css->cub_addr1);
- free_page((unsigned long)css->cub_addr2);
- }
+
+out:
+ if (!css->cm_enabled)
+ cub_free(css);
+
return ret;
}
@@ -1019,6 +1066,18 @@ chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
}
}
+static unsigned long scmc_get_speed(u32 s, u32 p)
+{
+ unsigned long speed = s;
+
+ if (!p)
+ p = 8;
+ while (p--)
+ speed *= 10;
+
+ return speed;
+}
+
int chsc_get_channel_measurement_chars(struct channel_path *chp)
{
unsigned long flags;
@@ -1033,20 +1092,13 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp)
u32 zeroes1;
struct chsc_header response;
u32 zeroes2;
- u32 not_valid : 1;
- u32 shared : 1;
- u32 : 22;
- u32 chpid : 8;
- u32 cmcv : 5;
- u32 : 11;
- u32 cmgq : 8;
- u32 cmg : 8;
- u32 zeroes3;
- u32 data[NR_MEASUREMENT_CHARS];
+ struct cmg_cmcb cmcb;
} *scmc_area;
chp->shared = -1;
chp->cmg = -1;
+ chp->extended = 0;
+ chp->speed = 0;
if (!css_chsc_characteristics.scmc || !css_chsc_characteristics.secm)
return -EINVAL;
@@ -1071,17 +1123,16 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp)
scmc_area->response.code);
goto out;
}
- if (scmc_area->not_valid)
+ chp->cmcb = scmc_area->cmcb;
+ if (scmc_area->cmcb.not_valid)
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;
- }
- chsc_initialize_cmg_chars(chp, scmc_area->cmcv,
- (struct cmg_chars *) &scmc_area->data);
+ chp->cmg = scmc_area->cmcb.cmg;
+ chp->shared = scmc_area->cmcb.shared;
+ chp->extended = scmc_area->cmcb.extended;
+ chp->speed = scmc_get_speed(scmc_area->cmcb.cmgs, scmc_area->cmcb.cmgp);
+ chsc_initialize_cmg_chars(chp, scmc_area->cmcb.cmcv,
+ (struct cmg_chars *)&scmc_area->cmcb.data);
out:
spin_unlock_irqrestore(&chsc_page_lock, flags);
return ret;
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index 03602295f335..6fe983ebf4b3 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -17,11 +17,32 @@ struct cmg_chars {
u32 values[NR_MEASUREMENT_CHARS];
};
+struct cmg_cmcb {
+ u32 not_valid : 1;
+ u32 shared : 1;
+ u32 extended : 1;
+ u32 : 21;
+ u32 chpid : 8;
+ u32 cmcv : 5;
+ u32 : 7;
+ u32 cmgp : 4;
+ u32 cmgq : 8;
+ u32 cmg : 8;
+ u32 : 16;
+ u32 cmgs : 16;
+ u32 data[NR_MEASUREMENT_CHARS];
+};
+
#define NR_MEASUREMENT_ENTRIES 8
struct cmg_entry {
u32 values[NR_MEASUREMENT_ENTRIES];
};
+#define NR_EXT_MEASUREMENT_ENTRIES 16
+struct cmg_ext_entry {
+ u32 values[NR_EXT_MEASUREMENT_ENTRIES];
+};
+
struct channel_path_desc_fmt1 {
u8 flags;
u8 lsn;
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
index e6c800653f98..1e58ee3cc87d 100644
--- a/drivers/s390/cio/chsc_sch.c
+++ b/drivers/s390/cio/chsc_sch.c
@@ -924,7 +924,6 @@ static const struct file_operations chsc_fops = {
.release = chsc_release,
.unlocked_ioctl = chsc_ioctl,
.compat_ioctl = chsc_ioctl,
- .llseek = no_llseek,
};
static struct miscdevice chsc_misc_device = {
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 7e759c21480e..ad17ab0a9314 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -459,10 +459,14 @@ int cio_update_schib(struct subchannel *sch)
{
struct schib schib;
- if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib))
+ if (stsch(sch->schid, &schib))
return -ENODEV;
memcpy(&sch->schib, &schib, sizeof(schib));
+
+ if (!css_sch_is_valid(&schib))
+ return -EACCES;
+
return 0;
}
EXPORT_SYMBOL_GPL(cio_update_schib);
@@ -535,7 +539,6 @@ static irqreturn_t do_cio_interrupt(int irq, void *dummy)
struct subchannel *sch;
struct irb *irb;
- set_cpu_flag(CIF_NOHZ_DELAY);
tpi_info = &get_irq_regs()->tpi_info;
trace_s390_cio_interrupt(tpi_info);
irb = this_cpu_ptr(&cio_irb);
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index a9057a5b670a..08a5e9380e75 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -19,7 +19,7 @@ struct pmcw {
u32 intparm; /* interruption parameter */
u32 qf : 1; /* qdio facility */
u32 w : 1;
- u32 isc : 3; /* interruption sublass */
+ u32 isc : 3; /* interruption subclass */
u32 res5 : 3; /* reserved zeros */
u32 ena : 1; /* enabled */
u32 lm : 2; /* limit mode */
diff --git a/drivers/s390/cio/cio_inject.c b/drivers/s390/cio/cio_inject.c
index 8613fa937237..a2e771ebae8e 100644
--- a/drivers/s390/cio/cio_inject.c
+++ b/drivers/s390/cio/cio_inject.c
@@ -95,7 +95,7 @@ static ssize_t crw_inject_write(struct file *file, const char __user *buf,
return -EINVAL;
}
- buffer = vmemdup_user(buf, lbuf);
+ buffer = memdup_user_nul(buf, lbuf);
if (IS_ERR(buffer))
return -ENOMEM;
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index f80dc18e2a76..fdab760f1f28 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -46,7 +46,7 @@
/* indices for READCMB */
enum cmb_index {
avg_utilization = -1,
- /* basic and exended format: */
+ /* basic and extended format: */
cmb_ssch_rsch_count = 0,
cmb_sample_count,
cmb_device_connect_time,
@@ -135,7 +135,7 @@ static inline u64 time_to_nsec(u32 value)
* Users are usually interested in average times,
* not accumulated time.
* This also helps us with atomicity problems
- * when reading sinlge values.
+ * when reading single values.
*/
static inline u64 time_to_avg_nsec(u32 value, u32 count)
{
@@ -977,8 +977,7 @@ static struct cmb_operations cmbops_extended = {
static ssize_t cmb_show_attr(struct device *dev, char *buf, enum cmb_index idx)
{
- return sprintf(buf, "%lld\n",
- (unsigned long long) cmf_read(to_ccwdev(dev), idx));
+ return sysfs_emit(buf, "%lld\n", cmf_read(to_ccwdev(dev), idx));
}
static ssize_t cmb_show_avg_sample_interval(struct device *dev,
@@ -998,7 +997,7 @@ static ssize_t cmb_show_avg_sample_interval(struct device *dev,
} else
interval = -1;
spin_unlock_irq(cdev->ccwlock);
- return sprintf(buf, "%ld\n", interval);
+ return sysfs_emit(buf, "%ld\n", interval);
}
static ssize_t cmb_show_avg_utilization(struct device *dev,
@@ -1007,7 +1006,7 @@ static ssize_t cmb_show_avg_utilization(struct device *dev,
{
unsigned long u = cmf_read(to_ccwdev(dev), avg_utilization);
- return sprintf(buf, "%02lu.%01lu%%\n", u / 10, u % 10);
+ return sysfs_emit(buf, "%02lu.%01lu%%\n", u / 10, u % 10);
}
#define cmf_attr(name) \
@@ -1080,7 +1079,7 @@ static ssize_t cmb_enable_show(struct device *dev,
{
struct ccw_device *cdev = to_ccwdev(dev);
- return sprintf(buf, "%d\n", cmf_enabled(cdev));
+ return sysfs_emit(buf, "%d\n", cmf_enabled(cdev));
}
static ssize_t cmb_enable_store(struct device *dev,
@@ -1227,7 +1226,7 @@ int cmf_readall(struct ccw_device *cdev, struct cmbdata *data)
return cmbops->readall(cdev, data);
}
-/* Reenable cmf when a disconnected device becomes available again. */
+/* Re-enable cmf when a disconnected device becomes available again. */
int cmf_reenable(struct ccw_device *cdev)
{
cmbops->reset(cdev);
diff --git a/drivers/s390/cio/crw.c b/drivers/s390/cio/crw.c
index 4916dd0a7eb1..3285ce636c5c 100644
--- a/drivers/s390/cio/crw.c
+++ b/drivers/s390/cio/crw.c
@@ -77,9 +77,8 @@ repeat:
if (unlikely(chain > 1)) {
struct crw tmp_crw;
- printk(KERN_WARNING"%s: Code does not support more "
- "than two chained crws; please report to "
- "linux390@de.ibm.com!\n", __func__);
+ printk(KERN_WARNING "%s: Code does not support more than two chained crws\n",
+ __func__);
ccode = stcrw(&tmp_crw);
printk(KERN_WARNING"%s: crw reports slct=%d, oflw=%d, "
"chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 1d68db1a3d4e..be78a57f9bfd 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -309,7 +309,7 @@ static ssize_t type_show(struct device *dev, struct device_attribute *attr,
{
struct subchannel *sch = to_subchannel(dev);
- return sprintf(buf, "%01x\n", sch->st);
+ return sysfs_emit(buf, "%01x\n", sch->st);
}
static DEVICE_ATTR_RO(type);
@@ -319,7 +319,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
{
struct subchannel *sch = to_subchannel(dev);
- return sprintf(buf, "css:t%01X\n", sch->st);
+ return sysfs_emit(buf, "css:t%01X\n", sch->st);
}
static DEVICE_ATTR_RO(modalias);
@@ -345,7 +345,7 @@ static ssize_t driver_override_show(struct device *dev,
ssize_t len;
device_lock(dev);
- len = snprintf(buf, PAGE_SIZE, "%s\n", sch->driver_override);
+ len = sysfs_emit(buf, "%s\n", sch->driver_override);
device_unlock(dev);
return len;
}
@@ -380,11 +380,11 @@ static ssize_t chpids_show(struct device *dev,
for (chp = 0; chp < 8; chp++) {
mask = 0x80 >> chp;
if (ssd->path_mask & mask)
- ret += sprintf(buf + ret, "%02x ", ssd->chpid[chp].id);
+ ret += sysfs_emit_at(buf, ret, "%02x ", ssd->chpid[chp].id);
else
- ret += sprintf(buf + ret, "00 ");
+ ret += sysfs_emit_at(buf, ret, "00 ");
}
- ret += sprintf(buf + ret, "\n");
+ ret += sysfs_emit_at(buf, ret, "\n");
return ret;
}
static DEVICE_ATTR_RO(chpids);
@@ -396,8 +396,8 @@ static ssize_t pimpampom_show(struct device *dev,
struct subchannel *sch = to_subchannel(dev);
struct pmcw *pmcw = &sch->schib.pmcw;
- return sprintf(buf, "%02x %02x %02x\n",
- pmcw->pim, pmcw->pam, pmcw->pom);
+ return sysfs_emit(buf, "%02x %02x %02x\n",
+ pmcw->pim, pmcw->pam, pmcw->pom);
}
static DEVICE_ATTR_RO(pimpampom);
@@ -881,7 +881,7 @@ static ssize_t real_cssid_show(struct device *dev, struct device_attribute *a,
if (!css->id_valid)
return -EINVAL;
- return sprintf(buf, "%x\n", css->cssid);
+ return sysfs_emit(buf, "%x\n", css->cssid);
}
static DEVICE_ATTR_RO(real_cssid);
@@ -904,7 +904,7 @@ static ssize_t cm_enable_show(struct device *dev, struct device_attribute *a,
int ret;
mutex_lock(&css->mutex);
- ret = sprintf(buf, "%x\n", css->cm_enabled);
+ ret = sysfs_emit(buf, "%x\n", css->cm_enabled);
mutex_unlock(&css->mutex);
return ret;
}
@@ -1332,7 +1332,6 @@ static ssize_t cio_settle_write(struct file *file, const char __user *buf,
static const struct proc_ops cio_settle_proc_ops = {
.proc_open = nonseekable_open,
.proc_write = cio_settle_write,
- .proc_lseek = no_llseek,
};
static int __init cio_settle_init(void)
@@ -1354,10 +1353,10 @@ int sch_is_pseudo_sch(struct subchannel *sch)
return sch == to_css(sch->dev.parent)->pseudo_subchannel;
}
-static int css_bus_match(struct device *dev, struct device_driver *drv)
+static int css_bus_match(struct device *dev, const struct device_driver *drv)
{
struct subchannel *sch = to_subchannel(dev);
- struct css_driver *driver = to_cssdriver(drv);
+ const struct css_driver *driver = to_cssdriver(drv);
struct css_device_id *id;
/* When driver_override is set, only bind to the matching driver */
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index ea5550554297..a65a27dc520c 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -35,6 +35,15 @@
#define SNID_STATE3_SINGLE_PATH 0
/*
+ * Miscellaneous constants
+ */
+
+#define CSS_NUM_CUB_PAGES 2
+#define CSS_CUES_PER_PAGE 128
+#define CSS_NUM_ECUB_PAGES 4
+#define CSS_ECUES_PER_PAGE 64
+
+/*
* Conditions used to specify which subchannels need evaluation
*/
enum css_eval_cond {
@@ -94,7 +103,7 @@ struct css_driver {
int (*settle)(void);
};
-#define to_cssdriver(n) container_of(n, struct css_driver, drv)
+#define to_cssdriver(n) container_of_const(n, struct css_driver, drv)
extern int css_driver_register(struct css_driver *);
extern void css_driver_unregister(struct css_driver *);
@@ -122,8 +131,8 @@ struct channel_subsystem {
struct mutex mutex;
/* channel measurement related */
int cm_enabled;
- void *cub_addr1;
- void *cub_addr2;
+ void *cub[CSS_NUM_CUB_PAGES];
+ void *ecub[CSS_NUM_ECUB_PAGES];
/* for orphaned ccw devices */
struct subchannel *pseudo_subchannel;
};
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 920f550bc313..fb2c07cb4d3d 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -58,10 +58,10 @@ static const struct bus_type ccw_bus_type;
* subsystem driver and one channel system per machine, but
* we still use the abstraction. T.R. says it's a good idea. */
static int
-ccw_bus_match (struct device * dev, struct device_driver * drv)
+ccw_bus_match (struct device * dev, const struct device_driver * drv)
{
struct ccw_device *cdev = to_ccwdev(dev);
- struct ccw_driver *cdrv = to_ccwdrv(drv);
+ const struct ccw_driver *cdrv = to_ccwdrv(drv);
const struct ccw_device_id *ids = cdrv->ids, *found;
if (!ids)
@@ -201,10 +201,9 @@ devtype_show (struct device *dev, struct device_attribute *attr, char *buf)
struct ccw_device_id *id = &(cdev->id);
if (id->dev_type != 0)
- return sprintf(buf, "%04x/%02x\n",
- id->dev_type, id->dev_model);
+ return sysfs_emit(buf, "%04x/%02x\n", id->dev_type, id->dev_model);
else
- return sprintf(buf, "n/a\n");
+ return sysfs_emit(buf, "n/a\n");
}
static ssize_t
@@ -213,8 +212,7 @@ cutype_show (struct device *dev, struct device_attribute *attr, char *buf)
struct ccw_device *cdev = to_ccwdev(dev);
struct ccw_device_id *id = &(cdev->id);
- return sprintf(buf, "%04x/%02x\n",
- id->cu_type, id->cu_model);
+ return sysfs_emit(buf, "%04x/%02x\n", id->cu_type, id->cu_model);
}
static ssize_t
@@ -234,7 +232,7 @@ online_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct ccw_device *cdev = to_ccwdev(dev);
- return sprintf(buf, cdev->online ? "1\n" : "0\n");
+ return sysfs_emit(buf, cdev->online ? "1\n" : "0\n");
}
int ccw_device_is_orphan(struct ccw_device *cdev)
@@ -546,21 +544,21 @@ available_show (struct device *dev, struct device_attribute *attr, char *buf)
struct subchannel *sch;
if (ccw_device_is_orphan(cdev))
- return sprintf(buf, "no device\n");
+ return sysfs_emit(buf, "no device\n");
switch (cdev->private->state) {
case DEV_STATE_BOXED:
- return sprintf(buf, "boxed\n");
+ return sysfs_emit(buf, "boxed\n");
case DEV_STATE_DISCONNECTED:
case DEV_STATE_DISCONNECTED_SENSE_ID:
case DEV_STATE_NOT_OPER:
sch = to_subchannel(dev->parent);
if (!sch->lpm)
- return sprintf(buf, "no path\n");
+ return sysfs_emit(buf, "no path\n");
else
- return sprintf(buf, "no device\n");
+ return sysfs_emit(buf, "no device\n");
default:
/* All other states considered fine. */
- return sprintf(buf, "good\n");
+ return sysfs_emit(buf, "good\n");
}
}
@@ -587,7 +585,7 @@ static ssize_t vpm_show(struct device *dev, struct device_attribute *attr,
{
struct subchannel *sch = to_subchannel(dev);
- return sprintf(buf, "%02x\n", sch->vpm);
+ return sysfs_emit(buf, "%02x\n", sch->vpm);
}
static DEVICE_ATTR_RO(devtype);
@@ -1387,14 +1385,18 @@ enum io_sch_action {
IO_SCH_VERIFY,
IO_SCH_DISC,
IO_SCH_NOP,
+ IO_SCH_ORPH_CDEV,
};
static enum io_sch_action sch_get_action(struct subchannel *sch)
{
struct ccw_device *cdev;
+ int rc;
cdev = sch_get_cdev(sch);
- if (cio_update_schib(sch)) {
+ rc = cio_update_schib(sch);
+
+ if (rc == -ENODEV) {
/* Not operational. */
if (!cdev)
return IO_SCH_UNREG;
@@ -1402,6 +1404,16 @@ static enum io_sch_action sch_get_action(struct subchannel *sch)
return IO_SCH_UNREG;
return IO_SCH_ORPH_UNREG;
}
+
+ /* Avoid unregistering subchannels without working device. */
+ if (rc == -EACCES) {
+ if (!cdev)
+ return IO_SCH_NOP;
+ if (ccw_device_notify(cdev, CIO_GONE) != NOTIFY_OK)
+ return IO_SCH_UNREG_CDEV;
+ return IO_SCH_ORPH_CDEV;
+ }
+
/* Operational. */
if (!cdev)
return IO_SCH_ATTACH;
@@ -1471,6 +1483,7 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
rc = 0;
goto out_unlock;
case IO_SCH_ORPH_UNREG:
+ case IO_SCH_ORPH_CDEV:
case IO_SCH_ORPH_ATTACH:
ccw_device_set_disconnected(cdev);
break;
@@ -1502,6 +1515,7 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
/* Handle attached ccw device. */
switch (action) {
case IO_SCH_ORPH_UNREG:
+ case IO_SCH_ORPH_CDEV:
case IO_SCH_ORPH_ATTACH:
/* Move ccw device to orphanage. */
rc = ccw_device_move_to_orph(cdev);
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 42791fa0b80e..e1b1fbdabb1b 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -115,7 +115,7 @@ void
ccw_device_set_timeout(struct ccw_device *cdev, int expires)
{
if (expires == 0)
- del_timer(&cdev->private->timer);
+ timer_delete(&cdev->private->timer);
else
mod_timer(&cdev->private->timer, jiffies + expires);
}
diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c
index a512eac83485..d0f65d97dd4a 100644
--- a/drivers/s390/cio/device_id.c
+++ b/drivers/s390/cio/device_id.c
@@ -12,6 +12,7 @@
#include <linux/string.h>
#include <linux/types.h>
#include <linux/errno.h>
+#include <asm/machine.h>
#include <asm/ccwdev.h>
#include <asm/setup.h>
#include <asm/cio.h>
@@ -175,7 +176,7 @@ static void snsid_callback(struct ccw_device *cdev, void *data, int rc)
struct senseid *senseid = &cdev->private->dma_area->senseid;
int vm = 0;
- if (rc && MACHINE_IS_VM) {
+ if (rc && machine_is_vm()) {
/* Try diag 0x210 fallback on z/VM. */
snsid_init(cdev);
if (diag210_get_dev_info(cdev) == 0) {
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index acd6790dba4d..61c07b4a0fe8 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -445,7 +445,7 @@ struct ciw *ccw_device_get_ciw(struct ccw_device *cdev, __u32 ct)
return NULL;
for (ciw_cnt = 0; ciw_cnt < MAX_CIWS; ciw_cnt++)
if (cdev->private->dma_area->senseid.ciw[ciw_cnt].ct == ct)
- return cdev->private->dma_area->senseid.ciw + ciw_cnt;
+ return &cdev->private->dma_area->senseid.ciw[ciw_cnt];
return NULL;
}
diff --git a/drivers/s390/cio/eadm_sch.c b/drivers/s390/cio/eadm_sch.c
index 165de1552301..ac382355dc04 100644
--- a/drivers/s390/cio/eadm_sch.c
+++ b/drivers/s390/cio/eadm_sch.c
@@ -114,7 +114,7 @@ static void eadm_subchannel_set_timeout(struct subchannel *sch, int expires)
struct eadm_private *private = get_eadm_private(sch);
if (expires == 0)
- del_timer(&private->timer);
+ timer_delete(&private->timer);
else
mod_timer(&private->timer, jiffies + expires);
}
diff --git a/drivers/s390/cio/idset.c b/drivers/s390/cio/idset.c
index 45f9c0736be4..e5f28370a903 100644
--- a/drivers/s390/cio/idset.c
+++ b/drivers/s390/cio/idset.c
@@ -16,20 +16,21 @@ struct idset {
unsigned long bitmap[];
};
-static inline unsigned long bitmap_size(int num_ssid, int num_id)
+static inline unsigned long idset_bitmap_size(int num_ssid, int num_id)
{
- return BITS_TO_LONGS(num_ssid * num_id) * sizeof(unsigned long);
+ return bitmap_size(size_mul(num_ssid, num_id));
}
static struct idset *idset_new(int num_ssid, int num_id)
{
struct idset *set;
- set = vmalloc(sizeof(struct idset) + bitmap_size(num_ssid, num_id));
+ set = vmalloc(sizeof(struct idset) +
+ idset_bitmap_size(num_ssid, num_id));
if (set) {
set->num_ssid = num_ssid;
set->num_id = num_id;
- memset(set->bitmap, 0, bitmap_size(num_ssid, num_id));
+ memset(set->bitmap, 0, idset_bitmap_size(num_ssid, num_id));
}
return set;
}
@@ -41,7 +42,8 @@ void idset_free(struct idset *set)
void idset_fill(struct idset *set)
{
- memset(set->bitmap, 0xff, bitmap_size(set->num_ssid, set->num_id));
+ memset(set->bitmap, 0xff,
+ idset_bitmap_size(set->num_ssid, set->num_id));
}
static inline void idset_add(struct idset *set, int ssid, int id)
diff --git a/drivers/s390/cio/ioasm.c b/drivers/s390/cio/ioasm.c
index acf1edd36549..a540045b64a6 100644
--- a/drivers/s390/cio/ioasm.c
+++ b/drivers/s390/cio/ioasm.c
@@ -8,6 +8,7 @@
#include <asm/asm-extable.h>
#include <asm/chpid.h>
#include <asm/schid.h>
+#include <asm/asm.h>
#include <asm/crw.h>
#include "ioasm.h"
@@ -18,19 +19,20 @@
static inline int __stsch(struct subchannel_id schid, struct schib *addr)
{
unsigned long r1 = *(unsigned int *)&schid;
- int ccode = -EIO;
+ int ccode, exception;
- asm volatile(
+ exception = 1;
+ asm_inline volatile(
" lgr 1,%[r1]\n"
" stsch %[addr]\n"
- "0: ipm %[cc]\n"
- " srl %[cc],28\n"
+ "0: lhi %[exc],0\n"
"1:\n"
+ CC_IPM(cc)
EX_TABLE(0b, 1b)
- : [cc] "+&d" (ccode), [addr] "=Q" (*addr)
+ : CC_OUT(cc, ccode), [addr] "=Q" (*addr), [exc] "+d" (exception)
: [r1] "d" (r1)
- : "cc", "1");
- return ccode;
+ : CC_CLOBBER_LIST("1"));
+ return exception ? -EIO : CC_TRANSFORM(ccode);
}
int stsch(struct subchannel_id schid, struct schib *addr)
@@ -47,19 +49,20 @@ EXPORT_SYMBOL(stsch);
static inline int __msch(struct subchannel_id schid, struct schib *addr)
{
unsigned long r1 = *(unsigned int *)&schid;
- int ccode = -EIO;
+ int ccode, exception;
- asm volatile(
+ exception = 1;
+ asm_inline volatile(
" lgr 1,%[r1]\n"
" msch %[addr]\n"
- "0: ipm %[cc]\n"
- " srl %[cc],28\n"
+ "0: lhi %[exc],0\n"
"1:\n"
+ CC_IPM(cc)
EX_TABLE(0b, 1b)
- : [cc] "+&d" (ccode)
+ : CC_OUT(cc, ccode), [exc] "+d" (exception)
: [r1] "d" (r1), [addr] "Q" (*addr)
- : "cc", "1");
- return ccode;
+ : CC_CLOBBER_LIST("1"));
+ return exception ? -EIO : CC_TRANSFORM(ccode);
}
int msch(struct subchannel_id schid, struct schib *addr)
@@ -80,12 +83,11 @@ static inline int __tsch(struct subchannel_id schid, struct irb *addr)
asm volatile(
" lgr 1,%[r1]\n"
" tsch %[addr]\n"
- " ipm %[cc]\n"
- " srl %[cc],28"
- : [cc] "=&d" (ccode), [addr] "=Q" (*addr)
+ CC_IPM(cc)
+ : CC_OUT(cc, ccode), [addr] "=Q" (*addr)
: [r1] "d" (r1)
- : "cc", "1");
- return ccode;
+ : CC_CLOBBER_LIST("1"));
+ return CC_TRANSFORM(ccode);
}
int tsch(struct subchannel_id schid, struct irb *addr)
@@ -101,19 +103,20 @@ int tsch(struct subchannel_id schid, struct irb *addr)
static inline int __ssch(struct subchannel_id schid, union orb *addr)
{
unsigned long r1 = *(unsigned int *)&schid;
- int ccode = -EIO;
+ int ccode, exception;
- asm volatile(
+ exception = 1;
+ asm_inline volatile(
" lgr 1,%[r1]\n"
" ssch %[addr]\n"
- "0: ipm %[cc]\n"
- " srl %[cc],28\n"
+ "0: lhi %[exc],0\n"
"1:\n"
+ CC_IPM(cc)
EX_TABLE(0b, 1b)
- : [cc] "+&d" (ccode)
+ : CC_OUT(cc, ccode), [exc] "+d" (exception)
: [r1] "d" (r1), [addr] "Q" (*addr)
- : "cc", "memory", "1");
- return ccode;
+ : CC_CLOBBER_LIST("memory", "1"));
+ return CC_TRANSFORM(ccode);
}
int ssch(struct subchannel_id schid, union orb *addr)
@@ -135,12 +138,11 @@ static inline int __csch(struct subchannel_id schid)
asm volatile(
" lgr 1,%[r1]\n"
" csch\n"
- " ipm %[cc]\n"
- " srl %[cc],28\n"
- : [cc] "=&d" (ccode)
+ CC_IPM(cc)
+ : CC_OUT(cc, ccode)
: [r1] "d" (r1)
- : "cc", "1");
- return ccode;
+ : CC_CLOBBER_LIST("1"));
+ return CC_TRANSFORM(ccode);
}
int csch(struct subchannel_id schid)
@@ -160,11 +162,11 @@ int tpi(struct tpi_info *addr)
asm volatile(
" tpi %[addr]\n"
- " ipm %[cc]\n"
- " srl %[cc],28"
- : [cc] "=&d" (ccode), [addr] "=Q" (*addr)
+ CC_IPM(cc)
+ : CC_OUT(cc, ccode), [addr] "=Q" (*addr)
:
- : "cc");
+ : CC_CLOBBER);
+ ccode = CC_TRANSFORM(ccode);
trace_s390_cio_tpi(addr, ccode);
return ccode;
@@ -173,17 +175,19 @@ int tpi(struct tpi_info *addr)
int chsc(void *chsc_area)
{
typedef struct { char _[4096]; } addr_type;
- int cc = -EIO;
+ int cc, exception;
- asm volatile(
+ exception = 1;
+ asm_inline volatile(
" .insn rre,0xb25f0000,%[chsc_area],0\n"
- "0: ipm %[cc]\n"
- " srl %[cc],28\n"
+ "0: lhi %[exc],0\n"
"1:\n"
+ CC_IPM(cc)
EX_TABLE(0b, 1b)
- : [cc] "+&d" (cc), "+m" (*(addr_type *)chsc_area)
+ : CC_OUT(cc, cc), "+m" (*(addr_type *)chsc_area), [exc] "+d" (exception)
: [chsc_area] "d" (chsc_area)
- : "cc");
+ : CC_CLOBBER);
+ cc = exception ? -EIO : CC_TRANSFORM(cc);
trace_s390_cio_chsc(chsc_area, cc);
return cc;
@@ -198,12 +202,11 @@ static inline int __rsch(struct subchannel_id schid)
asm volatile(
" lgr 1,%[r1]\n"
" rsch\n"
- " ipm %[cc]\n"
- " srl %[cc],28\n"
- : [cc] "=&d" (ccode)
+ CC_IPM(cc)
+ : CC_OUT(cc, ccode)
: [r1] "d" (r1)
- : "cc", "memory", "1");
- return ccode;
+ : CC_CLOBBER_LIST("memory", "1"));
+ return CC_TRANSFORM(ccode);
}
int rsch(struct subchannel_id schid)
@@ -224,12 +227,11 @@ static inline int __hsch(struct subchannel_id schid)
asm volatile(
" lgr 1,%[r1]\n"
" hsch\n"
- " ipm %[cc]\n"
- " srl %[cc],28\n"
- : [cc] "=&d" (ccode)
+ CC_IPM(cc)
+ : CC_OUT(cc, ccode)
: [r1] "d" (r1)
- : "cc", "1");
- return ccode;
+ : CC_CLOBBER_LIST("1"));
+ return CC_TRANSFORM(ccode);
}
int hsch(struct subchannel_id schid)
@@ -256,7 +258,7 @@ static inline int __xsch(struct subchannel_id schid)
: [cc] "=&d" (ccode)
: [r1] "d" (r1)
: "cc", "1");
- return ccode;
+ return CC_TRANSFORM(ccode);
}
int xsch(struct subchannel_id schid)
@@ -275,12 +277,11 @@ static inline int __stcrw(struct crw *crw)
asm volatile(
" stcrw %[crw]\n"
- " ipm %[cc]\n"
- " srl %[cc],28\n"
- : [cc] "=&d" (ccode), [crw] "=Q" (*crw)
+ CC_IPM(cc)
+ : CC_OUT(cc, ccode), [crw] "=Q" (*crw)
:
- : "cc");
- return ccode;
+ : CC_CLOBBER);
+ return CC_TRANSFORM(ccode);
}
static inline int _stcrw(struct crw *crw)
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index 641f0dbb65a9..4bd4c00c9c0c 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -210,11 +210,10 @@ struct qdio_q {
qdio_handler_t (*handler);
struct qdio_irq *irq_ptr;
+
+ /* memory page (PAGE_SIZE) used to place slib and sl on */
+ void *sl_page;
struct sl *sl;
- /*
- * A page is allocated under this pointer and used for slib and sl.
- * slib is 2048 bytes big and sl points to offset PAGE_SIZE / 2.
- */
struct slib *slib;
} __attribute__ ((aligned(256)));
@@ -266,7 +265,7 @@ struct qdio_irq {
#define is_thinint_irq(irq) \
(irq->qib.qfmt == QDIO_IQDIO_QFMT || \
- css_general_characteristics.aif_osa)
+ css_general_characteristics.aif_qdio)
#define qperf(__qdev, __attr) ((__qdev)->perf_stat.(__attr))
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index a1cb39f4b7a2..07e82816b77a 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -17,6 +17,7 @@
#include <linux/atomic.h>
#include <asm/debug.h>
#include <asm/qdio.h>
+#include <asm/asm.h>
#include <asm/ipl.h>
#include "cio.h"
@@ -42,13 +43,12 @@ static inline int do_siga_sync(unsigned long schid,
" lgr 2,%[out]\n"
" lgr 3,%[in]\n"
" siga 0\n"
- " ipm %[cc]\n"
- " srl %[cc],28\n"
- : [cc] "=&d" (cc)
+ CC_IPM(cc)
+ : CC_OUT(cc, cc)
: [fc] "d" (fc), [schid] "d" (schid),
[out] "d" (out_mask), [in] "d" (in_mask)
- : "cc", "0", "1", "2", "3");
- return cc;
+ : CC_CLOBBER_LIST("0", "1", "2", "3"));
+ return CC_TRANSFORM(cc);
}
static inline int do_siga_input(unsigned long schid, unsigned long mask,
@@ -61,12 +61,11 @@ static inline int do_siga_input(unsigned long schid, unsigned long mask,
" lgr 1,%[schid]\n"
" lgr 2,%[mask]\n"
" siga 0\n"
- " ipm %[cc]\n"
- " srl %[cc],28\n"
- : [cc] "=&d" (cc)
+ CC_IPM(cc)
+ : CC_OUT(cc, cc)
: [fc] "d" (fc), [schid] "d" (schid), [mask] "d" (mask)
- : "cc", "0", "1", "2");
- return cc;
+ : CC_CLOBBER_LIST("0", "1", "2"));
+ return CC_TRANSFORM(cc);
}
/**
@@ -93,13 +92,12 @@ static inline int do_siga_output(unsigned long schid, unsigned long mask,
" lgr 3,%[aob]\n"
" siga 0\n"
" lgr %[fc],0\n"
- " ipm %[cc]\n"
- " srl %[cc],28\n"
- : [cc] "=&d" (cc), [fc] "+&d" (fc)
+ CC_IPM(cc)
+ : CC_OUT(cc, cc), [fc] "+&d" (fc)
: [schid] "d" (schid), [mask] "d" (mask), [aob] "d" (aob)
- : "cc", "0", "1", "2", "3");
+ : CC_CLOBBER_LIST("0", "1", "2", "3"));
*bb = fc >> 31;
- return cc;
+ return CC_TRANSFORM(cc);
}
/**
@@ -695,7 +693,7 @@ static void qdio_int_handler_pci(struct qdio_irq *irq_ptr)
return;
qdio_deliver_irq(irq_ptr);
- irq_ptr->last_data_irq_time = S390_lowcore.int_clock;
+ irq_ptr->last_data_irq_time = get_lowcore()->int_clock;
}
static void qdio_handle_activate_check(struct qdio_irq *irq_ptr,
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
index 99c0fd23022d..ea09aadaae4e 100644
--- a/drivers/s390/cio/qdio_setup.c
+++ b/drivers/s390/cio/qdio_setup.c
@@ -83,7 +83,7 @@ static void __qdio_free_queues(struct qdio_q **queues, unsigned int count)
for (i = 0; i < count; i++) {
q = queues[i];
- free_page((unsigned long) q->slib);
+ free_page((unsigned long)q->sl_page);
kmem_cache_free(qdio_q_cache, q);
}
}
@@ -109,12 +109,16 @@ static int __qdio_allocate_qs(struct qdio_q **irq_ptr_qs, int nr_queues)
return -ENOMEM;
}
- q->slib = (struct slib *) __get_free_page(GFP_KERNEL);
- if (!q->slib) {
+ q->sl_page = (void *)__get_free_page(GFP_KERNEL);
+ if (!q->sl_page) {
kmem_cache_free(qdio_q_cache, q);
__qdio_free_queues(irq_ptr_qs, i);
return -ENOMEM;
}
+ q->slib = q->sl_page;
+ /* As per architecture: SLIB is 2K bytes long, and SL 1K. */
+ q->sl = (struct sl *)(q->slib + 1);
+
irq_ptr_qs[i] = q;
}
return 0;
@@ -142,11 +146,15 @@ int qdio_allocate_qs(struct qdio_irq *irq_ptr, int nr_input_qs, int nr_output_qs
static void setup_queues_misc(struct qdio_q *q, struct qdio_irq *irq_ptr,
qdio_handler_t *handler, int i)
{
- struct slib *slib = q->slib;
+ struct slib *const slib = q->slib;
+ void *const sl_page = q->sl_page;
+ struct sl *const sl = q->sl;
/* queue must be cleared for qdio_establish */
memset(q, 0, sizeof(*q));
- memset(slib, 0, PAGE_SIZE);
+ memset(sl_page, 0, PAGE_SIZE);
+ q->sl_page = sl_page;
+ q->sl = sl;
q->slib = slib;
q->irq_ptr = irq_ptr;
q->mask = 1 << (31 - i);
@@ -161,7 +169,6 @@ static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr,
int j;
DBF_HEX(&q, sizeof(void *));
- q->sl = (struct sl *)((char *)q->slib + PAGE_SIZE / 2);
/* fill in sbal */
for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++)
@@ -423,7 +430,7 @@ int __init qdio_setup_init(void)
/* Check for OSA/FCP thin interrupts (bit 67). */
DBF_EVENT("thinint:%1d",
- (css_general_characteristics.aif_osa) ? 1 : 0);
+ (css_general_characteristics.aif_qdio) ? 1 : 0);
/* Check for QEBSM support in general (bit 58). */
DBF_EVENT("cssQEBSM:%1d", css_general_characteristics.qebsm);
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index ccd4ed93bd92..f931954910c5 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -99,7 +99,7 @@ static inline u32 clear_shared_ind(void)
static void tiqdio_thinint_handler(struct airq_struct *airq,
struct tpi_info *tpi_info)
{
- u64 irq_time = S390_lowcore.int_clock;
+ u64 irq_time = get_lowcore()->int_clock;
u32 si_used = clear_shared_ind();
struct qdio_irq *irq;
diff --git a/drivers/s390/cio/scm.c b/drivers/s390/cio/scm.c
index c7894d61306d..a0825e372d42 100644
--- a/drivers/s390/cio/scm.c
+++ b/drivers/s390/cio/scm.c
@@ -91,7 +91,7 @@ static ssize_t show_##name(struct device *dev, \
int ret; \
\
device_lock(dev); \
- ret = sprintf(buf, "%u\n", scmdev->attrs.name); \
+ ret = sysfs_emit(buf, "%u\n", scmdev->attrs.name); \
device_unlock(dev); \
\
return ret; \
diff --git a/drivers/s390/cio/trace.h b/drivers/s390/cio/trace.h
index 86993de25345..6bca5315ee2a 100644
--- a/drivers/s390/cio/trace.h
+++ b/drivers/s390/cio/trace.h
@@ -50,7 +50,7 @@ DECLARE_EVENT_CLASS(s390_class_schib,
__entry->devno = schib->pmcw.dev;
__entry->schib = *schib;
__entry->pmcw_ena = schib->pmcw.ena;
- __entry->pmcw_st = schib->pmcw.ena;
+ __entry->pmcw_st = schib->pmcw.st;
__entry->pmcw_dnv = schib->pmcw.dnv;
__entry->pmcw_dev = schib->pmcw.dev;
__entry->pmcw_lpm = schib->pmcw.lpm;
@@ -169,7 +169,7 @@ TRACE_EVENT(s390_cio_tpi,
else if (addr)
__entry->tpi_info = *addr;
else
- __entry->tpi_info = S390_lowcore.tpi_info;
+ __entry->tpi_info = get_lowcore()->tpi_info;
__entry->cssid = __entry->tpi_info.schid.cssid;
__entry->ssid = __entry->tpi_info.schid.ssid;
__entry->schno = __entry->tpi_info.schid.sch_no;
diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c
index 6e5c508b1e07..5f6e10225627 100644
--- a/drivers/s390/cio/vfio_ccw_cp.c
+++ b/drivers/s390/cio/vfio_ccw_cp.c
@@ -490,13 +490,14 @@ static int ccwchain_fetch_tic(struct ccw1 *ccw,
struct channel_program *cp)
{
struct ccwchain *iter;
- u32 cda, ccw_head;
+ u32 offset, ccw_head;
list_for_each_entry(iter, &cp->ccwchain_list, next) {
ccw_head = iter->ch_iova;
if (is_cpa_within_range(ccw->cda, ccw_head, iter->ch_len)) {
- cda = (u64)iter->ch_ccw + dma32_to_u32(ccw->cda) - ccw_head;
- ccw->cda = u32_to_dma32(cda);
+ /* Calculate offset of TIC target */
+ offset = dma32_to_u32(ccw->cda) - ccw_head;
+ ccw->cda = virt_to_dma32((void *)iter->ch_ccw + offset);
return 0;
}
}
@@ -914,7 +915,7 @@ void cp_update_scsw(struct channel_program *cp, union scsw *scsw)
* in the ioctl directly. Path status changes etc.
*/
list_for_each_entry(chain, &cp->ccwchain_list, next) {
- ccw_head = (u32)(u64)chain->ch_ccw;
+ ccw_head = dma32_to_u32(virt_to_dma32(chain->ch_ccw));
/*
* On successful execution, cpa points just beyond the end
* of the chain.
diff --git a/drivers/s390/cio/vfio_ccw_drv.c b/drivers/s390/cio/vfio_ccw_drv.c
index 8ad49030a7bf..6ff5c9cfb7ed 100644
--- a/drivers/s390/cio/vfio_ccw_drv.c
+++ b/drivers/s390/cio/vfio_ccw_drv.c
@@ -171,7 +171,7 @@ static int vfio_ccw_sch_probe(struct subchannel *sch)
return -ENODEV;
}
- parent = kzalloc(struct_size(parent, mdev_types, 1), GFP_KERNEL);
+ parent = kzalloc(sizeof(*parent), GFP_KERNEL);
if (!parent)
return -ENOMEM;
@@ -186,10 +186,10 @@ static int vfio_ccw_sch_probe(struct subchannel *sch)
parent->mdev_type.sysfs_name = "io";
parent->mdev_type.pretty_name = "I/O subchannel (Non-QDIO)";
- parent->mdev_types[0] = &parent->mdev_type;
+ parent->mdev_types = &parent->mdev_type;
ret = mdev_register_parent(&parent->parent, &sch->dev,
&vfio_ccw_mdev_driver,
- parent->mdev_types, 1);
+ &parent->mdev_types, 1);
if (ret)
goto out_unreg;
@@ -488,4 +488,5 @@ static void __exit vfio_ccw_sch_exit(void)
module_init(vfio_ccw_sch_init);
module_exit(vfio_ccw_sch_exit);
+MODULE_DESCRIPTION("VFIO based Subchannel device driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/s390/cio/vfio_ccw_private.h b/drivers/s390/cio/vfio_ccw_private.h
index b62bbc5c6376..0501d4bbcdbd 100644
--- a/drivers/s390/cio/vfio_ccw_private.h
+++ b/drivers/s390/cio/vfio_ccw_private.h
@@ -79,7 +79,7 @@ struct vfio_ccw_parent {
struct mdev_parent parent;
struct mdev_type mdev_type;
- struct mdev_type *mdev_types[];
+ struct mdev_type *mdev_types;
};
/**