diff options
Diffstat (limited to 'drivers/s390/char')
-rw-r--r-- | drivers/s390/char/sclp_sdias.c | 101 |
1 files changed, 80 insertions, 21 deletions
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 @@ | |||
8 | #define KMSG_COMPONENT "sclp_sdias" | 8 | #define KMSG_COMPONENT "sclp_sdias" |
9 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | 9 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt |
10 | 10 | ||
11 | #include <linux/completion.h> | ||
11 | #include <linux/sched.h> | 12 | #include <linux/sched.h> |
12 | #include <asm/sclp.h> | 13 | #include <asm/sclp.h> |
13 | #include <asm/debug.h> | 14 | #include <asm/debug.h> |
@@ -62,15 +63,29 @@ struct sdias_sccb { | |||
62 | } __attribute__((packed)); | 63 | } __attribute__((packed)); |
63 | 64 | ||
64 | static struct sdias_sccb sccb __attribute__((aligned(4096))); | 65 | static struct sdias_sccb sccb __attribute__((aligned(4096))); |
66 | static struct sdias_evbuf sdias_evbuf; | ||
65 | 67 | ||
66 | static int sclp_req_done; | 68 | static DECLARE_COMPLETION(evbuf_accepted); |
67 | static wait_queue_head_t sdias_wq; | 69 | static DECLARE_COMPLETION(evbuf_done); |
68 | static DEFINE_MUTEX(sdias_mutex); | 70 | static DEFINE_MUTEX(sdias_mutex); |
69 | 71 | ||
72 | /* | ||
73 | * Called by SCLP base when read event data has been completed (async mode only) | ||
74 | */ | ||
75 | static void sclp_sdias_receiver_fn(struct evbuf_header *evbuf) | ||
76 | { | ||
77 | memcpy(&sdias_evbuf, evbuf, | ||
78 | min_t(unsigned long, sizeof(sdias_evbuf), evbuf->length)); | ||
79 | complete(&evbuf_done); | ||
80 | TRACE("sclp_sdias_receiver_fn done\n"); | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * Called by SCLP base when sdias event has been accepted | ||
85 | */ | ||
70 | static void sdias_callback(struct sclp_req *request, void *data) | 86 | static void sdias_callback(struct sclp_req *request, void *data) |
71 | { | 87 | { |
72 | sclp_req_done = 1; | 88 | complete(&evbuf_accepted); |
73 | wake_up(&sdias_wq); /* Inform caller, that request is complete */ | ||
74 | TRACE("callback done\n"); | 89 | TRACE("callback done\n"); |
75 | } | 90 | } |
76 | 91 | ||
@@ -80,7 +95,6 @@ static int sdias_sclp_send(struct sclp_req *req) | |||
80 | int rc; | 95 | int rc; |
81 | 96 | ||
82 | for (retries = SDIAS_RETRIES; retries; retries--) { | 97 | for (retries = SDIAS_RETRIES; retries; retries--) { |
83 | sclp_req_done = 0; | ||
84 | TRACE("add request\n"); | 98 | TRACE("add request\n"); |
85 | rc = sclp_add_request(req); | 99 | rc = sclp_add_request(req); |
86 | if (rc) { | 100 | if (rc) { |
@@ -91,16 +105,31 @@ static int sdias_sclp_send(struct sclp_req *req) | |||
91 | continue; | 105 | continue; |
92 | } | 106 | } |
93 | /* initiated, wait for completion of service call */ | 107 | /* initiated, wait for completion of service call */ |
94 | wait_event(sdias_wq, (sclp_req_done == 1)); | 108 | wait_for_completion(&evbuf_accepted); |
95 | if (req->status == SCLP_REQ_FAILED) { | 109 | if (req->status == SCLP_REQ_FAILED) { |
96 | TRACE("sclp request failed\n"); | 110 | TRACE("sclp request failed\n"); |
97 | rc = -EIO; | ||
98 | continue; | 111 | continue; |
99 | } | 112 | } |
113 | /* if not accepted, retry */ | ||
114 | if (!(sccb.evbuf.hdr.flags & 0x80)) { | ||
115 | TRACE("sclp request failed: flags=%x\n", | ||
116 | sccb.evbuf.hdr.flags); | ||
117 | continue; | ||
118 | } | ||
119 | /* | ||
120 | * for the sync interface the response is in the initial sccb | ||
121 | */ | ||
122 | if (!sclp_sdias_register.receiver_fn) { | ||
123 | memcpy(&sdias_evbuf, &sccb.evbuf, sizeof(sdias_evbuf)); | ||
124 | TRACE("sync request done\n"); | ||
125 | return 0; | ||
126 | } | ||
127 | /* otherwise we wait for completion */ | ||
128 | wait_for_completion(&evbuf_done); | ||
100 | TRACE("request done\n"); | 129 | TRACE("request done\n"); |
101 | break; | 130 | return 0; |
102 | } | 131 | } |
103 | return rc; | 132 | return -EIO; |
104 | } | 133 | } |
105 | 134 | ||
106 | /* | 135 | /* |
@@ -140,13 +169,12 @@ int sclp_sdias_blk_count(void) | |||
140 | goto out; | 169 | goto out; |
141 | } | 170 | } |
142 | 171 | ||
143 | switch (sccb.evbuf.event_status) { | 172 | switch (sdias_evbuf.event_status) { |
144 | case 0: | 173 | case 0: |
145 | rc = sccb.evbuf.blk_cnt; | 174 | rc = sdias_evbuf.blk_cnt; |
146 | break; | 175 | break; |
147 | default: | 176 | default: |
148 | pr_err("SCLP error: %x\n", | 177 | pr_err("SCLP error: %x\n", sdias_evbuf.event_status); |
149 | sccb.evbuf.event_status); | ||
150 | rc = -EIO; | 178 | rc = -EIO; |
151 | goto out; | 179 | goto out; |
152 | } | 180 | } |
@@ -211,18 +239,18 @@ int sclp_sdias_copy(void *dest, int start_blk, int nr_blks) | |||
211 | goto out; | 239 | goto out; |
212 | } | 240 | } |
213 | 241 | ||
214 | switch (sccb.evbuf.event_status) { | 242 | switch (sdias_evbuf.event_status) { |
215 | case EVSTATE_ALL_STORED: | 243 | case EVSTATE_ALL_STORED: |
216 | TRACE("all stored\n"); | 244 | TRACE("all stored\n"); |
217 | case EVSTATE_PART_STORED: | 245 | case EVSTATE_PART_STORED: |
218 | TRACE("part stored: %i\n", sccb.evbuf.blk_cnt); | 246 | TRACE("part stored: %i\n", sdias_evbuf.blk_cnt); |
219 | break; | 247 | break; |
220 | case EVSTATE_NO_DATA: | 248 | case EVSTATE_NO_DATA: |
221 | TRACE("no data\n"); | 249 | TRACE("no data\n"); |
222 | default: | 250 | default: |
223 | pr_err("Error from SCLP while copying hsa. " | 251 | pr_err("Error from SCLP while copying hsa. " |
224 | "Event status = %x\n", | 252 | "Event status = %x\n", |
225 | sccb.evbuf.event_status); | 253 | sdias_evbuf.event_status); |
226 | rc = -EIO; | 254 | rc = -EIO; |
227 | } | 255 | } |
228 | out: | 256 | out: |
@@ -230,19 +258,50 @@ out: | |||
230 | return rc; | 258 | return rc; |
231 | } | 259 | } |
232 | 260 | ||
233 | int __init sclp_sdias_init(void) | 261 | static int __init sclp_sdias_register_check(void) |
234 | { | 262 | { |
235 | int rc; | 263 | int rc; |
236 | 264 | ||
265 | rc = sclp_register(&sclp_sdias_register); | ||
266 | if (rc) | ||
267 | return rc; | ||
268 | if (sclp_sdias_blk_count() == 0) { | ||
269 | sclp_unregister(&sclp_sdias_register); | ||
270 | return -ENODEV; | ||
271 | } | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static int __init sclp_sdias_init_sync(void) | ||
276 | { | ||
277 | TRACE("Try synchronous mode\n"); | ||
278 | sclp_sdias_register.receive_mask = 0; | ||
279 | sclp_sdias_register.receiver_fn = NULL; | ||
280 | return sclp_sdias_register_check(); | ||
281 | } | ||
282 | |||
283 | static int __init sclp_sdias_init_async(void) | ||
284 | { | ||
285 | TRACE("Try asynchronous mode\n"); | ||
286 | sclp_sdias_register.receive_mask = EVTYP_SDIAS_MASK; | ||
287 | sclp_sdias_register.receiver_fn = sclp_sdias_receiver_fn; | ||
288 | return sclp_sdias_register_check(); | ||
289 | } | ||
290 | |||
291 | int __init sclp_sdias_init(void) | ||
292 | { | ||
237 | if (ipl_info.type != IPL_TYPE_FCP_DUMP) | 293 | if (ipl_info.type != IPL_TYPE_FCP_DUMP) |
238 | return 0; | 294 | return 0; |
239 | sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long)); | 295 | sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long)); |
240 | debug_register_view(sdias_dbf, &debug_sprintf_view); | 296 | debug_register_view(sdias_dbf, &debug_sprintf_view); |
241 | debug_set_level(sdias_dbf, 6); | 297 | debug_set_level(sdias_dbf, 6); |
242 | rc = sclp_register(&sclp_sdias_register); | 298 | if (sclp_sdias_init_sync() == 0) |
243 | if (rc) | 299 | goto out; |
244 | return rc; | 300 | if (sclp_sdias_init_async() == 0) |
245 | init_waitqueue_head(&sdias_wq); | 301 | goto out; |
302 | TRACE("init failed\n"); | ||
303 | return -ENODEV; | ||
304 | out: | ||
246 | TRACE("init done\n"); | 305 | TRACE("init done\n"); |
247 | return 0; | 306 | return 0; |
248 | } | 307 | } |