diff options
author | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2007-12-17 08:42:57 -0500 |
---|---|---|
committer | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2007-12-26 12:15:16 -0500 |
commit | d05c77a816974c09f8c7e8f48e5b9f7b59dafdf3 (patch) | |
tree | 6263ef3ad76c654e9d2635e5145f15c2b517edeb /drivers/mtd/ubi/vmt.c | |
parent | db6e5770ef0ab351a403ac26e1ab1309e58f15d7 (diff) |
UBI: introduce volume refcounting
Add ref_count field to UBI volumes and remove weired "vol->removed"
field. This way things are better understandable and we do not have
to do whold show_attr operation under spinlock.
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Diffstat (limited to 'drivers/mtd/ubi/vmt.c')
-rw-r--r-- | drivers/mtd/ubi/vmt.c | 88 |
1 files changed, 58 insertions, 30 deletions
diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 3d6ac029c17..18ef1e1da49 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c | |||
@@ -63,21 +63,24 @@ static struct device_attribute attr_vol_upd_marker = | |||
63 | * B. process 2 removes volume Y; | 63 | * B. process 2 removes volume Y; |
64 | * C. process 1 starts reading the /<sysfs>/class/ubi/ubiX_Y/reserved_ebs file; | 64 | * C. process 1 starts reading the /<sysfs>/class/ubi/ubiX_Y/reserved_ebs file; |
65 | * | 65 | * |
66 | * What we want to do in a situation like that is to return error when the file | 66 | * In this situation, this function will return %-ENODEV because it will find |
67 | * is read. This is done by means of the 'removed' flag and the 'vol_lock' of | 67 | * out that the volume was removed from the @ubi->volumes array. |
68 | * the UBI volume description object. | ||
69 | */ | 68 | */ |
70 | static ssize_t vol_attribute_show(struct device *dev, | 69 | static ssize_t vol_attribute_show(struct device *dev, |
71 | struct device_attribute *attr, char *buf) | 70 | struct device_attribute *attr, char *buf) |
72 | { | 71 | { |
73 | int ret = -ENODEV; | 72 | int ret; |
74 | 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; | ||
75 | 75 | ||
76 | spin_lock(&vol->ubi->volumes_lock); | 76 | spin_lock(&ubi->volumes_lock); |
77 | if (vol->removed) { | 77 | if (!ubi->volumes[vol->vol_id]) { |
78 | spin_unlock(&vol->ubi->volumes_lock); | 78 | spin_unlock(&ubi->volumes_lock); |
79 | return ret; | 79 | return -ENODEV; |
80 | } | 80 | } |
81 | /* Take a reference to prevent volume removal */ | ||
82 | vol->ref_count += 1; | ||
83 | spin_unlock(&ubi->volumes_lock); | ||
81 | 84 | ||
82 | if (attr == &attr_vol_reserved_ebs) | 85 | if (attr == &attr_vol_reserved_ebs) |
83 | ret = sprintf(buf, "%d\n", vol->reserved_pebs); | 86 | ret = sprintf(buf, "%d\n", vol->reserved_pebs); |
@@ -102,8 +105,13 @@ static ssize_t vol_attribute_show(struct device *dev, | |||
102 | else if (attr == &attr_vol_upd_marker) | 105 | else if (attr == &attr_vol_upd_marker) |
103 | ret = sprintf(buf, "%d\n", vol->upd_marker); | 106 | ret = sprintf(buf, "%d\n", vol->upd_marker); |
104 | else | 107 | else |
105 | BUG(); | 108 | /* This must be a bug */ |
106 | spin_unlock(&vol->ubi->volumes_lock); | 109 | ret = -EINVAL; |
110 | |||
111 | spin_lock(&ubi->volumes_lock); | ||
112 | vol->ref_count -= 1; | ||
113 | ubi_assert(vol->ref_count >= 0); | ||
114 | spin_unlock(&ubi->volumes_lock); | ||
107 | return ret; | 115 | return ret; |
108 | } | 116 | } |
109 | 117 | ||
@@ -179,7 +187,7 @@ static void volume_sysfs_close(struct ubi_volume *vol) | |||
179 | * @req: volume creation request | 187 | * @req: volume creation request |
180 | * | 188 | * |
181 | * This function creates volume described by @req. If @req->vol_id id | 189 | * This function creates volume described by @req. If @req->vol_id id |
182 | * %UBI_VOL_NUM_AUTO, this function automatically assigne ID to the new volume | 190 | * %UBI_VOL_NUM_AUTO, this function automatically assign ID to the new volume |
183 | * and saves it in @req->vol_id. Returns zero in case of success and a negative | 191 | * and saves it in @req->vol_id. Returns zero in case of success and a negative |
184 | * error code in case of failure. | 192 | * error code in case of failure. |
185 | */ | 193 | */ |
@@ -261,7 +269,6 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) | |||
261 | memcpy(vol->name, req->name, vol->name_len + 1); | 269 | memcpy(vol->name, req->name, vol->name_len + 1); |
262 | vol->exclusive = 1; | 270 | vol->exclusive = 1; |
263 | vol->ubi = ubi; | 271 | vol->ubi = ubi; |
264 | ubi->volumes[vol_id] = vol; | ||
265 | spin_unlock(&ubi->volumes_lock); | 272 | spin_unlock(&ubi->volumes_lock); |
266 | 273 | ||
267 | /* | 274 | /* |
@@ -345,6 +352,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) | |||
345 | spin_lock(&ubi->volumes_lock); | 352 | spin_lock(&ubi->volumes_lock); |
346 | ubi->vol_count += 1; | 353 | ubi->vol_count += 1; |
347 | vol->exclusive = 0; | 354 | vol->exclusive = 0; |
355 | ubi->volumes[vol_id] = vol; | ||
348 | spin_unlock(&ubi->volumes_lock); | 356 | spin_unlock(&ubi->volumes_lock); |
349 | 357 | ||
350 | paranoid_check_volumes(ubi); | 358 | paranoid_check_volumes(ubi); |
@@ -353,7 +361,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) | |||
353 | 361 | ||
354 | out_sysfs: | 362 | out_sysfs: |
355 | /* | 363 | /* |
356 | * We have degistered our device, we should not free the volume* | 364 | * We have registered our device, we should not free the volume* |
357 | * description object in this function in case of an error - it is | 365 | * description object in this function in case of an error - it is |
358 | * freed by the release function. | 366 | * freed by the release function. |
359 | * | 367 | * |
@@ -373,7 +381,6 @@ out_acc: | |||
373 | spin_lock(&ubi->volumes_lock); | 381 | spin_lock(&ubi->volumes_lock); |
374 | ubi->rsvd_pebs -= vol->reserved_pebs; | 382 | ubi->rsvd_pebs -= vol->reserved_pebs; |
375 | ubi->avail_pebs += vol->reserved_pebs; | 383 | ubi->avail_pebs += vol->reserved_pebs; |
376 | ubi->volumes[vol_id] = NULL; | ||
377 | out_unlock: | 384 | out_unlock: |
378 | spin_unlock(&ubi->volumes_lock); | 385 | spin_unlock(&ubi->volumes_lock); |
379 | mutex_unlock(&ubi->volumes_mutex); | 386 | mutex_unlock(&ubi->volumes_mutex); |
@@ -407,25 +414,32 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) | |||
407 | return -EROFS; | 414 | return -EROFS; |
408 | 415 | ||
409 | mutex_lock(&ubi->volumes_mutex); | 416 | mutex_lock(&ubi->volumes_mutex); |
417 | spin_lock(&ubi->volumes_lock); | ||
418 | if (vol->ref_count > 1) { | ||
419 | /* | ||
420 | * The volume is busy, probably someone is reading one of its | ||
421 | * sysfs files. | ||
422 | */ | ||
423 | err = -EBUSY; | ||
424 | goto out_unlock; | ||
425 | } | ||
426 | ubi->volumes[vol_id] = NULL; | ||
427 | spin_unlock(&ubi->volumes_lock); | ||
428 | |||
410 | err = ubi_destroy_gluebi(vol); | 429 | err = ubi_destroy_gluebi(vol); |
411 | if (err) | 430 | if (err) |
412 | goto out; | 431 | goto out_err; |
413 | 432 | ||
414 | err = ubi_change_vtbl_record(ubi, vol_id, NULL); | 433 | err = ubi_change_vtbl_record(ubi, vol_id, NULL); |
415 | if (err) | 434 | if (err) |
416 | goto out; | 435 | goto out_err; |
417 | 436 | ||
418 | for (i = 0; i < vol->reserved_pebs; i++) { | 437 | for (i = 0; i < vol->reserved_pebs; i++) { |
419 | err = ubi_eba_unmap_leb(ubi, vol, i); | 438 | err = ubi_eba_unmap_leb(ubi, vol, i); |
420 | if (err) | 439 | if (err) |
421 | goto out; | 440 | goto out_err; |
422 | } | 441 | } |
423 | 442 | ||
424 | spin_lock(&ubi->volumes_lock); | ||
425 | vol->removed = 1; | ||
426 | ubi->volumes[vol_id] = NULL; | ||
427 | spin_unlock(&ubi->volumes_lock); | ||
428 | |||
429 | kfree(vol->eba_tbl); | 443 | kfree(vol->eba_tbl); |
430 | vol->eba_tbl = NULL; | 444 | vol->eba_tbl = NULL; |
431 | cdev_del(&vol->cdev); | 445 | cdev_del(&vol->cdev); |
@@ -447,7 +461,15 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) | |||
447 | spin_unlock(&ubi->volumes_lock); | 461 | spin_unlock(&ubi->volumes_lock); |
448 | 462 | ||
449 | paranoid_check_volumes(ubi); | 463 | paranoid_check_volumes(ubi); |
450 | out: | 464 | mutex_unlock(&ubi->volumes_mutex); |
465 | return 0; | ||
466 | |||
467 | out_err: | ||
468 | ubi_err("cannot remove volume %d, error %d", vol_id, err); | ||
469 | spin_lock(&ubi->volumes_lock); | ||
470 | ubi->volumes[vol_id] = vol; | ||
471 | out_unlock: | ||
472 | spin_unlock(&ubi->volumes_lock); | ||
451 | mutex_unlock(&ubi->volumes_mutex); | 473 | mutex_unlock(&ubi->volumes_mutex); |
452 | return err; | 474 | return err; |
453 | } | 475 | } |
@@ -494,8 +516,17 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) | |||
494 | for (i = 0; i < reserved_pebs; i++) | 516 | for (i = 0; i < reserved_pebs; i++) |
495 | new_mapping[i] = UBI_LEB_UNMAPPED; | 517 | new_mapping[i] = UBI_LEB_UNMAPPED; |
496 | 518 | ||
497 | /* Reserve physical eraseblocks */ | ||
498 | mutex_lock(&ubi->volumes_mutex); | 519 | mutex_lock(&ubi->volumes_mutex); |
520 | spin_lock(&ubi->volumes_lock); | ||
521 | if (vol->ref_count > 1) { | ||
522 | spin_unlock(&ubi->volumes_lock); | ||
523 | err = -EBUSY; | ||
524 | goto out_free; | ||
525 | } | ||
526 | spin_unlock(&ubi->volumes_lock); | ||
527 | |||
528 | |||
529 | /* Reserve physical eraseblocks */ | ||
499 | pebs = reserved_pebs - vol->reserved_pebs; | 530 | pebs = reserved_pebs - vol->reserved_pebs; |
500 | if (pebs > 0) { | 531 | if (pebs > 0) { |
501 | spin_lock(&ubi->volumes_lock); | 532 | spin_lock(&ubi->volumes_lock); |
@@ -577,8 +608,8 @@ out_free: | |||
577 | * @ubi: UBI device description object | 608 | * @ubi: UBI device description object |
578 | * @vol: volume description object | 609 | * @vol: volume description object |
579 | * | 610 | * |
580 | * This function adds an existin volume and initializes all its data | 611 | * This function adds an existing volume and initializes all its data |
581 | * structures. Returnes zero in case of success and a negative error code in | 612 | * structures. Returns zero in case of success and a negative error code in |
582 | * case of failure. | 613 | * case of failure. |
583 | */ | 614 | */ |
584 | int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) | 615 | int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) |
@@ -588,7 +619,6 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) | |||
588 | 619 | ||
589 | dbg_msg("add volume %d", vol_id); | 620 | dbg_msg("add volume %d", vol_id); |
590 | ubi_dbg_dump_vol_info(vol); | 621 | ubi_dbg_dump_vol_info(vol); |
591 | ubi_assert(vol); | ||
592 | 622 | ||
593 | /* Register character device for the volume */ | 623 | /* Register character device for the volume */ |
594 | cdev_init(&vol->cdev, &ubi_vol_cdev_operations); | 624 | cdev_init(&vol->cdev, &ubi_vol_cdev_operations); |
@@ -645,11 +675,9 @@ void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol) | |||
645 | int err; | 675 | int err; |
646 | 676 | ||
647 | dbg_msg("free volume %d", vol->vol_id); | 677 | dbg_msg("free volume %d", vol->vol_id); |
648 | ubi_assert(vol); | ||
649 | 678 | ||
650 | vol->removed = 1; | ||
651 | err = ubi_destroy_gluebi(vol); | ||
652 | ubi->volumes[vol->vol_id] = NULL; | 679 | ubi->volumes[vol->vol_id] = NULL; |
680 | err = ubi_destroy_gluebi(vol); | ||
653 | cdev_del(&vol->cdev); | 681 | cdev_del(&vol->cdev); |
654 | volume_sysfs_close(vol); | 682 | volume_sysfs_close(vol); |
655 | } | 683 | } |