Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

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