aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/s390/char/vmur.c218
-rw-r--r--drivers/s390/char/vmur.h1
2 files changed, 161 insertions, 58 deletions
diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c
index 2d96c958df64..d70a6e65bf14 100644
--- a/drivers/s390/char/vmur.c
+++ b/drivers/s390/char/vmur.c
@@ -69,8 +69,26 @@ static struct ccw_driver ur_driver = {
69 .set_offline = ur_set_offline, 69 .set_offline = ur_set_offline,
70}; 70};
71 71
72static DEFINE_MUTEX(vmur_mutex);
73
72/* 74/*
73 * Allocation, freeing, getting and putting of urdev structures 75 * Allocation, freeing, getting and putting of urdev structures
76 *
77 * Each ur device (urd) contains a reference to its corresponding ccw device
78 * (cdev) using the urd->cdev pointer. Each ccw device has a reference to the
79 * ur device using the cdev->dev.driver_data pointer.
80 *
81 * urd references:
82 * - ur_probe gets a urd reference, ur_remove drops the reference
83 * (cdev->dev.driver_data)
84 * - ur_open gets a urd reference, ur_relase drops the reference
85 * (urf->urd)
86 *
87 * cdev references:
88 * - urdev_alloc get a cdev reference (urd->cdev)
89 * - urdev_free drops the cdev reference (urd->cdev)
90 *
91 * Setting and clearing of cdev->dev.driver_data is protected by the ccwdev lock
74 */ 92 */
75static struct urdev *urdev_alloc(struct ccw_device *cdev) 93static struct urdev *urdev_alloc(struct ccw_device *cdev)
76{ 94{
@@ -79,42 +97,61 @@ static struct urdev *urdev_alloc(struct ccw_device *cdev)
79 urd = kzalloc(sizeof(struct urdev), GFP_KERNEL); 97 urd = kzalloc(sizeof(struct urdev), GFP_KERNEL);
80 if (!urd) 98 if (!urd)
81 return NULL; 99 return NULL;
82 urd->cdev = cdev;
83 urd->reclen = cdev->id.driver_info; 100 urd->reclen = cdev->id.driver_info;
84 ccw_device_get_id(cdev, &urd->dev_id); 101 ccw_device_get_id(cdev, &urd->dev_id);
85 mutex_init(&urd->io_mutex); 102 mutex_init(&urd->io_mutex);
86 mutex_init(&urd->open_mutex); 103 mutex_init(&urd->open_mutex);
104 atomic_set(&urd->ref_count, 1);
105 urd->cdev = cdev;
106 get_device(&cdev->dev);
87 return urd; 107 return urd;
88} 108}
89 109
90static void urdev_free(struct urdev *urd) 110static void urdev_free(struct urdev *urd)
91{ 111{
112 TRACE("urdev_free: %p\n", urd);
113 if (urd->cdev)
114 put_device(&urd->cdev->dev);
92 kfree(urd); 115 kfree(urd);
93} 116}
94 117
95/* 118static void urdev_get(struct urdev *urd)
96 * This is how the character device driver gets a reference to a 119{
97 * ur device. When this call returns successfully, a reference has 120 atomic_inc(&urd->ref_count);
98 * been taken (by get_device) on the underlying kobject. The recipient 121}
99 * of this urdev pointer must eventually drop it with urdev_put(urd) 122
100 * which does the corresponding put_device(). 123static struct urdev *urdev_get_from_cdev(struct ccw_device *cdev)
101 */ 124{
125 struct urdev *urd;
126 unsigned long flags;
127
128 spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
129 urd = cdev->dev.driver_data;
130 if (urd)
131 urdev_get(urd);
132 spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
133 return urd;
134}
135
102static struct urdev *urdev_get_from_devno(u16 devno) 136static struct urdev *urdev_get_from_devno(u16 devno)
103{ 137{
104 char bus_id[16]; 138 char bus_id[16];
105 struct ccw_device *cdev; 139 struct ccw_device *cdev;
140 struct urdev *urd;
106 141
107 sprintf(bus_id, "0.0.%04x", devno); 142 sprintf(bus_id, "0.0.%04x", devno);
108 cdev = get_ccwdev_by_busid(&ur_driver, bus_id); 143 cdev = get_ccwdev_by_busid(&ur_driver, bus_id);
109 if (!cdev) 144 if (!cdev)
110 return NULL; 145 return NULL;
111 146 urd = urdev_get_from_cdev(cdev);
112 return cdev->dev.driver_data; 147 put_device(&cdev->dev);
148 return urd;
113} 149}
114 150
115static void urdev_put(struct urdev *urd) 151static void urdev_put(struct urdev *urd)
116{ 152{
117 put_device(&urd->cdev->dev); 153 if (atomic_dec_and_test(&urd->ref_count))
154 urdev_free(urd);
118} 155}
119 156
120/* 157/*
@@ -246,6 +283,7 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm,
246 return; 283 return;
247 } 284 }
248 urd = cdev->dev.driver_data; 285 urd = cdev->dev.driver_data;
286 BUG_ON(!urd);
249 /* On special conditions irb is an error pointer */ 287 /* On special conditions irb is an error pointer */
250 if (IS_ERR(irb)) 288 if (IS_ERR(irb))
251 urd->io_request_rc = PTR_ERR(irb); 289 urd->io_request_rc = PTR_ERR(irb);
@@ -263,9 +301,15 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm,
263static ssize_t ur_attr_reclen_show(struct device *dev, 301static ssize_t ur_attr_reclen_show(struct device *dev,
264 struct device_attribute *attr, char *buf) 302 struct device_attribute *attr, char *buf)
265{ 303{
266 struct urdev *urd = dev->driver_data; 304 struct urdev *urd;
305 int rc;
267 306
268 return sprintf(buf, "%zu\n", urd->reclen); 307 urd = urdev_get_from_cdev(to_ccwdev(dev));
308 if (!urd)
309 return -ENODEV;
310 rc = sprintf(buf, "%zu\n", urd->reclen);
311 urdev_put(urd);
312 return rc;
269} 313}
270 314
271static DEVICE_ATTR(reclen, 0444, ur_attr_reclen_show, NULL); 315static DEVICE_ATTR(reclen, 0444, ur_attr_reclen_show, NULL);
@@ -726,64 +770,63 @@ static struct file_operations ur_fops = {
726 770
727/* 771/*
728 * ccw_device infrastructure: 772 * ccw_device infrastructure:
729 * ur_probe gets its own ref to the device (i.e. get_device), 773 * ur_probe creates the struct urdev (with refcount = 1), the device
730 * creates the struct urdev, the device attributes, sets up 774 * attributes, sets up the interrupt handler and validates the virtual
731 * the interrupt handler and validates the virtual unit record device. 775 * unit record device.
732 * ur_remove removes the device attributes, frees the struct urdev 776 * ur_remove removes the device attributes and drops the reference to
733 * and drops (put_device) the ref to the device we got in ur_probe. 777 * struct urdev.
778 *
779 * ur_probe, ur_remove, ur_set_online and ur_set_offline are serialized
780 * by the vmur_mutex lock.
781 *
782 * urd->char_device is used as indication that the online function has
783 * been completed successfully.
734 */ 784 */
735static int ur_probe(struct ccw_device *cdev) 785static int ur_probe(struct ccw_device *cdev)
736{ 786{
737 struct urdev *urd; 787 struct urdev *urd;
738 int rc; 788 int rc;
739 789
740 TRACE("ur_probe: cdev=%p state=%d\n", cdev, *(int *) cdev->private); 790 TRACE("ur_probe: cdev=%p\n", cdev);
741
742 if (!get_device(&cdev->dev))
743 return -ENODEV;
744 791
792 mutex_lock(&vmur_mutex);
745 urd = urdev_alloc(cdev); 793 urd = urdev_alloc(cdev);
746 if (!urd) { 794 if (!urd) {
747 rc = -ENOMEM; 795 rc = -ENOMEM;
748 goto fail; 796 goto fail_unlock;
749 } 797 }
798
750 rc = ur_create_attributes(&cdev->dev); 799 rc = ur_create_attributes(&cdev->dev);
751 if (rc) { 800 if (rc) {
752 rc = -ENOMEM; 801 rc = -ENOMEM;
753 goto fail; 802 goto fail_urdev_put;
754 } 803 }
755 cdev->dev.driver_data = urd;
756 cdev->handler = ur_int_handler; 804 cdev->handler = ur_int_handler;
757 805
758 /* validate virtual unit record device */ 806 /* validate virtual unit record device */
759 urd->class = get_urd_class(urd); 807 urd->class = get_urd_class(urd);
760 if (urd->class < 0) { 808 if (urd->class < 0) {
761 rc = urd->class; 809 rc = urd->class;
762 goto fail; 810 goto fail_remove_attr;
763 } 811 }
764 if ((urd->class != DEV_CLASS_UR_I) && (urd->class != DEV_CLASS_UR_O)) { 812 if ((urd->class != DEV_CLASS_UR_I) && (urd->class != DEV_CLASS_UR_O)) {
765 rc = -ENOTSUPP; 813 rc = -ENOTSUPP;
766 goto fail; 814 goto fail_remove_attr;
767 } 815 }
816 spin_lock_irq(get_ccwdev_lock(cdev));
817 cdev->dev.driver_data = urd;
818 spin_unlock_irq(get_ccwdev_lock(cdev));
768 819
820 mutex_unlock(&vmur_mutex);
769 return 0; 821 return 0;
770 822
771fail: 823fail_remove_attr:
772 urdev_free(urd);
773 put_device(&cdev->dev);
774 return rc;
775}
776
777static void ur_remove(struct ccw_device *cdev)
778{
779 struct urdev *urd = cdev->dev.driver_data;
780
781 TRACE("ur_remove\n");
782 if (cdev->online)
783 ur_set_offline(cdev);
784 ur_remove_attributes(&cdev->dev); 824 ur_remove_attributes(&cdev->dev);
785 urdev_free(urd); 825fail_urdev_put:
786 put_device(&cdev->dev); 826 urdev_put(urd);
827fail_unlock:
828 mutex_unlock(&vmur_mutex);
829 return rc;
787} 830}
788 831
789static int ur_set_online(struct ccw_device *cdev) 832static int ur_set_online(struct ccw_device *cdev)
@@ -792,20 +835,29 @@ static int ur_set_online(struct ccw_device *cdev)
792 int minor, major, rc; 835 int minor, major, rc;
793 char node_id[16]; 836 char node_id[16];
794 837
795 TRACE("ur_set_online: cdev=%p state=%d\n", cdev, 838 TRACE("ur_set_online: cdev=%p\n", cdev);
796 *(int *) cdev->private);
797 839
798 if (!try_module_get(ur_driver.owner)) 840 mutex_lock(&vmur_mutex);
799 return -EINVAL; 841 urd = urdev_get_from_cdev(cdev);
842 if (!urd) {
843 /* ur_remove already deleted our urd */
844 rc = -ENODEV;
845 goto fail_unlock;
846 }
847
848 if (urd->char_device) {
849 /* Another ur_set_online was faster */
850 rc = -EBUSY;
851 goto fail_urdev_put;
852 }
800 853
801 urd = (struct urdev *) cdev->dev.driver_data;
802 minor = urd->dev_id.devno; 854 minor = urd->dev_id.devno;
803 major = MAJOR(ur_first_dev_maj_min); 855 major = MAJOR(ur_first_dev_maj_min);
804 856
805 urd->char_device = cdev_alloc(); 857 urd->char_device = cdev_alloc();
806 if (!urd->char_device) { 858 if (!urd->char_device) {
807 rc = -ENOMEM; 859 rc = -ENOMEM;
808 goto fail_module_put; 860 goto fail_urdev_put;
809 } 861 }
810 862
811 cdev_init(urd->char_device, &ur_fops); 863 cdev_init(urd->char_device, &ur_fops);
@@ -834,29 +886,79 @@ static int ur_set_online(struct ccw_device *cdev)
834 TRACE("ur_set_online: device_create rc=%d\n", rc); 886 TRACE("ur_set_online: device_create rc=%d\n", rc);
835 goto fail_free_cdev; 887 goto fail_free_cdev;
836 } 888 }
837 889 urdev_put(urd);
890 mutex_unlock(&vmur_mutex);
838 return 0; 891 return 0;
839 892
840fail_free_cdev: 893fail_free_cdev:
841 cdev_del(urd->char_device); 894 cdev_del(urd->char_device);
842fail_module_put: 895 urd->char_device = NULL;
843 module_put(ur_driver.owner); 896fail_urdev_put:
844 897 urdev_put(urd);
898fail_unlock:
899 mutex_unlock(&vmur_mutex);
845 return rc; 900 return rc;
846} 901}
847 902
848static int ur_set_offline(struct ccw_device *cdev) 903static int ur_set_offline_force(struct ccw_device *cdev, int force)
849{ 904{
850 struct urdev *urd; 905 struct urdev *urd;
906 int rc;
851 907
852 TRACE("ur_set_offline: cdev=%p cdev->private=%p state=%d\n", 908 TRACE("ur_set_offline: cdev=%p\n", cdev);
853 cdev, cdev->private, *(int *) cdev->private); 909 urd = urdev_get_from_cdev(cdev);
854 urd = (struct urdev *) cdev->dev.driver_data; 910 if (!urd)
911 /* ur_remove already deleted our urd */
912 return -ENODEV;
913 if (!urd->char_device) {
914 /* Another ur_set_offline was faster */
915 rc = -EBUSY;
916 goto fail_urdev_put;
917 }
918 if (!force && (atomic_read(&urd->ref_count) > 2)) {
919 /* There is still a user of urd (e.g. ur_open) */
920 TRACE("ur_set_offline: BUSY\n");
921 rc = -EBUSY;
922 goto fail_urdev_put;
923 }
855 device_destroy(vmur_class, urd->char_device->dev); 924 device_destroy(vmur_class, urd->char_device->dev);
856 cdev_del(urd->char_device); 925 cdev_del(urd->char_device);
857 module_put(ur_driver.owner); 926 urd->char_device = NULL;
927 rc = 0;
858 928
859 return 0; 929fail_urdev_put:
930 urdev_put(urd);
931 return rc;
932}
933
934static int ur_set_offline(struct ccw_device *cdev)
935{
936 int rc;
937
938 mutex_lock(&vmur_mutex);
939 rc = ur_set_offline_force(cdev, 0);
940 mutex_unlock(&vmur_mutex);
941 return rc;
942}
943
944static void ur_remove(struct ccw_device *cdev)
945{
946 unsigned long flags;
947
948 TRACE("ur_remove\n");
949
950 mutex_lock(&vmur_mutex);
951
952 if (cdev->online)
953 ur_set_offline_force(cdev, 1);
954 ur_remove_attributes(&cdev->dev);
955
956 spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
957 urdev_put(cdev->dev.driver_data);
958 cdev->dev.driver_data = NULL;
959 spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
960
961 mutex_unlock(&vmur_mutex);
860} 962}
861 963
862/* 964/*
diff --git a/drivers/s390/char/vmur.h b/drivers/s390/char/vmur.h
index 2b3c564e0472..fa959644735a 100644
--- a/drivers/s390/char/vmur.h
+++ b/drivers/s390/char/vmur.h
@@ -70,6 +70,7 @@ struct urdev {
70 size_t reclen; /* Record length for *write* CCWs */ 70 size_t reclen; /* Record length for *write* CCWs */
71 int class; /* VM device class */ 71 int class; /* VM device class */
72 int io_request_rc; /* return code from I/O request */ 72 int io_request_rc; /* return code from I/O request */
73 atomic_t ref_count; /* reference counter */
73}; 74};
74 75
75/* 76/*