aboutsummaryrefslogtreecommitdiff
path: root/drivers/s390/char/sclp_sdias.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/char/sclp_sdias.c')
-rw-r--r--drivers/s390/char/sclp_sdias.c101
1 files changed, 80 insertions, 21 deletions
diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c
index fa733ecd3d7..69e6c50d4cf 100644
--- a/drivers/s390/char/sclp_sdias.c
+++ b/drivers/s390/char/sclp_sdias.c
@@ -8,6 +8,7 @@
#define KMSG_COMPONENT "sclp_sdias"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#include <linux/completion.h>
#include <linux/sched.h>
#include <asm/sclp.h>
#include <asm/debug.h>
@@ -62,15 +63,29 @@ struct sdias_sccb {
} __attribute__((packed));
static struct sdias_sccb sccb __attribute__((aligned(4096)));
+static struct sdias_evbuf sdias_evbuf;
-static int sclp_req_done;
-static wait_queue_head_t sdias_wq;
+static DECLARE_COMPLETION(evbuf_accepted);
+static DECLARE_COMPLETION(evbuf_done);
static DEFINE_MUTEX(sdias_mutex);
+/*
+ * Called by SCLP base when read event data has been completed (async mode only)
+ */
+static void sclp_sdias_receiver_fn(struct evbuf_header *evbuf)
+{
+ memcpy(&sdias_evbuf, evbuf,
+ min_t(unsigned long, sizeof(sdias_evbuf), evbuf->length));
+ complete(&evbuf_done);
+ TRACE("sclp_sdias_receiver_fn done\n");
+}
+
+/*
+ * Called by SCLP base when sdias event has been accepted
+ */
static void sdias_callback(struct sclp_req *request, void *data)
{
- sclp_req_done = 1;
- wake_up(&sdias_wq); /* Inform caller, that request is complete */
+ complete(&evbuf_accepted);
TRACE("callback done\n");
}
@@ -80,7 +95,6 @@ static int sdias_sclp_send(struct sclp_req *req)
int rc;
for (retries = SDIAS_RETRIES; retries; retries--) {
- sclp_req_done = 0;
TRACE("add request\n");
rc = sclp_add_request(req);
if (rc) {
@@ -91,16 +105,31 @@ static int sdias_sclp_send(struct sclp_req *req)
continue;
}
/* initiated, wait for completion of service call */
- wait_event(sdias_wq, (sclp_req_done == 1));
+ wait_for_completion(&evbuf_accepted);
if (req->status == SCLP_REQ_FAILED) {
TRACE("sclp request failed\n");
- rc = -EIO;
continue;
}
+ /* if not accepted, retry */
+ if (!(sccb.evbuf.hdr.flags & 0x80)) {
+ TRACE("sclp request failed: flags=%x\n",
+ sccb.evbuf.hdr.flags);
+ continue;
+ }
+ /*
+ * for the sync interface the response is in the initial sccb
+ */
+ if (!sclp_sdias_register.receiver_fn) {
+ memcpy(&sdias_evbuf, &sccb.evbuf, sizeof(sdias_evbuf));
+ TRACE("sync request done\n");
+ return 0;
+ }
+ /* otherwise we wait for completion */
+ wait_for_completion(&evbuf_done);
TRACE("request done\n");
- break;
+ return 0;
}
- return rc;
+ return -EIO;
}
/*
@@ -140,13 +169,12 @@ int sclp_sdias_blk_count(void)
goto out;
}
- switch (sccb.evbuf.event_status) {
+ switch (sdias_evbuf.event_status) {
case 0:
- rc = sccb.evbuf.blk_cnt;
+ rc = sdias_evbuf.blk_cnt;
break;
default:
- pr_err("SCLP error: %x\n",
- sccb.evbuf.event_status);
+ pr_err("SCLP error: %x\n", sdias_evbuf.event_status);
rc = -EIO;
goto out;
}
@@ -211,18 +239,18 @@ int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
goto out;
}
- switch (sccb.evbuf.event_status) {
+ switch (sdias_evbuf.event_status) {
case EVSTATE_ALL_STORED:
TRACE("all stored\n");
case EVSTATE_PART_STORED:
- TRACE("part stored: %i\n", sccb.evbuf.blk_cnt);
+ TRACE("part stored: %i\n", sdias_evbuf.blk_cnt);
break;
case EVSTATE_NO_DATA:
TRACE("no data\n");
default:
pr_err("Error from SCLP while copying hsa. "
"Event status = %x\n",
- sccb.evbuf.event_status);
+ sdias_evbuf.event_status);
rc = -EIO;
}
out:
@@ -230,19 +258,50 @@ out:
return rc;
}
-int __init sclp_sdias_init(void)
+static int __init sclp_sdias_register_check(void)
{
int rc;
+ rc = sclp_register(&sclp_sdias_register);
+ if (rc)
+ return rc;
+ if (sclp_sdias_blk_count() == 0) {
+ sclp_unregister(&sclp_sdias_register);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int __init sclp_sdias_init_sync(void)
+{
+ TRACE("Try synchronous mode\n");
+ sclp_sdias_register.receive_mask = 0;
+ sclp_sdias_register.receiver_fn = NULL;
+ return sclp_sdias_register_check();
+}
+
+static int __init sclp_sdias_init_async(void)
+{
+ TRACE("Try asynchronous mode\n");
+ sclp_sdias_register.receive_mask = EVTYP_SDIAS_MASK;
+ sclp_sdias_register.receiver_fn = sclp_sdias_receiver_fn;
+ return sclp_sdias_register_check();
+}
+
+int __init sclp_sdias_init(void)
+{
if (ipl_info.type != IPL_TYPE_FCP_DUMP)
return 0;
sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long));
debug_register_view(sdias_dbf, &debug_sprintf_view);
debug_set_level(sdias_dbf, 6);
- rc = sclp_register(&sclp_sdias_register);
- if (rc)
- return rc;
- init_waitqueue_head(&sdias_wq);
+ if (sclp_sdias_init_sync() == 0)
+ goto out;
+ if (sclp_sdias_init_async() == 0)
+ goto out;
+ TRACE("init failed\n");
+ return -ENODEV;
+out:
TRACE("init done\n");
return 0;
}