1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * CCW device PGID and path verification I/O handling. |
4 | * |
5 | * Copyright IBM Corp. 2002, 2009 |
6 | * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> |
7 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
8 | * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> |
9 | */ |
10 | |
11 | #include <linux/kernel.h> |
12 | #include <linux/string.h> |
13 | #include <linux/bitops.h> |
14 | #include <linux/types.h> |
15 | #include <linux/errno.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/io.h> |
18 | #include <asm/ccwdev.h> |
19 | #include <asm/cio.h> |
20 | |
21 | #include "cio.h" |
22 | #include "cio_debug.h" |
23 | #include "device.h" |
24 | #include "io_sch.h" |
25 | |
26 | #define PGID_RETRIES 256 |
27 | #define PGID_TIMEOUT (10 * HZ) |
28 | |
29 | static void verify_start(struct ccw_device *cdev); |
30 | |
31 | /* |
32 | * Process path verification data and report result. |
33 | */ |
34 | static void verify_done(struct ccw_device *cdev, int rc) |
35 | { |
36 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
37 | struct ccw_dev_id *id = &cdev->private->dev_id; |
38 | int mpath = cdev->private->flags.mpath; |
39 | int pgroup = cdev->private->flags.pgroup; |
40 | |
41 | if (rc) |
42 | goto out; |
43 | /* Ensure consistent multipathing state at device and channel. */ |
44 | if (sch->config.mp != mpath) { |
45 | sch->config.mp = mpath; |
46 | rc = cio_commit_config(sch); |
47 | } |
48 | out: |
49 | CIO_MSG_EVENT(2, "vrfy: device 0.%x.%04x: rc=%d pgroup=%d mpath=%d " |
50 | "vpm=%02x\n" , id->ssid, id->devno, rc, pgroup, mpath, |
51 | sch->vpm); |
52 | ccw_device_verify_done(cdev, rc); |
53 | } |
54 | |
55 | /* |
56 | * Create channel program to perform a NOOP. |
57 | */ |
58 | static void nop_build_cp(struct ccw_device *cdev) |
59 | { |
60 | struct ccw_request *req = &cdev->private->req; |
61 | struct ccw1 *cp = cdev->private->dma_area->iccws; |
62 | |
63 | cp->cmd_code = CCW_CMD_NOOP; |
64 | cp->cda = 0; |
65 | cp->count = 0; |
66 | cp->flags = CCW_FLAG_SLI; |
67 | req->cp = cp; |
68 | } |
69 | |
70 | /* |
71 | * Perform NOOP on a single path. |
72 | */ |
73 | static void nop_do(struct ccw_device *cdev) |
74 | { |
75 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
76 | struct ccw_request *req = &cdev->private->req; |
77 | |
78 | req->lpm = lpm_adjust(lpm: req->lpm, mask: sch->schib.pmcw.pam & sch->opm & |
79 | ~cdev->private->path_noirq_mask); |
80 | if (!req->lpm) |
81 | goto out_nopath; |
82 | nop_build_cp(cdev); |
83 | ccw_request_start(cdev); |
84 | return; |
85 | |
86 | out_nopath: |
87 | verify_done(cdev, rc: sch->vpm ? 0 : -EACCES); |
88 | } |
89 | |
90 | /* |
91 | * Adjust NOOP I/O status. |
92 | */ |
93 | static enum io_status nop_filter(struct ccw_device *cdev, void *data, |
94 | struct irb *irb, enum io_status status) |
95 | { |
96 | /* Only subchannel status might indicate a path error. */ |
97 | if (status == IO_STATUS_ERROR && irb->scsw.cmd.cstat == 0) |
98 | return IO_DONE; |
99 | return status; |
100 | } |
101 | |
102 | /* |
103 | * Process NOOP request result for a single path. |
104 | */ |
105 | static void nop_callback(struct ccw_device *cdev, void *data, int rc) |
106 | { |
107 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
108 | struct ccw_request *req = &cdev->private->req; |
109 | |
110 | switch (rc) { |
111 | case 0: |
112 | sch->vpm |= req->lpm; |
113 | break; |
114 | case -ETIME: |
115 | cdev->private->path_noirq_mask |= req->lpm; |
116 | break; |
117 | case -EACCES: |
118 | cdev->private->path_notoper_mask |= req->lpm; |
119 | break; |
120 | default: |
121 | goto err; |
122 | } |
123 | /* Continue on the next path. */ |
124 | req->lpm >>= 1; |
125 | nop_do(cdev); |
126 | return; |
127 | |
128 | err: |
129 | verify_done(cdev, rc); |
130 | } |
131 | |
132 | /* |
133 | * Create channel program to perform SET PGID on a single path. |
134 | */ |
135 | static void spid_build_cp(struct ccw_device *cdev, u8 fn) |
136 | { |
137 | struct ccw_request *req = &cdev->private->req; |
138 | struct ccw1 *cp = cdev->private->dma_area->iccws; |
139 | int i = pathmask_to_pos(req->lpm); |
140 | struct pgid *pgid = &cdev->private->dma_area->pgid[i]; |
141 | |
142 | pgid->inf.fc = fn; |
143 | cp->cmd_code = CCW_CMD_SET_PGID; |
144 | cp->cda = (u32)virt_to_phys(address: pgid); |
145 | cp->count = sizeof(*pgid); |
146 | cp->flags = CCW_FLAG_SLI; |
147 | req->cp = cp; |
148 | } |
149 | |
150 | static void pgid_wipeout_callback(struct ccw_device *cdev, void *data, int rc) |
151 | { |
152 | if (rc) { |
153 | /* We don't know the path groups' state. Abort. */ |
154 | verify_done(cdev, rc); |
155 | return; |
156 | } |
157 | /* |
158 | * Path groups have been reset. Restart path verification but |
159 | * leave paths in path_noirq_mask out. |
160 | */ |
161 | cdev->private->flags.pgid_unknown = 0; |
162 | verify_start(cdev); |
163 | } |
164 | |
165 | /* |
166 | * Reset pathgroups and restart path verification, leave unusable paths out. |
167 | */ |
168 | static void pgid_wipeout_start(struct ccw_device *cdev) |
169 | { |
170 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
171 | struct ccw_dev_id *id = &cdev->private->dev_id; |
172 | struct ccw_request *req = &cdev->private->req; |
173 | u8 fn; |
174 | |
175 | CIO_MSG_EVENT(2, "wipe: device 0.%x.%04x: pvm=%02x nim=%02x\n" , |
176 | id->ssid, id->devno, cdev->private->pgid_valid_mask, |
177 | cdev->private->path_noirq_mask); |
178 | |
179 | /* Initialize request data. */ |
180 | memset(req, 0, sizeof(*req)); |
181 | req->timeout = PGID_TIMEOUT; |
182 | req->maxretries = PGID_RETRIES; |
183 | req->lpm = sch->schib.pmcw.pam; |
184 | req->callback = pgid_wipeout_callback; |
185 | fn = SPID_FUNC_DISBAND; |
186 | if (cdev->private->flags.mpath) |
187 | fn |= SPID_FUNC_MULTI_PATH; |
188 | spid_build_cp(cdev, fn); |
189 | ccw_request_start(cdev); |
190 | } |
191 | |
192 | /* |
193 | * Perform establish/resign SET PGID on a single path. |
194 | */ |
195 | static void spid_do(struct ccw_device *cdev) |
196 | { |
197 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
198 | struct ccw_request *req = &cdev->private->req; |
199 | u8 fn; |
200 | |
201 | /* Use next available path that is not already in correct state. */ |
202 | req->lpm = lpm_adjust(lpm: req->lpm, mask: cdev->private->pgid_todo_mask); |
203 | if (!req->lpm) |
204 | goto out_nopath; |
205 | /* Channel program setup. */ |
206 | if (req->lpm & sch->opm) |
207 | fn = SPID_FUNC_ESTABLISH; |
208 | else |
209 | fn = SPID_FUNC_RESIGN; |
210 | if (cdev->private->flags.mpath) |
211 | fn |= SPID_FUNC_MULTI_PATH; |
212 | spid_build_cp(cdev, fn); |
213 | ccw_request_start(cdev); |
214 | return; |
215 | |
216 | out_nopath: |
217 | if (cdev->private->flags.pgid_unknown) { |
218 | /* At least one SPID could be partially done. */ |
219 | pgid_wipeout_start(cdev); |
220 | return; |
221 | } |
222 | verify_done(cdev, rc: sch->vpm ? 0 : -EACCES); |
223 | } |
224 | |
225 | /* |
226 | * Process SET PGID request result for a single path. |
227 | */ |
228 | static void spid_callback(struct ccw_device *cdev, void *data, int rc) |
229 | { |
230 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
231 | struct ccw_request *req = &cdev->private->req; |
232 | |
233 | switch (rc) { |
234 | case 0: |
235 | sch->vpm |= req->lpm & sch->opm; |
236 | break; |
237 | case -ETIME: |
238 | cdev->private->flags.pgid_unknown = 1; |
239 | cdev->private->path_noirq_mask |= req->lpm; |
240 | break; |
241 | case -EACCES: |
242 | cdev->private->path_notoper_mask |= req->lpm; |
243 | break; |
244 | case -EOPNOTSUPP: |
245 | if (cdev->private->flags.mpath) { |
246 | /* Try without multipathing. */ |
247 | cdev->private->flags.mpath = 0; |
248 | goto out_restart; |
249 | } |
250 | /* Try without pathgrouping. */ |
251 | cdev->private->flags.pgroup = 0; |
252 | goto out_restart; |
253 | default: |
254 | goto err; |
255 | } |
256 | req->lpm >>= 1; |
257 | spid_do(cdev); |
258 | return; |
259 | |
260 | out_restart: |
261 | verify_start(cdev); |
262 | return; |
263 | err: |
264 | verify_done(cdev, rc); |
265 | } |
266 | |
267 | static void spid_start(struct ccw_device *cdev) |
268 | { |
269 | struct ccw_request *req = &cdev->private->req; |
270 | |
271 | /* Initialize request data. */ |
272 | memset(req, 0, sizeof(*req)); |
273 | req->timeout = PGID_TIMEOUT; |
274 | req->maxretries = PGID_RETRIES; |
275 | req->lpm = 0x80; |
276 | req->singlepath = 1; |
277 | req->callback = spid_callback; |
278 | spid_do(cdev); |
279 | } |
280 | |
281 | static int pgid_is_reset(struct pgid *p) |
282 | { |
283 | char *c; |
284 | |
285 | for (c = (char *)p + 1; c < (char *)(p + 1); c++) { |
286 | if (*c != 0) |
287 | return 0; |
288 | } |
289 | return 1; |
290 | } |
291 | |
292 | static int pgid_cmp(struct pgid *p1, struct pgid *p2) |
293 | { |
294 | return memcmp(p: (char *) p1 + 1, q: (char *) p2 + 1, |
295 | size: sizeof(struct pgid) - 1); |
296 | } |
297 | |
298 | /* |
299 | * Determine pathgroup state from PGID data. |
300 | */ |
301 | static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, |
302 | int *mismatch, u8 *reserved, u8 *reset) |
303 | { |
304 | struct pgid *pgid = &cdev->private->dma_area->pgid[0]; |
305 | struct pgid *first = NULL; |
306 | int lpm; |
307 | int i; |
308 | |
309 | *mismatch = 0; |
310 | *reserved = 0; |
311 | *reset = 0; |
312 | for (i = 0, lpm = 0x80; i < 8; i++, pgid++, lpm >>= 1) { |
313 | if ((cdev->private->pgid_valid_mask & lpm) == 0) |
314 | continue; |
315 | if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE) |
316 | *reserved |= lpm; |
317 | if (pgid_is_reset(p: pgid)) { |
318 | *reset |= lpm; |
319 | continue; |
320 | } |
321 | if (!first) { |
322 | first = pgid; |
323 | continue; |
324 | } |
325 | if (pgid_cmp(p1: pgid, p2: first) != 0) |
326 | *mismatch = 1; |
327 | } |
328 | if (!first) |
329 | first = &channel_subsystems[0]->global_pgid; |
330 | *p = first; |
331 | } |
332 | |
333 | static u8 pgid_to_donepm(struct ccw_device *cdev) |
334 | { |
335 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
336 | struct pgid *pgid; |
337 | int i; |
338 | int lpm; |
339 | u8 donepm = 0; |
340 | |
341 | /* Set bits for paths which are already in the target state. */ |
342 | for (i = 0; i < 8; i++) { |
343 | lpm = 0x80 >> i; |
344 | if ((cdev->private->pgid_valid_mask & lpm) == 0) |
345 | continue; |
346 | pgid = &cdev->private->dma_area->pgid[i]; |
347 | if (sch->opm & lpm) { |
348 | if (pgid->inf.ps.state1 != SNID_STATE1_GROUPED) |
349 | continue; |
350 | } else { |
351 | if (pgid->inf.ps.state1 != SNID_STATE1_UNGROUPED) |
352 | continue; |
353 | } |
354 | if (cdev->private->flags.mpath) { |
355 | if (pgid->inf.ps.state3 != SNID_STATE3_MULTI_PATH) |
356 | continue; |
357 | } else { |
358 | if (pgid->inf.ps.state3 != SNID_STATE3_SINGLE_PATH) |
359 | continue; |
360 | } |
361 | donepm |= lpm; |
362 | } |
363 | |
364 | return donepm; |
365 | } |
366 | |
367 | static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid) |
368 | { |
369 | int i; |
370 | |
371 | for (i = 0; i < 8; i++) |
372 | memcpy(&cdev->private->dma_area->pgid[i], pgid, |
373 | sizeof(struct pgid)); |
374 | } |
375 | |
376 | /* |
377 | * Process SENSE PGID data and report result. |
378 | */ |
379 | static void snid_done(struct ccw_device *cdev, int rc) |
380 | { |
381 | struct ccw_dev_id *id = &cdev->private->dev_id; |
382 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
383 | struct pgid *pgid; |
384 | int mismatch = 0; |
385 | u8 reserved = 0; |
386 | u8 reset = 0; |
387 | u8 donepm; |
388 | |
389 | if (rc) |
390 | goto out; |
391 | pgid_analyze(cdev, p: &pgid, mismatch: &mismatch, reserved: &reserved, reset: &reset); |
392 | if (reserved == cdev->private->pgid_valid_mask) |
393 | rc = -EUSERS; |
394 | else if (mismatch) |
395 | rc = -EOPNOTSUPP; |
396 | else { |
397 | donepm = pgid_to_donepm(cdev); |
398 | sch->vpm = donepm & sch->opm; |
399 | cdev->private->pgid_reset_mask |= reset; |
400 | cdev->private->pgid_todo_mask &= |
401 | ~(donepm | cdev->private->path_noirq_mask); |
402 | pgid_fill(cdev, pgid); |
403 | } |
404 | out: |
405 | CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x " |
406 | "todo=%02x mism=%d rsvd=%02x reset=%02x\n" , id->ssid, |
407 | id->devno, rc, cdev->private->pgid_valid_mask, sch->vpm, |
408 | cdev->private->pgid_todo_mask, mismatch, reserved, reset); |
409 | switch (rc) { |
410 | case 0: |
411 | if (cdev->private->flags.pgid_unknown) { |
412 | pgid_wipeout_start(cdev); |
413 | return; |
414 | } |
415 | /* Anything left to do? */ |
416 | if (cdev->private->pgid_todo_mask == 0) { |
417 | verify_done(cdev, rc: sch->vpm == 0 ? -EACCES : 0); |
418 | return; |
419 | } |
420 | /* Perform path-grouping. */ |
421 | spid_start(cdev); |
422 | break; |
423 | case -EOPNOTSUPP: |
424 | /* Path-grouping not supported. */ |
425 | cdev->private->flags.pgroup = 0; |
426 | cdev->private->flags.mpath = 0; |
427 | verify_start(cdev); |
428 | break; |
429 | default: |
430 | verify_done(cdev, rc); |
431 | } |
432 | } |
433 | |
434 | /* |
435 | * Create channel program to perform a SENSE PGID on a single path. |
436 | */ |
437 | static void snid_build_cp(struct ccw_device *cdev) |
438 | { |
439 | struct ccw_request *req = &cdev->private->req; |
440 | struct ccw1 *cp = cdev->private->dma_area->iccws; |
441 | int i = pathmask_to_pos(req->lpm); |
442 | |
443 | /* Channel program setup. */ |
444 | cp->cmd_code = CCW_CMD_SENSE_PGID; |
445 | cp->cda = (u32)virt_to_phys(address: &cdev->private->dma_area->pgid[i]); |
446 | cp->count = sizeof(struct pgid); |
447 | cp->flags = CCW_FLAG_SLI; |
448 | req->cp = cp; |
449 | } |
450 | |
451 | /* |
452 | * Perform SENSE PGID on a single path. |
453 | */ |
454 | static void snid_do(struct ccw_device *cdev) |
455 | { |
456 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
457 | struct ccw_request *req = &cdev->private->req; |
458 | int ret; |
459 | |
460 | req->lpm = lpm_adjust(lpm: req->lpm, mask: sch->schib.pmcw.pam & |
461 | ~cdev->private->path_noirq_mask); |
462 | if (!req->lpm) |
463 | goto out_nopath; |
464 | snid_build_cp(cdev); |
465 | ccw_request_start(cdev); |
466 | return; |
467 | |
468 | out_nopath: |
469 | if (cdev->private->pgid_valid_mask) |
470 | ret = 0; |
471 | else if (cdev->private->path_noirq_mask) |
472 | ret = -ETIME; |
473 | else |
474 | ret = -EACCES; |
475 | snid_done(cdev, rc: ret); |
476 | } |
477 | |
478 | /* |
479 | * Process SENSE PGID request result for single path. |
480 | */ |
481 | static void snid_callback(struct ccw_device *cdev, void *data, int rc) |
482 | { |
483 | struct ccw_request *req = &cdev->private->req; |
484 | |
485 | switch (rc) { |
486 | case 0: |
487 | cdev->private->pgid_valid_mask |= req->lpm; |
488 | break; |
489 | case -ETIME: |
490 | cdev->private->flags.pgid_unknown = 1; |
491 | cdev->private->path_noirq_mask |= req->lpm; |
492 | break; |
493 | case -EACCES: |
494 | cdev->private->path_notoper_mask |= req->lpm; |
495 | break; |
496 | default: |
497 | goto err; |
498 | } |
499 | /* Continue on the next path. */ |
500 | req->lpm >>= 1; |
501 | snid_do(cdev); |
502 | return; |
503 | |
504 | err: |
505 | snid_done(cdev, rc); |
506 | } |
507 | |
508 | /* |
509 | * Perform path verification. |
510 | */ |
511 | static void verify_start(struct ccw_device *cdev) |
512 | { |
513 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
514 | struct ccw_request *req = &cdev->private->req; |
515 | struct ccw_dev_id *devid = &cdev->private->dev_id; |
516 | |
517 | sch->vpm = 0; |
518 | sch->lpm = sch->schib.pmcw.pam; |
519 | |
520 | /* Initialize PGID data. */ |
521 | memset(cdev->private->dma_area->pgid, 0, |
522 | sizeof(cdev->private->dma_area->pgid)); |
523 | cdev->private->pgid_valid_mask = 0; |
524 | cdev->private->pgid_todo_mask = sch->schib.pmcw.pam; |
525 | cdev->private->path_notoper_mask = 0; |
526 | |
527 | /* Initialize request data. */ |
528 | memset(req, 0, sizeof(*req)); |
529 | req->timeout = PGID_TIMEOUT; |
530 | req->maxretries = PGID_RETRIES; |
531 | req->lpm = 0x80; |
532 | req->singlepath = 1; |
533 | if (cdev->private->flags.pgroup) { |
534 | CIO_TRACE_EVENT(4, "snid" ); |
535 | CIO_HEX_EVENT(4, devid, sizeof(*devid)); |
536 | req->callback = snid_callback; |
537 | snid_do(cdev); |
538 | } else { |
539 | CIO_TRACE_EVENT(4, "nop" ); |
540 | CIO_HEX_EVENT(4, devid, sizeof(*devid)); |
541 | req->filter = nop_filter; |
542 | req->callback = nop_callback; |
543 | nop_do(cdev); |
544 | } |
545 | } |
546 | |
547 | /** |
548 | * ccw_device_verify_start - perform path verification |
549 | * @cdev: ccw device |
550 | * |
551 | * Perform an I/O on each available channel path to @cdev to determine which |
552 | * paths are operational. The resulting path mask is stored in sch->vpm. |
553 | * If device options specify pathgrouping, establish a pathgroup for the |
554 | * operational paths. When finished, call ccw_device_verify_done with a |
555 | * return code specifying the result. |
556 | */ |
557 | void ccw_device_verify_start(struct ccw_device *cdev) |
558 | { |
559 | CIO_TRACE_EVENT(4, "vrfy" ); |
560 | CIO_HEX_EVENT(level: 4, data: &cdev->private->dev_id, length: sizeof(cdev->private->dev_id)); |
561 | /* |
562 | * Initialize pathgroup and multipath state with target values. |
563 | * They may change in the course of path verification. |
564 | */ |
565 | cdev->private->flags.pgroup = cdev->private->options.pgroup; |
566 | cdev->private->flags.mpath = cdev->private->options.mpath; |
567 | cdev->private->flags.doverify = 0; |
568 | cdev->private->path_noirq_mask = 0; |
569 | verify_start(cdev); |
570 | } |
571 | |
572 | /* |
573 | * Process disband SET PGID request result. |
574 | */ |
575 | static void disband_callback(struct ccw_device *cdev, void *data, int rc) |
576 | { |
577 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
578 | struct ccw_dev_id *id = &cdev->private->dev_id; |
579 | |
580 | if (rc) |
581 | goto out; |
582 | /* Ensure consistent multipathing state at device and channel. */ |
583 | cdev->private->flags.mpath = 0; |
584 | if (sch->config.mp) { |
585 | sch->config.mp = 0; |
586 | rc = cio_commit_config(sch); |
587 | } |
588 | out: |
589 | CIO_MSG_EVENT(0, "disb: device 0.%x.%04x: rc=%d\n" , id->ssid, id->devno, |
590 | rc); |
591 | ccw_device_disband_done(cdev, rc); |
592 | } |
593 | |
594 | /** |
595 | * ccw_device_disband_start - disband pathgroup |
596 | * @cdev: ccw device |
597 | * |
598 | * Execute a SET PGID channel program on @cdev to disband a previously |
599 | * established pathgroup. When finished, call ccw_device_disband_done with |
600 | * a return code specifying the result. |
601 | */ |
602 | void ccw_device_disband_start(struct ccw_device *cdev) |
603 | { |
604 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
605 | struct ccw_request *req = &cdev->private->req; |
606 | u8 fn; |
607 | |
608 | CIO_TRACE_EVENT(4, "disb" ); |
609 | CIO_HEX_EVENT(level: 4, data: &cdev->private->dev_id, length: sizeof(cdev->private->dev_id)); |
610 | /* Request setup. */ |
611 | memset(req, 0, sizeof(*req)); |
612 | req->timeout = PGID_TIMEOUT; |
613 | req->maxretries = PGID_RETRIES; |
614 | req->lpm = sch->schib.pmcw.pam & sch->opm; |
615 | req->singlepath = 1; |
616 | req->callback = disband_callback; |
617 | fn = SPID_FUNC_DISBAND; |
618 | if (cdev->private->flags.mpath) |
619 | fn |= SPID_FUNC_MULTI_PATH; |
620 | spid_build_cp(cdev, fn); |
621 | ccw_request_start(cdev); |
622 | } |
623 | |
624 | struct stlck_data { |
625 | struct completion done; |
626 | int rc; |
627 | }; |
628 | |
629 | static void stlck_build_cp(struct ccw_device *cdev, void *buf1, void *buf2) |
630 | { |
631 | struct ccw_request *req = &cdev->private->req; |
632 | struct ccw1 *cp = cdev->private->dma_area->iccws; |
633 | |
634 | cp[0].cmd_code = CCW_CMD_STLCK; |
635 | cp[0].cda = (u32)virt_to_phys(buf1); |
636 | cp[0].count = 32; |
637 | cp[0].flags = CCW_FLAG_CC; |
638 | cp[1].cmd_code = CCW_CMD_RELEASE; |
639 | cp[1].cda = (u32)virt_to_phys(buf2); |
640 | cp[1].count = 32; |
641 | cp[1].flags = 0; |
642 | req->cp = cp; |
643 | } |
644 | |
645 | static void stlck_callback(struct ccw_device *cdev, void *data, int rc) |
646 | { |
647 | struct stlck_data *sdata = data; |
648 | |
649 | sdata->rc = rc; |
650 | complete(&sdata->done); |
651 | } |
652 | |
653 | /** |
654 | * ccw_device_stlck_start - perform unconditional release |
655 | * @cdev: ccw device |
656 | * @data: data pointer to be passed to ccw_device_stlck_done |
657 | * @buf1: data pointer used in channel program |
658 | * @buf2: data pointer used in channel program |
659 | * |
660 | * Execute a channel program on @cdev to release an existing PGID reservation. |
661 | */ |
662 | static void ccw_device_stlck_start(struct ccw_device *cdev, void *data, |
663 | void *buf1, void *buf2) |
664 | { |
665 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
666 | struct ccw_request *req = &cdev->private->req; |
667 | |
668 | CIO_TRACE_EVENT(4, "stlck" ); |
669 | CIO_HEX_EVENT(level: 4, data: &cdev->private->dev_id, length: sizeof(cdev->private->dev_id)); |
670 | /* Request setup. */ |
671 | memset(req, 0, sizeof(*req)); |
672 | req->timeout = PGID_TIMEOUT; |
673 | req->maxretries = PGID_RETRIES; |
674 | req->lpm = sch->schib.pmcw.pam & sch->opm; |
675 | req->data = data; |
676 | req->callback = stlck_callback; |
677 | stlck_build_cp(cdev, buf1, buf2); |
678 | ccw_request_start(cdev); |
679 | } |
680 | |
681 | /* |
682 | * Perform unconditional reserve + release. |
683 | */ |
684 | int ccw_device_stlck(struct ccw_device *cdev) |
685 | { |
686 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
687 | struct stlck_data data; |
688 | u8 *buffer; |
689 | int rc; |
690 | |
691 | /* Check if steal lock operation is valid for this device. */ |
692 | if (cdev->drv) { |
693 | if (!cdev->private->options.force) |
694 | return -EINVAL; |
695 | } |
696 | buffer = kzalloc(size: 64, GFP_DMA | GFP_KERNEL); |
697 | if (!buffer) |
698 | return -ENOMEM; |
699 | init_completion(x: &data.done); |
700 | data.rc = -EIO; |
701 | spin_lock_irq(lock: sch->lock); |
702 | rc = cio_enable_subchannel(sch, (u32)virt_to_phys(address: sch)); |
703 | if (rc) |
704 | goto out_unlock; |
705 | /* Perform operation. */ |
706 | cdev->private->state = DEV_STATE_STEAL_LOCK; |
707 | ccw_device_stlck_start(cdev, data: &data, buf1: &buffer[0], buf2: &buffer[32]); |
708 | spin_unlock_irq(lock: sch->lock); |
709 | /* Wait for operation to finish. */ |
710 | if (wait_for_completion_interruptible(x: &data.done)) { |
711 | /* Got a signal. */ |
712 | spin_lock_irq(lock: sch->lock); |
713 | ccw_request_cancel(cdev); |
714 | spin_unlock_irq(lock: sch->lock); |
715 | wait_for_completion(&data.done); |
716 | } |
717 | rc = data.rc; |
718 | /* Check results. */ |
719 | spin_lock_irq(lock: sch->lock); |
720 | cio_disable_subchannel(sch); |
721 | cdev->private->state = DEV_STATE_BOXED; |
722 | out_unlock: |
723 | spin_unlock_irq(lock: sch->lock); |
724 | kfree(objp: buffer); |
725 | |
726 | return rc; |
727 | } |
728 | |