]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * drivers/s390/cio/device_pgid.c | |
3 | * | |
4 | * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, | |
5 | * IBM Corporation | |
4ce3b30c | 6 | * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) |
1da177e4 LT |
7 | * Martin Schwidefsky (schwidefsky@de.ibm.com) |
8 | * | |
9 | * Path Group ID functions. | |
10 | */ | |
11 | ||
1da177e4 LT |
12 | #include <linux/module.h> |
13 | #include <linux/init.h> | |
14 | ||
15 | #include <asm/ccwdev.h> | |
16 | #include <asm/cio.h> | |
17 | #include <asm/delay.h> | |
18 | #include <asm/lowcore.h> | |
19 | ||
20 | #include "cio.h" | |
21 | #include "cio_debug.h" | |
22 | #include "css.h" | |
23 | #include "device.h" | |
6810a2bc | 24 | #include "ioasm.h" |
1da177e4 | 25 | |
7b7db1b5 SB |
26 | /* |
27 | * Helper function called from interrupt context to decide whether an | |
28 | * operation should be tried again. | |
29 | */ | |
30 | static int __ccw_device_should_retry(struct scsw *scsw) | |
31 | { | |
32 | /* CC is only valid if start function bit is set. */ | |
33 | if ((scsw->fctl & SCSW_FCTL_START_FUNC) && scsw->cc == 1) | |
34 | return 1; | |
35 | /* No more activity. For sense and set PGID we stubbornly try again. */ | |
36 | if (!scsw->actl) | |
37 | return 1; | |
38 | return 0; | |
39 | } | |
40 | ||
1da177e4 LT |
41 | /* |
42 | * Start Sense Path Group ID helper function. Used in ccw_device_recog | |
43 | * and ccw_device_sense_pgid. | |
44 | */ | |
45 | static int | |
46 | __ccw_device_sense_pgid_start(struct ccw_device *cdev) | |
47 | { | |
48 | struct subchannel *sch; | |
49 | struct ccw1 *ccw; | |
50 | int ret; | |
7e560814 | 51 | int i; |
1da177e4 LT |
52 | |
53 | sch = to_subchannel(cdev->dev.parent); | |
7e560814 CH |
54 | /* Return if we already checked on all paths. */ |
55 | if (cdev->private->imask == 0) | |
56 | return (sch->lpm == 0) ? -ENODEV : -EACCES; | |
57 | i = 8 - ffs(cdev->private->imask); | |
58 | ||
1da177e4 LT |
59 | /* Setup sense path group id channel program. */ |
60 | ccw = cdev->private->iccws; | |
61 | ccw->cmd_code = CCW_CMD_SENSE_PGID; | |
1da177e4 LT |
62 | ccw->count = sizeof (struct pgid); |
63 | ccw->flags = CCW_FLAG_SLI; | |
64 | ||
65 | /* Reset device status. */ | |
66 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | |
67 | /* Try on every path. */ | |
68 | ret = -ENODEV; | |
69 | while (cdev->private->imask != 0) { | |
70 | /* Try every path multiple times. */ | |
7e560814 | 71 | ccw->cda = (__u32) __pa (&cdev->private->pgid[i]); |
1da177e4 LT |
72 | if (cdev->private->iretry > 0) { |
73 | cdev->private->iretry--; | |
74 | ret = cio_start (sch, cdev->private->iccws, | |
75 | cdev->private->imask); | |
76 | /* ret is 0, -EBUSY, -EACCES or -ENODEV */ | |
77 | if (ret != -EACCES) | |
78 | return ret; | |
79 | CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel " | |
fb6958a5 | 80 | "0.%x.%04x, lpm %02X, became 'not " |
1da177e4 | 81 | "operational'\n", |
fb6958a5 CH |
82 | cdev->private->devno, sch->schid.ssid, |
83 | sch->schid.sch_no, cdev->private->imask); | |
1da177e4 LT |
84 | |
85 | } | |
86 | cdev->private->imask >>= 1; | |
87 | cdev->private->iretry = 5; | |
7e560814 | 88 | i++; |
1da177e4 | 89 | } |
7e560814 | 90 | |
1da177e4 LT |
91 | return ret; |
92 | } | |
93 | ||
94 | void | |
95 | ccw_device_sense_pgid_start(struct ccw_device *cdev) | |
96 | { | |
97 | int ret; | |
98 | ||
99 | cdev->private->state = DEV_STATE_SENSE_PGID; | |
100 | cdev->private->imask = 0x80; | |
101 | cdev->private->iretry = 5; | |
7e560814 | 102 | memset (&cdev->private->pgid, 0, sizeof (cdev->private->pgid)); |
1da177e4 LT |
103 | ret = __ccw_device_sense_pgid_start(cdev); |
104 | if (ret && ret != -EBUSY) | |
105 | ccw_device_sense_pgid_done(cdev, ret); | |
106 | } | |
107 | ||
108 | /* | |
109 | * Called from interrupt context to check if a valid answer | |
110 | * to Sense Path Group ID was received. | |
111 | */ | |
112 | static int | |
113 | __ccw_device_check_sense_pgid(struct ccw_device *cdev) | |
114 | { | |
115 | struct subchannel *sch; | |
116 | struct irb *irb; | |
7e560814 | 117 | int i; |
1da177e4 LT |
118 | |
119 | sch = to_subchannel(cdev->dev.parent); | |
120 | irb = &cdev->private->irb; | |
121 | if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) | |
122 | return -ETIME; | |
123 | if (irb->esw.esw0.erw.cons && | |
124 | (irb->ecw[0]&(SNS0_CMD_REJECT|SNS0_INTERVENTION_REQ))) { | |
125 | /* | |
126 | * If the device doesn't support the Sense Path Group ID | |
127 | * command further retries wouldn't help ... | |
128 | */ | |
129 | return -EOPNOTSUPP; | |
130 | } | |
131 | if (irb->esw.esw0.erw.cons) { | |
fb6958a5 | 132 | CIO_MSG_EVENT(2, "SNID - device 0.%x.%04x, unit check, " |
1da177e4 LT |
133 | "lpum %02X, cnt %02d, sns : " |
134 | "%02X%02X%02X%02X %02X%02X%02X%02X ...\n", | |
fb6958a5 | 135 | cdev->private->ssid, cdev->private->devno, |
1da177e4 LT |
136 | irb->esw.esw0.sublog.lpum, |
137 | irb->esw.esw0.erw.scnt, | |
138 | irb->ecw[0], irb->ecw[1], | |
139 | irb->ecw[2], irb->ecw[3], | |
140 | irb->ecw[4], irb->ecw[5], | |
141 | irb->ecw[6], irb->ecw[7]); | |
142 | return -EAGAIN; | |
143 | } | |
144 | if (irb->scsw.cc == 3) { | |
fb6958a5 CH |
145 | CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x," |
146 | " lpm %02X, became 'not operational'\n", | |
147 | cdev->private->devno, sch->schid.ssid, | |
148 | sch->schid.sch_no, sch->orb.lpm); | |
1da177e4 LT |
149 | return -EACCES; |
150 | } | |
7e560814 CH |
151 | i = 8 - ffs(cdev->private->imask); |
152 | if (cdev->private->pgid[i].inf.ps.state2 == SNID_STATE2_RESVD_ELSE) { | |
fb6958a5 | 153 | CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x " |
1da177e4 | 154 | "is reserved by someone else\n", |
fb6958a5 CH |
155 | cdev->private->devno, sch->schid.ssid, |
156 | sch->schid.sch_no); | |
1da177e4 LT |
157 | return -EUSERS; |
158 | } | |
159 | return 0; | |
160 | } | |
161 | ||
162 | /* | |
163 | * Got interrupt for Sense Path Group ID. | |
164 | */ | |
165 | void | |
166 | ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event) | |
167 | { | |
168 | struct subchannel *sch; | |
169 | struct irb *irb; | |
170 | int ret; | |
171 | ||
172 | irb = (struct irb *) __LC_IRB; | |
7b7db1b5 | 173 | |
1da177e4 LT |
174 | if (irb->scsw.stctl == |
175 | (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { | |
7b7db1b5 | 176 | if (__ccw_device_should_retry(&irb->scsw)) { |
1da177e4 LT |
177 | ret = __ccw_device_sense_pgid_start(cdev); |
178 | if (ret && ret != -EBUSY) | |
179 | ccw_device_sense_pgid_done(cdev, ret); | |
180 | } | |
181 | return; | |
182 | } | |
183 | if (ccw_device_accumulate_and_sense(cdev, irb) != 0) | |
184 | return; | |
185 | sch = to_subchannel(cdev->dev.parent); | |
186 | ret = __ccw_device_check_sense_pgid(cdev); | |
187 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | |
188 | switch (ret) { | |
189 | /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */ | |
1da177e4 LT |
190 | case -EOPNOTSUPP: /* Sense Path Group ID not supported */ |
191 | ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP); | |
192 | break; | |
193 | case -ETIME: /* Sense path group id stopped by timeout. */ | |
194 | ccw_device_sense_pgid_done(cdev, -ETIME); | |
195 | break; | |
196 | case -EACCES: /* channel is not operational. */ | |
197 | sch->lpm &= ~cdev->private->imask; | |
7e560814 CH |
198 | /* Fall through. */ |
199 | case 0: /* Sense Path Group ID successful. */ | |
1da177e4 LT |
200 | cdev->private->imask >>= 1; |
201 | cdev->private->iretry = 5; | |
202 | /* Fall through. */ | |
203 | case -EAGAIN: /* Try again. */ | |
204 | ret = __ccw_device_sense_pgid_start(cdev); | |
205 | if (ret != 0 && ret != -EBUSY) | |
7e560814 | 206 | ccw_device_sense_pgid_done(cdev, ret); |
1da177e4 LT |
207 | break; |
208 | case -EUSERS: /* device is reserved for someone else. */ | |
209 | ccw_device_sense_pgid_done(cdev, -EUSERS); | |
210 | break; | |
211 | } | |
212 | } | |
213 | ||
214 | /* | |
215 | * Path Group ID helper function. | |
216 | */ | |
217 | static int | |
218 | __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func) | |
219 | { | |
220 | struct subchannel *sch; | |
221 | struct ccw1 *ccw; | |
222 | int ret; | |
223 | ||
224 | sch = to_subchannel(cdev->dev.parent); | |
225 | ||
226 | /* Setup sense path group id channel program. */ | |
7e560814 | 227 | cdev->private->pgid[0].inf.fc = func; |
1da177e4 LT |
228 | ccw = cdev->private->iccws; |
229 | if (!cdev->private->flags.pgid_single) { | |
7e560814 | 230 | cdev->private->pgid[0].inf.fc |= SPID_FUNC_MULTI_PATH; |
1da177e4 LT |
231 | ccw->cmd_code = CCW_CMD_SUSPEND_RECONN; |
232 | ccw->cda = 0; | |
233 | ccw->count = 0; | |
234 | ccw->flags = CCW_FLAG_SLI | CCW_FLAG_CC; | |
235 | ccw++; | |
236 | } else | |
7e560814 | 237 | cdev->private->pgid[0].inf.fc |= SPID_FUNC_SINGLE_PATH; |
1da177e4 LT |
238 | |
239 | ccw->cmd_code = CCW_CMD_SET_PGID; | |
7e560814 | 240 | ccw->cda = (__u32) __pa (&cdev->private->pgid[0]); |
1da177e4 LT |
241 | ccw->count = sizeof (struct pgid); |
242 | ccw->flags = CCW_FLAG_SLI; | |
243 | ||
244 | /* Reset device status. */ | |
245 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | |
246 | ||
247 | /* Try multiple times. */ | |
28bdc6f6 | 248 | ret = -EACCES; |
1da177e4 LT |
249 | if (cdev->private->iretry > 0) { |
250 | cdev->private->iretry--; | |
251 | ret = cio_start (sch, cdev->private->iccws, | |
252 | cdev->private->imask); | |
28bdc6f6 PO |
253 | /* We expect an interrupt in case of success or busy |
254 | * indication. */ | |
255 | if ((ret == 0) || (ret == -EBUSY)) | |
1da177e4 LT |
256 | return ret; |
257 | } | |
28bdc6f6 | 258 | /* PGID command failed on this path. */ |
1da177e4 | 259 | CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel " |
fb6958a5 CH |
260 | "0.%x.%04x, lpm %02X, became 'not operational'\n", |
261 | cdev->private->devno, sch->schid.ssid, | |
262 | sch->schid.sch_no, cdev->private->imask); | |
1da177e4 LT |
263 | return ret; |
264 | } | |
265 | ||
7e560814 CH |
266 | /* |
267 | * Helper function to send a nop ccw down a path. | |
268 | */ | |
269 | static int __ccw_device_do_nop(struct ccw_device *cdev) | |
270 | { | |
271 | struct subchannel *sch; | |
272 | struct ccw1 *ccw; | |
273 | int ret; | |
274 | ||
275 | sch = to_subchannel(cdev->dev.parent); | |
276 | ||
277 | /* Setup nop channel program. */ | |
278 | ccw = cdev->private->iccws; | |
279 | ccw->cmd_code = CCW_CMD_NOOP; | |
280 | ccw->cda = 0; | |
281 | ccw->count = 0; | |
282 | ccw->flags = CCW_FLAG_SLI; | |
283 | ||
284 | /* Reset device status. */ | |
285 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | |
286 | ||
287 | /* Try multiple times. */ | |
28bdc6f6 | 288 | ret = -EACCES; |
7e560814 CH |
289 | if (cdev->private->iretry > 0) { |
290 | cdev->private->iretry--; | |
291 | ret = cio_start (sch, cdev->private->iccws, | |
292 | cdev->private->imask); | |
28bdc6f6 PO |
293 | /* We expect an interrupt in case of success or busy |
294 | * indication. */ | |
295 | if ((ret == 0) || (ret == -EBUSY)) | |
7e560814 CH |
296 | return ret; |
297 | } | |
28bdc6f6 | 298 | /* nop command failed on this path. */ |
7e560814 CH |
299 | CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel " |
300 | "0.%x.%04x, lpm %02X, became 'not operational'\n", | |
301 | cdev->private->devno, sch->schid.ssid, | |
302 | sch->schid.sch_no, cdev->private->imask); | |
303 | return ret; | |
304 | } | |
305 | ||
306 | ||
1da177e4 LT |
307 | /* |
308 | * Called from interrupt context to check if a valid answer | |
309 | * to Set Path Group ID was received. | |
310 | */ | |
311 | static int | |
312 | __ccw_device_check_pgid(struct ccw_device *cdev) | |
313 | { | |
314 | struct subchannel *sch; | |
315 | struct irb *irb; | |
316 | ||
317 | sch = to_subchannel(cdev->dev.parent); | |
318 | irb = &cdev->private->irb; | |
319 | if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) | |
320 | return -ETIME; | |
321 | if (irb->esw.esw0.erw.cons) { | |
322 | if (irb->ecw[0] & SNS0_CMD_REJECT) | |
323 | return -EOPNOTSUPP; | |
324 | /* Hmm, whatever happened, try again. */ | |
fb6958a5 CH |
325 | CIO_MSG_EVENT(2, "SPID - device 0.%x.%04x, unit check, " |
326 | "cnt %02d, " | |
1da177e4 | 327 | "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n", |
fb6958a5 | 328 | cdev->private->ssid, |
1da177e4 LT |
329 | cdev->private->devno, irb->esw.esw0.erw.scnt, |
330 | irb->ecw[0], irb->ecw[1], | |
331 | irb->ecw[2], irb->ecw[3], | |
332 | irb->ecw[4], irb->ecw[5], | |
333 | irb->ecw[6], irb->ecw[7]); | |
334 | return -EAGAIN; | |
335 | } | |
336 | if (irb->scsw.cc == 3) { | |
fb6958a5 CH |
337 | CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel 0.%x.%04x," |
338 | " lpm %02X, became 'not operational'\n", | |
339 | cdev->private->devno, sch->schid.ssid, | |
340 | sch->schid.sch_no, cdev->private->imask); | |
1da177e4 LT |
341 | return -EACCES; |
342 | } | |
343 | return 0; | |
344 | } | |
345 | ||
7e560814 CH |
346 | /* |
347 | * Called from interrupt context to check the path status after a nop has | |
348 | * been send. | |
349 | */ | |
350 | static int __ccw_device_check_nop(struct ccw_device *cdev) | |
351 | { | |
352 | struct subchannel *sch; | |
353 | struct irb *irb; | |
354 | ||
355 | sch = to_subchannel(cdev->dev.parent); | |
356 | irb = &cdev->private->irb; | |
357 | if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) | |
358 | return -ETIME; | |
359 | if (irb->scsw.cc == 3) { | |
360 | CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel 0.%x.%04x," | |
361 | " lpm %02X, became 'not operational'\n", | |
362 | cdev->private->devno, sch->schid.ssid, | |
363 | sch->schid.sch_no, cdev->private->imask); | |
364 | return -EACCES; | |
365 | } | |
366 | return 0; | |
367 | } | |
368 | ||
1da177e4 LT |
369 | static void |
370 | __ccw_device_verify_start(struct ccw_device *cdev) | |
371 | { | |
372 | struct subchannel *sch; | |
28bdc6f6 | 373 | __u8 func; |
1da177e4 LT |
374 | int ret; |
375 | ||
376 | sch = to_subchannel(cdev->dev.parent); | |
28bdc6f6 PO |
377 | /* Repeat for all paths. */ |
378 | for (; cdev->private->imask; cdev->private->imask >>= 1, | |
379 | cdev->private->iretry = 5) { | |
380 | if ((cdev->private->imask & sch->schib.pmcw.pam) == 0) | |
381 | /* Path not available, try next. */ | |
382 | continue; | |
7e560814 | 383 | if (cdev->private->options.pgroup) { |
28bdc6f6 PO |
384 | if (sch->opm & cdev->private->imask) |
385 | func = SPID_FUNC_ESTABLISH; | |
386 | else | |
387 | func = SPID_FUNC_RESIGN; | |
7e560814 CH |
388 | ret = __ccw_device_do_pgid(cdev, func); |
389 | } else | |
390 | ret = __ccw_device_do_nop(cdev); | |
28bdc6f6 PO |
391 | /* We expect an interrupt in case of success or busy |
392 | * indication. */ | |
1da177e4 LT |
393 | if (ret == 0 || ret == -EBUSY) |
394 | return; | |
28bdc6f6 | 395 | /* Permanent path failure, try next. */ |
1da177e4 | 396 | } |
28bdc6f6 PO |
397 | /* Done with all paths. */ |
398 | ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -ENODEV); | |
1da177e4 LT |
399 | } |
400 | ||
401 | /* | |
402 | * Got interrupt for Set Path Group ID. | |
403 | */ | |
404 | void | |
405 | ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event) | |
406 | { | |
407 | struct subchannel *sch; | |
408 | struct irb *irb; | |
409 | int ret; | |
410 | ||
411 | irb = (struct irb *) __LC_IRB; | |
7b7db1b5 | 412 | |
1da177e4 LT |
413 | if (irb->scsw.stctl == |
414 | (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { | |
7b7db1b5 | 415 | if (__ccw_device_should_retry(&irb->scsw)) |
1da177e4 LT |
416 | __ccw_device_verify_start(cdev); |
417 | return; | |
418 | } | |
419 | if (ccw_device_accumulate_and_sense(cdev, irb) != 0) | |
420 | return; | |
421 | sch = to_subchannel(cdev->dev.parent); | |
7e560814 CH |
422 | if (cdev->private->options.pgroup) |
423 | ret = __ccw_device_check_pgid(cdev); | |
424 | else | |
425 | ret = __ccw_device_check_nop(cdev); | |
1da177e4 | 426 | memset(&cdev->private->irb, 0, sizeof(struct irb)); |
28bdc6f6 | 427 | |
1da177e4 LT |
428 | switch (ret) { |
429 | /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ | |
430 | case 0: | |
28bdc6f6 PO |
431 | /* Path verification ccw finished successfully, update lpm. */ |
432 | sch->vpm |= sch->opm & cdev->private->imask; | |
433 | /* Go on with next path. */ | |
434 | cdev->private->imask >>= 1; | |
1da177e4 LT |
435 | cdev->private->iretry = 5; |
436 | __ccw_device_verify_start(cdev); | |
437 | break; | |
438 | case -EOPNOTSUPP: | |
439 | /* | |
440 | * One of those strange devices which claim to be able | |
441 | * to do multipathing but not for Set Path Group ID. | |
442 | */ | |
7e560814 CH |
443 | if (cdev->private->flags.pgid_single) |
444 | cdev->private->options.pgroup = 0; | |
445 | else | |
446 | cdev->private->flags.pgid_single = 1; | |
28bdc6f6 PO |
447 | /* Retry */ |
448 | sch->vpm = 0; | |
449 | cdev->private->imask = 0x80; | |
450 | cdev->private->iretry = 5; | |
1da177e4 LT |
451 | /* fall through. */ |
452 | case -EAGAIN: /* Try again. */ | |
453 | __ccw_device_verify_start(cdev); | |
454 | break; | |
455 | case -ETIME: /* Set path group id stopped by timeout. */ | |
456 | ccw_device_verify_done(cdev, -ETIME); | |
457 | break; | |
458 | case -EACCES: /* channel is not operational. */ | |
28bdc6f6 | 459 | cdev->private->imask >>= 1; |
1da177e4 LT |
460 | cdev->private->iretry = 5; |
461 | __ccw_device_verify_start(cdev); | |
462 | break; | |
463 | } | |
464 | } | |
465 | ||
466 | void | |
467 | ccw_device_verify_start(struct ccw_device *cdev) | |
468 | { | |
6810a2bc CH |
469 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
470 | ||
1da177e4 | 471 | cdev->private->flags.pgid_single = 0; |
28bdc6f6 | 472 | cdev->private->imask = 0x80; |
1da177e4 | 473 | cdev->private->iretry = 5; |
28bdc6f6 PO |
474 | |
475 | /* Start with empty vpm. */ | |
476 | sch->vpm = 0; | |
477 | ||
478 | /* Get current pam. */ | |
a8237fc4 | 479 | if (stsch(sch->schid, &sch->schib)) { |
6810a2bc CH |
480 | ccw_device_verify_done(cdev, -ENODEV); |
481 | return; | |
482 | } | |
1da177e4 LT |
483 | __ccw_device_verify_start(cdev); |
484 | } | |
485 | ||
486 | static void | |
487 | __ccw_device_disband_start(struct ccw_device *cdev) | |
488 | { | |
489 | struct subchannel *sch; | |
490 | int ret; | |
491 | ||
492 | sch = to_subchannel(cdev->dev.parent); | |
493 | while (cdev->private->imask != 0) { | |
494 | if (sch->lpm & cdev->private->imask) { | |
495 | ret = __ccw_device_do_pgid(cdev, SPID_FUNC_DISBAND); | |
496 | if (ret == 0) | |
497 | return; | |
498 | } | |
499 | cdev->private->iretry = 5; | |
500 | cdev->private->imask >>= 1; | |
501 | } | |
6d751c43 | 502 | ccw_device_disband_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV); |
1da177e4 LT |
503 | } |
504 | ||
505 | /* | |
506 | * Got interrupt for Unset Path Group ID. | |
507 | */ | |
508 | void | |
509 | ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event) | |
510 | { | |
511 | struct subchannel *sch; | |
512 | struct irb *irb; | |
513 | int ret; | |
514 | ||
515 | irb = (struct irb *) __LC_IRB; | |
7b7db1b5 | 516 | |
1da177e4 LT |
517 | if (irb->scsw.stctl == |
518 | (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { | |
7b7db1b5 | 519 | if (__ccw_device_should_retry(&irb->scsw)) |
1da177e4 LT |
520 | __ccw_device_disband_start(cdev); |
521 | return; | |
522 | } | |
523 | if (ccw_device_accumulate_and_sense(cdev, irb) != 0) | |
524 | return; | |
525 | sch = to_subchannel(cdev->dev.parent); | |
526 | ret = __ccw_device_check_pgid(cdev); | |
527 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | |
528 | switch (ret) { | |
529 | /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ | |
530 | case 0: /* disband successful. */ | |
1da177e4 LT |
531 | ccw_device_disband_done(cdev, ret); |
532 | break; | |
533 | case -EOPNOTSUPP: | |
534 | /* | |
535 | * One of those strange devices which claim to be able | |
536 | * to do multipathing but not for Unset Path Group ID. | |
537 | */ | |
538 | cdev->private->flags.pgid_single = 1; | |
539 | /* fall through. */ | |
540 | case -EAGAIN: /* Try again. */ | |
541 | __ccw_device_disband_start(cdev); | |
542 | break; | |
543 | case -ETIME: /* Set path group id stopped by timeout. */ | |
544 | ccw_device_disband_done(cdev, -ETIME); | |
545 | break; | |
546 | case -EACCES: /* channel is not operational. */ | |
547 | cdev->private->imask >>= 1; | |
548 | cdev->private->iretry = 5; | |
549 | __ccw_device_disband_start(cdev); | |
550 | break; | |
551 | } | |
552 | } | |
553 | ||
554 | void | |
555 | ccw_device_disband_start(struct ccw_device *cdev) | |
556 | { | |
557 | cdev->private->flags.pgid_single = 0; | |
558 | cdev->private->iretry = 5; | |
559 | cdev->private->imask = 0x80; | |
560 | __ccw_device_disband_start(cdev); | |
561 | } |