diff options
Diffstat (limited to 'drivers/s390/cio/device_fsm.c')
-rw-r--r-- | drivers/s390/cio/device_fsm.c | 66 |
1 files changed, 40 insertions, 26 deletions
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 09c7672..51238e7 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -186,15 +186,14 @@ ccw_device_handle_oper(struct ccw_device *cdev) /* * Check if cu type and device type still match. If * not, it is certainly another device and we have to - * de- and re-register. Also check here for non-matching devno. + * de- and re-register. */ if (cdev->id.cu_type != cdev->private->senseid.cu_type || cdev->id.cu_model != cdev->private->senseid.cu_model || cdev->id.dev_type != cdev->private->senseid.dev_type || - cdev->id.dev_model != cdev->private->senseid.dev_model || - cdev->private->dev_id.devno != sch->schib.pmcw.dev) { + cdev->id.dev_model != cdev->private->senseid.dev_model) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_do_unreg_rereg, cdev); + ccw_device_do_unreg_rereg); queue_work(ccw_device_work, &cdev->private->kick_work); return 0; } @@ -207,7 +206,7 @@ ccw_device_handle_oper(struct ccw_device *cdev) * been varied online on the SE so we have to find out by magic (i. e. driving * the channel subsystem to device selection and updating our path masks). */ -static inline void +static void __recover_lost_chpids(struct subchannel *sch, int old_lpm) { int mask, i; @@ -329,19 +328,21 @@ ccw_device_sense_id_done(struct ccw_device *cdev, int err) } static void -ccw_device_oper_notify(void *data) +ccw_device_oper_notify(struct work_struct *work) { + struct ccw_device_private *priv; struct ccw_device *cdev; struct subchannel *sch; int ret; - cdev = data; + priv = container_of(work, struct ccw_device_private, kick_work); + cdev = priv->cdev; sch = to_subchannel(cdev->dev.parent); ret = (sch->driver && sch->driver->notify) ? sch->driver->notify(&sch->dev, CIO_OPER) : 0; if (!ret) /* Driver doesn't want device back. */ - ccw_device_do_unreg_rereg(cdev); + ccw_device_do_unreg_rereg(work); else { /* Reenable channel measurements, if needed. */ cmf_reenable(cdev); @@ -377,8 +378,7 @@ ccw_device_done(struct ccw_device *cdev, int state) if (cdev->private->flags.donotify) { cdev->private->flags.donotify = 0; - PREPARE_WORK(&cdev->private->kick_work, ccw_device_oper_notify, - cdev); + PREPARE_WORK(&cdev->private->kick_work, ccw_device_oper_notify); queue_work(ccw_device_notify_work, &cdev->private->kick_work); } wake_up(&cdev->private->wait_q); @@ -387,7 +387,7 @@ ccw_device_done(struct ccw_device *cdev, int state) put_device (&cdev->dev); } -static inline int cmp_pgid(struct pgid *p1, struct pgid *p2) +static int cmp_pgid(struct pgid *p1, struct pgid *p2) { char *c1; char *c2; @@ -528,13 +528,15 @@ ccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event) static void -ccw_device_nopath_notify(void *data) +ccw_device_nopath_notify(struct work_struct *work) { + struct ccw_device_private *priv; struct ccw_device *cdev; struct subchannel *sch; int ret; - cdev = data; + priv = container_of(work, struct ccw_device_private, kick_work); + cdev = priv->cdev; sch = to_subchannel(cdev->dev.parent); /* Extra sanity. */ if (sch->lpm) @@ -547,8 +549,7 @@ ccw_device_nopath_notify(void *data) cio_disable_subchannel(sch); if (get_device(&cdev->dev)) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_call_sch_unregister, - cdev); + ccw_device_call_sch_unregister); queue_work(ccw_device_work, &cdev->private->kick_work); } else @@ -607,7 +608,7 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) /* Reset oper notify indication after verify error. */ cdev->private->flags.donotify = 0; PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify, cdev); + ccw_device_nopath_notify); queue_work(ccw_device_notify_work, &cdev->private->kick_work); ccw_device_done(cdev, DEV_STATE_NOT_OPER); break; @@ -674,6 +675,10 @@ ccw_device_offline(struct ccw_device *cdev) { struct subchannel *sch; + if (ccw_device_is_orphan(cdev)) { + ccw_device_done(cdev, DEV_STATE_OFFLINE); + return 0; + } sch = to_subchannel(cdev->dev.parent); if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv) return -ENODEV; @@ -738,7 +743,7 @@ ccw_device_offline_notoper(struct ccw_device *cdev, enum dev_event dev_event) sch = to_subchannel(cdev->dev.parent); if (get_device(&cdev->dev)) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_call_sch_unregister, cdev); + ccw_device_call_sch_unregister); queue_work(ccw_device_work, &cdev->private->kick_work); } wake_up(&cdev->private->wait_q); @@ -769,7 +774,7 @@ ccw_device_online_notoper(struct ccw_device *cdev, enum dev_event dev_event) } if (get_device(&cdev->dev)) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_call_sch_unregister, cdev); + ccw_device_call_sch_unregister); queue_work(ccw_device_work, &cdev->private->kick_work); } wake_up(&cdev->private->wait_q); @@ -837,6 +842,8 @@ ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event) call_handler_unsol: if (cdev->handler) cdev->handler (cdev, 0, irb); + if (cdev->private->flags.doverify) + ccw_device_online_verify(cdev, 0); return; } /* Accumulate status and find out if a basic sense is needed. */ @@ -874,7 +881,7 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event) sch = to_subchannel(cdev->dev.parent); if (!sch->lpm) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify, cdev); + ccw_device_nopath_notify); queue_work(ccw_device_notify_work, &cdev->private->kick_work); } else @@ -887,7 +894,7 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event) /* * Got an interrupt for a basic sense. */ -void +static void ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event) { struct irb *irb; @@ -969,7 +976,7 @@ ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event) ERR_PTR(-EIO)); if (!sch->lpm) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify, cdev); + ccw_device_nopath_notify); queue_work(ccw_device_notify_work, &cdev->private->kick_work); } else if (cdev->private->flags.doverify) /* Start delayed path verification. */ @@ -992,7 +999,7 @@ ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event) sch = to_subchannel(cdev->dev.parent); if (!sch->lpm) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify, cdev); + ccw_device_nopath_notify); queue_work(ccw_device_notify_work, &cdev->private->kick_work); } else @@ -1021,7 +1028,7 @@ void device_kill_io(struct subchannel *sch) if (ret == -ENODEV) { if (!sch->lpm) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify, cdev); + ccw_device_nopath_notify); queue_work(ccw_device_notify_work, &cdev->private->kick_work); } else @@ -1033,7 +1040,7 @@ void device_kill_io(struct subchannel *sch) ERR_PTR(-EIO)); if (!sch->lpm) { PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify, cdev); + ccw_device_nopath_notify); queue_work(ccw_device_notify_work, &cdev->private->kick_work); } else /* Start delayed path verification. */ @@ -1104,7 +1111,8 @@ device_trigger_reprobe(struct subchannel *sch) /* Update some values. */ if (stsch(sch->schid, &sch->schib)) return; - + if (!sch->schib.pmcw.dnv) + return; /* * The pim, pam, pom values may not be accurate, but they are the best * we have before performing device selection :/ @@ -1118,7 +1126,13 @@ device_trigger_reprobe(struct subchannel *sch) sch->schib.pmcw.mp = 1; sch->schib.pmcw.intparm = (__u32)(unsigned long)sch; /* We should also udate ssd info, but this has to wait. */ - ccw_device_start_id(cdev, 0); + /* Check if this is another device which appeared on the same sch. */ + if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { + PREPARE_WORK(&cdev->private->kick_work, + ccw_device_move_to_orphanage); + queue_work(ccw_device_work, &cdev->private->kick_work); + } else + ccw_device_start_id(cdev, 0); } static void |