diff options
author | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2007-12-17 10:37:26 -0500 |
---|---|---|
committer | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2007-12-26 12:15:17 -0500 |
commit | e73f4459d969bb266f03dd4cbe21bdba8cb2732c (patch) | |
tree | 5af7655da65f2c33f6ea4efdfaa8b0e0670d6aea /drivers/mtd/ubi/build.c | |
parent | 9f961b57568960a150cc9781c52824c9093a0514 (diff) |
UBI: add UBI devices reference counting
This is one more step on the way to "removable" UBI devices. It
adds reference counting for UBI devices. Every time a volume on
this device is opened - the device's refcount is increased. It
is also increased if someone is reading any sysfs file of this
UBI device or of one of its volumes.
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Diffstat (limited to 'drivers/mtd/ubi/build.c')
-rw-r--r-- | drivers/mtd/ubi/build.c | 142 |
1 files changed, 127 insertions, 15 deletions
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 3f37b16f8774..a4faf71ee3f2 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c | |||
@@ -64,9 +64,6 @@ static int mtd_devs = 0; | |||
64 | /* MTD devices specification parameters */ | 64 | /* MTD devices specification parameters */ |
65 | static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES]; | 65 | static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES]; |
66 | 66 | ||
67 | /* All UBI devices in system */ | ||
68 | struct ubi_device *ubi_devices[UBI_MAX_DEVICES]; | ||
69 | |||
70 | /* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */ | 67 | /* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */ |
71 | struct class *ubi_class; | 68 | struct class *ubi_class; |
72 | 69 | ||
@@ -83,6 +80,12 @@ static struct miscdevice ubi_ctrl_cdev = { | |||
83 | .fops = &ubi_ctrl_cdev_operations, | 80 | .fops = &ubi_ctrl_cdev_operations, |
84 | }; | 81 | }; |
85 | 82 | ||
83 | /* All UBI devices in system */ | ||
84 | static struct ubi_device *ubi_devices[UBI_MAX_DEVICES]; | ||
85 | |||
86 | /* Protects @ubi_devices and @ubi->ref_count */ | ||
87 | static DEFINE_SPINLOCK(ubi_devices_lock); | ||
88 | |||
86 | /* "Show" method for files in '/<sysfs>/class/ubi/' */ | 89 | /* "Show" method for files in '/<sysfs>/class/ubi/' */ |
87 | static ssize_t ubi_version_show(struct class *class, char *buf) | 90 | static ssize_t ubi_version_show(struct class *class, char *buf) |
88 | { | 91 | { |
@@ -118,37 +121,145 @@ static struct device_attribute dev_min_io_size = | |||
118 | static struct device_attribute dev_bgt_enabled = | 121 | static struct device_attribute dev_bgt_enabled = |
119 | __ATTR(bgt_enabled, S_IRUGO, dev_attribute_show, NULL); | 122 | __ATTR(bgt_enabled, S_IRUGO, dev_attribute_show, NULL); |
120 | 123 | ||
124 | /** | ||
125 | * ubi_get_device - get UBI device. | ||
126 | * @ubi_num: UBI device number | ||
127 | * | ||
128 | * This function returns UBI device description object for UBI device number | ||
129 | * @ubi_num, or %NULL if the device does not exist. This function increases the | ||
130 | * device reference count to prevent removal of the device. In other words, the | ||
131 | * device cannot be removed if its reference count is not zero. | ||
132 | */ | ||
133 | struct ubi_device *ubi_get_device(int ubi_num) | ||
134 | { | ||
135 | struct ubi_device *ubi; | ||
136 | |||
137 | spin_lock(&ubi_devices_lock); | ||
138 | ubi = ubi_devices[ubi_num]; | ||
139 | if (ubi) { | ||
140 | ubi_assert(ubi->ref_count >= 0); | ||
141 | ubi->ref_count += 1; | ||
142 | get_device(&ubi->dev); | ||
143 | } | ||
144 | spin_unlock(&ubi_devices_lock); | ||
145 | |||
146 | return ubi; | ||
147 | } | ||
148 | |||
149 | /** | ||
150 | * ubi_put_device - drop an UBI device reference. | ||
151 | * @ubi: UBI device description object | ||
152 | */ | ||
153 | void ubi_put_device(struct ubi_device *ubi) | ||
154 | { | ||
155 | spin_lock(&ubi_devices_lock); | ||
156 | ubi->ref_count -= 1; | ||
157 | put_device(&ubi->dev); | ||
158 | spin_unlock(&ubi_devices_lock); | ||
159 | } | ||
160 | |||
161 | /** | ||
162 | * ubi_get_by_major - get UBI device description object by character device | ||
163 | * major number. | ||
164 | * @major: major number | ||
165 | * | ||
166 | * This function is similar to 'ubi_get_device()', but it searches the device | ||
167 | * by its major number. | ||
168 | */ | ||
169 | struct ubi_device *ubi_get_by_major(int major) | ||
170 | { | ||
171 | int i; | ||
172 | struct ubi_device *ubi; | ||
173 | |||
174 | spin_lock(&ubi_devices_lock); | ||
175 | for (i = 0; i < UBI_MAX_DEVICES; i++) { | ||
176 | ubi = ubi_devices[i]; | ||
177 | if (ubi && MAJOR(ubi->cdev.dev) == major) { | ||
178 | ubi_assert(ubi->ref_count >= 0); | ||
179 | ubi->ref_count += 1; | ||
180 | get_device(&ubi->dev); | ||
181 | spin_unlock(&ubi_devices_lock); | ||
182 | return ubi; | ||
183 | } | ||
184 | } | ||
185 | spin_unlock(&ubi_devices_lock); | ||
186 | |||
187 | return NULL; | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * ubi_major2num - get UBI device number by character device major number. | ||
192 | * @major: major number | ||
193 | * | ||
194 | * This function searches UBI device number object by its major number. If UBI | ||
195 | * device was not found, this function returns -ENODEV, othewise the UBI device | ||
196 | * number is returned. | ||
197 | */ | ||
198 | int ubi_major2num(int major) | ||
199 | { | ||
200 | int i, ubi_num = -ENODEV; | ||
201 | |||
202 | spin_lock(&ubi_devices_lock); | ||
203 | for (i = 0; i < UBI_MAX_DEVICES; i++) { | ||
204 | struct ubi_device *ubi = ubi_devices[i]; | ||
205 | |||
206 | if (ubi && MAJOR(ubi->cdev.dev) == major) { | ||
207 | ubi_num = ubi->ubi_num; | ||
208 | break; | ||
209 | } | ||
210 | } | ||
211 | spin_unlock(&ubi_devices_lock); | ||
212 | |||
213 | return ubi_num; | ||
214 | } | ||
215 | |||
121 | /* "Show" method for files in '/<sysfs>/class/ubi/ubiX/' */ | 216 | /* "Show" method for files in '/<sysfs>/class/ubi/ubiX/' */ |
122 | static ssize_t dev_attribute_show(struct device *dev, | 217 | static ssize_t dev_attribute_show(struct device *dev, |
123 | struct device_attribute *attr, char *buf) | 218 | struct device_attribute *attr, char *buf) |
124 | { | 219 | { |
125 | const struct ubi_device *ubi; | 220 | ssize_t ret; |
221 | struct ubi_device *ubi; | ||
126 | 222 | ||
223 | /* | ||
224 | * The below code looks weird, but it actually makes sense. We get the | ||
225 | * UBI device reference from the contained 'struct ubi_device'. But it | ||
226 | * is unclear if the device was removed or not yet. Indeed, if the | ||
227 | * device was removed before we increased its reference count, | ||
228 | * 'ubi_get_device()' will return -ENODEV and we fail. | ||
229 | * | ||
230 | * Remember, 'struct ubi_device' is freed in the release function, so | ||
231 | * we still can use 'ubi->ubi_num'. | ||
232 | */ | ||
127 | ubi = container_of(dev, struct ubi_device, dev); | 233 | ubi = container_of(dev, struct ubi_device, dev); |
234 | ubi = ubi_get_device(ubi->ubi_num); | ||
235 | if (!ubi) | ||
236 | return -ENODEV; | ||
237 | |||
128 | if (attr == &dev_eraseblock_size) | 238 | if (attr == &dev_eraseblock_size) |
129 | return sprintf(buf, "%d\n", ubi->leb_size); | 239 | ret = sprintf(buf, "%d\n", ubi->leb_size); |
130 | else if (attr == &dev_avail_eraseblocks) | 240 | else if (attr == &dev_avail_eraseblocks) |
131 | return sprintf(buf, "%d\n", ubi->avail_pebs); | 241 | ret = sprintf(buf, "%d\n", ubi->avail_pebs); |
132 | else if (attr == &dev_total_eraseblocks) | 242 | else if (attr == &dev_total_eraseblocks) |
133 | return sprintf(buf, "%d\n", ubi->good_peb_count); | 243 | ret = sprintf(buf, "%d\n", ubi->good_peb_count); |
134 | else if (attr == &dev_volumes_count) | 244 | else if (attr == &dev_volumes_count) |
135 | return sprintf(buf, "%d\n", ubi->vol_count); | 245 | ret = sprintf(buf, "%d\n", ubi->vol_count); |
136 | else if (attr == &dev_max_ec) | 246 | else if (attr == &dev_max_ec) |
137 | return sprintf(buf, "%d\n", ubi->max_ec); | 247 | ret = sprintf(buf, "%d\n", ubi->max_ec); |
138 | else if (attr == &dev_reserved_for_bad) | 248 | else if (attr == &dev_reserved_for_bad) |
139 | return sprintf(buf, "%d\n", ubi->beb_rsvd_pebs); | 249 | ret = sprintf(buf, "%d\n", ubi->beb_rsvd_pebs); |
140 | else if (attr == &dev_bad_peb_count) | 250 | else if (attr == &dev_bad_peb_count) |
141 | return sprintf(buf, "%d\n", ubi->bad_peb_count); | 251 | ret = sprintf(buf, "%d\n", ubi->bad_peb_count); |
142 | else if (attr == &dev_max_vol_count) | 252 | else if (attr == &dev_max_vol_count) |
143 | return sprintf(buf, "%d\n", ubi->vtbl_slots); | 253 | ret = sprintf(buf, "%d\n", ubi->vtbl_slots); |
144 | else if (attr == &dev_min_io_size) | 254 | else if (attr == &dev_min_io_size) |
145 | return sprintf(buf, "%d\n", ubi->min_io_size); | 255 | ret = sprintf(buf, "%d\n", ubi->min_io_size); |
146 | else if (attr == &dev_bgt_enabled) | 256 | else if (attr == &dev_bgt_enabled) |
147 | return sprintf(buf, "%d\n", ubi->thread_enabled); | 257 | ret = sprintf(buf, "%d\n", ubi->thread_enabled); |
148 | else | 258 | else |
149 | BUG(); | 259 | BUG(); |
150 | 260 | ||
151 | return 0; | 261 | ubi_put_device(ubi); |
262 | return ret; | ||
152 | } | 263 | } |
153 | 264 | ||
154 | /* Fake "release" method for UBI devices */ | 265 | /* Fake "release" method for UBI devices */ |
@@ -670,6 +781,7 @@ static void detach_mtd_dev(struct ubi_device *ubi) | |||
670 | int ubi_num = ubi->ubi_num, mtd_num = ubi->mtd->index; | 781 | int ubi_num = ubi->ubi_num, mtd_num = ubi->mtd->index; |
671 | 782 | ||
672 | dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num); | 783 | dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num); |
784 | ubi_assert(ubi->ref_count == 0); | ||
673 | uif_close(ubi); | 785 | uif_close(ubi); |
674 | ubi_eba_close(ubi); | 786 | ubi_eba_close(ubi); |
675 | ubi_wl_close(ubi); | 787 | ubi_wl_close(ubi); |