diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c index fa733ecd3d70..69e6c50d4cfb 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 #include #include #include @@ -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; }