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 | |
| 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>
| -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 | ||
