aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390
diff options
context:
space:
mode:
authorCornelia Huck <cornelia.huck@de.ibm.com>2008-12-25 07:39:07 -0500
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2008-12-25 07:39:08 -0500
commit6eff208f479d6fe99fd92c0e6bf7e930bb45cd30 (patch)
tree9dd40942bd0083873471f91e21d07b865a228414 /drivers/s390
parent9cd67421977a701272820987ff9e6f197b1b97b7 (diff)
[S390] cio: Fix I/O subchannel refcounting.
Subchannel refcounting was incorrect in some places, especially a refcount was missing when ccw_device_call_sch_unregister() was called and the refcount was not correctly switched after moving devices. Fix this by establishing the following rules: - The ccw_device obtains a reference on its parent subchannel when dev.parent is set and gives it up in its release function. This is needed because we need a parent reference for correct refcounting even before the ccw device is (if at all) registered. - When calling device_move(), obtain a reference on the new subchannel before moving the ccw device and give up the reference on the old parent after moving. This brings the refcount in line with the first rule. Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/cio/device.c75
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
811static void sch_attach_orphaned_device(struct subchannel *sch, 819static 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
828static void sch_create_and_recog_new_device(struct subchannel *sch) 848static 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);
963out: 996out:
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);
1112out: 1149out:
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