diff options
author | Peter Oberparleiter <peter.oberparleiter@de.ibm.com> | 2009-12-07 06:51:17 -0500 |
---|---|---|
committer | Martin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com> | 2009-12-07 06:51:29 -0500 |
commit | 5d6e6b6f6f3eac10a7f5a15e961bac3b36824d9d (patch) | |
tree | b64af704c59b0820c0537bd0bed45caa0d5e26b7 /drivers/s390/cio/device.c | |
parent | 60e4dac1abdf49ccdb7545ec406325f08423d848 (diff) |
[S390] cio: introduce parent-initiated device move
Change the initiative to update subchannel-ccw device associations
to the subchannel: when there is an indication that the internal
association no longer reflects the current hardware state, mark
each affected subchannel as requiring attention. Once processing
reaches a subchannel, determine the correct association for that
subchannel at that time and perform the necessary device_move
operations.
This change fixes problems with the previous approach which would
leave devices in an inconsistent state when a new hardware change
occurred while a device_move was already scheduled.
Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio/device.c')
-rw-r--r-- | drivers/s390/cio/device.c | 513 |
1 files changed, 182 insertions, 331 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 0efecefdb83a..6097763f1035 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -673,57 +673,19 @@ static int ccw_device_register(struct ccw_device *cdev) | |||
673 | return ret; | 673 | return ret; |
674 | } | 674 | } |
675 | 675 | ||
676 | struct match_data { | 676 | static int match_dev_id(struct device *dev, void *data) |
677 | struct ccw_dev_id dev_id; | ||
678 | struct ccw_device * sibling; | ||
679 | }; | ||
680 | |||
681 | static int | ||
682 | match_devno(struct device * dev, void * data) | ||
683 | { | ||
684 | struct match_data * d = data; | ||
685 | struct ccw_device * cdev; | ||
686 | |||
687 | cdev = to_ccwdev(dev); | ||
688 | if ((cdev->private->state == DEV_STATE_DISCONNECTED) && | ||
689 | !ccw_device_is_orphan(cdev) && | ||
690 | ccw_dev_id_is_equal(&cdev->private->dev_id, &d->dev_id) && | ||
691 | (cdev != d->sibling)) | ||
692 | return 1; | ||
693 | return 0; | ||
694 | } | ||
695 | |||
696 | static struct ccw_device * get_disc_ccwdev_by_dev_id(struct ccw_dev_id *dev_id, | ||
697 | struct ccw_device *sibling) | ||
698 | { | 677 | { |
699 | struct device *dev; | 678 | struct ccw_device *cdev = to_ccwdev(dev); |
700 | struct match_data data; | 679 | struct ccw_dev_id *dev_id = data; |
701 | |||
702 | data.dev_id = *dev_id; | ||
703 | data.sibling = sibling; | ||
704 | dev = bus_find_device(&ccw_bus_type, NULL, &data, match_devno); | ||
705 | |||
706 | return dev ? to_ccwdev(dev) : NULL; | ||
707 | } | ||
708 | |||
709 | static int match_orphan(struct device *dev, void *data) | ||
710 | { | ||
711 | struct ccw_dev_id *dev_id; | ||
712 | struct ccw_device *cdev; | ||
713 | 680 | ||
714 | dev_id = data; | ||
715 | cdev = to_ccwdev(dev); | ||
716 | return ccw_dev_id_is_equal(&cdev->private->dev_id, dev_id); | 681 | return ccw_dev_id_is_equal(&cdev->private->dev_id, dev_id); |
717 | } | 682 | } |
718 | 683 | ||
719 | static struct ccw_device * | 684 | static struct ccw_device *get_ccwdev_by_dev_id(struct ccw_dev_id *dev_id) |
720 | get_orphaned_ccwdev_by_dev_id(struct channel_subsystem *css, | ||
721 | struct ccw_dev_id *dev_id) | ||
722 | { | 685 | { |
723 | struct device *dev; | 686 | struct device *dev; |
724 | 687 | ||
725 | dev = device_find_child(&css->pseudo_subchannel->dev, dev_id, | 688 | dev = bus_find_device(&ccw_bus_type, NULL, dev_id, match_dev_id); |
726 | match_orphan); | ||
727 | 689 | ||
728 | return dev ? to_ccwdev(dev) : NULL; | 690 | return dev ? to_ccwdev(dev) : NULL; |
729 | } | 691 | } |
@@ -808,75 +770,6 @@ static struct ccw_device * io_subchannel_create_ccwdev(struct subchannel *sch) | |||
808 | 770 | ||
809 | static int io_subchannel_recog(struct ccw_device *, struct subchannel *); | 771 | static int io_subchannel_recog(struct ccw_device *, struct subchannel *); |
810 | 772 | ||
811 | static void sch_attach_device(struct subchannel *sch, | ||
812 | struct ccw_device *cdev) | ||
813 | { | ||
814 | css_update_ssd_info(sch); | ||
815 | spin_lock_irq(sch->lock); | ||
816 | sch_set_cdev(sch, cdev); | ||
817 | cdev->private->schid = sch->schid; | ||
818 | cdev->ccwlock = sch->lock; | ||
819 | ccw_device_trigger_reprobe(cdev); | ||
820 | spin_unlock_irq(sch->lock); | ||
821 | } | ||
822 | |||
823 | static void sch_attach_disconnected_device(struct subchannel *sch, | ||
824 | struct ccw_device *cdev) | ||
825 | { | ||
826 | struct subchannel *other_sch; | ||
827 | int ret; | ||
828 | |||
829 | /* Get reference for new parent. */ | ||
830 | if (!get_device(&sch->dev)) | ||
831 | return; | ||
832 | other_sch = to_subchannel(cdev->dev.parent); | ||
833 | /* Note: device_move() changes cdev->dev.parent */ | ||
834 | ret = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV); | ||
835 | if (ret) { | ||
836 | CIO_MSG_EVENT(0, "Moving disconnected device 0.%x.%04x failed " | ||
837 | "(ret=%d)!\n", cdev->private->dev_id.ssid, | ||
838 | cdev->private->dev_id.devno, ret); | ||
839 | /* Put reference for new parent. */ | ||
840 | put_device(&sch->dev); | ||
841 | return; | ||
842 | } | ||
843 | sch_set_cdev(other_sch, NULL); | ||
844 | /* No need to keep a subchannel without ccw device around. */ | ||
845 | css_sch_device_unregister(other_sch); | ||
846 | sch_attach_device(sch, cdev); | ||
847 | /* Put reference for old parent. */ | ||
848 | put_device(&other_sch->dev); | ||
849 | } | ||
850 | |||
851 | static void sch_attach_orphaned_device(struct subchannel *sch, | ||
852 | struct ccw_device *cdev) | ||
853 | { | ||
854 | int ret; | ||
855 | struct subchannel *pseudo_sch; | ||
856 | |||
857 | /* Get reference for new parent. */ | ||
858 | if (!get_device(&sch->dev)) | ||
859 | return; | ||
860 | pseudo_sch = to_subchannel(cdev->dev.parent); | ||
861 | /* | ||
862 | * Try to move the ccw device to its new subchannel. | ||
863 | * Note: device_move() changes cdev->dev.parent | ||
864 | */ | ||
865 | ret = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV); | ||
866 | if (ret) { | ||
867 | CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage " | ||
868 | "failed (ret=%d)!\n", | ||
869 | cdev->private->dev_id.ssid, | ||
870 | cdev->private->dev_id.devno, ret); | ||
871 | /* Put reference for new parent. */ | ||
872 | put_device(&sch->dev); | ||
873 | return; | ||
874 | } | ||
875 | sch_attach_device(sch, cdev); | ||
876 | /* Put reference on pseudo subchannel. */ | ||
877 | put_device(&pseudo_sch->dev); | ||
878 | } | ||
879 | |||
880 | static void sch_create_and_recog_new_device(struct subchannel *sch) | 773 | static void sch_create_and_recog_new_device(struct subchannel *sch) |
881 | { | 774 | { |
882 | struct ccw_device *cdev; | 775 | struct ccw_device *cdev; |
@@ -901,70 +794,6 @@ static void sch_create_and_recog_new_device(struct subchannel *sch) | |||
901 | } | 794 | } |
902 | } | 795 | } |
903 | 796 | ||
904 | |||
905 | void ccw_device_move_to_orphanage(struct work_struct *work) | ||
906 | { | ||
907 | struct ccw_device_private *priv; | ||
908 | struct ccw_device *cdev; | ||
909 | struct ccw_device *replacing_cdev; | ||
910 | struct subchannel *sch; | ||
911 | int ret; | ||
912 | struct channel_subsystem *css; | ||
913 | struct ccw_dev_id dev_id; | ||
914 | |||
915 | priv = container_of(work, struct ccw_device_private, kick_work); | ||
916 | cdev = priv->cdev; | ||
917 | sch = to_subchannel(cdev->dev.parent); | ||
918 | css = to_css(sch->dev.parent); | ||
919 | dev_id.devno = sch->schib.pmcw.dev; | ||
920 | dev_id.ssid = sch->schid.ssid; | ||
921 | |||
922 | /* Increase refcount for pseudo subchannel. */ | ||
923 | get_device(&css->pseudo_subchannel->dev); | ||
924 | /* | ||
925 | * Move the orphaned ccw device to the orphanage so the replacing | ||
926 | * ccw device can take its place on the subchannel. | ||
927 | * Note: device_move() changes cdev->dev.parent | ||
928 | */ | ||
929 | ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev, | ||
930 | DPM_ORDER_NONE); | ||
931 | if (ret) { | ||
932 | CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed " | ||
933 | "(ret=%d)!\n", cdev->private->dev_id.ssid, | ||
934 | cdev->private->dev_id.devno, ret); | ||
935 | /* Decrease refcount for pseudo subchannel again. */ | ||
936 | put_device(&css->pseudo_subchannel->dev); | ||
937 | return; | ||
938 | } | ||
939 | cdev->ccwlock = css->pseudo_subchannel->lock; | ||
940 | /* | ||
941 | * Search for the replacing ccw device | ||
942 | * - among the disconnected devices | ||
943 | * - in the orphanage | ||
944 | */ | ||
945 | replacing_cdev = get_disc_ccwdev_by_dev_id(&dev_id, cdev); | ||
946 | if (replacing_cdev) { | ||
947 | sch_attach_disconnected_device(sch, replacing_cdev); | ||
948 | /* Release reference from get_disc_ccwdev_by_dev_id() */ | ||
949 | put_device(&replacing_cdev->dev); | ||
950 | /* Release reference of subchannel from old cdev. */ | ||
951 | put_device(&sch->dev); | ||
952 | return; | ||
953 | } | ||
954 | replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id); | ||
955 | if (replacing_cdev) { | ||
956 | sch_attach_orphaned_device(sch, replacing_cdev); | ||
957 | /* Release reference from get_orphaned_ccwdev_by_dev_id() */ | ||
958 | put_device(&replacing_cdev->dev); | ||
959 | /* Release reference of subchannel from old cdev. */ | ||
960 | put_device(&sch->dev); | ||
961 | return; | ||
962 | } | ||
963 | sch_create_and_recog_new_device(sch); | ||
964 | /* Release reference of subchannel from old cdev. */ | ||
965 | put_device(&sch->dev); | ||
966 | } | ||
967 | |||
968 | /* | 797 | /* |
969 | * Register recognized device. | 798 | * Register recognized device. |
970 | */ | 799 | */ |
@@ -1131,53 +960,56 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) | |||
1131 | return rc; | 960 | return rc; |
1132 | } | 961 | } |
1133 | 962 | ||
1134 | static void ccw_device_move_to_sch(struct work_struct *work) | 963 | static int ccw_device_move_to_sch(struct ccw_device *cdev, |
964 | struct subchannel *sch) | ||
1135 | { | 965 | { |
1136 | struct ccw_device_private *priv; | 966 | struct subchannel *old_sch; |
1137 | int rc; | 967 | int rc; |
1138 | struct subchannel *sch; | ||
1139 | struct ccw_device *cdev; | ||
1140 | struct subchannel *former_parent; | ||
1141 | 968 | ||
1142 | priv = container_of(work, struct ccw_device_private, kick_work); | 969 | old_sch = to_subchannel(cdev->dev.parent); |
1143 | sch = priv->sch; | 970 | /* Obtain child reference for new parent. */ |
1144 | cdev = priv->cdev; | ||
1145 | former_parent = to_subchannel(cdev->dev.parent); | ||
1146 | /* Get reference for new parent. */ | ||
1147 | if (!get_device(&sch->dev)) | 971 | if (!get_device(&sch->dev)) |
1148 | return; | 972 | return -ENODEV; |
1149 | mutex_lock(&sch->reg_mutex); | 973 | mutex_lock(&sch->reg_mutex); |
1150 | /* | ||
1151 | * Try to move the ccw device to its new subchannel. | ||
1152 | * Note: device_move() changes cdev->dev.parent | ||
1153 | */ | ||
1154 | rc = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV); | 974 | rc = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV); |
1155 | mutex_unlock(&sch->reg_mutex); | 975 | mutex_unlock(&sch->reg_mutex); |
1156 | if (rc) { | 976 | if (rc) { |
1157 | CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to subchannel " | 977 | CIO_MSG_EVENT(0, "device_move(0.%x.%04x,0.%x.%04x)=%d\n", |
1158 | "0.%x.%04x failed (ret=%d)!\n", | ||
1159 | cdev->private->dev_id.ssid, | 978 | cdev->private->dev_id.ssid, |
1160 | cdev->private->dev_id.devno, sch->schid.ssid, | 979 | cdev->private->dev_id.devno, sch->schid.ssid, |
1161 | sch->schid.sch_no, rc); | 980 | sch->schib.pmcw.dev, rc); |
1162 | css_sch_device_unregister(sch); | 981 | /* Release child reference for new parent. */ |
1163 | /* Put reference for new parent again. */ | ||
1164 | put_device(&sch->dev); | 982 | put_device(&sch->dev); |
1165 | goto out; | 983 | return rc; |
1166 | } | 984 | } |
1167 | if (!sch_is_pseudo_sch(former_parent)) { | 985 | /* Clean up old subchannel. */ |
1168 | spin_lock_irq(former_parent->lock); | 986 | if (!sch_is_pseudo_sch(old_sch)) { |
1169 | sch_set_cdev(former_parent, NULL); | 987 | spin_lock_irq(old_sch->lock); |
1170 | spin_unlock_irq(former_parent->lock); | 988 | sch_set_cdev(old_sch, NULL); |
1171 | css_sch_device_unregister(former_parent); | 989 | cio_disable_subchannel(old_sch); |
1172 | /* Reset intparm to zeroes. */ | 990 | spin_unlock_irq(old_sch->lock); |
1173 | former_parent->config.intparm = 0; | 991 | css_schedule_eval(old_sch->schid); |
1174 | cio_commit_config(former_parent); | ||
1175 | } | 992 | } |
1176 | sch_attach_device(sch, cdev); | 993 | /* Release child reference for old parent. */ |
1177 | out: | 994 | put_device(&old_sch->dev); |
1178 | /* Put reference for old parent. */ | 995 | /* Initialize new subchannel. */ |
1179 | put_device(&former_parent->dev); | 996 | spin_lock_irq(sch->lock); |
1180 | put_device(&cdev->dev); | 997 | cdev->private->schid = sch->schid; |
998 | cdev->ccwlock = sch->lock; | ||
999 | if (!sch_is_pseudo_sch(sch)) | ||
1000 | sch_set_cdev(sch, cdev); | ||
1001 | spin_unlock_irq(sch->lock); | ||
1002 | if (!sch_is_pseudo_sch(sch)) | ||
1003 | css_update_ssd_info(sch); | ||
1004 | return 0; | ||
1005 | } | ||
1006 | |||
1007 | static int ccw_device_move_to_orph(struct ccw_device *cdev) | ||
1008 | { | ||
1009 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | ||
1010 | struct channel_subsystem *css = to_css(sch->dev.parent); | ||
1011 | |||
1012 | return ccw_device_move_to_sch(cdev, css->pseudo_subchannel); | ||
1181 | } | 1013 | } |
1182 | 1014 | ||
1183 | static void io_subchannel_irq(struct subchannel *sch) | 1015 | static void io_subchannel_irq(struct subchannel *sch) |
@@ -1244,8 +1076,6 @@ static int io_subchannel_probe(struct subchannel *sch) | |||
1244 | { | 1076 | { |
1245 | struct ccw_device *cdev; | 1077 | struct ccw_device *cdev; |
1246 | int rc; | 1078 | int rc; |
1247 | unsigned long flags; | ||
1248 | struct ccw_dev_id dev_id; | ||
1249 | 1079 | ||
1250 | if (cio_is_console(sch->schid)) { | 1080 | if (cio_is_console(sch->schid)) { |
1251 | rc = sysfs_create_group(&sch->dev.kobj, | 1081 | rc = sysfs_create_group(&sch->dev.kobj, |
@@ -1290,37 +1120,7 @@ static int io_subchannel_probe(struct subchannel *sch) | |||
1290 | GFP_KERNEL | GFP_DMA); | 1120 | GFP_KERNEL | GFP_DMA); |
1291 | if (!sch->private) | 1121 | if (!sch->private) |
1292 | goto out_schedule; | 1122 | goto out_schedule; |
1293 | /* | 1123 | css_schedule_eval(sch->schid); |
1294 | * First check if a fitting device may be found amongst the | ||
1295 | * disconnected devices or in the orphanage. | ||
1296 | */ | ||
1297 | dev_id.devno = sch->schib.pmcw.dev; | ||
1298 | dev_id.ssid = sch->schid.ssid; | ||
1299 | cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL); | ||
1300 | if (!cdev) | ||
1301 | cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent), | ||
1302 | &dev_id); | ||
1303 | if (cdev) { | ||
1304 | /* | ||
1305 | * Schedule moving the device until when we have a registered | ||
1306 | * subchannel to move to and succeed the probe. We can | ||
1307 | * unregister later again, when the probe is through. | ||
1308 | */ | ||
1309 | cdev->private->sch = sch; | ||
1310 | PREPARE_WORK(&cdev->private->kick_work, | ||
1311 | ccw_device_move_to_sch); | ||
1312 | queue_work(slow_path_wq, &cdev->private->kick_work); | ||
1313 | return 0; | ||
1314 | } | ||
1315 | cdev = io_subchannel_create_ccwdev(sch); | ||
1316 | if (IS_ERR(cdev)) | ||
1317 | goto out_schedule; | ||
1318 | rc = io_subchannel_recog(cdev, sch); | ||
1319 | if (rc) { | ||
1320 | spin_lock_irqsave(sch->lock, flags); | ||
1321 | io_subchannel_recog_done(cdev); | ||
1322 | spin_unlock_irqrestore(sch->lock, flags); | ||
1323 | } | ||
1324 | return 0; | 1124 | return 0; |
1325 | 1125 | ||
1326 | out_schedule: | 1126 | out_schedule: |
@@ -1349,16 +1149,6 @@ out_free: | |||
1349 | return 0; | 1149 | return 0; |
1350 | } | 1150 | } |
1351 | 1151 | ||
1352 | static int io_subchannel_notify(struct subchannel *sch, int event) | ||
1353 | { | ||
1354 | struct ccw_device *cdev; | ||
1355 | |||
1356 | cdev = sch_get_cdev(sch); | ||
1357 | if (!cdev) | ||
1358 | return 0; | ||
1359 | return ccw_device_notify(cdev, event); | ||
1360 | } | ||
1361 | |||
1362 | static void io_subchannel_verify(struct subchannel *sch) | 1152 | static void io_subchannel_verify(struct subchannel *sch) |
1363 | { | 1153 | { |
1364 | struct ccw_device *cdev; | 1154 | struct ccw_device *cdev; |
@@ -1482,19 +1272,6 @@ io_subchannel_shutdown(struct subchannel *sch) | |||
1482 | cio_disable_subchannel(sch); | 1272 | cio_disable_subchannel(sch); |
1483 | } | 1273 | } |
1484 | 1274 | ||
1485 | static int io_subchannel_get_status(struct subchannel *sch) | ||
1486 | { | ||
1487 | struct schib schib; | ||
1488 | |||
1489 | if (stsch(sch->schid, &schib) || !schib.pmcw.dnv) | ||
1490 | return CIO_GONE; | ||
1491 | if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev)) | ||
1492 | return CIO_REVALIDATE; | ||
1493 | if (!sch->lpm) | ||
1494 | return CIO_NO_PATH; | ||
1495 | return CIO_OPER; | ||
1496 | } | ||
1497 | |||
1498 | static int device_is_disconnected(struct ccw_device *cdev) | 1275 | static int device_is_disconnected(struct ccw_device *cdev) |
1499 | { | 1276 | { |
1500 | if (!cdev) | 1277 | if (!cdev) |
@@ -1626,91 +1403,165 @@ void ccw_device_set_notoper(struct ccw_device *cdev) | |||
1626 | cdev->private->state = DEV_STATE_NOT_OPER; | 1403 | cdev->private->state = DEV_STATE_NOT_OPER; |
1627 | } | 1404 | } |
1628 | 1405 | ||
1629 | static int io_subchannel_sch_event(struct subchannel *sch, int slow) | 1406 | enum io_sch_action { |
1407 | IO_SCH_UNREG, | ||
1408 | IO_SCH_ORPH_UNREG, | ||
1409 | IO_SCH_ATTACH, | ||
1410 | IO_SCH_UNREG_ATTACH, | ||
1411 | IO_SCH_ORPH_ATTACH, | ||
1412 | IO_SCH_REPROBE, | ||
1413 | IO_SCH_VERIFY, | ||
1414 | IO_SCH_DISC, | ||
1415 | IO_SCH_NOP, | ||
1416 | }; | ||
1417 | |||
1418 | static enum io_sch_action sch_get_action(struct subchannel *sch) | ||
1419 | { | ||
1420 | struct ccw_device *cdev; | ||
1421 | |||
1422 | cdev = sch_get_cdev(sch); | ||
1423 | if (cio_update_schib(sch)) { | ||
1424 | /* Not operational. */ | ||
1425 | if (!cdev) | ||
1426 | return IO_SCH_UNREG; | ||
1427 | if (!ccw_device_notify(cdev, CIO_GONE)) | ||
1428 | return IO_SCH_UNREG; | ||
1429 | return IO_SCH_ORPH_UNREG; | ||
1430 | } | ||
1431 | /* Operational. */ | ||
1432 | if (!cdev) | ||
1433 | return IO_SCH_ATTACH; | ||
1434 | if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { | ||
1435 | if (!ccw_device_notify(cdev, CIO_GONE)) | ||
1436 | return IO_SCH_UNREG_ATTACH; | ||
1437 | return IO_SCH_ORPH_ATTACH; | ||
1438 | } | ||
1439 | if ((sch->schib.pmcw.pam & sch->opm) == 0) { | ||
1440 | if (!ccw_device_notify(cdev, CIO_NO_PATH)) | ||
1441 | return IO_SCH_UNREG; | ||
1442 | return IO_SCH_DISC; | ||
1443 | } | ||
1444 | if (device_is_disconnected(cdev)) | ||
1445 | return IO_SCH_REPROBE; | ||
1446 | if (cdev->online) | ||
1447 | return IO_SCH_VERIFY; | ||
1448 | return IO_SCH_NOP; | ||
1449 | } | ||
1450 | |||
1451 | /** | ||
1452 | * io_subchannel_sch_event - process subchannel event | ||
1453 | * @sch: subchannel | ||
1454 | * @process: non-zero if function is called in process context | ||
1455 | * | ||
1456 | * An unspecified event occurred for this subchannel. Adjust data according | ||
1457 | * to the current operational state of the subchannel and device. Return | ||
1458 | * zero when the event has been handled sufficiently or -EAGAIN when this | ||
1459 | * function should be called again in process context. | ||
1460 | */ | ||
1461 | static int io_subchannel_sch_event(struct subchannel *sch, int process) | ||
1630 | { | 1462 | { |
1631 | int event, ret, disc; | ||
1632 | unsigned long flags; | 1463 | unsigned long flags; |
1633 | enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE, DISC } action; | ||
1634 | struct ccw_device *cdev; | 1464 | struct ccw_device *cdev; |
1465 | struct ccw_dev_id dev_id; | ||
1466 | enum io_sch_action action; | ||
1467 | int rc = -EAGAIN; | ||
1635 | 1468 | ||
1636 | spin_lock_irqsave(sch->lock, flags); | 1469 | spin_lock_irqsave(sch->lock, flags); |
1470 | if (!device_is_registered(&sch->dev)) | ||
1471 | goto out_unlock; | ||
1472 | action = sch_get_action(sch); | ||
1473 | CIO_MSG_EVENT(2, "event: sch 0.%x.%04x, process=%d, action=%d\n", | ||
1474 | sch->schid.ssid, sch->schid.sch_no, process, | ||
1475 | action); | ||
1476 | /* Perform immediate actions while holding the lock. */ | ||
1637 | cdev = sch_get_cdev(sch); | 1477 | cdev = sch_get_cdev(sch); |
1638 | disc = device_is_disconnected(cdev); | 1478 | switch (action) { |
1639 | if (disc && slow) { | 1479 | case IO_SCH_REPROBE: |
1640 | /* Disconnected devices are evaluated directly only.*/ | 1480 | /* Trigger device recognition. */ |
1641 | spin_unlock_irqrestore(sch->lock, flags); | 1481 | ccw_device_trigger_reprobe(cdev); |
1642 | return 0; | 1482 | rc = 0; |
1643 | } | 1483 | goto out_unlock; |
1644 | /* No interrupt after machine check - kill pending timers. */ | 1484 | case IO_SCH_VERIFY: |
1645 | if (cdev) | 1485 | /* Trigger path verification. */ |
1646 | ccw_device_set_timeout(cdev, 0); | 1486 | io_subchannel_verify(sch); |
1647 | if (!disc && !slow) { | 1487 | rc = 0; |
1648 | /* Non-disconnected devices are evaluated on the slow path. */ | 1488 | goto out_unlock; |
1649 | spin_unlock_irqrestore(sch->lock, flags); | 1489 | case IO_SCH_DISC: |
1650 | return -EAGAIN; | 1490 | ccw_device_set_disconnected(cdev); |
1491 | rc = 0; | ||
1492 | goto out_unlock; | ||
1493 | case IO_SCH_ORPH_UNREG: | ||
1494 | case IO_SCH_ORPH_ATTACH: | ||
1495 | ccw_device_set_disconnected(cdev); | ||
1496 | break; | ||
1497 | case IO_SCH_UNREG_ATTACH: | ||
1498 | case IO_SCH_UNREG: | ||
1499 | if (cdev) | ||
1500 | ccw_device_set_notoper(cdev); | ||
1501 | break; | ||
1502 | case IO_SCH_NOP: | ||
1503 | rc = 0; | ||
1504 | goto out_unlock; | ||
1505 | default: | ||
1506 | break; | ||
1651 | } | 1507 | } |
1652 | event = io_subchannel_get_status(sch); | 1508 | spin_unlock_irqrestore(sch->lock, flags); |
1653 | CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n", | 1509 | /* All other actions require process context. */ |
1654 | sch->schid.ssid, sch->schid.sch_no, event, | 1510 | if (!process) |
1655 | disc ? "disconnected" : "normal", | 1511 | goto out; |
1656 | slow ? "slow" : "fast"); | 1512 | /* Handle attached ccw device. */ |
1657 | /* Analyze subchannel status. */ | 1513 | switch (action) { |
1658 | action = NONE; | 1514 | case IO_SCH_ORPH_UNREG: |
1659 | switch (event) { | 1515 | case IO_SCH_ORPH_ATTACH: |
1660 | case CIO_NO_PATH: | 1516 | /* Move ccw device to orphanage. */ |
1661 | if (disc) { | 1517 | rc = ccw_device_move_to_orph(cdev); |
1662 | /* Check if paths have become available. */ | 1518 | if (rc) |
1663 | action = REPROBE; | 1519 | goto out; |
1664 | break; | ||
1665 | } | ||
1666 | /* fall through */ | ||
1667 | case CIO_GONE: | ||
1668 | /* Ask driver what to do with device. */ | ||
1669 | if (io_subchannel_notify(sch, event)) | ||
1670 | action = DISC; | ||
1671 | else | ||
1672 | action = UNREGISTER; | ||
1673 | break; | 1520 | break; |
1674 | case CIO_REVALIDATE: | 1521 | case IO_SCH_UNREG_ATTACH: |
1675 | /* Device will be removed, so no notify necessary. */ | 1522 | /* Unregister ccw device. */ |
1676 | if (disc) | 1523 | ccw_device_unregister(cdev); |
1677 | /* Reprobe because immediate unregister might block. */ | ||
1678 | action = REPROBE; | ||
1679 | else | ||
1680 | action = UNREGISTER_PROBE; | ||
1681 | break; | 1524 | break; |
1682 | case CIO_OPER: | 1525 | default: |
1683 | if (disc) | ||
1684 | /* Get device operational again. */ | ||
1685 | action = REPROBE; | ||
1686 | break; | 1526 | break; |
1687 | } | 1527 | } |
1688 | /* Perform action. */ | 1528 | /* Handle subchannel. */ |
1689 | ret = 0; | ||
1690 | switch (action) { | 1529 | switch (action) { |
1691 | case UNREGISTER: | 1530 | case IO_SCH_ORPH_UNREG: |
1692 | case UNREGISTER_PROBE: | 1531 | case IO_SCH_UNREG: |
1693 | ccw_device_set_notoper(cdev); | ||
1694 | /* Unregister device (will use subchannel lock). */ | ||
1695 | spin_unlock_irqrestore(sch->lock, flags); | ||
1696 | css_sch_device_unregister(sch); | 1532 | css_sch_device_unregister(sch); |
1697 | spin_lock_irqsave(sch->lock, flags); | ||
1698 | break; | 1533 | break; |
1699 | case REPROBE: | 1534 | case IO_SCH_ORPH_ATTACH: |
1535 | case IO_SCH_UNREG_ATTACH: | ||
1536 | case IO_SCH_ATTACH: | ||
1537 | dev_id.ssid = sch->schid.ssid; | ||
1538 | dev_id.devno = sch->schib.pmcw.dev; | ||
1539 | cdev = get_ccwdev_by_dev_id(&dev_id); | ||
1540 | if (!cdev) { | ||
1541 | sch_create_and_recog_new_device(sch); | ||
1542 | break; | ||
1543 | } | ||
1544 | rc = ccw_device_move_to_sch(cdev, sch); | ||
1545 | if (rc) { | ||
1546 | /* Release reference from get_ccwdev_by_dev_id() */ | ||
1547 | put_device(&cdev->dev); | ||
1548 | goto out; | ||
1549 | } | ||
1550 | spin_lock_irqsave(sch->lock, flags); | ||
1700 | ccw_device_trigger_reprobe(cdev); | 1551 | ccw_device_trigger_reprobe(cdev); |
1701 | break; | 1552 | spin_unlock_irqrestore(sch->lock, flags); |
1702 | case DISC: | 1553 | /* Release reference from get_ccwdev_by_dev_id() */ |
1703 | ccw_device_set_disconnected(cdev); | 1554 | put_device(&cdev->dev); |
1704 | break; | 1555 | break; |
1705 | default: | 1556 | default: |
1706 | break; | 1557 | break; |
1707 | } | 1558 | } |
1708 | spin_unlock_irqrestore(sch->lock, flags); | 1559 | return 0; |
1709 | /* Probe if necessary. */ | ||
1710 | if (action == UNREGISTER_PROBE) | ||
1711 | ret = css_probe_device(sch->schid); | ||
1712 | 1560 | ||
1713 | return ret; | 1561 | out_unlock: |
1562 | spin_unlock_irqrestore(sch->lock, flags); | ||
1563 | out: | ||
1564 | return rc; | ||
1714 | } | 1565 | } |
1715 | 1566 | ||
1716 | #ifdef CONFIG_CCW_CONSOLE | 1567 | #ifdef CONFIG_CCW_CONSOLE |