diff options
-rw-r--r-- | drivers/s390/cio/device.c | 75 |
1 files changed, 56 insertions, 19 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 039ef03cf217..cba33aa1df79 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -718,6 +718,8 @@ ccw_device_release(struct device *dev) | |||
718 | struct ccw_device *cdev; | 718 | struct ccw_device *cdev; |
719 | 719 | ||
720 | cdev = to_ccwdev(dev); | 720 | cdev = to_ccwdev(dev); |
721 | /* Release reference of parent subchannel. */ | ||
722 | put_device(cdev->dev.parent); | ||
721 | kfree(cdev->private); | 723 | kfree(cdev->private); |
722 | kfree(cdev); | 724 | kfree(cdev); |
723 | } | 725 | } |
@@ -792,37 +794,55 @@ static void sch_attach_disconnected_device(struct subchannel *sch, | |||
792 | struct subchannel *other_sch; | 794 | struct subchannel *other_sch; |
793 | int ret; | 795 | int ret; |
794 | 796 | ||
795 | other_sch = to_subchannel(get_device(cdev->dev.parent)); | 797 | /* Get reference for new parent. */ |
798 | if (!get_device(&sch->dev)) | ||
799 | return; | ||
800 | other_sch = to_subchannel(cdev->dev.parent); | ||
801 | /* Note: device_move() changes cdev->dev.parent */ | ||
796 | ret = device_move(&cdev->dev, &sch->dev); | 802 | ret = device_move(&cdev->dev, &sch->dev); |
797 | if (ret) { | 803 | if (ret) { |
798 | CIO_MSG_EVENT(0, "Moving disconnected device 0.%x.%04x failed " | 804 | CIO_MSG_EVENT(0, "Moving disconnected device 0.%x.%04x failed " |
799 | "(ret=%d)!\n", cdev->private->dev_id.ssid, | 805 | "(ret=%d)!\n", cdev->private->dev_id.ssid, |
800 | cdev->private->dev_id.devno, ret); | 806 | cdev->private->dev_id.devno, ret); |
801 | put_device(&other_sch->dev); | 807 | /* Put reference for new parent. */ |
808 | put_device(&sch->dev); | ||
802 | return; | 809 | return; |
803 | } | 810 | } |
804 | sch_set_cdev(other_sch, NULL); | 811 | sch_set_cdev(other_sch, NULL); |
805 | /* No need to keep a subchannel without ccw device around. */ | 812 | /* No need to keep a subchannel without ccw device around. */ |
806 | css_sch_device_unregister(other_sch); | 813 | css_sch_device_unregister(other_sch); |
807 | put_device(&other_sch->dev); | ||
808 | sch_attach_device(sch, cdev); | 814 | sch_attach_device(sch, cdev); |
815 | /* Put reference for old parent. */ | ||
816 | put_device(&other_sch->dev); | ||
809 | } | 817 | } |
810 | 818 | ||
811 | static void sch_attach_orphaned_device(struct subchannel *sch, | 819 | static void sch_attach_orphaned_device(struct subchannel *sch, |
812 | struct ccw_device *cdev) | 820 | struct ccw_device *cdev) |
813 | { | 821 | { |
814 | int ret; | 822 | int ret; |
823 | struct subchannel *pseudo_sch; | ||
815 | 824 | ||
816 | /* Try to move the ccw device to its new subchannel. */ | 825 | /* Get reference for new parent. */ |
826 | if (!get_device(&sch->dev)) | ||
827 | return; | ||
828 | pseudo_sch = to_subchannel(cdev->dev.parent); | ||
829 | /* | ||
830 | * Try to move the ccw device to its new subchannel. | ||
831 | * Note: device_move() changes cdev->dev.parent | ||
832 | */ | ||
817 | ret = device_move(&cdev->dev, &sch->dev); | 833 | ret = device_move(&cdev->dev, &sch->dev); |
818 | if (ret) { | 834 | if (ret) { |
819 | CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage " | 835 | CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage " |
820 | "failed (ret=%d)!\n", | 836 | "failed (ret=%d)!\n", |
821 | cdev->private->dev_id.ssid, | 837 | cdev->private->dev_id.ssid, |
822 | cdev->private->dev_id.devno, ret); | 838 | cdev->private->dev_id.devno, ret); |
839 | /* Put reference for new parent. */ | ||
840 | put_device(&sch->dev); | ||
823 | return; | 841 | return; |
824 | } | 842 | } |
825 | sch_attach_device(sch, cdev); | 843 | sch_attach_device(sch, cdev); |
844 | /* Put reference on pseudo subchannel. */ | ||
845 | put_device(&pseudo_sch->dev); | ||
826 | } | 846 | } |
827 | 847 | ||
828 | static void sch_create_and_recog_new_device(struct subchannel *sch) | 848 | static void sch_create_and_recog_new_device(struct subchannel *sch) |
@@ -844,9 +864,11 @@ static void sch_create_and_recog_new_device(struct subchannel *sch) | |||
844 | spin_lock_irq(sch->lock); | 864 | spin_lock_irq(sch->lock); |
845 | sch_set_cdev(sch, NULL); | 865 | sch_set_cdev(sch, NULL); |
846 | spin_unlock_irq(sch->lock); | 866 | spin_unlock_irq(sch->lock); |
847 | if (cdev->dev.release) | ||
848 | cdev->dev.release(&cdev->dev); | ||
849 | css_sch_device_unregister(sch); | 867 | css_sch_device_unregister(sch); |
868 | /* Put reference from io_subchannel_create_ccwdev(). */ | ||
869 | put_device(&sch->dev); | ||
870 | /* Give up initial reference. */ | ||
871 | put_device(&cdev->dev); | ||
850 | } | 872 | } |
851 | } | 873 | } |
852 | 874 | ||
@@ -868,15 +890,20 @@ void ccw_device_move_to_orphanage(struct work_struct *work) | |||
868 | dev_id.devno = sch->schib.pmcw.dev; | 890 | dev_id.devno = sch->schib.pmcw.dev; |
869 | dev_id.ssid = sch->schid.ssid; | 891 | dev_id.ssid = sch->schid.ssid; |
870 | 892 | ||
893 | /* Increase refcount for pseudo subchannel. */ | ||
894 | get_device(&css->pseudo_subchannel->dev); | ||
871 | /* | 895 | /* |
872 | * Move the orphaned ccw device to the orphanage so the replacing | 896 | * Move the orphaned ccw device to the orphanage so the replacing |
873 | * ccw device can take its place on the subchannel. | 897 | * ccw device can take its place on the subchannel. |
898 | * Note: device_move() changes cdev->dev.parent | ||
874 | */ | 899 | */ |
875 | ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev); | 900 | ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev); |
876 | if (ret) { | 901 | if (ret) { |
877 | CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed " | 902 | CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed " |
878 | "(ret=%d)!\n", cdev->private->dev_id.ssid, | 903 | "(ret=%d)!\n", cdev->private->dev_id.ssid, |
879 | cdev->private->dev_id.devno, ret); | 904 | cdev->private->dev_id.devno, ret); |
905 | /* Decrease refcount for pseudo subchannel again. */ | ||
906 | put_device(&css->pseudo_subchannel->dev); | ||
880 | return; | 907 | return; |
881 | } | 908 | } |
882 | cdev->ccwlock = css->pseudo_subchannel->lock; | 909 | cdev->ccwlock = css->pseudo_subchannel->lock; |
@@ -890,6 +917,8 @@ void ccw_device_move_to_orphanage(struct work_struct *work) | |||
890 | sch_attach_disconnected_device(sch, replacing_cdev); | 917 | sch_attach_disconnected_device(sch, replacing_cdev); |
891 | /* Release reference from get_disc_ccwdev_by_dev_id() */ | 918 | /* Release reference from get_disc_ccwdev_by_dev_id() */ |
892 | put_device(&replacing_cdev->dev); | 919 | put_device(&replacing_cdev->dev); |
920 | /* Release reference of subchannel from old cdev. */ | ||
921 | put_device(&sch->dev); | ||
893 | return; | 922 | return; |
894 | } | 923 | } |
895 | replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id); | 924 | replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id); |
@@ -897,9 +926,13 @@ void ccw_device_move_to_orphanage(struct work_struct *work) | |||
897 | sch_attach_orphaned_device(sch, replacing_cdev); | 926 | sch_attach_orphaned_device(sch, replacing_cdev); |
898 | /* Release reference from get_orphaned_ccwdev_by_dev_id() */ | 927 | /* Release reference from get_orphaned_ccwdev_by_dev_id() */ |
899 | put_device(&replacing_cdev->dev); | 928 | put_device(&replacing_cdev->dev); |
929 | /* Release reference of subchannel from old cdev. */ | ||
930 | put_device(&sch->dev); | ||
900 | return; | 931 | return; |
901 | } | 932 | } |
902 | sch_create_and_recog_new_device(sch); | 933 | sch_create_and_recog_new_device(sch); |
934 | /* Release reference of subchannel from old cdev. */ | ||
935 | put_device(&sch->dev); | ||
903 | } | 936 | } |
904 | 937 | ||
905 | /* | 938 | /* |
@@ -948,13 +981,13 @@ io_subchannel_register(struct work_struct *work) | |||
948 | CIO_MSG_EVENT(0, "Could not register ccw dev 0.%x.%04x: %d\n", | 981 | CIO_MSG_EVENT(0, "Could not register ccw dev 0.%x.%04x: %d\n", |
949 | cdev->private->dev_id.ssid, | 982 | cdev->private->dev_id.ssid, |
950 | cdev->private->dev_id.devno, ret); | 983 | cdev->private->dev_id.devno, ret); |
951 | put_device(&cdev->dev); | ||
952 | spin_lock_irqsave(sch->lock, flags); | 984 | spin_lock_irqsave(sch->lock, flags); |
953 | sch_set_cdev(sch, NULL); | 985 | sch_set_cdev(sch, NULL); |
954 | spin_unlock_irqrestore(sch->lock, flags); | 986 | spin_unlock_irqrestore(sch->lock, flags); |
955 | kfree (cdev->private); | 987 | /* Release reference for workqueue processing. */ |
956 | kfree (cdev); | 988 | put_device(&cdev->dev); |
957 | put_device(&sch->dev); | 989 | /* Release initial device reference. */ |
990 | put_device(&cdev->dev); | ||
958 | if (atomic_dec_and_test(&ccw_device_init_count)) | 991 | if (atomic_dec_and_test(&ccw_device_init_count)) |
959 | wake_up(&ccw_device_init_wq); | 992 | wake_up(&ccw_device_init_wq); |
960 | return; | 993 | return; |
@@ -962,7 +995,6 @@ io_subchannel_register(struct work_struct *work) | |||
962 | put_device(&cdev->dev); | 995 | put_device(&cdev->dev); |
963 | out: | 996 | out: |
964 | cdev->private->flags.recog_done = 1; | 997 | cdev->private->flags.recog_done = 1; |
965 | put_device(&sch->dev); | ||
966 | wake_up(&cdev->private->wait_q); | 998 | wake_up(&cdev->private->wait_q); |
967 | if (atomic_dec_and_test(&ccw_device_init_count)) | 999 | if (atomic_dec_and_test(&ccw_device_init_count)) |
968 | wake_up(&ccw_device_init_wq); | 1000 | wake_up(&ccw_device_init_wq); |
@@ -1012,8 +1044,6 @@ io_subchannel_recog_done(struct ccw_device *cdev) | |||
1012 | PREPARE_WORK(&cdev->private->kick_work, | 1044 | PREPARE_WORK(&cdev->private->kick_work, |
1013 | ccw_device_call_sch_unregister); | 1045 | ccw_device_call_sch_unregister); |
1014 | queue_work(slow_path_wq, &cdev->private->kick_work); | 1046 | queue_work(slow_path_wq, &cdev->private->kick_work); |
1015 | /* Release subchannel reference for asynchronous recognition. */ | ||
1016 | put_device(&sch->dev); | ||
1017 | if (atomic_dec_and_test(&ccw_device_init_count)) | 1047 | if (atomic_dec_and_test(&ccw_device_init_count)) |
1018 | wake_up(&ccw_device_init_wq); | 1048 | wake_up(&ccw_device_init_wq); |
1019 | break; | 1049 | break; |
@@ -1084,10 +1114,15 @@ static void ccw_device_move_to_sch(struct work_struct *work) | |||
1084 | priv = container_of(work, struct ccw_device_private, kick_work); | 1114 | priv = container_of(work, struct ccw_device_private, kick_work); |
1085 | sch = priv->sch; | 1115 | sch = priv->sch; |
1086 | cdev = priv->cdev; | 1116 | cdev = priv->cdev; |
1087 | former_parent = ccw_device_is_orphan(cdev) ? | 1117 | former_parent = to_subchannel(cdev->dev.parent); |
1088 | NULL : to_subchannel(get_device(cdev->dev.parent)); | 1118 | /* Get reference for new parent. */ |
1119 | if (!get_device(&sch->dev)) | ||
1120 | return; | ||
1089 | mutex_lock(&sch->reg_mutex); | 1121 | mutex_lock(&sch->reg_mutex); |
1090 | /* Try to move the ccw device to its new subchannel. */ | 1122 | /* |
1123 | * Try to move the ccw device to its new subchannel. | ||
1124 | * Note: device_move() changes cdev->dev.parent | ||
1125 | */ | ||
1091 | rc = device_move(&cdev->dev, &sch->dev); | 1126 | rc = device_move(&cdev->dev, &sch->dev); |
1092 | mutex_unlock(&sch->reg_mutex); | 1127 | mutex_unlock(&sch->reg_mutex); |
1093 | if (rc) { | 1128 | if (rc) { |
@@ -1097,9 +1132,11 @@ static void ccw_device_move_to_sch(struct work_struct *work) | |||
1097 | cdev->private->dev_id.devno, sch->schid.ssid, | 1132 | cdev->private->dev_id.devno, sch->schid.ssid, |
1098 | sch->schid.sch_no, rc); | 1133 | sch->schid.sch_no, rc); |
1099 | css_sch_device_unregister(sch); | 1134 | css_sch_device_unregister(sch); |
1135 | /* Put reference for new parent again. */ | ||
1136 | put_device(&sch->dev); | ||
1100 | goto out; | 1137 | goto out; |
1101 | } | 1138 | } |
1102 | if (former_parent) { | 1139 | if (!sch_is_pseudo_sch(former_parent)) { |
1103 | spin_lock_irq(former_parent->lock); | 1140 | spin_lock_irq(former_parent->lock); |
1104 | sch_set_cdev(former_parent, NULL); | 1141 | sch_set_cdev(former_parent, NULL); |
1105 | spin_unlock_irq(former_parent->lock); | 1142 | spin_unlock_irq(former_parent->lock); |
@@ -1110,8 +1147,8 @@ static void ccw_device_move_to_sch(struct work_struct *work) | |||
1110 | } | 1147 | } |
1111 | sch_attach_device(sch, cdev); | 1148 | sch_attach_device(sch, cdev); |
1112 | out: | 1149 | out: |
1113 | if (former_parent) | 1150 | /* Put reference for old parent. */ |
1114 | put_device(&former_parent->dev); | 1151 | put_device(&former_parent->dev); |
1115 | put_device(&cdev->dev); | 1152 | put_device(&cdev->dev); |
1116 | } | 1153 | } |
1117 | 1154 | ||