aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/cio')
-rw-r--r--drivers/s390/cio/chsc.c15
-rw-r--r--drivers/s390/cio/cio.c30
-rw-r--r--drivers/s390/cio/cio.h1
-rw-r--r--drivers/s390/cio/css.c16
-rw-r--r--drivers/s390/cio/css.h1
-rw-r--r--drivers/s390/cio/device.c1
-rw-r--r--drivers/s390/cio/device.h1
-rw-r--r--drivers/s390/cio/device_ops.c9
-rw-r--r--drivers/s390/cio/device_status.c8
-rw-r--r--drivers/s390/cio/qdio.c180
-rw-r--r--drivers/s390/cio/qdio.h28
11 files changed, 173 insertions, 117 deletions
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 007aaeb4f532..5de86908b0d0 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -217,6 +217,8 @@ void chsc_chp_offline(struct chp_id chpid)
if (chp_get_status(chpid) <= 0)
return;
+ /* Wait until previous actions have settled. */
+ css_wait_for_slow_path();
for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &chpid);
}
@@ -303,7 +305,8 @@ static void s390_process_res_acc (struct res_acc_data *res_data)
sprintf(dbf_txt, "fla%x", res_data->fla);
CIO_TRACE_EVENT( 2, dbf_txt);
}
-
+ /* Wait until previous actions have settled. */
+ css_wait_for_slow_path();
/*
* I/O resources may have become accessible.
* Scan through all subchannels that may be concerned and
@@ -561,9 +564,12 @@ void chsc_chp_online(struct chp_id chpid)
sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id);
CIO_TRACE_EVENT(2, dbf_txt);
- if (chp_get_status(chpid) != 0)
+ if (chp_get_status(chpid) != 0) {
+ /* Wait until previous actions have settled. */
+ css_wait_for_slow_path();
for_each_subchannel_staged(__chp_add, __chp_add_new_sch,
&chpid);
+ }
}
static void __s390_subchannel_vary_chpid(struct subchannel *sch,
@@ -650,6 +656,8 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data)
*/
int chsc_chp_vary(struct chp_id chpid, int on)
{
+ /* Wait until previous actions have settled. */
+ css_wait_for_slow_path();
/*
* Redo PathVerification on the devices the chpid connects to
*/
@@ -758,7 +766,6 @@ chsc_secm(struct channel_subsystem *css, int enable)
if (!secm_area)
return -ENOMEM;
- mutex_lock(&css->mutex);
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);
@@ -766,7 +773,6 @@ 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);
- mutex_unlock(&css->mutex);
return -ENOMEM;
}
}
@@ -787,7 +793,6 @@ chsc_secm(struct channel_subsystem *css, int enable)
free_page((unsigned long)css->cub_addr1);
free_page((unsigned long)css->cub_addr2);
}
- mutex_unlock(&css->mutex);
free_page((unsigned long)secm_area);
return ret;
}
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 60590a12d529..23ffcc4768a7 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -24,6 +24,7 @@
#include <asm/ipl.h>
#include <asm/chpid.h>
#include <asm/airq.h>
+#include <asm/cpu.h>
#include "cio.h"
#include "css.h"
#include "chsc.h"
@@ -649,13 +650,10 @@ do_IRQ (struct pt_regs *regs)
old_regs = set_irq_regs(regs);
irq_enter();
- asm volatile ("mc 0,0");
- if (S390_lowcore.int_clock >= S390_lowcore.jiffy_timer)
- /**
- * Make sure that the i/o interrupt did not "overtake"
- * the last HZ timer interrupt.
- */
- account_ticks(S390_lowcore.int_clock);
+ s390_idle_check();
+ if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator)
+ /* Serve timer interrupts first. */
+ clock_comparator_work();
/*
* Get interrupt information from lowcore
*/
@@ -672,10 +670,14 @@ do_IRQ (struct pt_regs *regs)
continue;
}
sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
- if (sch)
- spin_lock(sch->lock);
+ if (!sch) {
+ /* Clear pending interrupt condition. */
+ tsch(tpi_info->schid, irb);
+ continue;
+ }
+ spin_lock(sch->lock);
/* Store interrupt response block to lowcore. */
- if (tsch (tpi_info->schid, irb) == 0 && sch) {
+ if (tsch(tpi_info->schid, irb) == 0) {
/* Keep subchannel information word up to date. */
memcpy (&sch->schib.scsw, &irb->scsw,
sizeof (irb->scsw));
@@ -683,8 +685,7 @@ do_IRQ (struct pt_regs *regs)
if (sch->driver && sch->driver->irq)
sch->driver->irq(sch);
}
- if (sch)
- spin_unlock(sch->lock);
+ spin_unlock(sch->lock);
/*
* Are more interrupts pending?
* If so, the tpi instruction will update the lowcore
@@ -710,8 +711,9 @@ void *cio_get_console_priv(void)
/*
* busy wait for the next interrupt on the console
*/
-void
-wait_cons_dev (void)
+void wait_cons_dev(void)
+ __releases(console_subchannel.lock)
+ __acquires(console_subchannel.lock)
{
unsigned long cr6 __attribute__ ((aligned (8)));
unsigned long save_cr6 __attribute__ ((aligned (8)));
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 52afa4c784de..08f2235c5a6f 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -100,6 +100,7 @@ extern int cio_modify (struct subchannel *);
int cio_create_sch_lock(struct subchannel *);
void do_adapter_IO(void);
+void do_IRQ(struct pt_regs *);
/* Use with care. */
#ifdef CONFIG_CCW_CONSOLE
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 3b45bbe6cce0..c1afab5f72d6 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -533,6 +533,12 @@ void css_schedule_eval_all(void)
spin_unlock_irqrestore(&slow_subchannel_lock, flags);
}
+void css_wait_for_slow_path(void)
+{
+ flush_workqueue(ccw_device_notify_work);
+ flush_workqueue(slow_path_wq);
+}
+
/* Reprobe subchannel if unregistered. */
static int reprobe_subchannel(struct subchannel_id schid, void *data)
{
@@ -683,10 +689,14 @@ css_cm_enable_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct channel_subsystem *css = to_css(dev);
+ int ret;
if (!css)
return 0;
- return sprintf(buf, "%x\n", css->cm_enabled);
+ mutex_lock(&css->mutex);
+ ret = sprintf(buf, "%x\n", css->cm_enabled);
+ mutex_unlock(&css->mutex);
+ return ret;
}
static ssize_t
@@ -696,6 +706,7 @@ css_cm_enable_store(struct device *dev, struct device_attribute *attr,
struct channel_subsystem *css = to_css(dev);
int ret;
+ mutex_lock(&css->mutex);
switch (buf[0]) {
case '0':
ret = css->cm_enabled ? chsc_secm(css, 0) : 0;
@@ -706,6 +717,7 @@ css_cm_enable_store(struct device *dev, struct device_attribute *attr,
default:
ret = -EINVAL;
}
+ mutex_unlock(&css->mutex);
return ret < 0 ? ret : count;
}
@@ -752,9 +764,11 @@ static int css_reboot_event(struct notifier_block *this,
struct channel_subsystem *css;
css = channel_subsystems[i];
+ mutex_lock(&css->mutex);
if (css->cm_enabled)
if (chsc_secm(css, 0))
ret = NOTIFY_BAD;
+ mutex_unlock(&css->mutex);
}
return ret;
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index b70554523552..e1913518f354 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -144,6 +144,7 @@ struct schib;
int css_sch_is_valid(struct schib *);
extern struct workqueue_struct *slow_path_wq;
+void css_wait_for_slow_path(void);
extern struct attribute_group *subch_attr_groups[];
#endif
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index fec004f62bcf..e0c7adb8958e 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -577,7 +577,6 @@ static DEVICE_ATTR(devtype, 0444, devtype_show, NULL);
static DEVICE_ATTR(cutype, 0444, cutype_show, NULL);
static DEVICE_ATTR(modalias, 0444, modalias_show, NULL);
static DEVICE_ATTR(online, 0644, online_show, online_store);
-extern struct device_attribute dev_attr_cmb_enable;
static DEVICE_ATTR(availability, 0444, available_show, NULL);
static struct attribute * subch_attrs[] = {
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index d40a2ffaa000..cb08092be39f 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -127,4 +127,5 @@ extern struct bus_type ccw_bus_type;
void retry_set_schib(struct ccw_device *cdev);
void cmf_retry_copy_block(struct ccw_device *);
int cmf_reenable(struct ccw_device *);
+extern struct device_attribute dev_attr_cmb_enable;
#endif
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index 49b58eb0fab8..a1718a0aa539 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -193,8 +193,15 @@ int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa,
return -EACCES;
}
ret = cio_start_key (sch, cpa, lpm, key);
- if (ret == 0)
+ switch (ret) {
+ case 0:
cdev->private->intparm = intparm;
+ break;
+ case -EACCES:
+ case -ENODEV:
+ dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+ break;
+ }
return ret;
}
diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c
index ebe0848cfe33..4a38993000f2 100644
--- a/drivers/s390/cio/device_status.c
+++ b/drivers/s390/cio/device_status.c
@@ -62,7 +62,7 @@ ccw_device_path_notoper(struct ccw_device *cdev)
stsch (sch->schid, &sch->schib);
CIO_MSG_EVENT(0, "%s(0.%x.%04x) - path(s) %02x are "
- "not operational \n", __FUNCTION__,
+ "not operational \n", __func__,
sch->schid.ssid, sch->schid.sch_no,
sch->schib.pmcw.pnom);
@@ -312,6 +312,7 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb)
{
struct subchannel *sch;
struct ccw1 *sense_ccw;
+ int rc;
sch = to_subchannel(cdev->dev.parent);
@@ -337,7 +338,10 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb)
/* Reset internal retry indication. */
cdev->private->flags.intretry = 0;
- return cio_start(sch, sense_ccw, 0xff);
+ rc = cio_start(sch, sense_ccw, 0xff);
+ if (rc == -ENODEV || rc == -EACCES)
+ dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+ return rc;
}
/*
diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c
index 2b5bfb7c69e5..c359386708e9 100644
--- a/drivers/s390/cio/qdio.c
+++ b/drivers/s390/cio/qdio.c
@@ -1399,7 +1399,7 @@ __tiqdio_inbound_processing(struct qdio_q *q, int spare_ind_was_set)
* q->dev_st_chg_ind is the indicator, be it shared or not.
* only clear it, if indicator is non-shared
*/
- if (!spare_ind_was_set)
+ if (q->dev_st_chg_ind != &spare_indicator)
tiqdio_clear_summary_bit((__u32*)q->dev_st_chg_ind);
if (q->hydra_gives_outbound_pcis) {
@@ -2217,9 +2217,78 @@ qdio_synchronize(struct ccw_device *cdev, unsigned int flags,
return cc;
}
+static int
+qdio_get_ssqd_information(struct subchannel_id *schid,
+ struct qdio_chsc_ssqd **ssqd_area)
+{
+ int result;
+
+ QDIO_DBF_TEXT0(0, setup, "getssqd");
+ *ssqd_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC);
+ if (!ssqd_area) {
+ QDIO_PRINT_WARN("Could not get memory for chsc on sch x%x.\n",
+ schid->sch_no);
+ return -ENOMEM;
+ }
+
+ (*ssqd_area)->request = (struct chsc_header) {
+ .length = 0x0010,
+ .code = 0x0024,
+ };
+ (*ssqd_area)->first_sch = schid->sch_no;
+ (*ssqd_area)->last_sch = schid->sch_no;
+ (*ssqd_area)->ssid = schid->ssid;
+ result = chsc(*ssqd_area);
+
+ if (result) {
+ QDIO_PRINT_WARN("CHSC returned cc %i on sch 0.%x.%x.\n",
+ result, schid->ssid, schid->sch_no);
+ goto out;
+ }
+
+ if ((*ssqd_area)->response.code != QDIO_CHSC_RESPONSE_CODE_OK) {
+ QDIO_PRINT_WARN("CHSC response is 0x%x on sch 0.%x.%x.\n",
+ (*ssqd_area)->response.code,
+ schid->ssid, schid->sch_no);
+ goto out;
+ }
+ if (!((*ssqd_area)->flags & CHSC_FLAG_QDIO_CAPABILITY) ||
+ !((*ssqd_area)->flags & CHSC_FLAG_VALIDITY) ||
+ ((*ssqd_area)->sch != schid->sch_no)) {
+ QDIO_PRINT_WARN("huh? problems checking out sch 0.%x.%x... " \
+ "using all SIGAs.\n",
+ schid->ssid, schid->sch_no);
+ goto out;
+ }
+ return 0;
+out:
+ return -EINVAL;
+}
+
+int
+qdio_get_ssqd_pct(struct ccw_device *cdev)
+{
+ struct qdio_chsc_ssqd *ssqd_area;
+ struct subchannel_id schid;
+ char dbf_text[15];
+ int rc;
+ int pct = 0;
+
+ QDIO_DBF_TEXT0(0, setup, "getpct");
+ schid = ccw_device_get_subchannel_id(cdev);
+ rc = qdio_get_ssqd_information(&schid, &ssqd_area);
+ if (!rc)
+ pct = (int)ssqd_area->pct;
+ if (rc != -ENOMEM)
+ mempool_free(ssqd_area, qdio_mempool_scssc);
+ sprintf(dbf_text, "pct: %d", pct);
+ QDIO_DBF_TEXT2(0, setup, dbf_text);
+ return pct;
+}
+EXPORT_SYMBOL(qdio_get_ssqd_pct);
+
static void
-qdio_check_subchannel_qebsm(struct qdio_irq *irq_ptr, unsigned char qdioac,
- unsigned long token)
+qdio_check_subchannel_qebsm(struct qdio_irq *irq_ptr, unsigned long token)
{
struct qdio_q *q;
int i;
@@ -2227,7 +2296,7 @@ qdio_check_subchannel_qebsm(struct qdio_irq *irq_ptr, unsigned char qdioac,
char dbf_text[15];
/*check if QEBSM is disabled */
- if (!(irq_ptr->is_qebsm) || !(qdioac & 0x01)) {
+ if (!(irq_ptr->is_qebsm) || !(irq_ptr->qdioac & 0x01)) {
irq_ptr->is_qebsm = 0;
irq_ptr->sch_token = 0;
irq_ptr->qib.rflags &= ~QIB_RFLAGS_ENABLE_QEBSM;
@@ -2256,102 +2325,27 @@ qdio_check_subchannel_qebsm(struct qdio_irq *irq_ptr, unsigned char qdioac,
}
static void
-qdio_get_ssqd_information(struct qdio_irq *irq_ptr)
+qdio_get_ssqd_siga(struct qdio_irq *irq_ptr)
{
- int result;
- unsigned char qdioac;
- struct {
- struct chsc_header request;
- u16 reserved1:10;
- u16 ssid:2;
- u16 fmt:4;
- u16 first_sch;
- u16 reserved2;
- u16 last_sch;
- u32 reserved3;
- struct chsc_header response;
- u32 reserved4;
- u8 flags;
- u8 reserved5;
- u16 sch;
- u8 qfmt;
- u8 parm;
- u8 qdioac1;
- u8 sch_class;
- u8 reserved7;
- u8 icnt;
- u8 reserved8;
- u8 ocnt;
- u8 reserved9;
- u8 mbccnt;
- u16 qdioac2;
- u64 sch_token;
- } *ssqd_area;
+ int rc;
+ struct qdio_chsc_ssqd *ssqd_area;
QDIO_DBF_TEXT0(0,setup,"getssqd");
- qdioac = 0;
- ssqd_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC);
- if (!ssqd_area) {
- QDIO_PRINT_WARN("Could not get memory for chsc. Using all " \
- "SIGAs for sch x%x.\n", irq_ptr->schid.sch_no);
+ irq_ptr->qdioac = 0;
+ rc = qdio_get_ssqd_information(&irq_ptr->schid, &ssqd_area);
+ if (rc) {
+ QDIO_PRINT_WARN("using all SIGAs for sch x%x.n",
+ irq_ptr->schid.sch_no);
irq_ptr->qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY |
CHSC_FLAG_SIGA_OUTPUT_NECESSARY |
CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */
irq_ptr->is_qebsm = 0;
- irq_ptr->sch_token = 0;
- irq_ptr->qib.rflags &= ~QIB_RFLAGS_ENABLE_QEBSM;
- return;
- }
-
- ssqd_area->request = (struct chsc_header) {
- .length = 0x0010,
- .code = 0x0024,
- };
- ssqd_area->first_sch = irq_ptr->schid.sch_no;
- ssqd_area->last_sch = irq_ptr->schid.sch_no;
- ssqd_area->ssid = irq_ptr->schid.ssid;
- result = chsc(ssqd_area);
-
- if (result) {
- QDIO_PRINT_WARN("CHSC returned cc %i. Using all " \
- "SIGAs for sch 0.%x.%x.\n", result,
- irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
- qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY |
- CHSC_FLAG_SIGA_OUTPUT_NECESSARY |
- CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */
- irq_ptr->is_qebsm = 0;
- goto out;
- }
+ } else
+ irq_ptr->qdioac = ssqd_area->qdioac1;
- if (ssqd_area->response.code != QDIO_CHSC_RESPONSE_CODE_OK) {
- QDIO_PRINT_WARN("response upon checking SIGA needs " \
- "is 0x%x. Using all SIGAs for sch 0.%x.%x.\n",
- ssqd_area->response.code,
- irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
- qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY |
- CHSC_FLAG_SIGA_OUTPUT_NECESSARY |
- CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */
- irq_ptr->is_qebsm = 0;
- goto out;
- }
- if (!(ssqd_area->flags & CHSC_FLAG_QDIO_CAPABILITY) ||
- !(ssqd_area->flags & CHSC_FLAG_VALIDITY) ||
- (ssqd_area->sch != irq_ptr->schid.sch_no)) {
- QDIO_PRINT_WARN("huh? problems checking out sch 0.%x.%x... " \
- "using all SIGAs.\n",
- irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
- qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY |
- CHSC_FLAG_SIGA_OUTPUT_NECESSARY |
- CHSC_FLAG_SIGA_SYNC_NECESSARY; /* worst case */
- irq_ptr->is_qebsm = 0;
- goto out;
- }
- qdioac = ssqd_area->qdioac1;
-out:
- qdio_check_subchannel_qebsm(irq_ptr, qdioac,
- ssqd_area->sch_token);
- mempool_free(ssqd_area, qdio_mempool_scssc);
- irq_ptr->qdioac = qdioac;
+ qdio_check_subchannel_qebsm(irq_ptr, ssqd_area->sch_token);
+ if (rc != -ENOMEM)
+ mempool_free(ssqd_area, qdio_mempool_scssc);
}
static unsigned int
@@ -3227,7 +3221,7 @@ qdio_establish(struct qdio_initialize *init_data)
return -EIO;
}
- qdio_get_ssqd_information(irq_ptr);
+ qdio_get_ssqd_siga(irq_ptr);
/* if this gets set once, we're running under VM and can omit SVSes */
if (irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_NECESSARY)
omit_svs=1;
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index da8a272fd75b..c3df6b2c38b7 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -406,6 +406,34 @@ do_clear_global_summary(void)
#define CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS 0x08
#define CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS 0x04
+struct qdio_chsc_ssqd {
+ struct chsc_header request;
+ u16 reserved1:10;
+ u16 ssid:2;
+ u16 fmt:4;
+ u16 first_sch;
+ u16 reserved2;
+ u16 last_sch;
+ u32 reserved3;
+ struct chsc_header response;
+ u32 reserved4;
+ u8 flags;
+ u8 reserved5;
+ u16 sch;
+ u8 qfmt;
+ u8 parm;
+ u8 qdioac1;
+ u8 sch_class;
+ u8 pct;
+ u8 icnt;
+ u8 reserved7;
+ u8 ocnt;
+ u8 reserved8;
+ u8 mbccnt;
+ u16 qdioac2;
+ u64 sch_token;
+};
+
struct qdio_perf_stats {
#ifdef CONFIG_64BIT
atomic64_t tl_runs;