aboutsummaryrefslogtreecommitdiff
path: root/drivers/s390/cio
diff options
context:
space:
mode:
authorLinus Torvalds2017-02-22 10:20:04 -0800
committerLinus Torvalds2017-02-22 10:20:04 -0800
commitff47d8c05019d6e7753cef270d6399cb5a33be57 (patch)
tree78eec48c53554902062f9a06b57a700d7671330e /drivers/s390/cio
parent3051bf36c25d5153051704291782f8d44e744d36 (diff)
parentd24b98e3a9c66b16ed029e1b2bcdf3c90e9d82d9 (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
Pull s390 updates from Martin Schwidefsky: - New entropy generation for the pseudo random number generator. - Early boot printk output via sclp to help debug crashes on boot. This needs to be enabled with a kernel parameter. - Add proper no-execute support with a bit in the page table entry. - Bug fixes and cleanups. * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (65 commits) s390/syscall: fix single stepped system calls s390/zcrypt: make ap_bus explicitly non-modular s390/zcrypt: Removed unneeded debug feature directory creation. s390: add missing "do {} while (0)" loop constructs to multiline macros s390/mm: add cond_resched call to kernel page table dumper s390: get rid of MACHINE_HAS_PFMF and MACHINE_HAS_HPAGE s390/mm: make memory_block_size_bytes available for !MEMORY_HOTPLUG s390: replace ACCESS_ONCE with READ_ONCE s390: Audit and remove any remaining unnecessary uses of module.h s390: mm: Audit and remove any unnecessary uses of module.h s390: kernel: Audit and remove any unnecessary uses of module.h s390/kdump: Use "LINUX" ELF note name instead of "CORE" s390: add no-execute support s390: report new vector facilities s390: use correct input data address for setup_randomness s390/sclp: get rid of common response code handling s390/sclp: don't add new lines to each printed string s390/sclp: make early sclp code readable s390/sclp: disable early sclp code as soon as the base sclp driver is active s390/sclp: move early printk code to drivers ...
Diffstat (limited to 'drivers/s390/cio')
-rw-r--r--drivers/s390/cio/chp.c13
-rw-r--r--drivers/s390/cio/chp.h2
-rw-r--r--drivers/s390/cio/chsc.c48
-rw-r--r--drivers/s390/cio/chsc.h2
-rw-r--r--drivers/s390/cio/cmf.c10
-rw-r--r--drivers/s390/cio/css.c209
-rw-r--r--drivers/s390/cio/css.h13
-rw-r--r--drivers/s390/cio/qdio_main.c5
-rw-r--r--drivers/s390/cio/qdio_thinint.c19
9 files changed, 190 insertions, 131 deletions
diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c
index 876c7e6e3a99..7e0d4f724dda 100644
--- a/drivers/s390/cio/chp.c
+++ b/drivers/s390/cio/chp.c
@@ -444,6 +444,7 @@ int chp_update_desc(struct channel_path *chp)
*/
int chp_new(struct chp_id chpid)
{
+ struct channel_subsystem *css = css_by_id(chpid.cssid);
struct channel_path *chp;
int ret;
@@ -456,7 +457,7 @@ int chp_new(struct chp_id chpid)
/* fill in status, etc. */
chp->chpid = chpid;
chp->state = 1;
- chp->dev.parent = &channel_subsystems[chpid.cssid]->device;
+ chp->dev.parent = &css->device;
chp->dev.groups = chp_attr_groups;
chp->dev.release = chp_release;
mutex_init(&chp->lock);
@@ -479,17 +480,17 @@ int chp_new(struct chp_id chpid)
put_device(&chp->dev);
goto out;
}
- mutex_lock(&channel_subsystems[chpid.cssid]->mutex);
- if (channel_subsystems[chpid.cssid]->cm_enabled) {
+ mutex_lock(&css->mutex);
+ if (css->cm_enabled) {
ret = chp_add_cmg_attr(chp);
if (ret) {
device_unregister(&chp->dev);
- mutex_unlock(&channel_subsystems[chpid.cssid]->mutex);
+ mutex_unlock(&css->mutex);
goto out;
}
}
- channel_subsystems[chpid.cssid]->chps[chpid.id] = chp;
- mutex_unlock(&channel_subsystems[chpid.cssid]->mutex);
+ css->chps[chpid.id] = chp;
+ mutex_unlock(&css->mutex);
goto out;
out_free:
kfree(chp);
diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h
index bb5a68226cda..0d8437b7ea72 100644
--- a/drivers/s390/cio/chp.h
+++ b/drivers/s390/cio/chp.h
@@ -54,7 +54,7 @@ struct channel_path {
/* 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];
+ return css_by_id(chpid.cssid)->chps[chpid.id];
}
int chp_get_status(struct chp_id chpid);
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 11674698b36d..7b0b295b2313 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -1131,6 +1131,52 @@ int chsc_enable_facility(int operation_code)
return ret;
}
+int __init chsc_get_cssid(int idx)
+{
+ struct {
+ struct chsc_header request;
+ u8 atype;
+ u32 : 24;
+ u32 reserved1[6];
+ struct chsc_header response;
+ u32 reserved2[3];
+ struct {
+ u8 cssid;
+ u32 : 24;
+ } list[0];
+ } __packed *sdcal_area;
+ int ret;
+
+ spin_lock_irq(&chsc_page_lock);
+ memset(chsc_page, 0, PAGE_SIZE);
+ sdcal_area = chsc_page;
+ sdcal_area->request.length = 0x0020;
+ sdcal_area->request.code = 0x0034;
+ sdcal_area->atype = 4;
+
+ ret = chsc(sdcal_area);
+ if (ret) {
+ ret = (ret == 3) ? -ENODEV : -EBUSY;
+ goto exit;
+ }
+
+ ret = chsc_error_from_response(sdcal_area->response.code);
+ if (ret) {
+ CIO_CRW_EVENT(2, "chsc: sdcal failed (rc=%04x)\n",
+ sdcal_area->response.code);
+ goto exit;
+ }
+
+ if ((addr_t) &sdcal_area->list[idx] <
+ (addr_t) &sdcal_area->response + sdcal_area->response.length)
+ ret = sdcal_area->list[idx].cssid;
+ else
+ ret = -ENODEV;
+exit:
+ spin_unlock_irq(&chsc_page_lock);
+ return ret;
+}
+
struct css_general_char css_general_characteristics;
struct css_chsc_char css_chsc_characteristics;
@@ -1216,7 +1262,7 @@ int chsc_sstpi(void *page, void *result, size_t size)
struct chsc_header request;
unsigned int rsvd0[3];
struct chsc_header response;
- char data[size];
+ char data[];
} __attribute__ ((packed)) *rr;
int rc;
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index 67c87b6e63ec..321a3f765810 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -242,6 +242,8 @@ int chsc_pnso_brinfo(struct subchannel_id schid,
struct chsc_brinfo_resume_token resume_token,
int cnc);
+int __init chsc_get_cssid(int idx);
+
#ifdef CONFIG_SCM_BUS
int scm_update_information(void);
int scm_process_availability_information(void);
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index 6b6386e9a500..220491d27ef4 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -1085,15 +1085,9 @@ static ssize_t cmb_show_avg_utilization(struct device *dev,
data.function_pending_time +
data.device_disconnect_time;
- /* shift to avoid long long division */
- while (-1ul < (data.elapsed_time | utilization)) {
- utilization >>= 8;
- data.elapsed_time >>= 8;
- }
-
/* calculate value in 0.1 percent units */
- t = (unsigned long) data.elapsed_time / 1000;
- u = (unsigned long) utilization / t;
+ t = data.elapsed_time / 1000;
+ u = utilization / t;
return sprintf(buf, "%02ld.%01ld%%\n", u/ 10, u - (u/ 10) * 10);
}
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index bc099b61394d..e2aa944eb566 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -36,7 +36,8 @@
int css_init_done = 0;
int max_ssid;
-struct channel_subsystem *channel_subsystems[__MAX_CSSID + 1];
+#define MAX_CSS_IDX 0
+struct channel_subsystem *channel_subsystems[MAX_CSS_IDX + 1];
static struct bus_type css_bus_type;
int
@@ -702,7 +703,8 @@ css_generate_pgid(struct channel_subsystem *css, u32 tod_high)
if (css_general_characteristics.mcss) {
css->global_pgid.pgid_high.ext_cssid.version = 0x80;
- css->global_pgid.pgid_high.ext_cssid.cssid = css->cssid;
+ css->global_pgid.pgid_high.ext_cssid.cssid =
+ (css->cssid < 0) ? 0 : css->cssid;
} else {
css->global_pgid.pgid_high.cpu_addr = stap();
}
@@ -712,43 +714,44 @@ css_generate_pgid(struct channel_subsystem *css, u32 tod_high)
css->global_pgid.tod_high = tod_high;
}
-static void
-channel_subsystem_release(struct device *dev)
+static void channel_subsystem_release(struct device *dev)
{
- struct channel_subsystem *css;
+ struct channel_subsystem *css = to_css(dev);
- css = to_css(dev);
mutex_destroy(&css->mutex);
- if (css->pseudo_subchannel) {
- /* Implies that it has been generated but never registered. */
- css_subchannel_release(&css->pseudo_subchannel->dev);
- css->pseudo_subchannel = NULL;
- }
kfree(css);
}
-static ssize_t
-css_cm_enable_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t real_cssid_show(struct device *dev, struct device_attribute *a,
+ char *buf)
+{
+ struct channel_subsystem *css = to_css(dev);
+
+ if (css->cssid < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%x\n", css->cssid);
+}
+static DEVICE_ATTR_RO(real_cssid);
+
+static ssize_t cm_enable_show(struct device *dev, struct device_attribute *a,
+ char *buf)
{
struct channel_subsystem *css = to_css(dev);
int ret;
- if (!css)
- return 0;
mutex_lock(&css->mutex);
ret = sprintf(buf, "%x\n", css->cm_enabled);
mutex_unlock(&css->mutex);
return ret;
}
-static ssize_t
-css_cm_enable_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t cm_enable_store(struct device *dev, struct device_attribute *a,
+ const char *buf, size_t count)
{
struct channel_subsystem *css = to_css(dev);
- int ret;
unsigned long val;
+ int ret;
ret = kstrtoul(buf, 16, &val);
if (ret)
@@ -767,51 +770,104 @@ css_cm_enable_store(struct device *dev, struct device_attribute *attr,
mutex_unlock(&css->mutex);
return ret < 0 ? ret : count;
}
+static DEVICE_ATTR_RW(cm_enable);
+
+static umode_t cm_enable_mode(struct kobject *kobj, struct attribute *attr,
+ int index)
+{
+ return css_chsc_characteristics.secm ? attr->mode : 0;
+}
+
+static struct attribute *cssdev_attrs[] = {
+ &dev_attr_real_cssid.attr,
+ NULL,
+};
+
+static struct attribute_group cssdev_attr_group = {
+ .attrs = cssdev_attrs,
+};
+
+static struct attribute *cssdev_cm_attrs[] = {
+ &dev_attr_cm_enable.attr,
+ NULL,
+};
+
+static struct attribute_group cssdev_cm_attr_group = {
+ .attrs = cssdev_cm_attrs,
+ .is_visible = cm_enable_mode,
+};
-static DEVICE_ATTR(cm_enable, 0644, css_cm_enable_show, css_cm_enable_store);
+static const struct attribute_group *cssdev_attr_groups[] = {
+ &cssdev_attr_group,
+ &cssdev_cm_attr_group,
+ NULL,
+};
static int __init setup_css(int nr)
{
- u32 tod_high;
- int ret;
struct channel_subsystem *css;
+ int ret;
- css = channel_subsystems[nr];
- memset(css, 0, sizeof(struct channel_subsystem));
- css->pseudo_subchannel =
- kzalloc(sizeof(*css->pseudo_subchannel), GFP_KERNEL);
- if (!css->pseudo_subchannel)
+ css = kzalloc(sizeof(*css), GFP_KERNEL);
+ if (!css)
return -ENOMEM;
+
+ channel_subsystems[nr] = css;
+ dev_set_name(&css->device, "css%x", nr);
+ css->device.groups = cssdev_attr_groups;
+ css->device.release = channel_subsystem_release;
+
+ mutex_init(&css->mutex);
+ css->cssid = chsc_get_cssid(nr);
+ css_generate_pgid(css, (u32) (get_tod_clock() >> 32));
+
+ ret = device_register(&css->device);
+ if (ret) {
+ put_device(&css->device);
+ goto out_err;
+ }
+
+ css->pseudo_subchannel = kzalloc(sizeof(*css->pseudo_subchannel),
+ GFP_KERNEL);
+ if (!css->pseudo_subchannel) {
+ device_unregister(&css->device);
+ ret = -ENOMEM;
+ goto out_err;
+ }
+
css->pseudo_subchannel->dev.parent = &css->device;
css->pseudo_subchannel->dev.release = css_subchannel_release;
- dev_set_name(&css->pseudo_subchannel->dev, "defunct");
mutex_init(&css->pseudo_subchannel->reg_mutex);
ret = css_sch_create_locks(css->pseudo_subchannel);
if (ret) {
kfree(css->pseudo_subchannel);
- return ret;
+ device_unregister(&css->device);
+ goto out_err;
}
- mutex_init(&css->mutex);
- css->valid = 1;
- css->cssid = nr;
- dev_set_name(&css->device, "css%x", nr);
- css->device.release = channel_subsystem_release;
- tod_high = (u32) (get_tod_clock() >> 32);
- css_generate_pgid(css, tod_high);
- return 0;
+
+ dev_set_name(&css->pseudo_subchannel->dev, "defunct");
+ ret = device_register(&css->pseudo_subchannel->dev);
+ if (ret) {
+ put_device(&css->pseudo_subchannel->dev);
+ device_unregister(&css->device);
+ goto out_err;
+ }
+
+ return ret;
+out_err:
+ channel_subsystems[nr] = NULL;
+ return ret;
}
static int css_reboot_event(struct notifier_block *this,
unsigned long event,
void *ptr)
{
- int ret, i;
+ struct channel_subsystem *css;
+ int ret;
ret = NOTIFY_DONE;
- for (i = 0; i <= __MAX_CSSID; i++) {
- struct channel_subsystem *css;
-
- css = channel_subsystems[i];
+ for_each_css(css) {
mutex_lock(&css->mutex);
if (css->cm_enabled)
if (chsc_secm(css, 0))
@@ -835,16 +891,14 @@ static struct notifier_block css_reboot_notifier = {
static int css_power_event(struct notifier_block *this, unsigned long event,
void *ptr)
{
- int ret, i;
+ struct channel_subsystem *css;
+ int ret;
switch (event) {
case PM_HIBERNATION_PREPARE:
case PM_SUSPEND_PREPARE:
ret = NOTIFY_DONE;
- for (i = 0; i <= __MAX_CSSID; i++) {
- struct channel_subsystem *css;
-
- css = channel_subsystems[i];
+ for_each_css(css) {
mutex_lock(&css->mutex);
if (!css->cm_enabled) {
mutex_unlock(&css->mutex);
@@ -858,10 +912,7 @@ static int css_power_event(struct notifier_block *this, unsigned long event,
case PM_POST_HIBERNATION:
case PM_POST_SUSPEND:
ret = NOTIFY_DONE;
- for (i = 0; i <= __MAX_CSSID; i++) {
- struct channel_subsystem *css;
-
- css = channel_subsystems[i];
+ for_each_css(css) {
mutex_lock(&css->mutex);
if (!css->cm_enabled) {
mutex_unlock(&css->mutex);
@@ -916,36 +967,10 @@ static int __init css_bus_init(void)
goto out;
/* Setup css structure. */
- for (i = 0; i <= __MAX_CSSID; i++) {
- struct channel_subsystem *css;
-
- css = kmalloc(sizeof(struct channel_subsystem), GFP_KERNEL);
- if (!css) {
- ret = -ENOMEM;
- goto out_unregister;
- }
- channel_subsystems[i] = css;
+ for (i = 0; i <= MAX_CSS_IDX; i++) {
ret = setup_css(i);
- if (ret) {
- kfree(channel_subsystems[i]);
- goto out_unregister;
- }
- ret = device_register(&css->device);
- if (ret) {
- put_device(&css->device);
+ if (ret)
goto out_unregister;
- }
- if (css_chsc_characteristics.secm) {
- ret = device_create_file(&css->device,
- &dev_attr_cm_enable);
- if (ret)
- goto out_device;
- }
- ret = device_register(&css->pseudo_subchannel->dev);
- if (ret) {
- put_device(&css->pseudo_subchannel->dev);
- goto out_file;
- }
}
ret = register_reboot_notifier(&css_reboot_notifier);
if (ret)
@@ -961,23 +986,10 @@ static int __init css_bus_init(void)
isc_register(IO_SCH_ISC);
return 0;
-out_file:
- if (css_chsc_characteristics.secm)
- device_remove_file(&channel_subsystems[i]->device,
- &dev_attr_cm_enable);
-out_device:
- device_unregister(&channel_subsystems[i]->device);
out_unregister:
- while (i > 0) {
- struct channel_subsystem *css;
-
- i--;
- css = channel_subsystems[i];
+ while (i-- > 0) {
+ struct channel_subsystem *css = channel_subsystems[i];
device_unregister(&css->pseudo_subchannel->dev);
- css->pseudo_subchannel = NULL;
- if (css_chsc_characteristics.secm)
- device_remove_file(&css->device,
- &dev_attr_cm_enable);
device_unregister(&css->device);
}
bus_unregister(&css_bus_type);
@@ -993,14 +1005,9 @@ out:
static void __init css_bus_cleanup(void)
{
struct channel_subsystem *css;
- int i;
- for (i = 0; i <= __MAX_CSSID; i++) {
- css = channel_subsystems[i];
+ for_each_css(css) {
device_unregister(&css->pseudo_subchannel->dev);
- css->pseudo_subchannel = NULL;
- if (css_chsc_characteristics.secm)
- device_remove_file(&css->device, &dev_attr_cm_enable);
device_unregister(&css->device);
}
bus_unregister(&css_bus_type);
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index 2c9107e20251..c9f3fb39ebeb 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -113,8 +113,7 @@ extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
void css_update_ssd_info(struct subchannel *sch);
struct channel_subsystem {
- u8 cssid;
- int valid;
+ int cssid;
struct channel_path *chps[__MAX_CHPID + 1];
struct device device;
struct pgid global_pgid;
@@ -130,6 +129,16 @@ struct channel_subsystem {
extern struct channel_subsystem *channel_subsystems[];
+/* Dummy helper which needs to change once we support more than one css. */
+static inline struct channel_subsystem *css_by_id(u8 cssid)
+{
+ return channel_subsystems[0];
+}
+
+/* Dummy iterator which needs to change once we support more than one css. */
+#define for_each_css(css) \
+ for ((css) = channel_subsystems[0]; (css); (css) = NULL)
+
/* Helper functions to build lists for the slow path. */
void css_schedule_eval(struct subchannel_id schid);
void css_schedule_eval_all(void);
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index 71bf9bded485..a4ad39ba3873 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -457,7 +457,7 @@ static inline void inbound_primed(struct qdio_q *q, int count)
{
int new;
- DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim: %02x", count);
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim:%1d %02x", q->nr, count);
/* for QEBSM the ACK was already set by EQBS */
if (is_qebsm(q)) {
@@ -544,7 +544,8 @@ static int get_inbound_buffer_frontier(struct qdio_q *q)
case SLSB_P_INPUT_ACK:
if (q->irq_ptr->perf_stat_enabled)
q->q_stats.nr_sbal_nop++;
- DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in nop");
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in nop:%1d %#02x",
+ q->nr, q->first_to_check);
break;
default:
WARN_ON_ONCE(1);
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index 5d06253c2a7a..8ad98a902a91 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -147,11 +147,11 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq)
struct qdio_q *q;
int i;
- for_each_input_queue(irq, q, i) {
- if (!references_shared_dsci(irq) &&
- has_multiple_inq_on_dsci(irq))
- xchg(q->irq_ptr->dsci, 0);
+ if (!references_shared_dsci(irq) &&
+ has_multiple_inq_on_dsci(irq))
+ xchg(irq->dsci, 0);
+ for_each_input_queue(irq, q, i) {
if (q->u.in.queue_start_poll) {
/* skip if polling is enabled or already in work */
if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED,
@@ -161,11 +161,11 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq)
}
/* avoid dsci clear here, done after processing */
- q->u.in.queue_start_poll(q->irq_ptr->cdev, q->nr,
- q->irq_ptr->int_parm);
+ q->u.in.queue_start_poll(irq->cdev, q->nr,
+ irq->int_parm);
} else {
- if (!shared_ind(q->irq_ptr))
- xchg(q->irq_ptr->dsci, 0);
+ if (!shared_ind(irq))
+ xchg(irq->dsci, 0);
/*
* Call inbound processing but not directly
@@ -178,8 +178,7 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq)
/**
* tiqdio_thinint_handler - thin interrupt handler for qdio
- * @alsi: pointer to adapter local summary indicator
- * @data: NULL
+ * @airq: pointer to adapter interrupt descriptor
*/
static void tiqdio_thinint_handler(struct airq_struct *airq)
{