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 | |
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')
-rw-r--r-- | drivers/mtd/ubi/build.c | 142 | ||||
-rw-r--r-- | drivers/mtd/ubi/cdev.c | 34 | ||||
-rw-r--r-- | drivers/mtd/ubi/eba.c | 5 | ||||
-rw-r--r-- | drivers/mtd/ubi/kapi.c | 59 | ||||
-rw-r--r-- | drivers/mtd/ubi/ubi.h | 9 | ||||
-rw-r--r-- | drivers/mtd/ubi/vmt.c | 14 | ||||
-rw-r--r-- | drivers/mtd/ubi/wl.c | 1 |
7 files changed, 201 insertions, 63 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); |
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index bc900d24cdba..01978b57e9cb 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c | |||
@@ -56,23 +56,6 @@ | |||
56 | #endif | 56 | #endif |
57 | 57 | ||
58 | /** | 58 | /** |
59 | * major_to_device - get UBI device object by character device major number. | ||
60 | * @major: major number | ||
61 | * | ||
62 | * This function returns a pointer to the UBI device object. | ||
63 | */ | ||
64 | static struct ubi_device *major_to_device(int major) | ||
65 | { | ||
66 | int i; | ||
67 | |||
68 | for (i = 0; i < UBI_MAX_DEVICES; i++) | ||
69 | if (ubi_devices[i] && MAJOR(ubi_devices[i]->cdev.dev) == major) | ||
70 | return ubi_devices[i]; | ||
71 | BUG(); | ||
72 | return NULL; | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * get_exclusive - get exclusive access to an UBI volume. | 59 | * get_exclusive - get exclusive access to an UBI volume. |
77 | * @desc: volume descriptor | 60 | * @desc: volume descriptor |
78 | * | 61 | * |
@@ -129,9 +112,11 @@ static void revoke_exclusive(struct ubi_volume_desc *desc, int mode) | |||
129 | static int vol_cdev_open(struct inode *inode, struct file *file) | 112 | static int vol_cdev_open(struct inode *inode, struct file *file) |
130 | { | 113 | { |
131 | struct ubi_volume_desc *desc; | 114 | struct ubi_volume_desc *desc; |
132 | const struct ubi_device *ubi = major_to_device(imajor(inode)); | 115 | int vol_id = iminor(inode) - 1, mode, ubi_num; |
133 | int vol_id = iminor(inode) - 1; | 116 | |
134 | int mode; | 117 | ubi_num = ubi_major2num(imajor(inode)); |
118 | if (ubi_num < 0) | ||
119 | return ubi_num; | ||
135 | 120 | ||
136 | if (file->f_mode & FMODE_WRITE) | 121 | if (file->f_mode & FMODE_WRITE) |
137 | mode = UBI_READWRITE; | 122 | mode = UBI_READWRITE; |
@@ -140,7 +125,7 @@ static int vol_cdev_open(struct inode *inode, struct file *file) | |||
140 | 125 | ||
141 | dbg_msg("open volume %d, mode %d", vol_id, mode); | 126 | dbg_msg("open volume %d, mode %d", vol_id, mode); |
142 | 127 | ||
143 | desc = ubi_open_volume(ubi->ubi_num, vol_id, mode); | 128 | desc = ubi_open_volume(ubi_num, vol_id, mode); |
144 | if (IS_ERR(desc)) | 129 | if (IS_ERR(desc)) |
145 | return PTR_ERR(desc); | 130 | return PTR_ERR(desc); |
146 | 131 | ||
@@ -586,9 +571,9 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
586 | if (!capable(CAP_SYS_RESOURCE)) | 571 | if (!capable(CAP_SYS_RESOURCE)) |
587 | return -EPERM; | 572 | return -EPERM; |
588 | 573 | ||
589 | ubi = major_to_device(imajor(inode)); | 574 | ubi = ubi_get_by_major(imajor(inode)); |
590 | if (IS_ERR(ubi)) | 575 | if (!ubi) |
591 | return PTR_ERR(ubi); | 576 | return -ENODEV; |
592 | 577 | ||
593 | switch (cmd) { | 578 | switch (cmd) { |
594 | /* Create volume command */ | 579 | /* Create volume command */ |
@@ -695,6 +680,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, | |||
695 | break; | 680 | break; |
696 | } | 681 | } |
697 | 682 | ||
683 | ubi_put_device(ubi); | ||
698 | return err; | 684 | return err; |
699 | } | 685 | } |
700 | 686 | ||
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index c94f475758de..85297cde4ac5 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c | |||
@@ -339,6 +339,7 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, | |||
339 | { | 339 | { |
340 | int err, pnum, vol_id = vol->vol_id; | 340 | int err, pnum, vol_id = vol->vol_id; |
341 | 341 | ||
342 | ubi_assert(ubi->ref_count > 0); | ||
342 | ubi_assert(vol->ref_count > 0); | 343 | ubi_assert(vol->ref_count > 0); |
343 | 344 | ||
344 | if (ubi->ro_mode) | 345 | if (ubi->ro_mode) |
@@ -389,6 +390,7 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, | |||
389 | struct ubi_vid_hdr *vid_hdr; | 390 | struct ubi_vid_hdr *vid_hdr; |
390 | uint32_t uninitialized_var(crc); | 391 | uint32_t uninitialized_var(crc); |
391 | 392 | ||
393 | ubi_assert(ubi->ref_count > 0); | ||
392 | ubi_assert(vol->ref_count > 0); | 394 | ubi_assert(vol->ref_count > 0); |
393 | 395 | ||
394 | err = leb_read_lock(ubi, vol_id, lnum); | 396 | err = leb_read_lock(ubi, vol_id, lnum); |
@@ -614,6 +616,7 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, | |||
614 | int err, pnum, tries = 0, vol_id = vol->vol_id; | 616 | int err, pnum, tries = 0, vol_id = vol->vol_id; |
615 | struct ubi_vid_hdr *vid_hdr; | 617 | struct ubi_vid_hdr *vid_hdr; |
616 | 618 | ||
619 | ubi_assert(ubi->ref_count > 0); | ||
617 | ubi_assert(vol->ref_count > 0); | 620 | ubi_assert(vol->ref_count > 0); |
618 | 621 | ||
619 | if (ubi->ro_mode) | 622 | if (ubi->ro_mode) |
@@ -749,6 +752,7 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, | |||
749 | struct ubi_vid_hdr *vid_hdr; | 752 | struct ubi_vid_hdr *vid_hdr; |
750 | uint32_t crc; | 753 | uint32_t crc; |
751 | 754 | ||
755 | ubi_assert(ubi->ref_count > 0); | ||
752 | ubi_assert(vol->ref_count > 0); | 756 | ubi_assert(vol->ref_count > 0); |
753 | 757 | ||
754 | if (ubi->ro_mode) | 758 | if (ubi->ro_mode) |
@@ -865,6 +869,7 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, | |||
865 | struct ubi_vid_hdr *vid_hdr; | 869 | struct ubi_vid_hdr *vid_hdr; |
866 | uint32_t crc; | 870 | uint32_t crc; |
867 | 871 | ||
872 | ubi_assert(ubi->ref_count > 0); | ||
868 | ubi_assert(vol->ref_count > 0); | 873 | ubi_assert(vol->ref_count > 0); |
869 | 874 | ||
870 | if (ubi->ro_mode) | 875 | if (ubi->ro_mode) |
diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 780c273ff452..4ec3a33b2577 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c | |||
@@ -30,23 +30,27 @@ | |||
30 | * @ubi_num: UBI device number | 30 | * @ubi_num: UBI device number |
31 | * @di: the information is stored here | 31 | * @di: the information is stored here |
32 | * | 32 | * |
33 | * This function returns %0 in case of success and a %-ENODEV if there is no | 33 | * This function returns %0 in case of success, %-EINVAL if the UBI device |
34 | * such UBI device. | 34 | * number is invalid, and %-ENODEV if there is no such UBI device. |
35 | */ | 35 | */ |
36 | int ubi_get_device_info(int ubi_num, struct ubi_device_info *di) | 36 | int ubi_get_device_info(int ubi_num, struct ubi_device_info *di) |
37 | { | 37 | { |
38 | const struct ubi_device *ubi; | 38 | struct ubi_device *ubi; |
39 | 39 | ||
40 | if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES || | 40 | if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) |
41 | !ubi_devices[ubi_num]) | 41 | return -EINVAL; |
42 | |||
43 | ubi = ubi_get_device(ubi_num); | ||
44 | if (!ubi) | ||
42 | return -ENODEV; | 45 | return -ENODEV; |
43 | 46 | ||
44 | ubi = ubi_devices[ubi_num]; | ||
45 | di->ubi_num = ubi->ubi_num; | 47 | di->ubi_num = ubi->ubi_num; |
46 | di->leb_size = ubi->leb_size; | 48 | di->leb_size = ubi->leb_size; |
47 | di->min_io_size = ubi->min_io_size; | 49 | di->min_io_size = ubi->min_io_size; |
48 | di->ro_mode = ubi->ro_mode; | 50 | di->ro_mode = ubi->ro_mode; |
49 | di->cdev = ubi->cdev.dev; | 51 | di->cdev = ubi->cdev.dev; |
52 | |||
53 | ubi_put_device(ubi); | ||
50 | return 0; | 54 | return 0; |
51 | } | 55 | } |
52 | EXPORT_SYMBOL_GPL(ubi_get_device_info); | 56 | EXPORT_SYMBOL_GPL(ubi_get_device_info); |
@@ -111,16 +115,23 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) | |||
111 | mode != UBI_EXCLUSIVE) | 115 | mode != UBI_EXCLUSIVE) |
112 | return ERR_PTR(-EINVAL); | 116 | return ERR_PTR(-EINVAL); |
113 | 117 | ||
114 | ubi = ubi_devices[ubi_num]; | 118 | /* |
119 | * First of all, we have to get the UBI device to prevent its removal. | ||
120 | */ | ||
121 | ubi = ubi_get_device(ubi_num); | ||
115 | if (!ubi) | 122 | if (!ubi) |
116 | return ERR_PTR(-ENODEV); | 123 | return ERR_PTR(-ENODEV); |
117 | 124 | ||
118 | if (vol_id < 0 || vol_id >= ubi->vtbl_slots) | 125 | if (vol_id < 0 || vol_id >= ubi->vtbl_slots) { |
119 | return ERR_PTR(-EINVAL); | 126 | err = -EINVAL; |
127 | goto out_put_ubi; | ||
128 | } | ||
120 | 129 | ||
121 | desc = kmalloc(sizeof(struct ubi_volume_desc), GFP_KERNEL); | 130 | desc = kmalloc(sizeof(struct ubi_volume_desc), GFP_KERNEL); |
122 | if (!desc) | 131 | if (!desc) { |
123 | return ERR_PTR(-ENOMEM); | 132 | err = -ENOMEM; |
133 | goto out_put_ubi; | ||
134 | } | ||
124 | 135 | ||
125 | err = -ENODEV; | 136 | err = -ENODEV; |
126 | if (!try_module_get(THIS_MODULE)) | 137 | if (!try_module_get(THIS_MODULE)) |
@@ -188,6 +199,8 @@ out_unlock: | |||
188 | module_put(THIS_MODULE); | 199 | module_put(THIS_MODULE); |
189 | out_free: | 200 | out_free: |
190 | kfree(desc); | 201 | kfree(desc); |
202 | out_put_ubi: | ||
203 | ubi_put_device(ubi); | ||
191 | return ERR_PTR(err); | 204 | return ERR_PTR(err); |
192 | } | 205 | } |
193 | EXPORT_SYMBOL_GPL(ubi_open_volume); | 206 | EXPORT_SYMBOL_GPL(ubi_open_volume); |
@@ -205,6 +218,7 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, | |||
205 | { | 218 | { |
206 | int i, vol_id = -1, len; | 219 | int i, vol_id = -1, len; |
207 | struct ubi_device *ubi; | 220 | struct ubi_device *ubi; |
221 | struct ubi_volume_desc *ret; | ||
208 | 222 | ||
209 | dbg_msg("open volume %s, mode %d", name, mode); | 223 | dbg_msg("open volume %s, mode %d", name, mode); |
210 | 224 | ||
@@ -218,7 +232,7 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, | |||
218 | if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) | 232 | if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) |
219 | return ERR_PTR(-EINVAL); | 233 | return ERR_PTR(-EINVAL); |
220 | 234 | ||
221 | ubi = ubi_devices[ubi_num]; | 235 | ubi = ubi_get_device(ubi_num); |
222 | if (!ubi) | 236 | if (!ubi) |
223 | return ERR_PTR(-ENODEV); | 237 | return ERR_PTR(-ENODEV); |
224 | 238 | ||
@@ -234,10 +248,17 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, | |||
234 | } | 248 | } |
235 | spin_unlock(&ubi->volumes_lock); | 249 | spin_unlock(&ubi->volumes_lock); |
236 | 250 | ||
237 | if (vol_id < 0) | 251 | if (vol_id >= 0) |
238 | return ERR_PTR(-ENODEV); | 252 | ret = ubi_open_volume(ubi_num, vol_id, mode); |
253 | else | ||
254 | ret = ERR_PTR(-ENODEV); | ||
239 | 255 | ||
240 | return ubi_open_volume(ubi_num, vol_id, mode); | 256 | /* |
257 | * We should put the UBI device even in case of success, because | ||
258 | * 'ubi_open_volume()' took a reference as well. | ||
259 | */ | ||
260 | ubi_put_device(ubi); | ||
261 | return ret; | ||
241 | } | 262 | } |
242 | EXPORT_SYMBOL_GPL(ubi_open_volume_nm); | 263 | EXPORT_SYMBOL_GPL(ubi_open_volume_nm); |
243 | 264 | ||
@@ -248,10 +269,11 @@ EXPORT_SYMBOL_GPL(ubi_open_volume_nm); | |||
248 | void ubi_close_volume(struct ubi_volume_desc *desc) | 269 | void ubi_close_volume(struct ubi_volume_desc *desc) |
249 | { | 270 | { |
250 | struct ubi_volume *vol = desc->vol; | 271 | struct ubi_volume *vol = desc->vol; |
272 | struct ubi_device *ubi = vol->ubi; | ||
251 | 273 | ||
252 | dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode); | 274 | dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode); |
253 | 275 | ||
254 | spin_lock(&vol->ubi->volumes_lock); | 276 | spin_lock(&ubi->volumes_lock); |
255 | switch (desc->mode) { | 277 | switch (desc->mode) { |
256 | case UBI_READONLY: | 278 | case UBI_READONLY: |
257 | vol->readers -= 1; | 279 | vol->readers -= 1; |
@@ -263,10 +285,11 @@ void ubi_close_volume(struct ubi_volume_desc *desc) | |||
263 | vol->exclusive = 0; | 285 | vol->exclusive = 0; |
264 | } | 286 | } |
265 | vol->ref_count -= 1; | 287 | vol->ref_count -= 1; |
266 | spin_unlock(&vol->ubi->volumes_lock); | 288 | spin_unlock(&ubi->volumes_lock); |
267 | 289 | ||
268 | put_device(&vol->dev); | ||
269 | kfree(desc); | 290 | kfree(desc); |
291 | put_device(&vol->dev); | ||
292 | ubi_put_device(ubi); | ||
270 | module_put(THIS_MODULE); | 293 | module_put(THIS_MODULE); |
271 | } | 294 | } |
272 | EXPORT_SYMBOL_GPL(ubi_close_volume); | 295 | EXPORT_SYMBOL_GPL(ubi_close_volume); |
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 21c028366fd2..91fde0e8ff58 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h | |||
@@ -245,6 +245,7 @@ struct ubi_wl_entry; | |||
245 | * @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count, | 245 | * @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count, |
246 | * @vol->readers, @vol->writers, @vol->exclusive, | 246 | * @vol->readers, @vol->writers, @vol->exclusive, |
247 | * @vol->ref_count, @vol->mapping and @vol->eba_tbl. | 247 | * @vol->ref_count, @vol->mapping and @vol->eba_tbl. |
248 | * @ref_count: count of references on the UBI device | ||
248 | * | 249 | * |
249 | * @rsvd_pebs: count of reserved physical eraseblocks | 250 | * @rsvd_pebs: count of reserved physical eraseblocks |
250 | * @avail_pebs: count of available physical eraseblocks | 251 | * @avail_pebs: count of available physical eraseblocks |
@@ -325,6 +326,7 @@ struct ubi_device { | |||
325 | int vol_count; | 326 | int vol_count; |
326 | struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT]; | 327 | struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT]; |
327 | spinlock_t volumes_lock; | 328 | spinlock_t volumes_lock; |
329 | int ref_count; | ||
328 | 330 | ||
329 | int rsvd_pebs; | 331 | int rsvd_pebs; |
330 | int avail_pebs; | 332 | int avail_pebs; |
@@ -401,7 +403,6 @@ extern struct kmem_cache *ubi_wl_entry_slab; | |||
401 | extern struct file_operations ubi_ctrl_cdev_operations; | 403 | extern struct file_operations ubi_ctrl_cdev_operations; |
402 | extern struct file_operations ubi_cdev_operations; | 404 | extern struct file_operations ubi_cdev_operations; |
403 | extern struct file_operations ubi_vol_cdev_operations; | 405 | extern struct file_operations ubi_vol_cdev_operations; |
404 | extern struct ubi_device *ubi_devices[]; | ||
405 | extern struct class *ubi_class; | 406 | extern struct class *ubi_class; |
406 | 407 | ||
407 | /* vtbl.c */ | 408 | /* vtbl.c */ |
@@ -479,6 +480,12 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, | |||
479 | int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, | 480 | int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, |
480 | struct ubi_vid_hdr *vid_hdr); | 481 | struct ubi_vid_hdr *vid_hdr); |
481 | 482 | ||
483 | /* build.c */ | ||
484 | struct ubi_device *ubi_get_device(int ubi_num); | ||
485 | void ubi_put_device(struct ubi_device *ubi); | ||
486 | struct ubi_device *ubi_get_by_major(int major); | ||
487 | int ubi_major2num(int major); | ||
488 | |||
482 | /* | 489 | /* |
483 | * ubi_rb_for_each_entry - walk an RB-tree. | 490 | * ubi_rb_for_each_entry - walk an RB-tree. |
484 | * @rb: a pointer to type 'struct rb_node' to to use as a loop counter | 491 | * @rb: a pointer to type 'struct rb_node' to to use as a loop counter |
diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 3ed63dc37386..42d3dd70f2d0 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c | |||
@@ -71,11 +71,16 @@ static ssize_t vol_attribute_show(struct device *dev, | |||
71 | { | 71 | { |
72 | int ret; | 72 | int ret; |
73 | struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); | 73 | struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); |
74 | struct ubi_device *ubi = vol->ubi; | 74 | struct ubi_device *ubi; |
75 | |||
76 | ubi = ubi_get_device(vol->ubi->ubi_num); | ||
77 | if (!ubi) | ||
78 | return -ENODEV; | ||
75 | 79 | ||
76 | spin_lock(&ubi->volumes_lock); | 80 | spin_lock(&ubi->volumes_lock); |
77 | if (!ubi->volumes[vol->vol_id]) { | 81 | if (!ubi->volumes[vol->vol_id]) { |
78 | spin_unlock(&ubi->volumes_lock); | 82 | spin_unlock(&ubi->volumes_lock); |
83 | ubi_put_device(ubi); | ||
79 | return -ENODEV; | 84 | return -ENODEV; |
80 | } | 85 | } |
81 | /* Take a reference to prevent volume removal */ | 86 | /* Take a reference to prevent volume removal */ |
@@ -108,10 +113,12 @@ static ssize_t vol_attribute_show(struct device *dev, | |||
108 | /* This must be a bug */ | 113 | /* This must be a bug */ |
109 | ret = -EINVAL; | 114 | ret = -EINVAL; |
110 | 115 | ||
116 | /* We've done the operation, drop volume and UBI device references */ | ||
111 | spin_lock(&ubi->volumes_lock); | 117 | spin_lock(&ubi->volumes_lock); |
112 | vol->ref_count -= 1; | 118 | vol->ref_count -= 1; |
113 | ubi_assert(vol->ref_count >= 0); | 119 | ubi_assert(vol->ref_count >= 0); |
114 | spin_unlock(&ubi->volumes_lock); | 120 | spin_unlock(&ubi->volumes_lock); |
121 | ubi_put_device(ubi); | ||
115 | return ret; | 122 | return ret; |
116 | } | 123 | } |
117 | 124 | ||
@@ -260,6 +267,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) | |||
260 | } | 267 | } |
261 | ubi->avail_pebs -= vol->reserved_pebs; | 268 | ubi->avail_pebs -= vol->reserved_pebs; |
262 | ubi->rsvd_pebs += vol->reserved_pebs; | 269 | ubi->rsvd_pebs += vol->reserved_pebs; |
270 | spin_unlock(&ubi->volumes_lock); | ||
263 | 271 | ||
264 | vol->vol_id = vol_id; | 272 | vol->vol_id = vol_id; |
265 | vol->alignment = req->alignment; | 273 | vol->alignment = req->alignment; |
@@ -267,9 +275,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) | |||
267 | vol->vol_type = req->vol_type; | 275 | vol->vol_type = req->vol_type; |
268 | vol->name_len = req->name_len; | 276 | vol->name_len = req->name_len; |
269 | memcpy(vol->name, req->name, vol->name_len + 1); | 277 | memcpy(vol->name, req->name, vol->name_len + 1); |
270 | vol->exclusive = 1; | ||
271 | vol->ubi = ubi; | 278 | vol->ubi = ubi; |
272 | spin_unlock(&ubi->volumes_lock); | ||
273 | 279 | ||
274 | /* | 280 | /* |
275 | * Finish all pending erases because there may be some LEBs belonging | 281 | * Finish all pending erases because there may be some LEBs belonging |
@@ -350,8 +356,6 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) | |||
350 | goto out_sysfs; | 356 | goto out_sysfs; |
351 | 357 | ||
352 | spin_lock(&ubi->volumes_lock); | 358 | spin_lock(&ubi->volumes_lock); |
353 | ubi->vol_count += 1; | ||
354 | vol->exclusive = 0; | ||
355 | ubi->volumes[vol_id] = vol; | 359 | ubi->volumes[vol_id] = vol; |
356 | spin_unlock(&ubi->volumes_lock); | 360 | spin_unlock(&ubi->volumes_lock); |
357 | 361 | ||
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 7d32f71d6f1e..bfc64c824165 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c | |||
@@ -1303,6 +1303,7 @@ int ubi_wl_flush(struct ubi_device *ubi) | |||
1303 | * Make sure all the works which have been done in parallel are | 1303 | * Make sure all the works which have been done in parallel are |
1304 | * finished. | 1304 | * finished. |
1305 | */ | 1305 | */ |
1306 | ubi_assert(ubi->ref_count > 0); | ||
1306 | down_write(&ubi->work_sem); | 1307 | down_write(&ubi->work_sem); |
1307 | up_write(&ubi->work_sem); | 1308 | up_write(&ubi->work_sem); |
1308 | 1309 | ||