aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio
diff options
context:
space:
mode:
authorCornelia Huck <cornelia.huck@de.ibm.com>2007-10-12 10:11:26 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2007-10-12 10:13:02 -0400
commit3f4cf6e72f9f6a0b046b32881acc4f829f3aaa46 (patch)
tree84302932647aed49fa65b506fd0f5652af276915 /drivers/s390/cio
parent3952c8d43aa23862181ad23ecdf72895b4be3143 (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')
-rw-r--r--drivers/s390/cio/device.c3
-rw-r--r--drivers/s390/cio/device.h1
-rw-r--r--drivers/s390/cio/device_fsm.c144
3 files changed, 21 insertions, 127 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index c96380db0136..39f02b48e4c7 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -969,8 +969,7 @@ out:
969 wake_up(&ccw_device_init_wq); 969 wake_up(&ccw_device_init_wq);
970} 970}
971 971
972void 972static void ccw_device_call_sch_unregister(struct work_struct *work)
973ccw_device_call_sch_unregister(struct work_struct *work)
974{ 973{
975 struct ccw_device_private *priv; 974 struct ccw_device_private *priv;
976 struct ccw_device *cdev; 975 struct ccw_device *cdev;
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index b66338b76579..0d4089600439 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -80,7 +80,6 @@ void io_subchannel_recog_done(struct ccw_device *cdev);
80int ccw_device_cancel_halt_clear(struct ccw_device *); 80int ccw_device_cancel_halt_clear(struct ccw_device *);
81 81
82void ccw_device_do_unreg_rereg(struct work_struct *); 82void ccw_device_do_unreg_rereg(struct work_struct *);
83void ccw_device_call_sch_unregister(struct work_struct *);
84void ccw_device_move_to_orphanage(struct work_struct *); 83void ccw_device_move_to_orphanage(struct work_struct *);
85int ccw_device_is_orphan(struct ccw_device *); 84int ccw_device_is_orphan(struct ccw_device *);
86 85
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
547static void
548ccw_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 }
588out_unlock:
589 spin_unlock_irqrestore(cdev->ccwlock, flags);
590}
591
592void 547void
593ccw_device_verify_done(struct ccw_device *cdev, int err) 548ccw_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 */
771static void 719static void ccw_device_generic_notoper(struct ccw_device *cdev,
772ccw_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 */
789static void
790ccw_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