]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | From: Gerald Schaefer <geraldsc@de.ibm.com> |
2 | Subject: cio: ccwgroup - fix unbind behaviour | |
3 | References: bnc#482818,LTC#52098 | |
4 | ||
5 | Symptom: Oops after unbinding a ccwgroup device. | |
6 | Problem: There is no special callback to tell a driver to unbind | |
7 | a device - the normal remove is called. In this case | |
8 | the device remains partly registered in the driver core. | |
9 | Solution: Use a bus notifier function to handle this special case | |
10 | and call ungroup since this virtual device can not exist | |
11 | without a driver. | |
12 | ||
13 | Acked-by: John Jolly <jjolly@suse.de> | |
14 | --- | |
15 | drivers/s390/cio/ccwgroup.c | 73 +++++++++++++++++++++++++++++++++----------- | |
16 | 1 file changed, 56 insertions(+), 17 deletions(-) | |
17 | ||
18 | Index: linux-sles11/drivers/s390/cio/ccwgroup.c | |
19 | =================================================================== | |
20 | --- linux-sles11.orig/drivers/s390/cio/ccwgroup.c 2009-03-09 15:36:56.000000000 +0100 | |
21 | +++ linux-sles11/drivers/s390/cio/ccwgroup.c 2009-03-09 15:53:56.000000000 +0100 | |
22 | @@ -313,16 +313,32 @@ error: | |
23 | } | |
24 | EXPORT_SYMBOL(ccwgroup_create_from_string); | |
25 | ||
26 | -static int __init | |
27 | -init_ccwgroup (void) | |
28 | +static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action, | |
29 | + void *data); | |
30 | + | |
31 | +static struct notifier_block ccwgroup_nb = { | |
32 | + .notifier_call = ccwgroup_notifier | |
33 | +}; | |
34 | + | |
35 | +static int __init init_ccwgroup(void) | |
36 | { | |
37 | - return bus_register (&ccwgroup_bus_type); | |
38 | + int ret; | |
39 | + | |
40 | + ret = bus_register(&ccwgroup_bus_type); | |
41 | + if (ret) | |
42 | + return ret; | |
43 | + | |
44 | + ret = bus_register_notifier(&ccwgroup_bus_type, &ccwgroup_nb); | |
45 | + if (ret) | |
46 | + bus_unregister(&ccwgroup_bus_type); | |
47 | + | |
48 | + return ret; | |
49 | } | |
50 | ||
51 | -static void __exit | |
52 | -cleanup_ccwgroup (void) | |
53 | +static void __exit cleanup_ccwgroup(void) | |
54 | { | |
55 | - bus_unregister (&ccwgroup_bus_type); | |
56 | + bus_unregister_notifier(&ccwgroup_bus_type, &ccwgroup_nb); | |
57 | + bus_unregister(&ccwgroup_bus_type); | |
58 | } | |
59 | ||
60 | module_init(init_ccwgroup); | |
61 | @@ -390,27 +406,28 @@ ccwgroup_online_store (struct device *de | |
62 | unsigned long value; | |
63 | int ret; | |
64 | ||
65 | - gdev = to_ccwgroupdev(dev); | |
66 | if (!dev->driver) | |
67 | - return count; | |
68 | + return -ENODEV; | |
69 | + | |
70 | + gdev = to_ccwgroupdev(dev); | |
71 | + gdrv = to_ccwgroupdrv(dev->driver); | |
72 | ||
73 | - gdrv = to_ccwgroupdrv (gdev->dev.driver); | |
74 | if (!try_module_get(gdrv->owner)) | |
75 | return -EINVAL; | |
76 | ||
77 | ret = strict_strtoul(buf, 0, &value); | |
78 | if (ret) | |
79 | goto out; | |
80 | - ret = count; | |
81 | + | |
82 | if (value == 1) | |
83 | - ccwgroup_set_online(gdev); | |
84 | + ret = ccwgroup_set_online(gdev); | |
85 | else if (value == 0) | |
86 | - ccwgroup_set_offline(gdev); | |
87 | + ret = ccwgroup_set_offline(gdev); | |
88 | else | |
89 | ret = -EINVAL; | |
90 | out: | |
91 | module_put(gdrv->owner); | |
92 | - return ret; | |
93 | + return (ret == 0) ? count : ret; | |
94 | } | |
95 | ||
96 | static ssize_t | |
97 | @@ -452,13 +469,18 @@ ccwgroup_remove (struct device *dev) | |
98 | struct ccwgroup_device *gdev; | |
99 | struct ccwgroup_driver *gdrv; | |
100 | ||
101 | + device_remove_file(dev, &dev_attr_online); | |
102 | + device_remove_file(dev, &dev_attr_ungroup); | |
103 | + | |
104 | + if (!dev->driver) | |
105 | + return 0; | |
106 | + | |
107 | gdev = to_ccwgroupdev(dev); | |
108 | gdrv = to_ccwgroupdrv(dev->driver); | |
109 | ||
110 | - device_remove_file(dev, &dev_attr_online); | |
111 | - | |
112 | - if (gdrv && gdrv->remove) | |
113 | + if (gdrv->remove) | |
114 | gdrv->remove(gdev); | |
115 | + | |
116 | return 0; | |
117 | } | |
118 | ||
119 | @@ -467,9 +489,13 @@ static void ccwgroup_shutdown(struct dev | |
120 | struct ccwgroup_device *gdev; | |
121 | struct ccwgroup_driver *gdrv; | |
122 | ||
123 | + if (!dev->driver) | |
124 | + return; | |
125 | + | |
126 | gdev = to_ccwgroupdev(dev); | |
127 | gdrv = to_ccwgroupdrv(dev->driver); | |
128 | - if (gdrv && gdrv->shutdown) | |
129 | + | |
130 | + if (gdrv->shutdown) | |
131 | gdrv->shutdown(gdev); | |
132 | } | |
133 | ||
134 | @@ -482,6 +508,19 @@ static struct bus_type ccwgroup_bus_type | |
135 | .shutdown = ccwgroup_shutdown, | |
136 | }; | |
137 | ||
138 | + | |
139 | +static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action, | |
140 | + void *data) | |
141 | +{ | |
142 | + struct device *dev = data; | |
143 | + | |
144 | + if (action == BUS_NOTIFY_UNBIND_DRIVER) | |
145 | + device_schedule_callback(dev, ccwgroup_ungroup_callback); | |
146 | + | |
147 | + return NOTIFY_OK; | |
148 | +} | |
149 | + | |
150 | + | |
151 | /** | |
152 | * ccwgroup_driver_register() - register a ccw group driver | |
153 | * @cdriver: driver to be registered |