Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[pandora-kernel.git] / 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  * 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
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;
51         int i;
52
53         sch = to_subchannel(cdev->dev.parent);
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
59         /* Setup sense path group id channel program. */
60         ccw = cdev->private->iccws;
61         ccw->cmd_code = CCW_CMD_SENSE_PGID;
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. */
71                 ccw->cda = (__u32) __pa (&cdev->private->pgid[i]);
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 "
80                                       "0.%x.%04x, lpm %02X, became 'not "
81                                       "operational'\n",
82                                       cdev->private->devno, sch->schid.ssid,
83                                       sch->schid.sch_no, cdev->private->imask);
84
85                 }
86                 cdev->private->imask >>= 1;
87                 cdev->private->iretry = 5;
88                 i++;
89         }
90
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;
102         memset (&cdev->private->pgid, 0, sizeof (cdev->private->pgid));
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;
117         int i;
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) {
132                 CIO_MSG_EVENT(2, "SNID - device 0.%x.%04x, unit check, "
133                               "lpum %02X, cnt %02d, sns : "
134                               "%02X%02X%02X%02X %02X%02X%02X%02X ...\n",
135                               cdev->private->ssid, cdev->private->devno,
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) {
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);
149                 return -EACCES;
150         }
151         i = 8 - ffs(cdev->private->imask);
152         if (cdev->private->pgid[i].inf.ps.state2 == SNID_STATE2_RESVD_ELSE) {
153                 CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x "
154                               "is reserved by someone else\n",
155                               cdev->private->devno, sch->schid.ssid,
156                               sch->schid.sch_no);
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;
173
174         if (irb->scsw.stctl ==
175             (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
176                 if (__ccw_device_should_retry(&irb->scsw)) {
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 */
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;
198                 /* Fall through. */
199         case 0:                 /* Sense Path Group ID successful. */
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)
206                         ccw_device_sense_pgid_done(cdev, ret);
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. */
227         cdev->private->pgid[0].inf.fc = func;
228         ccw = cdev->private->iccws;
229         if (!cdev->private->flags.pgid_single) {
230                 cdev->private->pgid[0].inf.fc |= SPID_FUNC_MULTI_PATH;
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
237                 cdev->private->pgid[0].inf.fc |= SPID_FUNC_SINGLE_PATH;
238
239         ccw->cmd_code = CCW_CMD_SET_PGID;
240         ccw->cda = (__u32) __pa (&cdev->private->pgid[0]);
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. */
248         ret = -ENODEV;
249         if (cdev->private->iretry > 0) {
250                 cdev->private->iretry--;
251                 ret = cio_start (sch, cdev->private->iccws,
252                                  cdev->private->imask);
253                 /* ret is 0, -EBUSY, -EACCES or -ENODEV */
254                 if ((ret != -EACCES) && (ret != -ENODEV))
255                         return ret;
256         }
257         /* PGID command failed on this path. Switch it off. */
258         sch->lpm &= ~cdev->private->imask;
259         sch->vpm &= ~cdev->private->imask;
260         CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel "
261                       "0.%x.%04x, lpm %02X, became 'not operational'\n",
262                       cdev->private->devno, sch->schid.ssid,
263                       sch->schid.sch_no, cdev->private->imask);
264         return ret;
265 }
266
267 /*
268  * Helper function to send a nop ccw down a path.
269  */
270 static int __ccw_device_do_nop(struct ccw_device *cdev)
271 {
272         struct subchannel *sch;
273         struct ccw1 *ccw;
274         int ret;
275
276         sch = to_subchannel(cdev->dev.parent);
277
278         /* Setup nop channel program. */
279         ccw = cdev->private->iccws;
280         ccw->cmd_code = CCW_CMD_NOOP;
281         ccw->cda = 0;
282         ccw->count = 0;
283         ccw->flags = CCW_FLAG_SLI;
284
285         /* Reset device status. */
286         memset(&cdev->private->irb, 0, sizeof(struct irb));
287
288         /* Try multiple times. */
289         ret = -ENODEV;
290         if (cdev->private->iretry > 0) {
291                 cdev->private->iretry--;
292                 ret = cio_start (sch, cdev->private->iccws,
293                                  cdev->private->imask);
294                 /* ret is 0, -EBUSY, -EACCES or -ENODEV */
295                 if ((ret != -EACCES) && (ret != -ENODEV))
296                         return ret;
297         }
298         /* nop command failed on this path. Switch it off. */
299         sch->lpm &= ~cdev->private->imask;
300         sch->vpm &= ~cdev->private->imask;
301         CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel "
302                       "0.%x.%04x, lpm %02X, became 'not operational'\n",
303                       cdev->private->devno, sch->schid.ssid,
304                       sch->schid.sch_no, cdev->private->imask);
305         return ret;
306 }
307
308
309 /*
310  * Called from interrupt context to check if a valid answer
311  * to Set Path Group ID was received.
312  */
313 static int
314 __ccw_device_check_pgid(struct ccw_device *cdev)
315 {
316         struct subchannel *sch;
317         struct irb *irb;
318
319         sch = to_subchannel(cdev->dev.parent);
320         irb = &cdev->private->irb;
321         if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
322                 return -ETIME;
323         if (irb->esw.esw0.erw.cons) {
324                 if (irb->ecw[0] & SNS0_CMD_REJECT)
325                         return -EOPNOTSUPP;
326                 /* Hmm, whatever happened, try again. */
327                 CIO_MSG_EVENT(2, "SPID - device 0.%x.%04x, unit check, "
328                               "cnt %02d, "
329                               "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
330                               cdev->private->ssid,
331                               cdev->private->devno, irb->esw.esw0.erw.scnt,
332                               irb->ecw[0], irb->ecw[1],
333                               irb->ecw[2], irb->ecw[3],
334                               irb->ecw[4], irb->ecw[5],
335                               irb->ecw[6], irb->ecw[7]);
336                 return -EAGAIN;
337         }
338         if (irb->scsw.cc == 3) {
339                 CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel 0.%x.%04x,"
340                               " lpm %02X, became 'not operational'\n",
341                               cdev->private->devno, sch->schid.ssid,
342                               sch->schid.sch_no, cdev->private->imask);
343                 return -EACCES;
344         }
345         return 0;
346 }
347
348 /*
349  * Called from interrupt context to check the path status after a nop has
350  * been send.
351  */
352 static int __ccw_device_check_nop(struct ccw_device *cdev)
353 {
354         struct subchannel *sch;
355         struct irb *irb;
356
357         sch = to_subchannel(cdev->dev.parent);
358         irb = &cdev->private->irb;
359         if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
360                 return -ETIME;
361         if (irb->scsw.cc == 3) {
362                 CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel 0.%x.%04x,"
363                               " lpm %02X, became 'not operational'\n",
364                               cdev->private->devno, sch->schid.ssid,
365                               sch->schid.sch_no, cdev->private->imask);
366                 return -EACCES;
367         }
368         return 0;
369 }
370
371 static void
372 __ccw_device_verify_start(struct ccw_device *cdev)
373 {
374         struct subchannel *sch;
375         __u8 imask, func;
376         int ret;
377
378         sch = to_subchannel(cdev->dev.parent);
379         while (sch->vpm != sch->lpm) {
380                 /* Find first unequal bit in vpm vs. lpm */
381                 for (imask = 0x80; imask != 0; imask >>= 1)
382                         if ((sch->vpm & imask) != (sch->lpm & imask))
383                                 break;
384                 cdev->private->imask = imask;
385                 if (cdev->private->options.pgroup) {
386                         func = (sch->vpm & imask) ?
387                                 SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH;
388                         ret = __ccw_device_do_pgid(cdev, func);
389                 } else
390                         ret = __ccw_device_do_nop(cdev);
391                 if (ret == 0 || ret == -EBUSY)
392                         return;
393                 cdev->private->iretry = 5;
394         }
395         ccw_device_verify_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
396 }
397                 
398 /*
399  * Got interrupt for Set Path Group ID.
400  */
401 void
402 ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
403 {
404         struct subchannel *sch;
405         struct irb *irb;
406         int ret;
407
408         irb = (struct irb *) __LC_IRB;
409
410         if (irb->scsw.stctl ==
411             (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
412                 if (__ccw_device_should_retry(&irb->scsw))
413                         __ccw_device_verify_start(cdev);
414                 return;
415         }
416         if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
417                 return;
418         sch = to_subchannel(cdev->dev.parent);
419         if (cdev->private->options.pgroup)
420                 ret = __ccw_device_check_pgid(cdev);
421         else
422                 ret = __ccw_device_check_nop(cdev);
423         memset(&cdev->private->irb, 0, sizeof(struct irb));
424         switch (ret) {
425         /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
426         case 0:
427                 /* Establish or Resign Path Group done. Update vpm. */
428                 if ((sch->lpm & cdev->private->imask) != 0)
429                         sch->vpm |= cdev->private->imask;
430                 else
431                         sch->vpm &= ~cdev->private->imask;
432                 cdev->private->iretry = 5;
433                 __ccw_device_verify_start(cdev);
434                 break;
435         case -EOPNOTSUPP:
436                 /*
437                  * One of those strange devices which claim to be able
438                  * to do multipathing but not for Set Path Group ID.
439                  */
440                 if (cdev->private->flags.pgid_single)
441                         cdev->private->options.pgroup = 0;
442                 else
443                         cdev->private->flags.pgid_single = 1;
444                 /* fall through. */
445         case -EAGAIN:           /* Try again. */
446                 __ccw_device_verify_start(cdev);
447                 break;
448         case -ETIME:            /* Set path group id stopped by timeout. */
449                 ccw_device_verify_done(cdev, -ETIME);
450                 break;
451         case -EACCES:           /* channel is not operational. */
452                 sch->lpm &= ~cdev->private->imask;
453                 sch->vpm &= ~cdev->private->imask;
454                 cdev->private->iretry = 5;
455                 __ccw_device_verify_start(cdev);
456                 break;
457         }
458 }
459
460 void
461 ccw_device_verify_start(struct ccw_device *cdev)
462 {
463         struct subchannel *sch = to_subchannel(cdev->dev.parent);
464
465         cdev->private->flags.pgid_single = 0;
466         cdev->private->iretry = 5;
467         /*
468          * Update sch->lpm with current values to catch paths becoming
469          * available again.
470          */
471         if (stsch(sch->schid, &sch->schib)) {
472                 ccw_device_verify_done(cdev, -ENODEV);
473                 return;
474         }
475         sch->lpm = sch->schib.pmcw.pim &
476                 sch->schib.pmcw.pam &
477                 sch->schib.pmcw.pom &
478                 sch->opm;
479         __ccw_device_verify_start(cdev);
480 }
481
482 static void
483 __ccw_device_disband_start(struct ccw_device *cdev)
484 {
485         struct subchannel *sch;
486         int ret;
487
488         sch = to_subchannel(cdev->dev.parent);
489         while (cdev->private->imask != 0) {
490                 if (sch->lpm & cdev->private->imask) {
491                         ret = __ccw_device_do_pgid(cdev, SPID_FUNC_DISBAND);
492                         if (ret == 0)
493                                 return;
494                 }
495                 cdev->private->iretry = 5;
496                 cdev->private->imask >>= 1;
497         }
498         ccw_device_disband_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
499 }
500
501 /*
502  * Got interrupt for Unset Path Group ID.
503  */
504 void
505 ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
506 {
507         struct subchannel *sch;
508         struct irb *irb;
509         int ret;
510
511         irb = (struct irb *) __LC_IRB;
512
513         if (irb->scsw.stctl ==
514             (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
515                 if (__ccw_device_should_retry(&irb->scsw))
516                         __ccw_device_disband_start(cdev);
517                 return;
518         }
519         if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
520                 return;
521         sch = to_subchannel(cdev->dev.parent);
522         ret = __ccw_device_check_pgid(cdev);
523         memset(&cdev->private->irb, 0, sizeof(struct irb));
524         switch (ret) {
525         /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
526         case 0:                 /* disband successful. */
527                 sch->vpm = 0;
528                 ccw_device_disband_done(cdev, ret);
529                 break;
530         case -EOPNOTSUPP:
531                 /*
532                  * One of those strange devices which claim to be able
533                  * to do multipathing but not for Unset Path Group ID.
534                  */
535                 cdev->private->flags.pgid_single = 1;
536                 /* fall through. */
537         case -EAGAIN:           /* Try again. */
538                 __ccw_device_disband_start(cdev);
539                 break;
540         case -ETIME:            /* Set path group id stopped by timeout. */
541                 ccw_device_disband_done(cdev, -ETIME);
542                 break;
543         case -EACCES:           /* channel is not operational. */
544                 cdev->private->imask >>= 1;
545                 cdev->private->iretry = 5;
546                 __ccw_device_disband_start(cdev);
547                 break;
548         }
549 }
550
551 void
552 ccw_device_disband_start(struct ccw_device *cdev)
553 {
554         cdev->private->flags.pgid_single = 0;
555         cdev->private->iretry = 5;
556         cdev->private->imask = 0x80;
557         __ccw_device_disband_start(cdev);
558 }