diff options
author | Cornelia Huck <cornelia.huck@de.ibm.com> | 2007-10-12 10:11:26 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2007-10-12 10:13:02 -0400 |
commit | 3f4cf6e72f9f6a0b046b32881acc4f829f3aaa46 (patch) | |
tree | 84302932647aed49fa65b506fd0f5652af276915 /drivers/s390/cio/device_fsm.c | |
parent | 3952c8d43aa23862181ad23ecdf72895b4be3143 (diff) |
[S390] cio: Avoid machine check vs. not operational races.
There was the possibilty that an action like ccw_device_set_offline()
triggered by a device gone machine check might trigger a not oper
event. Unfortunately, this could lead to the situation that we tried
to unregister a subchannel twice: Once from the slow path evaluation,
and once via the not oper event.
Fix this by always using the same mechanism (css_schedule_eval()) for
triggering the unregister. This makes sure that unregistration will
only be done once. As an added bonus, it also simplyfies the code.
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio/device_fsm.c')
-rw-r--r-- | drivers/s390/cio/device_fsm.c | 144 |
1 files changed, 20 insertions, 124 deletions
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index f772ef0a4f22..8867443b8060 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c | |||
@@ -544,51 +544,6 @@ ccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event) | |||
544 | } | 544 | } |
545 | 545 | ||
546 | 546 | ||
547 | static void | ||
548 | ccw_device_nopath_notify(struct work_struct *work) | ||
549 | { | ||
550 | struct ccw_device_private *priv; | ||
551 | struct ccw_device *cdev; | ||
552 | struct subchannel *sch; | ||
553 | int ret; | ||
554 | unsigned long flags; | ||
555 | |||
556 | priv = container_of(work, struct ccw_device_private, kick_work); | ||
557 | cdev = priv->cdev; | ||
558 | spin_lock_irqsave(cdev->ccwlock, flags); | ||
559 | sch = to_subchannel(cdev->dev.parent); | ||
560 | /* Extra sanity. */ | ||
561 | if (sch->lpm) | ||
562 | goto out_unlock; | ||
563 | if (sch->driver && sch->driver->notify) { | ||
564 | spin_unlock_irqrestore(cdev->ccwlock, flags); | ||
565 | ret = sch->driver->notify(&sch->dev, CIO_NO_PATH); | ||
566 | spin_lock_irqsave(cdev->ccwlock, flags); | ||
567 | } else | ||
568 | ret = 0; | ||
569 | if (!ret) { | ||
570 | if (get_device(&sch->dev)) { | ||
571 | /* Driver doesn't want to keep device. */ | ||
572 | cio_disable_subchannel(sch); | ||
573 | if (get_device(&cdev->dev)) { | ||
574 | PREPARE_WORK(&cdev->private->kick_work, | ||
575 | ccw_device_call_sch_unregister); | ||
576 | queue_work(ccw_device_work, | ||
577 | &cdev->private->kick_work); | ||
578 | } else | ||
579 | put_device(&sch->dev); | ||
580 | } | ||
581 | } else { | ||
582 | cio_disable_subchannel(sch); | ||
583 | ccw_device_set_timeout(cdev, 0); | ||
584 | cdev->private->flags.fake_irb = 0; | ||
585 | cdev->private->state = DEV_STATE_DISCONNECTED; | ||
586 | wake_up(&cdev->private->wait_q); | ||
587 | } | ||
588 | out_unlock: | ||
589 | spin_unlock_irqrestore(cdev->ccwlock, flags); | ||
590 | } | ||
591 | |||
592 | void | 547 | void |
593 | ccw_device_verify_done(struct ccw_device *cdev, int err) | 548 | ccw_device_verify_done(struct ccw_device *cdev, int err) |
594 | { | 549 | { |
@@ -632,12 +587,9 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) | |||
632 | default: | 587 | default: |
633 | /* Reset oper notify indication after verify error. */ | 588 | /* Reset oper notify indication after verify error. */ |
634 | cdev->private->flags.donotify = 0; | 589 | cdev->private->flags.donotify = 0; |
635 | if (cdev->online) { | 590 | if (cdev->online) |
636 | PREPARE_WORK(&cdev->private->kick_work, | 591 | dev_fsm_event(cdev, DEV_EVENT_NOTOPER); |
637 | ccw_device_nopath_notify); | 592 | else |
638 | queue_work(ccw_device_notify_work, | ||
639 | &cdev->private->kick_work); | ||
640 | } else | ||
641 | ccw_device_done(cdev, DEV_STATE_NOT_OPER); | 593 | ccw_device_done(cdev, DEV_STATE_NOT_OPER); |
642 | break; | 594 | break; |
643 | } | 595 | } |
@@ -691,11 +643,7 @@ ccw_device_disband_done(struct ccw_device *cdev, int err) | |||
691 | break; | 643 | break; |
692 | default: | 644 | default: |
693 | cdev->private->flags.donotify = 0; | 645 | cdev->private->flags.donotify = 0; |
694 | if (get_device(&cdev->dev)) { | 646 | dev_fsm_event(cdev, DEV_EVENT_NOTOPER); |
695 | PREPARE_WORK(&cdev->private->kick_work, | ||
696 | ccw_device_call_sch_unregister); | ||
697 | queue_work(ccw_device_work, &cdev->private->kick_work); | ||
698 | } | ||
699 | ccw_device_done(cdev, DEV_STATE_NOT_OPER); | 647 | ccw_device_done(cdev, DEV_STATE_NOT_OPER); |
700 | break; | 648 | break; |
701 | } | 649 | } |
@@ -766,59 +714,16 @@ ccw_device_recog_notoper(struct ccw_device *cdev, enum dev_event dev_event) | |||
766 | } | 714 | } |
767 | 715 | ||
768 | /* | 716 | /* |
769 | * Handle not operational event while offline. | 717 | * Handle not operational event in non-special state. |
770 | */ | 718 | */ |
771 | static void | 719 | static void ccw_device_generic_notoper(struct ccw_device *cdev, |
772 | ccw_device_offline_notoper(struct ccw_device *cdev, enum dev_event dev_event) | 720 | enum dev_event dev_event) |
773 | { | 721 | { |
774 | struct subchannel *sch; | 722 | struct subchannel *sch; |
775 | 723 | ||
776 | cdev->private->state = DEV_STATE_NOT_OPER; | 724 | cdev->private->state = DEV_STATE_NOT_OPER; |
777 | sch = to_subchannel(cdev->dev.parent); | 725 | sch = to_subchannel(cdev->dev.parent); |
778 | if (get_device(&cdev->dev)) { | 726 | css_schedule_eval(sch->schid); |
779 | PREPARE_WORK(&cdev->private->kick_work, | ||
780 | ccw_device_call_sch_unregister); | ||
781 | queue_work(ccw_device_work, &cdev->private->kick_work); | ||
782 | } | ||
783 | wake_up(&cdev->private->wait_q); | ||
784 | } | ||
785 | |||
786 | /* | ||
787 | * Handle not operational event while online. | ||
788 | */ | ||
789 | static void | ||
790 | ccw_device_online_notoper(struct ccw_device *cdev, enum dev_event dev_event) | ||
791 | { | ||
792 | struct subchannel *sch; | ||
793 | int ret; | ||
794 | |||
795 | sch = to_subchannel(cdev->dev.parent); | ||
796 | if (sch->driver->notify) { | ||
797 | spin_unlock_irq(cdev->ccwlock); | ||
798 | ret = sch->driver->notify(&sch->dev, | ||
799 | sch->lpm ? CIO_GONE : CIO_NO_PATH); | ||
800 | spin_lock_irq(cdev->ccwlock); | ||
801 | } else | ||
802 | ret = 0; | ||
803 | if (ret) { | ||
804 | ccw_device_set_timeout(cdev, 0); | ||
805 | cdev->private->flags.fake_irb = 0; | ||
806 | cdev->private->state = DEV_STATE_DISCONNECTED; | ||
807 | wake_up(&cdev->private->wait_q); | ||
808 | return; | ||
809 | } | ||
810 | cdev->private->state = DEV_STATE_NOT_OPER; | ||
811 | cio_disable_subchannel(sch); | ||
812 | if (sch->schib.scsw.actl != 0) { | ||
813 | // FIXME: not-oper indication to device driver ? | ||
814 | ccw_device_call_handler(cdev); | ||
815 | } | ||
816 | if (get_device(&cdev->dev)) { | ||
817 | PREPARE_WORK(&cdev->private->kick_work, | ||
818 | ccw_device_call_sch_unregister); | ||
819 | queue_work(ccw_device_work, &cdev->private->kick_work); | ||
820 | } | ||
821 | wake_up(&cdev->private->wait_q); | ||
822 | } | 727 | } |
823 | 728 | ||
824 | /* | 729 | /* |
@@ -916,18 +821,9 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event) | |||
916 | cdev->private->state = DEV_STATE_TIMEOUT_KILL; | 821 | cdev->private->state = DEV_STATE_TIMEOUT_KILL; |
917 | return; | 822 | return; |
918 | } | 823 | } |
919 | if (ret == -ENODEV) { | 824 | if (ret == -ENODEV) |
920 | struct subchannel *sch; | 825 | dev_fsm_event(cdev, DEV_EVENT_NOTOPER); |
921 | 826 | else if (cdev->handler) | |
922 | sch = to_subchannel(cdev->dev.parent); | ||
923 | if (!sch->lpm) { | ||
924 | PREPARE_WORK(&cdev->private->kick_work, | ||
925 | ccw_device_nopath_notify); | ||
926 | queue_work(ccw_device_notify_work, | ||
927 | &cdev->private->kick_work); | ||
928 | } else | ||
929 | dev_fsm_event(cdev, DEV_EVENT_NOTOPER); | ||
930 | } else if (cdev->handler) | ||
931 | cdev->handler(cdev, cdev->private->intparm, | 827 | cdev->handler(cdev, cdev->private->intparm, |
932 | ERR_PTR(-ETIMEDOUT)); | 828 | ERR_PTR(-ETIMEDOUT)); |
933 | } | 829 | } |
@@ -1234,7 +1130,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { | |||
1234 | [DEV_EVENT_VERIFY] = ccw_device_nop, | 1130 | [DEV_EVENT_VERIFY] = ccw_device_nop, |
1235 | }, | 1131 | }, |
1236 | [DEV_STATE_SENSE_PGID] = { | 1132 | [DEV_STATE_SENSE_PGID] = { |
1237 | [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, | 1133 | [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, |
1238 | [DEV_EVENT_INTERRUPT] = ccw_device_sense_pgid_irq, | 1134 | [DEV_EVENT_INTERRUPT] = ccw_device_sense_pgid_irq, |
1239 | [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, | 1135 | [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, |
1240 | [DEV_EVENT_VERIFY] = ccw_device_nop, | 1136 | [DEV_EVENT_VERIFY] = ccw_device_nop, |
@@ -1246,50 +1142,50 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { | |||
1246 | [DEV_EVENT_VERIFY] = ccw_device_nop, | 1142 | [DEV_EVENT_VERIFY] = ccw_device_nop, |
1247 | }, | 1143 | }, |
1248 | [DEV_STATE_OFFLINE] = { | 1144 | [DEV_STATE_OFFLINE] = { |
1249 | [DEV_EVENT_NOTOPER] = ccw_device_offline_notoper, | 1145 | [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, |
1250 | [DEV_EVENT_INTERRUPT] = ccw_device_offline_irq, | 1146 | [DEV_EVENT_INTERRUPT] = ccw_device_offline_irq, |
1251 | [DEV_EVENT_TIMEOUT] = ccw_device_nop, | 1147 | [DEV_EVENT_TIMEOUT] = ccw_device_nop, |
1252 | [DEV_EVENT_VERIFY] = ccw_device_nop, | 1148 | [DEV_EVENT_VERIFY] = ccw_device_nop, |
1253 | }, | 1149 | }, |
1254 | [DEV_STATE_VERIFY] = { | 1150 | [DEV_STATE_VERIFY] = { |
1255 | [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, | 1151 | [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, |
1256 | [DEV_EVENT_INTERRUPT] = ccw_device_verify_irq, | 1152 | [DEV_EVENT_INTERRUPT] = ccw_device_verify_irq, |
1257 | [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, | 1153 | [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, |
1258 | [DEV_EVENT_VERIFY] = ccw_device_delay_verify, | 1154 | [DEV_EVENT_VERIFY] = ccw_device_delay_verify, |
1259 | }, | 1155 | }, |
1260 | [DEV_STATE_ONLINE] = { | 1156 | [DEV_STATE_ONLINE] = { |
1261 | [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, | 1157 | [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, |
1262 | [DEV_EVENT_INTERRUPT] = ccw_device_irq, | 1158 | [DEV_EVENT_INTERRUPT] = ccw_device_irq, |
1263 | [DEV_EVENT_TIMEOUT] = ccw_device_online_timeout, | 1159 | [DEV_EVENT_TIMEOUT] = ccw_device_online_timeout, |
1264 | [DEV_EVENT_VERIFY] = ccw_device_online_verify, | 1160 | [DEV_EVENT_VERIFY] = ccw_device_online_verify, |
1265 | }, | 1161 | }, |
1266 | [DEV_STATE_W4SENSE] = { | 1162 | [DEV_STATE_W4SENSE] = { |
1267 | [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, | 1163 | [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, |
1268 | [DEV_EVENT_INTERRUPT] = ccw_device_w4sense, | 1164 | [DEV_EVENT_INTERRUPT] = ccw_device_w4sense, |
1269 | [DEV_EVENT_TIMEOUT] = ccw_device_nop, | 1165 | [DEV_EVENT_TIMEOUT] = ccw_device_nop, |
1270 | [DEV_EVENT_VERIFY] = ccw_device_online_verify, | 1166 | [DEV_EVENT_VERIFY] = ccw_device_online_verify, |
1271 | }, | 1167 | }, |
1272 | [DEV_STATE_DISBAND_PGID] = { | 1168 | [DEV_STATE_DISBAND_PGID] = { |
1273 | [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, | 1169 | [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, |
1274 | [DEV_EVENT_INTERRUPT] = ccw_device_disband_irq, | 1170 | [DEV_EVENT_INTERRUPT] = ccw_device_disband_irq, |
1275 | [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, | 1171 | [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, |
1276 | [DEV_EVENT_VERIFY] = ccw_device_nop, | 1172 | [DEV_EVENT_VERIFY] = ccw_device_nop, |
1277 | }, | 1173 | }, |
1278 | [DEV_STATE_BOXED] = { | 1174 | [DEV_STATE_BOXED] = { |
1279 | [DEV_EVENT_NOTOPER] = ccw_device_offline_notoper, | 1175 | [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, |
1280 | [DEV_EVENT_INTERRUPT] = ccw_device_stlck_done, | 1176 | [DEV_EVENT_INTERRUPT] = ccw_device_stlck_done, |
1281 | [DEV_EVENT_TIMEOUT] = ccw_device_stlck_done, | 1177 | [DEV_EVENT_TIMEOUT] = ccw_device_stlck_done, |
1282 | [DEV_EVENT_VERIFY] = ccw_device_nop, | 1178 | [DEV_EVENT_VERIFY] = ccw_device_nop, |
1283 | }, | 1179 | }, |
1284 | /* states to wait for i/o completion before doing something */ | 1180 | /* states to wait for i/o completion before doing something */ |
1285 | [DEV_STATE_CLEAR_VERIFY] = { | 1181 | [DEV_STATE_CLEAR_VERIFY] = { |
1286 | [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, | 1182 | [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, |
1287 | [DEV_EVENT_INTERRUPT] = ccw_device_clear_verify, | 1183 | [DEV_EVENT_INTERRUPT] = ccw_device_clear_verify, |
1288 | [DEV_EVENT_TIMEOUT] = ccw_device_nop, | 1184 | [DEV_EVENT_TIMEOUT] = ccw_device_nop, |
1289 | [DEV_EVENT_VERIFY] = ccw_device_nop, | 1185 | [DEV_EVENT_VERIFY] = ccw_device_nop, |
1290 | }, | 1186 | }, |
1291 | [DEV_STATE_TIMEOUT_KILL] = { | 1187 | [DEV_STATE_TIMEOUT_KILL] = { |
1292 | [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, | 1188 | [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, |
1293 | [DEV_EVENT_INTERRUPT] = ccw_device_killing_irq, | 1189 | [DEV_EVENT_INTERRUPT] = ccw_device_killing_irq, |
1294 | [DEV_EVENT_TIMEOUT] = ccw_device_killing_timeout, | 1190 | [DEV_EVENT_TIMEOUT] = ccw_device_killing_timeout, |
1295 | [DEV_EVENT_VERIFY] = ccw_device_nop, //FIXME | 1191 | [DEV_EVENT_VERIFY] = ccw_device_nop, //FIXME |