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